diff --git a/Cargo.lock b/Cargo.lock index ab06d988c2..7c1b846cbc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,7 +112,7 @@ name = "cgl" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gleam 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -277,6 +277,18 @@ dependencies = [ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "direct-composition" +version = "0.1.0" +dependencies = [ + "euclid 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)", + "mozangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "webrender 0.57.0", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dlib" version = "0.4.0" @@ -434,28 +446,27 @@ name = "gl_generator" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "khronos_api 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "khronos_api 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gl_generator" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "khronos_api 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "khronos_api 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gleam" -version = "0.4.20" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gl_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -578,7 +589,7 @@ dependencies = [ [[package]] name = "khronos_api" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -698,10 +709,11 @@ dependencies = [ [[package]] name = "mozangle" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1254,12 +1266,12 @@ dependencies = [ "euclid 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "mozangle 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mozangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "plane-split 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1367,7 +1379,7 @@ dependencies = [ "env_logger 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "font-loader 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gleam 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1493,8 +1505,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" "checksum gif 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e41945ba23db3bf51b24756d73d81acb4f28d85c3dccc32c6fae904438c25f" "checksum gl_generator 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3e0220a68b8875b5a311fe67ee3b76d3d9b719a92277aff0ec5bb5e7b0ec1" -"checksum gl_generator 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f5c19cde55637681450c92f7a05ea16c78e2b6d0587e601ec1ebdab6960854b" -"checksum gleam 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "959c818d9bbe9f7b7db55dce0bc44673c4da4f4ee122536c40550f984c3b8017" +"checksum gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a795170cbd85b5a7baa58d6d7525cae6a03e486859860c220f7ebbbdd379d0a" +"checksum gleam 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a7f5351837630a7dd0cd6d7976de547929f9fabd381b9d5ac35f82e90be2" "checksum glutin 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "25c70edeb14581cb6edb486eb15d55b41815fade469308474932549fd9703fe5" "checksum httparse 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "af2f2dd97457e8fb1ae7c5a420db346af389926e36f43768b96f101546b04a07" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" @@ -1506,7 +1518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum jpeg-decoder 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "0dfe27a6c0dabd772d0f9b9f8701c4ca12c4d1eebcadf2be1f6f70396f6a1434" "checksum json 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)" = "5e7eb285e773498f9473a6e2255feffe95db9c55579c7931a6db83c9e02a4673" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum khronos_api 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d867c645cfeb8a7fec503731679eac03ac11b7105aa5a71cb8f8ee5271636add" +"checksum khronos_api 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ef23fcc4059260c5936f638c9805ebfc87cb172fa6661d130cba7f97d58f55" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b" @@ -1523,7 +1535,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum mio 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6d19442734abd7d780b981c590c325680d933e99795fe1f693f0686c9ed48022" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum mozangle 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4d916e4f2d39a00eeeb082ceb7c63c741e7c9d4f7915945f9225ae5e3b284092" +"checksum mozangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b41d9b5488bd3bf519736839a5b573953bc20b093f9a444a02ca1c8956fe59f" "checksum net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "5edf9cb6be97212423aed9413dd4729d62b370b5e1c571750e882cebbbc1e3e2" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "bde7c03b09e7c6a301ee81f6ddf66d7a28ec305699e3d3b056d2fc56470e3120" diff --git a/Cargo.toml b/Cargo.toml index baabc9ad4b..f67115098c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] license = "MPL-2.0" members = [ + "direct-composition", "webrender", "webrender_api", "wrench", diff --git a/appveyor.yml b/appveyor.yml index 74b24dc5d1..3cd08e8e1b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,11 +1,11 @@ environment: PATH: 'C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%;C:\Rust\bin' RUST_BACKTRACE: 1 + TARGET: x86_64-pc-windows-msvc install: - - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.23.0-x86_64-pc-windows-gnu.msi" - - msiexec /passive /i "rust-1.23.0-x86_64-pc-windows-gnu.msi" ADDLOCAL=Rustc,Cargo,Std INSTALLDIR=C:\Rust - - bash -lc "pacman -S --noconfirm mingw-w64-x86_64-cmake" + - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.23.0-${env:TARGET}.msi" + - msiexec /passive /i "rust-1.23.0-%TARGET%.msi" ADDLOCAL=Rustc,Cargo,Std INSTALLDIR=C:\Rust - rustc -V - cargo -V @@ -18,3 +18,5 @@ test_script: - cargo test --verbose - cd ../wrench - cargo test --verbose + - cd ../direct-composition + - cargo build --verbose diff --git a/direct-composition/Cargo.toml b/direct-composition/Cargo.toml new file mode 100644 index 0000000000..205e0df207 --- /dev/null +++ b/direct-composition/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "direct-composition" +version = "0.1.0" +authors = ["Simon Sapin "] +license = "MPL-2.0" + +[target.'cfg(windows)'.dependencies] +euclid = "0.17" +gleam = "0.4" +mozangle = {version = "0.1", features = ["egl"]} +webrender = {path = "../webrender"} +winapi = {version = "0.3", features = ["winerror", "d3d11", "dcomp"]} +winit = "0.10" diff --git a/direct-composition/src/com.rs b/direct-composition/src/com.rs new file mode 100644 index 0000000000..8fb384695c --- /dev/null +++ b/direct-composition/src/com.rs @@ -0,0 +1,112 @@ +/* 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/. */ + +use std::ops; +use std::ptr; +use winapi::Interface; +use winapi::ctypes::c_void; +use winapi::shared::guiddef::GUID; +use winapi::shared::winerror::HRESULT; +use winapi::shared::winerror::SUCCEEDED; +use winapi::um::unknwnbase::IUnknown; + +pub fn as_ptr(x: &T) -> *mut T { + x as *const T as _ +} + +pub trait CheckHResult { + fn check_hresult(self); +} + +impl CheckHResult for HRESULT { + fn check_hresult(self) { + if !SUCCEEDED(self) { + panic_com(self) + } + } +} + +fn panic_com(hresult: HRESULT) -> ! { + panic!("COM error 0x{:08X}", hresult as u32) +} + +/// Forked from +#[derive(PartialEq, Debug)] +pub struct ComPtr(*mut T) where T: Interface; + +impl ComPtr where T: Interface { + /// Creates a `ComPtr` to wrap a raw pointer. + /// It takes ownership over the pointer which means it does __not__ call `AddRef`. + /// `T` __must__ be a COM interface that inherits from `IUnknown`. + pub unsafe fn from_raw(ptr: *mut T) -> ComPtr { + assert!(!ptr.is_null()); + ComPtr(ptr) + } + + /// For use with APIs that take an interface UUID and + /// "return" a new COM object through a `*mut *mut c_void` out-parameter. + pub unsafe fn new_with_uuid(f: F) -> Self + where F: FnOnce(&GUID, *mut *mut c_void) -> HRESULT + { + Self::new_with(|ptr| f(&T::uuidof(), ptr as _)) + } + + /// For use with APIs that "return" a new COM object through a `*mut *mut T` out-parameter. + pub unsafe fn new_with(f: F) -> Self + where F: FnOnce(*mut *mut T) -> HRESULT + { + let mut ptr = ptr::null_mut(); + let hresult = f(&mut ptr); + if SUCCEEDED(hresult) { + ComPtr::from_raw(ptr) + } else { + if !ptr.is_null() { + let ptr = ptr as *mut IUnknown; + (*ptr).Release(); + } + panic_com(hresult) + } + } + + pub fn as_raw(&self) -> *mut T { + self.0 + } + + fn as_unknown(&self) -> &IUnknown { + unsafe { + &*(self.0 as *mut IUnknown) + } + } + + /// Performs QueryInterface fun. + pub fn cast(&self) -> ComPtr where U: Interface { + unsafe { + ComPtr::::new_with_uuid(|uuid, ptr| self.as_unknown().QueryInterface(uuid, ptr)) + } + } +} + +impl ops::Deref for ComPtr where T: Interface { + type Target = T; + fn deref(&self) -> &T { + unsafe { &*self.0 } + } +} + +impl Clone for ComPtr where T: Interface { + fn clone(&self) -> Self { + unsafe { + self.as_unknown().AddRef(); + ComPtr(self.0) + } + } +} + +impl Drop for ComPtr where T: Interface { + fn drop(&mut self) { + unsafe { + self.as_unknown().Release(); + } + } +} diff --git a/direct-composition/src/egl.rs b/direct-composition/src/egl.rs new file mode 100644 index 0000000000..8bd5afb72a --- /dev/null +++ b/direct-composition/src/egl.rs @@ -0,0 +1,174 @@ +/* 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/. */ + +use mozangle::egl::ffi::*; +use std::os::raw::c_void; +use std::ptr; +use std::rc::Rc; +use winapi::um::d3d11::ID3D11Device; +use winapi::um::d3d11::ID3D11Texture2D; + +pub use mozangle::egl::get_proc_address; + +pub struct SharedEglThings { + device: EGLDeviceEXT, + display: types::EGLDisplay, + config: types::EGLConfig, + context: types::EGLContext, +} + +fn cast_attributes(slice: &[types::EGLenum]) -> &EGLint { + unsafe { + &*(slice.as_ptr() as *const EGLint) + } +} + +macro_rules! attributes { + ($( $key: expr => $value: expr, )*) => { + cast_attributes(&[ + $( $key, $value, )* + NONE, + ]) + } +} + +impl SharedEglThings { + pub unsafe fn new(d3d_device: *mut ID3D11Device) -> Rc { + let device = eglCreateDeviceANGLE( + D3D11_DEVICE_ANGLE, + d3d_device as *mut c_void, + ptr::null(), + ).check(); + let display = GetPlatformDisplayEXT( + PLATFORM_DEVICE_EXT, + device, + attributes! [ + EXPERIMENTAL_PRESENT_PATH_ANGLE => EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE, + ], + ).check(); + Initialize(display, ptr::null_mut(), ptr::null_mut()).check(); + + // Adapted from + // https://searchfox.org/mozilla-central/rev/056a4057/gfx/gl/GLContextProviderEGL.cpp#635 + let mut configs = [ptr::null(); 64]; + let mut num_configs = 0; + ChooseConfig( + display, + attributes! [ + SURFACE_TYPE => WINDOW_BIT, + RENDERABLE_TYPE => OPENGL_ES2_BIT, + RED_SIZE => 8, + GREEN_SIZE => 8, + BLUE_SIZE => 8, + ALPHA_SIZE => 8, + ], + configs.as_mut_ptr(), + configs.len() as i32, + &mut num_configs, + ).check(); + let config = pick_config(&configs[..num_configs as usize]); + + let context = CreateContext( + display, config, NO_CONTEXT, + attributes![ + CONTEXT_CLIENT_VERSION => 3, + ] + ).check(); + MakeCurrent(display, NO_SURFACE, NO_SURFACE, context).check(); + + Rc::new(SharedEglThings { device, display, config, context }) + } +} + +fn pick_config(configs: &[types::EGLConfig]) -> types::EGLConfig { + // FIXME: better criteria to make this choice? + // Firefox uses GetConfigAttrib to find a config that has the requested r/g/b/a sizes + // https://searchfox.org/mozilla-central/rev/056a4057/gfx/gl/GLContextProviderEGL.cpp#662-685 + + configs[0] +} + +impl Drop for SharedEglThings { + fn drop(&mut self) { + unsafe { + // FIXME does EGLDisplay or EGLConfig need clean up? How? + DestroyContext(self.display, self.context).check(); + eglReleaseDeviceANGLE(self.device).check(); + } + } +} + +pub struct PerVisualEglThings { + shared: Rc, + surface: types::EGLSurface, +} + +impl PerVisualEglThings { + pub unsafe fn new(shared: Rc, buffer: *const ID3D11Texture2D, + width: u32, height: u32) + -> Self { + let surface = CreatePbufferFromClientBuffer( + shared.display, + D3D_TEXTURE_ANGLE, + buffer as types::EGLClientBuffer, + shared.config, + attributes! [ + WIDTH => width, + HEIGHT => height, + FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE => TRUE, + ], + ).check(); + + PerVisualEglThings { shared, surface } + } + + pub fn make_current(&self) { + unsafe { + MakeCurrent(self.shared.display, self.surface, self.surface, self.shared.context).check(); + } + } +} + +impl Drop for PerVisualEglThings { + fn drop(&mut self) { + unsafe { + DestroySurface(self.shared.display, self.surface).check(); + } + } +} + +fn check_error() { + unsafe { + let error = GetError() as types::EGLenum; + assert_eq!(error, SUCCESS, "0x{:x} != 0x{:x}", error, SUCCESS); + } +} + +trait Check { + fn check(self) -> Self; +} + +impl Check for *const c_void { + fn check(self) -> Self { + check_error(); + assert!(!self.is_null()); + self + } +} + +impl Check for *mut c_void { + fn check(self) -> Self { + check_error(); + assert!(!self.is_null()); + self + } +} + +impl Check for types::EGLBoolean { + fn check(self) -> Self { + check_error(); + assert_eq!(self, TRUE); + self + } +} diff --git a/direct-composition/src/lib.rs b/direct-composition/src/lib.rs new file mode 100644 index 0000000000..01cd32c4e6 --- /dev/null +++ b/direct-composition/src/lib.rs @@ -0,0 +1,179 @@ +/* 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/. */ + +#![cfg(windows)] + +extern crate gleam; +extern crate mozangle; +extern crate winapi; + +use com::{ComPtr, CheckHResult, as_ptr}; +use std::ptr; +use std::rc::Rc; +use winapi::shared::dxgi1_2::DXGI_SWAP_CHAIN_DESC1; +use winapi::shared::dxgi1_2::IDXGIFactory2; +use winapi::shared::minwindef::{TRUE, FALSE}; +use winapi::shared::windef::HWND; +use winapi::um::d3d11::ID3D11Device; +use winapi::um::dcomp::IDCompositionDevice; +use winapi::um::dcomp::IDCompositionTarget; +use winapi::um::dcomp::IDCompositionVisual; + +mod com; +mod egl; + +pub struct DirectComposition { + d3d_device: ComPtr, + dxgi_factory: ComPtr, + + egl: Rc, + pub gleam: Rc, + + composition_device: ComPtr, + root_visual: ComPtr, + + #[allow(unused)] // Needs to be kept alive + composition_target: ComPtr, +} + +impl DirectComposition { + /// Initialize DirectComposition in the given window + /// + /// # Safety + /// + /// `hwnd` must be a valid handle to a window. + pub unsafe fn new(hwnd: HWND) -> Self { + let d3d_device = ComPtr::new_with(|ptr_ptr| winapi::um::d3d11::D3D11CreateDevice( + ptr::null_mut(), + winapi::um::d3dcommon::D3D_DRIVER_TYPE_HARDWARE, + ptr::null_mut(), + winapi::um::d3d11::D3D11_CREATE_DEVICE_BGRA_SUPPORT | + if cfg!(debug_assertions) { + winapi::um::d3d11::D3D11_CREATE_DEVICE_DEBUG + } else { + 0 + }, + ptr::null_mut(), + 0, + winapi::um::d3d11::D3D11_SDK_VERSION, + ptr_ptr, + &mut 0, + ptr::null_mut(), + )); + + let egl = egl::SharedEglThings::new(d3d_device.as_raw()); + let gleam = gleam::gl::GlesFns::load_with(egl::get_proc_address); + + let dxgi_device = d3d_device.cast::(); + + // https://msdn.microsoft.com/en-us/library/windows/desktop/hh404556(v=vs.85).aspx#code-snippet-1 + // “Because you can create a Direct3D device without creating a swap chain, + // you might need to retrieve the factory that is used to create the device + // in order to create a swap chain.” + let adapter = ComPtr::new_with(|ptr_ptr| dxgi_device.GetAdapter(ptr_ptr)); + let dxgi_factory = ComPtr::::new_with_uuid(|uuid, ptr_ptr| { + adapter.GetParent(uuid, ptr_ptr) + }); + + // Create the DirectComposition device object. + let composition_device = ComPtr::::new_with_uuid(|uuid, ptr_ptr| { + winapi::um::dcomp::DCompositionCreateDevice(&*dxgi_device, uuid, ptr_ptr) + }); + + // Create the composition target object based on the + // specified application window. + let composition_target = ComPtr::new_with(|ptr_ptr| { + composition_device.CreateTargetForHwnd(hwnd, TRUE, ptr_ptr) + }); + + let root_visual = ComPtr::new_with(|ptr_ptr| composition_device.CreateVisual(ptr_ptr)); + composition_target.SetRoot(&*root_visual).check_hresult(); + + DirectComposition { + d3d_device, dxgi_factory, + egl, gleam, + composition_device, composition_target, root_visual, + } + } + + /// Execute changes to the DirectComposition scene. + pub fn commit(&self) { + unsafe { + self.composition_device.Commit().check_hresult() + } + } + + pub fn create_angle_visual(&self, width: u32, height: u32) -> AngleVisual { + unsafe { + let desc = DXGI_SWAP_CHAIN_DESC1 { + Width: width, + Height: height, + Format: winapi::shared::dxgiformat::DXGI_FORMAT_B8G8R8A8_UNORM, + Stereo: FALSE, + SampleDesc: winapi::shared::dxgitype::DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: winapi::shared::dxgitype::DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: 2, + Scaling: winapi::shared::dxgi1_2::DXGI_SCALING_STRETCH, + SwapEffect: winapi::shared::dxgi::DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, + AlphaMode: winapi::shared::dxgi1_2::DXGI_ALPHA_MODE_PREMULTIPLIED, + Flags: 0, + }; + let swap_chain = ComPtr::::new_with(|ptr_ptr| { + self.dxgi_factory.CreateSwapChainForComposition( + as_ptr(&self.d3d_device), + &desc, + ptr::null_mut(), + ptr_ptr, + ) + }); + let back_buffer = ComPtr::::new_with_uuid(|uuid, ptr_ptr| { + swap_chain.GetBuffer(0, uuid, ptr_ptr) + }); + let egl = egl::PerVisualEglThings::new(self.egl.clone(), &*back_buffer, width, height); + let gleam = self.gleam.clone(); + + let visual = ComPtr::new_with(|ptr_ptr| self.composition_device.CreateVisual(ptr_ptr)); + visual.SetContent(&*****swap_chain).check_hresult(); + self.root_visual.AddVisual(&*visual, FALSE, ptr::null_mut()).check_hresult(); + + AngleVisual { visual, swap_chain, egl, gleam } + } + } +} + +/// A DirectComposition "visual" configured for rendering with Direct3D. +pub struct AngleVisual { + visual: ComPtr, + swap_chain: ComPtr, + egl: egl::PerVisualEglThings, + pub gleam: Rc, +} + +impl AngleVisual { + pub fn set_offset_x(&self, offset_x: f32) { + unsafe { + self.visual.SetOffsetX_1(offset_x).check_hresult() + } + } + + pub fn set_offset_y(&self, offset_y: f32) { + unsafe { + self.visual.SetOffsetY_1(offset_y).check_hresult() + } + } + + pub fn make_current(&self) { + self.egl.make_current() + } + + pub fn present(&self) { + self.gleam.finish(); + unsafe { + self.swap_chain.Present(0, 0).check_hresult() + } + } +} diff --git a/direct-composition/src/main.rs b/direct-composition/src/main.rs new file mode 100644 index 0000000000..e1999f5f8f --- /dev/null +++ b/direct-composition/src/main.rs @@ -0,0 +1,11 @@ +/* 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/. */ + +#[cfg(not(windows))] +fn main() { + println!("This demo only runs on Windows."); +} + +#[cfg(windows)] +include!("main_windows.rs"); diff --git a/direct-composition/src/main_windows.rs b/direct-composition/src/main_windows.rs new file mode 100644 index 0000000000..899635be96 --- /dev/null +++ b/direct-composition/src/main_windows.rs @@ -0,0 +1,189 @@ +/* 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/. */ + +extern crate direct_composition; +extern crate euclid; +extern crate gleam; +extern crate webrender; +extern crate winit; + +use direct_composition::DirectComposition; +use std::sync::mpsc; +use webrender::api; +use winit::os::windows::WindowExt; + +fn main() { + let mut events_loop = winit::EventsLoop::new(); + + let (tx, rx) = mpsc::channel(); + let notifier = Box::new(Notifier { events_proxy: events_loop.create_proxy(), tx }); + + let window = winit::WindowBuilder::new() + .with_title("WebRender + ANGLE + DirectComposition") + .with_dimensions(1024, 768) + .build(&events_loop) + .unwrap(); + + let composition = direct_composition_from_window(&window); + let factor = window.hidpi_factor(); + + let mut clicks: usize = 0; + let mut offset_y = 100.; + let size = api::DeviceUintSize::new; + let mut rects = [ + Rectangle::new(&composition, ¬ifier, factor, size(300, 200), 0., 0.2, 0.4, 1.), + Rectangle::new(&composition, ¬ifier, factor, size(400, 300), 0., 0.5, 0., 0.5), + ]; + rects[0].render(factor, &rx); + rects[1].render(factor, &rx); + + rects[0].visual.set_offset_x(100.); + rects[0].visual.set_offset_y(50.); + + rects[1].visual.set_offset_x(200.); + rects[1].visual.set_offset_y(offset_y); + + composition.commit(); + + events_loop.run_forever(|event| { + if let winit::Event::WindowEvent { event, .. } = event { + match event { + winit::WindowEvent::Closed => { + return winit::ControlFlow::Break + } + winit::WindowEvent::MouseWheel { delta, .. } => { + let dy = match delta { + winit::MouseScrollDelta::LineDelta(_, dy) => dy, + winit::MouseScrollDelta::PixelDelta(_, dy) => dy, + }; + offset_y = (offset_y - 10. * dy).max(0.).min(468.); + + rects[1].visual.set_offset_y(offset_y); + composition.commit(); + } + winit::WindowEvent::MouseInput { + button: winit::MouseButton::Left, + state: winit::ElementState::Pressed, + .. + } => { + clicks += 1; + let rect = &mut rects[clicks % 2]; + rect.color.g += 0.1; + rect.color.g %= 1.; + rect.render(factor, &rx) + } + _ => {} + } + } + winit::ControlFlow::Continue + }); +} + +fn direct_composition_from_window(window: &winit::Window) -> DirectComposition { + unsafe { + DirectComposition::new(window.get_hwnd() as _) + } +} + +struct Rectangle { + visual: direct_composition::AngleVisual, + renderer: Option, + api: api::RenderApi, + document_id: api::DocumentId, + size: api::DeviceUintSize, + color: api::ColorF, +} + +impl Rectangle { + fn new(composition: &DirectComposition, notifier: &Box, + device_pixel_ratio: f32, size: api::DeviceUintSize, r: f32, g: f32, b: f32, a: f32) + -> Self { + let visual = composition.create_angle_visual(size.width, size.height); + visual.make_current(); + + let (renderer, sender) = webrender::Renderer::new( + composition.gleam.clone(), + notifier.clone(), + webrender::RendererOptions { + clear_color: Some(api::ColorF::new(0., 0., 0., 0.)), + device_pixel_ratio, + ..webrender::RendererOptions::default() + }, + ).unwrap(); + let api = sender.create_api(); + + Rectangle { + visual, + renderer: Some(renderer), + document_id: api.add_document(size, 0), + api, + size, + color: api::ColorF { r, g, b, a }, + } + } + + fn render(&mut self, device_pixel_ratio: f32, rx: &mpsc::Receiver<()>) { + self.visual.make_current(); + + let pipeline_id = api::PipelineId(0, 0); + let layout_size = self.size.to_f32() / euclid::TypedScale::new(device_pixel_ratio); + let mut builder = api::DisplayListBuilder::new(pipeline_id, layout_size); + + let rect = euclid::TypedRect::new(euclid::TypedPoint2D::zero(), layout_size); + builder.push_rect( + &api::PrimitiveInfo::with_clip( + rect, + api::LocalClip::RoundedRect(rect, api::ComplexClipRegion::new( + rect, api::BorderRadius::uniform(20.), api::ClipMode::Clip, + )) + ), + self.color, + ); + + let mut transaction = api::Transaction::new(); + transaction.set_display_list( + api::Epoch(0), + None, + layout_size, + builder.finalize(), + true, + ); + transaction.set_root_pipeline(pipeline_id); + transaction.generate_frame(); + self.api.send_transaction(self.document_id, transaction); + rx.recv().unwrap(); + let renderer = self.renderer.as_mut().unwrap(); + renderer.update(); + renderer.render(self.size).unwrap(); + let _ = renderer.flush_pipeline_info(); + self.visual.present(); + } +} + +impl Drop for Rectangle { + fn drop(&mut self) { + self.renderer.take().unwrap().deinit() + } +} + +#[derive(Clone)] +struct Notifier { + events_proxy: winit::EventsLoopProxy, + tx: mpsc::Sender<()>, +} + +impl api::RenderNotifier for Notifier { + fn clone(&self) -> Box { + Box::new(Clone::clone(self)) + } + + fn wake_up(&self) { + self.tx.send(()).unwrap(); + let _ = self.events_proxy.wakeup(); + } + + fn new_document_ready(&self, _: api::DocumentId, _: bool, _: bool) { + self.wake_up(); + } +}