blob: b4f00285fd1be44ef83d0dab87340070741bc087 [file] [log] [blame]
// Copyright 2016 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/accelerated_widget_mac/ca_layer_tree_coordinator.h"
#import <AVFoundation/AVFoundation.h>
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "ui/base/cocoa/animation_utils.h"
#include "ui/base/cocoa/remote_layer_api.h"
#include "ui/gfx/ca_layer_params.h"
#include "ui/gl/gl_implementation.h"
namespace ui {
CALayerTreeCoordinator::CALayerTreeCoordinator(
bool allow_av_sample_buffer_display_layer,
bool new_presentation_feedback_timestamps,
BufferPresentedCallback buffer_presented_callback)
: allow_remote_layers_(ui::RemoteLayerAPISupported()),
allow_av_sample_buffer_display_layer_(
allow_av_sample_buffer_display_layer),
new_presentation_feedback_timestamps_(
new_presentation_feedback_timestamps),
buffer_presented_callback_(buffer_presented_callback) {
if (allow_remote_layers_) {
root_ca_layer_ = [[CALayer alloc] init];
#if BUILDFLAG(IS_MAC)
// iOS' UIKit has default coordinate system where the origin is at the upper
// left of the drawing area. In contrast, AppKit and Core Graphics that
// macOS uses has its origin at the lower left of the drawing area. Thus, we
// don't need to flip the coordinate system on iOS as it's already set the
// way we want it to be.
root_ca_layer_.geometryFlipped = YES;
#endif
root_ca_layer_.opaque = YES;
// Create the CAContext to send this to the GPU process, and the layer for
// the context.
#if BUILDFLAG(IS_MAC)
CGSConnectionID connection_id = CGSMainConnectionID();
ca_context_ = [CAContext contextWithCGSConnection:connection_id
options:@{}];
#else
// Use a very large display ID to ensure that the context is never put
// on-screen without being explicitly parented.
ca_context_ = [CAContext remoteContextWithOptions:@{
kCAContextIgnoresHitTest : @YES,
kCAContextDisplayId : @10000
}];
#endif
ca_context_.layer = root_ca_layer_;
}
}
CALayerTreeCoordinator::~CALayerTreeCoordinator() = default;
void CALayerTreeCoordinator::Resize(const gfx::Size& pixel_size,
float scale_factor) {
pixel_size_ = pixel_size;
scale_factor_ = scale_factor;
}
CARendererLayerTree* CALayerTreeCoordinator::GetPendingCARendererLayerTree() {
if (!unpresented_ca_renderer_layer_tree_) {
CHECK_LT(presented_frames_.size(), presented_ca_layer_trees_max_length_);
unpresented_ca_renderer_layer_tree_ = std::make_unique<CARendererLayerTree>(
allow_av_sample_buffer_display_layer_, false);
}
return unpresented_ca_renderer_layer_tree_.get();
}
uint64_t CALayerTreeCoordinator::GetCurrentCommittedFrameFence() const {
if (!presented_frames_.empty() && presented_frames_.front()->has_committed) {
return presented_frames_.front()->backpressure_fence;
} else {
return 0;
}
}
void CALayerTreeCoordinator::Present(
gl::Presenter::SwapCompletionCallback completion_callback,
gl::Presenter::PresentationCallback presentation_callback,
uint64_t backpressure_fence,
gfx::CALayerResult ca_layer_error_code) {
presented_frames_.push(std::make_unique<PresentedFrame>(
std::move(completion_callback), std::move(presentation_callback),
backpressure_fence, ca_layer_error_code,
/*ready_timestamp=*/base::TimeTicks::Now(),
std::move(unpresented_ca_renderer_layer_tree_)));
}
void CALayerTreeCoordinator::CommitPresentedFrameToCA(
base::TimeDelta frame_interval,
base::TimeTicks display_time) {
// Update the CALayer hierarchy.
ScopedCAActionDisabler disabler;
std::unique_ptr<CARendererLayerTree> current_tree;
if (!presented_frames_.empty() && presented_frames_.front()->has_committed) {
current_tree.swap(presented_frames_.front()->layer_tree);
// Now we are done with the current committed frame. Remove it from the
// |presented_frames_| queue;
presented_frames_.pop();
}
if (presented_frames_.empty()) {
TRACE_EVENT0("gpu", "Blank frame: No overlays or CALayers");
DLOG(WARNING) << "Blank frame: No overlays or CALayers";
root_ca_layer_.sublayers = nil;
return;
}
// Get the frame to be committed.
auto* frame = presented_frames_.front().get();
DCHECK(frame);
if (frame->layer_tree) {
frame->layer_tree->CommitScheduledCALayers(
root_ca_layer_, std::move(current_tree), pixel_size_, scale_factor_);
} else {
root_ca_layer_.sublayers = nil;
}
frame->has_committed = true;
// Populate the CA layer parameters to send to the browser.
// Send the swap parameters to the browser.
if (frame->completion_callback) {
gfx::CALayerParams params;
TRACE_EVENT_INSTANT2("test_gpu", "SwapBuffers", TRACE_EVENT_SCOPE_THREAD,
"GLImpl", static_cast<int>(gl::GetGLImplementation()),
"width", pixel_size_.width());
if (allow_remote_layers_) {
params.ca_context_id = [ca_context_ contextId];
} else {
IOSurfaceRef io_surface = frame->layer_tree->GetContentIOSurface();
if (io_surface) {
DCHECK(!allow_remote_layers_);
params.io_surface_mach_port.reset(IOSurfaceCreateMachPort(io_surface));
}
}
params.pixel_size = pixel_size_;
params.scale_factor = scale_factor_;
params.is_empty = false;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(frame->completion_callback),
gfx::SwapCompletionResult(
gfx::SwapResult::SWAP_ACK,
std::make_unique<gfx::CALayerParams>(params))));
}
gfx::PresentationFeedback feedback(base::TimeTicks::Now(), base::Hertz(60),
/*flags=*/0);
feedback.ca_layer_error_code = frame->ca_layer_error_code;
#if BUILDFLAG(IS_MAC)
if (new_presentation_feedback_timestamps_) {
feedback.ready_timestamp = frame->ready_timestamp;
feedback.latch_timestamp = base::TimeTicks::Now();
feedback.interval = frame_interval;
feedback.timestamp = display_time;
// `update_vsync_params_callback` is not available in
// SkiaOutputSurfaceImpl::BufferPresented(). Setting kVSync here will not
// update vsync params.
feedback.flags = gfx::PresentationFeedback::kHWCompletion |
gfx::PresentationFeedback::kVSync;
}
#endif
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(buffer_presented_callback_,
std::move(frame->presentation_callback), feedback));
}
void CALayerTreeCoordinator::SetMaxCALayerTrees(int max_ca_layer_trees) {
presented_ca_layer_trees_max_length_ = max_ca_layer_trees;
}
int CALayerTreeCoordinator::NumPendingSwaps() {
int num = presented_frames_.size();
if (num > 0 && presented_frames_.front()->has_committed) {
num--;
}
return num;
}
PresentedFrame::PresentedFrame(
gl::Presenter::SwapCompletionCallback completion_cb,
gl::Presenter::PresentationCallback presentation_cb,
uint64_t fence,
gfx::CALayerResult error_code,
base::TimeTicks ready_timestamp,
std::unique_ptr<CARendererLayerTree> tree)
: completion_callback(std::move(completion_cb)),
presentation_callback(std::move(presentation_cb)),
backpressure_fence(fence),
ca_layer_error_code(error_code),
ready_timestamp(ready_timestamp),
layer_tree(std::move(tree)),
has_committed(false) {}
PresentedFrame::~PresentedFrame() = default;
} // namespace ui