Detailed changes
@@ -72,10 +72,14 @@ impl AppContext {
&self.text_system
}
- pub fn with_platform<R: Send + 'static>(
+ pub fn spawn_on_main<F, R>(
&mut self,
- f: impl FnOnce(&dyn Platform, &mut Self) -> R + Send + 'static,
- ) -> impl Future<Output = R> {
+ f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static,
+ ) -> impl Future<Output = R>
+ where
+ F: Future<Output = R> + 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<S> + Send + 'static,
) -> impl Future<Output = WindowHandle<S>> {
- 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<R>(
@@ -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<R>(
+pub(crate) fn spawn_on_main<F, R>(
dispatcher: Arc<dyn PlatformDispatcher>,
- future: impl Future<Output = R> + Send + 'static,
+ func: impl FnOnce() -> F + Send + 'static,
) -> impl Future<Output = R>
where
+ F: Future<Output = R> + '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),
);
@@ -235,10 +235,26 @@ impl Edges<Pixels> {
#[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<DevicePixels> 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<f32> for Pixels {
@@ -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<T: 'static + ?Sized> MainThreadOnly<T> {
&self.value
}
- pub(crate) fn read<R>(
+ pub(crate) fn read<R, F>(
&self,
- f: impl FnOnce(&T) -> R + Send + 'static,
+ f: impl FnOnce(&T) -> F + Send + 'static,
) -> impl Future<Output = R>
where
+ F: Future<Output = R> + '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
})
}
}
@@ -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<Pixels>;
fn scale_factor(&self) -> f32;
@@ -148,6 +153,25 @@ pub trait PlatformWindow: HasRawWindowHandle + HasRawDisplayHandle {
fn is_topmost_for_position(&self, position: Point<Pixels>) -> 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<crate::DevicePixels> {
+ 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);
@@ -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()
}
@@ -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<u32>;
+pub(crate) trait RenderTarget: HasRawWindowHandle + HasRawDisplayHandle {
+ fn content_device_size(&self) -> Size<DevicePixels>;
}
impl Renderer {
- pub async fn new<W>(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) {
@@ -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<Box<dyn PlatformWindow>>,
+ renderer: Renderer,
rem_size: Pixels,
layout_engine: TaffyLayoutEngine,
text_style_stack: Vec<TextStyleRefinement>,
@@ -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<Output = Window> + '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,
+ }
}
}
}