chromium/gpu/command_buffer/service/shared_image/dcomp_image_backing_factory.cc

// Copyright 2022 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/dcomp_image_backing_factory.h"

#include <d3d11_1.h>

#include "components/viz/common/resources/shared_image_format.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/shared_image/dcomp_surface_image_backing.h"
#include "gpu/command_buffer/service/shared_image/dxgi_swap_chain_image_backing.h"
#include "ui/gfx/color_space_win.h"

namespace gpu {

namespace {

// Check if a format is supported by DXGI for DComp surfaces or swap chains.
// https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/converting-data-color-space
bool IsFormatSupportedForScanout(viz::SharedImageFormat format) {
  return ((format == viz::SinglePlaneFormat::kRGBA_8888) ||
          (format == viz::SinglePlaneFormat::kBGRA_8888) ||
          (format == viz::SinglePlaneFormat::kRGBX_8888) ||
          (format == viz::SinglePlaneFormat::kBGRX_8888) ||
          (format == viz::SinglePlaneFormat::kRGBA_F16) ||
          (format == viz::SinglePlaneFormat::kRGBA_1010102));
}

constexpr SharedImageUsageSet kDXGISwapChainUsage =
    SHARED_IMAGE_USAGE_DISPLAY_READ | SHARED_IMAGE_USAGE_DISPLAY_WRITE |
    SHARED_IMAGE_USAGE_SCANOUT | SHARED_IMAGE_USAGE_SCANOUT_DXGI_SWAP_CHAIN;
constexpr SharedImageUsageSet kDCompSurfaceUsage =
    SHARED_IMAGE_USAGE_DISPLAY_WRITE | SHARED_IMAGE_USAGE_SCANOUT |
    SHARED_IMAGE_USAGE_SCANOUT_DCOMP_SURFACE;
constexpr SharedImageUsageSet kSupportedUsage =
    kDXGISwapChainUsage | kDCompSurfaceUsage;

}  // namespace

DCompImageBackingFactory::DCompImageBackingFactory(
    scoped_refptr<SharedContextState> context_state)
    : SharedImageBackingFactory(kSupportedUsage),
      context_state_(std::move(context_state)) {}

DCompImageBackingFactory::~DCompImageBackingFactory() = default;

std::unique_ptr<SharedImageBacking> DCompImageBackingFactory::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) {
  DCHECK(!is_thread_safe);
  CHECK(alpha_type == kOpaque_SkAlphaType || alpha_type == kPremul_SkAlphaType);

  // DXGI only supports a handful of formats for scan-out, so we map the
  // requested format to a supported compatible DXGI format.
  DXGI_FORMAT internal_format = gfx::ColorSpaceWin::GetDXGIFormat(color_space);

  if (usage.Has(SHARED_IMAGE_USAGE_SCANOUT_DCOMP_SURFACE)) {
    DCHECK_NE(internal_format, DXGI_FORMAT_R10G10B10A2_UNORM);
    return DCompSurfaceImageBacking::Create(
        mailbox, format, internal_format, size, color_space, surface_origin,
        alpha_type, usage, std::move(debug_label));
  } else {
    return DXGISwapChainImageBacking::Create(
        context_state_->GetD3D11Device(), mailbox, format, internal_format,
        size, color_space, surface_origin, alpha_type, usage,
        std::move(debug_label));
  }
}

std::unique_ptr<SharedImageBacking> DCompImageBackingFactory::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) {
  NOTREACHED_IN_MIGRATION();
  return nullptr;
}

std::unique_ptr<SharedImageBacking> DCompImageBackingFactory::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) {
  NOTREACHED();
}

bool DCompImageBackingFactory::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 (format.is_multi_plane()) {
    return false;
  }

  // TODO(tangm): Allow write-only swap chain usage?
  bool is_usage_valid =
      usage == kDXGISwapChainUsage || usage == kDCompSurfaceUsage;
  if (!is_usage_valid) {
    return false;
  }

  if (!IsFormatSupportedForScanout(format)) {
    return false;
  }

  // IDCompositionDevice2::CreateSurface does not support rgb10. In cases where
  // dc overlays are to be used for rgb10, the caller should use swap chains
  // instead.
  if (usage == kDCompSurfaceUsage &&
      format == viz::SinglePlaneFormat::kRGBA_1010102) {
    return false;
  }

  if (thread_safe) {
    return false;
  }

  if (gmb_type != gfx::GpuMemoryBufferType::EMPTY_BUFFER) {
    return false;
  }

  if (!pixel_data.empty()) {
    return false;
  }

  return true;
}

SharedImageBackingType DCompImageBackingFactory::GetBackingType() {
  return SharedImageBackingType::kDCompSurface;
}

}  // namespace gpu