@@ -3,8 +3,8 @@
use super::{BladeBelt, BladeBeltDescriptor};
use crate::{
- AtlasTextureKind, AtlasTile, BladeAtlas, ContentMask, Path, PathId, PathVertex, PrimitiveBatch,
- Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT,
+ AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, Path, PathId, PathVertex,
+ PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT,
};
use bytemuck::{Pod, Zeroable};
use collections::HashMap;
@@ -40,10 +40,27 @@ struct ShaderPathRasterizationData {
b_path_vertices: gpu::BufferPiece,
}
+#[derive(blade_macros::ShaderData)]
+struct ShaderPathsData {
+ globals: GlobalParams,
+ t_tile: gpu::TextureView,
+ s_tile: gpu::Sampler,
+ b_path_sprites: gpu::BufferPiece,
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[repr(C)]
+struct PathSprite {
+ bounds: Bounds<ScaledPixels>,
+ color: Hsla,
+ tile: AtlasTile,
+}
+
struct BladePipelines {
quads: gpu::RenderPipeline,
shadows: gpu::RenderPipeline,
path_rasterization: gpu::RenderPipeline,
+ paths: gpu::RenderPipeline,
}
impl BladePipelines {
@@ -57,9 +74,12 @@ impl BladePipelines {
mem::size_of::<PathVertex<ScaledPixels>>(),
shader.get_struct_size("PathVertex") as usize,
);
+ shader.check_struct_size::<PathSprite>();
+
let quads_layout = <ShaderQuadsData as gpu::ShaderData>::layout();
let shadows_layout = <ShaderShadowsData as gpu::ShaderData>::layout();
let path_rasterization_layout = <ShaderPathRasterizationData as gpu::ShaderData>::layout();
+ let paths_layout = <ShaderPathsData as gpu::ShaderData>::layout();
Self {
quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
@@ -110,6 +130,22 @@ impl BladePipelines {
write_mask: gpu::ColorWrites::default(),
}],
}),
+ paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
+ name: "paths",
+ data_layouts: &[&paths_layout],
+ vertex: shader.at("vs_path"),
+ primitive: gpu::PrimitiveState {
+ topology: gpu::PrimitiveTopology::TriangleStrip,
+ ..Default::default()
+ },
+ depth_stencil: None,
+ fragment: shader.at("fs_path"),
+ color_targets: &[gpu::ColorTargetState {
+ format: surface_format,
+ blend: Some(gpu::BlendState::ALPHA_BLENDING),
+ write_mask: gpu::ColorWrites::default(),
+ }],
+ }),
}
}
}
@@ -123,6 +159,7 @@ pub struct BladeRenderer {
viewport_size: gpu::Extent,
path_tiles: HashMap<PathId, AtlasTile>,
atlas: Arc<BladeAtlas>,
+ atlas_sampler: gpu::Sampler,
}
impl BladeRenderer {
@@ -142,6 +179,12 @@ impl BladeRenderer {
min_chunk_size: 0x1000,
});
let atlas = Arc::new(BladeAtlas::new(&gpu));
+ let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc {
+ name: "atlas",
+ mag_filter: gpu::FilterMode::Linear,
+ min_filter: gpu::FilterMode::Linear,
+ ..Default::default()
+ });
Self {
gpu,
@@ -152,6 +195,7 @@ impl BladeRenderer {
viewport_size: size,
path_tiles: HashMap::default(),
atlas,
+ atlas_sampler,
}
}
@@ -285,7 +329,35 @@ impl BladeRenderer {
);
encoder.draw(0, 4, 0, shadows.len() as u32);
}
- PrimitiveBatch::Paths(paths) => {}
+ PrimitiveBatch::Paths(paths) => {
+ let mut encoder = pass.with(&self.pipelines.paths);
+ //TODO: group by texture ID
+ for path in paths {
+ let tile = &self.path_tiles[&path.id];
+ let tex_info = self.atlas.get_texture_info(tile.texture_id);
+ let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
+ let sprites = [PathSprite {
+ bounds: Bounds {
+ origin: origin.map(|p| p.floor()),
+ size: tile.bounds.size.map(Into::into),
+ },
+ color: path.color,
+ tile: (*tile).clone(),
+ }];
+
+ let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu);
+ encoder.bind(
+ 0,
+ &ShaderPathsData {
+ globals,
+ t_tile: tex_info.raw_view.unwrap(),
+ s_tile: self.atlas_sampler,
+ b_path_sprites: instance_buf,
+ },
+ );
+ encoder.draw(0, 4, 0, sprites.len() as u32);
+ }
+ }
_ => continue,
}
}
@@ -4,6 +4,8 @@ struct Globals {
}
var<uniform> globals: Globals;
+var t_tile: texture_2d<f32>;
+var s_tile: sampler;
const M_PI_F: f32 = 3.1415926;
@@ -35,6 +37,18 @@ struct Hsla {
a: f32,
}
+struct AtlasTextureId {
+ index: u32,
+ kind: u32,
+}
+
+struct AtlasTile {
+ texture_id: AtlasTextureId,
+ tile_id: u32,
+ padding: u32,
+ bounds: Bounds,
+}
+
fn to_device_position_impl(position: vec2<f32>) -> vec4<f32> {
let device_position = position / globals.viewport_size * vec2<f32>(2.0, -2.0) + vec2<f32>(-1.0, 1.0);
return vec4<f32>(device_position, 0.0, 1.0);
@@ -45,6 +59,11 @@ fn to_device_position(unit_vertex: vec2<f32>, bounds: Bounds) -> vec4<f32> {
return to_device_position_impl(position);
}
+fn to_tile_position(unit_vertex: vec2<f32>, tile: AtlasTile) -> vec2<f32> {
+ let atlas_size = vec2<f32>(textureDimensions(t_tile, 0));
+ return (tile.bounds.origin + unit_vertex * tile.bounds.size) / atlas_size;
+}
+
fn distance_from_clip_rect_impl(position: vec2<f32>, clip_bounds: Bounds) -> vec4<f32> {
let tl = position - clip_bounds.origin;
let br = clip_bounds.origin + clip_bounds.size - position;
@@ -325,14 +344,49 @@ fn vs_path_rasterization(@builtin(vertex_index) vertex_id: u32) -> PathRasteriza
@fragment
fn fs_path_rasterization(input: PathRasterizationVarying) -> @location(0) f32 {
- let dx = dpdx(input.st_position);
- let dy = dpdy(input.st_position);
+ let dx = dpdx(input.st_position);
+ let dy = dpdy(input.st_position);
if (any(input.clip_distances < vec4<f32>(0.0))) {
return 0.0;
}
- let gradient = 2.0 * input.st_position * vec2<f32>(dx.x, dy.x) - vec2<f32>(dx.y, dy.y);
- let f = input.st_position.x * input.st_position.x - input.st_position.y;
- let distance = f / length(gradient);
- return saturate(0.5 - distance);
+ let gradient = 2.0 * input.st_position * vec2<f32>(dx.x, dy.x) - vec2<f32>(dx.y, dy.y);
+ let f = input.st_position.x * input.st_position.x - input.st_position.y;
+ let distance = f / length(gradient);
+ return saturate(0.5 - distance);
+}
+
+// --- paths --- //
+
+struct PathSprite {
+ bounds: Bounds,
+ color: Hsla,
+ tile: AtlasTile,
+}
+var<storage, read> b_path_sprites: array<PathSprite>;
+
+struct PathVarying {
+ @builtin(position) position: vec4<f32>,
+ @location(0) tile_position: vec2<f32>,
+ @location(1) color: vec4<f32>,
+}
+
+@vertex
+fn vs_path(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> PathVarying {
+ let unit_vertex = vec2<f32>(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u));
+ let sprite = b_path_sprites[instance_id];
+ // Don't apply content mask because it was already accounted for when rasterizing the path.
+
+ var out = PathVarying();
+ out.position = to_device_position(unit_vertex, sprite.bounds);
+ out.tile_position = to_tile_position(unit_vertex, sprite.tile);
+ out.color = hsla_to_rgba(sprite.color);
+ return out;
+}
+
+@fragment
+fn fs_path(input: PathVarying) -> @location(0) vec4<f32> {
+ let sample = textureSample(t_tile, s_tile, input.tile_position).r;
+ let mask = 1.0 - abs(1.0 - sample % 2.0);
+ return input.color * mask;
}