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
|