Another project
1use crate::gpu::Gpu;
2
3#[repr(C, align(16))]
4#[derive(Copy, Clone, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
5pub struct ChromeInstance {
6 pub rect_xywh_px: [f32; 4],
7 pub fill_premul_rgba: [f32; 4],
8 pub border_premul_rgba: [f32; 4],
9 pub thickness_radius_px: [f32; 2],
10 pub(crate) pad: [f32; 2],
11}
12
13impl ChromeInstance {
14 #[must_use]
15 pub const fn new(
16 rect_xywh_px: [f32; 4],
17 fill_premul_rgba: [f32; 4],
18 border_premul_rgba: [f32; 4],
19 thickness_radius_px: [f32; 2],
20 ) -> Self {
21 Self {
22 rect_xywh_px,
23 fill_premul_rgba,
24 border_premul_rgba,
25 thickness_radius_px,
26 pad: [0.0, 0.0],
27 }
28 }
29}
30
31const INSTANCE_STRIDE: u64 = core::mem::size_of::<ChromeInstance>() as u64;
32const INITIAL_INSTANCE_CAP: u64 = 256;
33
34#[repr(C, align(16))]
35#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
36struct ChromeFrame {
37 viewport_px: [f32; 2],
38 pad: [f32; 2],
39}
40
41const FRAME_SIZE: u64 = core::mem::size_of::<ChromeFrame>() as u64;
42
43pub struct ChromePipeline {
44 device: wgpu::Device,
45 queue: wgpu::Queue,
46 pipeline: wgpu::RenderPipeline,
47 uniform_buffer: wgpu::Buffer,
48 bind_group: wgpu::BindGroup,
49 instance_buffer: wgpu::Buffer,
50 instance_capacity: u64,
51}
52
53impl ChromePipeline {
54 #[must_use]
55 pub fn new(gpu: &Gpu, color_format: wgpu::TextureFormat) -> Self {
56 let device = gpu.device().clone();
57 let queue = gpu.queue().clone();
58 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
59 label: Some("bone-render:chrome-shader"),
60 source: wgpu::ShaderSource::Wgsl(include_str!("chrome.wgsl").into()),
61 });
62 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
63 label: Some("bone-render:chrome-bgl"),
64 entries: &[wgpu::BindGroupLayoutEntry {
65 binding: 0,
66 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
67 ty: wgpu::BindingType::Buffer {
68 ty: wgpu::BufferBindingType::Uniform,
69 has_dynamic_offset: false,
70 min_binding_size: wgpu::BufferSize::new(FRAME_SIZE),
71 },
72 count: None,
73 }],
74 });
75 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
76 label: Some("bone-render:chrome-layout"),
77 bind_group_layouts: &[Some(&bind_group_layout)],
78 immediate_size: 0,
79 });
80 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
81 label: Some("bone-render:chrome-pipeline"),
82 layout: Some(&pipeline_layout),
83 vertex: wgpu::VertexState {
84 module: &shader,
85 entry_point: Some("vs"),
86 compilation_options: wgpu::PipelineCompilationOptions::default(),
87 buffers: &[wgpu::VertexBufferLayout {
88 array_stride: INSTANCE_STRIDE,
89 step_mode: wgpu::VertexStepMode::Instance,
90 attributes: &INSTANCE_ATTRS,
91 }],
92 },
93 fragment: Some(wgpu::FragmentState {
94 module: &shader,
95 entry_point: Some("fs"),
96 compilation_options: wgpu::PipelineCompilationOptions::default(),
97 targets: &[Some(wgpu::ColorTargetState {
98 format: color_format,
99 blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
100 write_mask: wgpu::ColorWrites::ALL,
101 })],
102 }),
103 primitive: wgpu::PrimitiveState {
104 topology: wgpu::PrimitiveTopology::TriangleList,
105 strip_index_format: None,
106 front_face: wgpu::FrontFace::Ccw,
107 cull_mode: None,
108 polygon_mode: wgpu::PolygonMode::Fill,
109 conservative: false,
110 unclipped_depth: false,
111 },
112 depth_stencil: None,
113 multisample: wgpu::MultisampleState::default(),
114 multiview_mask: None,
115 cache: None,
116 });
117 let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
118 label: Some("bone-render:chrome-uniform"),
119 size: FRAME_SIZE,
120 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
121 mapped_at_creation: false,
122 });
123 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
124 label: Some("bone-render:chrome-bg"),
125 layout: &bind_group_layout,
126 entries: &[wgpu::BindGroupEntry {
127 binding: 0,
128 resource: uniform_buffer.as_entire_binding(),
129 }],
130 });
131 let instance_buffer = create_instance_buffer(&device, INITIAL_INSTANCE_CAP);
132 Self {
133 device,
134 queue,
135 pipeline,
136 uniform_buffer,
137 bind_group,
138 instance_buffer,
139 instance_capacity: INITIAL_INSTANCE_CAP,
140 }
141 }
142
143 pub fn upload(&mut self, viewport_px: [f32; 2], instances: &[ChromeInstance]) {
144 let frame = ChromeFrame {
145 viewport_px,
146 pad: [0.0, 0.0],
147 };
148 self.queue
149 .write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&frame));
150 if instances.is_empty() {
151 return;
152 }
153 let needed = instances.len() as u64;
154 if needed > self.instance_capacity {
155 let new_cap = needed.next_power_of_two().max(self.instance_capacity * 2);
156 self.instance_buffer = create_instance_buffer(&self.device, new_cap);
157 self.instance_capacity = new_cap;
158 }
159 self.queue
160 .write_buffer(&self.instance_buffer, 0, bytemuck::cast_slice(instances));
161 }
162
163 pub fn draw_range(
164 &self,
165 encoder: &mut wgpu::CommandEncoder,
166 color_view: &wgpu::TextureView,
167 range: core::ops::Range<u32>,
168 ) {
169 if range.start >= range.end {
170 return;
171 }
172 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
173 label: Some("bone-render:chrome-pass"),
174 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
175 view: color_view,
176 resolve_target: None,
177 depth_slice: None,
178 ops: wgpu::Operations {
179 load: wgpu::LoadOp::Load,
180 store: wgpu::StoreOp::Store,
181 },
182 })],
183 depth_stencil_attachment: None,
184 timestamp_writes: None,
185 occlusion_query_set: None,
186 multiview_mask: None,
187 });
188 pass.set_pipeline(&self.pipeline);
189 pass.set_bind_group(0, &self.bind_group, &[]);
190 let start_bytes = u64::from(range.start) * INSTANCE_STRIDE;
191 let end_bytes = u64::from(range.end) * INSTANCE_STRIDE;
192 pass.set_vertex_buffer(0, self.instance_buffer.slice(start_bytes..end_bytes));
193 pass.draw(0..6, 0..(range.end - range.start));
194 }
195}
196
197fn create_instance_buffer(device: &wgpu::Device, capacity: u64) -> wgpu::Buffer {
198 device.create_buffer(&wgpu::BufferDescriptor {
199 label: Some("bone-render:chrome-instances"),
200 size: capacity * INSTANCE_STRIDE,
201 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
202 mapped_at_creation: false,
203 })
204}
205
206impl core::fmt::Debug for ChromePipeline {
207 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208 f.debug_struct("ChromePipeline").finish_non_exhaustive()
209 }
210}
211
212const INSTANCE_ATTRS: [wgpu::VertexAttribute; 4] = wgpu::vertex_attr_array![
213 0 => Float32x4,
214 1 => Float32x4,
215 2 => Float32x4,
216 3 => Float32x2,
217];