0

[ios blink] Use BELayerHierarchy for passing surfaces

The CAContext id way of passing surfaces between the GPU process and
browser did not work on device. Move to the actual supported API
of binding a BELayerHierarchy and BELayerHierarchyHostingView
to simplify the code.

One complexity is that the BELayerHierarchy needs to be passed
as an xpc_object across the channel. Since this can't go across
mojo we have to hook it to pass it across the original xpc_connection
that bootstrapped mojo.

Bug: 40254930
Change-Id: If551283037519754641e44402e474c4ad9048657
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6254053
Reviewed-by: ccameron chromium <ccameron@chromium.org>
Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1419977}
This commit is contained in:
Dave Tapuska
2025-02-13 10:10:56 -08:00
committed by Chromium LUCI CQ
parent 8adb2dee63
commit 700bf2c06e
23 changed files with 249 additions and 23 deletions

@ -2,3 +2,6 @@ per-file sandbox_helper_win.cc=file://sandbox/win/OWNERS
# For threading and startup/shutdown sequences (ContentMainRunner, etc.)
per-file content_main_runner*=gab@chromium.org
# Security reviews for xpc IPC
per-file ios/appex/child_process_bridge.mm=file://ipc/SECURITY_OWNERS

@ -22,6 +22,7 @@ source_set("child_process_bridge") {
deps = [
":child_process_bridge_header",
"//base",
"//gpu/ipc/common",
]
frameworks = [ "Foundation.framework" ]
}

@ -17,6 +17,7 @@ extern "C" {
#endif
void ChildProcessInit(id<ChildProcessExtension> process);
void GpuProcessInit();
void ChildProcessHandleNewConnection(xpc_connection_t connection);
#ifdef __cplusplus

@ -15,21 +15,47 @@
#include "base/apple/bundle_locations.h"
#include "base/apple/mach_port_rendezvous.h"
#include "base/check_op.h"
#include "base/logging.h"
#include "base/system/sys_info.h"
#include "content/app/ios/appex/child_process_sandbox.h"
#include "gpu/ipc/common/ios/be_layer_hierarchy_transport.h"
class GPUProcessTransport;
// Leaked variables for now.
static size_t g_argc = 0;
static const char** g_argv = nullptr;
static pthread_t g_main_thread;
static id<ChildProcessExtension> g_swift_process;
static xpc_connection_t g_connection;
static std::unique_ptr<GPUProcessTransport> g_gpu_transport;
#define IOS_INIT_EXPORT __attribute__((visibility("default")))
// The embedder must implement this.
extern "C" int ChildProcessMain(int argc, const char** argv);
class GPUProcessTransport : public gpu::BELayerHierarchyTransport {
public:
GPUProcessTransport() { gpu::BELayerHierarchyTransport::SetInstance(this); }
~GPUProcessTransport() override {
gpu::BELayerHierarchyTransport::SetInstance(nullptr);
}
void ForwardBELayerHierarchyToBrowser(
gpu::SurfaceHandle surface_handle,
xpc_object_t ipc_representation) override {
xpc_object_t message = xpc_dictionary_create(nil, nil, 0);
xpc_dictionary_set_string(message, "message", "layerHandle");
xpc_dictionary_set_value(message, "layer", ipc_representation);
xpc_dictionary_set_uint64(message, "handle", surface_handle);
xpc_connection_send_message(g_connection, message);
}
};
extern "C" IOS_INIT_EXPORT void GpuProcessInit() {
g_gpu_transport = std::make_unique<GPUProcessTransport>();
}
extern "C" IOS_INIT_EXPORT void ChildProcessInit(
id<ChildProcessExtension> process) {
// Up two levels: chrome.app/Extensions/chrome_content_process.appex
@ -81,6 +107,7 @@ extern "C" IOS_INIT_EXPORT void ChildProcessHandleNewConnection(
pthread_create(&g_main_thread, NULL, RunMain, NULL);
});
xpc_connection_activate(connection);
g_connection = connection;
}
namespace content {

@ -11,6 +11,7 @@ class GPUProcess: NSObject, ChildProcessExtension, RenderingExtension {
override required init() {
super.init()
ChildProcessInit(self)
GpuProcessInit()
}
public func handle(xpcConnection: xpc_connection_t) {

@ -20,6 +20,7 @@ per-file browser_child_process_host_impl_receiver_bindings.*=set noparent
per-file browser_child_process_host_impl_receiver_bindings.*=file://ipc/SECURITY_OWNERS
per-file utility_process_host_receiver_bindings.*=set noparent
per-file utility_process_host_receiver_bindings.*=file://ipc/SECURITY_OWNERS
per-file child_process_launcher_helper_ios.mm=file://ipc/SECURITY_OWNERS
per-file host_zoom_*=wjmaclean@chromium.org
per-file site_per_process_*=kenrb@chromium.org

@ -15,7 +15,9 @@
#include "content/browser/child_process_launcher_helper_posix.h"
#include "content/public/browser/child_process_launcher_utils.h"
#include "content/public/common/content_switches.h"
#include "gpu/ipc/common/surface_handle.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "ui/accelerated_widget_mac/ca_layer_frame_sink_provider.h"
namespace content {
namespace internal {
@ -280,10 +282,51 @@ void ChildProcessLauncherHelper::OnChildProcessStarted(
xpc_connection_t xpc_connection =
launch_result->CreateXPCConnection(&error);
if (xpc_connection) {
scoped_refptr<base::SequencedTaskRunner> client_task_runner =
client_task_runner_;
bool is_gpu_process = GetProcessType() == switches::kGpuProcess;
xpc_connection_set_event_handler(xpc_connection, ^(xpc_object_t event) {
if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
if (event == XPC_ERROR_CONNECTION_INTERRUPTED ||
event == XPC_ERROR_CONNECTION_INVALID) {
OnChildProcessTerminatedOnAnyThread(process_id);
}
const char* message_type = xpc_dictionary_get_string(event, "message");
if (message_type && strcmp(message_type, "layerHandle") == 0) {
// We only expect this message from the GPU process.
if (!is_gpu_process) {
xpc_connection_cancel(xpc_connection);
return;
}
xpc_object_t ca_layer_handle =
xpc_dictionary_get_value(event, "layer");
gpu::SurfaceHandle view_handle =
xpc_dictionary_get_uint64(event, "handle");
// Validate arguments.
if (!ca_layer_handle || !view_handle) {
xpc_connection_cancel(xpc_connection);
return;
}
client_task_runner->PostTask(
FROM_HERE,
base::BindOnce(
[](gpu::SurfaceHandle view_handle,
xpc_object_t ca_layer_handle) {
NSError* error = nullptr;
BELayerHierarchyHandle* be_handle = [BELayerHierarchyHandle
handleWithXPCRepresentation:ca_layer_handle
error:&error];
CALayerFrameSinkProvider* sink =
[CALayerFrameSinkProvider lookupByHandle:view_handle];
if (sink) {
sink.handle = be_handle;
}
},
view_handle, ca_layer_handle));
}
});
xpc_connection_resume(xpc_connection);
xpc_object_t message = xpc_dictionary_create(nil, nil, 0);

@ -21,7 +21,6 @@
#include "ui/events/gesture_detection/filtered_gesture_provider.h"
namespace ui {
class DisplayCALayerTree;
enum class DomCode : uint32_t;
} // namespace ui
@ -263,7 +262,6 @@ class CONTENT_EXPORT RenderWidgetHostViewIOS
std::unique_ptr<BrowserCompositorIOS> browser_compositor_;
std::unique_ptr<UIViewHolder> ui_view_;
std::unique_ptr<ui::DisplayCALayerTree> display_tree_;
base::WeakPtrFactory<RenderWidgetHostViewIOS> weak_factory_{this};
};

@ -92,15 +92,12 @@ RenderWidgetHostViewIOS::RenderWidgetHostViewIOS(RenderWidgetHost* widget)
ui_view_->view_ =
[[RenderWidgetUIView alloc] initWithWidget:weak_factory_.GetWeakPtr()];
display_tree_ =
std::make_unique<ui::DisplayCALayerTree>([ui_view_->view_ layer]);
auto* screen = display::Screen::GetScreen();
screen_infos_ =
screen->GetScreenInfosNearestDisplay(screen->GetPrimaryDisplay().id());
browser_compositor_ = std::make_unique<BrowserCompositorIOS>(
(uint64_t)(__bridge void*)ui_view_->view_, this, host()->is_hidden(),
[ui_view_->view_ viewHandle], this, host()->is_hidden(),
host()->GetFrameSinkId());
if (IsTesting()) {
@ -407,10 +404,7 @@ void RenderWidgetHostViewIOS::OnSynchronizedDisplayPropertiesChanged(
}
void RenderWidgetHostViewIOS::UpdateCALayerTree(
const gfx::CALayerParams& ca_layer_params) {
DCHECK(display_tree_);
display_tree_->UpdateCALayerTree(ca_layer_params);
}
const gfx::CALayerParams& ca_layer_params) {}
void RenderWidgetHostViewIOS::OnOldViewDidNavigatePreCommit() {
CHECK(browser_compositor_) << "Shouldn't be called during destruction!";

@ -158,6 +158,13 @@ source_set("ipc_common_sources") {
frameworks = [ "IOSurface.framework" ]
}
if (is_ios) {
sources += [
"ios/be_layer_hierarchy_transport.cc",
"ios/be_layer_hierarchy_transport.h",
]
}
if (!is_nacl && !is_minimal_toolchain) {
deps += [ "//ui/gfx/ipc/skia" ]
}

@ -0,0 +1,29 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/ipc/common/ios/be_layer_hierarchy_transport.h"
#include "base/check.h"
namespace gpu {
namespace {
BELayerHierarchyTransport* g_instance = nullptr;
} // namespace
// static
BELayerHierarchyTransport* BELayerHierarchyTransport::GetInstance() {
DCHECK(g_instance);
return g_instance;
}
// static
void BELayerHierarchyTransport::SetInstance(
BELayerHierarchyTransport* instance) {
DCHECK(!g_instance || !instance);
g_instance = instance;
}
} // namespace gpu

@ -0,0 +1,35 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GPU_IPC_COMMON_IOS_BE_LAYER_HIERARCHY_TRANSPORT_H_
#define GPU_IPC_COMMON_IOS_BE_LAYER_HIERARCHY_TRANSPORT_H_
#include <xpc/xpc.h>
#include "gpu/gpu_export.h"
#include "gpu/ipc/common/surface_handle.h"
namespace gpu {
// Allows the BELayerHierarchy to be transported from the GPU process
// to the browser process. Since BELayerHierarchy is only serializable
// over XPC (and not mojo) it needs a hook into the XPC IPC channel.
class GPU_EXPORT BELayerHierarchyTransport {
public:
static BELayerHierarchyTransport* GetInstance();
static void SetInstance(BELayerHierarchyTransport* instance);
// Sends the BELayerHierarchy represented as an xpc_object_t that
// is associated with `surface_handle` to the browser process.
virtual void ForwardBELayerHierarchyToBrowser(
SurfaceHandle surface_handle,
xpc_object_t ipc_representation) = 0;
protected:
virtual ~BELayerHierarchyTransport() {}
};
} // namespace gpu
#endif // GPU_IPC_COMMON_IOS_BE_LAYER_HIERARCHY_TRANSPORT_H_

@ -22,7 +22,7 @@ scoped_refptr<gl::Presenter> ImageTransportSurface::CreatePresenter(
if (gl::GetGLImplementation() == gl::kGLImplementationEGLGLES2 ||
gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE) {
return base::MakeRefCounted<ImageTransportSurfaceOverlayMacEGL>(
dawn_context_provider);
surface_handle, dawn_context_provider);
}
return nullptr;

@ -22,7 +22,7 @@ scoped_refptr<gl::Presenter> ImageTransportSurface::CreatePresenter(
if (gl::GetGLImplementation() == gl::kGLImplementationEGLGLES2 ||
gl::GetGLImplementation() == gl::kGLImplementationEGLANGLE) {
return base::MakeRefCounted<ImageTransportSurfaceOverlayMacEGL>(
dawn_context_provider);
surface_handle, dawn_context_provider);
}
return nullptr;

@ -23,6 +23,10 @@
#include "ui/display/types/display_constants.h"
#endif
#if BUILDFLAG(IS_IOS)
#include <BrowserEngineKit/BrowserEngineKit.h>
#endif
@class CAContext;
@class CALayer;
@ -36,6 +40,7 @@ namespace gpu {
class ImageTransportSurfaceOverlayMacEGL : public gl::Presenter {
public:
ImageTransportSurfaceOverlayMacEGL(
SurfaceHandle surface_handle,
DawnContextProvider* dawn_context_provider);
// Presenter implementation
@ -105,6 +110,10 @@ class ImageTransportSurfaceOverlayMacEGL : public gl::Presenter {
base::TimeDelta frame_interval_;
#endif
#if BUILDFLAG(IS_IOS)
BELayerHierarchy* __strong layer_hierarchy_;
#endif
int cap_max_pending_swaps_ = 1;
raw_ptr<DawnContextProvider> dawn_context_provider_ = nullptr;

@ -27,6 +27,10 @@
#include "ui/gfx/overlay_plane_data.h"
#include "ui/gl/ca_renderer_layer_params.h"
#if BUILDFLAG(IS_IOS)
#include "gpu/ipc/common/ios/be_layer_hierarchy_transport.h"
#endif
// From ANGLE's EGL/eglext_angle.h. This should be included instead of being
// redefined here.
#ifndef EGL_ANGLE_device_metal
@ -64,6 +68,7 @@ void RecordVSyncCallbackDelay(base::TimeDelta delay) {
} // namespace
ImageTransportSurfaceOverlayMacEGL::ImageTransportSurfaceOverlayMacEGL(
SurfaceHandle surface_handle,
DawnContextProvider* dawn_context_provider)
: dawn_context_provider_(dawn_context_provider), weak_ptr_factory_(this) {
static bool av_disabled_at_command_line =
@ -75,10 +80,36 @@ ImageTransportSurfaceOverlayMacEGL::ImageTransportSurfaceOverlayMacEGL(
ca_layer_tree_coordinator_ = std::make_unique<ui::CALayerTreeCoordinator>(
!av_disabled_at_command_line, std::move(buffer_presented_callback));
#if BUILDFLAG(IS_IOS)
// The BELayerHierarchy needs to be created on a thread that supports
// libdispatch, so we proxy over to the main dispatch queue to do that.
CALayer* root_ca_layer = ca_layer_tree_coordinator_->root_ca_layer();
__block xpc_object_t ipc_representation;
dispatch_sync(dispatch_get_main_queue(), ^{
NSError* error = nullptr;
layer_hierarchy_ = [BELayerHierarchy layerHierarchyWithError:&error];
layer_hierarchy_.layer = root_ca_layer;
ipc_representation = [layer_hierarchy_.handle createXPCRepresentation];
});
BELayerHierarchyTransport* transport =
BELayerHierarchyTransport::GetInstance();
CHECK(transport);
transport->ForwardBELayerHierarchyToBrowser(surface_handle,
ipc_representation);
#endif
}
ImageTransportSurfaceOverlayMacEGL::~ImageTransportSurfaceOverlayMacEGL() {
ca_layer_tree_coordinator_.reset();
#if BUILDFLAG(IS_IOS)
BELayerHierarchy* layer_hierarchy = std::move(layer_hierarchy_);
dispatch_async(dispatch_get_main_queue(), ^{
[layer_hierarchy invalidate];
});
#endif
}
void ImageTransportSurfaceOverlayMacEGL::BufferPresented(

@ -13,8 +13,6 @@ component("accelerated_widget_mac") {
"ca_layer_tree_coordinator.mm",
"ca_renderer_layer_tree.h",
"ca_renderer_layer_tree.mm",
"display_ca_layer_tree.h",
"display_ca_layer_tree.mm",
]
defines = [
@ -47,6 +45,8 @@ component("accelerated_widget_mac") {
"accelerated_widget_mac_export.h",
"ca_transaction_observer.h",
"ca_transaction_observer.mm",
"display_ca_layer_tree.h",
"display_ca_layer_tree.mm",
"io_surface_context.h",
"io_surface_context.mm",
"window_resize_helper_mac.cc",

@ -5,6 +5,7 @@
#ifndef UI_ACCELERATED_WIDGET_MAC_CA_LAYER_FRAME_SINK_H_
#define UI_ACCELERATED_WIDGET_MAC_CA_LAYER_FRAME_SINK_H_
#include "build/build_config.h"
#include "ui/accelerated_widget_mac/accelerated_widget_mac_export.h"
#include "ui/gfx/ca_layer_params.h"
#include "ui/gfx/native_widget_types.h"

@ -21,9 +21,8 @@ CALayerFrameSink* CALayerFrameSink::FromAcceleratedWidget(
#if BUILDFLAG(IS_MAC)
return AcceleratedWidgetMac::Get(widget);
#else
id object = (__bridge id)(void*)widget;
if ([object isKindOfClass:[CALayerFrameSinkProvider class]]) {
return [(CALayerFrameSinkProvider*)object frameSink];
if (auto* provider = [CALayerFrameSinkProvider lookupByHandle:widget]) {
return [provider frameSink];
}
return nullptr;
#endif

@ -5,6 +5,7 @@
#ifndef UI_ACCELERATED_WIDGET_MAC_CA_LAYER_FRAME_SINK_PROVIDER_H_
#define UI_ACCELERATED_WIDGET_MAC_CA_LAYER_FRAME_SINK_PROVIDER_H_
#include <BrowserEngineKit/BrowserEngineKit.h>
#include <UIKit/UIKit.h>
#include "ui/accelerated_widget_mac/accelerated_widget_mac_export.h"
@ -15,9 +16,11 @@ namespace ui {
class CALayerFrameSink;
}
@interface CALayerFrameSinkProvider : UIView
@interface CALayerFrameSinkProvider : BELayerHierarchyHostingView
- (id)init;
- (ui::CALayerFrameSink*)frameSink;
- (gfx::AcceleratedWidget)viewHandle;
+ (CALayerFrameSinkProvider*)lookupByHandle:(uint64_t)viewHandle;
@end
#endif // UI_ACCELERATED_WIDGET_MAC_CA_LAYER_FRAME_SINK_PROVIDER_H_

@ -4,8 +4,49 @@
#import "ui/accelerated_widget_mac/ca_layer_frame_sink_provider.h"
@implementation CALayerFrameSinkProvider
#include <map>
namespace {
std::map<gfx::AcceleratedWidget, CALayerFrameSinkProvider*>&
WidgetToSinkProviderMap() {
static std::map<gfx::AcceleratedWidget, CALayerFrameSinkProvider*> map;
return map;
}
} // namespace
@implementation CALayerFrameSinkProvider {
uint64_t _view_handle;
}
- (ui::CALayerFrameSink*)frameSink {
return nil;
}
- (gfx::AcceleratedWidget)viewHandle {
return _view_handle;
}
- (id)init {
self = [super init];
if (self) {
static uint64_t last_sequence_number = 0;
_view_handle = ++last_sequence_number;
WidgetToSinkProviderMap().insert(std::make_pair(_view_handle, self));
}
return self;
}
- (void)dealloc {
WidgetToSinkProviderMap().erase(_view_handle);
}
+ (CALayerFrameSinkProvider*)lookupByHandle:(uint64_t)viewHandle {
auto found = WidgetToSinkProviderMap().find(viewHandle);
if (found == WidgetToSinkProviderMap().end()) {
return nullptr;
}
return found->second;
}
@end

@ -95,6 +95,8 @@ class ACCELERATED_WIDGET_MAC_EXPORT CALayerTreeCoordinator {
int NumPendingSwaps();
CALayer* root_ca_layer() { return root_ca_layer_; }
private:
uint64_t CreateBackpressureFence();

@ -236,7 +236,7 @@ using NativeViewId = intptr_t;
using AcceleratedWidget = HWND;
constexpr AcceleratedWidget kNullAcceleratedWidget = nullptr;
#elif BUILDFLAG(IS_IOS)
using AcceleratedWidget = uint64_t; // A UIView*.
using AcceleratedWidget = uint64_t;
constexpr AcceleratedWidget kNullAcceleratedWidget = 0;
#elif BUILDFLAG(IS_MAC)
using AcceleratedWidget = uint64_t;