| // Copyright 2018 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 "chrome/browser/android/vr/arcore_device/ar_image_transport.h" |
| |
| #include "base/android/android_hardware_buffer_compat.h" |
| #include "base/android/scoped_hardware_buffer_handle.h" |
| #include "base/containers/queue.h" |
| #include "base/trace_event/trace_event.h" |
| #include "base/trace_event/traced_value.h" |
| #include "chrome/browser/android/vr/mailbox_to_surface_bridge.h" |
| #include "chrome/browser/android/vr/web_xr_presentation_state.h" |
| #include "gpu/command_buffer/common/shared_image_usage.h" |
| #include "gpu/ipc/common/gpu_memory_buffer_impl_android_hardware_buffer.h" |
| #include "ui/gfx/gpu_fence.h" |
| #include "ui/gl/gl_bindings.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_fence_egl.h" |
| #include "ui/gl/gl_image_ahardwarebuffer.h" |
| #include "ui/gl/gl_surface.h" |
| #include "ui/gl/init/gl_factory.h" |
| |
| namespace device { |
| |
| ArImageTransport::ArImageTransport( |
| std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge) |
| : gl_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| mailbox_bridge_(std::move(mailbox_bridge)) { |
| DVLOG(2) << __func__; |
| } |
| |
| ArImageTransport::~ArImageTransport() = default; |
| |
| void ArImageTransport::DestroySharedBuffers(vr::WebXrPresentationState* webxr) { |
| DVLOG(2) << __func__; |
| DCHECK(IsOnGlThread()); |
| |
| if (!webxr) |
| return; |
| |
| std::vector<std::unique_ptr<vr::WebXrSharedBuffer>> buffers = |
| webxr->TakeSharedBuffers(); |
| for (auto& buffer : buffers) { |
| if (!buffer->mailbox_holder.mailbox.IsZero()) { |
| DCHECK(mailbox_bridge_); |
| DVLOG(2) << ": DestroySharedImage, mailbox=" |
| << buffer->mailbox_holder.mailbox.ToDebugString(); |
| // Note: the sync token in mailbox_holder may not be accurate. See |
| // comment in TransferFrame below. |
| mailbox_bridge_->DestroySharedImage(buffer->mailbox_holder); |
| } |
| } |
| } |
| |
| void ArImageTransport::Initialize(vr::WebXrPresentationState* webxr, |
| base::OnceClosure callback) { |
| DCHECK(IsOnGlThread()); |
| DVLOG(2) << __func__; |
| |
| glDisable(GL_DEPTH_TEST); |
| glDepthMask(GL_FALSE); |
| ar_renderer_ = std::make_unique<ArRenderer>(); |
| glGenTextures(1, &camera_texture_id_arcore_); |
| |
| glGenFramebuffersEXT(1, &camera_fbo_); |
| |
| mailbox_bridge_->CreateAndBindContextProvider( |
| base::BindOnce(&ArImageTransport::OnMailboxBridgeReady, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void ArImageTransport::OnMailboxBridgeReady(base::OnceClosure callback) { |
| DVLOG(2) << __func__; |
| DCHECK(IsOnGlThread()); |
| |
| DCHECK(mailbox_bridge_->IsConnected()); |
| |
| std::move(callback).Run(); |
| } |
| |
| GLuint ArImageTransport::GetCameraTextureId() { |
| return camera_texture_id_arcore_; |
| } |
| |
| void ArImageTransport::ResizeSharedBuffer(vr::WebXrPresentationState* webxr, |
| const gfx::Size& size, |
| vr::WebXrSharedBuffer* buffer) { |
| DCHECK(IsOnGlThread()); |
| |
| if (buffer->size == size) |
| return; |
| |
| TRACE_EVENT0("gpu", __FUNCTION__); |
| // Unbind previous image (if any). |
| if (!buffer->mailbox_holder.mailbox.IsZero()) { |
| DVLOG(2) << ": DestroySharedImage, mailbox=" |
| << buffer->mailbox_holder.mailbox.ToDebugString(); |
| // Note: the sync token in mailbox_holder may not be accurate. See comment |
| // in TransferFrame below. |
| mailbox_bridge_->DestroySharedImage(buffer->mailbox_holder); |
| } |
| |
| DVLOG(2) << __FUNCTION__ << ": width=" << size.width() |
| << " height=" << size.height(); |
| // Remove reference to previous image (if any). |
| buffer->local_glimage = nullptr; |
| |
| static constexpr gfx::BufferFormat format = gfx::BufferFormat::RGBA_8888; |
| static constexpr gfx::BufferUsage usage = gfx::BufferUsage::SCANOUT; |
| |
| gfx::GpuMemoryBufferId kBufferId(webxr->next_memory_buffer_id++); |
| buffer->gmb = gpu::GpuMemoryBufferImplAndroidHardwareBuffer::Create( |
| kBufferId, size, format, usage, |
| gpu::GpuMemoryBufferImpl::DestructionCallback()); |
| |
| uint32_t shared_image_usage = gpu::SHARED_IMAGE_USAGE_SCANOUT | |
| gpu::SHARED_IMAGE_USAGE_DISPLAY | |
| gpu::SHARED_IMAGE_USAGE_GLES2; |
| buffer->mailbox_holder = mailbox_bridge_->CreateSharedImage( |
| buffer->gmb.get(), gfx::ColorSpace(), shared_image_usage); |
| DVLOG(2) << ": CreateSharedImage, mailbox=" |
| << buffer->mailbox_holder.mailbox.ToDebugString(); |
| |
| auto img = base::MakeRefCounted<gl::GLImageAHardwareBuffer>(size); |
| |
| base::android::ScopedHardwareBufferHandle ahb = |
| buffer->gmb->CloneHandle().android_hardware_buffer; |
| bool ret = img->Initialize(ahb.get(), false /* preserved */); |
| if (!ret) { |
| DLOG(WARNING) << __FUNCTION__ << ": ERROR: failed to initialize image!"; |
| return; |
| } |
| glBindTexture(GL_TEXTURE_EXTERNAL_OES, buffer->local_texture); |
| img->BindTexImage(GL_TEXTURE_EXTERNAL_OES); |
| buffer->local_glimage = std::move(img); |
| |
| // Save size to avoid resize next time. |
| DVLOG(1) << __FUNCTION__ << ": resized to " << size.width() << "x" |
| << size.height(); |
| buffer->size = size; |
| } |
| |
| std::unique_ptr<vr::WebXrSharedBuffer> ArImageTransport::CreateBuffer() { |
| std::unique_ptr<vr::WebXrSharedBuffer> buffer = |
| std::make_unique<vr::WebXrSharedBuffer>(); |
| // Local resources |
| glGenTextures(1, &buffer->local_texture); |
| return buffer; |
| } |
| |
| gpu::MailboxHolder ArImageTransport::TransferFrame( |
| vr::WebXrPresentationState* webxr, |
| const gfx::Size& frame_size, |
| const gfx::Transform& uv_transform) { |
| DCHECK(IsOnGlThread()); |
| |
| if (!webxr->GetAnimatingFrame()->shared_buffer) { |
| webxr->GetAnimatingFrame()->shared_buffer = CreateBuffer(); |
| } |
| vr::WebXrSharedBuffer* shared_buffer = |
| webxr->GetAnimatingFrame()->shared_buffer.get(); |
| ResizeSharedBuffer(webxr, frame_size, shared_buffer); |
| |
| mailbox_bridge_->GenSyncToken(&shared_buffer->mailbox_holder.sync_token); |
| return shared_buffer->mailbox_holder; |
| } |
| |
| void ArImageTransport::CreateGpuFenceForSyncToken( |
| const gpu::SyncToken& sync_token, |
| base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) { |
| DVLOG(2) << __func__; |
| mailbox_bridge_->CreateGpuFence(sync_token, std::move(callback)); |
| } |
| |
| void ArImageTransport::WaitSyncToken(const gpu::SyncToken& sync_token) { |
| mailbox_bridge_->WaitSyncToken(sync_token); |
| } |
| |
| void ArImageTransport::CopyCameraImageToFramebuffer( |
| const gfx::Size& frame_size, |
| const gfx::Transform& uv_transform) { |
| glDisable(GL_BLEND); |
| CopyTextureToFramebuffer(camera_texture_id_arcore_, frame_size, uv_transform); |
| } |
| |
| void ArImageTransport::CopyDrawnImageToFramebuffer( |
| vr::WebXrPresentationState* webxr, |
| const gfx::Size& frame_size, |
| const gfx::Transform& uv_transform) { |
| DVLOG(2) << __func__; |
| |
| vr::WebXrSharedBuffer* shared_buffer = |
| webxr->GetRenderingFrame()->shared_buffer.get(); |
| |
| // Set the blend mode for combining the drawn image (source) with the camera |
| // image (destination). WebXR assumes that the canvas has premultiplied alpha, |
| // so the source blend function is GL_ONE. The destination blend function is |
| // (1 - src_alpha) as usual. (Setting that to GL_ONE would simulate an |
| // additive AR headset that can't draw opaque black.) |
| glEnable(GL_BLEND); |
| glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); |
| glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0); |
| CopyTextureToFramebuffer(shared_buffer->local_texture, frame_size, |
| uv_transform); |
| } |
| |
| void ArImageTransport::CopyTextureToFramebuffer( |
| GLuint texture, |
| const gfx::Size& frame_size, |
| const gfx::Transform& uv_transform) { |
| DVLOG(2) << __func__; |
| // Don't need face culling, depth testing, blending, etc. Turn it all off. |
| // TODO(klausw): see if we can do this one time on initialization. That would |
| // be a tiny bit more efficient, but is only safe if ARCore and ArRenderer |
| // don't modify these states. |
| glDisable(GL_CULL_FACE); |
| glDisable(GL_SCISSOR_TEST); |
| glDisable(GL_POLYGON_OFFSET_FILL); |
| glViewport(0, 0, frame_size.width(), frame_size.height()); |
| |
| // Draw the ARCore texture! |
| float uv_transform_floats[16]; |
| uv_transform.matrix().asColMajorf(uv_transform_floats); |
| ar_renderer_->Draw(texture, uv_transform_floats, 0, 0); |
| } |
| |
| bool ArImageTransport::IsOnGlThread() const { |
| return gl_thread_task_runner_->BelongsToCurrentThread(); |
| } |
| |
| std::unique_ptr<ArImageTransport> ArImageTransportFactory::Create( |
| std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge) { |
| return std::make_unique<ArImageTransport>(std::move(mailbox_bridge)); |
| } |
| |
| } // namespace device |