From 3dc1e917bfdc7f1cd5ddc339e954374ed732b0d2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 22 Sep 2023 14:40:39 -0600 Subject: [PATCH] Checkpoint Co-Authored-By: Nathan Sobo --- crates/gpui3/src/app.rs | 24 ++- crates/gpui3/src/executor.rs | 17 +- crates/gpui3/src/geometry.rs | 16 ++ crates/gpui3/src/gpui3.rs | 12 +- crates/gpui3/src/platform.rs | 30 +++- crates/gpui3/src/platform/mac/window.rs | 18 +-- crates/gpui3/src/renderer.rs | 205 ++++++++++++------------ crates/gpui3/src/window.rs | 37 +++-- 8 files changed, 215 insertions(+), 144 deletions(-) diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index 69f88b0cb4ae08c3af855bb582fa6292a07d31f3..164a04199c2657062099a86a7d730bfd219e92a6 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -72,10 +72,14 @@ impl AppContext { &self.text_system } - pub fn with_platform( + pub fn spawn_on_main( &mut self, - f: impl FnOnce(&dyn Platform, &mut Self) -> R + Send + 'static, - ) -> impl Future { + f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static, + ) -> impl Future + where + F: Future + Send + 'static, + R: Send + 'static, + { let this = self.this.upgrade().unwrap(); self.platform.read(move |platform| { let cx = &mut *this.lock(); @@ -88,17 +92,21 @@ impl AppContext { options: crate::WindowOptions, build_root_view: impl FnOnce(&mut WindowContext) -> RootView + Send + 'static, ) -> impl Future> { - self.with_platform(move |platform, cx| { - let id = cx.windows.insert(None); - let handle = WindowHandle::new(id); + let id = self.windows.insert(None); + let handle = WindowHandle::new(id); + let window = + self.spawn_on_main(move |platform, _cx| Window::new(handle.into(), options, platform)); - let mut window = Window::new(handle.into(), options, platform); + let this = self.this.upgrade().unwrap(); + async move { + let mut window = window.await; + let cx = &mut *this.lock(); let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window)); window.root_view.replace(Box::new(root_view)); cx.windows.get_mut(id).unwrap().replace(window); handle - }) + } } pub(crate) fn update_window( diff --git a/crates/gpui3/src/executor.rs b/crates/gpui3/src/executor.rs index c30a159274a172cc9636f604c8b82b3e350afb21..b85e5471f25ed393a0c2cad82c6e02dd69ba365a 100644 --- a/crates/gpui3/src/executor.rs +++ b/crates/gpui3/src/executor.rs @@ -19,18 +19,25 @@ use std::{ /// Enqueues the given closure to be run on the application's event loop. Can be /// called on any thread. -pub(crate) fn spawn_on_main( +pub(crate) fn spawn_on_main( dispatcher: Arc, - future: impl Future + Send + 'static, + func: impl FnOnce() -> F + Send + 'static, ) -> impl Future where + F: Future + 'static, R: Send + 'static, { let (tx, rx) = oneshot::channel(); let (runnable, task) = async_task::spawn( - async move { - let result = future.await; - let _ = tx.send(result); + { + let dispatcher = dispatcher.clone(); + async move { + let future = func(); + let _ = spawn_on_main_local(dispatcher, async move { + let result = future.await; + let _ = tx.send(result); + }); + } }, move |runnable| dispatcher.run_on_main_thread(runnable), ); diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 1acff88511102dc5e5c0ca3430b08c32068dab26..fadff5778570b97b4fbed92c2fe55b14746be9fd 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -235,10 +235,26 @@ impl Edges { #[repr(transparent)] pub struct Pixels(pub(crate) f32); +#[derive( + Clone, Copy, Debug, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd, +)] +#[repr(transparent)] +pub struct DevicePixels(pub(crate) u32); + +impl From for u32 { + fn from(device_pixels: DevicePixels) -> Self { + device_pixels.0 + } +} + impl Pixels { pub fn round(&self) -> Self { Self(self.0.round()) } + + pub fn to_device_pixels(&self, scale: f32) -> DevicePixels { + DevicePixels((self.0 * scale).ceil() as u32) + } } impl Mul for Pixels { diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index b62545cd83d8714f876ffe8f96290458fcc36e38..f8f6cab941f0e1143d2bb330615fe9166de6ef0b 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -22,11 +22,11 @@ pub use color::*; pub use element::*; pub use elements::*; pub use executor::*; -use futures::channel::oneshot; pub use geometry::*; pub use gpui3_macros::*; pub use platform::*; pub use refineable::*; +use renderer::*; pub use scene::*; pub use serde; pub use serde_json; @@ -142,18 +142,20 @@ impl MainThreadOnly { &self.value } - pub(crate) fn read( + pub(crate) fn read( &self, - f: impl FnOnce(&T) -> R + Send + 'static, + f: impl FnOnce(&T) -> F + Send + 'static, ) -> impl Future where + F: Future + 'static, R: Send + 'static, { let this = self.clone(); - crate::spawn_on_main(self.dispatcher.clone(), async move { + crate::spawn_on_main(self.dispatcher.clone(), || async move { // Required so we move `this` instead of this.value. Only `this` is `Send`. let this = this; - f(&this.value) + let result = f(&this.value); + result.await }) } } diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 4974d7e9e9cdacbb0710278396ff2fe946b95f2a..a37ac4fab6a7f9096dc04485a7a7c92d658c4399 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -7,12 +7,14 @@ mod test; use crate::{ AnyWindowHandle, Bounds, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, GlyphId, - LineLayout, Pixels, Point, Result, RunStyle, SharedString, Size, + LineLayout, Pixels, Point, RenderTarget, Result, RunStyle, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; use futures::channel::oneshot; -use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; +use raw_window_handle::{ + HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, +}; use seahash::SeaHasher; use serde::{Deserialize, Serialize}; use std::ffi::c_void; @@ -114,7 +116,10 @@ impl Debug for PlatformScreenHandle { unsafe impl Send for PlatformScreenHandle {} -pub trait PlatformWindow: HasRawWindowHandle + HasRawDisplayHandle { +pub trait PlatformWindow { + fn raw_window_handle(&self) -> RawWindowHandle; + fn raw_display_handle(&self) -> RawDisplayHandle; + fn bounds(&self) -> WindowBounds; fn content_size(&self) -> Size; fn scale_factor(&self) -> f32; @@ -148,6 +153,25 @@ pub trait PlatformWindow: HasRawWindowHandle + HasRawDisplayHandle { fn is_topmost_for_position(&self, position: Point) -> bool; } +unsafe impl<'a> HasRawWindowHandle for &'a dyn PlatformWindow { + fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { + self.raw_window_handle() + } +} + +unsafe impl<'a> HasRawDisplayHandle for &'a dyn PlatformWindow { + fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { + self.raw_display_handle() + } +} + +impl<'a> RenderTarget for &'a dyn PlatformWindow { + fn content_device_size(&self) -> Size { + self.content_size() + .map(|d| d.to_device_pixels(self.scale_factor())) + } +} + pub trait PlatformDispatcher: Send + Sync { fn is_main_thread(&self) -> bool; fn run_on_main_thread(&self, task: Runnable); diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index d5ef95c76c90f7cdc722eaff0ebf6908d8cbc121..6c8ac56045ea317868f498b3b2582e89870fc4a3 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,9 +1,9 @@ use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, InputHandler, KeyDownEvent, Keystroke, - MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, - MouseUpEvent, NSRectExt, Pixels, Platform, PlatformDispatcher, PlatformScreen, PlatformWindow, - Point, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, - WindowPromptLevel, + point, px, size, AnyWindowHandle, Bounds, DevicePixels, Event, InputHandler, KeyDownEvent, + Keystroke, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, + MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, Platform, PlatformDispatcher, PlatformScreen, + PlatformWindow, Point, RenderTarget, Size, Timer, WindowAppearance, WindowBounds, WindowKind, + WindowOptions, WindowPromptLevel, }; use block::ConcreteBlock; use cocoa::{ @@ -617,7 +617,7 @@ impl Drop for MacWindow { fn drop(&mut self) { let this = self.0.clone(); let dispatcher = self.0.lock().dispatcher.clone(); - let _ = crate::spawn_on_main(dispatcher, async move { + let _ = crate::spawn_on_main(dispatcher, || async move { unsafe { this.lock().native_window.close(); } @@ -625,7 +625,7 @@ impl Drop for MacWindow { } } -unsafe impl HasRawWindowHandle for MacWindow { +impl PlatformWindow for MacWindow { fn raw_window_handle(&self) -> RawWindowHandle { let ns_window = self.0.lock().native_window; let ns_view = unsafe { ns_window.contentView() }; @@ -634,15 +634,11 @@ unsafe impl HasRawWindowHandle for MacWindow { handle.ns_view = ns_view as *mut c_void; handle.into() } -} -unsafe impl HasRawDisplayHandle for MacWindow { fn raw_display_handle(&self) -> RawDisplayHandle { AppKitDisplayHandle::empty().into() } -} -impl PlatformWindow for MacWindow { fn bounds(&self) -> WindowBounds { self.0.as_ref().lock().bounds() } diff --git a/crates/gpui3/src/renderer.rs b/crates/gpui3/src/renderer.rs index 9bcd347f8d26b5aa784cf12392228e5c6db97665..5d5f0e5e605373ee36613696625f3eaf65aae089 100644 --- a/crates/gpui3/src/renderer.rs +++ b/crates/gpui3/src/renderer.rs @@ -1,7 +1,7 @@ +use crate::{DevicePixels, Scene, Size}; +use futures::{future::BoxFuture, FutureExt}; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; -use super::{Scene, Size}; - pub struct Renderer { device: wgpu::Device, queue: wgpu::Queue, @@ -12,113 +12,118 @@ pub struct Renderer { vertex_count: u32, } -pub trait Window: HasRawWindowHandle + HasRawDisplayHandle { - fn inner_size(&self) -> Size; +pub(crate) trait RenderTarget: HasRawWindowHandle + HasRawDisplayHandle { + fn content_device_size(&self) -> Size; } impl Renderer { - pub async fn new(window: &W) -> Self + pub(crate) fn new<'a, W>(window: &'a W) -> BoxFuture<'static, Self> where - W: Window, + W: RenderTarget, { let instance = wgpu::Instance::new(Default::default()); let surface = unsafe { instance.create_surface(window).unwrap() }; + let width = window.content_device_size().width; + let height = window.content_device_size().height; + + async move { + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions::default()) + .await + .unwrap(); + + let (device, queue) = adapter + .request_device(&wgpu::DeviceDescriptor::default(), None) + .await + .unwrap(); + + let surface_config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: wgpu::TextureFormat::Bgra8UnormSrgb, + width: width.into(), + height: height.into(), + + // "FIFO" mode renders frames in queue synced with the display's refresh rate. + // Avoids screen tearing but may not offer the lowest latency. Ideal when image + // quality takes priority over input latency. + present_mode: wgpu::PresentMode::Fifo, + + // Use the Premultiplied alpha mode. With premultiplication, the color components + // are multiplied by the alpha value before storage or blending, meaning calculations + // with colors already factor in the influence of alpha. This typically results + // in better performance and avoids a separate multiplication operation during blending. + alpha_mode: wgpu::CompositeAlphaMode::PreMultiplied, + + // Specify the color formats for the views the surface can have. + // In this case, the format is BGRA (blue, green, red, alpha) with unsigned + // normalised integers in the 8-bit range and the color space is sRGB (standard RGB). + // sRGB is the standard color space for displaying images and video on digital displays, + // as it optimises color accuracy and consistency. + view_formats: vec![wgpu::TextureFormat::Bgra8UnormSrgb], + }; + + surface.configure(&device, &surface_config); + + let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Vertex Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.vert.wgsl").into()), + }); - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions::default()) - .await - .unwrap(); - - let (device, queue) = adapter - .request_device(&wgpu::DeviceDescriptor::default(), None) - .await - .unwrap(); - - let surface_config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, - width: window.inner_size().width, - height: window.inner_size().height, - - // "FIFO" mode renders frames in queue synced with the display's refresh rate. - // Avoids screen tearing but may not offer the lowest latency. Ideal when image - // quality takes priority over input latency. - present_mode: wgpu::PresentMode::Fifo, - - // Use the Premultiplied alpha mode. With premultiplication, the color components - // are multiplied by the alpha value before storage or blending, meaning calculations - // with colors already factor in the influence of alpha. This typically results - // in better performance and avoids a separate multiplication operation during blending. - alpha_mode: wgpu::CompositeAlphaMode::PreMultiplied, - - // Specify the color formats for the views the surface can have. - // In this case, the format is BGRA (blue, green, red, alpha) with unsigned - // normalised integers in the 8-bit range and the color space is sRGB (standard RGB). - // sRGB is the standard color space for displaying images and video on digital displays, - // as it optimises color accuracy and consistency. - view_formats: vec![wgpu::TextureFormat::Bgra8UnormSrgb], - }; - - surface.configure(&device, &surface_config); - - let vs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Vertex Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("shader.vert.wgsl").into()), - }); - - let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Fragment Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("shader.frag.wgsl").into()), - }); - - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[], - push_constant_ranges: &[], - }); - - let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor { - label: Some("Vertex Buffer"), - size: 0, - usage: wgpu::BufferUsages::VERTEX, - mapped_at_creation: false, - }); - - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &vs_module, - entry_point: "main", - buffers: &[], - }, - fragment: Some(wgpu::FragmentState { - module: &fs_module, - entry_point: "main", - targets: &[Some(wgpu::ColorTargetState { - format: surface_config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleStrip, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }); - - Self { - device, - queue, - surface, - surface_config, - pipeline, - vertex_buffer, - vertex_count: 0, + let fs_module = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Fragment Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.frag.wgsl").into()), + }); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("Vertex Buffer"), + size: 0, + usage: wgpu::BufferUsages::VERTEX, + mapped_at_creation: false, + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &vs_module, + entry_point: "main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &fs_module, + entry_point: "main", + targets: &[Some(wgpu::ColorTargetState { + format: surface_config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + Self { + device, + queue, + surface, + surface_config, + pipeline, + vertex_buffer, + vertex_count: 0, + } } + .boxed() } pub fn render(&mut self, scene: &Scene) { diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index bce9412e21134b660f1ccf952a84c1f566b59ace..6ea41fbb26a51f081b94cfa3937481b969ca333f 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,6 +1,6 @@ use crate::{ - AvailableSpace, MainThreadOnly, Platform, PlatformWindow, Point, Size, Style, TextStyle, - TextStyleRefinement, WindowOptions, + renderer::Renderer, AvailableSpace, MainThreadOnly, Platform, PlatformWindow, Point, Size, + Style, TextStyle, TextStyleRefinement, WindowOptions, }; use super::{ @@ -12,6 +12,7 @@ use derive_more::{Deref, DerefMut}; use refineable::Refineable; use std::{ any::{Any, TypeId}, + future::Future, marker::PhantomData, sync::Arc, }; @@ -21,6 +22,7 @@ pub struct AnyWindow {} pub struct Window { handle: AnyWindowHandle, platform_window: MainThreadOnly>, + renderer: Renderer, rem_size: Pixels, layout_engine: TaffyLayoutEngine, text_style_stack: Vec, @@ -29,17 +31,28 @@ pub struct Window { } impl Window { - pub fn new(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Window { - let platform_window = Arc::new(platform.open_window(handle, options)); + pub fn new( + handle: AnyWindowHandle, + options: WindowOptions, + platform: &dyn Platform, + ) -> impl Future + 'static { + let platform_window = platform.open_window(handle, options); + let renderer = Renderer::new(&platform_window.as_ref()); let mouse_position = platform_window.mouse_position(); - Window { - handle, - platform_window: MainThreadOnly::new(platform_window, platform.dispatcher()), - rem_size: px(16.), - layout_engine: TaffyLayoutEngine::new(), - text_style_stack: Vec::new(), - root_view: None, - mouse_position, + let platform_window = MainThreadOnly::new(Arc::new(platform_window), platform.dispatcher()); + + async move { + let renderer = renderer.await; + Window { + handle, + platform_window, + renderer, + rem_size: px(16.), + layout_engine: TaffyLayoutEngine::new(), + text_style_stack: Vec::new(), + root_view: None, + mouse_position, + } } } }