Vendor the current version of WebRender

This is a step toward upgrading WebRender, which will be upgraded and
patched in the `third_party` directory. This change vendors the current
private branch of WebRender that we use and adds a `patches` directory
which tracks the changes on top of the upstream WebRender commit
described by third_party/webrender/patches/head.
This commit is contained in:
Martin Robinson 2023-07-03 17:43:57 +02:00
parent c19eb800de
commit 49277f5c3f
No known key found for this signature in database
GPG key ID: D56AA4FA55EFE6F8
1215 changed files with 185677 additions and 34 deletions

View file

@ -0,0 +1,13 @@
[package]
name = "compositor"
version = "0.1.0"
authors = ["Glenn Watson <gw@intuitionlibrary.com>"]
edition = "2018"
license = "MPL-2.0"
[dependencies]
webrender = { path = "../../webrender" }
gleam = "0.12.0"
[target.'cfg(windows)'.dependencies]
compositor-windows = { path = "../compositor-windows" }

View file

@ -0,0 +1,484 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
An example of how to implement the Compositor trait that
allows picture caching surfaces to be composited by the operating
system.
The current example supports DirectComposite on Windows only.
*/
use euclid::Angle;
use gleam::gl;
use std::ffi::CString;
use std::sync::mpsc;
use webrender::api::*;
use webrender::api::units::*;
#[cfg(target_os = "windows")]
use compositor_windows as compositor;
use std::{env, f32, process};
// A very hacky integration with DirectComposite. It proxies calls from the compositor
// interface to a simple C99 library which does the DirectComposition / D3D11 / ANGLE
// interfacing. This is a very unsafe impl due to the way the window pointer is passed
// around!
struct DirectCompositeInterface {
window: *mut compositor::Window,
}
impl DirectCompositeInterface {
fn new(window: *mut compositor::Window) -> Self {
DirectCompositeInterface {
window,
}
}
}
impl webrender::Compositor for DirectCompositeInterface {
fn create_surface(
&mut self,
id: webrender::NativeSurfaceId,
tile_size: DeviceIntSize,
is_opaque: bool,
) {
compositor::create_surface(
self.window,
id.0,
tile_size.width,
tile_size.height,
is_opaque,
);
}
fn destroy_surface(
&mut self,
id: webrender::NativeSurfaceId,
) {
compositor::destroy_surface(self.window, id.0);
}
fn create_tile(
&mut self,
id: webrender::NativeTileId,
) {
compositor::create_tile(
self.window,
id.surface_id.0,
id.x,
id.y,
);
}
fn destroy_tile(
&mut self,
id: webrender::NativeTileId,
) {
compositor::destroy_tile(
self.window,
id.surface_id.0,
id.x,
id.y,
);
}
fn bind(
&mut self,
id: webrender::NativeTileId,
dirty_rect: DeviceIntRect,
) -> webrender::NativeSurfaceInfo {
let (fbo_id, x, y) = compositor::bind_surface(
self.window,
id.surface_id.0,
id.x,
id.y,
dirty_rect.origin.x,
dirty_rect.origin.y,
dirty_rect.size.width,
dirty_rect.size.height,
);
webrender::NativeSurfaceInfo {
origin: DeviceIntPoint::new(x, y),
fbo_id,
}
}
fn unbind(&mut self) {
compositor::unbind_surface(self.window);
}
fn begin_frame(&mut self) {
compositor::begin_transaction(self.window);
}
fn add_surface(
&mut self,
id: webrender::NativeSurfaceId,
position: DeviceIntPoint,
clip_rect: DeviceIntRect,
) {
compositor::add_surface(
self.window,
id.0,
position.x,
position.y,
clip_rect.origin.x,
clip_rect.origin.y,
clip_rect.size.width,
clip_rect.size.height,
);
}
fn end_frame(&mut self) {
compositor::end_transaction(self.window);
}
}
// Simplisitic implementation of the WR notifier interface to know when a frame
// has been prepared and can be rendered.
struct Notifier {
tx: mpsc::Sender<()>,
}
impl Notifier {
fn new(tx: mpsc::Sender<()>) -> Self {
Notifier {
tx,
}
}
}
impl RenderNotifier for Notifier {
fn clone(&self) -> Box<dyn RenderNotifier> {
Box::new(Notifier {
tx: self.tx.clone()
})
}
fn wake_up(&self) {
}
fn new_frame_ready(&self,
_: DocumentId,
_scrolled: bool,
_composite_needed: bool,
_render_time: Option<u64>) {
self.tx.send(()).ok();
}
}
fn push_rotated_rect(
builder: &mut DisplayListBuilder,
rect: LayoutRect,
color: ColorF,
spatial_id: SpatialId,
root_pipeline_id: PipelineId,
angle: f32,
time: f32,
) {
let color = color.scale_rgb(time);
let rotation = LayoutTransform::create_rotation(
0.0,
0.0,
1.0,
Angle::radians(2.0 * std::f32::consts::PI * angle),
);
let transform_origin = LayoutVector3D::new(
rect.origin.x + rect.size.width * 0.5,
rect.origin.y + rect.size.height * 0.5,
0.0,
);
let transform = rotation
.pre_translate(-transform_origin)
.post_translate(transform_origin);
let spatial_id = builder.push_reference_frame(
LayoutPoint::zero(),
spatial_id,
TransformStyle::Flat,
PropertyBinding::Value(transform),
ReferenceFrameKind::Transform,
);
builder.push_rect(
&CommonItemProperties::new(
rect,
SpaceAndClipInfo {
spatial_id,
clip_id: ClipId::root(root_pipeline_id),
},
),
rect,
color,
);
}
fn build_display_list(
builder: &mut DisplayListBuilder,
scroll_id: ExternalScrollId,
root_pipeline_id: PipelineId,
layout_size: LayoutSize,
time: f32,
invalidations: Invalidations,
) {
let size_factor = match invalidations {
Invalidations::Small => 0.1,
Invalidations::Large | Invalidations::Scrolling => 1.0,
};
let fixed_space_info = SpaceAndClipInfo {
spatial_id: SpatialId::root_scroll_node(root_pipeline_id),
clip_id: ClipId::root(root_pipeline_id),
};
let scroll_space_info = builder.define_scroll_frame(
&fixed_space_info,
Some(scroll_id),
LayoutRect::new(LayoutPoint::zero(), layout_size),
LayoutRect::new(LayoutPoint::zero(), layout_size),
ScrollSensitivity::Script,
LayoutVector2D::zero(),
);
builder.push_rect(
&CommonItemProperties::new(
LayoutRect::new(LayoutPoint::zero(), layout_size).inflate(-10.0, -10.0),
fixed_space_info,
),
LayoutRect::new(LayoutPoint::zero(), layout_size).inflate(-10.0, -10.0),
ColorF::new(0.8, 0.8, 0.8, 1.0),
);
push_rotated_rect(
builder,
LayoutRect::new(
LayoutPoint::new(100.0, 100.0),
LayoutSize::new(size_factor * 400.0, size_factor * 400.0),
),
ColorF::new(1.0, 0.0, 0.0, 1.0),
scroll_space_info.spatial_id,
root_pipeline_id,
time,
time,
);
push_rotated_rect(
builder,
LayoutRect::new(
LayoutPoint::new(800.0, 100.0),
LayoutSize::new(size_factor * 100.0, size_factor * 600.0),
),
ColorF::new(0.0, 1.0, 0.0, 1.0),
fixed_space_info.spatial_id,
root_pipeline_id,
0.2,
time,
);
push_rotated_rect(
builder,
LayoutRect::new(
LayoutPoint::new(700.0, 200.0),
LayoutSize::new(size_factor * 300.0, size_factor * 300.0),
),
ColorF::new(0.0, 0.0, 1.0, 1.0),
scroll_space_info.spatial_id,
root_pipeline_id,
0.1,
time,
);
}
#[derive(Debug, Copy, Clone)]
enum Invalidations {
Large,
Small,
Scrolling,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
enum Sync {
None = 0,
Swap = 1,
Commit = 2,
Flush = 3,
Query = 4,
}
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() != 6 {
println!("USAGE: compositor [native|none] [small|large|scroll] [none|swap|commit|flush|query] width height");
process::exit(0);
}
let enable_compositor = match args[1].parse::<String>().unwrap().as_str() {
"native" => true,
"none" => false,
_ => panic!("invalid compositor [native, none]"),
};
let inv_mode = match args[2].parse::<String>().unwrap().as_str() {
"small" => Invalidations::Small,
"large" => Invalidations::Large,
"scroll" => Invalidations::Scrolling,
_ => panic!("invalid invalidations [small, large, scroll]"),
};
let sync_mode = match args[3].parse::<String>().unwrap().as_str() {
"none" => Sync::None,
"swap" => Sync::Swap,
"commit" => Sync::Commit,
"flush" => Sync::Flush,
"query" => Sync::Query,
_ => panic!("invalid sync mode [none, swap, commit, flush, query]"),
};
let width = args[4].parse().unwrap();
let height = args[5].parse().unwrap();
let device_size = DeviceIntSize::new(width, height);
// Load GL, construct WR and the native compositor interface.
let window = compositor::create_window(
device_size.width,
device_size.height,
enable_compositor,
sync_mode as i32,
);
let debug_flags = DebugFlags::empty();
let compositor_config = if enable_compositor {
webrender::CompositorConfig::Native {
max_update_rects: 1,
compositor: Box::new(DirectCompositeInterface::new(window)),
}
} else {
webrender::CompositorConfig::Draw {
max_partial_present_rects: 0,
}
};
let opts = webrender::RendererOptions {
clear_color: Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
debug_flags,
enable_picture_caching: true,
compositor_config,
..webrender::RendererOptions::default()
};
let (tx, rx) = mpsc::channel();
let notifier = Box::new(Notifier::new(tx));
let gl = unsafe {
gl::GlesFns::load_with(
|symbol| {
let symbol = CString::new(symbol).unwrap();
let ptr = compositor::get_proc_address(symbol.as_ptr());
ptr
}
)
};
let (mut renderer, sender) = webrender::Renderer::new(
gl.clone(),
notifier,
opts,
None,
device_size,
).unwrap();
let api = sender.create_api();
let document_id = api.add_document(device_size, 0);
let device_pixel_ratio = 1.0;
let mut current_epoch = Epoch(0);
let root_pipeline_id = PipelineId(0, 0);
let layout_size = device_size.to_f32() / euclid::Scale::new(device_pixel_ratio);
let mut time = 0.0;
let scroll_id = ExternalScrollId(3, root_pipeline_id);
// Kick off first transaction which will mean we get a notify below to build the DL and render.
let mut txn = Transaction::new();
txn.set_root_pipeline(root_pipeline_id);
if let Invalidations::Scrolling = inv_mode {
let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size);
build_display_list(
&mut root_builder,
scroll_id,
root_pipeline_id,
layout_size,
1.0,
inv_mode,
);
txn.set_display_list(
current_epoch,
None,
layout_size,
root_builder.finalize(),
true,
);
}
txn.generate_frame();
api.send_transaction(document_id, txn);
// Tick the compositor (in this sample, we don't block on UI events)
while compositor::tick(window) {
// If there is a new frame ready to draw
if let Ok(..) = rx.try_recv() {
// Update and render. This will invoke the native compositor interface implemented above
// as required.
renderer.update();
renderer.render(device_size).unwrap();
let _ = renderer.flush_pipeline_info();
// Construct a simple display list that can be drawn and composited by DC.
let mut txn = Transaction::new();
match inv_mode {
Invalidations::Small | Invalidations::Large => {
let mut root_builder = DisplayListBuilder::new(root_pipeline_id, layout_size);
build_display_list(
&mut root_builder,
scroll_id,
root_pipeline_id,
layout_size,
time,
inv_mode,
);
txn.set_display_list(
current_epoch,
None,
layout_size,
root_builder.finalize(),
true,
);
}
Invalidations::Scrolling => {
let d = 0.5 - 0.5 * (2.0 * f32::consts::PI * 5.0 * time).cos();
txn.scroll_node_with_id(
LayoutPoint::new(0.0, (d * 100.0).round()),
scroll_id,
ScrollClamping::NoClamping,
);
}
}
txn.generate_frame();
api.send_transaction(document_id, txn);
current_epoch.0 += 1;
time += 0.001;
if time > 1.0 {
time = 0.0;
}
// This does nothing when native compositor is enabled
compositor::swap_buffers(window);
}
}
renderer.deinit();
compositor::destroy_window(window);
}