blob: 30fca12b48a0fafe0d1a953669d3b84976bb4e6c [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/config/gpu_driver_bug_workaround_type.h"
#include "third_party/blink/renderer/platform/graphics/accelerated_static_bitmap_image.h"
#include "third_party/blink/renderer/platform/graphics/gpu/drawing_buffer.h"
#include "third_party/blink/renderer/platform/graphics/gpu/extensions_3d_util.h"
#include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h"
#include "third_party/blink/renderer/platform/graphics/unaccelerated_static_bitmap_image.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/skia/include/core/SkSurface.h"
namespace blink {
// Large parts of this file have been shamelessly borrowed from
// platform/graphics/gpu/DrawingBuffer.cpp and simplified where applicable due
// to the more narrow use case. It may make sense in the future to abstract out
// some of the common bits into a base class?
XRWebGLDrawingBuffer::ColorBuffer::ColorBuffer(
base::WeakPtr<XRWebGLDrawingBuffer> drawing_buffer,
const gfx::Size& size,
const gpu::Mailbox& mailbox,
GLuint texture_id)
: owning_thread_ref(base::PlatformThread::CurrentRef()),
drawing_buffer(std::move(drawing_buffer)),
size(size),
texture_id(texture_id),
mailbox(mailbox) {}
XRWebGLDrawingBuffer::ColorBuffer::~ColorBuffer() {
if (base::PlatformThread::CurrentRef() != owning_thread_ref ||
!drawing_buffer) {
// If the context has been destroyed no cleanup is necessary since all
// resources below are automatically destroyed. Note that if a ColorBuffer
// is being destroyed on a different thread, it implies that the owning
// thread was destroyed which means the associated context was also
// destroyed.
return;
}
gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL();
if (receive_sync_token.HasData())
gl->WaitSyncTokenCHROMIUM(receive_sync_token.GetConstData());
gl->DeleteTextures(1, &texture_id);
gpu::SyncToken sync_token;
gl->GenUnverifiedSyncTokenCHROMIUM(sync_token.GetData());
auto* sii = drawing_buffer->drawing_buffer_->ContextProvider()
->SharedImageInterface();
sii->DestroySharedImage(sync_token, mailbox);
}
scoped_refptr<XRWebGLDrawingBuffer> XRWebGLDrawingBuffer::Create(
DrawingBuffer* drawing_buffer,
GLuint framebuffer,
const gfx::Size& size,
bool want_alpha_channel,
bool want_depth_buffer,
bool want_stencil_buffer,
bool want_antialiasing) {
DCHECK(drawing_buffer);
// Don't proceeed if the context is already lost.
if (drawing_buffer->destroyed())
return nullptr;
gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL();
std::unique_ptr<Extensions3DUtil> extensions_util =
Extensions3DUtil::Create(gl);
if (!extensions_util->IsValid()) {
return nullptr;
}
DCHECK(extensions_util->SupportsExtension("GL_OES_packed_depth_stencil"));
extensions_util->EnsureExtensionEnabled("GL_OES_packed_depth_stencil");
bool multisample_supported =
want_antialiasing &&
(extensions_util->SupportsExtension(
"GL_CHROMIUM_framebuffer_multisample") ||
extensions_util->SupportsExtension(
"GL_EXT_multisampled_render_to_texture")) &&
extensions_util->SupportsExtension("GL_OES_rgb8_rgba8");
if (multisample_supported) {
extensions_util->EnsureExtensionEnabled("GL_OES_rgb8_rgba8");
if (extensions_util->SupportsExtension(
"GL_CHROMIUM_framebuffer_multisample")) {
extensions_util->EnsureExtensionEnabled(
"GL_CHROMIUM_framebuffer_multisample");
} else {
extensions_util->EnsureExtensionEnabled(
"GL_EXT_multisampled_render_to_texture");
}
}
bool discard_framebuffer_supported =
extensions_util->SupportsExtension("GL_EXT_discard_framebuffer");
if (discard_framebuffer_supported)
extensions_util->EnsureExtensionEnabled("GL_EXT_discard_framebuffer");
scoped_refptr<XRWebGLDrawingBuffer> xr_drawing_buffer =
base::AdoptRef(new XRWebGLDrawingBuffer(
drawing_buffer, framebuffer, discard_framebuffer_supported,
want_alpha_channel, want_depth_buffer, want_stencil_buffer));
if (!xr_drawing_buffer->Initialize(size, multisample_supported)) {
DLOG(ERROR) << "XRWebGLDrawingBuffer Initialization Failed";
return nullptr;
}
return xr_drawing_buffer;
}
XRWebGLDrawingBuffer::XRWebGLDrawingBuffer(DrawingBuffer* drawing_buffer,
GLuint framebuffer,
bool discard_framebuffer_supported,
bool want_alpha_channel,
bool want_depth_buffer,
bool want_stencil_buffer)
: drawing_buffer_(drawing_buffer),
framebuffer_(framebuffer),
discard_framebuffer_supported_(discard_framebuffer_supported),
depth_(want_depth_buffer),
stencil_(want_stencil_buffer),
alpha_(want_alpha_channel),
weak_factory_(this) {}
void XRWebGLDrawingBuffer::BeginDestruction() {
if (back_color_buffer_) {
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
gl->EndSharedImageAccessDirectCHROMIUM(back_color_buffer_->texture_id);
back_color_buffer_ = nullptr;
}
front_color_buffer_ = nullptr;
recycled_color_buffer_queue_.clear();
}
// TODO(bajones): The GL resources allocated in this function are leaking. Add
// a way to clean up the buffers when the layer is GCed or the session ends.
bool XRWebGLDrawingBuffer::Initialize(const gfx::Size& size,
bool use_multisampling) {
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
std::unique_ptr<Extensions3DUtil> extensions_util =
Extensions3DUtil::Create(gl);
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size_);
DVLOG(2) << __FUNCTION__ << ": max_texture_size_=" << max_texture_size_;
// Check context capabilities
int max_sample_count = 0;
anti_aliasing_mode_ = kNone;
if (use_multisampling) {
gl->GetIntegerv(GL_MAX_SAMPLES_ANGLE, &max_sample_count);
anti_aliasing_mode_ = kMSAAExplicitResolve;
if (extensions_util->SupportsExtension(
"GL_EXT_multisampled_render_to_texture")) {
anti_aliasing_mode_ = kMSAAImplicitResolve;
}
}
DVLOG(2) << __FUNCTION__
<< ": anti_aliasing_mode_=" << static_cast<int>(anti_aliasing_mode_);
#if BUILDFLAG(IS_ANDROID)
// On Android devices use a smaller number of samples to provide more breathing
// room for fill-rate-bound applications.
sample_count_ = std::min(2, max_sample_count);
#else
sample_count_ = std::min(4, max_sample_count);
#endif
Resize(size);
// It's possible that the drawing buffer allocation provokes a context loss,
// so check again just in case.
if (gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
return false;
}
return true;
}
gpu::gles2::GLES2Interface* XRWebGLDrawingBuffer::ContextGL() {
return drawing_buffer_->ContextGL();
}
bool XRWebGLDrawingBuffer::ContextLost() {
return drawing_buffer_->destroyed();
}
gfx::Size XRWebGLDrawingBuffer::AdjustSize(const gfx::Size& new_size) {
// Ensure we always have at least a 1x1 buffer
float width = std::max(1, new_size.width());
float height = std::max(1, new_size.height());
float adjusted_scale =
std::min(static_cast<float>(max_texture_size_) / width,
static_cast<float>(max_texture_size_) / height);
// Clamp if the desired size is greater than the maximum texture size for the
// device. Scale both dimensions proportionally so that we avoid stretching.
if (adjusted_scale < 1.0f) {
width *= adjusted_scale;
height *= adjusted_scale;
}
return gfx::Size(width, height);
}
void XRWebGLDrawingBuffer::UseSharedBuffer(
const gpu::MailboxHolder& buffer_mailbox_holder) {
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
// Ensure that the mailbox holder is ready to use, the following actions need
// to be sequenced after setup steps that were done through a different
// process's GPU command buffer context.
//
// TODO(https://crbug.com/1111526): Investigate handling context loss and
// recovery for cases where these assumptions may not be accurate.
DCHECK(buffer_mailbox_holder.sync_token.HasData());
DCHECK(!buffer_mailbox_holder.mailbox.IsZero());
DVLOG(3) << __func__
<< ": mailbox=" << buffer_mailbox_holder.mailbox.ToDebugString()
<< ", SyncToken="
<< buffer_mailbox_holder.sync_token.ToDebugString();
gl->WaitSyncTokenCHROMIUM(buffer_mailbox_holder.sync_token.GetConstData());
// Create a texture backed by the shared buffer image.
DCHECK(!shared_buffer_texture_id_);
DCHECK(buffer_mailbox_holder.mailbox.IsSharedImage());
shared_buffer_texture_id_ = gl->CreateAndTexStorage2DSharedImageCHROMIUM(
buffer_mailbox_holder.mailbox.name);
gl->BeginSharedImageAccessDirectCHROMIUM(
shared_buffer_texture_id_,
GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
if (WantExplicitResolve()) {
// Bind the shared texture to the destination framebuffer of
// the explicit resolve step.
if (!resolved_framebuffer_) {
gl->GenFramebuffers(1, &resolved_framebuffer_);
}
gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
} else {
// Bind the shared texture directly to the drawing framebuffer.
gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
}
if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
gl->FramebufferTexture2DMultisampleEXT(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
shared_buffer_texture_id_, 0, sample_count_);
} else {
// Explicit resolve, screen space antialiasing, or no antialiasing.
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, shared_buffer_texture_id_, 0);
}
if (!framebuffer_complete_checked_for_sharedbuffer_) {
DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
GL_FRAMEBUFFER_COMPLETE);
framebuffer_complete_checked_for_sharedbuffer_ = true;
}
if (WantExplicitResolve()) {
// Bind the drawing framebuffer if it wasn't bound previously.
gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
}
ClearBoundFramebuffer();
DrawingBuffer::Client* client = drawing_buffer_->client();
if (!client)
return;
client->DrawingBufferClientRestoreFramebufferBinding();
}
void XRWebGLDrawingBuffer::DoneWithSharedBuffer() {
DVLOG(3) << __FUNCTION__;
BindAndResolveDestinationFramebuffer();
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
// Discard the depth and stencil attachments since we're done with them.
// Don't discard the color buffer, we do need this rendered into the
// shared buffer.
if (discard_framebuffer_supported_) {
const GLenum kAttachments[3] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT};
gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 2, kAttachments);
}
// Always bind to the default framebuffer as a hint to the GPU to start
// rendering now.
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
// Done with the texture created by CreateAndTexStorage2DSharedImageCHROMIUM
// finish accessing and delete it.
DCHECK(shared_buffer_texture_id_);
gl->EndSharedImageAccessDirectCHROMIUM(shared_buffer_texture_id_);
gl->DeleteTextures(1, &shared_buffer_texture_id_);
shared_buffer_texture_id_ = 0;
DrawingBuffer::Client* client = drawing_buffer_->client();
if (!client)
return;
client->DrawingBufferClientRestoreFramebufferBinding();
}
void XRWebGLDrawingBuffer::ClearBoundFramebuffer() {
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
GLbitfield clear_bits = GL_COLOR_BUFFER_BIT;
gl->ColorMask(true, true, true, true);
gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
if (depth_) {
clear_bits |= GL_DEPTH_BUFFER_BIT;
gl->DepthMask(true);
gl->ClearDepthf(1.0f);
}
if (stencil_) {
clear_bits |= GL_STENCIL_BUFFER_BIT;
gl->StencilMaskSeparate(GL_FRONT, true);
gl->ClearStencil(0);
}
gl->Disable(GL_SCISSOR_TEST);
gl->Clear(clear_bits);
DrawingBuffer::Client* client = drawing_buffer_->client();
if (!client)
return;
client->DrawingBufferClientRestoreScissorTest();
client->DrawingBufferClientRestoreMaskAndClearValues();
}
void XRWebGLDrawingBuffer::Resize(const gfx::Size& new_size) {
gfx::Size adjusted_size = AdjustSize(new_size);
if (adjusted_size == size_)
return;
// Don't attempt to resize if the context is lost.
if (ContextLost())
return;
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
size_ = adjusted_size;
// Free all mailboxes, because they are now of the wrong size. Only the
// first call in this loop has any effect.
recycled_color_buffer_queue_.clear();
gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
// Provide a depth and/or stencil buffer if requested.
if (depth_ || stencil_) {
if (depth_stencil_buffer_) {
gl->DeleteRenderbuffers(1, &depth_stencil_buffer_);
depth_stencil_buffer_ = 0;
}
gl->GenRenderbuffers(1, &depth_stencil_buffer_);
gl->BindRenderbuffer(GL_RENDERBUFFER, depth_stencil_buffer_);
if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
gl->RenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, sample_count_,
GL_DEPTH24_STENCIL8_OES,
size_.width(), size_.height());
} else if (anti_aliasing_mode_ == kMSAAExplicitResolve) {
gl->RenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, sample_count_,
GL_DEPTH24_STENCIL8_OES,
size_.width(), size_.height());
} else {
gl->RenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES,
size_.width(), size_.height());
}
gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, depth_stencil_buffer_);
}
if (WantExplicitResolve()) {
// If we're doing an explicit multisample resolve use the main framebuffer
// as the multisample target and resolve into resolved_fbo_ when needed.
GLenum multisample_format = alpha_ ? GL_RGBA8_OES : GL_RGB8_OES;
if (multisample_renderbuffer_) {
gl->DeleteRenderbuffers(1, &multisample_renderbuffer_);
multisample_renderbuffer_ = 0;
}
gl->GenRenderbuffers(1, &multisample_renderbuffer_);
gl->BindRenderbuffer(GL_RENDERBUFFER, multisample_renderbuffer_);
gl->RenderbufferStorageMultisampleCHROMIUM(GL_RENDERBUFFER, sample_count_,
multisample_format,
size_.width(), size_.height());
gl->FramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, multisample_renderbuffer_);
// Now bind the resolve target framebuffer to attach the color textures to.
if (!resolved_framebuffer_) {
gl->GenFramebuffers(1, &resolved_framebuffer_);
}
gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
}
if (back_color_buffer_) {
gl->EndSharedImageAccessDirectCHROMIUM(back_color_buffer_->texture_id);
}
back_color_buffer_ = CreateColorBuffer();
front_color_buffer_ = nullptr;
gl->BeginSharedImageAccessDirectCHROMIUM(
back_color_buffer_->texture_id,
GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
gl->FramebufferTexture2DMultisampleEXT(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
back_color_buffer_->texture_id, 0, sample_count_);
} else {
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, back_color_buffer_->texture_id, 0);
}
if (!framebuffer_complete_checked_for_resize_) {
DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
GL_FRAMEBUFFER_COMPLETE);
framebuffer_complete_checked_for_resize_ = true;
}
DrawingBuffer::Client* client = drawing_buffer_->client();
client->DrawingBufferClientRestoreRenderbufferBinding();
client->DrawingBufferClientRestoreFramebufferBinding();
}
scoped_refptr<XRWebGLDrawingBuffer::ColorBuffer>
XRWebGLDrawingBuffer::CreateColorBuffer() {
auto* sii = drawing_buffer_->ContextProvider()->SharedImageInterface();
uint32_t usage = gpu::SHARED_IMAGE_USAGE_DISPLAY_READ |
gpu::SHARED_IMAGE_USAGE_GLES2 |
gpu::SHARED_IMAGE_USAGE_GLES2_FRAMEBUFFER_HINT;
gpu::Mailbox mailbox = sii->CreateSharedImage(
alpha_ ? viz::RGBA_8888 : viz::RGBX_8888, size_, gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin, kPremul_SkAlphaType, usage,
gpu::kNullSurfaceHandle);
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
gl->WaitSyncTokenCHROMIUM(sii->GenUnverifiedSyncToken().GetConstData());
// The shared image is imported into a texture on the GL context. We take a
// read/write access scope whenever the color buffer is used as the back
// buffer.
GLuint texture_id =
gl->CreateAndTexStorage2DSharedImageCHROMIUM(mailbox.name);
DrawingBuffer::Client* client = drawing_buffer_->client();
client->DrawingBufferClientRestoreTexture2DBinding();
return base::MakeRefCounted<ColorBuffer>(weak_factory_.GetWeakPtr(), size_,
mailbox, texture_id);
}
scoped_refptr<XRWebGLDrawingBuffer::ColorBuffer>
XRWebGLDrawingBuffer::CreateOrRecycleColorBuffer() {
if (!recycled_color_buffer_queue_.empty()) {
scoped_refptr<ColorBuffer> recycled =
recycled_color_buffer_queue_.TakeLast();
if (recycled->receive_sync_token.HasData()) {
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
gl->WaitSyncTokenCHROMIUM(recycled->receive_sync_token.GetData());
}
DCHECK(recycled->size == size_);
return recycled;
}
return CreateColorBuffer();
}
bool XRWebGLDrawingBuffer::WantExplicitResolve() const {
return anti_aliasing_mode_ == kMSAAExplicitResolve;
}
void XRWebGLDrawingBuffer::BindAndResolveDestinationFramebuffer() {
// Ensure that the mode-appropriate destination framebuffer's color
// attachment contains the drawn content after any antialiasing steps needed.
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
DrawingBuffer::Client* client = drawing_buffer_->client();
// Resolve multisample buffers if needed
if (WantExplicitResolve()) {
DVLOG(3) << __FUNCTION__ << ": explicit resolve";
gl->BindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, framebuffer_);
gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, resolved_framebuffer_);
gl->Disable(GL_SCISSOR_TEST);
int width = size_.width();
int height = size_.height();
// Use NEAREST, because there is no scale performed during the blit.
gl->BlitFramebufferCHROMIUM(0, 0, width, height, 0, 0, width, height,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_);
client->DrawingBufferClientRestoreScissorTest();
} else {
gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
DVLOG(3) << __FUNCTION__ << ": nothing to do";
}
// On exit, leaves the destination framebuffer active. Caller is responsible
// for restoring client bindings.
}
// Swap the front and back buffers. After this call the front buffer should
// contain the previously rendered content, resolved from the multisample
// renderbuffer if needed.
void XRWebGLDrawingBuffer::SwapColorBuffers() {
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
DrawingBuffer::Client* client = drawing_buffer_->client();
BindAndResolveDestinationFramebuffer();
if (back_color_buffer_) {
gl->EndSharedImageAccessDirectCHROMIUM(back_color_buffer_->texture_id);
}
// Swap buffers
front_color_buffer_ = back_color_buffer_;
back_color_buffer_ = CreateOrRecycleColorBuffer();
gl->BeginSharedImageAccessDirectCHROMIUM(
back_color_buffer_->texture_id,
GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
if (anti_aliasing_mode_ == kMSAAImplicitResolve) {
gl->FramebufferTexture2DMultisampleEXT(
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
back_color_buffer_->texture_id, 0, sample_count_);
} else {
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, back_color_buffer_->texture_id, 0);
}
if (!framebuffer_complete_checked_for_swap_) {
DCHECK(gl->CheckFramebufferStatus(GL_FRAMEBUFFER) ==
GL_FRAMEBUFFER_COMPLETE);
framebuffer_complete_checked_for_swap_ = true;
}
if (WantExplicitResolve()) {
// Bind the drawing framebuffer if it wasn't bound previously.
gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
}
ClearBoundFramebuffer();
client->DrawingBufferClientRestoreFramebufferBinding();
}
scoped_refptr<StaticBitmapImage>
XRWebGLDrawingBuffer::TransferToStaticBitmapImage() {
gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL();
scoped_refptr<ColorBuffer> buffer;
bool success = false;
// Ensure the context isn't lost and the framebuffer is complete before
// continuing.
if (!ContextLost()) {
SwapColorBuffers();
buffer = front_color_buffer_;
gl->GenUnverifiedSyncTokenCHROMIUM(buffer->produce_sync_token.GetData());
// This should only fail if the context is lost during the buffer swap.
if (buffer->produce_sync_token.HasData()) {
success = true;
}
}
if (!success) {
// If we can't get a mailbox, return an transparent black ImageBitmap.
// The only situation in which this could happen is when two or more calls
// to transferToImageBitmap are made back-to-back, if the framebuffer is
// incomplete (likely due to a failed buffer allocation), or when the
// context gets lost.
sk_sp<SkSurface> surface =
SkSurface::MakeRasterN32Premul(size_.width(), size_.height());
return UnacceleratedStaticBitmapImage::Create(surface->makeImageSnapshot());
}
// This holds a ref on the XRWebGLDrawingBuffer that will keep it alive
// until the mailbox is released (and while the callback is running).
viz::ReleaseCallback release_callback =
base::BindOnce(&XRWebGLDrawingBuffer::NotifyMailboxReleased, buffer);
const SkImageInfo sk_image_info =
SkImageInfo::MakeN32Premul(size_.width(), size_.height());
return AcceleratedStaticBitmapImage::CreateFromCanvasMailbox(
buffer->mailbox, buffer->produce_sync_token,
/* shared_image_texture_id = */ 0, sk_image_info, GL_TEXTURE_2D,
/* is_origin_top_left = */ false,
drawing_buffer_->ContextProviderWeakPtr(),
base::PlatformThread::CurrentRef(),
ThreadScheduler::Current()->CleanupTaskRunner(),
std::move(release_callback),
/*supports_display_compositing=*/true,
// CreateColorBuffer() never sets the SCANOUT usage bit.
/*is_overlay_candidate=*/false);
}
// static
void XRWebGLDrawingBuffer::NotifyMailboxReleased(
scoped_refptr<ColorBuffer> color_buffer,
const gpu::SyncToken& sync_token,
bool lost_resource) {
DCHECK(color_buffer->owning_thread_ref == base::PlatformThread::CurrentRef());
// Update the SyncToken to ensure that we will wait for it even if we
// immediately destroy this buffer.
color_buffer->receive_sync_token = sync_token;
if (color_buffer->drawing_buffer) {
color_buffer->drawing_buffer->MailboxReleased(color_buffer, lost_resource);
}
}
void XRWebGLDrawingBuffer::MailboxReleased(
scoped_refptr<ColorBuffer> color_buffer,
bool lost_resource) {
// If the mailbox has been returned by the compositor then it is no
// longer being presented, and so is no longer the front buffer.
if (color_buffer == front_color_buffer_)
front_color_buffer_ = nullptr;
if (drawing_buffer_->destroyed() || color_buffer->size != size_ ||
lost_resource) {
return;
}
const size_t cache_limit = 2;
while (recycled_color_buffer_queue_.size() >= cache_limit)
recycled_color_buffer_queue_.TakeLast();
recycled_color_buffer_queue_.push_front(color_buffer);
}
} // namespace blink