blob: 419e533df019064922b0da938bc38700a4eb2fa5 [file] [log] [blame]
// 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_argument.h"
#include "chrome/browser/android/vr/mailbox_to_surface_bridge.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 {
namespace {
// Number of shared buffers to use in rotation. Two would be sufficient if
// strictly sequenced, but use an extra one since we currently don't know
// exactly when the Renderer is done with it.
constexpr int kSharedBufferSwapChainSize = 3;
} // namespace
// TODO(klausw): share this with WebXrPresentationState.
struct SharedFrameBuffer {
SharedFrameBuffer() = default;
~SharedFrameBuffer() = default;
gfx::Size size;
std::unique_ptr<gpu::GpuMemoryBufferImplAndroidHardwareBuffer>
shared_gpu_memory_buffer;
// Resources in the remote GPU process command buffer context
std::unique_ptr<gpu::MailboxHolder> mailbox_holder;
GLuint remote_texture_id = 0;
GLuint remote_image_id = 0;
// Resources in the local GL context
GLuint local_texture_id = 0;
// This refptr keeps the image alive while processing a frame. That's
// required because it owns underlying resources and must still be
// alive when the mailbox texture backed by this image is used.
scoped_refptr<gl::GLImageEGL> local_glimage;
};
struct SharedFrameBufferSwapChain {
SharedFrameBufferSwapChain() = default;
~SharedFrameBufferSwapChain() = default;
base::queue<std::unique_ptr<SharedFrameBuffer>> buffers;
int next_memory_buffer_id = 0;
};
ArImageTransport::ArImageTransport(
std::unique_ptr<vr::MailboxToSurfaceBridge> mailbox_bridge)
: gl_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
mailbox_bridge_(std::move(mailbox_bridge)),
swap_chain_(std::make_unique<SharedFrameBufferSwapChain>()) {}
ArImageTransport::~ArImageTransport() {}
bool ArImageTransport::Initialize() {
DCHECK(IsOnGlThread());
mailbox_bridge_->BindContextProviderToCurrentThread();
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
ar_renderer_ = std::make_unique<ArRenderer>();
glGenTextures(1, &camera_texture_id_arcore_);
SetupHardwareBuffers();
return true;
}
GLuint ArImageTransport::GetCameraTextureId() {
return camera_texture_id_arcore_;
}
void ArImageTransport::ResizeSharedBuffer(const gfx::Size& size,
SharedFrameBuffer* buffer) {
DCHECK(IsOnGlThread());
if (buffer->size == size)
return;
TRACE_EVENT0("gpu", __FUNCTION__);
// Unbind previous image (if any).
if (buffer->remote_image_id) {
DVLOG(2) << ": UnbindSharedBuffer, remote_image="
<< buffer->remote_image_id;
mailbox_bridge_->UnbindSharedBuffer(buffer->remote_image_id,
buffer->remote_texture_id);
buffer->remote_image_id = 0;
}
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(swap_chain_->next_memory_buffer_id++);
buffer->shared_gpu_memory_buffer =
gpu::GpuMemoryBufferImplAndroidHardwareBuffer::Create(
kBufferId, size, format, usage,
gpu::GpuMemoryBufferImpl::DestructionCallback());
buffer->remote_image_id = mailbox_bridge_->BindSharedBufferImage(
buffer->shared_gpu_memory_buffer.get(), size, format, usage,
buffer->remote_texture_id);
DVLOG(2) << ": BindSharedBufferImage, remote_image="
<< buffer->remote_image_id;
auto img = base::MakeRefCounted<gl::GLImageAHardwareBuffer>(size);
base::android::ScopedHardwareBufferHandle ahb =
buffer->shared_gpu_memory_buffer->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_id);
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;
}
void ArImageTransport::SetupHardwareBuffers() {
DCHECK(IsOnGlThread());
glGenFramebuffersEXT(1, &camera_fbo_);
for (int i = 0; i < kSharedBufferSwapChainSize; ++i) {
std::unique_ptr<SharedFrameBuffer> buffer =
std::make_unique<SharedFrameBuffer>();
// Remote resources
buffer->mailbox_holder = std::make_unique<gpu::MailboxHolder>();
buffer->mailbox_holder->texture_target = GL_TEXTURE_2D;
buffer->remote_texture_id =
mailbox_bridge_->CreateMailboxTexture(&buffer->mailbox_holder->mailbox);
// Local resources
glGenTextures(1, &buffer->local_texture_id);
// Add to swap chain
swap_chain_->buffers.push(std::move(buffer));
}
glGenFramebuffersEXT(1, &transfer_fbo_);
}
gpu::MailboxHolder ArImageTransport::TransferFrame(
const gfx::Size& frame_size,
const gfx::Transform& uv_transform) {
DCHECK(IsOnGlThread());
// TODO(klausw): find out when a buffer is actually done being used
// including by GL so we can know if we are overwriting one.
DCHECK(swap_chain_->buffers.size() > 0);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, transfer_fbo_);
std::unique_ptr<SharedFrameBuffer> shared_buffer =
std::move(swap_chain_->buffers.front());
swap_chain_->buffers.pop();
ResizeSharedBuffer(frame_size, shared_buffer.get());
glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_EXTERNAL_OES,
shared_buffer->local_texture_id, 0);
if (!transfer_fbo_completeness_checked_) {
auto status = glCheckFramebufferStatusEXT(GL_DRAW_FRAMEBUFFER);
DVLOG(1) << __FUNCTION__ << ": framebuffer status=" << std::hex << status;
DCHECK(status == GL_FRAMEBUFFER_COMPLETE);
transfer_fbo_completeness_checked_ = true;
}
// 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_BLEND);
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(camera_texture_id_arcore_, uv_transform_floats, 0, 0);
glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER, 0);
// Make a GpuFence and place it in the GPU stream for sequencing.
std::unique_ptr<gl::GLFence> gl_fence = gl::GLFence::CreateForGpuFence();
std::unique_ptr<gfx::GpuFence> gpu_fence = gl_fence->GetGpuFence();
mailbox_bridge_->WaitForClientGpuFence(gpu_fence.get());
mailbox_bridge_->GenSyncToken(&shared_buffer->mailbox_holder->sync_token);
gpu::MailboxHolder rendered_frame_holder = *shared_buffer->mailbox_holder;
// Done with the shared buffer.
swap_chain_->buffers.push(std::move(shared_buffer));
return rendered_frame_holder;
}
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