From e61fbfffda8d09080c964489b8ebed80e9d1cb29 Mon Sep 17 00:00:00 2001 From: zijiehe Date: Tue, 29 Nov 2016 16:09:51 -0800 Subject: [PATCH] Use RotateDesktopFrame in DirectX capturer To support rotation in DirectX capturer, several other changes are also required. 1. Removing AddRect in RotateDesktopFrame, this is a performance improvement. DxgiOutputDuplicator creates a rotated DesktopRegion, which can be directly add to updated_region. 2. DxgiOutputDuplicator::SourceRect() is not accurate, the rectangle in source is controlled by |offset| or |rotation_| + |offset|, instead of desktop_rect(). 3. The |region| in DxgiTexture::CopyFrom() is not accurate. It needs an unrotated DesktopRegion which offsets by |offset| instead of desktop_rect(). To avoid generating both rotated and unrotated updated_region, this parameter has been removed. This impacts DxgiTextureStagning performance a little bit (1.5ms). Refer to bug for details. BUG=webrtc:6646 Review-Url: https://codereview.webrtc.org/2530303002 Cr-Commit-Position: refs/heads/master@{#15308} --- .../desktop_capture/desktop_frame_rotation.cc | 1 - .../desktop_capture/desktop_frame_rotation.h | 3 +- .../win/dxgi_duplicator_controller.h | 6 +- .../win/dxgi_output_duplicator.cc | 110 ++++++++++++------ .../win/dxgi_output_duplicator.h | 8 +- .../desktop_capture/win/dxgi_texture.cc | 6 +- .../desktop_capture/win/dxgi_texture.h | 11 +- .../win/dxgi_texture_mapping.cc | 7 +- .../win/dxgi_texture_mapping.h | 5 +- .../win/dxgi_texture_staging.cc | 27 ++--- .../win/dxgi_texture_staging.h | 5 +- 11 files changed, 105 insertions(+), 84 deletions(-) diff --git a/webrtc/modules/desktop_capture/desktop_frame_rotation.cc b/webrtc/modules/desktop_capture/desktop_frame_rotation.cc index 7a3a8acc4f..71cd74b4e5 100644 --- a/webrtc/modules/desktop_capture/desktop_frame_rotation.cc +++ b/webrtc/modules/desktop_capture/desktop_frame_rotation.cc @@ -108,7 +108,6 @@ void RotateDesktopFrame(const DesktopFrame& source, return; } - target->mutable_updated_region()->AddRect(target_rect); int result = libyuv::ARGBRotate( source.GetFrameDataAtPos(source_rect.top_left()), source.stride(), target->GetFrameDataAtPos(target_rect.top_left()), target->stride(), diff --git a/webrtc/modules/desktop_capture/desktop_frame_rotation.h b/webrtc/modules/desktop_capture/desktop_frame_rotation.h index e78ea83f67..c0024aaf21 100644 --- a/webrtc/modules/desktop_capture/desktop_frame_rotation.h +++ b/webrtc/modules/desktop_capture/desktop_frame_rotation.h @@ -25,8 +25,7 @@ enum class Rotation { }; // Rotates input DesktopFrame |source|, copies pixel in an unrotated rectangle -// |source_rect| into the target rectangle of another DesktopFrame |target|, and -// adds target rectangle into |target|->mutable_updated_region(). +// |source_rect| into the target rectangle of another DesktopFrame |target|. // Target rectangle here is the rotated |source_rect| plus |target_offset|. // |rotation| specifies |source| to |target| rotation. |source_rect| is in // |source| coordinate. |target_offset| is in |target| coordinate. diff --git a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h index 9d37010551..d13961d0da 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h +++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h @@ -26,8 +26,8 @@ namespace webrtc { // A controller for all the objects we need to call Windows DirectX capture APIs -// It's a singleton because, only one IDXGIOutputDuplication instance per -// monitor is allowed per application. +// It's a singleton because only one IDXGIOutputDuplication instance per monitor +// is allowed per application. // // Consumers should create a DxgiDuplicatorController::Context and keep it // throughout their lifetime, and pass it when calling Duplicate(). Consumers @@ -81,7 +81,7 @@ class DxgiDuplicatorController { // containers are destructed in correct order. ~DxgiDuplicatorController(); - // All the following functions implicitly call Initialize() function is + // All the following functions implicitly call Initialize() function if // current instance has not been initialized. // Detects whether the system supports DXGI based capturer. diff --git a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc index 9c0f14c43d..9e35b6cdc5 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -36,6 +37,29 @@ DesktopRect RECTToDesktopRect(const RECT& rect) { return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); } +Rotation DxgiRotationToRotation(DXGI_MODE_ROTATION rotation) { + switch (rotation) { + case DXGI_MODE_ROTATION_IDENTITY: + case DXGI_MODE_ROTATION_UNSPECIFIED: + return Rotation::CLOCK_WISE_0; + case DXGI_MODE_ROTATION_ROTATE90: + return Rotation::CLOCK_WISE_90; + case DXGI_MODE_ROTATION_ROTATE180: + return Rotation::CLOCK_WISE_180; + case DXGI_MODE_ROTATION_ROTATE270: + return Rotation::CLOCK_WISE_270; + } + + RTC_NOTREACHED(); + return Rotation::CLOCK_WISE_0; +} + +// Translates |rect| with the reverse of |offset| +DesktopRect ReverseTranslate(DesktopRect rect, DesktopVector offset) { + rect.Translate(-offset.x(), -offset.y()); + return rect; +} + } // namespace DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device, @@ -61,10 +85,13 @@ DxgiOutputDuplicator::~DxgiOutputDuplicator() { bool DxgiOutputDuplicator::Initialize() { if (DuplicateOutput()) { + DesktopSize unrotated_size = + RotateSize(desktop_rect().size(), ReverseRotation(rotation_)); if (desc_.DesktopImageInSystemMemory) { - texture_.reset(new DxgiTextureMapping(desktop_rect_, duplication_.Get())); + texture_.reset( + new DxgiTextureMapping(unrotated_size, duplication_.Get())); } else { - texture_.reset(new DxgiTextureStaging(desktop_rect_, device_)); + texture_.reset(new DxgiTextureStaging(unrotated_size, device_)); } return true; } else { @@ -104,6 +131,10 @@ bool DxgiOutputDuplicator::DuplicateOutput() { return false; } + rotation_ = DxgiRotationToRotation(desc_.Rotation); + unrotated_size_ = + RotateSize(desktop_rect_.size(), ReverseRotation(rotation_)); + return true; } @@ -142,29 +173,36 @@ bool DxgiOutputDuplicator::Duplicate(Context* context, return false; } - // We need to merge updated region with the one from last frame, since current - // frame contains the content one frame before. Note, this is for double - // buffering implementation, as what we have in ScreenCapturerWinDirectx. If - // a consumer uses single buffering, we should clear context->updated_region - // after it has been merged to updated_region. + // We need to merge updated region with the one from context, but only spread + // updated region from current frame. So keeps a copy of updated region from + // context here. DesktopRegion updated_region; updated_region.Swap(&context->updated_region); if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0 && resource) { DetectUpdatedRegion(frame_info, offset, &context->updated_region); - if (!texture_->CopyFrom(frame_info, resource.Get(), - context->updated_region)) { + if (!texture_->CopyFrom(frame_info, resource.Get())) { return false; } SpreadContextChange(context); updated_region.AddRegion(context->updated_region); const DesktopFrame& source = texture_->AsDesktopFrame(); - for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); - it.Advance()) { - target->CopyPixelsFrom( - source, SourceRect(it.rect()).top_left(), it.rect()); + if (rotation_ != Rotation::CLOCK_WISE_0) { + for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); + it.Advance()) { + const DesktopRect source_rect = + RotateRect(ReverseTranslate(it.rect(), offset), + desktop_rect().size(), ReverseRotation(rotation_)); + RotateDesktopFrame(source, source_rect, rotation_, offset, target); + } + } else { + for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); + it.Advance()) { + target->CopyPixelsFrom( + source, ReverseTranslate(it.rect(), offset).top_left(), it.rect()); + } } last_frame_ = target->Share(); last_frame_offset_ = offset; @@ -218,17 +256,17 @@ bool DxgiOutputDuplicator::DoDetectUpdatedRegion( return false; } - if (metadata.capacity() < frame_info.TotalMetadataBufferSize) { - metadata.clear(); // Avoid data copy - metadata.reserve(frame_info.TotalMetadataBufferSize); + if (metadata_.capacity() < frame_info.TotalMetadataBufferSize) { + metadata_.clear(); // Avoid data copy + metadata_.reserve(frame_info.TotalMetadataBufferSize); } UINT buff_size = 0; DXGI_OUTDUPL_MOVE_RECT* move_rects = - reinterpret_cast(metadata.data()); + reinterpret_cast(metadata_.data()); size_t move_rects_count = 0; _com_error error = duplication_->GetFrameMoveRects( - static_cast(metadata.capacity()), move_rects, &buff_size); + static_cast(metadata_.capacity()), move_rects, &buff_size); if (error.Error() != S_OK) { LOG(LS_ERROR) << "Failed to get move rectangles, error " << error.ErrorMessage() << ", code " << error.Error(); @@ -236,10 +274,10 @@ bool DxgiOutputDuplicator::DoDetectUpdatedRegion( } move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT); - RECT* dirty_rects = reinterpret_cast(metadata.data() + buff_size); + RECT* dirty_rects = reinterpret_cast(metadata_.data() + buff_size); size_t dirty_rects_count = 0; error = duplication_->GetFrameDirtyRects( - static_cast(metadata.capacity()) - buff_size, dirty_rects, + static_cast(metadata_.capacity()) - buff_size, dirty_rects, &buff_size); if (error.Error() != S_OK) { LOG(LS_ERROR) << "Failed to get dirty rectangles, error " @@ -249,21 +287,29 @@ bool DxgiOutputDuplicator::DoDetectUpdatedRegion( dirty_rects_count = buff_size / sizeof(RECT); while (move_rects_count > 0) { - updated_region->AddRect(DesktopRect::MakeXYWH( - move_rects->SourcePoint.x, move_rects->SourcePoint.y, - move_rects->DestinationRect.right - move_rects->DestinationRect.left, - move_rects->DestinationRect.bottom - move_rects->DestinationRect.top)); - updated_region->AddRect(DesktopRect::MakeLTRB( - move_rects->DestinationRect.left, move_rects->DestinationRect.top, - move_rects->DestinationRect.right, move_rects->DestinationRect.bottom)); + updated_region->AddRect( + RotateRect(DesktopRect::MakeXYWH(move_rects->SourcePoint.x, + move_rects->SourcePoint.y, + move_rects->DestinationRect.right - + move_rects->DestinationRect.left, + move_rects->DestinationRect.bottom - + move_rects->DestinationRect.top), + unrotated_size_, rotation_)); + updated_region->AddRect( + RotateRect(DesktopRect::MakeLTRB(move_rects->DestinationRect.left, + move_rects->DestinationRect.top, + move_rects->DestinationRect.right, + move_rects->DestinationRect.bottom), + unrotated_size_, rotation_)); move_rects++; move_rects_count--; } while (dirty_rects_count > 0) { - updated_region->AddRect( + updated_region->AddRect(RotateRect( DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top, - dirty_rects->right, dirty_rects->bottom)); + dirty_rects->right, dirty_rects->bottom), + unrotated_size_, rotation_)); dirty_rects++; dirty_rects_count--; } @@ -295,10 +341,4 @@ void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { } } -DesktopRect DxgiOutputDuplicator::SourceRect(DesktopRect rect) { - // |texture_|->AsDesktopFrame() starts from (0, 0). - rect.Translate(-desktop_rect_.left(), -desktop_rect_.top()); - return rect; -} - } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h index b57569014f..1f22643971 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h +++ b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h @@ -23,6 +23,7 @@ #include "webrtc/base/thread_annotations.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" #include "webrtc/modules/desktop_capture/desktop_region.h" +#include "webrtc/modules/desktop_capture/desktop_frame_rotation.h" #include "webrtc/modules/desktop_capture/shared_desktop_frame.h" #include "webrtc/modules/desktop_capture/win/d3d_device.h" #include "webrtc/modules/desktop_capture/win/dxgi_texture.h" @@ -105,16 +106,15 @@ class DxgiOutputDuplicator { // contexts_. void SpreadContextChange(const Context* const context); - // Returns a DesktopRect in the coordinate of |texture_|->AsDesktopFrame(). - DesktopRect SourceRect(DesktopRect rect); - const D3dDevice device_; const Microsoft::WRL::ComPtr output_; const DesktopRect desktop_rect_; Microsoft::WRL::ComPtr duplication_; DXGI_OUTDUPL_DESC desc_; - std::vector metadata; + std::vector metadata_; std::unique_ptr texture_; + Rotation rotation_; + DesktopSize unrotated_size_; // After each AcquireNextFrame() function call, updated_region_(s) of all // active Context(s) need to be updated. Since they have missed the diff --git a/webrtc/modules/desktop_capture/win/dxgi_texture.cc b/webrtc/modules/desktop_capture/win/dxgi_texture.cc index c798631bdc..9af8a06cd5 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_texture.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_texture.cc @@ -19,7 +19,7 @@ namespace { class DxgiDesktopFrame : public DesktopFrame { public: explicit DxgiDesktopFrame(const DxgiTexture& texture) - : DesktopFrame(texture.desktop_rect().size(), + : DesktopFrame(texture.desktop_size(), texture.pitch(), texture.bits(), nullptr) {} @@ -29,8 +29,8 @@ class DxgiDesktopFrame : public DesktopFrame { } // namespace -DxgiTexture::DxgiTexture(const DesktopRect& desktop_rect) - : desktop_rect_(desktop_rect) {} +DxgiTexture::DxgiTexture(const DesktopSize& desktop_size) + : desktop_size_(desktop_size) {} DxgiTexture::~DxgiTexture() {} diff --git a/webrtc/modules/desktop_capture/win/dxgi_texture.h b/webrtc/modules/desktop_capture/win/dxgi_texture.h index 9500082c94..a4bd237e01 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_texture.h +++ b/webrtc/modules/desktop_capture/win/dxgi_texture.h @@ -25,19 +25,18 @@ class DesktopRegion; // A texture copied or mapped from a DXGI_OUTDUPL_FRAME_INFO and IDXGIResource. class DxgiTexture { public: - // Creates a DxgiTexture instance, which represents the DesktopRect area of + // Creates a DxgiTexture instance, which represents the |desktop_size| area of // entire screen -- usually a monitor on the system. - explicit DxgiTexture(const DesktopRect& desktop_rect); + explicit DxgiTexture(const DesktopSize& desktop_size); virtual ~DxgiTexture(); // Copies selected regions of a frame represented by frame_info and resource. // Returns false if anything wrong. virtual bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info, - IDXGIResource* resource, - const DesktopRegion& region) = 0; + IDXGIResource* resource) = 0; - const DesktopRect& desktop_rect() const { return desktop_rect_; } + const DesktopSize& desktop_size() const { return desktop_size_; } uint8_t* bits() const { return static_cast(rect_.pBits); } @@ -60,7 +59,7 @@ class DxgiTexture { private: virtual bool DoRelease() = 0; - const DesktopRect desktop_rect_; + const DesktopSize desktop_size_; std::unique_ptr frame_; }; diff --git a/webrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc b/webrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc index 573b9a053f..dbe4948506 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc @@ -19,17 +19,16 @@ namespace webrtc { -DxgiTextureMapping::DxgiTextureMapping(const DesktopRect& desktop_rect, +DxgiTextureMapping::DxgiTextureMapping(const DesktopSize& desktop_size, IDXGIOutputDuplication* duplication) - : DxgiTexture(desktop_rect), duplication_(duplication) { + : DxgiTexture(desktop_size), duplication_(duplication) { RTC_DCHECK(duplication_); } DxgiTextureMapping::~DxgiTextureMapping() = default; bool DxgiTextureMapping::CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info, - IDXGIResource* resource, - const DesktopRegion& region) { + IDXGIResource* resource) { RTC_DCHECK(resource && frame_info.AccumulatedFrames > 0); rect_ = {0}; _com_error error = duplication_->MapDesktopSurface(&rect_); diff --git a/webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h b/webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h index dd3f407475..f6760df6df 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h +++ b/webrtc/modules/desktop_capture/win/dxgi_texture_mapping.h @@ -28,14 +28,13 @@ class DxgiTextureMapping : public DxgiTexture { public: // Creates a DxgiTextureMapping instance. Caller must maintain the lifetime // of input duplication to make sure it outlives this instance. - DxgiTextureMapping(const DesktopRect& desktop_rect, + DxgiTextureMapping(const DesktopSize& desktop_size, IDXGIOutputDuplication* duplication); ~DxgiTextureMapping() override; bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info, - IDXGIResource* resource, - const DesktopRegion& region) override; + IDXGIResource* resource) override; bool DoRelease() override; diff --git a/webrtc/modules/desktop_capture/win/dxgi_texture_staging.cc b/webrtc/modules/desktop_capture/win/dxgi_texture_staging.cc index 6866d1e4c6..f11a3c1e28 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_texture_staging.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_texture_staging.cc @@ -22,9 +22,9 @@ using Microsoft::WRL::ComPtr; namespace webrtc { -DxgiTextureStaging::DxgiTextureStaging(const DesktopRect& desktop_rect, +DxgiTextureStaging::DxgiTextureStaging(const DesktopSize& desktop_size, const D3dDevice& device) - : DxgiTexture(desktop_rect), device_(device) {} + : DxgiTexture(desktop_size), device_(device) {} DxgiTextureStaging::~DxgiTextureStaging() = default; @@ -32,8 +32,8 @@ bool DxgiTextureStaging::InitializeStage(ID3D11Texture2D* texture) { RTC_DCHECK(texture); D3D11_TEXTURE2D_DESC desc = {0}; texture->GetDesc(&desc); - if (static_cast(desc.Width) != desktop_rect().width() || - static_cast(desc.Height) != desktop_rect().height()) { + if (static_cast(desc.Width) != desktop_size().width() || + static_cast(desc.Height) != desktop_size().height()) { LOG(LS_ERROR) << "Texture size is not consistent with current DxgiTexture."; return false; } @@ -91,8 +91,7 @@ void DxgiTextureStaging::AssertStageAndSurfaceAreSameObject() { } bool DxgiTextureStaging::CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info, - IDXGIResource* resource, - const DesktopRegion& region) { + IDXGIResource* resource) { RTC_DCHECK(resource && frame_info.AccumulatedFrames > 0); ComPtr texture; _com_error error = resource->QueryInterface( @@ -111,20 +110,8 @@ bool DxgiTextureStaging::CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info, return false; } - for (DesktopRegion::Iterator it(region); !it.IsAtEnd(); it.Advance()) { - DesktopRect rect(it.rect()); - rect.Translate(-desktop_rect().left(), -desktop_rect().top()); - D3D11_BOX box; - box.left = rect.left(); - box.top = rect.top(); - box.right = rect.right(); - box.bottom = rect.bottom(); - box.front = 0; - box.back = 1; - device_.context()->CopySubresourceRegion( - static_cast(stage_.Get()), 0, rect.left(), rect.top(), - 0, static_cast(texture.Get()), 0, &box); - } + device_.context()->CopyResource(static_cast(stage_.Get()), + static_cast(texture.Get())); rect_ = {0}; error = surface_->Map(&rect_, DXGI_MAP_READ); diff --git a/webrtc/modules/desktop_capture/win/dxgi_texture_staging.h b/webrtc/modules/desktop_capture/win/dxgi_texture_staging.h index 47f75fa8c1..3bdf30e215 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_texture_staging.h +++ b/webrtc/modules/desktop_capture/win/dxgi_texture_staging.h @@ -33,15 +33,14 @@ class DxgiTextureStaging : public DxgiTexture { public: // Creates a DxgiTextureStaging instance. Caller must maintain the lifetime // of input device to make sure it outlives this instance. - DxgiTextureStaging(const DesktopRect& desktop_rect, const D3dDevice& device); + DxgiTextureStaging(const DesktopSize& desktop_size, const D3dDevice& device); ~DxgiTextureStaging() override; // Copies selected regions of a frame represented by frame_info and resource. // Returns false if anything wrong. bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info, - IDXGIResource* resource, - const DesktopRegion& region) override; + IDXGIResource* resource) override; bool DoRelease() override;