blob: ede1f0f44bf2b2718673299900c57db549efabc8 [file] [log] [blame]
// Copyright 2019 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 "gpu/command_buffer/service/shared_image_backing_factory_d3d.h"
#include <d3d11_1.h>
#include "components/viz/common/resources/resource_format_utils.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/shared_image_backing_d3d.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gl/direct_composition_surface_win.h"
#include "ui/gl/gl_angle_util_win.h"
#include "ui/gl/gl_bindings.h"
namespace gpu {
namespace {
bool ClearBackBuffer(Microsoft::WRL::ComPtr<IDXGISwapChain1>& swap_chain,
Microsoft::WRL::ComPtr<ID3D11Device>& d3d11_device) {
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
HRESULT hr = swap_chain->GetBuffer(0, IID_PPV_ARGS(&d3d11_texture));
if (FAILED(hr)) {
DLOG(ERROR) << "GetBuffer failed with error " << std::hex << hr;
return false;
}
DCHECK(d3d11_texture);
Microsoft::WRL::ComPtr<ID3D11RenderTargetView> render_target;
hr = d3d11_device->CreateRenderTargetView(d3d11_texture.Get(), nullptr,
&render_target);
if (FAILED(hr)) {
DLOG(ERROR) << "CreateRenderTargetView failed with error " << std::hex
<< hr;
return false;
}
DCHECK(render_target);
Microsoft::WRL::ComPtr<ID3D11DeviceContext> d3d11_device_context;
d3d11_device->GetImmediateContext(&d3d11_device_context);
DCHECK(d3d11_device_context);
float color_rgba[4] = {0.0f, 0.0f, 0.0f, 1.0f};
d3d11_device_context->ClearRenderTargetView(render_target.Get(), color_rgba);
return true;
}
// Only RGBA formats supported by CreateSharedImage.
absl::optional<DXGI_FORMAT> GetSupportedRGBAFormat(
viz::ResourceFormat viz_resource_format) {
switch (viz_resource_format) {
case viz::RGBA_F16:
return DXGI_FORMAT_R16G16B16A16_FLOAT;
case viz::BGRA_8888:
return DXGI_FORMAT_B8G8R8A8_UNORM;
case viz::RGBA_8888:
return DXGI_FORMAT_R8G8B8A8_UNORM;
default:
NOTREACHED();
return {};
}
}
// Formats supported by CreateSharedImage(GMB) and CreateSharedImageVideoPlanes.
DXGI_FORMAT GetDXGIFormat(gfx::BufferFormat buffer_format) {
switch (buffer_format) {
case gfx::BufferFormat::RGBA_8888:
return DXGI_FORMAT_R8G8B8A8_UNORM;
case gfx::BufferFormat::BGRA_8888:
return DXGI_FORMAT_B8G8R8A8_UNORM;
case gfx::BufferFormat::RGBA_F16:
return DXGI_FORMAT_R16G16B16A16_FLOAT;
case gfx::BufferFormat::YUV_420_BIPLANAR:
return DXGI_FORMAT_NV12;
default:
NOTREACHED();
return DXGI_FORMAT_UNKNOWN;
}
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> ValidateAndOpenSharedHandle(
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
const gfx::GpuMemoryBufferHandle& handle,
gfx::BufferFormat format,
const gfx::Size& size) {
if (handle.type != gfx::DXGI_SHARED_HANDLE || !handle.dxgi_handle.IsValid()) {
DLOG(ERROR) << "Invalid handle with type: " << handle.type;
return nullptr;
}
if (!gpu::IsImageSizeValidForGpuMemoryBufferFormat(size, format)) {
DLOG(ERROR) << "Invalid image size " << size.ToString() << " for "
<< gfx::BufferFormatToString(format);
return nullptr;
}
Microsoft::WRL::ComPtr<ID3D11Device1> d3d11_device1;
HRESULT hr = d3d11_device.As(&d3d11_device1);
if (FAILED(hr)) {
DLOG(ERROR) << "Failed to query for ID3D11Device1. Error: "
<< logging::SystemErrorCodeToString(hr);
return nullptr;
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
hr = d3d11_device1->OpenSharedResource1(handle.dxgi_handle.Get(),
IID_PPV_ARGS(&d3d11_texture));
if (FAILED(hr)) {
DLOG(ERROR) << "Unable to open shared resource from DXGI handle. Error: "
<< logging::SystemErrorCodeToString(hr);
return nullptr;
}
D3D11_TEXTURE2D_DESC desc;
d3d11_texture->GetDesc(&desc);
// TODO: Add checks for device specific limits.
if (desc.Width != static_cast<UINT>(size.width()) ||
desc.Height != static_cast<UINT>(size.height())) {
DLOG(ERROR) << "Size must match texture being opened";
return nullptr;
}
if (desc.Format != GetDXGIFormat(format)) {
DLOG(ERROR) << "Format must match texture being opened";
return nullptr;
}
return d3d11_texture;
}
} // anonymous namespace
SharedImageBackingFactoryD3D::SharedImageBackingFactoryD3D()
: d3d11_device_(gl::QueryD3D11DeviceObjectFromANGLE()) {}
SharedImageBackingFactoryD3D::~SharedImageBackingFactoryD3D() = default;
SharedImageBackingFactoryD3D::SwapChainBackings::SwapChainBackings(
std::unique_ptr<SharedImageBacking> front_buffer,
std::unique_ptr<SharedImageBacking> back_buffer)
: front_buffer(std::move(front_buffer)),
back_buffer(std::move(back_buffer)) {}
SharedImageBackingFactoryD3D::SwapChainBackings::~SwapChainBackings() = default;
SharedImageBackingFactoryD3D::SwapChainBackings::SwapChainBackings(
SharedImageBackingFactoryD3D::SwapChainBackings&&) = default;
SharedImageBackingFactoryD3D::SwapChainBackings&
SharedImageBackingFactoryD3D::SwapChainBackings::operator=(
SharedImageBackingFactoryD3D::SwapChainBackings&&) = default;
// static
bool SharedImageBackingFactoryD3D::IsSwapChainSupported() {
return gl::DirectCompositionSurfaceWin::IsDirectCompositionSupported() &&
gl::DirectCompositionSurfaceWin::IsSwapChainTearingSupported();
}
SharedImageBackingFactoryD3D::SwapChainBackings
SharedImageBackingFactoryD3D::CreateSwapChain(
const Mailbox& front_buffer_mailbox,
const Mailbox& back_buffer_mailbox,
viz::ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage) {
if (!SharedImageBackingFactoryD3D::IsSwapChainSupported())
return {nullptr, nullptr};
DXGI_FORMAT swap_chain_format;
switch (format) {
case viz::RGBA_8888:
case viz::RGBX_8888:
case viz::BGRA_8888:
swap_chain_format = DXGI_FORMAT_B8G8R8A8_UNORM;
break;
case viz::RGBA_F16:
swap_chain_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
break;
default:
DLOG(ERROR) << gfx::BufferFormatToString(viz::BufferFormat(format))
<< " format is not supported by swap chain.";
return {nullptr, nullptr};
}
Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device;
d3d11_device_.As(&dxgi_device);
DCHECK(dxgi_device);
Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
dxgi_device->GetAdapter(&dxgi_adapter);
DCHECK(dxgi_adapter);
Microsoft::WRL::ComPtr<IDXGIFactory2> dxgi_factory;
dxgi_adapter->GetParent(IID_PPV_ARGS(&dxgi_factory));
DCHECK(dxgi_factory);
DXGI_SWAP_CHAIN_DESC1 desc = {};
desc.Width = size.width();
desc.Height = size.height();
desc.Format = swap_chain_format;
desc.Stereo = FALSE;
desc.SampleDesc.Count = 1;
desc.BufferCount = 2;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
desc.Scaling = DXGI_SCALING_STRETCH;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
desc.AlphaMode = viz::HasAlpha(format) ? DXGI_ALPHA_MODE_PREMULTIPLIED
: DXGI_ALPHA_MODE_IGNORE;
Microsoft::WRL::ComPtr<IDXGISwapChain1> swap_chain;
HRESULT hr = dxgi_factory->CreateSwapChainForComposition(
d3d11_device_.Get(), &desc, nullptr, &swap_chain);
if (FAILED(hr)) {
DLOG(ERROR) << "CreateSwapChainForComposition failed with error "
<< std::hex << hr;
return {nullptr, nullptr};
}
// Explicitly clear front and back buffers to ensure that there are no
// uninitialized pixels.
if (!ClearBackBuffer(swap_chain, d3d11_device_))
return {nullptr, nullptr};
DXGI_PRESENT_PARAMETERS params = {};
params.DirtyRectsCount = 0;
params.pDirtyRects = nullptr;
hr = swap_chain->Present1(0 /* interval */, 0 /* flags */, &params);
if (FAILED(hr)) {
DLOG(ERROR) << "Present1 failed with error " << std::hex << hr;
return {nullptr, nullptr};
}
if (!ClearBackBuffer(swap_chain, d3d11_device_))
return {nullptr, nullptr};
Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer_texture;
hr = swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer_texture));
if (FAILED(hr)) {
DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
return {nullptr, nullptr};
}
auto back_buffer_backing = SharedImageBackingD3D::CreateFromSwapChainBuffer(
back_buffer_mailbox, format, size, color_space, surface_origin,
alpha_type, usage, std::move(back_buffer_texture), swap_chain,
/*buffer_index=*/0);
if (!back_buffer_backing)
return {nullptr, nullptr};
back_buffer_backing->SetCleared();
Microsoft::WRL::ComPtr<ID3D11Texture2D> front_buffer_texture;
hr = swap_chain->GetBuffer(1, IID_PPV_ARGS(&front_buffer_texture));
if (FAILED(hr)) {
DLOG(ERROR) << "GetBuffer failed with error " << std::hex;
return {nullptr, nullptr};
}
auto front_buffer_backing = SharedImageBackingD3D::CreateFromSwapChainBuffer(
front_buffer_mailbox, format, size, color_space, surface_origin,
alpha_type, usage, std::move(front_buffer_texture), swap_chain,
/*buffer_index=*/1);
if (!front_buffer_backing)
return {nullptr, nullptr};
front_buffer_backing->SetCleared();
return {std::move(front_buffer_backing), std::move(back_buffer_backing)};
}
std::unique_ptr<SharedImageBacking>
SharedImageBackingFactoryD3D::CreateSharedImage(
const Mailbox& mailbox,
viz::ResourceFormat format,
SurfaceHandle surface_handle,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
bool is_thread_safe) {
DCHECK(!is_thread_safe);
// Without D3D11, we cannot do shared images. This will happen if we're
// running with Vulkan, D3D9, GL or with the non-passthrough command decoder
// in tests.
if (!d3d11_device_) {
return nullptr;
}
const absl::optional<DXGI_FORMAT> dxgi_format =
GetSupportedRGBAFormat(format);
if (!dxgi_format.has_value()) {
DLOG(ERROR) << "Unsupported viz format found: " << format;
return nullptr;
}
D3D11_TEXTURE2D_DESC desc;
desc.Width = size.width();
desc.Height = size.height();
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = dxgi_format.value();
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
desc.CPUAccessFlags = 0;
desc.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) << "CreateTexture2D failed with error " << std::hex << hr;
return nullptr;
}
Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
hr = d3d11_texture.As(&dxgi_resource);
if (FAILED(hr)) {
DLOG(ERROR) << "QueryInterface for IDXGIResource failed with error "
<< std::hex << hr;
return nullptr;
}
HANDLE shared_handle;
hr = dxgi_resource->CreateSharedHandle(
nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
&shared_handle);
if (FAILED(hr)) {
DLOG(ERROR) << "Unable to create shared handle for DXGIResource "
<< std::hex << hr;
return nullptr;
}
// Put the shared handle into an RAII object as quickly as possible to
// ensure we do not leak it.
base::win::ScopedHandle scoped_shared_handle(shared_handle);
return SharedImageBackingD3D::CreateFromSharedHandle(
mailbox, format, size, color_space, surface_origin, alpha_type, usage,
std::move(d3d11_texture), std::move(scoped_shared_handle));
}
std::unique_ptr<SharedImageBacking>
SharedImageBackingFactoryD3D::CreateSharedImage(
const Mailbox& mailbox,
viz::ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
base::span<const uint8_t> pixel_data) {
NOTIMPLEMENTED();
return nullptr;
}
std::unique_ptr<SharedImageBacking>
SharedImageBackingFactoryD3D::CreateSharedImage(
const Mailbox& mailbox,
int client_id,
gfx::GpuMemoryBufferHandle handle,
gfx::BufferFormat format,
gfx::BufferPlane plane,
SurfaceHandle surface_handle,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage) {
// TODO: Add support for shared memory GMBs.
if (!GetSupportedRGBAFormat(viz::GetResourceFormat(format))) {
DLOG(ERROR) << "Unsupported format " << gfx::BufferFormatToString(format);
return nullptr;
}
if (plane != gfx::BufferPlane::DEFAULT) {
DLOG(ERROR) << "Invalid plane " << gfx::BufferPlaneToString(plane);
return nullptr;
}
auto d3d11_texture =
ValidateAndOpenSharedHandle(d3d11_device_, handle, format, size);
if (!d3d11_texture)
return nullptr;
auto backing = SharedImageBackingD3D::CreateFromSharedHandle(
mailbox, viz::GetResourceFormat(format), size, color_space,
surface_origin, alpha_type, usage, std::move(d3d11_texture),
std::move(handle.dxgi_handle));
if (backing)
backing->SetCleared();
return backing;
}
std::vector<std::unique_ptr<SharedImageBacking>>
SharedImageBackingFactoryD3D::CreateSharedImageVideoPlanes(
base::span<const Mailbox> mailboxes,
gfx::GpuMemoryBufferHandle handle,
gfx::BufferFormat format,
const gfx::Size& size,
uint32_t usage) {
// Only supports NV12 for now.
if (format != gfx::BufferFormat::YUV_420_BIPLANAR) {
DLOG(ERROR) << "Unsupported format: " << gfx::BufferFormatToString(format);
return {};
}
auto d3d11_texture =
ValidateAndOpenSharedHandle(d3d11_device_, handle, format, size);
if (!d3d11_texture)
return {};
return SharedImageBackingD3D::CreateFromVideoTexture(
mailboxes, GetDXGIFormat(format), size, usage, std::move(d3d11_texture),
/*array_slice=*/0, std::move(handle.dxgi_handle));
}
// Returns true if the specified GpuMemoryBufferType can be imported using
// this factory.
bool SharedImageBackingFactoryD3D::CanImportGpuMemoryBuffer(
gfx::GpuMemoryBufferType memory_buffer_type) {
return (memory_buffer_type == gfx::DXGI_SHARED_HANDLE);
}
bool SharedImageBackingFactoryD3D::IsSupported(
uint32_t usage,
viz::ResourceFormat format,
bool thread_safe,
gfx::GpuMemoryBufferType gmb_type,
GrContextType gr_context_type,
bool* allow_legacy_mailbox,
bool is_pixel_used) {
if (is_pixel_used) {
return false;
}
if (gmb_type != gfx::EMPTY_BUFFER && !CanImportGpuMemoryBuffer(gmb_type)) {
return false;
}
// TODO(crbug.com/969114): Not all shared image factory implementations
// support concurrent read/write usage.
if (usage & SHARED_IMAGE_USAGE_CONCURRENT_READ_WRITE) {
return false;
}
*allow_legacy_mailbox = false;
return true;
}
} // namespace gpu