[DesktopCapture][WGC] Avoid artifacts when capture source is resized

This CL fixes the issue where artifacts appear during capture with WGC
when the capture source is resized. A video of the issue is available
here: https://bugs.chromium.org/p/webrtc/issues/detail?id=9273#c44

The solution is to use CopySubresourceRegion instead of CopyResource to
only copy valid data into our texture. Additionally, we moved the call
to CreateMappedTexture to before the call to CopySubresourceRegion, as
the latter requires both textures to be of the same size.

Bug: webrtc:9273
Change-Id: I114458d95cbf58550ff653a985dd84db4741e0f8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/254100
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Austin Orion <auorion@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#36163}
This commit is contained in:
Austin Orion
2022-03-08 15:47:17 -08:00
committed by WebRTC LUCI CQ
parent 878c0299b3
commit a5f3018c24

View File

@ -223,8 +223,8 @@ HRESULT WgcCaptureSession::GetFrame(
return hr;
}
// We need to get this CaptureFrame as an ID3D11Texture2D so that we can get
// the raw image data in the format required by the DesktopFrame interface.
// We need to get `capture_frame` as an `ID3D11Texture2D` so that we can get
// the raw image data in the format required by the `DesktopFrame` interface.
ComPtr<ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface>
d3d_surface;
hr = capture_frame->get_Surface(&d3d_surface);
@ -261,16 +261,6 @@ HRESULT WgcCaptureSession::GetFrame(
// Otherwise it would only be readable by the GPU.
ComPtr<ID3D11DeviceContext> d3d_context;
d3d11_device_->GetImmediateContext(&d3d_context);
d3d_context->CopyResource(mapped_texture_.Get(), texture_2D.Get());
D3D11_MAPPED_SUBRESOURCE map_info;
hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0,
D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0,
&map_info);
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kMapFrameFailed);
return hr;
}
ABI::Windows::Graphics::SizeInt32 new_size;
hr = capture_frame->get_ContentSize(&new_size);
@ -279,34 +269,9 @@ HRESULT WgcCaptureSession::GetFrame(
return hr;
}
// If the size has changed since the last capture, we must be sure to use
// the smaller dimensions. Otherwise we might overrun our buffer, or
// read stale data from the last frame.
int image_height = std::min(previous_size_.Height, new_size.Height);
int image_width = std::min(previous_size_.Width, new_size.Width);
int row_data_length = image_width * DesktopFrame::kBytesPerPixel;
// Make a copy of the data pointed to by `map_info.pData` so we are free to
// unmap our texture.
uint8_t* src_data = static_cast<uint8_t*>(map_info.pData);
std::vector<uint8_t> image_data;
image_data.resize(image_height * row_data_length);
uint8_t* image_data_ptr = image_data.data();
for (int i = 0; i < image_height; i++) {
memcpy(image_data_ptr, src_data, row_data_length);
image_data_ptr += row_data_length;
src_data += map_info.RowPitch;
}
// Transfer ownership of `image_data` to the output_frame.
DesktopSize size(image_width, image_height);
*output_frame = std::make_unique<WgcDesktopFrame>(size, row_data_length,
std::move(image_data));
d3d_context->Unmap(mapped_texture_.Get(), 0);
// If the size changed, we must resize the texture and frame pool to fit the
// new size.
// If the size changed, we must resize `mapped_texture_` and `frame_pool_` to
// fit the new size. This must be done before `CopySubresourceRegion` so that
// the textures are the same size.
if (previous_size_.Height != new_size.Height ||
previous_size_.Width != new_size.Width) {
hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height);
@ -323,9 +288,57 @@ HRESULT WgcCaptureSession::GetFrame(
}
}
RecordGetFrameResult(GetFrameResult::kSuccess);
// If the size has changed since the last capture, we must be sure to use
// the smaller dimensions. Otherwise we might overrun our buffer, or
// read stale data from the last frame.
int image_height = std::min(previous_size_.Height, new_size.Height);
int image_width = std::min(previous_size_.Width, new_size.Width);
D3D11_BOX copy_region;
copy_region.left = 0;
copy_region.top = 0;
copy_region.right = image_width;
copy_region.bottom = image_height;
// Our textures are 2D so we just want one "slice" of the box.
copy_region.front = 0;
copy_region.back = 1;
d3d_context->CopySubresourceRegion(mapped_texture_.Get(),
/*dst_subresource_index=*/0, /*dst_x=*/0,
/*dst_y=*/0, /*dst_z=*/0, texture_2D.Get(),
/*src_subresource_index=*/0, &copy_region);
D3D11_MAPPED_SUBRESOURCE map_info;
hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0,
D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0,
&map_info);
if (FAILED(hr)) {
RecordGetFrameResult(GetFrameResult::kMapFrameFailed);
return hr;
}
int row_data_length = image_width * DesktopFrame::kBytesPerPixel;
// Make a copy of the data pointed to by `map_info.pData` so we are free to
// unmap our texture.
uint8_t* src_data = static_cast<uint8_t*>(map_info.pData);
std::vector<uint8_t> image_data;
image_data.resize(image_height * row_data_length);
uint8_t* image_data_ptr = image_data.data();
for (int i = 0; i < image_height; i++) {
memcpy(image_data_ptr, src_data, row_data_length);
image_data_ptr += row_data_length;
src_data += map_info.RowPitch;
}
d3d_context->Unmap(mapped_texture_.Get(), 0);
// Transfer ownership of `image_data` to the output_frame.
DesktopSize size(image_width, image_height);
*output_frame = std::make_unique<WgcDesktopFrame>(size, row_data_length,
std::move(image_data));
previous_size_ = new_size;
RecordGetFrameResult(GetFrameResult::kSuccess);
return hr;
}