| // Copyright 2017 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 "third_party/blink/renderer/platform/graphics/gpu/xr_webgl_drawing_buffer.h" |
| |
| #include "build/build_config.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/instrumentation/tracing/trace_event.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( |
| XRWebGLDrawingBuffer* drawing_buffer, |
| const IntSize& size, |
| GLuint texture_id) |
| : drawing_buffer(drawing_buffer), size(size), texture_id(texture_id) { |
| gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL(); |
| gl->ProduceTextureDirectCHROMIUM(texture_id, mailbox.name); |
| } |
| |
| XRWebGLDrawingBuffer::ColorBuffer::~ColorBuffer() { |
| gpu::gles2::GLES2Interface* gl = drawing_buffer->ContextGL(); |
| if (receive_sync_token.HasData()) |
| gl->WaitSyncTokenCHROMIUM(receive_sync_token.GetConstData()); |
| gl->DeleteTextures(1, &texture_id); |
| } |
| |
| scoped_refptr<XRWebGLDrawingBuffer> XRWebGLDrawingBuffer::Create( |
| DrawingBuffer* drawing_buffer, |
| GLuint framebuffer, |
| const IntSize& size, |
| bool want_alpha_channel, |
| bool want_depth_buffer, |
| bool want_stencil_buffer, |
| bool want_antialiasing, |
| bool want_multiview) { |
| 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"); |
| |
| // TODO(bajones): Support multiview. |
| bool multiview_supported = false; |
| |
| 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, |
| multiview_supported)); |
| if (!xr_drawing_buffer->Initialize(size, multisample_supported, |
| multiview_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, |
| bool multiview_supported) |
| : drawing_buffer_(drawing_buffer), |
| framebuffer_(framebuffer), |
| discard_framebuffer_supported_(discard_framebuffer_supported), |
| depth_(want_depth_buffer), |
| stencil_(want_stencil_buffer), |
| alpha_(want_alpha_channel), |
| multiview_(false) {} |
| |
| void XRWebGLDrawingBuffer::BeginDestruction() { |
| 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 IntSize& size, |
| bool use_multisampling, |
| bool use_multiview) { |
| 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; |
| } else if (extensions_util->SupportsExtension( |
| "GL_CHROMIUM_screen_space_antialiasing")) { |
| anti_aliasing_mode_ = kScreenSpaceAntialiasing; |
| } |
| } |
| DVLOG(2) << __FUNCTION__ |
| << ": anti_aliasing_mode_=" << static_cast<int>(anti_aliasing_mode_); |
| |
| storage_texture_supported_ = |
| (drawing_buffer_->webgl_version() > DrawingBuffer::kWebGL1 || |
| extensions_util->SupportsExtension("GL_EXT_texture_storage")) && |
| anti_aliasing_mode_ == kScreenSpaceAntialiasing; |
| |
| #if defined(OS_ANDROID) |
| // On Android devices use a smaller numer 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(); |
| } |
| |
| void XRWebGLDrawingBuffer::SetMirrorClient(MirrorClient* client) { |
| mirror_client_ = client; |
| if (mirror_client_) { |
| // Immediately send a black 1x1 image to the mirror client to ensure that |
| // it has content to show. |
| sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(1, 1); |
| mirror_client_->OnMirrorImageAvailable( |
| StaticBitmapImage::Create(surface->makeImageSnapshot()), nullptr); |
| } |
| } |
| |
| bool XRWebGLDrawingBuffer::ContextLost() { |
| return drawing_buffer_->destroyed(); |
| } |
| |
| IntSize XRWebGLDrawingBuffer::AdjustSize(const IntSize& 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 IntSize(width, height); |
| } |
| |
| void XRWebGLDrawingBuffer::OverwriteColorBufferFromMailboxTexture( |
| const gpu::MailboxHolder& mailbox_holder, |
| const IntSize& size_in) { |
| TRACE_EVENT0("gpu", __FUNCTION__); |
| gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL(); |
| |
| gl->WaitSyncTokenCHROMIUM(mailbox_holder.sync_token.GetConstData()); |
| |
| GLuint source_texture = |
| gl->CreateAndConsumeTextureCHROMIUM(mailbox_holder.mailbox.name); |
| |
| GLuint dest_texture = back_color_buffer_->texture_id; |
| |
| // TODO(836496): clean this up and move some of the math to call site. |
| int dest_width = size_.Width(); |
| int dest_height = size_.Height(); |
| int source_width = size_in.Width(); |
| int source_height = size_in.Height(); |
| |
| int copy_width = std::min(source_width, dest_width); |
| int copy_height = std::min(source_height, dest_height); |
| |
| // If the source is too small, center the image. |
| int dest_x0 = source_width < dest_width ? (dest_width - source_width) / 2 : 0; |
| int dest_y0 = |
| source_height < dest_height ? (dest_height - source_height) / 2 : 0; |
| int src_x0 = source_width > dest_width ? (source_width - dest_width) / 2 : 0; |
| int src_y0 = |
| source_height > dest_height ? (source_height - dest_height) / 2 : 0; |
| |
| gl->CopySubTextureCHROMIUM( |
| source_texture, 0, GL_TEXTURE_2D, dest_texture, 0, dest_x0, dest_y0, |
| src_x0, src_y0, copy_width, copy_height, false /* flipY */, |
| false /* premultiplyAlpha */, false /* unmultiplyAlpha */); |
| } |
| |
| void XRWebGLDrawingBuffer::UseSharedBuffer( |
| const gpu::MailboxHolder& buffer_mailbox_holder) { |
| DVLOG(3) << __FUNCTION__; |
| |
| gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL(); |
| |
| // Create a texture backed by the shared buffer image. |
| DCHECK(!shared_buffer_texture_id_); |
| shared_buffer_texture_id_ = |
| gl->CreateAndConsumeTextureCHROMIUM(buffer_mailbox_holder.mailbox.name); |
| |
| 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 (discard_framebuffer_supported_) { |
| const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, |
| GL_STENCIL_ATTACHMENT}; |
| gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments); |
| } |
| |
| 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(); |
| if (discard_framebuffer_supported_) { |
| // 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 (WantExplicitResolve()) { |
| gl->BindFramebuffer(GL_FRAMEBUFFER, resolved_framebuffer_); |
| } else { |
| gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer_); |
| } |
| const GLenum kAttachments[] = {GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT}; |
| gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, |
| sizeof(kAttachments) / sizeof(kAttachments[0]), |
| 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 CreateAndConsumeTexture, delete it. |
| DCHECK(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::Resize(const IntSize& new_size) { |
| IntSize 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_); |
| } |
| |
| back_color_buffer_ = CreateColorBuffer(); |
| front_color_buffer_ = nullptr; |
| |
| 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() { |
| gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL(); |
| |
| GLuint texture_id = 0; |
| gl->GenTextures(1, &texture_id); |
| gl->BindTexture(GL_TEXTURE_2D, texture_id); |
| gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
| gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| if (storage_texture_supported_) { |
| GLenum internal_storage_format = alpha_ ? GL_RGBA8 : GL_RGB8; |
| gl->TexStorage2DEXT(GL_TEXTURE_2D, 1, internal_storage_format, |
| size_.Width(), size_.Height()); |
| } else { |
| GLenum gl_format = alpha_ ? GL_RGBA : GL_RGB; |
| gl->TexImage2D(GL_TEXTURE_2D, 0, gl_format, size_.Width(), size_.Height(), |
| 0, gl_format, GL_UNSIGNED_BYTE, nullptr); |
| } |
| |
| DrawingBuffer::Client* client = drawing_buffer_->client(); |
| client->DrawingBufferClientRestoreTexture2DBinding(); |
| |
| return base::AdoptRef(new ColorBuffer(this, size_, texture_id)); |
| } |
| |
| scoped_refptr<XRWebGLDrawingBuffer::ColorBuffer> |
| XRWebGLDrawingBuffer::CreateOrRecycleColorBuffer() { |
| if (!recycled_color_buffer_queue_.IsEmpty()) { |
| 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_); |
| if (anti_aliasing_mode_ == kScreenSpaceAntialiasing) { |
| DVLOG(3) << __FUNCTION__ << ": screen space antialiasing"; |
| gl->ApplyScreenSpaceAntialiasingCHROMIUM(); |
| } else { |
| 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(); |
| |
| // Swap buffers |
| front_color_buffer_ = back_color_buffer_; |
| back_color_buffer_ = CreateOrRecycleColorBuffer(); |
| |
| 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 (discard_framebuffer_supported_) { |
| const GLenum kAttachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, |
| GL_STENCIL_ATTACHMENT}; |
| gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, kAttachments); |
| } |
| |
| client->DrawingBufferClientRestoreFramebufferBinding(); |
| } |
| |
| scoped_refptr<StaticBitmapImage> |
| XRWebGLDrawingBuffer::TransferToStaticBitmapImage( |
| std::unique_ptr<viz::SingleReleaseCallback>* out_release_callback) { |
| 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 StaticBitmapImage::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). |
| auto func = |
| WTF::Bind(mirror_client_ ? &XRWebGLDrawingBuffer::MailboxReleasedToMirror |
| : &XRWebGLDrawingBuffer::MailboxReleased, |
| scoped_refptr<XRWebGLDrawingBuffer>(this), buffer); |
| |
| std::unique_ptr<viz::SingleReleaseCallback> release_callback = |
| viz::SingleReleaseCallback::Create(std::move(func)); |
| |
| // Make our own textureId that is a reference on the same texture backing |
| // being used as the front buffer. We do not need to wait on the sync |
| // token since the mailbox was produced on the same GL context that we are |
| // using here. Similarly, the release callback will run on the same context so |
| // we don't need to send a sync token for this consume action back to it. |
| GLuint texture_id = gl->CreateAndConsumeTextureCHROMIUM(buffer->mailbox.name); |
| |
| if (out_release_callback) { |
| *out_release_callback = std::move(release_callback); |
| } else { |
| release_callback->Run(gpu::SyncToken(), true /* lost_resource */); |
| } |
| |
| return AcceleratedStaticBitmapImage::CreateFromWebGLContextImage( |
| buffer->mailbox, buffer->produce_sync_token, texture_id, |
| drawing_buffer_->ContextProviderWeakPtr(), size_); |
| } |
| |
| void XRWebGLDrawingBuffer::MailboxReleased( |
| scoped_refptr<ColorBuffer> color_buffer, |
| const gpu::SyncToken& sync_token, |
| 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; |
| |
| // 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 (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); |
| } |
| |
| void XRWebGLDrawingBuffer::MailboxReleasedToMirror( |
| scoped_refptr<ColorBuffer> color_buffer, |
| const gpu::SyncToken& sync_token, |
| bool lost_resource) { |
| if (!mirror_client_ || lost_resource) { |
| MailboxReleased(std::move(color_buffer), sync_token, lost_resource); |
| return; |
| } |
| |
| gpu::gles2::GLES2Interface* gl = drawing_buffer_->ContextGL(); |
| color_buffer->receive_sync_token = sync_token; |
| |
| auto func = |
| WTF::Bind(&XRWebGLDrawingBuffer::MailboxReleased, |
| scoped_refptr<XRWebGLDrawingBuffer>(this), color_buffer); |
| |
| std::unique_ptr<viz::SingleReleaseCallback> release_callback = |
| viz::SingleReleaseCallback::Create(std::move(func)); |
| |
| GLuint texture_id = |
| gl->CreateAndConsumeTextureCHROMIUM(color_buffer->mailbox.name); |
| |
| scoped_refptr<StaticBitmapImage> image = |
| AcceleratedStaticBitmapImage::CreateFromWebGLContextImage( |
| color_buffer->mailbox, color_buffer->produce_sync_token, texture_id, |
| drawing_buffer_->ContextProviderWeakPtr(), color_buffer->size); |
| |
| mirror_client_->OnMirrorImageAvailable(std::move(image), |
| std::move(release_callback)); |
| } |
| |
| } // namespace blink |