File: ca_layer_tree_coordinator.mm

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (239 lines) | stat: -rw-r--r-- 8,571 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// 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_context.h"
#include "ui/gl/gl_implementation.h"

#if BUILDFLAG(IS_MAC)
#include "ui/accelerated_widget_mac/io_surface_context.h"
#include "ui/gl/gl_context.h"
#endif

namespace ui {

CALayerTreeCoordinator::CALayerTreeCoordinator(
    bool allow_av_sample_buffer_display_layer,
    BufferPresentedCallback buffer_presented_callback,
    id<MTLDevice> metal_device)
    : allow_remote_layers_(ui::RemoteLayerAPISupported()),
      allow_av_sample_buffer_display_layer_(
          allow_av_sample_buffer_display_layer),
      buffer_presented_callback_(buffer_presented_callback),
      metal_device_(metal_device) {
  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;
}

void CALayerTreeCoordinator::SetCALayerErrorCode(
    gfx::CALayerResult ca_layer_error_code) {
  ca_layer_error_code_ = ca_layer_error_code;
}

CARendererLayerTree* CALayerTreeCoordinator::GetPendingCARendererLayerTree() {
  if (!unpresented_ca_renderer_layer_tree_) {
    CHECK_LE(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, metal_device_);
  }
  return unpresented_ca_renderer_layer_tree_.get();
}

uint64_t CALayerTreeCoordinator::CreateBackpressureFence() {
  gl::GLContext* current_context = gl::GLContext::GetCurrent();
  if (current_context) {
    return current_context->BackpressureFenceCreate();
  }
  return 0;
}

void CALayerTreeCoordinator::ApplyBackpressure() {
  // No frame has been committed yet - this is the first frame being presented.
  if (presented_frames_.empty() || !presented_frames_.front()->has_committed) {
    return;
  }

  TRACE_EVENT0("gpu", "CALayerTreeCoordinator::ApplyBackpressure");

  // Apply back pressure to the previous frame.
  uint64_t frame_fence = presented_frames_.front()->backpressure_fence;

  // Waiting on the previous frame's fence (to maximize CPU and GPU execution
  // overlap).
  gl::GLContext* current_context = gl::GLContext::GetCurrent();
  if (current_context) {
    current_context->BackpressureFenceWait(frame_fence);
  }
}

void CALayerTreeCoordinator::Present(
    gl::Presenter::SwapCompletionCallback completion_callback,
    gl::Presenter::PresentationCallback presentation_callback) {
  presented_frames_.push(std::make_unique<PresentedFrame>(
      std::move(completion_callback), std::move(presentation_callback),
      CreateBackpressureFence(), 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;

  // Remove the committed frame which is displayed on the screen from the
  // |presented_frames_| queue;
  std::unique_ptr<CARendererLayerTree> current_tree;
  if (!presented_frames_.empty() && presented_frames_.front()->has_committed) {
    current_tree.swap(presented_frames_.front()->layer_tree);

    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)
    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