blade: path sprite rendering
This commit is contained in:
parent
fdaffdbfff
commit
c000d2e16b
5 changed files with 138 additions and 9 deletions
|
@ -304,6 +304,7 @@ pub(crate) trait PlatformAtlas: Send + Sync {
|
|||
pub(crate) struct AtlasTile {
|
||||
pub(crate) texture_id: AtlasTextureId,
|
||||
pub(crate) tile_id: TileId,
|
||||
pub(crate) padding: u32,
|
||||
pub(crate) bounds: Bounds<DevicePixels>,
|
||||
}
|
||||
|
||||
|
|
|
@ -276,6 +276,7 @@ impl BladeAtlasTexture {
|
|||
let tile = AtlasTile {
|
||||
texture_id: self.id,
|
||||
tile_id: allocation.id.into(),
|
||||
padding: 0,
|
||||
bounds: Bounds {
|
||||
origin: allocation.rectangle.min.into(),
|
||||
size,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -344,6 +344,7 @@ impl PlatformAtlas for TestAtlas {
|
|||
kind: crate::AtlasTextureKind::Path,
|
||||
},
|
||||
tile_id: TileId(tile_id),
|
||||
padding: 0,
|
||||
bounds: crate::Bounds {
|
||||
origin: Point::default(),
|
||||
size,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue