File: display_ca_layer_tree.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 (190 lines) | stat: -rw-r--r-- 6,304 bytes parent folder | download | duplicates (9)
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
// 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/accelerated_widget_mac/display_ca_layer_tree.h"

#import <QuartzCore/QuartzCore.h>

#include "base/apple/scoped_cftyperef.h"
#include "base/debug/dump_without_crashing.h"
#include "base/logging.h"
#include "base/mac/mac_util.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "ui/base/cocoa/animation_utils.h"
#include "ui/base/cocoa/remote_layer_api.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/size_conversions.h"

@interface CALayer (PrivateAPI)
- (void)setContentsChanged;
@end

namespace ui {
namespace {

// The maximum number of times to dump before throttling (to avoid sending
// thousands of crash dumps).
const int kMaxCrashDumps = 10;

}  // namespace

DisplayCALayerTree::DisplayCALayerTree(CALayer* root_layer)
    : root_layer_(root_layer) {
  // Disable the fade-in animation as the layers are added.
  ScopedCAActionDisabler disabler;

  // Add a flipped transparent layer as a child, so that we don't need to
  // fiddle with the position of sub-layers -- they will always be at the
  // origin. Note that flipping is only applicable to macOS.
  maybe_flipped_layer_ = [[CALayer alloc] init];
#if BUILDFLAG(IS_MAC)
  maybe_flipped_layer_.geometryFlipped = YES;
  maybe_flipped_layer_.autoresizingMask =
      kCALayerWidthSizable | kCALayerHeightSizable;
#endif
  maybe_flipped_layer_.anchorPoint = CGPointZero;
  [root_layer_ addSublayer:maybe_flipped_layer_];

#if BUILDFLAG(IS_IOS)
  [root_layer_ setDrawsAsynchronously:YES];
#endif
}

DisplayCALayerTree::~DisplayCALayerTree() {
  // Disable the fade-out animation as the view is removed.
  ScopedCAActionDisabler disabler;

  [maybe_flipped_layer_ removeFromSuperlayer];
  [remote_layer_ removeFromSuperlayer];
  [io_surface_layer_ removeFromSuperlayer];
  remote_layer_ = nil;
  io_surface_layer_ = nil;
}

void DisplayCALayerTree::UpdateCALayerTree(
    const gfx::CALayerParams& ca_layer_params) {
  // TODO(danakj): We should avoid lossy conversions to integer DIPs. The OS
  // wants a floating point value.
  gfx::Size dip_size = gfx::ToFlooredSize(gfx::ConvertSizeToDips(
      ca_layer_params.pixel_size, ca_layer_params.scale_factor));

  // iOS doesn't support autoresizing mask. Thus, adjust the bounds.
#if BUILDFLAG(IS_IOS)
  maybe_flipped_layer_.bounds =
      CGRectMake(0, 0, dip_size.width(), dip_size.height());

  if (maybe_flipped_layer_.contentsScale != ca_layer_params.scale_factor) {
    maybe_flipped_layer_.contentsScale = ca_layer_params.scale_factor;
  }
#endif

  // Remote layers are the most common case.
  if (ca_layer_params.ca_context_id) {
    GotCALayerFrame(ca_layer_params.ca_context_id);
    return;
  }

  // IOSurfaces can be sent from software compositing, or if remote layers are
  // manually disabled.
  if (ca_layer_params.io_surface_mach_port) {
    base::apple::ScopedCFTypeRef<IOSurfaceRef> io_surface(
        IOSurfaceLookupFromMachPort(
            ca_layer_params.io_surface_mach_port.get()));
    if (io_surface) {
      GotIOSurfaceFrame(io_surface, dip_size, ca_layer_params.scale_factor);
      return;
    }
    LOG(ERROR) << "Unable to open IOSurface for frame.";
    static int dump_counter = kMaxCrashDumps;
    if (dump_counter) {
      dump_counter -= 1;
      base::debug::DumpWithoutCrashing();
    }
  }

  // Warn if the frame specified neither.
  if (ca_layer_params.io_surface_mach_port && !ca_layer_params.ca_context_id) {
    LOG(ERROR) << "Frame had neither valid CAContext nor valid IOSurface.";
  }

  // If there was an error or if the frame specified nothing, then clear all
  // contents.
  if (io_surface_layer_ || remote_layer_) {
    ScopedCAActionDisabler disabler;
    [io_surface_layer_ removeFromSuperlayer];
    io_surface_layer_ = nil;
    [remote_layer_ removeFromSuperlayer];
    remote_layer_ = nil;
  }
}

void DisplayCALayerTree::GotCALayerFrame(uint32_t ca_context_id) {
  // Early-out if the remote layer has not changed.
  if (remote_layer_.contextId == ca_context_id) {
    return;
  }

  TRACE_EVENT0("ui", "DisplayCALayerTree::GotCAContextFrame");
  ScopedCAActionDisabler disabler;

  // Create the new CALayerHost.
  CALayerHost* new_remote_layer = [[CALayerHost alloc] init];
  // Anchor point on iOS might be at (0.5,0.5) as it's a default value there.
  // Thus, explicitly set it to (0,0), which doesn't hurt macOS as it also
  // expects to have all the attached layers of the context at (0,0).
  new_remote_layer.anchorPoint = CGPointZero;
  new_remote_layer.contextId = ca_context_id;
#if BUILDFLAG(IS_MAC)
  new_remote_layer.autoresizingMask = kCALayerMaxXMargin | kCALayerMaxYMargin;
#endif

  // Update the local CALayer tree.
  [maybe_flipped_layer_ addSublayer:new_remote_layer];
  [remote_layer_ removeFromSuperlayer];
  remote_layer_ = new_remote_layer;

  // Ensure that the IOSurface layer be removed.
  if (io_surface_layer_) {
    [io_surface_layer_ removeFromSuperlayer];
    io_surface_layer_ = nil;
  }
}

void DisplayCALayerTree::GotIOSurfaceFrame(
    base::apple::ScopedCFTypeRef<IOSurfaceRef> io_surface,
    const gfx::Size& dip_size,
    float scale_factor) {
  DCHECK(io_surface);
  TRACE_EVENT0("ui", "DisplayCALayerTree::GotIOSurfaceFrame");
  ScopedCAActionDisabler disabler;

  // Create (if needed) and update the IOSurface layer with new content.
  if (!io_surface_layer_) {
    io_surface_layer_ = [[CALayer alloc] init];
    io_surface_layer_.contentsGravity = kCAGravityTopLeft;
    io_surface_layer_.anchorPoint = CGPointZero;
    [maybe_flipped_layer_ addSublayer:io_surface_layer_];
  }
  id new_contents = (__bridge id)io_surface.get();
  if (new_contents && new_contents == io_surface_layer_.contents) {
    [io_surface_layer_ setContentsChanged];
  } else {
    io_surface_layer_.contents = new_contents;
  }

  io_surface_layer_.bounds =
      CGRectMake(0, 0, dip_size.width(), dip_size.height());
  if (io_surface_layer_.contentsScale != scale_factor) {
    io_surface_layer_.contentsScale = scale_factor;
  }

  // Ensure that the remote layer be removed.
  if (remote_layer_) {
    [remote_layer_ removeFromSuperlayer];
    remote_layer_ = nil;
  }
}

}  // namespace ui