@@ -10,6 +10,8 @@ use etagere::BucketedAtlasAllocator;
use parking_lot::Mutex;
use std::{borrow::Cow, sync::Arc};
+pub(crate) const PATH_TEXTURE_FORMAT: gpu::TextureFormat = gpu::TextureFormat::R16Float;
+
pub(crate) struct BladeAtlas(Mutex<BladeAtlasState>);
struct BladeAtlasState {
@@ -32,6 +34,7 @@ impl BladeAtlasState {
}
for texture in self.path_textures.drain(..) {
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.upload_belt.destroy(&self.gpu);
@@ -78,6 +81,11 @@ impl BladeAtlas {
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 {
let mut lock = self.0.lock();
let gpu = lock.gpu.clone();
@@ -85,6 +93,16 @@ impl BladeAtlas {
lock.upload_belt.flush(&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 {
@@ -146,7 +164,7 @@ impl BladeAtlasState {
usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE;
}
AtlasTextureKind::Path => {
- format = gpu::TextureFormat::R16Float;
+ format = PATH_TEXTURE_FORMAT;
usage = gpu::TextureUsage::COPY
| gpu::TextureUsage::RESOURCE
| gpu::TextureUsage::TARGET;
@@ -166,6 +184,17 @@ impl BladeAtlasState {
dimension: gpu::TextureDimension::D2,
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 {
AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
@@ -180,6 +209,7 @@ impl BladeAtlasState {
allocator: etagere::BucketedAtlasAllocator::new(size.into()),
format,
raw,
+ raw_view,
};
textures.push(atlas_texture);
textures.last_mut().unwrap()
@@ -218,6 +248,7 @@ struct BladeAtlasTexture {
id: AtlasTextureId,
allocator: BucketedAtlasAllocator,
raw: gpu::Texture,
+ raw_view: Option<gpu::TextureView>,
format: gpu::TextureFormat,
}
@@ -2,8 +2,12 @@
#![allow(irrefutable_let_patterns)]
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 collections::HashMap;
use blade_graphics as gpu;
use std::sync::Arc;
@@ -33,6 +37,7 @@ struct ShaderShadowsData {
struct BladePipelines {
quads: gpu::RenderPipeline,
shadows: gpu::RenderPipeline,
+ path_rasterization: gpu::RenderPipeline,
}
impl BladePipelines {
@@ -77,6 +82,22 @@ impl BladePipelines {
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,
instance_belt: BladeBelt,
viewport_size: gpu::Extent,
+ path_tiles: HashMap<PathId, AtlasTile>,
+ atlas: Arc<BladeAtlas>,
}
impl BladeRenderer {
@@ -106,6 +129,8 @@ impl BladeRenderer {
memory: gpu::Memory::Shared,
min_chunk_size: 0x1000,
});
+ let atlas = Arc::new(BladeAtlas::new(&gpu));
+
Self {
gpu,
command_encoder,
@@ -113,6 +138,8 @@ impl BladeRenderer {
pipelines,
instance_belt,
viewport_size: size,
+ path_tiles: HashMap::default(),
+ atlas,
}
}
@@ -126,6 +153,7 @@ impl BladeRenderer {
pub fn destroy(&mut self) {
self.wait_for_gpu();
+ self.atlas.destroy();
self.instance_belt.destroy(&self.gpu);
self.gpu.destroy_command_encoder(&mut self.command_encoder);
}
@@ -140,11 +168,56 @@ impl BladeRenderer {
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) {
let frame = self.gpu.acquire_frame();
self.command_encoder.start();
self.command_encoder.init_texture(frame.texture());
+ self.rasterize_paths(scene.paths());
+
let globals = GlobalParams {
viewport_size: [
self.viewport_size.width as f32,
@@ -187,6 +260,7 @@ impl BladeRenderer {
);
encoder.draw(0, 4, 0, shadows.len() as u32);
}
+ PrimitiveBatch::Paths(paths) => {}
_ => continue,
}
}
@@ -1,6 +1,6 @@
use super::BladeRenderer;
use crate::{
- BladeAtlas, Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler,
+ Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler,
PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms,
};
use blade_graphics as gpu;
@@ -57,7 +57,6 @@ pub(crate) struct LinuxWindowState {
x_window: x::Window,
callbacks: Mutex<Callbacks>,
inner: Mutex<LinuxWindowInner>,
- sprite_atlas: Arc<BladeAtlas>,
}
#[derive(Clone)]
@@ -186,7 +185,6 @@ impl LinuxWindowState {
height: bounds.size.height as u32,
depth: 1,
};
- let sprite_atlas = Arc::new(BladeAtlas::new(&gpu));
Self {
xcb_connection: Arc::clone(xcb_connection),
@@ -200,16 +198,11 @@ impl LinuxWindowState {
scale_factor: 1.0,
renderer: BladeRenderer::new(gpu, gpu_extent),
}),
- sprite_atlas,
}
}
pub fn destroy(&self) {
- self.sprite_atlas.destroy();
- {
- let mut inner = self.inner.lock();
- inner.renderer.destroy();
- }
+ self.inner.lock().renderer.destroy();
self.xcb_connection.send_request(&x::UnmapWindow {
window: self.x_window,
});
@@ -379,6 +372,7 @@ impl PlatformWindow for LinuxWindow {
}
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()
}
}