// Copyright 2019 The Chromium Authors
// 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/d3d_image_backing_factory.h"
#include <d3d11_1.h>
#include "base/memory/shared_memory_mapping.h"
#include "base/win/scoped_handle.h"
#include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/dxgi_shared_handle_manager.h"
#include "gpu/command_buffer/service/shared_image/d3d_image_backing.h"
#include "gpu/command_buffer/service/shared_image/d3d_image_utils.h"
#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
#include "gpu/config/gpu_finch_features.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/color_space_win.h"
#include "ui/gl/direct_composition_support.h"
#include "ui/gl/gl_angle_util_win.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_utils.h"
namespace gpu {
namespace {
// Formats supported by CreateSharedImage() for uploading initial data.
bool IsFormatSupportedForInitialData(viz::SharedImageFormat format) {
// The set of formats is artificially limited to avoid needing to handle
// formats outside of what is required. If more are needed, we may need to
// adjust our initial data's packing or the |D3D11_SUBRESOURCE_DATA|'s pitch.
return format == viz::SinglePlaneFormat::kRGBA_8888 ||
format == viz::SinglePlaneFormat::kBGRA_8888;
}
bool IsFormatSupportedForDCompTexture(DXGI_FORMAT dxgi_format) {
switch (dxgi_format) {
case DXGI_FORMAT_B8G8R8A8_UNORM:
case DXGI_FORMAT_R8G8B8A8_UNORM:
case DXGI_FORMAT_R16G16B16A16_FLOAT:
case DXGI_FORMAT_R10G10B10A2_UNORM:
case DXGI_FORMAT_NV12:
case DXGI_FORMAT_YUY2:
case DXGI_FORMAT_420_OPAQUE:
case DXGI_FORMAT_P010:
return true;
default:
return false;
}
}
bool IsColorSpaceSupportedForDCompTexture(
DXGI_COLOR_SPACE_TYPE dxgi_color_space) {
switch (dxgi_color_space) {
case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709:
case DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709:
case DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709:
case DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020:
case DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601:
case DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601:
case DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601:
case DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709:
case DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709:
case DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020:
case DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020:
case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020:
case DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020:
case DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020:
case DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020:
case DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020:
case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020:
return true;
default:
return false;
}
}
// Returns |true| if |desc| describes a texture that we can wrap in an
// |IDCompositionTexture|.
bool DCompTextureIsSupported(const D3D11_TEXTURE2D_DESC& desc) {
if (!gl::DirectCompositionTextureSupported()) {
return false;
}
return desc.MipLevels == 1 && desc.ArraySize == 1 &&
IsFormatSupportedForDCompTexture(desc.Format) &&
desc.SampleDesc.Count == 1 && desc.SampleDesc.Quality == 0 &&
desc.Usage == D3D11_USAGE_DEFAULT &&
(desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) != 0 &&
desc.CPUAccessFlags == 0 &&
desc.MiscFlags ==
(D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_NTHANDLE);
}
// Formats supported by CreateSharedImage() with no GpuMemoryBufferHandle.
DXGI_FORMAT GetDXGIFormatForCreateTexture(viz::SharedImageFormat format) {
if (format == viz::SinglePlaneFormat::kRGBA_F16) {
return DXGI_FORMAT_R16G16B16A16_FLOAT;
} else if (format == viz::SinglePlaneFormat::kBGRA_8888) {
return DXGI_FORMAT_B8G8R8A8_UNORM;
} else if (format == viz::SinglePlaneFormat::kRGBA_8888) {
return DXGI_FORMAT_R8G8B8A8_UNORM;
} else if (format == viz::SinglePlaneFormat::kBGRX_8888) {
return DXGI_FORMAT_B8G8R8A8_UNORM;
} else if (format == viz::SinglePlaneFormat::kRGBX_8888) {
return DXGI_FORMAT_R8G8B8A8_UNORM;
} else if (format == viz::SinglePlaneFormat::kR_8) {
return DXGI_FORMAT_R8_UNORM;
} else if (format == viz::SinglePlaneFormat::kRG_88) {
return DXGI_FORMAT_R8G8_UNORM;
} else if (format == viz::SinglePlaneFormat::kR_16) {
return DXGI_FORMAT_R16_UNORM;
} else if (format == viz::SinglePlaneFormat::kRG_1616) {
return DXGI_FORMAT_R16G16_UNORM;
} else if (format == viz::MultiPlaneFormat::kNV12) {
return DXGI_FORMAT_NV12;
}
return DXGI_FORMAT_UNKNOWN;
}
// Formats supported by CreateSharedImage(GMB).
DXGI_FORMAT GetDXGIFormatForGMB(viz::SharedImageFormat format) {
if (format == viz::SinglePlaneFormat::kRGBA_8888) {
return DXGI_FORMAT_R8G8B8A8_UNORM;
} else if (format == viz::SinglePlaneFormat::kBGRA_8888) {
return DXGI_FORMAT_B8G8R8A8_UNORM;
} else if (format == viz::SinglePlaneFormat::kRGBA_F16) {
return DXGI_FORMAT_R16G16B16A16_FLOAT;
} else if (format == viz::MultiPlaneFormat::kNV12) {
return DXGI_FORMAT_NV12;
}
return DXGI_FORMAT_UNKNOWN;
}
// Typeless formats supported by CreateSharedImage(GMB) for XR.
DXGI_FORMAT GetDXGITypelessFormat(viz::SharedImageFormat format) {
if (format == viz::SinglePlaneFormat::kRGBA_8888) {
return DXGI_FORMAT_R8G8B8A8_TYPELESS;
} else if (format == viz::SinglePlaneFormat::kBGRA_8888) {
return DXGI_FORMAT_B8G8R8A8_TYPELESS;
} else if (format == viz::SinglePlaneFormat::kRGBA_F16) {
return DXGI_FORMAT_R16G16B16A16_TYPELESS;
}
return DXGI_FORMAT_UNKNOWN;
}
bool UseUpdateSubresource1(const GpuDriverBugWorkarounds& workarounds) {
return base::FeatureList::IsEnabled(
features::kD3DBackingUploadWithUpdateSubresource) &&
!workarounds.disable_d3d11_update_subresource1;
}
constexpr SharedImageUsageSet kSupportedUsage =
SHARED_IMAGE_USAGE_GLES2_READ | SHARED_IMAGE_USAGE_GLES2_WRITE |
SHARED_IMAGE_USAGE_GLES2_FOR_RASTER_ONLY |
SHARED_IMAGE_USAGE_DISPLAY_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ |
SHARED_IMAGE_USAGE_RASTER_READ | SHARED_IMAGE_USAGE_RASTER_WRITE |
SHARED_IMAGE_USAGE_RASTER_OVER_GLES2_ONLY |
SHARED_IMAGE_USAGE_OOP_RASTERIZATION | SHARED_IMAGE_USAGE_SCANOUT |
SHARED_IMAGE_USAGE_WEBGPU_READ | SHARED_IMAGE_USAGE_WEBGPU_WRITE |
SHARED_IMAGE_USAGE_VIDEO_DECODE |
SHARED_IMAGE_USAGE_WEBGPU_SWAP_CHAIN_TEXTURE |
SHARED_IMAGE_USAGE_HIGH_PERFORMANCE_GPU | SHARED_IMAGE_USAGE_CPU_UPLOAD |
SHARED_IMAGE_USAGE_WEBGPU_STORAGE_TEXTURE |
SHARED_IMAGE_USAGE_RASTER_DELEGATED_COMPOSITING;
} // anonymous namespace
D3DImageBackingFactory::D3DImageBackingFactory(
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
scoped_refptr<DXGISharedHandleManager> dxgi_shared_handle_manager,
const GLFormatCaps& gl_format_caps,
const GpuDriverBugWorkarounds& workarounds)
: SharedImageBackingFactory(kSupportedUsage),
d3d11_device_(std::move(d3d11_device)),
dxgi_shared_handle_manager_(std::move(dxgi_shared_handle_manager)),
angle_d3d11_device_(gl::QueryD3D11DeviceObjectFromANGLE()),
gl_format_caps_(gl_format_caps),
use_update_subresource1_(UseUpdateSubresource1(workarounds)) {
CHECK(angle_d3d11_device_);
}
D3DImageBackingFactory::~D3DImageBackingFactory() = default;
D3DImageBackingFactory::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)) {}
D3DImageBackingFactory::SwapChainBackings::~SwapChainBackings() = default;
D3DImageBackingFactory::SwapChainBackings::SwapChainBackings(
D3DImageBackingFactory::SwapChainBackings&&) = default;
D3DImageBackingFactory::SwapChainBackings&
D3DImageBackingFactory::SwapChainBackings::operator=(
D3DImageBackingFactory::SwapChainBackings&&) = default;
// static
bool D3DImageBackingFactory::IsD3DSharedImageSupported(
const GpuPreferences& gpu_preferences) {
// Only supported for passthrough command decoder.
if (!gpu_preferences.use_passthrough_cmd_decoder ||
!gl::PassthroughCommandDecoderSupported()) {
return false;
}
// D3D11 device will be null if ANGLE is using the D3D9 backend.
if (!gl::QueryD3D11DeviceObjectFromANGLE()) {
return false;
}
// Only supported for Skia GL or Skia GraphiteDawn
if (gpu_preferences.gr_context_type != GrContextType::kGL &&
gpu_preferences.gr_context_type != GrContextType::kGraphiteDawn) {
return false;
}
return true;
}
// static
bool D3DImageBackingFactory::IsSwapChainSupported(
const GpuPreferences& gpu_preferences) {
// TODO(crbug.com/40074896): enable swapchain support when d3d11 is shared
// with ANGLE.
return gl::DirectCompositionSupported() &&
gl::DXGISwapChainTearingSupported() &&
gpu_preferences.gr_context_type == GrContextType::kGL;
}
// static
bool D3DImageBackingFactory::ClearBackBufferToColor(IDXGISwapChain1* swap_chain,
const SkColor4f& color) {
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
HRESULT hr = swap_chain->GetBuffer(0, IID_PPV_ARGS(&d3d11_texture));
if (FAILED(hr)) {
LOG(ERROR) << "GetBuffer failed: " << logging::SystemErrorCodeToString(hr);
return false;
}
DCHECK(d3d11_texture);
return ClearD3D11TextureToColor(d3d11_texture, color);
}
D3DImageBackingFactory::SwapChainBackings
D3DImageBackingFactory::CreateSwapChain(const Mailbox& front_buffer_mailbox,
const Mailbox& back_buffer_mailbox,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
gpu::SharedImageUsageSet usage) {
DXGI_FORMAT swap_chain_format;
if ((format == viz::SinglePlaneFormat::kRGBA_8888) ||
(format == viz::SinglePlaneFormat::kRGBX_8888) ||
(format == viz::SinglePlaneFormat::kBGRA_8888)) {
swap_chain_format = DXGI_FORMAT_B8G8R8A8_UNORM;
} else if (format == viz::SinglePlaneFormat::kRGBA_F16) {
swap_chain_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
} else {
LOG(ERROR) << format.ToString()
<< " 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 = 0;
if (gl::DXGISwapChainTearingSupported()) {
desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
}
if (gl::DXGIWaitableSwapChainEnabled()) {
desc.Flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
}
desc.AlphaMode = format.HasAlpha() ? 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)) {
LOG(ERROR) << "CreateSwapChainForComposition failed with error " << std::hex
<< hr;
return {nullptr, nullptr};
}
if (gl::DXGIWaitableSwapChainEnabled()) {
Microsoft::WRL::ComPtr<IDXGISwapChain3> swap_chain3;
if (SUCCEEDED(swap_chain.As(&swap_chain3))) {
hr = swap_chain3->SetMaximumFrameLatency(
gl::GetDXGIWaitableSwapChainMaxQueuedFrames());
DCHECK(SUCCEEDED(hr)) << "SetMaximumFrameLatency failed with error "
<< logging::SystemErrorCodeToString(hr);
}
}
// Explicitly clear front and back buffers to ensure that there are no
// uninitialized pixels.
if (!ClearBackBufferToColor(swap_chain.Get(), SkColors::kBlack)) {
return {nullptr, nullptr};
}
DXGI_PRESENT_PARAMETERS params = {};
params.DirtyRectsCount = 0;
params.pDirtyRects = nullptr;
hr = swap_chain->Present1(/*interval=*/0, /*flags=*/0, ¶ms);
if (FAILED(hr)) {
LOG(ERROR) << "Present1 failed with error " << std::hex << hr;
return {nullptr, nullptr};
}
if (!ClearBackBufferToColor(swap_chain.Get(), SkColors::kBlack)) {
return {nullptr, nullptr};
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> back_buffer_texture;
hr = swap_chain->GetBuffer(0, IID_PPV_ARGS(&back_buffer_texture));
if (FAILED(hr)) {
LOG(ERROR) << "GetBuffer failed with error " << std::hex;
return {nullptr, nullptr};
}
auto back_buffer_backing = D3DImageBacking::CreateFromSwapChainBuffer(
back_buffer_mailbox, format, size, color_space, surface_origin,
alpha_type, usage, std::move(back_buffer_texture), swap_chain,
gl_format_caps_, /*is_back_buffer=*/true);
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)) {
LOG(ERROR) << "GetBuffer failed with error " << std::hex;
return {nullptr, nullptr};
}
auto front_buffer_backing = D3DImageBacking::CreateFromSwapChainBuffer(
front_buffer_mailbox, format, size, color_space, surface_origin,
alpha_type, usage, std::move(front_buffer_texture), swap_chain,
gl_format_caps_, /*is_back_buffer=*/false);
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> D3DImageBackingFactory::CreateSharedImage(
const Mailbox& mailbox,
viz::SharedImageFormat format,
SurfaceHandle surface_handle,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
SharedImageUsageSet usage,
std::string debug_label,
bool is_thread_safe) {
return CreateSharedImage(mailbox, format, size, color_space, surface_origin,
alpha_type, usage, std::move(debug_label),
is_thread_safe, base::span<const uint8_t>());
}
std::unique_ptr<SharedImageBacking> D3DImageBackingFactory::CreateSharedImage(
const Mailbox& mailbox,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
SharedImageUsageSet usage,
std::string debug_label,
bool is_thread_safe,
base::span<const uint8_t> pixel_data) {
DCHECK(!is_thread_safe);
// Without D3D11, we cannot do shared images. This will happen if we're
// running with Vulkan, D3D12, D3D9, GL or with the non-passthrough command
// decoder in tests.
if (!d3d11_device_) {
return nullptr;
}
DXGI_FORMAT dxgi_format = GetDXGIFormatForCreateTexture(format);
DCHECK_NE(dxgi_format, DXGI_FORMAT_UNKNOWN);
// GL_TEXTURE_2D is ok to use here as D3D11_BIND_RENDER_TARGET is being used.
const GLenum texture_target = GL_TEXTURE_2D;
D3D11_TEXTURE2D_DESC desc;
desc.Width = size.width();
desc.Height = size.height();
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = dxgi_format;
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 = 0;
if (usage.Has(gpu::SHARED_IMAGE_USAGE_WEBGPU_STORAGE_TEXTURE) &&
format.is_single_plane()) {
DCHECK(usage.HasAny(SHARED_IMAGE_USAGE_WEBGPU_READ |
SHARED_IMAGE_USAGE_WEBGPU_WRITE));
// WebGPU can always use RGBA_8888 and RGBA_16 for STORAGE_BINDING.
if (format == viz::SinglePlaneFormat::kRGBA_8888 ||
format == viz::SinglePlaneFormat::kRGBA_F16) {
desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
}
// WebGPU can use BGRA_8888 for STORAGE_BINDING only when supported for UAV.
if (format == viz::SinglePlaneFormat::kBGRA_8888) {
if (SupportsBGRA8UnormStorage()) {
desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
} else {
LOG(ERROR)
<< "D3D11_BIND_UNORDERED_ACCESS is not supported on BGRA_8888";
return nullptr;
}
}
}
const bool has_webgpu_usage = usage.HasAny(SHARED_IMAGE_USAGE_WEBGPU_READ |
SHARED_IMAGE_USAGE_WEBGPU_WRITE);
const bool has_gl_usage = HasGLES2ReadOrWriteUsage(usage);
const bool want_dcomp_texture =
usage.Has(SHARED_IMAGE_USAGE_SCANOUT) &&
IsFormatSupportedForDCompTexture(desc.Format) &&
IsColorSpaceSupportedForDCompTexture(
gfx::ColorSpaceWin::GetDXGIColorSpace(color_space));
// TODO(crbug.com/40204134): Look into using DXGI handle when MF VEA is used.
const bool needs_cross_device_synchronization =
has_webgpu_usage ||
(has_gl_usage && (d3d11_device_ != angle_d3d11_device_));
const bool needs_shared_handle =
needs_cross_device_synchronization || want_dcomp_texture;
if (needs_shared_handle) {
// TODO(crbug.com/40068319): Many texture formats cannot be shared on old
// GPUs/drivers to try to detect that and implement a fallback path or
// disallow Graphite/WebGPU in those cases.
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE |
(gfx::D3DSharedFence::IsSupported(d3d11_device_.Get())
? D3D11_RESOURCE_MISC_SHARED
: D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX);
}
D3D11_SUBRESOURCE_DATA initial_data = {};
if (!pixel_data.empty()) {
if (!IsFormatSupportedForInitialData(format)) {
LOG(ERROR) << "Unsupported format: " << format.ToString();
return nullptr;
}
if (pixel_data.size_bytes() < format.EstimatedSizeInBytes(size)) {
LOG(ERROR) << "Not enough pixel data";
return nullptr;
}
initial_data.pSysMem = pixel_data.data();
initial_data.SysMemPitch = format.BitsPerPixel() * size.width() / 8;
initial_data.SysMemSlicePitch = 0;
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
HRESULT hr = d3d11_device_->CreateTexture2D(
&desc, initial_data.pSysMem ? &initial_data : nullptr, &d3d11_texture);
if (FAILED(hr)) {
LOG(ERROR) << "CreateTexture2D failed with error " << std::hex << hr;
return nullptr;
}
debug_label = "D3DSharedImage_" + debug_label;
d3d11_texture->SetPrivateData(WKPDID_D3DDebugObjectName, debug_label.length(),
debug_label.c_str());
scoped_refptr<DXGISharedHandleState> dxgi_shared_handle_state;
if (needs_cross_device_synchronization) {
Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
hr = d3d11_texture.As(&dxgi_resource);
if (FAILED(hr)) {
LOG(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)) {
LOG(ERROR) << "Unable to create shared handle for DXGIResource "
<< std::hex << hr;
return nullptr;
}
dxgi_shared_handle_state =
dxgi_shared_handle_manager_->CreateAnonymousSharedHandleState(
base::win::ScopedHandle(shared_handle), d3d11_texture);
}
Microsoft::WRL::ComPtr<IDCompositionTexture> dcomp_texture;
if (want_dcomp_texture) {
// If this trips, it means we're claiming support for SCANOUT when DComp
// textures is not supported by the system, or an incompatible texture was
// created above.
CHECK(DCompTextureIsSupported(desc));
Microsoft::WRL::ComPtr<IDCompositionDevice3> dcomp_device =
gl::GetDirectCompositionDevice();
Microsoft::WRL::ComPtr<IDCompositionDevice4> dcomp_device4;
hr = dcomp_device.As(&dcomp_device4);
CHECK_EQ(hr, S_OK) << ", QueryInterface failed: "
<< logging::SystemErrorCodeToString(hr);
hr = dcomp_device4->CreateCompositionTexture(d3d11_texture.Get(),
&dcomp_texture);
CHECK_EQ(hr, S_OK) << ", CreateCompositionTexture failed: "
<< logging::SystemErrorCodeToString(hr);
hr = dcomp_texture->SetAlphaMode(SkAlphaTypeIsOpaque(alpha_type)
? DXGI_ALPHA_MODE_IGNORE
: DXGI_ALPHA_MODE_PREMULTIPLIED);
CHECK_EQ(hr, S_OK) << ", SetAlphaMode failed: "
<< logging::SystemErrorCodeToString(hr);
hr = dcomp_texture->SetColorSpace(
gfx::ColorSpaceWin::GetDXGIColorSpace(color_space));
CHECK_EQ(hr, S_OK) << ", SetColorSpace failed: "
<< logging::SystemErrorCodeToString(hr);
}
// SkiaOutputDeviceDComp will hold onto DComp texture overlay accesses for
// longer than a frame, due to DWM synchronization requirements. This is
// incompatible with the assumption that keyed mutex access will be minimal.
CHECK(!dcomp_texture || !(dxgi_shared_handle_state &&
dxgi_shared_handle_state->has_keyed_mutex()));
auto backing = D3DImageBacking::Create(
mailbox, format, size, color_space, surface_origin, alpha_type, usage,
std::move(debug_label), std::move(d3d11_texture),
std::move(dcomp_texture), std::move(dxgi_shared_handle_state),
gl_format_caps_, texture_target, /*array_slice=*/0u,
use_update_subresource1_);
if (backing && !pixel_data.empty()) {
backing->SetCleared();
}
return backing;
}
std::unique_ptr<SharedImageBacking> D3DImageBackingFactory::CreateSharedImage(
const Mailbox& mailbox,
viz::SharedImageFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
SharedImageUsageSet usage,
std::string debug_label,
gfx::GpuMemoryBufferHandle handle) {
// Windows does not support external sampler.
CHECK(!format.PrefersExternalSampler());
// TOOD(hitawala): Move this size check to IsSupported.
const gfx::BufferFormat buffer_format = gpu::ToBufferFormat(format);
if (!gpu::IsImageSizeValidForGpuMemoryBufferFormat(size, buffer_format)) {
LOG(ERROR) << "Invalid image size " << size.ToString() << " for "
<< gfx::BufferFormatToString(buffer_format);
return nullptr;
}
if (handle.type != gfx::DXGI_SHARED_HANDLE || !handle.dxgi_handle.IsValid()) {
LOG(ERROR) << "Invalid handle with type: " << handle.type;
return nullptr;
}
if (!handle.dxgi_token.has_value()) {
LOG(ERROR) << "Missing token for DXGI handle";
return nullptr;
}
scoped_refptr<DXGISharedHandleState> dxgi_shared_handle_state =
dxgi_shared_handle_manager_->GetOrCreateSharedHandleState(
std::move(handle.dxgi_token.value()), std::move(handle.dxgi_handle),
d3d11_device_);
if (!dxgi_shared_handle_state) {
LOG(ERROR) << "Failed to retrieve matching DXGI shared handle state";
return nullptr;
}
Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture =
dxgi_shared_handle_state->GetOrCreateD3D11Texture(d3d11_device_);
CHECK(d3d11_texture);
D3D11_TEXTURE2D_DESC d3d11_texture_desc = {};
d3d11_texture->GetDesc(&d3d11_texture_desc);
// TODO: Add checks for device specific limits.
if (d3d11_texture_desc.Width != static_cast<UINT>(size.width()) ||
d3d11_texture_desc.Height != static_cast<UINT>(size.height())) {
LOG(ERROR) << "Size must match texture being opened";
return nullptr;
}
if ((d3d11_texture_desc.Format != GetDXGIFormatForGMB(format)) &&
(d3d11_texture_desc.Format != GetDXGITypelessFormat(format))) {
LOG(ERROR) << "Format must match texture being opened";
return nullptr;
}
std::unique_ptr<D3DImageBacking> backing = D3DImageBacking::Create(
mailbox, format, size, color_space, surface_origin, alpha_type, usage,
std::move(debug_label), std::move(d3d11_texture),
/*dcomp_texture=*/nullptr, std::move(dxgi_shared_handle_state),
gl_format_caps_, /*texture_target=*/GL_TEXTURE_2D, /*array_slice=*/0u,
use_update_subresource1_);
if (backing) {
backing->SetCleared();
}
return backing;
}
bool D3DImageBackingFactory::SupportsBGRA8UnormStorage() {
if (!supports_bgra8unorm_storage_.has_value()) {
D3D11_FEATURE_DATA_FORMAT_SUPPORT bgra8UnormSupport = {};
bgra8UnormSupport.InFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
HRESULT hr = d3d11_device_->CheckFeatureSupport(
D3D11_FEATURE_FORMAT_SUPPORT, &bgra8UnormSupport,
sizeof(D3D11_FEATURE_DATA_FORMAT_SUPPORT));
if (SUCCEEDED(hr)) {
supports_bgra8unorm_storage_.emplace(
bgra8UnormSupport.OutFormatSupport &
D3D11_FORMAT_SUPPORT_TYPED_UNORDERED_ACCESS_VIEW);
} else {
VLOG(1) << "Failed to retrieve D3D11_FEATURE_FORMAT_SUPPORT. hr = "
<< std::hex << hr;
supports_bgra8unorm_storage_.emplace(false);
}
}
return supports_bgra8unorm_storage_.value();
}
bool D3DImageBackingFactory::IsSupported(SharedImageUsageSet usage,
viz::SharedImageFormat format,
const gfx::Size& size,
bool thread_safe,
gfx::GpuMemoryBufferType gmb_type,
GrContextType gr_context_type,
base::span<const uint8_t> pixel_data) {
if (!pixel_data.empty() && !IsFormatSupportedForInitialData(format)) {
return false;
}
const bool is_scanout = usage.Has(gpu::SHARED_IMAGE_USAGE_SCANOUT);
const bool is_video_decode = usage.Has(gpu::SHARED_IMAGE_USAGE_VIDEO_DECODE);
if (is_scanout) {
if (is_video_decode || gmb_type == gfx::DXGI_SHARED_HANDLE) {
// Video decode and video frames via GMBs are handled specially in
// |SwapChainPresenter|, so we must assume it's safe to create a scanout
// image backing for it.
} else if (gmb_type == gfx::EMPTY_BUFFER) {
return gl::DirectCompositionTextureSupported() &&
IsFormatSupportedForDCompTexture(
GetDXGIFormatForCreateTexture(format));
}
}
if (gmb_type == gfx::EMPTY_BUFFER) {
if (GetDXGIFormatForCreateTexture(format) == DXGI_FORMAT_UNKNOWN) {
return false;
}
} else if (gmb_type == gfx::DXGI_SHARED_HANDLE) {
if (GetDXGIFormatForGMB(format) == DXGI_FORMAT_UNKNOWN) {
return false;
}
} else {
return false;
}
return true;
}
SharedImageBackingType D3DImageBackingFactory::GetBackingType() {
return SharedImageBackingType::kD3D;
}
} // namespace gpu