blob: 69102a4f210b5b6bb4317f9039b9351f366071db [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 "ui/gl/gl_surface_egl_surface_control.h"
#include "base/android/android_hardware_buffer_compat.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gl/gl_fence_android_native_fence_sync.h"
#include "ui/gl/gl_image_ahardwarebuffer.h"
namespace gl {
namespace {
constexpr char kRootSurfaceName[] = "ChromeNativeWindowSurface";
constexpr char kChildSurfaceName[] = "ChromeChildSurface";
gfx::Size GetBufferSize(const AHardwareBuffer* buffer) {
AHardwareBuffer_Desc desc;
base::AndroidHardwareBufferCompat::GetInstance().Describe(buffer, &desc);
return gfx::Size(desc.width, desc.height);
}
struct TransactionAckCtx {
scoped_refptr<base::SingleThreadTaskRunner> task_runner;
base::OnceCallback<void(int32_t)> callback;
};
// Note that the framework API states that this callback can be dispatched on
// any thread (in practice it should be the binder thread), so we need to post
// a task back to the GPU thread.
void OnTransactionCompletedOnAnyThread(void* ctx, int32_t present_fence) {
auto* ack_ctx = static_cast<TransactionAckCtx*>(ctx);
ack_ctx->task_runner->PostTask(
FROM_HERE, base::BindOnce(std::move(ack_ctx->callback), present_fence));
delete ack_ctx;
}
} // namespace
GLSurfaceEGLSurfaceControl::GLSurfaceEGLSurfaceControl(
ANativeWindow* window,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: root_surface_(window, kRootSurfaceName),
gpu_task_runner_(std::move(task_runner)),
weak_factory_(this) {}
GLSurfaceEGLSurfaceControl::~GLSurfaceEGLSurfaceControl() = default;
int GLSurfaceEGLSurfaceControl::GetBufferCount() const {
// Triple buffering to match framework's BufferQueue.
return 3;
}
bool GLSurfaceEGLSurfaceControl::Initialize(GLSurfaceFormat format) {
format_ = format;
return true;
}
void GLSurfaceEGLSurfaceControl::Destroy() {
pending_transaction_.reset();
surface_list_.clear();
root_surface_ = SurfaceControl::Surface();
}
bool GLSurfaceEGLSurfaceControl::Resize(const gfx::Size& size,
float scale_factor,
ColorSpace color_space,
bool has_alpha) {
// Resizing requires resizing the SurfaceView in the browser.
return true;
}
bool GLSurfaceEGLSurfaceControl::IsOffscreen() {
return false;
}
gfx::SwapResult GLSurfaceEGLSurfaceControl::SwapBuffers(
const PresentationCallback& callback) {
NOTREACHED();
return gfx::SwapResult::SWAP_FAILED;
}
void GLSurfaceEGLSurfaceControl::SwapBuffersAsync(
const SwapCompletionCallback& completion_callback,
const PresentationCallback& presentation_callback) {
CommitPendingTransaction(completion_callback, presentation_callback);
}
gfx::SwapResult GLSurfaceEGLSurfaceControl::CommitOverlayPlanes(
const PresentationCallback& callback) {
NOTREACHED();
return gfx::SwapResult::SWAP_FAILED;
}
void GLSurfaceEGLSurfaceControl::CommitOverlayPlanesAsync(
const SwapCompletionCallback& completion_callback,
const PresentationCallback& presentation_callback) {
CommitPendingTransaction(completion_callback, presentation_callback);
}
void GLSurfaceEGLSurfaceControl::CommitPendingTransaction(
const SwapCompletionCallback& completion_callback,
const PresentationCallback& present_callback) {
DCHECK(pending_transaction_);
// Release resources for the current frame once the next frame is acked.
ResourceRefs resources_to_release;
resources_to_release.swap(current_frame_resources_);
current_frame_resources_.clear();
// Track resources to be owned by the framework after this transaction.
current_frame_resources_.swap(pending_frame_resources_);
pending_frame_resources_.clear();
// Set up the callback to be notified when the frame is presented by the
// framework. Note that it is assumed that all GPU/display work for this frame
// is finished when the callback is dispatched, and all resources from the
// previous frame can be reused.
TransactionAckCtx* ack_ctx = new TransactionAckCtx;
ack_ctx->task_runner = gpu_task_runner_;
ack_ctx->callback =
base::BindOnce(&GLSurfaceEGLSurfaceControl::OnTransactionAckOnGpuThread,
weak_factory_.GetWeakPtr(), completion_callback,
present_callback, std::move(resources_to_release));
pending_transaction_->SetOnCompleteFunc(&OnTransactionCompletedOnAnyThread,
ack_ctx);
pending_transaction_->Apply();
pending_transaction_.reset();
DCHECK_GE(surface_list_.size(), pending_surfaces_count_);
surface_list_.resize(pending_surfaces_count_);
pending_surfaces_count_ = 0u;
}
gfx::Size GLSurfaceEGLSurfaceControl::GetSize() {
return gfx::Size(0, 0);
}
bool GLSurfaceEGLSurfaceControl::OnMakeCurrent(GLContext* context) {
return true;
}
bool GLSurfaceEGLSurfaceControl::ScheduleOverlayPlane(
int z_order,
gfx::OverlayTransform transform,
GLImage* image,
const gfx::Rect& bounds_rect,
const gfx::RectF& crop_rect,
bool enable_blend,
std::unique_ptr<gfx::GpuFence> gpu_fence) {
if (!pending_transaction_)
pending_transaction_.emplace();
bool uninitialized = false;
if (pending_surfaces_count_ == surface_list_.size()) {
uninitialized = true;
surface_list_.emplace_back(root_surface_);
}
pending_surfaces_count_++;
auto& surface_state = surface_list_.at(pending_surfaces_count_ - 1);
if (uninitialized || surface_state.z_order != z_order) {
surface_state.z_order = z_order;
pending_transaction_->SetZOrder(surface_state.surface, z_order);
}
AHardwareBuffer* hardware_buffer = nullptr;
base::ScopedFD fence_fd;
auto scoped_hardware_buffer = image->GetAHardwareBuffer();
if (scoped_hardware_buffer) {
hardware_buffer = scoped_hardware_buffer->buffer();
fence_fd = scoped_hardware_buffer->TakeFence();
pending_frame_resources_.push_back(std::move(scoped_hardware_buffer));
}
if (uninitialized || surface_state.hardware_buffer != hardware_buffer) {
surface_state.hardware_buffer = hardware_buffer;
if (!fence_fd.is_valid() && gpu_fence && surface_state.hardware_buffer) {
auto fence_handle =
gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle());
DCHECK(!fence_handle.is_null());
fence_fd = base::ScopedFD(fence_handle.native_fd.fd);
}
pending_transaction_->SetBuffer(surface_state.surface,
surface_state.hardware_buffer,
std::move(fence_fd));
}
if (hardware_buffer) {
gfx::Rect dst = bounds_rect;
gfx::Size buffer_size = GetBufferSize(hardware_buffer);
gfx::RectF scaled_rect =
gfx::RectF(crop_rect.x() * buffer_size.width(),
crop_rect.y() * buffer_size.height(),
crop_rect.width() * buffer_size.width(),
crop_rect.height() * buffer_size.height());
gfx::Rect src = gfx::ToEnclosedRect(scaled_rect);
if (uninitialized || surface_state.src != src || surface_state.dst != dst ||
surface_state.transform != transform) {
surface_state.src = src;
surface_state.dst = dst;
surface_state.transform = transform;
pending_transaction_->SetGeometry(surface_state.surface, src, dst,
transform);
}
}
bool opaque = !enable_blend;
if (uninitialized || surface_state.opaque != opaque) {
surface_state.opaque = opaque;
pending_transaction_->SetOpaque(surface_state.surface, opaque);
}
return true;
}
bool GLSurfaceEGLSurfaceControl::IsSurfaceless() const {
return true;
}
void* GLSurfaceEGLSurfaceControl::GetHandle() {
return nullptr;
}
bool GLSurfaceEGLSurfaceControl::SupportsAsyncSwap() {
return true;
}
bool GLSurfaceEGLSurfaceControl::SupportsPlaneGpuFences() const {
return true;
}
bool GLSurfaceEGLSurfaceControl::SupportsPresentationCallback() {
return true;
}
bool GLSurfaceEGLSurfaceControl::SupportsSwapBuffersWithBounds() {
// TODO(khushalsagar): Add support for partial swap.
return false;
}
bool GLSurfaceEGLSurfaceControl::SupportsCommitOverlayPlanes() {
return true;
}
void GLSurfaceEGLSurfaceControl::OnTransactionAckOnGpuThread(
SwapCompletionCallback completion_callback,
PresentationCallback presentation_callback,
ResourceRefs released_resources,
int32_t present_fence) {
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
// Insert a service wait for this fence to ensure any resource reuse is after
// it is signaled.
gfx::GpuFenceHandle handle;
handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync;
handle.native_fd = base::FileDescriptor(present_fence, /*auto_close=*/true);
gfx::GpuFence gpu_fence(handle);
// TODO(khushalsagar): But what about vulkan?
auto gl_fence = GLFence::CreateFromGpuFence(gpu_fence);
gl_fence->ServerWait();
// The presentation feedback callback must run after swap completion.
completion_callback.Run(gfx::SwapResult::SWAP_ACK, nullptr);
// TODO(khushalsagar): Maintain a queue of fences so we poll to see if they
// are signaled every frame, and get a signal timestamp to feed into this
// feedback.
gfx::PresentationFeedback feedback(base::TimeTicks::Now(), base::TimeDelta(),
0 /* flags */);
presentation_callback.Run(feedback);
released_resources.clear();
}
GLSurfaceEGLSurfaceControl::SurfaceState::SurfaceState(
const SurfaceControl::Surface& parent)
: surface(parent, kChildSurfaceName) {}
GLSurfaceEGLSurfaceControl::SurfaceState::SurfaceState() = default;
GLSurfaceEGLSurfaceControl::SurfaceState::SurfaceState(SurfaceState&& other) =
default;
GLSurfaceEGLSurfaceControl::SurfaceState&
GLSurfaceEGLSurfaceControl::SurfaceState::operator=(SurfaceState&& other) =
default;
GLSurfaceEGLSurfaceControl::SurfaceState::~SurfaceState() = default;
} // namespace gl