WIP path rasterization

This commit is contained in:
Dzmitry Malyshau 2024-02-01 00:42:38 -08:00
parent ce84a2a671
commit ed679c9347
4 changed files with 113 additions and 12 deletions

View file

@ -10,6 +10,8 @@ use etagere::BucketedAtlasAllocator;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{borrow::Cow, sync::Arc}; use std::{borrow::Cow, sync::Arc};
pub(crate) const PATH_TEXTURE_FORMAT: gpu::TextureFormat = gpu::TextureFormat::R16Float;
pub(crate) struct BladeAtlas(Mutex<BladeAtlasState>); pub(crate) struct BladeAtlas(Mutex<BladeAtlasState>);
struct BladeAtlasState { struct BladeAtlasState {
@ -32,6 +34,7 @@ impl BladeAtlasState {
} }
for texture in self.path_textures.drain(..) { for texture in self.path_textures.drain(..) {
self.gpu.destroy_texture(texture.raw); self.gpu.destroy_texture(texture.raw);
self.gpu.destroy_texture_view(texture.raw_view.unwrap());
} }
self.gpu.destroy_command_encoder(&mut self.gpu_encoder); self.gpu.destroy_command_encoder(&mut self.gpu_encoder);
self.upload_belt.destroy(&self.gpu); self.upload_belt.destroy(&self.gpu);
@ -78,6 +81,11 @@ impl BladeAtlas {
lock.gpu_encoder.start(); lock.gpu_encoder.start();
} }
pub fn allocate(&self, size: Size<DevicePixels>, texture_kind: AtlasTextureKind) -> AtlasTile {
let mut lock = self.0.lock();
lock.allocate(size, texture_kind)
}
pub fn finish_frame(&self) -> gpu::SyncPoint { pub fn finish_frame(&self) -> gpu::SyncPoint {
let mut lock = self.0.lock(); let mut lock = self.0.lock();
let gpu = lock.gpu.clone(); let gpu = lock.gpu.clone();
@ -85,6 +93,16 @@ impl BladeAtlas {
lock.upload_belt.flush(&sync_point); lock.upload_belt.flush(&sync_point);
sync_point sync_point
} }
pub fn get_texture_view(&self, id: AtlasTextureId) -> gpu::TextureView {
let lock = self.0.lock();
let textures = match id.kind {
crate::AtlasTextureKind::Monochrome => &lock.monochrome_textures,
crate::AtlasTextureKind::Polychrome => &lock.polychrome_textures,
crate::AtlasTextureKind::Path => &lock.path_textures,
};
textures[id.index as usize].raw_view.unwrap()
}
} }
impl PlatformAtlas for BladeAtlas { impl PlatformAtlas for BladeAtlas {
@ -146,7 +164,7 @@ impl BladeAtlasState {
usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE; usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE;
} }
AtlasTextureKind::Path => { AtlasTextureKind::Path => {
format = gpu::TextureFormat::R16Float; format = PATH_TEXTURE_FORMAT;
usage = gpu::TextureUsage::COPY usage = gpu::TextureUsage::COPY
| gpu::TextureUsage::RESOURCE | gpu::TextureUsage::RESOURCE
| gpu::TextureUsage::TARGET; | gpu::TextureUsage::TARGET;
@ -166,6 +184,17 @@ impl BladeAtlasState {
dimension: gpu::TextureDimension::D2, dimension: gpu::TextureDimension::D2,
usage, usage,
}); });
let raw_view = if usage.contains(gpu::TextureUsage::TARGET) {
Some(self.gpu.create_texture_view(gpu::TextureViewDesc {
name: "",
texture: raw,
format,
dimension: gpu::ViewDimension::D2,
subresources: &Default::default(),
}))
} else {
None
};
let textures = match kind { let textures = match kind {
AtlasTextureKind::Monochrome => &mut self.monochrome_textures, AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
@ -180,6 +209,7 @@ impl BladeAtlasState {
allocator: etagere::BucketedAtlasAllocator::new(size.into()), allocator: etagere::BucketedAtlasAllocator::new(size.into()),
format, format,
raw, raw,
raw_view,
}; };
textures.push(atlas_texture); textures.push(atlas_texture);
textures.last_mut().unwrap() textures.last_mut().unwrap()
@ -218,6 +248,7 @@ struct BladeAtlasTexture {
id: AtlasTextureId, id: AtlasTextureId,
allocator: BucketedAtlasAllocator, allocator: BucketedAtlasAllocator,
raw: gpu::Texture, raw: gpu::Texture,
raw_view: Option<gpu::TextureView>,
format: gpu::TextureFormat, format: gpu::TextureFormat,
} }

View file

@ -2,8 +2,12 @@
#![allow(irrefutable_let_patterns)] #![allow(irrefutable_let_patterns)]
use super::{BladeBelt, BladeBeltDescriptor}; use super::{BladeBelt, BladeBeltDescriptor};
use crate::{PrimitiveBatch, Quad, Scene, Shadow}; use crate::{
AtlasTextureKind, AtlasTile, BladeAtlas, ContentMask, Path, PathId, PathVertex, PrimitiveBatch,
Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT,
};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use collections::HashMap;
use blade_graphics as gpu; use blade_graphics as gpu;
use std::sync::Arc; use std::sync::Arc;
@ -33,6 +37,7 @@ struct ShaderShadowsData {
struct BladePipelines { struct BladePipelines {
quads: gpu::RenderPipeline, quads: gpu::RenderPipeline,
shadows: gpu::RenderPipeline, shadows: gpu::RenderPipeline,
path_rasterization: gpu::RenderPipeline,
} }
impl BladePipelines { impl BladePipelines {
@ -77,6 +82,22 @@ impl BladePipelines {
write_mask: gpu::ColorWrites::default(), write_mask: gpu::ColorWrites::default(),
}], }],
}), }),
path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
name: "path_rasterization",
data_layouts: &[&shadows_layout],
vertex: shader.at("vs_path_rasterization"),
primitive: gpu::PrimitiveState {
topology: gpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
fragment: shader.at("fs_path_rasterization"),
color_targets: &[gpu::ColorTargetState {
format: PATH_TEXTURE_FORMAT,
blend: Some(gpu::BlendState::ALPHA_BLENDING),
write_mask: gpu::ColorWrites::default(),
}],
}),
} }
} }
} }
@ -88,6 +109,8 @@ pub struct BladeRenderer {
pipelines: BladePipelines, pipelines: BladePipelines,
instance_belt: BladeBelt, instance_belt: BladeBelt,
viewport_size: gpu::Extent, viewport_size: gpu::Extent,
path_tiles: HashMap<PathId, AtlasTile>,
atlas: Arc<BladeAtlas>,
} }
impl BladeRenderer { impl BladeRenderer {
@ -106,6 +129,8 @@ impl BladeRenderer {
memory: gpu::Memory::Shared, memory: gpu::Memory::Shared,
min_chunk_size: 0x1000, min_chunk_size: 0x1000,
}); });
let atlas = Arc::new(BladeAtlas::new(&gpu));
Self { Self {
gpu, gpu,
command_encoder, command_encoder,
@ -113,6 +138,8 @@ impl BladeRenderer {
pipelines, pipelines,
instance_belt, instance_belt,
viewport_size: size, viewport_size: size,
path_tiles: HashMap::default(),
atlas,
} }
} }
@ -126,6 +153,7 @@ impl BladeRenderer {
pub fn destroy(&mut self) { pub fn destroy(&mut self) {
self.wait_for_gpu(); self.wait_for_gpu();
self.atlas.destroy();
self.instance_belt.destroy(&self.gpu); self.instance_belt.destroy(&self.gpu);
self.gpu.destroy_command_encoder(&mut self.command_encoder); self.gpu.destroy_command_encoder(&mut self.command_encoder);
} }
@ -140,11 +168,56 @@ impl BladeRenderer {
self.viewport_size = size; self.viewport_size = size;
} }
pub fn atlas(&self) -> &Arc<BladeAtlas> {
&self.atlas
}
fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
self.path_tiles.clear();
let mut vertices_by_texture_id = HashMap::default();
for path in paths {
let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
let tile = self
.atlas
.allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path);
vertices_by_texture_id
.entry(tile.texture_id)
.or_insert(Vec::new())
.extend(path.vertices.iter().map(|vertex| PathVertex {
xy_position: vertex.xy_position - clipped_bounds.origin
+ tile.bounds.origin.map(Into::into),
st_position: vertex.st_position,
content_mask: ContentMask {
bounds: tile.bounds.map(Into::into),
},
}));
self.path_tiles.insert(path.id, tile);
}
for (texture_id, vertices) in vertices_by_texture_id {
let instances = self.instance_belt.alloc_data(&vertices, &self.gpu);
let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
colors: &[gpu::RenderTarget {
view: self.atlas.get_texture_view(texture_id),
init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack),
finish_op: gpu::FinishOp::Store,
}],
depth_stencil: None,
});
let mut encoder = pass.with(&self.pipelines.path_rasterization);
encoder.draw(0, vertices.len() as u32, 0, 1);
}
}
pub fn draw(&mut self, scene: &Scene) { pub fn draw(&mut self, scene: &Scene) {
let frame = self.gpu.acquire_frame(); let frame = self.gpu.acquire_frame();
self.command_encoder.start(); self.command_encoder.start();
self.command_encoder.init_texture(frame.texture()); self.command_encoder.init_texture(frame.texture());
self.rasterize_paths(scene.paths());
let globals = GlobalParams { let globals = GlobalParams {
viewport_size: [ viewport_size: [
self.viewport_size.width as f32, self.viewport_size.width as f32,
@ -187,6 +260,7 @@ impl BladeRenderer {
); );
encoder.draw(0, 4, 0, shadows.len() as u32); encoder.draw(0, 4, 0, shadows.len() as u32);
} }
PrimitiveBatch::Paths(paths) => {}
_ => continue, _ => continue,
} }
} }

View file

@ -287,3 +287,5 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4<f32> {
return input.color * vec4<f32>(1.0, 1.0, 1.0, alpha); return input.color * vec4<f32>(1.0, 1.0, 1.0, alpha);
} }
// --- path rasterization --- //

View file

@ -1,6 +1,6 @@
use super::BladeRenderer; use super::BladeRenderer;
use crate::{ use crate::{
BladeAtlas, Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler,
PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms, PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms,
}; };
use blade_graphics as gpu; use blade_graphics as gpu;
@ -57,7 +57,6 @@ pub(crate) struct LinuxWindowState {
x_window: x::Window, x_window: x::Window,
callbacks: Mutex<Callbacks>, callbacks: Mutex<Callbacks>,
inner: Mutex<LinuxWindowInner>, inner: Mutex<LinuxWindowInner>,
sprite_atlas: Arc<BladeAtlas>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -186,7 +185,6 @@ impl LinuxWindowState {
height: bounds.size.height as u32, height: bounds.size.height as u32,
depth: 1, depth: 1,
}; };
let sprite_atlas = Arc::new(BladeAtlas::new(&gpu));
Self { Self {
xcb_connection: Arc::clone(xcb_connection), xcb_connection: Arc::clone(xcb_connection),
@ -200,16 +198,11 @@ impl LinuxWindowState {
scale_factor: 1.0, scale_factor: 1.0,
renderer: BladeRenderer::new(gpu, gpu_extent), renderer: BladeRenderer::new(gpu, gpu_extent),
}), }),
sprite_atlas,
} }
} }
pub fn destroy(&self) { pub fn destroy(&self) {
self.sprite_atlas.destroy(); self.inner.lock().renderer.destroy();
{
let mut inner = self.inner.lock();
inner.renderer.destroy();
}
self.xcb_connection.send_request(&x::UnmapWindow { self.xcb_connection.send_request(&x::UnmapWindow {
window: self.x_window, window: self.x_window,
}); });
@ -379,6 +372,7 @@ impl PlatformWindow for LinuxWindow {
} }
fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> { fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
self.0.sprite_atlas.clone() let mut inner = self.0.inner.lock();
inner.renderer.atlas().clone()
} }
} }