blob: 8ca2d1aa98eabdc7a137ff75d8ff84e730697486 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/vr/android/xr_image_transport_base.h"
#include "base/android/scoped_hardware_buffer_handle.h"
#include "base/feature_list.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "device/vr/android/mailbox_to_surface_bridge.h"
#include "device/vr/android/web_xr_presentation_state.h"
#include "device/vr/public/cpp/features.h"
#include "gpu/command_buffer/client/client_shared_image.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/gfx/gpu_fence.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_fence.h"
namespace device {
XrImageTransportBase::XrImageTransportBase(
std::unique_ptr<MailboxToSurfaceBridge> mailbox_bridge)
: mailbox_bridge_(std::move(mailbox_bridge)),
gl_thread_task_runner_(
base::SingleThreadTaskRunner::GetCurrentDefault()) {
DVLOG(2) << __func__;
}
XrImageTransportBase::~XrImageTransportBase() = default;
void XrImageTransportBase::DestroySharedBuffers(WebXrPresentationState* webxr) {
DVLOG(2) << __func__;
CHECK(IsOnGlThread());
if (!webxr) {
return;
}
std::vector<std::unique_ptr<WebXrSharedBuffer>> buffers =
webxr->TakeSharedBuffers();
for (auto& buffer : buffers) {
if (buffer->shared_image) {
DCHECK(mailbox_bridge_);
DVLOG(2) << ": DestroySharedImage, mailbox="
<< buffer->shared_image->mailbox().ToDebugString();
// Note: the sync token may not be accurate. See comment in TransferFrame
// below.
mailbox_bridge_->DestroySharedImage(buffer->sync_token,
std::move(buffer->shared_image));
}
}
}
void XrImageTransportBase::Initialize(WebXrPresentationState* webxr,
XrInitStatusCallback callback,
bool webgpu_session) {
CHECK(IsOnGlThread());
DVLOG(2) << __func__;
webgpu_session_ = webgpu_session;
DoRuntimeInitialization();
mailbox_bridge_->CreateAndBindContextProvider(
base::BindOnce(&XrImageTransportBase::OnMailboxBridgeReady,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void XrImageTransportBase::OnMailboxBridgeReady(XrInitStatusCallback callback) {
DVLOG(2) << __func__;
CHECK(IsOnGlThread());
DCHECK(mailbox_bridge_->IsConnected());
std::move(callback).Run(true);
}
bool XrImageTransportBase::ResizeSharedBuffer(WebXrPresentationState* webxr,
const gfx::Size& size,
WebXrSharedBuffer* buffer) {
CHECK(IsOnGlThread());
if (buffer->shared_image && buffer->shared_image->size() == size) {
return false;
}
TRACE_EVENT0("gpu", "ResizeSharedBuffer");
// Unbind previous image (if any).
if (buffer->shared_image) {
DVLOG(2) << ": DestroySharedImage, mailbox="
<< buffer->shared_image->mailbox().ToDebugString();
// Note: the sync token may not be accurate. See comment in TransferFrame
// below.
mailbox_bridge_->DestroySharedImage(buffer->sync_token,
std::move(buffer->shared_image));
}
DVLOG(2) << __func__ << ": width=" << size.width()
<< " height=" << size.height();
// Remove reference to previous image (if any).
buffer->local_eglimage.reset();
static constexpr viz::SharedImageFormat format =
viz::SinglePlaneFormat::kRGBA_8888;
static constexpr gfx::BufferUsage usage = gfx::BufferUsage::SCANOUT;
// The SharedImages created here will eventually be transferred to other
// processes to have their contents read/written via WebGL for WebXR.
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;
// If the XRSession is producing frames with WebGPU then the appropriate usage
// also needs to be added.
if (IsWebGPUSession()) {
shared_image_usage |= gpu::SHARED_IMAGE_USAGE_WEBGPU_READ |
gpu::SHARED_IMAGE_USAGE_WEBGPU_WRITE;
}
// Create a new AHardwareBuffer backed handle.
buffer->scoped_ahb_handle =
gpu::CreateScopedHardwareBufferHandle(size, format, usage);
// Create a GMB Handle from AHardwareBuffer handle.
gfx::GpuMemoryBufferHandle gmb_handle;
gmb_handle.type = gfx::ANDROID_HARDWARE_BUFFER;
gmb_handle.android_hardware_buffer = buffer->scoped_ahb_handle.Clone();
buffer->shared_image = mailbox_bridge_->CreateSharedImage(
std::move(gmb_handle), format, size, gfx::ColorSpace(),
shared_image_usage, buffer->sync_token);
CHECK(buffer->shared_image);
DVLOG(2) << ": CreateSharedImage, mailbox="
<< buffer->shared_image->mailbox().ToDebugString()
<< ", SyncToken=" << buffer->sync_token.ToDebugString()
<< ", size=" << size.ToString();
// Create an EGLImage for the buffer.
auto egl_image =
gpu::CreateEGLImageFromAHardwareBuffer(buffer->scoped_ahb_handle.get());
if (!egl_image.is_valid()) {
DLOG(WARNING) << __func__ << ": ERROR: failed to initialize image!";
return false;
}
glBindTexture(buffer->local_texture.target, buffer->local_texture.id);
glTexParameteri(buffer->local_texture.target, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE);
glTexParameteri(buffer->local_texture.target, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE);
glTexParameteri(buffer->local_texture.target, GL_TEXTURE_MIN_FILTER,
GL_LINEAR);
glTexParameteri(buffer->local_texture.target, GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
glEGLImageTargetTexture2DOES(buffer->local_texture.target, egl_image.get());
buffer->local_eglimage = std::move(egl_image);
return true;
}
std::unique_ptr<WebXrSharedBuffer> XrImageTransportBase::CreateBuffer() {
CHECK(IsOnGlThread());
std::unique_ptr<WebXrSharedBuffer> buffer =
std::make_unique<WebXrSharedBuffer>();
// Local resources
glGenTextures(1, &buffer->local_texture.id);
buffer->local_texture.target = GL_TEXTURE_2D;
return buffer;
}
WebXrSharedBuffer* XrImageTransportBase::TransferFrame(
WebXrPresentationState* webxr,
const gfx::Size& frame_size,
const gfx::Transform& uv_transform) {
CHECK(IsOnGlThread());
if (!webxr->GetAnimatingFrame()->shared_buffer) {
webxr->GetAnimatingFrame()->shared_buffer = CreateBuffer();
}
WebXrSharedBuffer* shared_buffer =
webxr->GetAnimatingFrame()->shared_buffer.get();
ResizeSharedBuffer(webxr, frame_size, shared_buffer);
// Sanity check that the lazily created/resized buffer looks valid.
DCHECK(shared_buffer->shared_image);
DCHECK(shared_buffer->local_eglimage.is_valid());
DCHECK_EQ(shared_buffer->shared_image->size(), frame_size);
// We don't need to create a sync token here. ResizeSharedBuffer has created
// one on reallocation, including initial buffer creation, and we can use
// that. The shared image interface internally uses its own command buffer ID
// and separate sync token release count namespace, and we must not overwrite
// that. We don't need a new sync token when reusing a correctly-sized buffer,
// it's only eligible for reuse after all reads from it are complete, meaning
// that it's transitioned through "processing" and "rendering" states back
// to "animating".
DCHECK(shared_buffer->sync_token.HasData());
DVLOG(2) << ": SyncToken=" << shared_buffer->sync_token.ToDebugString();
return shared_buffer;
}
void XrImageTransportBase::CreateGpuFenceForSyncToken(
const gpu::SyncToken& sync_token,
base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) {
CHECK(IsOnGlThread());
DVLOG(2) << __func__;
mailbox_bridge_->CreateGpuFence(sync_token, std::move(callback));
}
void XrImageTransportBase::WaitSyncToken(const gpu::SyncToken& sync_token) {
CHECK(IsOnGlThread());
mailbox_bridge_->WaitSyncToken(sync_token);
}
void XrImageTransportBase::ServerWaitForGpuFence(
std::unique_ptr<gfx::GpuFence> gpu_fence) {
CHECK(IsOnGlThread());
std::unique_ptr<gl::GLFence> local_fence =
gl::GLFence::CreateFromGpuFence(*gpu_fence);
local_fence->ServerWait();
}
LocalTexture XrImageTransportBase::GetRenderingTexture(
WebXrPresentationState* webxr) {
CHECK(IsOnGlThread());
return webxr->GetRenderingFrame()->shared_buffer->local_texture;
}
bool XrImageTransportBase::IsOnGlThread() const {
return gl_thread_task_runner_->BelongsToCurrentThread();
}
} // namespace device