diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /gfx/wr/example-compositor/compositor/src | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/wr/example-compositor/compositor/src')
-rw-r--r-- | gfx/wr/example-compositor/compositor/src/main.rs | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/gfx/wr/example-compositor/compositor/src/main.rs b/gfx/wr/example-compositor/compositor/src/main.rs new file mode 100644 index 0000000000..0a0275fe3f --- /dev/null +++ b/gfx/wr/example-compositor/compositor/src/main.rs @@ -0,0 +1,549 @@ +/* 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::{CompositorSurfaceTransform, Transaction, api::*}; +use webrender::api::units::*; +use webrender::Device; +#[cfg(target_os = "windows")] +use compositor_windows as compositor; +#[cfg(target_os = "linux")] +use compositor_wayland 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, + _device: &mut Device, + id: webrender::NativeSurfaceId, + _virtual_offset: DeviceIntPoint, + 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, _device: &mut Device, id: webrender::NativeSurfaceId) { + compositor::destroy_surface(self.window, id.0); + } + + fn create_tile(&mut self, _device: &mut Device, id: webrender::NativeTileId) { + compositor::create_tile(self.window, id.surface_id.0, id.x, id.y); + } + + fn destroy_tile(&mut self, _device: &mut Device, id: webrender::NativeTileId) { + compositor::destroy_tile(self.window, id.surface_id.0, id.x, id.y); + } + + fn bind( + &mut self, + _device: &mut Device, + id: webrender::NativeTileId, + dirty_rect: DeviceIntRect, + _valid_rect: DeviceIntRect, + ) -> webrender::NativeSurfaceInfo { + let (fbo_id, x, y) = compositor::bind_surface( + self.window, + id.surface_id.0, + id.x, + id.y, + dirty_rect.min.x, + dirty_rect.min.y, + dirty_rect.width(), + dirty_rect.height(), + ); + + webrender::NativeSurfaceInfo { + origin: DeviceIntPoint::new(x, y), + fbo_id, + } + } + + fn unbind(&mut self, _device: &mut Device) { + compositor::unbind_surface(self.window); + } + + fn begin_frame(&mut self, _device: &mut Device) { + compositor::begin_transaction(self.window); + } + + fn add_surface( + &mut self, + _device: &mut Device, + id: webrender::NativeSurfaceId, + transform: CompositorSurfaceTransform, + clip_rect: DeviceIntRect, + _image_rendering: ImageRendering, + ) { + compositor::add_surface( + self.window, + id.0, + transform.offset.x as i32, + transform.offset.y as i32, + clip_rect.min.x, + clip_rect.min.y, + clip_rect.width(), + clip_rect.height(), + ); + } + + fn end_frame(&mut self, _device: &mut Device) { + compositor::end_transaction(self.window); + } + fn create_external_surface( + &mut self, + _device: &mut Device, + _id: webrender::NativeSurfaceId, + _: bool, + ) { + todo!() + } + + fn attach_external_image( + &mut self, + _device: &mut Device, + _id: webrender::NativeSurfaceId, + _external_image: ExternalImageId, + ) { + todo!() + } + + fn enable_native_compositor(&mut self, _device: &mut Device, _enable: bool) { + todo!() + } + + fn deinit(&mut self, _device: &mut Device) { + compositor::deinit(self.window); + } + + fn get_capabilities(&self, _device: &mut Device) -> webrender::CompositorCapabilities { + webrender::CompositorCapabilities { + virtual_surface_size: 1024 * 1024, + ..Default::default() + } + } + + fn invalidate_tile( + &mut self, + _device: &mut Device, + _id: webrender::NativeTileId, + _valid_rect: DeviceIntRect, + ) { + } + + fn start_compositing( + &mut self, + _device: &mut Device, + _color: webrender::webrender_api::ColorF, + _dirty_rects: &[DeviceIntRect], + _opaque_rects: &[DeviceIntRect], + ) { + } + + fn create_backdrop_surface( + &mut self, + _device: &mut Device, + _id: webrender::NativeSurfaceId, + _color: webrender::webrender_api::ColorF, + ) { + todo!() + } + + fn get_window_visibility(&self, _device: &mut Device) -> webrender::WindowVisibility { + todo!() + } +} + +// 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, _composite_needed: bool) {} + + fn new_frame_ready(&self, _: DocumentId, _scrolled: bool, _composite_needed: bool, _: FramePublishId) { + 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, + item_key: SpatialTreeItemKey, +) { + let color = color.scale_rgb(time); + let rotation = LayoutTransform::rotation( + 0.0, + 0.0, + 1.0, + Angle::radians(2.0 * std::f32::consts::PI * angle), + ); + let center = rect.center(); + let transform_origin = LayoutVector3D::new(center.x, center.y, 0.0); + let transform = rotation + .pre_translate(-transform_origin) + .then_translate(transform_origin); + let spatial_id = builder.push_reference_frame( + LayoutPoint::zero(), + spatial_id, + TransformStyle::Flat, + PropertyBinding::Value(transform), + ReferenceFrameKind::Transform { + is_2d_scale_translation: false, + should_snap: false, + paired_with_perspective: false, + }, + item_key, + ); + builder.push_rect( + &CommonItemProperties::new( + rect, + SpaceAndClipInfo { + spatial_id, + clip_chain_id: ClipChainId::INVALID, + }, + ), + 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_chain_id: ClipChainId::INVALID, + }; + + let scroll_spatial_id = builder.define_scroll_frame( + fixed_space_info.spatial_id, + scroll_id, + LayoutRect::from_size(layout_size), + LayoutRect::from_size(layout_size), + LayoutVector2D::zero(), + APZScrollGeneration::default(), + HasScrollLinkedEffect::No, + SpatialTreeItemKey::new(1, 0), + ); + + builder.push_rect( + &CommonItemProperties::new( + LayoutRect::from_size(layout_size).inflate(-10.0, -10.0), + fixed_space_info, + ), + LayoutRect::from_size(layout_size).inflate(-10.0, -10.0), + ColorF::new(0.8, 0.8, 0.8, 1.0), + ); + + push_rotated_rect( + builder, + LayoutRect::from_origin_and_size( + 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_spatial_id, + root_pipeline_id, + time, + time, + SpatialTreeItemKey::new(1, 1), + ); + + push_rotated_rect( + builder, + LayoutRect::from_origin_and_size( + 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, + SpatialTreeItemKey::new(1, 2), + ); + + push_rotated_rect( + builder, + LayoutRect::from_origin_and_size( + 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_spatial_id, + root_pipeline_id, + 0.1, + time, + SpatialTreeItemKey::new(1, 3), + ); + + push_rotated_rect( + builder, + LayoutRect::from_origin_and_size( + LayoutPoint::new(100.0, 600.0), + LayoutSize::new(size_factor * 400.0, size_factor * 400.0), + ), + ColorF::new(1.0, 1.0, 0.0, 1.0), + scroll_spatial_id, + root_pipeline_id, + time, + time, + SpatialTreeItemKey::new(1, 4), + ); + + push_rotated_rect( + builder, + LayoutRect::from_origin_and_size( + LayoutPoint::new(700.0, 600.0), + LayoutSize::new(size_factor * 400.0, size_factor * 400.0), + ), + ColorF::new(0.0, 1.0, 1.0, 1.0), + scroll_spatial_id, + root_pipeline_id, + time, + time, + SpatialTreeItemKey::new(1, 5), + ); +} + +#[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 { + compositor: Box::new(DirectCompositeInterface::new(window)), + } + } else { + webrender::CompositorConfig::Draw { + max_partial_present_rects: 0, + draw_previous_partial_present_regions: false, + partial_present: None, + } + }; + let opts = webrender::WebRenderOptions { + clear_color: ColorF::new(1.0, 1.0, 1.0, 1.0), + debug_flags, + compositor_config, + surface_origin_is_top_left: false, + ..webrender::WebRenderOptions::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::create_webrender_instance(gl.clone(), notifier, opts, None).unwrap(); + let mut api = sender.create_api(); + let document_id = api.add_document(device_size); + 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); + root_builder.begin(); + + build_display_list( + &mut root_builder, + scroll_id, + root_pipeline_id, + layout_size, + 1.0, + inv_mode, + ); + + txn.set_display_list(current_epoch, root_builder.end()); + } + + txn.generate_frame(0, RenderReasons::empty()); + 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, 0).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); + root_builder.begin(); + + build_display_list( + &mut root_builder, + scroll_id, + root_pipeline_id, + layout_size, + time, + inv_mode, + ); + + txn.set_display_list(current_epoch, root_builder.end()); + } + Invalidations::Scrolling => { + let d = 0.5 - 0.5 * (2.0 * f32::consts::PI * 5.0 * time).cos(); + txn.set_scroll_offsets( + scroll_id, + vec![SampledScrollOffset { + offset: LayoutVector2D::new(0.0, (d * 100.0).round()), + generation: APZScrollGeneration::default(), + }], + ); + } + } + + txn.generate_frame(0, RenderReasons::empty()); + 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); +} |