blob: fd76f9ea4861941096f212c15471669176839111 [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/capture/video/win/gpu_memory_buffer_tracker.h"
#include "base/check.h"
#include "base/notreached.h"
#include "base/win/scoped_handle.h"
#include "gpu/ipc/common/dxgi_helpers.h"
#include "media/base/win/mf_helpers.h"
#include "media/capture/video/video_capture_buffer_handle.h"
#include "ui/gfx/geometry/size.h"
#include <dxgi1_2.h>
namespace media {
namespace {
base::win::ScopedHandle CreateNV12Texture(ID3D11Device* d3d11_device,
const gfx::Size& size) {
const DXGI_FORMAT dxgi_format = DXGI_FORMAT_NV12;
D3D11_TEXTURE2D_DESC desc = {
.Width = static_cast<UINT>(size.width()),
.Height = static_cast<UINT>(size.height()),
.MipLevels = 1,
.ArraySize = 1,
.Format = dxgi_format,
.SampleDesc = {1, 0},
.Usage = D3D11_USAGE_DEFAULT,
.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET,
.CPUAccessFlags = 0,
.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE |
D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX};
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
HRESULT hr = d3d11_device->CreateTexture2D(&desc, nullptr, &d3d11_texture);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to create D3D11 texture: "
<< logging::SystemErrorCodeToString(hr);
return base::win::ScopedHandle();
}
hr = SetDebugName(d3d11_texture.Get(), "Camera_MemoryBufferTracker");
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to label D3D11 texture: "
<< logging::SystemErrorCodeToString(hr);
return base::win::ScopedHandle();
}
Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
hr = d3d11_texture.As(&dxgi_resource);
CHECK(SUCCEEDED(hr));
HANDLE texture_handle;
hr = dxgi_resource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
&texture_handle);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to create shared D3D11 texture handle: "
<< logging::SystemErrorCodeToString(hr);
return base::win::ScopedHandle();
}
return base::win::ScopedHandle(texture_handle);
}
} // namespace
GpuMemoryBufferTracker::GpuMemoryBufferTracker(
scoped_refptr<DXGIDeviceManager> dxgi_device_manager)
: dxgi_device_manager_(std::move(dxgi_device_manager)),
d3d_device_(dxgi_device_manager_->GetDevice()) {}
GpuMemoryBufferTracker::~GpuMemoryBufferTracker() = default;
bool GpuMemoryBufferTracker::Init(const gfx::Size& dimensions,
VideoPixelFormat format,
const mojom::PlaneStridesPtr& strides) {
// Only support NV12
if (format != PIXEL_FORMAT_NV12) {
NOTREACHED() << "Unsupported VideoPixelFormat " << format;
return false;
}
buffer_size_ = dimensions;
return CreateBufferInternal();
}
bool GpuMemoryBufferTracker::CreateBufferInternal() {
gfx::GpuMemoryBufferHandle buffer_handle;
buffer_handle.dxgi_handle =
CreateNV12Texture(d3d_device_.Get(), buffer_size_);
buffer_ = gpu::GpuMemoryBufferImplDXGI::CreateFromHandle(
std::move(buffer_handle), buffer_size_,
gfx::BufferFormat::YUV_420_BIPLANAR, gfx::BufferUsage::GPU_READ,
gpu::GpuMemoryBufferImpl::DestructionCallback(), nullptr, nullptr);
if (!buffer_) {
NOTREACHED() << "Failed to create GPU memory buffer";
return false;
}
region_ = base::UnsafeSharedMemoryRegion::Create(GetMemorySizeInBytes());
return true;
}
bool GpuMemoryBufferTracker::EnsureD3DDevice() {
// Check for and handle device loss by recreating the texture
if (FAILED(d3d_device_->GetDeviceRemovedReason())) {
DVLOG(1) << "Detected device loss.";
dxgi_device_manager_->ResetDevice();
d3d_device_ = dxgi_device_manager_->GetDevice();
if (!d3d_device_) {
return false;
}
return CreateBufferInternal();
}
return true;
}
bool GpuMemoryBufferTracker::IsReusableForFormat(
const gfx::Size& dimensions,
VideoPixelFormat format,
const mojom::PlaneStridesPtr& strides) {
return (format == PIXEL_FORMAT_NV12) && (dimensions == buffer_->GetSize());
}
std::unique_ptr<VideoCaptureBufferHandle>
GpuMemoryBufferTracker::GetMemoryMappedAccess() {
NOTREACHED() << "Unsupported operation";
return std::make_unique<NullHandle>();
}
base::UnsafeSharedMemoryRegion
GpuMemoryBufferTracker::DuplicateAsUnsafeRegion() {
if (!buffer_) {
return base::UnsafeSharedMemoryRegion();
}
CHECK(region_.IsValid());
if (!gpu::CopyDXGIBufferToShMem(buffer_->GetHandle(), region_.Duplicate(),
d3d_device_.Get(), &staging_texture_)) {
DLOG(ERROR) << "Couldn't copy DXGI buffer to shmem";
return base::UnsafeSharedMemoryRegion();
}
return region_.Duplicate();
}
mojo::ScopedSharedBufferHandle GpuMemoryBufferTracker::DuplicateAsMojoBuffer() {
NOTREACHED() << "Unsupported operation";
return mojo::ScopedSharedBufferHandle();
}
gfx::GpuMemoryBufferHandle GpuMemoryBufferTracker::GetGpuMemoryBufferHandle() {
if (!EnsureD3DDevice()) {
return gfx::GpuMemoryBufferHandle();
}
auto handle = buffer_->CloneHandle();
handle.region = region_.Duplicate();
return handle;
}
uint32_t GpuMemoryBufferTracker::GetMemorySizeInBytes() {
DCHECK(buffer_);
return (buffer_->GetSize().width() * buffer_->GetSize().height() * 3) / 2;
}
} // namespace media