| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/vr/graphics_delegate_android.h" |
| |
| #include "base/android/android_hardware_buffer_compat.h" |
| #include "components/webxr/mailbox_to_surface_bridge_impl.h" |
| #include "device/vr/android/xr_image_transport_base.h" |
| #include "gpu/command_buffer/common/shared_image_usage.h" |
| #include "gpu/command_buffer/service/ahardwarebuffer_utils.h" |
| #include "gpu/ipc/common/android/android_hardware_buffer_utils.h" |
| #include "ui/gfx/color_space.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_surface.h" |
| #include "ui/gl/gl_utils.h" |
| #include "ui/gl/init/gl_factory.h" |
| |
| namespace vr { |
| |
| GraphicsDelegateAndroid::GraphicsDelegateAndroid() = default; |
| GraphicsDelegateAndroid::~GraphicsDelegateAndroid() = default; |
| |
| void GraphicsDelegateAndroid::Initialize(base::OnceClosure on_initialized) { |
| // We can only share native GL resources with the runtimes, and they don't |
| // support ANGLE, so disable it. |
| // TODO(crbug.com/40744597): support ANGLE? |
| gl::DisableANGLE(); |
| |
| gl::GLDisplay* display = nullptr; |
| if (gl::GetGLImplementation() == gl::kGLImplementationNone) { |
| display = gl::init::InitializeGLOneOff( |
| /*gpu_preference=*/gl::GpuPreference::kDefault); |
| if (!display) { |
| LOG(ERROR) << "gl::init::InitializeGLOneOff failed"; |
| return; |
| } |
| } else { |
| display = gl::GetDefaultDisplayEGL(); |
| } |
| |
| surface_ = gl::init::CreateOffscreenGLSurface(display, gfx::Size()); |
| if (!surface_.get()) { |
| LOG(ERROR) << "gl::init::CreateOffscreenGLSurface failed"; |
| return; |
| } |
| |
| context_ = gl::init::CreateGLContext(nullptr, surface_.get(), |
| gl::GLContextAttribs()); |
| if (!context_.get()) { |
| DLOG(ERROR) << "gl::init::CreateGLContext failed"; |
| return; |
| } |
| |
| if (!context_->MakeCurrent(surface_.get())) { |
| DLOG(ERROR) << "gl::GLContext::MakeCurrent() failed"; |
| return; |
| } |
| |
| mailbox_bridge_ = std::make_unique<webxr::MailboxToSurfaceBridgeImpl>(); |
| mailbox_bridge_->CreateAndBindContextProvider(base::BindOnce( |
| &GraphicsDelegateAndroid::OnMailboxBridgeReady, |
| weak_ptr_factory_.GetWeakPtr(), std::move(on_initialized))); |
| } |
| |
| void GraphicsDelegateAndroid::OnMailboxBridgeReady( |
| base::OnceClosure on_inititalized) { |
| DCHECK(mailbox_bridge_->IsConnected()); |
| std::move(on_inititalized).Run(); |
| } |
| |
| bool GraphicsDelegateAndroid::BindContext() { |
| return true; |
| } |
| |
| void GraphicsDelegateAndroid::ClearContext() {} |
| |
| bool GraphicsDelegateAndroid::PreRender() { |
| BindContext(); |
| |
| // Create a memory buffer and a shared image referencing that memory buffer. |
| if (!EnsureMemoryBuffer()) { |
| DLOG(ERROR) << __func__ << " Failed to ensure memory buffer"; |
| return false; |
| } |
| DVLOG(3) << __func__ << " mailbox: " |
| << shared_buffer_->shared_image->mailbox().ToDebugString() |
| << " sync_token: " << shared_buffer_->sync_token.ToDebugString(); |
| |
| GLenum target = shared_buffer_->local_texture.target; |
| glBindTexture(target, shared_buffer_->local_texture.id); |
| glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| |
| // Bind our image/texture/memory buffer as the draw framebuffer. |
| glGenFramebuffersEXT(1, &draw_frame_buffer_); |
| glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, draw_frame_buffer_); |
| glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, |
| shared_buffer_->local_texture.id, 0); |
| |
| return true; |
| } |
| |
| void GraphicsDelegateAndroid::PostRender() { |
| // Unbind the drawing buffer. |
| glBindFramebufferEXT(GL_FRAMEBUFFER, 0); |
| glDeleteFramebuffersEXT(1, &draw_frame_buffer_); |
| |
| glBindTexture(GL_TEXTURE_2D, 0); |
| draw_frame_buffer_ = 0; |
| |
| // Generate a SyncToken after GPU is done accessing the texture. |
| mailbox_bridge_->GenSyncToken(&shared_buffer_->sync_token); |
| |
| ClearContext(); |
| glFlush(); |
| } |
| |
| gfx::GpuMemoryBufferHandle GraphicsDelegateAndroid::GetTexture() { |
| if (!shared_buffer_) { |
| return gfx::GpuMemoryBufferHandle(); |
| } |
| return gfx::GpuMemoryBufferHandle(shared_buffer_->scoped_ahb_handle.Clone()); |
| } |
| |
| gpu::SyncToken GraphicsDelegateAndroid::GetSyncToken() { |
| if (!shared_buffer_) { |
| return gpu::SyncToken(); |
| } |
| |
| return shared_buffer_->sync_token; |
| } |
| |
| bool GraphicsDelegateAndroid::EnsureMemoryBuffer() { |
| // The code here is very similar to that used in both XrImageTransportBase |
| // and OpenXrGraphicsDelegateOpenGLES's ResizeShardBuffer methods. However, |
| // they all have subtly different uses. |
| // TODO(https://crbug.com/40909689): Consolidate this usage. |
| gfx::Size buffer_size = GetTextureSize(); |
| if (shared_buffer_ && shared_buffer_->size == buffer_size) { |
| return true; |
| } |
| |
| if (!shared_buffer_) { |
| shared_buffer_ = std::make_unique<device::WebXrSharedBuffer>(); |
| shared_buffer_->local_texture.target = GL_TEXTURE_2D; |
| glGenTextures(1, &shared_buffer_->local_texture.id); |
| } |
| |
| if (shared_buffer_->shared_image) { |
| mailbox_bridge_->DestroySharedImage( |
| shared_buffer_->sync_token, std::move(shared_buffer_->shared_image)); |
| } |
| |
| // Remove reference to previous image (if any). |
| shared_buffer_->local_eglimage.reset(); |
| |
| static constexpr gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888; |
| static constexpr gfx::BufferUsage usage = gfx::BufferUsage::SCANOUT; |
| gpu::SharedImageUsageSet shared_image_usage = |
| gpu::SHARED_IMAGE_USAGE_SCANOUT | gpu::SHARED_IMAGE_USAGE_DISPLAY_READ | |
| gpu::SHARED_IMAGE_USAGE_GLES2_READ | gpu::SHARED_IMAGE_USAGE_GLES2_WRITE; |
| |
| shared_buffer_->scoped_ahb_handle = |
| gpu::CreateScopedHardwareBufferHandle(buffer_size, format, usage); |
| |
| // Create a GMB Handle from AHardwareBuffer handle. |
| gfx::GpuMemoryBufferHandle gmb_handle; |
| gmb_handle.type = gfx::ANDROID_HARDWARE_BUFFER; |
| // GpuMemoryBufferId is not used in this case and hence hardcoding it to 1 |
| // here. |
| gmb_handle.id = gfx::GpuMemoryBufferId(1); |
| gmb_handle.android_hardware_buffer = |
| shared_buffer_->scoped_ahb_handle.Clone(); |
| |
| shared_buffer_->shared_image = mailbox_bridge_->CreateSharedImage( |
| std::move(gmb_handle), format, buffer_size, gfx::ColorSpace(), |
| shared_image_usage, shared_buffer_->sync_token); |
| DVLOG(2) << ": CreateSharedImage, mailbox=" |
| << shared_buffer_->shared_image->mailbox().ToDebugString() |
| << ", SyncToken=" << shared_buffer_->sync_token.ToDebugString() |
| << ", size=" << buffer_size.ToString(); |
| |
| // Create an EGLImage for the buffer. |
| auto egl_image = gpu::CreateEGLImageFromAHardwareBuffer( |
| shared_buffer_->scoped_ahb_handle.get()); |
| if (!egl_image.is_valid()) { |
| return false; |
| } |
| |
| glBindTexture(shared_buffer_->local_texture.target, |
| shared_buffer_->local_texture.id); |
| glEGLImageTargetTexture2DOES(shared_buffer_->local_texture.target, |
| egl_image.get()); |
| shared_buffer_->local_eglimage = std::move(egl_image); |
| |
| // Save size to avoid resize next time. |
| DVLOG(1) << __func__ << ": resized to " << buffer_size.width() << "x" |
| << buffer_size.height(); |
| shared_buffer_->size = buffer_size; |
| return true; |
| } |
| |
| void GraphicsDelegateAndroid::ResetMemoryBuffer() { |
| // Stop using a memory buffer if we had an error submitting with it. |
| if (shared_buffer_ && shared_buffer_->shared_image) { |
| DCHECK(mailbox_bridge_); |
| DVLOG(2) << ": DestroySharedImage, mailbox=" |
| << shared_buffer_->shared_image->mailbox().ToDebugString(); |
| mailbox_bridge_->DestroySharedImage( |
| shared_buffer_->sync_token, std::move(shared_buffer_->shared_image)); |
| shared_buffer_->size = {0, 0}; |
| } |
| } |
| |
| void GraphicsDelegateAndroid::ClearBufferToBlack() { |
| glClearColor(0, 0, 0, 0); |
| glClear(GL_COLOR_BUFFER_BIT); |
| } |
| |
| } // namespace vr |