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}
This commit is contained in:
zijiehe
2016-11-29 16:09:51 -08:00
committed by Commit bot
parent 166e59a70f
commit e61fbfffda
11 changed files with 105 additions and 84 deletions

View File

@ -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(),

View File

@ -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.

View File

@ -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.

View File

@ -13,6 +13,7 @@
#include <string.h>
#include <unknwn.h>
#include <DXGI.h>
#include <DXGIFormat.h>
#include <Windows.h>
@ -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<DXGI_OUTDUPL_MOVE_RECT*>(metadata.data());
reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata_.data());
size_t move_rects_count = 0;
_com_error error = duplication_->GetFrameMoveRects(
static_cast<UINT>(metadata.capacity()), move_rects, &buff_size);
static_cast<UINT>(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<RECT*>(metadata.data() + buff_size);
RECT* dirty_rects = reinterpret_cast<RECT*>(metadata_.data() + buff_size);
size_t dirty_rects_count = 0;
error = duplication_->GetFrameDirtyRects(
static_cast<UINT>(metadata.capacity()) - buff_size, dirty_rects,
static_cast<UINT>(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

View File

@ -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<IDXGIOutput1> output_;
const DesktopRect desktop_rect_;
Microsoft::WRL::ComPtr<IDXGIOutputDuplication> duplication_;
DXGI_OUTDUPL_DESC desc_;
std::vector<uint8_t> metadata;
std::vector<uint8_t> metadata_;
std::unique_ptr<DxgiTexture> 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

View File

@ -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() {}

View File

@ -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<uint8_t*>(rect_.pBits); }
@ -60,7 +59,7 @@ class DxgiTexture {
private:
virtual bool DoRelease() = 0;
const DesktopRect desktop_rect_;
const DesktopSize desktop_size_;
std::unique_ptr<DesktopFrame> frame_;
};

View File

@ -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_);

View File

@ -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;

View File

@ -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<int>(desc.Width) != desktop_rect().width() ||
static_cast<int>(desc.Height) != desktop_rect().height()) {
if (static_cast<int>(desc.Width) != desktop_size().width() ||
static_cast<int>(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<ID3D11Texture2D> 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<ID3D11Resource*>(stage_.Get()), 0, rect.left(), rect.top(),
0, static_cast<ID3D11Resource*>(texture.Get()), 0, &box);
}
device_.context()->CopyResource(static_cast<ID3D11Resource*>(stage_.Get()),
static_cast<ID3D11Resource*>(texture.Get()));
rect_ = {0};
error = surface_->Map(&rect_, DXGI_MAP_READ);

View File

@ -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;