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 }