blob: df341211752b3726acddc5891c3ed8b779237b4f [file] [log] [blame]
// Copyright 2020 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_gl_image.h"
#include "base/trace_event/memory_dump_manager.h"
#include "build/build_config.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/resource_sizes.h"
#include "gpu/command_buffer/common/shared_image_trace_utils.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/shared_context_state.h"
#include "gpu/command_buffer/service/skia_utils.h"
#include "third_party/skia/include/core/SkPromiseImageTexture.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_fence.h"
#include "ui/gl/gl_gl_api_implementation.h"
#include "ui/gl/trace_util.h"
#if defined(OS_MAC)
#include "gpu/command_buffer/service/shared_image_backing_factory_iosurface.h"
#endif
namespace gpu {
namespace {
size_t EstimatedSize(viz::ResourceFormat format, const gfx::Size& size) {
size_t estimated_size = 0;
viz::ResourceSizes::MaybeSizeInBytes(size, format, &estimated_size);
return estimated_size;
}
using UnpackStateAttribs = SharedImageBackingGLCommon::UnpackStateAttribs;
using ScopedResetAndRestoreUnpackState =
SharedImageBackingGLCommon::ScopedResetAndRestoreUnpackState;
using ScopedRestoreTexture = SharedImageBackingGLCommon::ScopedRestoreTexture;
using InitializeGLTextureParams =
SharedImageBackingGLCommon::InitializeGLTextureParams;
} // namespace
///////////////////////////////////////////////////////////////////////////////
// SharedImageRepresentationGLTextureImpl
// Representation of a SharedImageBackingGLTexture as a GL Texture.
SharedImageRepresentationGLTextureImpl::SharedImageRepresentationGLTextureImpl(
SharedImageManager* manager,
SharedImageBacking* backing,
SharedImageRepresentationGLTextureClient* client,
MemoryTypeTracker* tracker,
gles2::Texture* texture)
: SharedImageRepresentationGLTexture(manager, backing, tracker),
client_(client),
texture_(texture) {}
SharedImageRepresentationGLTextureImpl::
~SharedImageRepresentationGLTextureImpl() {
texture_ = nullptr;
if (client_)
client_->SharedImageRepresentationGLTextureRelease(has_context());
}
gles2::Texture* SharedImageRepresentationGLTextureImpl::GetTexture() {
return texture_;
}
bool SharedImageRepresentationGLTextureImpl::BeginAccess(GLenum mode) {
if (client_ && mode != GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM)
return client_->SharedImageRepresentationGLTextureBeginAccess();
return true;
}
void SharedImageRepresentationGLTextureImpl::EndAccess() {
if (client_)
return client_->SharedImageRepresentationGLTextureEndAccess();
}
///////////////////////////////////////////////////////////////////////////////
// SharedImageRepresentationGLTexturePassthroughImpl
SharedImageRepresentationGLTexturePassthroughImpl::
SharedImageRepresentationGLTexturePassthroughImpl(
SharedImageManager* manager,
SharedImageBacking* backing,
SharedImageRepresentationGLTextureClient* client,
MemoryTypeTracker* tracker,
scoped_refptr<gles2::TexturePassthrough> texture_passthrough)
: SharedImageRepresentationGLTexturePassthrough(manager, backing, tracker),
client_(client),
texture_passthrough_(std::move(texture_passthrough)) {}
SharedImageRepresentationGLTexturePassthroughImpl::
~SharedImageRepresentationGLTexturePassthroughImpl() {
texture_passthrough_.reset();
if (client_)
client_->SharedImageRepresentationGLTextureRelease(has_context());
}
const scoped_refptr<gles2::TexturePassthrough>&
SharedImageRepresentationGLTexturePassthroughImpl::GetTexturePassthrough() {
return texture_passthrough_;
}
bool SharedImageRepresentationGLTexturePassthroughImpl::BeginAccess(
GLenum mode) {
if (client_ && mode != GL_SHARED_IMAGE_ACCESS_MODE_OVERLAY_CHROMIUM)
return client_->SharedImageRepresentationGLTextureBeginAccess();
return true;
}
void SharedImageRepresentationGLTexturePassthroughImpl::EndAccess() {
if (client_)
return client_->SharedImageRepresentationGLTextureEndAccess();
}
///////////////////////////////////////////////////////////////////////////////
// SharedImageRepresentationSkiaImpl
SharedImageRepresentationSkiaImpl::SharedImageRepresentationSkiaImpl(
SharedImageManager* manager,
SharedImageBacking* backing,
SharedImageRepresentationGLTextureClient* client,
scoped_refptr<SharedContextState> context_state,
sk_sp<SkPromiseImageTexture> promise_texture,
MemoryTypeTracker* tracker)
: SharedImageRepresentationSkia(manager, backing, tracker),
client_(client),
context_state_(std::move(context_state)),
promise_texture_(promise_texture) {
DCHECK(promise_texture_);
#if DCHECK_IS_ON()
if (context_state_->GrContextIsGL())
context_ = gl::GLContext::GetCurrent();
#endif
}
SharedImageRepresentationSkiaImpl::~SharedImageRepresentationSkiaImpl() {
if (write_surface_) {
DLOG(ERROR) << "SharedImageRepresentationSkia was destroyed while still "
<< "open for write access.";
}
promise_texture_.reset();
if (client_) {
DCHECK(context_state_->GrContextIsGL());
client_->SharedImageRepresentationGLTextureRelease(has_context());
}
}
sk_sp<SkSurface> SharedImageRepresentationSkiaImpl::BeginWriteAccess(
int final_msaa_count,
const SkSurfaceProps& surface_props,
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores) {
CheckContext();
if (client_) {
DCHECK(context_state_->GrContextIsGL());
if (!client_->SharedImageRepresentationGLTextureBeginAccess())
return nullptr;
}
if (write_surface_)
return nullptr;
if (!promise_texture_)
return nullptr;
SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
/*gpu_compositing=*/true, format());
auto surface = SkSurface::MakeFromBackendTexture(
context_state_->gr_context(), promise_texture_->backendTexture(),
surface_origin(), final_msaa_count, sk_color_type,
backing()->color_space().ToSkColorSpace(), &surface_props);
write_surface_ = surface.get();
return surface;
}
void SharedImageRepresentationSkiaImpl::EndWriteAccess(
sk_sp<SkSurface> surface) {
DCHECK_EQ(surface.get(), write_surface_);
DCHECK(surface->unique());
CheckContext();
// TODO(ericrk): Keep the surface around for re-use.
write_surface_ = nullptr;
if (client_)
client_->SharedImageRepresentationGLTextureEndAccess();
}
sk_sp<SkPromiseImageTexture> SharedImageRepresentationSkiaImpl::BeginReadAccess(
std::vector<GrBackendSemaphore>* begin_semaphores,
std::vector<GrBackendSemaphore>* end_semaphores) {
CheckContext();
if (client_) {
DCHECK(context_state_->GrContextIsGL());
if (!client_->SharedImageRepresentationGLTextureBeginAccess())
return nullptr;
}
return promise_texture_;
}
void SharedImageRepresentationSkiaImpl::EndReadAccess() {
if (client_)
client_->SharedImageRepresentationGLTextureEndAccess();
}
bool SharedImageRepresentationSkiaImpl::SupportsMultipleConcurrentReadAccess() {
return true;
}
void SharedImageRepresentationSkiaImpl::CheckContext() {
#if DCHECK_IS_ON()
if (context_)
DCHECK(gl::GLContext::GetCurrent() == context_);
#endif
}
///////////////////////////////////////////////////////////////////////////////
// SharedImageRepresentationOverlayImpl
SharedImageRepresentationOverlayImpl::SharedImageRepresentationOverlayImpl(
SharedImageManager* manager,
SharedImageBacking* backing,
MemoryTypeTracker* tracker,
scoped_refptr<gl::GLImage> gl_image)
: SharedImageRepresentationOverlay(manager, backing, tracker),
gl_image_(gl_image) {}
SharedImageRepresentationOverlayImpl::~SharedImageRepresentationOverlayImpl() =
default;
bool SharedImageRepresentationOverlayImpl::BeginReadAccess() {
return true;
}
void SharedImageRepresentationOverlayImpl::EndReadAccess() {}
gl::GLImage* SharedImageRepresentationOverlayImpl::GetGLImage() {
return gl_image_.get();
}
///////////////////////////////////////////////////////////////////////////////
// SharedImageBackingGLImage
SharedImageBackingGLImage::SharedImageBackingGLImage(
scoped_refptr<gl::GLImage> image,
const Mailbox& mailbox,
viz::ResourceFormat format,
const gfx::Size& size,
const gfx::ColorSpace& color_space,
GrSurfaceOrigin surface_origin,
SkAlphaType alpha_type,
uint32_t usage,
const InitializeGLTextureParams& params,
const UnpackStateAttribs& attribs,
bool is_passthrough)
: SharedImageBacking(mailbox,
format,
size,
color_space,
surface_origin,
alpha_type,
usage,
EstimatedSize(format, size),
false /* is_thread_safe */),
image_(image),
gl_params_(params),
gl_unpack_attribs_(attribs),
is_passthrough_(is_passthrough),
cleared_rect_(params.is_cleared ? gfx::Rect(size) : gfx::Rect()),
weak_factory_(this) {
DCHECK(image_);
}
SharedImageBackingGLImage::~SharedImageBackingGLImage() {
if (gl_texture_retained_for_legacy_mailbox_)
ReleaseGLTexture(have_context());
DCHECK_EQ(gl_texture_retain_count_, 0u);
}
void SharedImageBackingGLImage::RetainGLTexture() {
gl_texture_retain_count_ += 1;
if (gl_texture_retain_count_ > 1)
return;
// Allocate the GL texture.
SharedImageBackingGLCommon::MakeTextureAndSetParameters(
gl_params_.target, 0 /* service_id */,
gl_params_.framebuffer_attachment_angle,
is_passthrough_ ? &passthrough_texture_ : nullptr,
is_passthrough_ ? nullptr : &texture_);
// Set the GLImage to be initially unbound from the GL texture.
image_bind_or_copy_needed_ = true;
if (is_passthrough_) {
passthrough_texture_->SetEstimatedSize(EstimatedSize(format(), size()));
passthrough_texture_->SetLevelImage(gl_params_.target, 0, image_.get());
passthrough_texture_->set_is_bind_pending(true);
} else {
texture_->SetLevelInfo(gl_params_.target, 0, gl_params_.internal_format,
size().width(), size().height(), 1, 0,
gl_params_.format, gl_params_.type, cleared_rect_);
texture_->SetLevelImage(gl_params_.target, 0, image_.get(),
gles2::Texture::UNBOUND);
texture_->SetImmutable(true, false /* has_immutable_storage */);
}
}
void SharedImageBackingGLImage::ReleaseGLTexture(bool have_context) {
DCHECK_GT(gl_texture_retain_count_, 0u);
gl_texture_retain_count_ -= 1;
if (gl_texture_retain_count_ > 0)
return;
// If the cached promise texture is referencing the GL texture, then it needs
// to be deleted, too.
if (cached_promise_texture_) {
if (cached_promise_texture_->backendTexture().backend() ==
GrBackendApi::kOpenGL) {
cached_promise_texture_.reset();
}
}
if (rgb_emulation_texture_) {
rgb_emulation_texture_->RemoveLightweightRef(have_context);
rgb_emulation_texture_ = nullptr;
}
if (IsPassthrough()) {
if (passthrough_texture_) {
if (!have_context)
passthrough_texture_->MarkContextLost();
passthrough_texture_.reset();
}
} else {
if (texture_) {
cleared_rect_ = texture_->GetLevelClearedRect(texture_->target(), 0);
texture_->RemoveLightweightRef(have_context);
texture_ = nullptr;
}
}
}
GLenum SharedImageBackingGLImage::GetGLTarget() const {
return gl_params_.target;
}
GLuint SharedImageBackingGLImage::GetGLServiceId() const {
if (texture_)
return texture_->service_id();
if (passthrough_texture_)
return passthrough_texture_->service_id();
return 0;
}
scoped_refptr<gfx::NativePixmap> SharedImageBackingGLImage::GetNativePixmap() {
return image_->GetNativePixmap();
}
void SharedImageBackingGLImage::OnMemoryDump(
const std::string& dump_name,
base::trace_event::MemoryAllocatorDump* dump,
base::trace_event::ProcessMemoryDump* pmd,
uint64_t client_tracing_id) {
// Add a |service_guid| which expresses shared ownership between the
// various GPU dumps.
auto client_guid = GetSharedImageGUIDForTracing(mailbox());
if (auto service_id = GetGLServiceId()) {
auto service_guid = gl::GetGLTextureServiceGUIDForTracing(GetGLServiceId());
pmd->CreateSharedGlobalAllocatorDump(service_guid);
// TODO(piman): coalesce constant with TextureManager::DumpTextureRef.
int importance = 2; // This client always owns the ref.
pmd->AddOwnershipEdge(client_guid, service_guid, importance);
}
image_->OnMemoryDump(pmd, client_tracing_id, dump_name);
}
gfx::Rect SharedImageBackingGLImage::ClearedRect() const {
if (texture_)
return texture_->GetLevelClearedRect(texture_->target(), 0);
return cleared_rect_;
}
void SharedImageBackingGLImage::SetClearedRect(const gfx::Rect& cleared_rect) {
if (texture_)
texture_->SetLevelClearedRect(texture_->target(), 0, cleared_rect);
else
cleared_rect_ = cleared_rect;
}
bool SharedImageBackingGLImage::ProduceLegacyMailbox(
MailboxManager* mailbox_manager) {
if (!gl_texture_retained_for_legacy_mailbox_) {
RetainGLTexture();
gl_texture_retained_for_legacy_mailbox_ = true;
}
if (IsPassthrough())
mailbox_manager->ProduceTexture(mailbox(), passthrough_texture_.get());
else
mailbox_manager->ProduceTexture(mailbox(), texture_);
return true;
}
std::unique_ptr<SharedImageRepresentationGLTexture>
SharedImageBackingGLImage::ProduceGLTexture(SharedImageManager* manager,
MemoryTypeTracker* tracker) {
// The corresponding release will be done when the returned representation is
// destroyed, in SharedImageRepresentationGLTextureRelease.
RetainGLTexture();
DCHECK(texture_);
return std::make_unique<SharedImageRepresentationGLTextureImpl>(
manager, this, this, tracker, texture_);
}
std::unique_ptr<SharedImageRepresentationGLTexturePassthrough>
SharedImageBackingGLImage::ProduceGLTexturePassthrough(
SharedImageManager* manager,
MemoryTypeTracker* tracker) {
// The corresponding release will be done when the returned representation is
// destroyed, in SharedImageRepresentationGLTextureRelease.
RetainGLTexture();
DCHECK(passthrough_texture_);
return std::make_unique<SharedImageRepresentationGLTexturePassthroughImpl>(
manager, this, this, tracker, passthrough_texture_);
}
std::unique_ptr<SharedImageRepresentationOverlay>
SharedImageBackingGLImage::ProduceOverlay(SharedImageManager* manager,
MemoryTypeTracker* tracker) {
#if defined(OS_MAC)
return std::make_unique<SharedImageRepresentationOverlayImpl>(
manager, this, tracker, image_);
#else // defined(OS_MAC)
return SharedImageBacking::ProduceOverlay(manager, tracker);
#endif // !defined(OS_MAC)
}
std::unique_ptr<SharedImageRepresentationDawn>
SharedImageBackingGLImage::ProduceDawn(SharedImageManager* manager,
MemoryTypeTracker* tracker,
WGPUDevice device) {
#if defined(OS_MAC)
auto result = SharedImageBackingFactoryIOSurface::ProduceDawn(
manager, this, tracker, device, image_);
if (result)
return result;
#endif // defined(OS_MAC)
if (!factory()) {
DLOG(ERROR) << "No SharedImageFactory to create a dawn representation.";
return nullptr;
}
return SharedImageBackingGLCommon::ProduceDawnCommon(
factory(), manager, tracker, device, this, IsPassthrough());
}
std::unique_ptr<SharedImageRepresentationSkia>
SharedImageBackingGLImage::ProduceSkia(
SharedImageManager* manager,
MemoryTypeTracker* tracker,
scoped_refptr<SharedContextState> context_state) {
SharedImageRepresentationGLTextureClient* gl_client = nullptr;
if (context_state->GrContextIsGL()) {
// The corresponding release will be done when the returned representation
// is destroyed, in SharedImageRepresentationGLTextureRelease.
RetainGLTexture();
gl_client = this;
}
if (!cached_promise_texture_) {
if (context_state->GrContextIsMetal()) {
#if defined(OS_MAC)
cached_promise_texture_ =
SharedImageBackingFactoryIOSurface::ProduceSkiaPromiseTextureMetal(
this, context_state, image_);
DCHECK(cached_promise_texture_);
#endif
} else {
GrBackendTexture backend_texture;
GetGrBackendTexture(context_state->feature_info(), GetGLTarget(), size(),
GetGLServiceId(), format(), &backend_texture);
cached_promise_texture_ = SkPromiseImageTexture::Make(backend_texture);
}
}
return std::make_unique<SharedImageRepresentationSkiaImpl>(
manager, this, gl_client, std::move(context_state),
cached_promise_texture_, tracker);
}
std::unique_ptr<SharedImageRepresentationGLTexture>
SharedImageBackingGLImage::ProduceRGBEmulationGLTexture(
SharedImageManager* manager,
MemoryTypeTracker* tracker) {
if (IsPassthrough())
return nullptr;
RetainGLTexture();
if (!rgb_emulation_texture_) {
const GLenum target = GetGLTarget();
gl::GLApi* api = gl::g_current_gl_context;
ScopedRestoreTexture scoped_restore(api, target);
// Set to false as this code path is only used on Mac.
const bool framebuffer_attachment_angle = false;
SharedImageBackingGLCommon::MakeTextureAndSetParameters(
target, 0 /* service_id */, framebuffer_attachment_angle, nullptr,
&rgb_emulation_texture_);
api->glBindTextureFn(target, rgb_emulation_texture_->service_id());
gles2::Texture::ImageState image_state = gles2::Texture::BOUND;
gl::GLImage* image = texture_->GetLevelImage(target, 0, &image_state);
DCHECK_EQ(image, image_.get());
DCHECK(image->ShouldBindOrCopy() == gl::GLImage::BIND);
const GLenum internal_format = GL_RGB;
if (!image->BindTexImageWithInternalformat(target, internal_format)) {
LOG(ERROR) << "Failed to bind image to rgb texture.";
rgb_emulation_texture_->RemoveLightweightRef(true /* have_context */);
rgb_emulation_texture_ = nullptr;
ReleaseGLTexture(true /* has_context */);
return nullptr;
}
GLenum format =
gles2::TextureManager::ExtractFormatFromStorageFormat(internal_format);
GLenum type =
gles2::TextureManager::ExtractTypeFromStorageFormat(internal_format);
const gles2::Texture::LevelInfo* info = texture_->GetLevelInfo(target, 0);
rgb_emulation_texture_->SetLevelInfo(target, 0, internal_format,
info->width, info->height, 1, 0,
format, type, info->cleared_rect);
rgb_emulation_texture_->SetLevelImage(target, 0, image, image_state);
rgb_emulation_texture_->SetImmutable(true, false);
}
return std::make_unique<SharedImageRepresentationGLTextureImpl>(
manager, this, this, tracker, rgb_emulation_texture_);
}
void SharedImageBackingGLImage::Update(
std::unique_ptr<gfx::GpuFence> in_fence) {
if (in_fence) {
// TODO(dcastagna): Don't wait for the fence if the SharedImage is going
// to be scanned out as an HW overlay. Currently we don't know that at
// this point and we always bind the image, therefore we need to wait for
// the fence.
std::unique_ptr<gl::GLFence> egl_fence =
gl::GLFence::CreateFromGpuFence(*in_fence.get());
egl_fence->ServerWait();
}
image_bind_or_copy_needed_ = true;
}
bool SharedImageBackingGLImage::
SharedImageRepresentationGLTextureBeginAccess() {
return BindOrCopyImageIfNeeded();
}
void SharedImageBackingGLImage::SharedImageRepresentationGLTextureEndAccess() {
#if defined(OS_MAC)
// If this image could potentially be shared with Metal via WebGPU, then flush
// the GL context to ensure Metal will see it.
if (usage() & SHARED_IMAGE_USAGE_WEBGPU) {
gl::GLApi* api = gl::g_current_gl_context;
api->glFlushFn();
}
#endif
}
void SharedImageBackingGLImage::SharedImageRepresentationGLTextureRelease(
bool has_context) {
ReleaseGLTexture(has_context);
}
bool SharedImageBackingGLImage::BindOrCopyImageIfNeeded() {
// This is called by code that has retained the GL texture.
DCHECK(texture_ || passthrough_texture_);
if (!image_bind_or_copy_needed_)
return true;
const GLenum target = GetGLTarget();
gl::GLApi* api = gl::g_current_gl_context;
ScopedRestoreTexture scoped_restore(api, target);
api->glBindTextureFn(target, GetGLServiceId());
// Un-bind the GLImage from the texture if it is currently bound.
if (image_->ShouldBindOrCopy() == gl::GLImage::BIND) {
bool is_bound = false;
if (IsPassthrough()) {
is_bound = !passthrough_texture_->is_bind_pending();
} else {
gles2::Texture::ImageState old_state = gles2::Texture::UNBOUND;
texture_->GetLevelImage(target, 0, &old_state);
is_bound = old_state == gles2::Texture::BOUND;
}
if (is_bound)
image_->ReleaseTexImage(target);
}
// Bind or copy the GLImage to the texture.
gles2::Texture::ImageState new_state = gles2::Texture::UNBOUND;
if (image_->ShouldBindOrCopy() == gl::GLImage::BIND) {
if (gl_params_.is_rgb_emulation) {
if (!image_->BindTexImageWithInternalformat(target, GL_RGB)) {
LOG(ERROR) << "Failed to bind GLImage to RGB target";
return false;
}
} else {
if (!image_->BindTexImage(target)) {
LOG(ERROR) << "Failed to bind GLImage to target";
return false;
}
}
new_state = gles2::Texture::BOUND;
} else {
ScopedResetAndRestoreUnpackState scoped_unpack_state(api,
gl_unpack_attribs_,
/*upload=*/true);
if (!image_->CopyTexImage(target)) {
LOG(ERROR) << "Failed to copy GLImage to target";
return false;
}
new_state = gles2::Texture::COPIED;
}
if (IsPassthrough()) {
passthrough_texture_->set_is_bind_pending(new_state ==
gles2::Texture::UNBOUND);
} else {
texture_->SetLevelImage(target, 0, image_.get(), new_state);
}
image_bind_or_copy_needed_ = false;
return true;
}
void SharedImageBackingGLImage::InitializePixels(GLenum format,
GLenum type,
const uint8_t* data) {
DCHECK_EQ(image_->ShouldBindOrCopy(), gl::GLImage::BIND);
#if defined(OS_MAC)
if (SharedImageBackingFactoryIOSurface::InitializePixels(this, image_, data))
return;
#else
RetainGLTexture();
BindOrCopyImageIfNeeded();
const GLenum target = GetGLTarget();
gl::GLApi* api = gl::g_current_gl_context;
ScopedRestoreTexture scoped_restore(api, target);
api->glBindTextureFn(target, GetGLServiceId());
ScopedResetAndRestoreUnpackState scoped_unpack_state(
api, gl_unpack_attribs_, true /* uploading_data */);
api->glTexSubImage2DFn(target, 0, 0, 0, size().width(), size().height(),
format, type, data);
ReleaseGLTexture(true /* have_context */);
#endif
}
} // namespace gpu