blob: ad6f5317dfba6cce3792b4236d10f97c7c09e776 [file] [log] [blame]
// Copyright 2018 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/shared_image_representation.h"
#include <tuple>
#include "base/notreached.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/shared_image/shared_image_format_service_utils.h"
#include "gpu/command_buffer/service/shared_image/shared_image_manager.h"
#include "gpu/command_buffer/service/texture_manager.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/gpu/MutableTextureState.h"
#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
#include "third_party/skia/include/gpu/ganesh/GrYUVABackendTextures.h"
#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
#include "third_party/skia/include/gpu/graphite/Image.h"
#include "third_party/skia/include/gpu/graphite/YUVABackendTextures.h"
#include "third_party/skia/include/private/chromium/GrPromiseImageTexture.h"
#include "ui/gl/gl_fence.h"
#if BUILDFLAG(ENABLE_VULKAN)
#include "gpu/vulkan/vulkan_fence_helper.h"
#endif
namespace gpu {
namespace {
using GraphiteTextureHolder = SkiaImageRepresentation::GraphiteTextureHolder;
// Wrap the original release proc in a newer release proc that will release
// the given GraphiteTextureHolder refs.
std::tuple<SkImages::TextureReleaseProc, SkImages::ReleaseContext>
CreateGraphiteSkImageReleaseProc(
SkImages::TextureReleaseProc texture_release_proc,
SkImages::ReleaseContext release_context,
std::vector<scoped_refptr<GraphiteTextureHolder>> texture_holders) {
SkImages::TextureReleaseProc wrapped_release_proc;
SkImages::ReleaseContext wrapped_release_context;
// Keep the GraphiteTextureHolder refs until the wrapped_release_proc is
// called. This will keep the GraphiteTextureHolder alive until Graphite is
// done with the SkImage.
struct WrappedReleaseContext {
std::vector<scoped_refptr<GraphiteTextureHolder>> texture_holders;
SkImages::TextureReleaseProc original_release_proc;
SkImages::ReleaseContext original_context;
};
auto* wrapped_context_ptr = new WrappedReleaseContext;
wrapped_context_ptr->texture_holders = std::move(texture_holders);
wrapped_context_ptr->original_release_proc = texture_release_proc;
wrapped_context_ptr->original_context = release_context;
wrapped_release_proc = [](SkImages::ReleaseContext context) {
auto* wrapped_context = static_cast<WrappedReleaseContext*>(context);
if (wrapped_context->original_release_proc) {
wrapped_context->original_release_proc(wrapped_context->original_context);
}
delete wrapped_context;
};
wrapped_release_context = wrapped_context_ptr;
return {wrapped_release_proc, wrapped_release_context};
}
} // namespace
SkiaImageRepresentation::GraphiteTextureHolder::GraphiteTextureHolder(
skgpu::graphite::BackendTexture texture)
: texture_(std::move(texture)) {}
SkiaImageRepresentation::GraphiteTextureHolder::~GraphiteTextureHolder() =
default;
///////////////////////////////////////////////////////////////////////////////
// SharedImageRepresentation
SharedImageRepresentation::SharedImageRepresentation(
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* owning_tracker)
: manager_(manager), backing_(backing), tracker_(owning_tracker) {
DCHECK(tracker_);
// TODO(hitawala): Rewrite the reference counting so that
// SharedImageRepresentation does not need manager and manager attaches to
// backing in Register().
if (manager_ && backing_->is_ref_counted()) {
backing_->AddRef(this);
}
}
SharedImageRepresentation::~SharedImageRepresentation() {
// CHECK here as we'll crash later anyway, and this makes it clearer what the
// error is.
CHECK_EQ(access_mode_, AccessMode::kNone)
<< "Destroying a SharedImageRepresentation with "
"outstanding Scoped*Access objects.";
if (manager_ && backing_->is_ref_counted()) {
manager_->OnRepresentationDestroyed(backing_->mailbox(), this);
}
}
size_t SharedImageRepresentation::NumPlanesExpected() const {
if (format().PrefersExternalSampler()) {
return 1;
}
return static_cast<size_t>(format().NumberOfPlanes());
}
///////////////////////////////////////////////////////////////////////////////
// GLTextureImageRepresentationBase
std::unique_ptr<GLTextureImageRepresentation::ScopedAccess>
GLTextureImageRepresentationBase::BeginScopedAccess(
GLenum mode,
AllowUnclearedAccess allow_uncleared) {
if (allow_uncleared != AllowUnclearedAccess::kYes && !IsCleared()) {
LOG(ERROR)
<< "Attempt to access an uninitialized SharedImage. debug_label: "
<< debug_label();
return nullptr;
}
if (!BeginAccess(mode)) {
return nullptr;
}
UpdateClearedStateOnBeginAccess();
AccessMode access_mode;
if (mode == kReadAccessMode) {
access_mode = AccessMode::kRead;
} else {
access_mode = AccessMode::kWrite;
}
return std::make_unique<ScopedAccess>(
base::PassKey<GLTextureImageRepresentationBase>(), this, access_mode);
}
gpu::TextureBase* GLTextureImageRepresentationBase::GetTextureBase() {
CHECK_EQ(NumPlanesExpected(), 1u);
return GetTextureBase(0);
}
bool GLTextureImageRepresentationBase::SupportsMultipleConcurrentReadAccess() {
return false;
}
///////////////////////////////////////////////////////////////////////////////
// GLTextureImageRepresentation
gpu::TextureBase* GLTextureImageRepresentation::GetTextureBase(
int plane_index) {
return GetTexture(plane_index);
}
gles2::Texture* GLTextureImageRepresentation::GetTexture() {
CHECK_EQ(NumPlanesExpected(), 1u);
return GetTexture(0);
}
void GLTextureImageRepresentation::UpdateClearedStateOnEndAccess() {
auto* texture = GetTexture();
// Operations on the gles2::Texture may have cleared or uncleared it. Make
// sure this state is reflected back in the SharedImage.
gfx::Rect cleared_rect = texture->GetLevelClearedRect(texture->target(), 0);
if (cleared_rect != ClearedRect()) {
SetClearedRect(cleared_rect);
}
}
void GLTextureImageRepresentation::UpdateClearedStateOnBeginAccess() {
auto* texture = GetTexture();
// Operations outside of the gles2::Texture may have cleared or uncleared it.
// Make sure this state is reflected back in gles2::Texture.
gfx::Rect cleared_rect = ClearedRect();
if (cleared_rect != texture->GetLevelClearedRect(texture->target(), 0)) {
texture->SetLevelClearedRect(texture->target(), 0, cleared_rect);
}
}
///////////////////////////////////////////////////////////////////////////////
// GLTexturePassthroughImageRepresentation
gpu::TextureBase* GLTexturePassthroughImageRepresentation::GetTextureBase(
int plane_index) {
return GetTexturePassthrough(plane_index).get();
}
const scoped_refptr<gles2::TexturePassthrough>&
GLTexturePassthroughImageRepresentation::GetTexturePassthrough() {
CHECK_EQ(NumPlanesExpected(), 1u);
return GetTexturePassthrough(0);
}
bool GLTexturePassthroughImageRepresentation::
NeedsSuspendAccessForDXGIKeyedMutex() const {
return false;
}
///////////////////////////////////////////////////////////////////////////////
// SkiaImageRepresentation
SkiaImageRepresentation::SkiaImageRepresentation(SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker)
: SharedImageRepresentation(manager, backing, tracker) {}
SkiaImageRepresentation::~SkiaImageRepresentation() = default;
bool SkiaImageRepresentation::SupportsMultipleConcurrentReadAccess() {
return false;
}
SkiaImageRepresentation::ScopedWriteAccess::ScopedWriteAccess(
SkiaImageRepresentation* representation,
std::vector<sk_sp<SkSurface>> surfaces)
: ScopedAccessBase(representation, AccessMode::kWrite),
surfaces_(std::move(surfaces)) {
CHECK(!surfaces_.empty());
}
SkiaImageRepresentation::ScopedWriteAccess::ScopedWriteAccess(
SkiaImageRepresentation* representation,
std::vector<sk_sp<GrPromiseImageTexture>> promise_image_textures)
: ScopedAccessBase(representation, AccessMode::kWrite),
promise_image_textures_(std::move(promise_image_textures)) {
CHECK(!promise_image_textures_.empty());
CHECK(graphite_texture_holders_.empty());
}
SkiaImageRepresentation::ScopedWriteAccess::ScopedWriteAccess(
SkiaImageRepresentation* representation,
std::vector<scoped_refptr<GraphiteTextureHolder>> graphite_textures)
: ScopedAccessBase(representation, AccessMode::kWrite),
graphite_texture_holders_(std::move(graphite_textures)) {
CHECK(!graphite_texture_holders_.empty());
CHECK(promise_image_textures_.empty());
}
SkiaImageRepresentation::ScopedWriteAccess::~ScopedWriteAccess() {
// Ensure no one uses `surfaces_` by dropping the reference before calling
// EndWriteAccess.
surfaces_.clear();
representation()->EndWriteAccess();
}
bool SkiaImageRepresentation::ScopedWriteAccess::NeedGraphiteContextSubmit() {
return representation()->NeedGraphiteContextSubmitBeforeEndAccess();
}
SkiaImageRepresentation::ScopedReadAccess::ScopedReadAccess(
SkiaImageRepresentation* representation,
std::vector<sk_sp<GrPromiseImageTexture>> promise_image_textures)
: ScopedAccessBase(representation, AccessMode::kRead),
promise_image_textures_(std::move(promise_image_textures)) {
CHECK(!promise_image_textures_.empty());
CHECK(graphite_texture_holders_.empty());
}
SkiaImageRepresentation::ScopedReadAccess::ScopedReadAccess(
SkiaImageRepresentation* representation,
std::vector<scoped_refptr<GraphiteTextureHolder>> graphite_textures)
: ScopedAccessBase(representation, AccessMode::kRead),
graphite_texture_holders_(std::move(graphite_textures)) {
CHECK(!graphite_texture_holders_.empty());
CHECK(promise_image_textures_.empty());
}
SkiaImageRepresentation::ScopedReadAccess::~ScopedReadAccess() {
representation()->EndReadAccess();
}
bool SkiaImageRepresentation::ScopedReadAccess::NeedGraphiteContextSubmit() {
return representation()->NeedGraphiteContextSubmitBeforeEndAccess();
}
///////////////////////////////////////////////////////////////////////////////
// SkiaGaneshImageRepresentation
SkiaGaneshImageRepresentation::SkiaGaneshImageRepresentation(
GrDirectContext* gr_context,
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker)
: SkiaImageRepresentation(manager, backing, tracker),
gr_context_(gr_context) {}
bool SkiaGaneshImageRepresentation::NeedGraphiteContextSubmitBeforeEndAccess() {
// Ganesh shouldn't need a Graphite context submit.
return false;
}
SkiaGaneshImageRepresentation::ScopedGaneshWriteAccess::ScopedGaneshWriteAccess(
base::PassKey<SkiaGaneshImageRepresentation> /* pass_key */,
SkiaImageRepresentation* representation,
std::vector<sk_sp<SkSurface>> surfaces,
std::unique_ptr<skgpu::MutableTextureState> end_state)
: ScopedWriteAccess(representation, std::move(surfaces)),
end_state_(std::move(end_state)) {
DCHECK(!surfaces_.empty());
}
SkiaGaneshImageRepresentation::ScopedGaneshWriteAccess::ScopedGaneshWriteAccess(
base::PassKey<SkiaGaneshImageRepresentation> /* pass_key */,
SkiaImageRepresentation* representation,
std::vector<sk_sp<GrPromiseImageTexture>> promise_image_textures,
std::unique_ptr<skgpu::MutableTextureState> end_state)
: ScopedWriteAccess(representation, std::move(promise_image_textures)),
end_state_(std::move(end_state)) {
DCHECK(!promise_image_textures_.empty());
}
SkiaGaneshImageRepresentation::ScopedGaneshWriteAccess::
~ScopedGaneshWriteAccess() {
if (end_state_) {
NOTREACHED() << "Before ending write access TakeEndState() must be called "
"and the result passed to skia to make sure all layout and "
"ownership transitions are done.";
}
}
bool SkiaGaneshImageRepresentation::ScopedGaneshWriteAccess::
HasBackendSurfaceEndState() {
return !!end_state_;
}
void SkiaGaneshImageRepresentation::ScopedGaneshWriteAccess::
ApplyBackendSurfaceEndState() {
if (!end_state_) {
return;
}
DCHECK(promise_image_textures_.empty() || surfaces_.empty());
int num_planes = representation()->format().NumberOfPlanes();
GrDirectContext* direct_context = ganesh_representation()->gr_context();
CHECK(direct_context);
if (!surfaces_.empty()) {
for (int plane = 0; plane < num_planes; plane++) {
direct_context->flush(surface(plane), /*info=*/{}, end_state_.get());
}
}
if (!promise_image_textures_.empty()) {
for (int plane = 0; plane < num_planes; plane++) {
if (!direct_context->setBackendTextureState(
promise_image_texture(plane)->backendTexture(), *end_state_)) {
LOG(ERROR) << "setBackendTextureState() failed for plane: " << plane;
}
}
}
end_state_ = nullptr;
}
std::unique_ptr<SkiaImageRepresentation::ScopedWriteAccess>
SkiaGaneshImageRepresentation::BeginScopedWriteAccess(
int final_msaa_count,
const SkSurfaceProps& surface_props,
const gfx::Rect& update_rect,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
AllowUnclearedAccess allow_uncleared,
bool use_sk_surface) {
if (allow_uncleared != AllowUnclearedAccess::kYes && !IsCleared()) {
LOG(ERROR)
<< "Attempt to write to an uninitialized SharedImage. debug_label: "
<< debug_label();
return nullptr;
}
if (surface_origin() != kTopLeft_GrSurfaceOrigin) {
LOG(ERROR) << "Skia write access is only allowed for top left origin "
"surfaces. debug_label: "
<< debug_label();
return nullptr;
}
std::unique_ptr<skgpu::MutableTextureState> end_state;
if (use_sk_surface) {
std::vector<sk_sp<SkSurface>> surfaces =
BeginWriteAccess(final_msaa_count, surface_props, update_rect,
begin_semaphores, end_semaphores, &end_state);
if (surfaces.empty()) {
LOG(ERROR) << "Unable to initialize SkSurface";
return nullptr;
}
return std::make_unique<ScopedGaneshWriteAccess>(
base::PassKey<SkiaGaneshImageRepresentation>(), this,
std::move(surfaces), std::move(end_state));
}
std::vector<sk_sp<GrPromiseImageTexture>> promise_image_textures =
BeginWriteAccess(begin_semaphores, end_semaphores, &end_state);
if (promise_image_textures.empty()) {
LOG(ERROR) << "Unable to initialize GrPromiseImageTexture";
return nullptr;
}
return std::make_unique<ScopedGaneshWriteAccess>(
base::PassKey<SkiaGaneshImageRepresentation>(), this,
std::move(promise_image_textures), std::move(end_state));
}
std::unique_ptr<SkiaImageRepresentation::ScopedWriteAccess>
SkiaGaneshImageRepresentation::BeginScopedWriteAccess(
int final_msaa_count,
const SkSurfaceProps& surface_props,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
AllowUnclearedAccess allow_uncleared,
bool use_sk_surface) {
return BeginScopedWriteAccess(
final_msaa_count, surface_props, gfx::Rect(size()), begin_semaphores,
end_semaphores, allow_uncleared, use_sk_surface);
}
std::unique_ptr<SkiaImageRepresentation::ScopedWriteAccess>
SkiaGaneshImageRepresentation::BeginScopedWriteAccess(
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
AllowUnclearedAccess allow_uncleared,
bool use_sk_surface) {
return BeginScopedWriteAccess(
/*final_msaa_count=*/1, SkSurfaceProps(), begin_semaphores,
end_semaphores, allow_uncleared, use_sk_surface);
}
SkiaGaneshImageRepresentation::ScopedGaneshReadAccess::ScopedGaneshReadAccess(
base::PassKey<SkiaGaneshImageRepresentation> /* pass_key */,
SkiaImageRepresentation* representation,
std::vector<sk_sp<GrPromiseImageTexture>> promise_image_textures,
std::unique_ptr<skgpu::MutableTextureState> end_state)
: ScopedReadAccess(representation, std::move(promise_image_textures)),
end_state_(std::move(end_state)) {
DCHECK(!promise_image_textures_.empty());
}
SkiaGaneshImageRepresentation::ScopedGaneshReadAccess::
~ScopedGaneshReadAccess() {
if (end_state_) {
NOTREACHED() << "Before ending read access TakeEndState() must be called "
"and the result passed to skia to make sure all layout and "
"ownership transitions are done.";
}
}
sk_sp<SkImage>
SkiaGaneshImageRepresentation::ScopedGaneshReadAccess::CreateSkImage(
SharedContextState* context_state,
SkImages::TextureReleaseProc texture_release_proc,
SkImages::ReleaseContext release_context) {
auto format = representation()->format();
auto surface_origin = representation()->surface_origin();
auto sk_color_space =
representation()->color_space().GetAsFullRangeRGB().ToSkColorSpace();
if (format.is_single_plane() || format.PrefersExternalSampler()) {
DCHECK_EQ(static_cast<int>(promise_image_textures_.size()), 1);
auto alpha_type = representation()->alpha_type();
auto color_type = format.PrefersExternalSampler()
? ToClosestSkColorTypeExternalSampler(format)
: viz::ToClosestSkColorType(format);
return SkImages::BorrowTextureFrom(
context_state->gr_context(), promise_image_texture()->backendTexture(),
surface_origin, color_type, alpha_type, sk_color_space,
texture_release_proc, release_context);
} else {
DCHECK_EQ(static_cast<int>(promise_image_textures_.size()),
format.NumberOfPlanes());
std::array<GrBackendTexture, SkYUVAInfo::kMaxPlanes> yuva_textures;
// Get the texture per plane.
for (int plane_index = 0; plane_index < format.NumberOfPlanes();
plane_index++) {
yuva_textures[plane_index] =
promise_image_texture(plane_index)->backendTexture();
}
SkISize sk_size = gfx::SizeToSkISize(representation()->size());
// TODO(crbug.com/41380578): This should really default to rec709.
SkYUVColorSpace yuv_color_space = kRec601_SkYUVColorSpace;
representation()->color_space().ToSkYUVColorSpace(
format.MultiplanarBitDepth(), &yuv_color_space);
SkYUVAInfo yuva_info(sk_size, ToSkYUVAPlaneConfig(format),
ToSkYUVASubsampling(format), yuv_color_space);
GrYUVABackendTextures yuva_backend_textures(yuva_info, yuva_textures.data(),
surface_origin);
return SkImages::TextureFromYUVATextures(
context_state->gr_context(), yuva_backend_textures, sk_color_space,
texture_release_proc, release_context);
}
}
sk_sp<SkImage>
SkiaGaneshImageRepresentation::ScopedGaneshReadAccess::CreateSkImageForPlane(
int plane_index,
SharedContextState* context_state,
SkImages::TextureReleaseProc texture_release_proc,
SkImages::ReleaseContext release_context) {
auto format = representation()->format();
DCHECK(format.is_multi_plane());
DCHECK_EQ(static_cast<int>(promise_image_textures_.size()),
format.NumberOfPlanes());
auto surface_origin = representation()->surface_origin();
auto alpha_type = SkAlphaType::kOpaque_SkAlphaType;
auto color_type = viz::ToClosestSkColorType(format, plane_index);
return SkImages::BorrowTextureFrom(
context_state->gr_context(),
promise_image_texture(plane_index)->backendTexture(), surface_origin,
color_type, alpha_type, /*sk_color_space=*/nullptr, texture_release_proc,
release_context);
}
bool SkiaGaneshImageRepresentation::ScopedGaneshReadAccess::
HasBackendSurfaceEndState() {
return !!end_state_;
}
void SkiaGaneshImageRepresentation::ScopedGaneshReadAccess::
ApplyBackendSurfaceEndState() {
if (!end_state_) {
return;
}
for (size_t plane = 0; plane < representation()->NumPlanesExpected();
plane++) {
if (!ganesh_representation()->gr_context()->setBackendTextureState(
promise_image_texture(plane)->backendTexture(), *end_state_)) {
LOG(ERROR) << "setBackendTextureState() failed for plane: " << plane;
}
}
end_state_ = nullptr;
}
std::unique_ptr<SkiaImageRepresentation::ScopedReadAccess>
SkiaGaneshImageRepresentation::BeginScopedReadAccess(
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores) {
if (!IsCleared()) {
auto cr = ClearedRect();
LOG(ERROR) << base::StringPrintf(
"Attempt to read from an uninitialized SharedImage. "
"Initialized region: (%d, %d, %d, %d) Size: (%d, %d)",
cr.x(), cr.y(), cr.width(), cr.height(), size().width(),
size().height());
return nullptr;
}
std::unique_ptr<skgpu::MutableTextureState> end_state;
std::vector<sk_sp<GrPromiseImageTexture>> promise_image_textures =
BeginReadAccess(begin_semaphores, end_semaphores, &end_state);
if (promise_image_textures.empty()) {
LOG(ERROR) << "Unable to initialize GrPromiseImageTexture";
return nullptr;
}
return std::make_unique<ScopedGaneshReadAccess>(
base::PassKey<SkiaGaneshImageRepresentation>(), this,
std::move(promise_image_textures), std::move(end_state));
}
///////////////////////////////////////////////////////////////////////////////
// SkiaGraphiteImageRepresentation
SkiaGraphiteImageRepresentation::SkiaGraphiteImageRepresentation(
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker)
: SkiaImageRepresentation(manager, backing, tracker) {}
bool SkiaGraphiteImageRepresentation::
NeedGraphiteContextSubmitBeforeEndAccess() {
// As default, assume Graphite context submit is needed.
return true;
}
SkiaGraphiteImageRepresentation::ScopedGraphiteWriteAccess::
ScopedGraphiteWriteAccess(
base::PassKey<SkiaGraphiteImageRepresentation> /* pass_key */,
SkiaImageRepresentation* representation,
std::vector<sk_sp<SkSurface>> surfaces)
: ScopedWriteAccess(representation, std::move(surfaces)) {
CHECK(!surfaces_.empty());
}
SkiaGraphiteImageRepresentation::ScopedGraphiteWriteAccess::
ScopedGraphiteWriteAccess(
base::PassKey<SkiaGraphiteImageRepresentation> /* pass_key */,
SkiaImageRepresentation* representation,
std::vector<scoped_refptr<GraphiteTextureHolder>> backend_textures)
: ScopedWriteAccess(representation, std::move(backend_textures)) {
CHECK(!graphite_texture_holders_.empty());
}
SkiaGraphiteImageRepresentation::ScopedGraphiteWriteAccess::
~ScopedGraphiteWriteAccess() = default;
bool SkiaGraphiteImageRepresentation::ScopedGraphiteWriteAccess::
HasBackendSurfaceEndState() {
return false;
}
// Graphite-Dawn backend handles Vulkan transitions by itself, so nothing to do
// here.
void SkiaGraphiteImageRepresentation::ScopedGraphiteWriteAccess::
ApplyBackendSurfaceEndState() {}
std::unique_ptr<SkiaImageRepresentation::ScopedWriteAccess>
SkiaGraphiteImageRepresentation::BeginScopedWriteAccess(
int final_msaa_count,
const SkSurfaceProps& surface_props,
const gfx::Rect& update_rect,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
AllowUnclearedAccess allow_uncleared,
bool use_sk_surface) {
if (allow_uncleared != AllowUnclearedAccess::kYes && !IsCleared()) {
LOG(ERROR)
<< "Attempt to write to an uninitialized SharedImage. debug_label: "
<< debug_label();
return nullptr;
}
if (surface_origin() != kTopLeft_GrSurfaceOrigin) {
LOG(ERROR) << "Skia write access is only allowed for top left origin "
"surfaces. debug_label: "
<< debug_label();
return nullptr;
}
if (use_sk_surface) {
std::vector<sk_sp<SkSurface>> surfaces =
BeginWriteAccess(surface_props, update_rect);
if (surfaces.empty()) {
LOG(ERROR) << "Unable to initialize SkSurface";
return nullptr;
}
return std::make_unique<ScopedGraphiteWriteAccess>(
base::PassKey<SkiaGraphiteImageRepresentation>(), this,
std::move(surfaces));
}
std::vector<scoped_refptr<GraphiteTextureHolder>> graphite_textures =
BeginWriteAccess();
if (graphite_textures.empty()) {
LOG(ERROR) << "Unable to initialize graphite::BackendTextures";
return nullptr;
}
return std::make_unique<ScopedGraphiteWriteAccess>(
base::PassKey<SkiaGraphiteImageRepresentation>(), this,
graphite_textures);
}
std::unique_ptr<SkiaImageRepresentation::ScopedWriteAccess>
SkiaGraphiteImageRepresentation::BeginScopedWriteAccess(
int final_msaa_count,
const SkSurfaceProps& surface_props,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
AllowUnclearedAccess allow_uncleared,
bool use_sk_surface) {
return BeginScopedWriteAccess(
final_msaa_count, surface_props, gfx::Rect(size()), begin_semaphores,
end_semaphores, allow_uncleared, use_sk_surface);
}
std::unique_ptr<SkiaImageRepresentation::ScopedWriteAccess>
SkiaGraphiteImageRepresentation::BeginScopedWriteAccess(
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores,
AllowUnclearedAccess allow_uncleared,
bool use_sk_surface) {
return BeginScopedWriteAccess(
/*final_msaa_count=*/1, SkSurfaceProps(), begin_semaphores,
end_semaphores, allow_uncleared, use_sk_surface);
}
SkiaGraphiteImageRepresentation::ScopedGraphiteReadAccess::
ScopedGraphiteReadAccess(
base::PassKey<SkiaGraphiteImageRepresentation> /* pass_key */,
SkiaImageRepresentation* representation,
std::vector<scoped_refptr<GraphiteTextureHolder>> graphite_textures)
: ScopedReadAccess(representation, graphite_textures) {
CHECK(!graphite_texture_holders_.empty());
}
SkiaGraphiteImageRepresentation::ScopedGraphiteReadAccess::
~ScopedGraphiteReadAccess() = default;
sk_sp<SkImage>
SkiaGraphiteImageRepresentation::ScopedGraphiteReadAccess::CreateSkImage(
SharedContextState* context_state,
SkImages::TextureReleaseProc texture_release_proc,
SkImages::ReleaseContext release_context) {
auto format = representation()->format();
auto sk_color_space =
representation()->color_space().GetAsFullRangeRGB().ToSkColorSpace();
auto* recorder = context_state->gpu_main_graphite_recorder();
auto [wrapped_release_proc, wrapped_release_context] =
CreateGraphiteSkImageReleaseProc(texture_release_proc, release_context,
graphite_texture_holders_);
if (format.is_single_plane() || format.PrefersExternalSampler()) {
CHECK_EQ(static_cast<int>(graphite_texture_holders_.size()), 1);
auto alpha_type = representation()->alpha_type();
auto color_type = format.PrefersExternalSampler()
? ToClosestSkColorTypeExternalSampler(format)
: viz::ToClosestSkColorType(format);
auto origin = representation()->surface_origin() == kTopLeft_GrSurfaceOrigin
? skgpu::Origin::kTopLeft
: skgpu::Origin::kBottomLeft;
return SkImages::WrapTexture(recorder, graphite_texture(), color_type,
alpha_type, sk_color_space, origin,
wrapped_release_proc, wrapped_release_context);
} else {
CHECK_EQ(static_cast<int>(graphite_texture_holders_.size()),
format.NumberOfPlanes());
SkISize sk_size = gfx::SizeToSkISize(representation()->size());
// TODO(crbug.com/41380578): This should really default to rec709.
SkYUVColorSpace yuv_color_space = kRec601_SkYUVColorSpace;
representation()->color_space().ToSkYUVColorSpace(
format.MultiplanarBitDepth(), &yuv_color_space);
SkYUVAInfo yuva_info(sk_size, ToSkYUVAPlaneConfig(format),
ToSkYUVASubsampling(format), yuv_color_space);
std::vector<skgpu::graphite::BackendTexture> backend_textures(
graphite_texture_holders_.size());
for (int i = 0; i < format.NumberOfPlanes(); ++i) {
backend_textures[i] = graphite_texture_holders_[i]->texture();
}
skgpu::graphite::YUVABackendTextures yuva_backend_textures(
yuva_info, backend_textures);
return SkImages::TextureFromYUVATextures(
recorder, yuva_backend_textures, sk_color_space, wrapped_release_proc,
wrapped_release_context);
}
}
sk_sp<SkImage> SkiaGraphiteImageRepresentation::ScopedGraphiteReadAccess::
CreateSkImageForPlane(int plane_index,
SharedContextState* context_state,
SkImages::TextureReleaseProc texture_release_proc,
SkImages::ReleaseContext release_context) {
auto [wrapped_release_proc, wrapped_release_context] =
CreateGraphiteSkImageReleaseProc(texture_release_proc, release_context,
{graphite_texture_holder(plane_index)});
auto format = representation()->format();
CHECK(format.is_multi_plane());
CHECK_EQ(static_cast<int>(graphite_texture_holders_.size()),
format.NumberOfPlanes());
auto alpha_type = SkAlphaType::kOpaque_SkAlphaType;
auto color_type = viz::ToClosestSkColorType(format, plane_index);
return SkImages::WrapTexture(context_state->gpu_main_graphite_recorder(),
graphite_texture(plane_index), color_type,
alpha_type, /*colorSpace=*/nullptr,
wrapped_release_proc, wrapped_release_context);
}
bool SkiaGraphiteImageRepresentation::ScopedGraphiteReadAccess::
HasBackendSurfaceEndState() {
return false;
}
// Graphite-Dawn backend handles Vulkan transitions by itself, so nothing to do
// here.
void SkiaGraphiteImageRepresentation::ScopedGraphiteReadAccess::
ApplyBackendSurfaceEndState() {}
std::unique_ptr<SkiaImageRepresentation::ScopedReadAccess>
SkiaGraphiteImageRepresentation::BeginScopedReadAccess(
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores) {
if (!IsCleared()) {
auto cr = ClearedRect();
LOG(ERROR) << base::StringPrintf(
"Attempt to read from an uninitialized SharedImage. "
"Initialized region: (%d, %d, %d, %d) Size: (%d, %d) Debug Label: %s",
cr.x(), cr.y(), cr.width(), cr.height(), size().width(),
size().height(), debug_label());
return nullptr;
}
std::vector<scoped_refptr<GraphiteTextureHolder>> graphite_textures =
BeginReadAccess();
if (graphite_textures.empty()) {
LOG(ERROR) << "Unable to initialize graphite::BackendTextures";
return nullptr;
}
return std::make_unique<ScopedGraphiteReadAccess>(
base::PassKey<SkiaGraphiteImageRepresentation>(), this,
graphite_textures);
}
std::string SkiaGraphiteImageRepresentation::WrappedTextureDebugLabel(
int plane) const {
std::string debug_label;
if (format().is_single_plane()) {
debug_label = base::StringPrintf("%s_%s", backing()->GetName(),
backing()->debug_label().c_str());
} else {
debug_label = base::StringPrintf("%s_%s_Plane%d", backing()->GetName(),
backing()->debug_label().c_str(), plane);
}
return debug_label;
}
///////////////////////////////////////////////////////////////////////////////
// WebNNTensorRepresentation
WebNNTensorRepresentation::ScopedAccess::ScopedAccess(
base::PassKey<WebNNTensorRepresentation> /* pass_key */,
WebNNTensorRepresentation* representation,
AccessMode access_mode)
: ScopedAccessBase(representation, access_mode) {}
WebNNTensorRepresentation::ScopedAccess::~ScopedAccess() {
representation()->EndAccess();
}
std::unique_ptr<WebNNTensorRepresentation::ScopedAccess>
WebNNTensorRepresentation::BeginScopedAccess() {
if (!BeginAccess()) {
return nullptr;
}
return std::make_unique<ScopedAccess>(
base::PassKey<WebNNTensorRepresentation>(), this, AccessMode::kWrite);
}
#if BUILDFLAG(IS_WIN)
Microsoft::WRL::ComPtr<ID3D12Resource>
WebNNTensorRepresentation::GetD3D12Buffer() const {
NOTREACHED();
}
void WebNNTensorRepresentation::ConsumeWebNNTensor(
base::WeakPtr<webnn::native::d3d12::WebNNTensor> webnn_tensor) {
NOTREACHED();
}
#endif
#if BUILDFLAG(IS_MAC)
IOSurfaceRef WebNNTensorRepresentation::GetIOSurface() const {
NOTREACHED();
}
#endif // BUILDFLAG(IS_MAC)
///////////////////////////////////////////////////////////////////////////////
// OverlayImageRepresentation
#if BUILDFLAG(IS_ANDROID)
AHardwareBuffer* OverlayImageRepresentation::GetAHardwareBuffer() {
NOTREACHED();
}
std::unique_ptr<base::android::ScopedHardwareBufferFenceSync>
OverlayImageRepresentation::GetAHardwareBufferFenceSync() {
NOTREACHED();
}
#elif BUILDFLAG(IS_OZONE)
scoped_refptr<gfx::NativePixmap> OverlayImageRepresentation::GetNativePixmap() {
return backing()->GetNativePixmap();
}
#elif BUILDFLAG(IS_WIN)
std::optional<gl::DCLayerOverlayImage>
OverlayImageRepresentation::GetDCLayerOverlayImage() {
NOTREACHED();
}
#elif BUILDFLAG(IS_APPLE)
gfx::ScopedIOSurface OverlayImageRepresentation::GetIOSurface() const {
return gfx::ScopedIOSurface();
}
bool OverlayImageRepresentation::IsInUseByWindowServer() const {
return false;
}
#endif
OverlayImageRepresentation::ScopedReadAccess::ScopedReadAccess(
base::PassKey<OverlayImageRepresentation> pass_key,
OverlayImageRepresentation* representation,
gfx::GpuFenceHandle acquire_fence)
: ScopedAccessBase(representation, AccessMode::kRead),
acquire_fence_(std::move(acquire_fence)) {}
OverlayImageRepresentation::ScopedReadAccess::~ScopedReadAccess() {
representation()->EndReadAccess(std::move(release_fence_));
}
std::unique_ptr<OverlayImageRepresentation::ScopedReadAccess>
OverlayImageRepresentation::BeginScopedReadAccess() {
if (!IsCleared()) {
LOG(ERROR)
<< "Attempt to read from an uninitialized SharedImage. debug_label: "
<< debug_label();
return nullptr;
}
gfx::GpuFenceHandle acquire_fence;
if (!BeginReadAccess(acquire_fence)) {
return nullptr;
}
return std::make_unique<ScopedReadAccess>(
base::PassKey<OverlayImageRepresentation>(), this,
std::move(acquire_fence));
}
///////////////////////////////////////////////////////////////////////////////
// DawnImageRepresentation
DawnImageRepresentation::ScopedAccess::ScopedAccess(
base::PassKey<DawnImageRepresentation> /* pass_key */,
DawnImageRepresentation* representation,
wgpu::Texture texture,
AccessMode access_mode)
: ScopedAccessBase(representation, access_mode),
texture_(std::move(texture)) {}
DawnImageRepresentation::ScopedAccess::~ScopedAccess() {
representation()->EndAccess();
}
std::unique_ptr<DawnImageRepresentation::ScopedAccess>
DawnImageRepresentation::BeginScopedAccess(
wgpu::TextureUsage usage,
AllowUnclearedAccess allow_uncleared) {
return BeginScopedAccess(usage, wgpu::TextureUsage::None, allow_uncleared,
gfx::Rect(size()));
}
std::unique_ptr<DawnImageRepresentation::ScopedAccess>
DawnImageRepresentation::BeginScopedAccess(
wgpu::TextureUsage usage,
wgpu::TextureUsage internal_usage,
AllowUnclearedAccess allow_uncleared) {
return BeginScopedAccess(usage, internal_usage, allow_uncleared,
gfx::Rect(size()));
}
std::unique_ptr<DawnImageRepresentation::ScopedAccess>
DawnImageRepresentation::BeginScopedAccess(wgpu::TextureUsage usage,
AllowUnclearedAccess allow_uncleared,
const gfx::Rect& update_rect) {
return BeginScopedAccess(usage, wgpu::TextureUsage::None, allow_uncleared,
update_rect);
}
std::unique_ptr<DawnImageRepresentation::ScopedAccess>
DawnImageRepresentation::BeginScopedAccess(wgpu::TextureUsage usage,
wgpu::TextureUsage internal_usage,
AllowUnclearedAccess allow_uncleared,
const gfx::Rect& update_rect) {
if (allow_uncleared != AllowUnclearedAccess::kYes && !IsCleared()) {
LOG(ERROR)
<< "Attempt to access an uninitialized SharedImage. debug_label: "
<< debug_label();
return nullptr;
}
wgpu::Texture texture = BeginAccess(usage, internal_usage, update_rect);
if (!texture) {
LOG(ERROR) << "Error creating wgpu::Texture";
return nullptr;
}
AccessMode access_mode;
if (usage & kWriteUsage) {
access_mode = AccessMode::kWrite;
} else {
access_mode = AccessMode::kRead;
}
return std::make_unique<ScopedAccess>(
base::PassKey<DawnImageRepresentation>(), this, std::move(texture),
access_mode);
}
wgpu::Texture DawnImageRepresentation::BeginAccess(
wgpu::TextureUsage usage,
wgpu::TextureUsage internal_usage,
const gfx::Rect& update_rect) {
return this->BeginAccess(usage, internal_usage);
}
///////////////////////////////////////////////////////////////////////////////
// DawnBufferRepresentation
DawnBufferRepresentation::ScopedAccess::ScopedAccess(
base::PassKey<DawnBufferRepresentation> /* pass_key */,
DawnBufferRepresentation* representation,
wgpu::Buffer buffer,
AccessMode access_mode)
: ScopedAccessBase(representation, access_mode),
buffer_(std::move(buffer)) {}
DawnBufferRepresentation::ScopedAccess::~ScopedAccess() {
representation()->EndAccess();
}
std::unique_ptr<DawnBufferRepresentation::ScopedAccess>
DawnBufferRepresentation::BeginScopedAccess(wgpu::BufferUsage usage) {
wgpu::Buffer buffer = BeginAccess(usage);
if (!buffer) {
LOG(ERROR) << "Error creating wgpu::Buffer";
return nullptr;
}
return std::make_unique<ScopedAccess>(
base::PassKey<DawnBufferRepresentation>(), this, std::move(buffer),
AccessMode::kWrite);
}
///////////////////////////////////////////////////////////////////////////////
// SharedImageRepresentationFactoryRef
SharedImageRepresentationFactoryRef::SharedImageRepresentationFactoryRef(
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker,
bool is_primary)
: SharedImageRepresentation(manager, backing, tracker),
is_primary_(is_primary) {
// If this is secondary reference, we need to notify SharedImageBacking that
// it can significantly outlive its owning SharedImageFactory and can't rely
// on it for operation.
if (!is_primary) {
backing->OnAddSecondaryReference();
}
}
SharedImageRepresentationFactoryRef::~SharedImageRepresentationFactoryRef() {
// Only primary refs provide link to the owning SharedImageFactory.
if (is_primary_) {
backing()->UnregisterImageFactory();
backing()->MarkForDestruction();
}
}
///////////////////////////////////////////////////////////////////////////////
// MemoryImageRepresentation
MemoryImageRepresentation::ScopedReadAccess::ScopedReadAccess(
base::PassKey<MemoryImageRepresentation> pass_key,
MemoryImageRepresentation* representation,
SkPixmap pixmap)
: ScopedAccessBase(representation, AccessMode::kRead), pixmap_(pixmap) {}
MemoryImageRepresentation::ScopedReadAccess::~ScopedReadAccess() = default;
std::unique_ptr<MemoryImageRepresentation::ScopedReadAccess>
MemoryImageRepresentation::BeginScopedReadAccess() {
return std::make_unique<ScopedReadAccess>(
base::PassKey<MemoryImageRepresentation>(), this, BeginReadAccess());
}
///////////////////////////////////////////////////////////////////////////////
// RasterImageRepresentation
RasterImageRepresentation::ScopedReadAccess::ScopedReadAccess(
base::PassKey<RasterImageRepresentation> pass_key,
RasterImageRepresentation* representation,
const cc::PaintOpBuffer* paint_op_buffer,
const std::optional<SkColor4f>& clear_color)
: ScopedAccessBase(representation, AccessMode::kRead),
paint_op_buffer_(paint_op_buffer),
clear_color_(clear_color) {}
RasterImageRepresentation::ScopedReadAccess::~ScopedReadAccess() {
representation()->EndReadAccess();
}
RasterImageRepresentation::ScopedWriteAccess::ScopedWriteAccess(
base::PassKey<RasterImageRepresentation> pass_key,
RasterImageRepresentation* representation,
cc::PaintOpBuffer* paint_op_buffer)
: ScopedAccessBase(representation, AccessMode::kWrite),
paint_op_buffer_(paint_op_buffer) {}
RasterImageRepresentation::ScopedWriteAccess::~ScopedWriteAccess() {
representation()->EndWriteAccess(std::move(callback_));
}
std::unique_ptr<RasterImageRepresentation::ScopedReadAccess>
RasterImageRepresentation::BeginScopedReadAccess() {
std::optional<SkColor4f> clear_color;
auto* paint_op_buffer = BeginReadAccess(clear_color);
if (!paint_op_buffer) {
return nullptr;
}
return std::make_unique<ScopedReadAccess>(
base::PassKey<RasterImageRepresentation>(), this, paint_op_buffer,
clear_color);
}
std::unique_ptr<RasterImageRepresentation::ScopedWriteAccess>
RasterImageRepresentation::BeginScopedWriteAccess(
scoped_refptr<SharedContextState> context_state,
int final_msaa_count,
const SkSurfaceProps& surface_props,
const std::optional<SkColor4f>& clear_color,
bool visible) {
return std::make_unique<ScopedWriteAccess>(
base::PassKey<RasterImageRepresentation>(), this,
BeginWriteAccess(std::move(context_state), final_msaa_count,
surface_props, clear_color, visible));
}
///////////////////////////////////////////////////////////////////////////////
// VideoImageRepresentation
VideoImageRepresentation::VideoImageRepresentation(SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker)
: SharedImageRepresentation(manager, backing, tracker) {}
VideoImageRepresentation::~VideoImageRepresentation() = default;
VideoImageRepresentation::ScopedWriteAccess::ScopedWriteAccess(
base::PassKey<VideoImageRepresentation> /* pass_key */,
VideoImageRepresentation* representation)
: ScopedAccessBase(representation, AccessMode::kWrite) {}
VideoImageRepresentation::ScopedWriteAccess::~ScopedWriteAccess() {
representation()->EndWriteAccess();
}
std::unique_ptr<VideoImageRepresentation::ScopedWriteAccess>
VideoImageRepresentation::BeginScopedWriteAccess() {
if (!BeginWriteAccess()) {
return nullptr;
}
return std::make_unique<ScopedWriteAccess>(
base::PassKey<VideoImageRepresentation>(), this);
}
VideoImageRepresentation::ScopedReadAccess::ScopedReadAccess(
base::PassKey<VideoImageRepresentation> /* pass_key */,
VideoImageRepresentation* representation)
: ScopedAccessBase(representation, AccessMode::kWrite) {}
VideoImageRepresentation::ScopedReadAccess::~ScopedReadAccess() {
representation()->EndReadAccess();
}
std::unique_ptr<VideoImageRepresentation::ScopedReadAccess>
VideoImageRepresentation::BeginScopedReadAccess() {
if (!BeginReadAccess()) {
return nullptr;
}
return std::make_unique<ScopedReadAccess>(
base::PassKey<VideoImageRepresentation>(), this);
}
///////////////////////////////////////////////////////////////////////////////
// VulkanImageRepresentation
#if BUILDFLAG(ENABLE_VULKAN)
VulkanImageRepresentation::VulkanImageRepresentation(
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker,
std::unique_ptr<gpu::VulkanImage> vulkan_image,
gpu::VulkanDeviceQueue* vulkan_device_queue,
gpu::VulkanImplementation& vulkan_impl)
: SharedImageRepresentation(manager, backing, tracker),
vulkan_image_(std::move(vulkan_image)),
vulkan_device_queue_(vulkan_device_queue),
vulkan_impl_(vulkan_impl) {}
VulkanImageRepresentation::~VulkanImageRepresentation() {
vulkan_device_queue_->GetFenceHelper()
->EnqueueVulkanObjectCleanupForSubmittedWork<gpu::VulkanImage>(
std::move(vulkan_image_));
}
VulkanImageRepresentation::ScopedAccess::ScopedAccess(
VulkanImageRepresentation* representation,
AccessMode access_mode,
std::vector<VkSemaphore> begin_semaphores,
VkSemaphore end_semaphore)
: ScopedAccessBase(representation, access_mode),
is_read_only_(access_mode == AccessMode::kRead),
begin_semaphores_(begin_semaphores),
end_semaphore_(end_semaphore) {}
VulkanImageRepresentation::ScopedAccess::~ScopedAccess() {
representation()->EndScopedAccess(is_read_only_, end_semaphore_);
auto* fence_helper = representation()->vulkan_device_queue_->GetFenceHelper();
fence_helper->EnqueueSemaphoresCleanupForSubmittedWork(
std::move(begin_semaphores_));
fence_helper->EnqueueSemaphoreCleanupForSubmittedWork(end_semaphore_);
}
gpu::VulkanImage& VulkanImageRepresentation::ScopedAccess::GetVulkanImage() {
return *representation()->vulkan_image_;
}
#endif
} // namespace gpu