| // 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 <utility> |
| |
| #include "base/android/android_hardware_buffer_compat.h" |
| #include "base/android/scoped_hardware_buffer_fence_sync.h" |
| #include "base/bind.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_image_ahardwarebuffer.h" |
| #include "ui/gl/gl_utils.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); |
| } |
| |
| } // namespace |
| |
| GLSurfaceEGLSurfaceControl::GLSurfaceEGLSurfaceControl( |
| ANativeWindow* window, |
| scoped_refptr<base::SingleThreadTaskRunner> task_runner) |
| : window_rect_(0, |
| 0, |
| ANativeWindow_getWidth(window), |
| ANativeWindow_getHeight(window)), |
| root_surface_(new SurfaceControl::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_.reset(); |
| } |
| |
| bool GLSurfaceEGLSurfaceControl::Resize(const gfx::Size& size, |
| float scale_factor, |
| ColorSpace color_space, |
| bool has_alpha) { |
| window_rect_ = gfx::Rect(0, 0, size.width(), size.height()); |
| return true; |
| } |
| |
| bool GLSurfaceEGLSurfaceControl::IsOffscreen() { |
| return false; |
| } |
| |
| gfx::SwapResult GLSurfaceEGLSurfaceControl::SwapBuffers( |
| PresentationCallback callback) { |
| NOTREACHED(); |
| return gfx::SwapResult::SWAP_FAILED; |
| } |
| |
| gfx::SwapResult GLSurfaceEGLSurfaceControl::CommitOverlayPlanes( |
| PresentationCallback callback) { |
| NOTREACHED(); |
| return gfx::SwapResult::SWAP_FAILED; |
| } |
| |
| gfx::SwapResult GLSurfaceEGLSurfaceControl::PostSubBuffer( |
| int x, |
| int y, |
| int width, |
| int height, |
| PresentationCallback callback) { |
| NOTREACHED(); |
| return gfx::SwapResult::SWAP_FAILED; |
| } |
| |
| void GLSurfaceEGLSurfaceControl::SwapBuffersAsync( |
| SwapCompletionCallback completion_callback, |
| PresentationCallback presentation_callback) { |
| CommitPendingTransaction(window_rect_, std::move(completion_callback), |
| std::move(presentation_callback)); |
| } |
| |
| void GLSurfaceEGLSurfaceControl::CommitOverlayPlanesAsync( |
| SwapCompletionCallback completion_callback, |
| PresentationCallback presentation_callback) { |
| CommitPendingTransaction(window_rect_, std::move(completion_callback), |
| std::move(presentation_callback)); |
| } |
| |
| void GLSurfaceEGLSurfaceControl::PostSubBufferAsync( |
| int x, |
| int y, |
| int width, |
| int height, |
| SwapCompletionCallback completion_callback, |
| PresentationCallback presentation_callback) { |
| CommitPendingTransaction(gfx::Rect(x, y, width, height), |
| std::move(completion_callback), |
| std::move(presentation_callback)); |
| } |
| |
| void GLSurfaceEGLSurfaceControl::CommitPendingTransaction( |
| const gfx::Rect& damage_rect, |
| SwapCompletionCallback completion_callback, |
| PresentationCallback present_callback) { |
| DCHECK(pending_transaction_); |
| |
| // Mark the intersection of a surface's rect with the damage rect as the dirty |
| // rect for that surface. |
| DCHECK_LE(pending_surfaces_count_, surface_list_.size()); |
| for (size_t i = 0; i < pending_surfaces_count_; ++i) { |
| const auto& surface_state = surface_list_[i]; |
| if (!surface_state.buffer_updated_in_pending_transaction) |
| continue; |
| |
| gfx::Rect surface_damage_rect = surface_state.dst; |
| surface_damage_rect.Intersect(damage_rect); |
| pending_transaction_->SetDamageRect(*surface_state.surface, |
| surface_damage_rect); |
| } |
| |
| // Surfaces which are present in the current frame but not in the next frame |
| // need to be explicitly updated in order to get a release fence for them in |
| // the next transaction. |
| for (size_t i = pending_surfaces_count_; i < surface_list_.size(); ++i) { |
| pending_transaction_->SetBuffer(*surface_list_[i].surface, nullptr, |
| base::ScopedFD()); |
| } |
| |
| // 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(); |
| |
| SurfaceControl::Transaction::OnCompleteCb callback = base::BindOnce( |
| &GLSurfaceEGLSurfaceControl::OnTransactionAckOnGpuThread, |
| weak_factory_.GetWeakPtr(), std::move(completion_callback), |
| std::move(present_callback), std::move(resources_to_release)); |
| pending_transaction_->SetOnCompleteCb(std::move(callback), gpu_task_runner_); |
| |
| pending_transaction_acks_++; |
| 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) { |
| context_ = 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 (!SurfaceControl::SupportsColorSpace(image->color_space())) { |
| LOG(ERROR) << "Not supported color space used with overlay : " |
| << image->color_space().ToString(); |
| } |
| |
| 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(); |
| |
| auto* a_surface = surface_state.surface->surface(); |
| DCHECK_EQ(pending_frame_resources_.count(a_surface), 0u); |
| |
| auto& resource_ref = pending_frame_resources_[a_surface]; |
| resource_ref.surface = surface_state.surface; |
| resource_ref.scoped_buffer = std::move(scoped_hardware_buffer); |
| } |
| |
| surface_state.buffer_updated_in_pending_transaction = |
| uninitialized || surface_state.hardware_buffer != hardware_buffer; |
| if (surface_state.buffer_updated_in_pending_transaction) { |
| surface_state.hardware_buffer = hardware_buffer; |
| |
| if (gpu_fence && surface_state.hardware_buffer) { |
| auto fence_handle = |
| gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle()); |
| DCHECK(!fence_handle.is_null()); |
| fence_fd = MergeFDs(std::move(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); |
| } |
| |
| if (uninitialized || surface_state.color_space != image->color_space()) { |
| surface_state.color_space = image->color_space(); |
| pending_transaction_->SetColorSpace(*surface_state.surface, |
| image->color_space()); |
| } |
| |
| return true; |
| } |
| |
| bool GLSurfaceEGLSurfaceControl::IsSurfaceless() const { |
| return true; |
| } |
| |
| void* GLSurfaceEGLSurfaceControl::GetHandle() { |
| return nullptr; |
| } |
| |
| bool GLSurfaceEGLSurfaceControl::SupportsPostSubBuffer() { |
| return true; |
| } |
| |
| bool GLSurfaceEGLSurfaceControl::SupportsAsyncSwap() { |
| return true; |
| } |
| |
| bool GLSurfaceEGLSurfaceControl::SupportsPlaneGpuFences() const { |
| return true; |
| } |
| |
| bool GLSurfaceEGLSurfaceControl::SupportsPresentationCallback() { |
| return true; |
| } |
| |
| bool GLSurfaceEGLSurfaceControl::SupportsCommitOverlayPlanes() { |
| return true; |
| } |
| |
| void GLSurfaceEGLSurfaceControl::OnTransactionAckOnGpuThread( |
| SwapCompletionCallback completion_callback, |
| PresentationCallback presentation_callback, |
| ResourceRefs released_resources, |
| SurfaceControl::TransactionStats transaction_stats) { |
| DCHECK(gpu_task_runner_->BelongsToCurrentThread()); |
| DCHECK_GT(pending_transaction_acks_, 0u); |
| |
| pending_transaction_acks_--; |
| |
| // The presentation feedback callback must run after swap completion. |
| std::move(completion_callback).Run(gfx::SwapResult::SWAP_ACK, nullptr); |
| |
| // TODO(khushalsagar): Maintain a queue of present 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 */); |
| std::move(presentation_callback).Run(feedback); |
| |
| for (auto& surface_stat : transaction_stats.surface_stats) { |
| auto it = released_resources.find(surface_stat.surface); |
| |
| // The transaction ack includes data for all surfaces updated in this |
| // transaction. So the following condition can occur if a new surface was |
| // added in this transaction with a buffer. It'll be included in the ack |
| // with no fence, since its not being released and so shouldn't be in |
| // |released_resources| either. |
| if (it == released_resources.end()) { |
| DCHECK(!surface_stat.fence.is_valid()); |
| continue; |
| } |
| |
| if (surface_stat.fence.is_valid()) |
| it->second.scoped_buffer->SetReadFence(std::move(surface_stat.fence)); |
| } |
| |
| // Note that we may not see |surface_stats| for every resource above. This is |
| // because we take a ref on every buffer used in a frame, even if it is not |
| // updated in that frame. Since the transaction ack only includes surfaces |
| // which were updated in that transaction, the surfaces with no buffer updates |
| // won't be present in the ack. |
| released_resources.clear(); |
| } |
| |
| GLSurfaceEGLSurfaceControl::SurfaceState::SurfaceState( |
| const SurfaceControl::Surface& parent) |
| : surface(new SurfaceControl::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; |
| |
| GLSurfaceEGLSurfaceControl::ResourceRef::ResourceRef() = default; |
| GLSurfaceEGLSurfaceControl::ResourceRef::~ResourceRef() = default; |
| GLSurfaceEGLSurfaceControl::ResourceRef::ResourceRef(ResourceRef&& other) = |
| default; |
| GLSurfaceEGLSurfaceControl::ResourceRef& |
| GLSurfaceEGLSurfaceControl::ResourceRef::operator=(ResourceRef&& other) = |
| default; |
| |
| } // namespace gl |