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

// 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/android_video_image_backing.h"

#include <dawn/webgpu.h>

#include "base/android/scoped_hardware_buffer_fence_sync.h"
#include "components/viz/common/gpu/vulkan_context_provider.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/abstract_texture_android.h"
#include "gpu/command_buffer/service/dawn_context_provider.h"
#include "gpu/command_buffer/service/ref_counted_lock.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/video_image_reader_image_backing.h"
#include "gpu/command_buffer/service/shared_image/video_surface_texture_image_backing.h"
#include "gpu/command_buffer/service/texture_owner.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/vulkan/vulkan_device_queue.h"
#include "gpu/vulkan/vulkan_implementation.h"
#include "ui/gfx/gpu_fence.h"

namespace gpu {

AndroidVideoImageBacking::AndroidVideoImageBacking(
    const Mailbox& mailbox,
    const gfx::Size& size,
    const gfx::ColorSpace color_space,
    GrSurfaceOrigin surface_origin,
    SkAlphaType alpha_type,
    std::string debug_label,
    bool is_thread_safe)
    : AndroidImageBacking(
          mailbox,
          viz::SinglePlaneFormat::kRGBA_8888,
          size,
          color_space,
          surface_origin,
          alpha_type,
          // This SI will be used to back a VideoFrame. As such, it
          // will potentially be sent to the display compositor and read by the
          // GL interface for WebGL.
          // TODO: crbug.com/354856448 - add a parameter to the constructor that
          // allows to specify whether SCANOUT is needed.
          {SHARED_IMAGE_USAGE_DISPLAY_READ, SHARED_IMAGE_USAGE_GLES2_READ,
           SHARED_IMAGE_USAGE_SCANOUT},
          std::move(debug_label),
          viz::SinglePlaneFormat::kRGBA_8888.EstimatedSizeInBytes(size),
          is_thread_safe,
          base::ScopedFD()) {}

AndroidVideoImageBacking::~AndroidVideoImageBacking() {}

// Static.
std::unique_ptr<AndroidVideoImageBacking> AndroidVideoImageBacking::Create(
    const Mailbox& mailbox,
    const gfx::Size& size,
    const gfx::ColorSpace color_space,
    GrSurfaceOrigin surface_origin,
    SkAlphaType alpha_type,
    std::string debug_label,
    scoped_refptr<StreamTextureSharedImageInterface> stream_texture_sii,
    scoped_refptr<SharedContextState> context_state,
    scoped_refptr<RefCountedLock> drdc_lock) {
  if (features::IsAImageReaderEnabled()) {
    return std::make_unique<VideoImageReaderImageBacking>(
        mailbox, size, color_space, surface_origin, alpha_type,
        std::move(debug_label), std::move(stream_texture_sii),
        std::move(context_state), std::move(drdc_lock));
  } else {
    DCHECK(!drdc_lock);
    return std::make_unique<VideoSurfaceTextureImageBacking>(
        mailbox, size, color_space, surface_origin, alpha_type,
        std::move(debug_label), std::move(stream_texture_sii),
        std::move(context_state));
  }
}

// Static.
std::optional<VulkanYCbCrInfo> AndroidVideoImageBacking::GetYcbcrInfo(
    TextureOwner* texture_owner,
    viz::VulkanContextProvider* vulkan_context_provider,
    DawnContextProvider* dawn_context_provider) {
  if (!vulkan_context_provider && !dawn_context_provider) {
    return std::nullopt;
  }

  // Get AHardwareBuffer from the latest frame.
  auto scoped_hardware_buffer = texture_owner->GetAHardwareBuffer();
  if (!scoped_hardware_buffer)
    return std::nullopt;

  DCHECK(scoped_hardware_buffer->buffer());

  if (vulkan_context_provider) {
    CHECK(!dawn_context_provider);

    VulkanImplementation* vk_implementation =
        vulkan_context_provider->GetVulkanImplementation();
    VkDevice vk_device =
        vulkan_context_provider->GetDeviceQueue()->GetVulkanDevice();

    VulkanYCbCrInfo ycbcr_info;
    if (!vk_implementation->GetSamplerYcbcrConversionInfo(
            vk_device, scoped_hardware_buffer->TakeBuffer(), &ycbcr_info)) {
      LOG(ERROR) << "Failed to get the ycbcr info.";
      return std::nullopt;
    }
    return std::optional<VulkanYCbCrInfo>(ycbcr_info);
  }

#if BUILDFLAG(SKIA_USE_DAWN)
  // Get the YCbCr info from Dawn.

  auto device = dawn_context_provider->GetDevice();

  wgpu::AHardwareBufferProperties ahb_properties;
  if (!device.GetAHardwareBufferProperties(scoped_hardware_buffer->buffer(),
                                           &ahb_properties)) {
    LOG(ERROR) << "Failed to get the ycbcr info.";
    return std::nullopt;
  }

  // Populate the Chrome-side YCbCr info from the Dawn info.
  auto ycbcr_info = ahb_properties.yCbCrInfo;

  if (!ycbcr_info.externalFormat) {
    LOG(ERROR) << "Failed to get the ycbcr info.";
    return std::nullopt;
  }

  // NOTE: VulkanYCbCrInfo requires that the format be UNDEFINED if the external
  // format is non-zero.
  auto vk_format = VK_FORMAT_UNDEFINED;

  // NOTE: Dawn does not explicitly reflect `formatFeatures`, but
  // VulkanYCbCrInfo requires that `format_features` be non-zero when the
  // external format is non-zero. The below bit must always be set by the Vulkan
  // spec when YCbCr sampling is used, so it can safely be set here to satisfy
  // this constraint.
  uint32_t format_features = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;

  // Pass along the bit of whether linear filtering is supported. Viz will
  // extract this information and use it to construct `vkChromaFilter` when
  // passing YCbCr info to Skia via DawnTextureInfo.
  if (ycbcr_info.vkChromaFilter == wgpu::FilterMode::Linear) {
    format_features |=
        VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT;
  }

  return VulkanYCbCrInfo(vk_format, ycbcr_info.externalFormat,
                         ycbcr_info.vkYCbCrModel, ycbcr_info.vkYCbCrRange,
                         ycbcr_info.vkXChromaOffset, ycbcr_info.vkYChromaOffset,
                         format_features);
#else
  return std::nullopt;
#endif
}

std::unique_ptr<AbstractTextureAndroid>
AndroidVideoImageBacking::GenAbstractTexture(const bool passthrough) {
  if (passthrough) {
    return AbstractTextureAndroid::CreateForPassthrough(size());
  } else {
    return AbstractTextureAndroid::CreateForValidating(size());
  }
}

SharedImageBackingType AndroidVideoImageBacking::GetType() const {
  return SharedImageBackingType::kVideo;
}

gfx::Rect AndroidVideoImageBacking::ClearedRect() const {
  // AndroidVideoImageBacking objects are always created from pre-initialized
  // textures provided by the media decoder. Always treat these as cleared
  // (return the full rectangle).
  return gfx::Rect(size());
}

void AndroidVideoImageBacking::SetClearedRect(const gfx::Rect& cleared_rect) {}

void AndroidVideoImageBacking::Update(std::unique_ptr<gfx::GpuFence> in_fence) {
  DCHECK(!in_fence);
}

size_t AndroidVideoImageBacking::GetEstimatedSizeForMemoryDump() const {
  // None of these images own memory directly, so we report 0. The real memory
  // will be reported by `TextureOwner`s.
  return 0;
}

}  // namespace gpu