summaryrefslogtreecommitdiffstats
path: root/gfx/wr/direct-composition/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /gfx/wr/direct-composition/src
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/wr/direct-composition/src')
-rw-r--r--gfx/wr/direct-composition/src/com.rs112
-rw-r--r--gfx/wr/direct-composition/src/egl.rs174
-rw-r--r--gfx/wr/direct-composition/src/lib.rs179
-rw-r--r--gfx/wr/direct-composition/src/main.rs11
-rw-r--r--gfx/wr/direct-composition/src/main_windows.rs212
5 files changed, 688 insertions, 0 deletions
diff --git a/gfx/wr/direct-composition/src/com.rs b/gfx/wr/direct-composition/src/com.rs
new file mode 100644
index 0000000000..8fb384695c
--- /dev/null
+++ b/gfx/wr/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<T>(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 <https://github.com/retep998/wio-rs/blob/44093f7db8/src/com.rs>
+#[derive(PartialEq, Debug)]
+pub struct ComPtr<T>(*mut T) where T: Interface;
+
+impl<T> ComPtr<T> 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<T> {
+ 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: 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: 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<U>(&self) -> ComPtr<U> where U: Interface {
+ unsafe {
+ ComPtr::<U>::new_with_uuid(|uuid, ptr| self.as_unknown().QueryInterface(uuid, ptr))
+ }
+ }
+}
+
+impl<T> ops::Deref for ComPtr<T> where T: Interface {
+ type Target = T;
+ fn deref(&self) -> &T {
+ unsafe { &*self.0 }
+ }
+}
+
+impl<T> Clone for ComPtr<T> where T: Interface {
+ fn clone(&self) -> Self {
+ unsafe {
+ self.as_unknown().AddRef();
+ ComPtr(self.0)
+ }
+ }
+}
+
+impl<T> Drop for ComPtr<T> where T: Interface {
+ fn drop(&mut self) {
+ unsafe {
+ self.as_unknown().Release();
+ }
+ }
+}
diff --git a/gfx/wr/direct-composition/src/egl.rs b/gfx/wr/direct-composition/src/egl.rs
new file mode 100644
index 0000000000..8bd5afb72a
--- /dev/null
+++ b/gfx/wr/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<Self> {
+ 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<SharedEglThings>,
+ surface: types::EGLSurface,
+}
+
+impl PerVisualEglThings {
+ pub unsafe fn new(shared: Rc<SharedEglThings>, 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/gfx/wr/direct-composition/src/lib.rs b/gfx/wr/direct-composition/src/lib.rs
new file mode 100644
index 0000000000..fadb8f2b72
--- /dev/null
+++ b/gfx/wr/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<ID3D11Device>,
+ dxgi_factory: ComPtr<IDXGIFactory2>,
+
+ egl: Rc<egl::SharedEglThings>,
+ pub gleam: Rc<dyn gleam::gl::Gl>,
+
+ composition_device: ComPtr<IDCompositionDevice>,
+ root_visual: ComPtr<IDCompositionVisual>,
+
+ #[allow(unused)] // Needs to be kept alive
+ composition_target: ComPtr<IDCompositionTarget>,
+}
+
+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::<winapi::shared::dxgi::IDXGIDevice>();
+
+ // 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::<IDXGIFactory2>::new_with_uuid(|uuid, ptr_ptr| {
+ adapter.GetParent(uuid, ptr_ptr)
+ });
+
+ // Create the DirectComposition device object.
+ let composition_device = ComPtr::<IDCompositionDevice>::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::<winapi::shared::dxgi1_2::IDXGISwapChain1>::new_with(|ptr_ptr| {
+ self.dxgi_factory.CreateSwapChainForComposition(
+ as_ptr(&self.d3d_device),
+ &desc,
+ ptr::null_mut(),
+ ptr_ptr,
+ )
+ });
+ let back_buffer = ComPtr::<winapi::um::d3d11::ID3D11Texture2D>::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<IDCompositionVisual>,
+ swap_chain: ComPtr<winapi::shared::dxgi1_2::IDXGISwapChain1>,
+ egl: egl::PerVisualEglThings,
+ pub gleam: Rc<dyn gleam::gl::Gl>,
+}
+
+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/gfx/wr/direct-composition/src/main.rs b/gfx/wr/direct-composition/src/main.rs
new file mode 100644
index 0000000000..e1999f5f8f
--- /dev/null
+++ b/gfx/wr/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/gfx/wr/direct-composition/src/main_windows.rs b/gfx/wr/direct-composition/src/main_windows.rs
new file mode 100644
index 0000000000..bf06480f2a
--- /dev/null
+++ b/gfx/wr/direct-composition/src/main_windows.rs
@@ -0,0 +1,212 @@
+/* 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 euclid::size2;
+use direct_composition::DirectComposition;
+use std::sync::mpsc;
+use webrender::api;
+use webrender::render_api::*;
+use winit::os::windows::{WindowExt, WindowBuilderExt};
+use winit::dpi::LogicalSize;
+
+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(LogicalSize::new(1024., 768.))
+ .with_decorations(true)
+ .with_transparency(true)
+ .with_no_redirection_bitmap(true)
+ .build(&events_loop)
+ .unwrap();
+
+ let composition = direct_composition_from_window(&window);
+ let factor = window.get_hidpi_factor() as f32;
+
+ let mut clicks: usize = 0;
+ let mut offset_y = 100.;
+ let mut rects = [
+ Rectangle::new(&composition, &notifier, factor, size2(300, 200), 0., 0.2, 0.4, 1.),
+ Rectangle::new(&composition, &notifier, factor, size2(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::CloseRequested => {
+ return winit::ControlFlow::Break
+ }
+ winit::WindowEvent::MouseWheel { delta, .. } => {
+ let dy = match delta {
+ winit::MouseScrollDelta::LineDelta(_, dy) => dy,
+ winit::MouseScrollDelta::PixelDelta(pos) => pos.y as f32,
+ };
+ 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<webrender::Renderer>,
+ api: RenderApi,
+ document_id: api::DocumentId,
+ size: api::units::DeviceIntSize,
+ color: api::ColorF,
+}
+
+impl Rectangle {
+ fn new(composition: &DirectComposition, notifier: &Box<Notifier>,
+ device_pixel_ratio: f32, size: api::units::DeviceIntSize, r: f32, g: f32, b: f32, a: f32)
+ -> Self {
+ let visual = composition.create_angle_visual(size.width as u32, size.height as u32);
+ 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()
+ },
+ None,
+ ).unwrap();
+ let api = sender.create_api();
+
+ Rectangle {
+ visual,
+ renderer: Some(renderer),
+ document_id: api.add_document(size),
+ 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::Scale::new(device_pixel_ratio);
+ let mut builder = api::DisplayListBuilder::new(pipeline_id);
+
+ let rect = euclid::Rect::new(euclid::Point2D::zero(), layout_size);
+
+ let region = api::ComplexClipRegion::new(
+ rect,
+ api::BorderRadius::uniform(20.),
+ api::ClipMode::Clip
+ );
+ let clip_id = builder.define_clip_rounded_rect(
+ &api::SpaceAndClipInfo::root_scroll(pipeline_id),
+ region,
+ );
+
+ builder.push_rect(
+ &api::CommonItemProperties::new(
+ rect,
+ api::SpaceAndClipInfo {
+ spatial_id: api::SpatialId::root_scroll_node(pipeline_id),
+ clip_id,
+ },
+ ),
+ rect,
+ self.color,
+ );
+
+ let mut transaction = Transaction::new();
+ transaction.set_display_list(
+ api::Epoch(0),
+ None,
+ layout_size,
+ builder.finalize(),
+ true,
+ );
+ transaction.set_root_pipeline(pipeline_id);
+ transaction.generate_frame(0);
+ self.api.send_transaction(self.document_id, transaction);
+ rx.recv().unwrap();
+ let renderer = self.renderer.as_mut().unwrap();
+ renderer.update();
+ renderer.render(self.size, 0).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<dyn api::RenderNotifier> {
+ Box::new(Clone::clone(self))
+ }
+
+ fn wake_up(&self, _composite_needed: bool) {
+ self.tx.send(()).unwrap();
+ let _ = self.events_proxy.wakeup();
+ }
+
+ fn new_frame_ready(&self,
+ _: api::DocumentId,
+ _: bool,
+ composite_needed: bool,
+ _: Option<u64>) {
+ self.wake_up(composite_needed);
+ }
+}