tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

composite.rs (9312B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 use std::os::raw::c_void;
      6 use std::ptr;
      7 
      8 use glutin::platform::windows::EGLContext;
      9 use webrender::{CompositorInputConfig, CompositorSurfaceTransform, LayerCompositor};
     10 use winit::platform::windows::WindowExtWindows;
     11 
     12 use crate::WindowWrapper;
     13 
     14 use mozangle::egl::ffi::types::{EGLDisplay, EGLSurface, EGLint};
     15 use mozangle::egl;
     16 
     17 // A simplistic implementation of the `LayerCompositor` trait to allow wrench to
     18 // composite via DirectComposition. In this initial version, only a single
     19 // swap-chain is supported. Follow up patches will add layer and external
     20 // surface support.
     21 
     22 #[repr(C)]
     23 #[derive(Copy, Clone, Debug)]
     24 pub struct CompositorHandle(usize);
     25 
     26 #[repr(C)]
     27 #[derive(Copy, Clone, PartialEq, Debug)]
     28 struct LayerId(usize);
     29 
     30 impl LayerId {
     31    const INVALID: LayerId = LayerId(0);
     32 }
     33 
     34 // FFI bindings to `composite.cpp`
     35 #[link(name="wr_composite", kind="static")]
     36 extern "C" {
     37    fn wrc_new(d3d11_device: *const c_void, hwnd: *const c_void) -> CompositorHandle;
     38    fn wrc_delete(compositor: CompositorHandle);
     39 
     40    fn wrc_create_layer(compositor: CompositorHandle, width: i32, height: i32, is_opaque: bool) -> LayerId;
     41    fn wrc_get_layer_backbuffer(layer_id: LayerId) -> *mut c_void;
     42    fn wrc_set_layer_position(layer_id: LayerId, x: f32, y: f32);
     43    fn wrc_present_layer(layer_id: LayerId);
     44    fn wrc_add_layer(compositor: CompositorHandle, layer_id: LayerId);
     45 
     46    fn wrc_begin_frame(compositor: CompositorHandle);
     47    fn wrc_end_frame(compositor: CompositorHandle);
     48 }
     49 
     50 // A basic layer - we only create one primary layer in the initial commit
     51 struct WrLayer {
     52    // Layer dimensions
     53    width: i32,
     54    height: i32,
     55    // EGL surface that gets bound for webrender to draw to
     56    surface: EGLSurface,
     57    // Handle to the FFI layer
     58    layer_id: LayerId,
     59 }
     60 
     61 impl WrLayer {
     62    fn empty() -> Self {
     63        WrLayer {
     64            width: 0,
     65            height: 0,
     66            layer_id: LayerId::INVALID,
     67            surface: ptr::null(),
     68        }
     69    }
     70 }
     71 
     72 // A basic `LayerCompositor` implementation for wrench
     73 pub struct WrCompositor {
     74    // EGL display and content, provided by winit
     75    context: EGLContext,
     76    display: EGLDisplay,
     77    // FFI compositor handle
     78    compositor: CompositorHandle,
     79    // The swapchain layers needed for current scene
     80    layers: Vec<WrLayer>,
     81 }
     82 
     83 impl WrCompositor {
     84    pub fn new(window: &WindowWrapper) -> Self {
     85        // Retrieve the D3D11 device from winit - this was created when ANGLE was initialized
     86        let d3d11_device = window.get_d3d11_device();
     87 
     88        // Get the win32 and EGL information needed from the window
     89        let (hwnd, display, context) = match window {
     90            WindowWrapper::Angle(window, angle, _, _) => {
     91                (window.hwnd(), angle.get_display(), angle.get_context())
     92            }
     93            _ => unreachable!(),
     94        };
     95 
     96        // Construct the FFI part of the compositor impl
     97        let compositor = unsafe {
     98            wrc_new(d3d11_device, hwnd)
     99        };
    100 
    101        WrCompositor {
    102            display,
    103            context,
    104            compositor,
    105            layers: Vec::new(),
    106        }
    107    }
    108 }
    109 
    110 impl Drop for WrCompositor {
    111    fn drop(&mut self) {
    112        unsafe {
    113            wrc_delete(self.compositor);
    114        }
    115    }
    116 }
    117 
    118 impl LayerCompositor for WrCompositor {
    119    // Begin compositing a frame with the supplied input config
    120    // The main job of this method is to inspect the input config, create the output
    121    // config, and create any native OS resources that will be needed as layers get
    122    // composited.
    123    fn begin_frame(
    124        &mut self,
    125        input: &CompositorInputConfig,
    126    ) -> bool {
    127        unsafe {
    128            // Reset DC visual tree
    129            wrc_begin_frame(self.compositor);
    130        }
    131 
    132        let prev_layer_count = self.layers.len();
    133        let curr_layer_count = input.layers.len();
    134 
    135        if prev_layer_count > curr_layer_count {
    136            todo!();
    137        } else if curr_layer_count > prev_layer_count {
    138            // Construct new empty layers, they'll get resized below
    139            for _ in 0 .. curr_layer_count-prev_layer_count {
    140                self.layers.push(WrLayer::empty());
    141            }
    142        }
    143 
    144        assert_eq!(self.layers.len(), input.layers.len());
    145 
    146        for (input, layer) in input.layers.iter().zip(self.layers.iter_mut()) {
    147            let input_size = input.clip_rect.size();
    148 
    149            // TODO(gwc): Handle External surfaces as swapchains separate from content
    150 
    151            // See if we need to resize the swap-chain layer
    152            if input_size.width != layer.width ||
    153                input_size.height != layer.height {
    154                // TODO: Handle resize of layer (not needed for initial commit,
    155                // but will need to support resizing wrench window)
    156                assert_eq!(layer.layer_id, LayerId::INVALID);
    157 
    158                // Construct a DC swap-chain
    159                layer.width = input_size.width;
    160                layer.height = input_size.height;
    161                layer.layer_id = unsafe {
    162                    wrc_create_layer(self.compositor, layer.width, layer.height, input.is_opaque)
    163                };
    164 
    165                let pbuffer_attribs: [EGLint; 5] = [
    166                    egl::ffi::WIDTH as EGLint, layer.width,
    167                    egl::ffi::HEIGHT as EGLint, layer.height,
    168                    egl::ffi::NONE as EGLint,
    169                ];
    170 
    171                let attribs: [EGLint; 18] = [
    172                    egl::ffi::SURFACE_TYPE as EGLint, egl::ffi::WINDOW_BIT as EGLint,
    173                    egl::ffi::RED_SIZE as EGLint, 8,
    174                    egl::ffi::GREEN_SIZE as EGLint, 8,
    175                    egl::ffi::BLUE_SIZE as EGLint, 8,
    176                    egl::ffi::ALPHA_SIZE as EGLint, 8,
    177                    egl::ffi::RENDERABLE_TYPE as EGLint, egl::ffi::OPENGL_ES2_BIT as EGLint,
    178                    // TODO(gw): Can we disable z-buffer for compositing always?
    179                    egl::ffi::DEPTH_SIZE as EGLint, 24,
    180                    egl::ffi::STENCIL_SIZE as EGLint, 8,
    181                    egl::ffi::NONE as EGLint, egl::ffi::NONE as EGLint
    182                ];
    183 
    184                // Get the D3D backbuffer texture for the swap-chain
    185                let back_buffer = unsafe {
    186                    wrc_get_layer_backbuffer(layer.layer_id)
    187                };
    188 
    189                // Create an EGL surface <-> binding to the D3D backbuffer texture
    190                let mut egl_config = ptr::null();
    191                let mut cfg_count = 0;
    192 
    193                let ok = unsafe {
    194                    egl::ffi::ChooseConfig(self.display, attribs.as_ptr(), &mut egl_config, 1, &mut cfg_count)
    195                };
    196 
    197                assert_ne!(ok, 0);
    198                assert_eq!(cfg_count, 1);
    199                assert_ne!(egl_config, ptr::null());
    200 
    201                let surface = unsafe {
    202                    egl::ffi::CreatePbufferFromClientBuffer(
    203                        self.display,
    204                        egl::ffi::D3D_TEXTURE_ANGLE,
    205                        back_buffer,
    206                        egl_config,
    207                        pbuffer_attribs.as_ptr(),
    208                    )
    209                };
    210                assert_ne!(surface, ptr::null());
    211 
    212                layer.surface = surface;
    213            }
    214 
    215            unsafe {
    216                wrc_set_layer_position(
    217                    layer.layer_id,
    218                    input.offset.x as f32,
    219                    input.offset.y as f32,
    220                );
    221            }
    222        }
    223 
    224        true
    225    }
    226 
    227    // Bind a layer by index for compositing into
    228    fn bind_layer(&mut self, index: usize, _dirty_rects: &[crate::DeviceIntRect]) {
    229        // Bind the DC surface to EGL so that WR can composite to the layer
    230        let layer = &self.layers[index];
    231 
    232        let ok = unsafe {
    233            egl::ffi::MakeCurrent(
    234                self.display,
    235                layer.surface,
    236                layer.surface,
    237                self.context,
    238            )
    239        };
    240        assert!(ok != 0);
    241    }
    242 
    243    // Finish compositing a layer and present the swapchain
    244    fn present_layer(&mut self, index: usize, _dirty_rects: &[crate::DeviceIntRect]) {
    245        let layer = &self.layers[index];
    246 
    247        unsafe {
    248            wrc_present_layer(layer.layer_id);
    249        }
    250    }
    251 
    252    fn add_surface(
    253        &mut self,
    254        index: usize,
    255        _transform: CompositorSurfaceTransform,
    256        _clip_rect: webrender::api::units::DeviceIntRect,
    257        _image_rendering: webrender::api::ImageRendering,
    258        _rounded_clip_rect: webrender::api::units::DeviceIntRect,
    259        _rounded_clip_radii: webrender::ClipRadius,
    260    ) {
    261        let layer = &self.layers[index];
    262 
    263        unsafe {
    264            wrc_add_layer(self.compositor, layer.layer_id);
    265        }
    266    }
    267 
    268    // Finish compositing this frame
    269    fn end_frame(&mut self) {
    270        unsafe {
    271            // Do any final commits to DC
    272            wrc_end_frame(self.compositor);
    273        }
    274    }
    275 
    276    fn get_window_properties(&self) -> webrender::WindowProperties {
    277        webrender::WindowProperties {
    278            is_opaque: true,
    279            enable_screenshot: false,
    280        }
    281    }
    282 }