Skip to content

Commit

Permalink
Switch to non D3D11 encoding if DXGI manager is not successfully conf…
Browse files Browse the repository at this point in the history
…igured.

This CL prepares for future Intel driver fix that rejects D3D manager
setting on HMFT when selected encode adapter is not the same as the
video capture adapter. In this situation we create IMFSample in CPU
memory instead by copying from the input DXGI handle from capture
module.
Be noted we still block encoding on multiple-adapter system for AV1 at present until follow up CL unblocks this with correct driver information
that has the adapter check logic.

Bug: 1367038

Change-Id: Ie15788280495c86f0d04c9c0807cfc874c11006b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4003333
Reviewed-by: Daniel Cheng <dcheng@chromium.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@chromium.org>
Commit-Queue: Daniel Cheng <dcheng@chromium.org>
Auto-Submit: Jianlin Qiu <jianlin.qiu@intel.com>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1069008}
  • Loading branch information
taste1981 authored and Chromium LUCI CQ committed Nov 9, 2022
1 parent 8cd3d2b commit 5067760
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 31 deletions.
81 changes: 53 additions & 28 deletions gpu/ipc/common/dxgi_helpers.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ bool CopyDXGIBufferToShMem(
ID3D11Device* d3d11_device,
Microsoft::WRL::ComPtr<ID3D11Texture2D>* staging_texture) {
DCHECK(d3d11_device);
DCHECK(staging_texture);

uint8_t* dest_buffer = shared_memory.data();
size_t dst_buffer_size = shared_memory.size_bytes();

Microsoft::WRL::ComPtr<ID3D11Device1> device1;
HRESULT hr = d3d11_device->QueryInterface(IID_PPV_ARGS(&device1));
Expand All @@ -90,14 +92,34 @@ bool CopyDXGIBufferToShMem(
return false;
}

return CopyD3D11TexToMem(texture.Get(), dest_buffer, dst_buffer_size,
d3d11_device, staging_texture) > 0;
}

bool CopyD3D11TexToMem(
ID3D11Texture2D* src_texture,
uint8_t* dst_buffer,
size_t buffer_size,
ID3D11Device* d3d11_device,
Microsoft::WRL::ComPtr<ID3D11Texture2D>* staging_texture) {
DCHECK(d3d11_device);
DCHECK(staging_texture);
DCHECK(dst_buffer);
DCHECK(src_texture);

D3D11_TEXTURE2D_DESC texture_desc = {};
texture->GetDesc(&texture_desc);
src_texture->GetDesc(&texture_desc);

if (texture_desc.Format != DXGI_FORMAT_NV12) {
DLOG(ERROR) << "Can't copy non-NV12 texture. format="
<< static_cast<int>(texture_desc.Format);
return false;
}
size_t copy_size = texture_desc.Height * texture_desc.Width * 3 / 2;
if (buffer_size < copy_size) {
DLOG(ERROR) << "Invalid buffer size for copy.";
return false;
}

// The texture isn't accessible for CPU reads, thus a staging texture is used.
bool create_staging_texture = !*staging_texture;
Expand All @@ -117,51 +139,54 @@ bool CopyDXGIBufferToShMem(

Microsoft::WRL::ComPtr<ID3D11DeviceContext> device_context;
d3d11_device->GetImmediateContext(&device_context);
HRESULT hr = S_OK;

Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex;
hr = texture.As(&keyed_mutex);
if (texture_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX) {
Microsoft::WRL::ComPtr<IDXGIKeyedMutex> keyed_mutex;

if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get keyed mutex. hr=" << std::hex << hr;
return false;
}
hr = src_texture->QueryInterface(IID_PPV_ARGS(&keyed_mutex));

// Key equal to 0 is also used by the producer. Therefore, this keyed mutex
// acts purely as a regular mutex.
hr = keyed_mutex->AcquireSync(0, INFINITE);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to acquire keyed mutex. hr=" << std::hex << hr;
return false;
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to get keyed mutex. Error msg: "
<< logging::SystemErrorCodeToString(hr);
return false;
}

// Key equal to 0 is also used by the producer. Therefore, this keyed
// mutex acts purely as a regular mutex.
hr = keyed_mutex->AcquireSync(0, INFINITE);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to acquire keyed mutex. Error msg: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
DXGIScopedReleaseKeyedMutex release_keyed_mutex(keyed_mutex, 0);

device_context->CopySubresourceRegion(staging_texture->Get(), 0, 0, 0, 0,
src_texture, 0, nullptr);
} else {
device_context->CopySubresourceRegion(staging_texture->Get(), 0, 0, 0, 0,
src_texture, 0, nullptr);
}
DXGIScopedReleaseKeyedMutex release_keyed_mutex(keyed_mutex, 0);

device_context->CopySubresourceRegion(staging_texture->Get(), 0, 0, 0, 0,
texture.Get(), 0, nullptr);

D3D11_MAPPED_SUBRESOURCE mapped_resource = {};
hr = device_context->Map(staging_texture->Get(), 0, D3D11_MAP_READ, 0,
&mapped_resource);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to map texture for read. hr=" << std::hex << hr;
DLOG(ERROR) << "Failed to map texture for read. Error msg: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
D3D11ScopedTextureUnmap scoped_unmap(device_context, *staging_texture);

// Copy mapped texture to shared memory region for client
size_t buffer_size = texture_desc.Height * texture_desc.Width * 3 / 2;
if (shared_memory.size() < buffer_size)
return false;

const uint8_t* source_buffer = static_cast<uint8_t*>(mapped_resource.pData);
uint8_t* dest_buffer = shared_memory.data();

const uint32_t source_stride = mapped_resource.RowPitch;
const uint32_t dest_stride = texture_desc.Width;

return libyuv::NV12Copy(source_buffer, source_stride,
source_buffer + texture_desc.Height * source_stride,
source_stride, dest_buffer, dest_stride,
dest_buffer + texture_desc.Height * dest_stride,
source_stride, dst_buffer, dest_stride,
dst_buffer + texture_desc.Height * dest_stride,
dest_stride, texture_desc.Width,
texture_desc.Height) == 0;
}
Expand Down
10 changes: 10 additions & 0 deletions gpu/ipc/common/dxgi_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ GPU_EXPORT bool CopyDXGIBufferToShMem(
ID3D11Device* d3d11_device,
Microsoft::WRL::ComPtr<ID3D11Texture2D>* staging_texture);

// Copies from |input_texture| to |dst_buffer| using provided D3D11 device, and
// a staging texture. The staging texture may be recreated if it does not match
// input texture size or format. Returns true if succeeded.
GPU_EXPORT bool CopyD3D11TexToMem(
ID3D11Texture2D* input_texture,
uint8_t* dst_buffer,
size_t buffer_size,
ID3D11Device* d3d11_device,
Microsoft::WRL::ComPtr<ID3D11Texture2D>* staging_texture);

} // namespace gpu

#endif // GPU_IPC_COMMON_DXGI_HELPERS_H_
94 changes: 91 additions & 3 deletions media/gpu/windows/media_foundation_video_encode_accelerator_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -581,8 +581,12 @@ void MediaFoundationVideoEncodeAccelerator::EncoderInitializeTask(
hr = encoder_->ProcessMessage(
MFT_MESSAGE_SET_D3D_MANAGER,
reinterpret_cast<ULONG_PTR>(mf_dxgi_device_manager.Get()));
NOTIFY_RETURN_ON_HR_FAILURE(
hr, "Couldn't set ProcessMessage MFT_MESSAGE_SET_D3D_MANAGER", );
// If HMFT rejects setting D3D manager, fallback to non-D3D11 encoding.
if (FAILED(hr)) {
dxgi_resource_mapping_required_ = true;
MEDIA_LOG(INFO, media_log.get())
<< "Couldn't set DXGIDeviceManager, fallback to non-D3D11 encoding";
}
}

// Start the asynchronous processing model
Expand Down Expand Up @@ -1113,7 +1117,11 @@ HRESULT MediaFoundationVideoEncodeAccelerator::PopulateInputSampleBuffer(

if (gmb->GetType() == gfx::GpuMemoryBufferType::DXGI_SHARED_HANDLE &&
dxgi_device_manager_ != nullptr) {
return PopulateInputSampleBufferGpu(std::move(frame));
if (!dxgi_resource_mapping_required_) {
return PopulateInputSampleBufferGpu(std::move(frame));
} else {
return CopyInputSampleBufferFromGpu(*(frame.get()));
}
}

// ConvertToMemoryMappedFrame() doesn't copy pixel data,
Expand Down Expand Up @@ -1179,6 +1187,86 @@ HRESULT MediaFoundationVideoEncodeAccelerator::PopulateInputSampleBuffer(
return S_OK;
}

// Handle case where video frame is backed by a GPU texture, but needs to be
// copied to CPU memory, if HMFT does not accept texture from adapter different
// from that is currently used for encoding.
HRESULT MediaFoundationVideoEncodeAccelerator::CopyInputSampleBufferFromGpu(
const VideoFrame& frame) {
DCHECK_EQ(frame.storage_type(),
VideoFrame::StorageType::STORAGE_GPU_MEMORY_BUFFER);
DCHECK(frame.HasGpuMemoryBuffer());
DCHECK_EQ(frame.GetGpuMemoryBuffer()->GetType(),
gfx::GpuMemoryBufferType::DXGI_SHARED_HANDLE);
DCHECK(dxgi_device_manager_);

gfx::GpuMemoryBufferHandle buffer_handle =
frame.GetGpuMemoryBuffer()->CloneHandle();

auto d3d_device = dxgi_device_manager_->GetDevice();
if (!d3d_device) {
DLOG(ERROR) << "Failed to get device from MF DXGI device manager";
return E_HANDLE;
}
Microsoft::WRL::ComPtr<ID3D11Device1> device1;
HRESULT hr = d3d_device.As(&device1);

RETURN_ON_HR_FAILURE(hr, "Failed to query ID3D11Device1", hr);
Microsoft::WRL::ComPtr<ID3D11Texture2D> input_texture;
hr = device1->OpenSharedResource1(buffer_handle.dxgi_handle.Get(),
IID_PPV_ARGS(&input_texture));
RETURN_ON_HR_FAILURE(hr, "Failed to open shared GMB D3D texture", hr);

// Check if we need to scale the input texture
D3D11_TEXTURE2D_DESC input_desc = {};
input_texture->GetDesc(&input_desc);

Microsoft::WRL::ComPtr<ID3D11Texture2D> sample_texture;
if (input_desc.Width != static_cast<uint32_t>(input_visible_size_.width()) ||
input_desc.Height !=
static_cast<uint32_t>(input_visible_size_.height())) {
hr = PerformD3DScaling(input_texture.Get());
RETURN_ON_HR_FAILURE(hr, "Failed to perform D3D video processing", hr);
sample_texture = scaled_d3d11_texture_;
} else {
sample_texture = input_texture;
}

const auto kTargetPixelFormat = PIXEL_FORMAT_NV12;
Microsoft::WRL::ComPtr<IMFMediaBuffer> input_buffer;

// Allocate a new buffer.
MFT_INPUT_STREAM_INFO input_stream_info;
hr = encoder_->GetInputStreamInfo(input_stream_id_, &input_stream_info);
RETURN_ON_HR_FAILURE(hr, "Couldn't get input stream info", hr);
hr = MFCreateAlignedMemoryBuffer(
input_stream_info.cbSize
? input_stream_info.cbSize
: VideoFrame::AllocationSize(kTargetPixelFormat, input_visible_size_),
input_stream_info.cbAlignment == 0 ? input_stream_info.cbAlignment
: input_stream_info.cbAlignment - 1,
&input_buffer);
RETURN_ON_HR_FAILURE(hr, "Failed to create memory buffer for input sample",
hr);

MediaBufferScopedPointer scoped_buffer(input_buffer.Get());
bool copy_succeeded = gpu::CopyD3D11TexToMem(
sample_texture.Get(), scoped_buffer.get(), scoped_buffer.max_length(),
d3d_device.Get(), &staging_texture_);
if (!copy_succeeded) {
DLOG(ERROR) << "Failed to copy sample to memory.";
return E_FAIL;
}
size_t copied_bytes =
input_visible_size_.width() * input_visible_size_.height() * 3 / 2;
hr = input_buffer->SetCurrentLength(copied_bytes);
RETURN_ON_HR_FAILURE(hr, "Failed to set current buffer length", hr);
hr = input_sample_->RemoveAllBuffers();
RETURN_ON_HR_FAILURE(hr, "Failed to remove buffers from sample", hr);
hr = input_sample_->AddBuffer(input_buffer.Get());
RETURN_ON_HR_FAILURE(hr, "Failed to add buffer to sample", hr);
return S_OK;
}

// Handle case where video frame is backed by a GPU texture
HRESULT MediaFoundationVideoEncodeAccelerator::PopulateInputSampleBufferGpu(
scoped_refptr<VideoFrame> frame) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator
// Populates input sample buffer with contents of a video frame
HRESULT PopulateInputSampleBuffer(scoped_refptr<VideoFrame> frame);
HRESULT PopulateInputSampleBufferGpu(scoped_refptr<VideoFrame> frame);
HRESULT CopyInputSampleBufferFromGpu(const VideoFrame& frame);

// Assign TemporalID by bitstream or external state machine(based on SVC
// Spec).
Expand Down Expand Up @@ -234,6 +235,11 @@ class MEDIA_GPU_EXPORT MediaFoundationVideoEncodeAccelerator

// DXGI device manager for handling hardware input textures
scoped_refptr<DXGIDeviceManager> dxgi_device_manager_;
// Mapping of dxgi resource needed when HMFT rejects setting D3D11 manager.
bool dxgi_resource_mapping_required_ = false;
// Staging texture for copying from GPU memory if HMFT does not operate in
// D3D11 mode.
Microsoft::WRL::ComPtr<ID3D11Texture2D> staging_texture_;

// Preferred adapter for DXGIDeviceManager.
const CHROME_LUID luid_;
Expand Down

0 comments on commit 5067760

Please sign in to comment.