Checkpoint
This commit is contained in:
@@ -10,10 +10,13 @@ path = "src/storybook.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
bytemuck = "1.14.0"
|
||||
derive_more.workspace = true
|
||||
gpui2 = { path = "../gpui2" }
|
||||
itertools = "0.11.0"
|
||||
log.workspace = true
|
||||
plane-split = "0.18.0"
|
||||
raw-window-handle = "0.5.2"
|
||||
refineable = { path = "../refineable" }
|
||||
rust-embed.workspace = true
|
||||
serde.workspace = true
|
||||
@@ -22,6 +25,7 @@ simplelog = "0.9"
|
||||
slotmap = "1.0.6"
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
wgpu = "0.17.0"
|
||||
|
||||
[dev-dependencies]
|
||||
gpui2 = { path = "../gpui2", features = ["test-support"] }
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use serde::de::{self, Deserialize, Deserializer, Visitor};
|
||||
use std::fmt;
|
||||
use std::num::ParseIntError;
|
||||
@@ -101,6 +102,7 @@ impl TryFrom<&'_ str> for Rgba {
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct Hsla {
|
||||
pub h: f32,
|
||||
pub s: f32,
|
||||
@@ -108,6 +110,9 @@ pub struct Hsla {
|
||||
pub a: f32,
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for Hsla {}
|
||||
unsafe impl Pod for Hsla {}
|
||||
|
||||
pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
|
||||
Hsla {
|
||||
h: h.clamp(0., 1.),
|
||||
@@ -126,6 +131,13 @@ pub fn black() -> Hsla {
|
||||
}
|
||||
}
|
||||
|
||||
impl Hsla {
|
||||
/// Returns true if the HSLA color is fully transparent, false otherwise.
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
self.a == 0.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rgba> for Hsla {
|
||||
fn from(color: Rgba) -> Self {
|
||||
let r = color.r;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use core::fmt::Debug;
|
||||
use derive_more::{Add, AddAssign, Div, Mul, Sub};
|
||||
use refineable::Refineable;
|
||||
@@ -5,11 +6,18 @@ use std::ops::Mul;
|
||||
|
||||
#[derive(Refineable, Default, Add, AddAssign, Sub, Mul, Div, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[refineable(debug)]
|
||||
#[repr(C)]
|
||||
pub struct Point<T: Clone + Debug> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug> Point<T> {
|
||||
pub fn new(x: T, y: T) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug> Clone for Point<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
@@ -19,7 +27,11 @@ impl<T: Clone + Debug> Clone for Point<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Refineable, Default, Clone, Debug)]
|
||||
unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Point<T> {}
|
||||
|
||||
unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Point<T> {}
|
||||
|
||||
#[derive(Refineable, Default, Clone, Copy, Debug)]
|
||||
#[refineable(debug)]
|
||||
pub struct Size<T: Clone + Debug> {
|
||||
pub width: T,
|
||||
@@ -60,6 +72,8 @@ pub struct Bounds<T: Clone + Debug> {
|
||||
pub size: Size<T>,
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug + Copy> Copy for Bounds<T> {}
|
||||
|
||||
#[derive(Refineable, Clone, Default, Debug)]
|
||||
#[refineable(debug)]
|
||||
pub struct Edges<T: Clone + Debug> {
|
||||
@@ -127,8 +141,12 @@ impl Edges<Pixels> {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Add, AddAssign, Sub, Mul, Div, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Pixels(pub(crate) f32);
|
||||
|
||||
unsafe impl bytemuck::Pod for Pixels {}
|
||||
unsafe impl bytemuck::Zeroable for Pixels {}
|
||||
|
||||
impl Debug for Pixels {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{} px", self.0)
|
||||
|
||||
@@ -3,7 +3,7 @@ mod color;
|
||||
mod element;
|
||||
mod elements;
|
||||
mod geometry;
|
||||
mod ordered;
|
||||
mod renderer;
|
||||
mod scene;
|
||||
mod style;
|
||||
mod taffy;
|
||||
@@ -18,7 +18,6 @@ pub use elements::*;
|
||||
pub use geometry::*;
|
||||
pub use gpui2::ArcCow;
|
||||
use gpui2::Reference;
|
||||
use ordered::*;
|
||||
pub use scene::*;
|
||||
pub use style::*;
|
||||
pub use taffy::LayoutId;
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
pub struct Ordered<P> {
|
||||
pub order: u32,
|
||||
pub primitive: P,
|
||||
}
|
||||
|
||||
impl<P> Ord for Ordered<P> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> PartialOrd for Ordered<P> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> Eq for Ordered<P> {}
|
||||
|
||||
impl<P> PartialEq for Ordered<P> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.order == other.order
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
|
||||
use super::{Scene, Size};
|
||||
|
||||
pub struct Renderer {
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
surface: wgpu::Surface,
|
||||
surface_config: wgpu::SurfaceConfiguration,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
vertex_buffer: wgpu::Buffer,
|
||||
vertex_count: u32,
|
||||
}
|
||||
|
||||
pub trait Window: HasRawWindowHandle + HasRawDisplayHandle {
|
||||
fn inner_size(&self) -> Size<u32>;
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub async fn new<W>(window: &W) -> Self
|
||||
where
|
||||
W: Window,
|
||||
{
|
||||
let instance = wgpu::Instance::new(Default::default());
|
||||
let surface = unsafe { instance.create_surface(window).unwrap() };
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&mut self, scene: &Scene) {
|
||||
let frame = self.surface.get_current_texture().unwrap();
|
||||
let view = frame
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
self.queue.write_buffer(
|
||||
&self.vertex_buffer,
|
||||
0,
|
||||
bytemuck::cast_slice(&scene.opaque_primitives().quads),
|
||||
);
|
||||
self.vertex_count = scene.opaque_primitives().quads.len() as u32;
|
||||
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Render Encoder"),
|
||||
});
|
||||
|
||||
{
|
||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
|
||||
render_pass.draw(0..self.vertex_count, 0..1);
|
||||
}
|
||||
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,111 @@
|
||||
use super::Ordered;
|
||||
use super::{Bounds, Hsla, Pixels, Point};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use plane_split::BspSplitter;
|
||||
|
||||
/// A platform neutral representation of all geometry to be drawn for the current frame.
|
||||
pub struct Scene {
|
||||
quads: Vec<Ordered<Quad>>,
|
||||
glyphs: Vec<Ordered<Glyph>>,
|
||||
opaque_primitives: PrimitiveBatch,
|
||||
transparent_primitives: slotmap::SlotMap<slotmap::DefaultKey, Primitive>,
|
||||
splitter: BspSplitter<slotmap::DefaultKey>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new() -> Self {
|
||||
pub fn new() -> Scene {
|
||||
Scene {
|
||||
quads: Vec::new(),
|
||||
glyphs: Vec::new(),
|
||||
opaque_primitives: PrimitiveBatch::default(),
|
||||
transparent_primitives: slotmap::SlotMap::new(),
|
||||
splitter: BspSplitter::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, order: u32, primitive: impl Into<Primitive>) {
|
||||
match primitive.into() {
|
||||
Primitive::Quad(primitive) => self.quads.push(Ordered { order, primitive }),
|
||||
Primitive::Glyph(primitive) => self.glyphs.push(Ordered { order, primitive }),
|
||||
pub fn insert(&mut self, primitive: impl Into<Primitive>, is_transparent: bool) {
|
||||
if is_transparent {
|
||||
self.transparent_primitives.insert(primitive.into());
|
||||
} else {
|
||||
match primitive.into() {
|
||||
Primitive::Quad(quad) => self.opaque_primitives.quads.push(quad),
|
||||
Primitive::Glyph(glyph) => self.opaque_primitives.glyphs.push(glyph),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&mut self) {
|
||||
self.quads.sort_unstable();
|
||||
self.glyphs.sort_unstable();
|
||||
pub fn opaque_primitives(&self) -> &PrimitiveBatch {
|
||||
&self.opaque_primitives
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Primitive {
|
||||
Quad(Quad),
|
||||
Glyph(Glyph),
|
||||
// Icon(Icon),
|
||||
// Image(Image),
|
||||
// Shadow(Shadow),
|
||||
// Curve(Curve),
|
||||
}
|
||||
|
||||
#[derive(Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct Quad {}
|
||||
pub struct Glyph {}
|
||||
// pub struct Icon {}
|
||||
// pub struct Image {}
|
||||
// pub struct Shadow {}
|
||||
// pub struct Curve {}
|
||||
impl Primitive {
|
||||
pub fn is_transparent(&self) -> bool {
|
||||
match self {
|
||||
Primitive::Quad(quad) => {
|
||||
quad.background.is_transparent() && quad.border_color.is_transparent()
|
||||
}
|
||||
Primitive::Glyph(glyph) => glyph.color.is_transparent(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct PrimitiveBatch {
|
||||
pub quads: Vec<Quad>,
|
||||
pub glyphs: Vec<Glyph>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub struct Quad {
|
||||
pub order: f32,
|
||||
pub bounds: Bounds<Pixels>,
|
||||
pub background: Hsla,
|
||||
pub border_color: Hsla,
|
||||
pub corner_radius: Pixels,
|
||||
pub border_left: Pixels,
|
||||
pub border_right: Pixels,
|
||||
pub border_top: Pixels,
|
||||
pub border_bottom: Pixels,
|
||||
}
|
||||
|
||||
impl Quad {
|
||||
pub fn vertices(&self) -> impl Iterator<Item = Point<Pixels>> {
|
||||
let x1 = self.bounds.origin.x;
|
||||
let y1 = self.bounds.origin.y;
|
||||
let x2 = x1 + self.bounds.size.width;
|
||||
let y2 = y1 + self.bounds.size.height;
|
||||
[
|
||||
Point::new(x1, y1),
|
||||
Point::new(x2, y1),
|
||||
Point::new(x2, y2),
|
||||
Point::new(x1, y2),
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Zeroable for Quad {}
|
||||
|
||||
unsafe impl Pod for Quad {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Glyph {
|
||||
pub order: f32,
|
||||
pub bounds: Bounds<Pixels>,
|
||||
pub color: Hsla,
|
||||
// ...
|
||||
}
|
||||
|
||||
impl From<Quad> for Primitive {
|
||||
fn from(quad: Quad) -> Self {
|
||||
Primitive::Quad(quad)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Glyph> for Primitive {
|
||||
fn from(glyph: Glyph) -> Self {
|
||||
Primitive::Glyph(glyph)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user