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:
@ -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(),
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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() {}
|
||||
|
||||
|
||||
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@ -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_);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user