// Copyright 2018 The Chromium Authors
// 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/build_info.h"
#include "base/android/scoped_hardware_buffer_fence_sync.h"
#include "base/functional/bind.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/strcat.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/math_util.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/overlay_plane_data.h"
#include "ui/gfx/overlay_transform_utils.h"
#include "ui/gl/android/scoped_a_native_window.h"
#include "ui/gl/android/scoped_java_surface_control.h"
#include "ui/gl/egl_util.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_features.h"
#include "ui/gl/gl_fence_android_native_fence_sync.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);
}
std::string BuildSurfaceName(const char* suffix) {
return base::StrCat(
{base::android::BuildInfo::GetInstance()->package_name(), "/", suffix});
}
base::TimeTicks GetSignalTime(const base::ScopedFD& fence) {
if (!fence.is_valid())
return base::TimeTicks();
base::TimeTicks signal_time;
auto status = gfx::GpuFence::GetStatusChangeTime(fence.get(), &signal_time);
if (status != gfx::GpuFence::kSignaled)
return base::TimeTicks();
return signal_time;
}
} // namespace
GLSurfaceEGLSurfaceControl::GLSurfaceEGLSurfaceControl(
gl::ScopedANativeWindow window,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: GLSurfaceEGLSurfaceControl(
base::MakeRefCounted<gfx::SurfaceControl::Surface>(
window.a_native_window(),
BuildSurfaceName(kRootSurfaceName).c_str()),
std::move(task_runner)) {}
GLSurfaceEGLSurfaceControl::GLSurfaceEGLSurfaceControl(
gl::ScopedJavaSurfaceControl scoped_java_surface_control,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: GLSurfaceEGLSurfaceControl(scoped_java_surface_control.MakeSurface(),
std::move(task_runner)) {}
GLSurfaceEGLSurfaceControl::GLSurfaceEGLSurfaceControl(
scoped_refptr<gfx::SurfaceControl::Surface> root_surface,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: child_surface_name_(BuildSurfaceName(kChildSurfaceName)),
root_surface_(std::move(root_surface)),
transaction_ack_timeout_manager_(task_runner),
gpu_task_runner_(std::move(task_runner)),
use_target_deadline_(features::IsAndroidFrameDeadlineEnabled()),
using_on_commit_callback_(!use_target_deadline_ &&
gfx::SurfaceControl::SupportsOnCommit()) {}
GLSurfaceEGLSurfaceControl::~GLSurfaceEGLSurfaceControl() {
Destroy();
}
bool GLSurfaceEGLSurfaceControl::Initialize() {
if (!root_surface_->surface())
return false;
return true;
}
void GLSurfaceEGLSurfaceControl::PreserveChildSurfaceControls() {
TRACE_EVENT_INSTANT0(
"gpu", "GLSurfaceEGLSurfaceControl::PreserveChildSurfaceControls",
TRACE_EVENT_SCOPE_THREAD);
preserve_children_ = true;
}
void GLSurfaceEGLSurfaceControl::Destroy() {
TRACE_EVENT0("gpu", "GLSurfaceEGLSurfaceControl::Destroy");
// Detach all child layers to prevent leaking unless browser asked us not too.
if (!preserve_children_) {
gfx::SurfaceControl::Transaction transaction;
for (auto& surface : surface_list_) {
transaction.SetParent(*surface.surface, nullptr);
}
transaction.Apply();
}
pending_transaction_.reset();
surface_list_.clear();
root_surface_.reset();
}
bool GLSurfaceEGLSurfaceControl::Resize(const gfx::Size& size,
float scale_factor,
const gfx::ColorSpace& color_space,
bool has_alpha) {
// TODO(khushalsagar): Update GLSurfaceFormat using the |color_space| above?
// We don't do this for the NativeViewGLSurfaceEGL as well yet.
return true;
}
void GLSurfaceEGLSurfaceControl::Present(
SwapCompletionCallback completion_callback,
PresentationCallback presentation_callback,
gfx::FrameData data) {
CommitPendingTransaction(std::move(completion_callback),
std::move(presentation_callback));
}
void GLSurfaceEGLSurfaceControl::CommitPendingTransaction(
SwapCompletionCallback completion_callback,
PresentationCallback present_callback) {
// The transaction is initialized on the first ScheduleOverlayPlane call. If
// we don't have a transaction at this point, it means the scheduling the
// overlay plane failed. Simply report a swap failure to lose the context and
// recreate the surface.
if (!pending_transaction_ || surface_lost_) {
LOG(ERROR) << "CommitPendingTransaction failed because surface is lost";
surface_lost_ = true;
std::move(completion_callback)
.Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_FAILED));
std::move(present_callback).Run(gfx::PresentationFeedback::Failure());
return;
}
// This is to workaround an Android bug where not specifying a damage region
// is assumed to mean nothing is damaged. See crbug.com/993977.
for (size_t i = 0; i < pending_surfaces_count_; ++i) {
const auto& surface_state = surface_list_[i];
if (!surface_state.hardware_buffer)
continue;
pending_transaction_->SetDamageRect(
*surface_state.surface,
gfx::Rect(GetBufferSize(surface_state.hardware_buffer)));
}
// 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.
DCHECK_LE(pending_surfaces_count_, surface_list_.size());
for (size_t i = pending_surfaces_count_; i < surface_list_.size(); ++i) {
auto& surface_state = surface_list_[i];
if (surface_state.hardware_buffer) {
pending_transaction_->SetBuffer(*surface_state.surface, nullptr,
base::ScopedFD());
surface_state.hardware_buffer = nullptr;
}
if (surface_state.visibility) {
pending_transaction_->SetVisibility(*surface_state.surface, false);
surface_state.visibility = false;
}
}
// TODO(khushalsagar): Consider using the SetDamageRect API for partial
// invalidations. Note that the damage rect set should be in the space in
// which the content is rendered (including the pre-transform). See
// crbug.com/988857 for details.
// 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();
OnTransactionAckArgs::SequenceId ack_id =
pending_transaction_ack_id_generator_.GenerateNextId();
pending_transaction_acks_.emplace_back(
ack_id, std::move(completion_callback), std::move(present_callback),
std::move(resources_to_release), std::move(primary_plane_fences_));
gfx::SurfaceControl::Transaction::OnCompleteCb complete_cb =
base::BindOnce(&GLSurfaceEGLSurfaceControl::OnTransactionAckOnGpuThread,
weak_factory_.GetWeakPtr(), ack_id);
primary_plane_fences_.reset();
pending_transaction_->SetOnCompleteCb(std::move(complete_cb),
gpu_task_runner_);
if (use_target_deadline_) {
DCHECK(!!choreographer_vsync_id_for_next_frame_);
DCHECK(gfx::SurfaceControl::SupportsSetFrameTimeline());
pending_transaction_->SetFrameTimelineId(
choreographer_vsync_id_for_next_frame_.value());
choreographer_vsync_id_for_next_frame_.reset();
}
if (using_on_commit_callback_) {
gfx::SurfaceControl::Transaction::OnCommitCb commit_cb = base::BindOnce(
&GLSurfaceEGLSurfaceControl::OnTransactionCommittedOnGpuThread,
weak_factory_.GetWeakPtr());
pending_transaction_->SetOnCommitCb(std::move(commit_cb), gpu_task_runner_);
}
pending_surfaces_count_ = 0u;
frame_rate_update_pending_ = false;
if (num_transaction_commit_or_ack_pending_ && !use_target_deadline_) {
pending_transaction_queue_.push(std::move(pending_transaction_).value());
} else {
num_transaction_commit_or_ack_pending_++;
pending_transaction_->Apply();
transaction_ack_timeout_manager_.ScheduleHangDetection();
}
pending_transaction_.reset();
}
bool GLSurfaceEGLSurfaceControl::ScheduleOverlayPlane(
OverlayImage image,
std::unique_ptr<gfx::GpuFence> gpu_fence,
const gfx::OverlayPlaneData& overlay_plane_data) {
if (surface_lost_) {
LOG(ERROR) << "ScheduleOverlayPlane failed because surface is lost";
return false;
}
if (!pending_transaction_)
pending_transaction_.emplace();
bool uninitialized = false;
if (pending_surfaces_count_ == surface_list_.size()) {
uninitialized = true;
surface_list_.emplace_back(*root_surface_, child_surface_name_);
}
pending_surfaces_count_++;
auto& surface_state = surface_list_.at(pending_surfaces_count_ - 1);
// Make the surface visible if its hidden or uninitialized..
if (uninitialized || !surface_state.visibility) {
pending_transaction_->SetVisibility(*surface_state.surface, true);
surface_state.visibility = true;
}
if (uninitialized || surface_state.z_order != overlay_plane_data.z_order) {
surface_state.z_order = overlay_plane_data.z_order;
pending_transaction_->SetZOrder(*surface_state.surface,
overlay_plane_data.z_order);
}
if (uninitialized && use_target_deadline_) {
pending_transaction_->SetEnableBackPressure(*surface_state.surface, true);
}
AHardwareBuffer* hardware_buffer = nullptr;
auto scoped_hardware_buffer = std::move(image);
bool is_primary_plane = overlay_plane_data.is_root_overlay;
if (scoped_hardware_buffer) {
hardware_buffer = scoped_hardware_buffer->buffer();
// We currently only promote the display compositor's buffer or a video
// buffer to an overlay. So if this buffer is not for video then it implies
// its the primary plane.
DCHECK(!is_primary_plane || !primary_plane_fences_);
if (is_primary_plane) {
primary_plane_fences_.emplace();
primary_plane_fences_->available_fence =
scoped_hardware_buffer->TakeAvailableFence();
}
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);
}
if (uninitialized || surface_state.hardware_buffer != hardware_buffer ||
gpu_fence) {
surface_state.hardware_buffer = hardware_buffer;
base::ScopedFD fence_fd;
if (gpu_fence && surface_state.hardware_buffer) {
auto fence_handle = gpu_fence->GetGpuFenceHandle().Clone();
DCHECK(!fence_handle.is_null());
fence_fd = fence_handle.Release();
}
if (is_primary_plane) {
primary_plane_fences_->ready_fence =
base::ScopedFD(HANDLE_EINTR(dup(fence_fd.get())));
}
pending_transaction_->SetBuffer(*surface_state.surface,
surface_state.hardware_buffer,
std::move(fence_fd));
}
if (hardware_buffer) {
gfx::Size buffer_size = GetBufferSize(hardware_buffer);
gfx::RectF scaled_rect =
gfx::ScaleRect(overlay_plane_data.crop_rect, buffer_size.width(),
buffer_size.height());
gfx::Rect dst = gfx::ToNearestRect(overlay_plane_data.display_bounds);
gfx::Rect src = gfx::ToEnclosedRect(scaled_rect);
// When the video is being scrolled offscreen DisplayCompositor will crop it
// to only visible portion and adjust crop_rect accordingly. When the video
// is smaller than the surface is can lead to the crop rect being less than
// a pixel in size. This adjusts the crop rect size to at least 1 pixel as
// we want to stretch last visible pixel line/column in this case.
// Note: We will do it even if crop_rect width/height is exact 0.0f. In
// reality this should never happen and there is no way to display video
// with empty crop rect, so display compositor should not request this.
if (src.width() == 0) {
src.set_width(1);
if (src.right() > buffer_size.width())
src.set_x(buffer_size.width() - 1);
}
if (src.height() == 0) {
src.set_height(1);
if (src.bottom() > buffer_size.height())
src.set_y(buffer_size.height() - 1);
}
// When display compositor rounds up destination rect to integer coordinates
// it becomes slightly bigger. After we adjust source rect accordingly, it
// can become larger then a buffer so we clip it here. See crbug.com/1083412
src.Intersect(gfx::Rect(buffer_size));
auto transform =
absl::get<gfx::OverlayTransform>(overlay_plane_data.plane_transform);
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 = !overlay_plane_data.enable_blend;
if (uninitialized || surface_state.opaque != opaque) {
surface_state.opaque = opaque;
pending_transaction_->SetOpaque(*surface_state.surface, opaque);
}
const auto& image_color_space = overlay_plane_data.color_space;
if (!gfx::SurfaceControl::SupportsColorSpace(image_color_space)) {
LOG(DFATAL) << "Not supported color space used with overlay : "
<< image_color_space.ToString();
}
if (uninitialized || surface_state.color_space != image_color_space ||
surface_state.hdr_metadata != overlay_plane_data.hdr_metadata) {
surface_state.color_space = image_color_space;
surface_state.hdr_metadata = overlay_plane_data.hdr_metadata;
pending_transaction_->SetColorSpace(
*surface_state.surface, image_color_space, surface_state.hdr_metadata);
}
if (frame_rate_update_pending_)
pending_transaction_->SetFrameRate(*surface_state.surface, frame_rate_);
return true;
}
bool GLSurfaceEGLSurfaceControl::SupportsPlaneGpuFences() const {
return true;
}
void GLSurfaceEGLSurfaceControl::OnTransactionAckOnGpuThread(
OnTransactionAckArgs::SequenceId id,
gfx::SurfaceControl::TransactionStats transaction_stats) {
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
transaction_ack_timeout_manager_.OnTransactionAck();
bool found = false;
for (auto& args : pending_transaction_acks_) {
if (args.id == id) {
CHECK(!args.transaction_stats.has_value());
found = true;
args.transaction_stats = std::move(transaction_stats);
break;
}
}
CHECK(found);
while (!pending_transaction_acks_.empty()) {
auto& args = pending_transaction_acks_.front();
if (!args.transaction_stats) {
break;
}
OrderedOnTransactionAckOnGpuThread(
std::move(args.completion_callback),
std::move(args.presentation_callback),
std::move(args.released_resources),
std::move(args.primary_plane_fences),
std::move(args.transaction_stats.value()));
pending_transaction_acks_.pop_front();
}
}
void GLSurfaceEGLSurfaceControl::OrderedOnTransactionAckOnGpuThread(
SwapCompletionCallback completion_callback,
PresentationCallback presentation_callback,
ResourceRefs released_resources,
std::optional<PrimaryPlaneFences> primary_plane_fences,
gfx::SurfaceControl::TransactionStats transaction_stats) {
TRACE_EVENT0("gpu",
"GLSurfaceEGLSurfaceControl::OnTransactionAckOnGpuThread");
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
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()) {
// TODO(vasilyt): We used to DCHECK(!surface_stat.fence.is_valid()) here,
// but due to flinger behavior it doesn't hold. This seems to be a
// potential fligner bug. DCHECK is useful for catching resource
// life-time issues, so we should consider bringing it back when Android
// side will be fixed.
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();
// The presentation feedback callback must run after swap completion.
std::move(completion_callback)
.Run(gfx::SwapCompletionResult(gfx::SwapResult::SWAP_ACK));
PendingPresentationCallback pending_cb;
if (primary_plane_fences) {
pending_cb.available_time =
GetSignalTime(primary_plane_fences->available_fence);
pending_cb.ready_time = GetSignalTime(primary_plane_fences->ready_fence);
}
pending_cb.latch_time = transaction_stats.latch_time;
pending_cb.present_fence = std::move(transaction_stats.present_fence);
pending_cb.callback = std::move(presentation_callback);
pending_presentation_callback_queue_.push(std::move(pending_cb));
if (!using_on_commit_callback_) {
CHECK(num_transaction_commit_or_ack_pending_);
num_transaction_commit_or_ack_pending_--;
}
CheckPendingPresentationCallbacks();
// If we don't use OnCommit, we advance transaction queue after we received
// OnComplete.
if (!use_target_deadline_ && !using_on_commit_callback_)
AdvanceTransactionQueue();
}
void GLSurfaceEGLSurfaceControl::OnTransactionCommittedOnGpuThread() {
TRACE_EVENT0("gpu",
"GLSurfaceEGLSurfaceControl::OnTransactionCommittedOnGpuThread");
DCHECK(using_on_commit_callback_);
CHECK(num_transaction_commit_or_ack_pending_);
num_transaction_commit_or_ack_pending_--;
AdvanceTransactionQueue();
}
void GLSurfaceEGLSurfaceControl::AdvanceTransactionQueue() {
if (!pending_transaction_queue_.empty()) {
num_transaction_commit_or_ack_pending_++;
pending_transaction_queue_.front().Apply();
pending_transaction_queue_.pop();
transaction_ack_timeout_manager_.ScheduleHangDetection();
}
}
void GLSurfaceEGLSurfaceControl::CheckPendingPresentationCallbacks() {
TRACE_EVENT0("gpu",
"GLSurfaceEGLSurfaceControl::CheckPendingPresentationCallbacks");
check_pending_presentation_callback_queue_task_.Cancel();
while (!pending_presentation_callback_queue_.empty()) {
auto& pending_cb = pending_presentation_callback_queue_.front();
base::TimeTicks signal_time;
auto status = pending_cb.present_fence.is_valid()
? gfx::GpuFence::GetStatusChangeTime(
pending_cb.present_fence.get(), &signal_time)
: gfx::GpuFence::kInvalid;
if (status == gfx::GpuFence::kNotSignaled)
break;
auto flags = gfx::PresentationFeedback::kHWCompletion |
gfx::PresentationFeedback::kVSync;
if (status == gfx::GpuFence::kInvalid) {
signal_time = pending_cb.latch_time;
flags = 0u;
}
TRACE_EVENT_INSTANT0(
"gpu",
"GLSurfaceEGLSurfaceControl::CheckPendingPresentationCallbacks - "
"presentation_feedback",
TRACE_EVENT_SCOPE_THREAD);
gfx::PresentationFeedback feedback(signal_time, base::TimeDelta(), flags);
feedback.available_timestamp = pending_cb.available_time;
feedback.ready_timestamp = pending_cb.ready_time;
feedback.latch_timestamp = pending_cb.latch_time;
std::move(pending_cb.callback).Run(feedback);
pending_presentation_callback_queue_.pop();
}
// If there are unsignaled fences and we don't have any pending transactions,
// schedule a task to poll the fences again. If there is a pending transaction
// already, then we'll poll when that transaction is acked.
// Note this check is interested in pending ack, not pending commit. However
// pending commit always implies pending ack, so there is no false negative
// where a recheck is necessary but not posted.
if (!pending_presentation_callback_queue_.empty() &&
pending_transaction_queue_.empty() &&
!num_transaction_commit_or_ack_pending_) {
check_pending_presentation_callback_queue_task_.Reset(base::BindOnce(
&GLSurfaceEGLSurfaceControl::CheckPendingPresentationCallbacks,
weak_factory_.GetWeakPtr()));
gpu_task_runner_->PostDelayedTask(
FROM_HERE, check_pending_presentation_callback_queue_task_.callback(),
base::Seconds(1) / 60);
}
}
void GLSurfaceEGLSurfaceControl::SetFrameRate(float frame_rate) {
if (frame_rate_ == frame_rate)
return;
frame_rate_ = frame_rate;
frame_rate_update_pending_ = true;
}
void GLSurfaceEGLSurfaceControl::SetChoreographerVsyncIdForNextFrame(
std::optional<int64_t> choreographer_vsync_id) {
choreographer_vsync_id_for_next_frame_ = choreographer_vsync_id;
}
GLSurfaceEGLSurfaceControl::SurfaceState::SurfaceState(
const gfx::SurfaceControl::Surface& parent,
const std::string& name)
: surface(new gfx::SurfaceControl::Surface(parent, name.c_str())) {}
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;
GLSurfaceEGLSurfaceControl::PendingPresentationCallback::
PendingPresentationCallback() = default;
GLSurfaceEGLSurfaceControl::PendingPresentationCallback::
~PendingPresentationCallback() = default;
GLSurfaceEGLSurfaceControl::PendingPresentationCallback::
PendingPresentationCallback(PendingPresentationCallback&& other) = default;
GLSurfaceEGLSurfaceControl::PendingPresentationCallback&
GLSurfaceEGLSurfaceControl::PendingPresentationCallback::operator=(
PendingPresentationCallback&& other) = default;
GLSurfaceEGLSurfaceControl::PrimaryPlaneFences::PrimaryPlaneFences() = default;
GLSurfaceEGLSurfaceControl::PrimaryPlaneFences::~PrimaryPlaneFences() = default;
GLSurfaceEGLSurfaceControl::PrimaryPlaneFences::PrimaryPlaneFences(
PrimaryPlaneFences&& other) = default;
GLSurfaceEGLSurfaceControl::PrimaryPlaneFences&
GLSurfaceEGLSurfaceControl::PrimaryPlaneFences::operator=(
PrimaryPlaneFences&& other) = default;
GLSurfaceEGLSurfaceControl::TransactionAckTimeoutManager::
TransactionAckTimeoutManager(
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: gpu_task_runner_(std::move(task_runner)) {}
GLSurfaceEGLSurfaceControl::TransactionAckTimeoutManager::
~TransactionAckTimeoutManager() = default;
void GLSurfaceEGLSurfaceControl::TransactionAckTimeoutManager::
ScheduleHangDetection() {
DCHECK(gpu_task_runner_->BelongsToCurrentThread());
++current_transaction_id_;
if (!hang_detection_cb_.IsCancelled())
return;
constexpr int kIdleDelaySeconds = 5;
hang_detection_cb_.Reset(
base::BindOnce(&GLSurfaceEGLSurfaceControl::TransactionAckTimeoutManager::
OnTransactionTimeout,
base::Unretained(this), current_transaction_id_));
gpu_task_runner_->PostDelayedTask(FROM_HERE, hang_detection_cb_.callback(),
base::Seconds(kIdleDelaySeconds));
}
void GLSurfaceEGLSurfaceControl::TransactionAckTimeoutManager::
OnTransactionAck() {
// Since only one transaction is in flight at a time, an ack is for the latest
// transaction.
last_acked_transaction_id_ = current_transaction_id_;
}
void GLSurfaceEGLSurfaceControl::TransactionAckTimeoutManager::
OnTransactionTimeout(TransactionId transaction_id) {
hang_detection_cb_.Cancel();
// If the last transaction was already acked, we do not need to schedule
// any checks until a new transaction comes.
if (current_transaction_id_ == last_acked_transaction_id_)
return;
// If more transactions have happened since the last task, schedule another
// hang detection check.
if (transaction_id < current_transaction_id_) {
// Decrement the |current_transaction_id_| since ScheduleHangDetection()
// will increment it again.
--current_transaction_id_;
ScheduleHangDetection();
return;
}
LOG(ERROR) << "Transaction id " << transaction_id
<< " haven't received any ack from past 5 second which indicates "
"it hanged";
}
GLSurfaceEGLSurfaceControl::OnTransactionAckArgs::OnTransactionAckArgs(
SequenceId id,
SwapCompletionCallback completion_callback,
PresentationCallback presentation_callback,
ResourceRefs released_resources,
std::optional<PrimaryPlaneFences> primary_plane_fences)
: id(id),
completion_callback(std::move(completion_callback)),
presentation_callback(std::move(presentation_callback)),
released_resources(std::move(released_resources)),
primary_plane_fences(std::move(primary_plane_fences)) {}
GLSurfaceEGLSurfaceControl::OnTransactionAckArgs::OnTransactionAckArgs(
OnTransactionAckArgs&& other) = default;
GLSurfaceEGLSurfaceControl::OnTransactionAckArgs&
GLSurfaceEGLSurfaceControl::OnTransactionAckArgs::operator=(
GLSurfaceEGLSurfaceControl::OnTransactionAckArgs&& other) = default;
GLSurfaceEGLSurfaceControl::OnTransactionAckArgs::~OnTransactionAckArgs() =
default;
} // namespace gl