Krolyxon
Commit 92928dd

Branches

Tags

Refs →

remagesrcrenderprims.rs

154 lines · 5.2 KB · Rust

1//! One instanced SDF shader for rects, rounded rects, borders and dots.
2
3use bytemuck::{Pod, Zeroable};
4use wgpu::util::DeviceExt;
5
6/// A primitive in physical pixels; colours are linear with straight alpha.
7#[repr(C)]
8#[derive(Clone, Copy, Debug, Pod, Zeroable)]
9pub struct Prim {
10 pub rect: [f32; 4],
11 pub color: [f32; 4],
12 pub border_color: [f32; 4],
13 /// x: corner radius, y: border width.
14 pub params: [f32; 4],
15}
16
17impl Prim {
18 pub fn rect(rect: [f32; 4], color: [f32; 4]) -> Self {
19 Self {
20 rect,
21 color,
22 border_color: [0.0; 4],
23 params: [0.0; 4],
24 }
25 }
26}
27
28pub struct PrimPipeline {
29 pipeline: wgpu::RenderPipeline,
30 globals: wgpu::Buffer,
31 bind_group: wgpu::BindGroup,
32 instances: wgpu::Buffer,
33 capacity: usize,
34 count: u32,
35}
36
37const ATTRS: [wgpu::VertexAttribute; 4] =
38 wgpu::vertex_attr_array![0 => Float32x4, 1 => Float32x4, 2 => Float32x4, 3 => Float32x4];
39
40fn instance_buffer(device: &wgpu::Device, capacity: usize) -> wgpu::Buffer {
41 device.create_buffer(&wgpu::BufferDescriptor {
42 label: Some("prims"),
43 size: (capacity * size_of::<Prim>()) as u64,
44 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
45 mapped_at_creation: false,
46 })
47}
48
49impl PrimPipeline {
50 pub fn new(device: &wgpu::Device, target: wgpu::TextureFormat) -> Self {
51 let globals = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
52 label: Some("prim globals"),
53 contents: bytemuck::bytes_of(&[0.0f32; 4]),
54 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
55 });
56 let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
57 label: Some("prims"),
58 entries: &[wgpu::BindGroupLayoutEntry {
59 binding: 0,
60 visibility: wgpu::ShaderStages::VERTEX,
61 ty: wgpu::BindingType::Buffer {
62 ty: wgpu::BufferBindingType::Uniform,
63 has_dynamic_offset: false,
64 min_binding_size: None,
65 },
66 count: None,
67 }],
68 });
69 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
70 label: Some("prims"),
71 layout: &layout,
72 entries: &[wgpu::BindGroupEntry {
73 binding: 0,
74 resource: globals.as_entire_binding(),
75 }],
76 });
77 let shader = device.create_shader_module(wgpu::include_wgsl!("shaders/prims.wgsl"));
78 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
79 label: Some("prims"),
80 bind_group_layouts: &[Some(&layout)],
81 immediate_size: 0,
82 });
83 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
84 label: Some("prims"),
85 layout: Some(&pipeline_layout),
86 vertex: wgpu::VertexState {
87 module: &shader,
88 entry_point: Some("vs"),
89 compilation_options: Default::default(),
90 buffers: &[Some(wgpu::VertexBufferLayout {
91 array_stride: size_of::<Prim>() as u64,
92 step_mode: wgpu::VertexStepMode::Instance,
93 attributes: &ATTRS,
94 })],
95 },
96 primitive: wgpu::PrimitiveState::default(),
97 depth_stencil: None,
98 multisample: wgpu::MultisampleState::default(),
99 fragment: Some(wgpu::FragmentState {
100 module: &shader,
101 entry_point: Some("fs"),
102 compilation_options: Default::default(),
103 targets: &[Some(wgpu::ColorTargetState {
104 format: target,
105 blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
106 write_mask: wgpu::ColorWrites::ALL,
107 })],
108 }),
109 multiview_mask: None,
110 cache: None,
111 });
112 let capacity = 64;
113 Self {
114 pipeline,
115 globals,
116 bind_group,
117 instances: instance_buffer(device, capacity),
118 capacity,
119 count: 0,
120 }
121 }
122
123 pub fn prepare(
124 &mut self,
125 device: &wgpu::Device,
126 queue: &wgpu::Queue,
127 prims: &[Prim],
128 viewport: [f32; 2],
129 ) {
130 queue.write_buffer(
131 &self.globals,
132 0,
133 bytemuck::bytes_of(&[viewport[0], viewport[1], 0.0, 0.0]),
134 );
135 if prims.len() > self.capacity {
136 self.capacity = prims.len().next_power_of_two();
137 self.instances = instance_buffer(device, self.capacity);
138 }
139 if !prims.is_empty() {
140 queue.write_buffer(&self.instances, 0, bytemuck::cast_slice(prims));
141 }
142 self.count = prims.len() as u32;
143 }
144
145 pub fn draw(&self, pass: &mut wgpu::RenderPass<'_>) {
146 if self.count == 0 {
147 return;
148 }
149 pass.set_pipeline(&self.pipeline);
150 pass.set_bind_group(0, &self.bind_group, &[]);
151 pass.set_vertex_buffer(0, self.instances.slice(..));
152 pass.draw(0..6, 0..self.count);
153 }
154}