1use std::{borrow::Cow, mem, num::NonZeroU32, slice, time::Duration};
16
17use anyhow::Error;
18use wgpu::util::DeviceExt;
19
20use crate::{segment::SegmentBufferView, styling::Color, utils::DivCeil, Composition};
21
22use super::{conveyor_sort, painter, rasterizer, GpuContext, StyleMap, TimeStamp};
23
24#[derive(Debug)]
25pub struct Timings {
26 pub rasterize: Duration,
27 pub sort: Duration,
28 pub paint: Duration,
29 pub render: Duration,
30}
31
32impl Timings {
33 pub(crate) const fn size() -> usize {
34 mem::size_of::<Timings>() / mem::size_of::<u64>()
35 }
36}
37
38#[derive(Debug)]
39struct RenderContext {
40 pipeline: wgpu::RenderPipeline,
41 bind_group_layout: wgpu::BindGroupLayout,
42 sampler: wgpu::Sampler,
43}
44
45#[derive(Debug, Default)]
46struct Resources {
47 atlas: Option<wgpu::Texture>,
48 texture: Option<(wgpu::Texture, u32, u32)>,
49}
50
51#[derive(Debug)]
52pub struct Renderer {
53 rasterizer: rasterizer::RasterizerContext,
54 sort: conveyor_sort::SortContext,
55 paint: painter::PaintContext,
56 render: RenderContext,
57 common: Resources,
58 styling_map: StyleMap,
59 has_timestamp_query: bool,
60}
61
62impl Renderer {
63 pub fn minimum_device(adapter: &wgpu::Adapter) -> (wgpu::DeviceDescriptor, bool) {
64 let adapter_features = adapter.features();
65 let has_timestamp_query = adapter_features.contains(wgpu::Features::TIMESTAMP_QUERY);
66
67 let desc = wgpu::DeviceDescriptor {
68 label: None,
69 features: wgpu::Features::TIMESTAMP_QUERY & adapter_features,
70 limits: wgpu::Limits {
71 max_texture_dimension_2d: 4096,
72 max_storage_buffer_binding_size: 1 << 30,
73 ..wgpu::Limits::downlevel_defaults()
74 },
75 };
76
77 (desc, has_timestamp_query)
78 }
79
80 pub fn new(
81 device: &wgpu::Device,
82 swap_chain_format: wgpu::TextureFormat,
83 has_timestamp_query: bool,
84 ) -> Self {
85 let rasterizer = rasterizer::RasterizerContext::init(device);
86 let sort = conveyor_sort::SortContext::init(device);
87 let paint = painter::PaintContext::init(device);
88
89 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
90 label: None,
91 source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("draw_texture.wgsl"))),
92 });
93
94 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
95 label: None,
96 layout: None,
97 vertex: wgpu::VertexState {
98 module: &shader,
99 entry_point: "vs_main",
100 buffers: &[],
101 },
102 fragment: Some(wgpu::FragmentState {
103 module: &shader,
104 entry_point: "fs_main",
105 targets: &[Some(swap_chain_format.into())],
106 }),
107 primitive: wgpu::PrimitiveState::default(),
108 depth_stencil: None,
109 multisample: wgpu::MultisampleState::default(),
110 multiview: None,
111 });
112
113 let bind_group_layout = pipeline.get_bind_group_layout(0);
114
115 let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
116
117 let render = RenderContext {
118 pipeline,
119 bind_group_layout,
120 sampler,
121 };
122
123 Self {
124 rasterizer,
125 sort,
126 paint,
127 render,
128 common: Resources::default(),
129 styling_map: StyleMap::new(),
130 has_timestamp_query,
131 }
132 }
133
134 #[allow(clippy::too_many_arguments)]
135 fn render_inner(
136 &mut self,
137 device: &wgpu::Device,
138 queue: &wgpu::Queue,
139 view: &wgpu::TextureView,
140 width: u32,
141 height: u32,
142 segment_buffer_view: &SegmentBufferView,
143 background_color: painter::Color,
144 ) -> Result<Option<Timings>, Error> {
145 let timestamp_context = self.has_timestamp_query.then(|| {
146 let timestamp = device.create_query_set(&wgpu::QuerySetDescriptor {
147 label: None,
148 count: Timings::size() as u32,
149 ty: wgpu::QueryType::Timestamp,
150 });
151
152 let timestamp_period = queue.get_timestamp_period();
153
154 let data_buffer = device.create_buffer(&wgpu::BufferDescriptor {
155 label: None,
156 size: mem::size_of::<Timings>() as wgpu::BufferAddress,
157 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
158 mapped_at_creation: false,
159 });
160
161 (timestamp, timestamp_period, data_buffer)
162 });
163
164 if let Some((texture, current_width, current_height)) = self.common.texture.as_ref() {
165 if *current_width != width || *current_height != height {
166 texture.destroy();
167
168 self.common.texture = None;
169 }
170 }
171
172 let output_texture = self
173 .common
174 .texture
175 .get_or_insert_with(|| {
176 let texture = device.create_texture(&wgpu::TextureDescriptor {
177 label: None,
178 size: wgpu::Extent3d {
179 width,
180 height,
181 depth_or_array_layers: 1,
182 },
183 mip_level_count: 1,
184 sample_count: 1,
185 dimension: wgpu::TextureDimension::D2,
186 format: wgpu::TextureFormat::Rgba16Float,
187 usage: wgpu::TextureUsages::STORAGE_BINDING
188 | wgpu::TextureUsages::TEXTURE_BINDING,
189 });
190
191 (texture, width, height)
192 })
193 .0
194 .create_view(&wgpu::TextureViewDescriptor::default());
195
196 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
197 layout: &self.render.bind_group_layout,
198 entries: &[
199 wgpu::BindGroupEntry {
200 binding: 0,
201 resource: wgpu::BindingResource::TextureView(&output_texture),
202 },
203 wgpu::BindGroupEntry {
204 binding: 1,
205 resource: wgpu::BindingResource::Sampler(&self.render.sampler),
206 },
207 ],
208 label: None,
209 });
210
211 let mut encoder =
212 device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
213
214 let atlas_texture = self.common.atlas.get_or_insert_with(|| {
215 device.create_texture(&wgpu::TextureDescriptor {
216 label: Some("atlas"),
217 size: wgpu::Extent3d {
218 width: 4096,
219 height: 4096,
220 depth_or_array_layers: 1,
221 },
222 mip_level_count: 1,
223 sample_count: 1,
224 dimension: wgpu::TextureDimension::D2,
225 format: wgpu::TextureFormat::Rgba16Float,
226 usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,
227 })
228 });
229
230 for (image, [xmin, ymin, _, _]) in self.styling_map.new_allocs() {
231 queue.write_texture(
232 wgpu::ImageCopyTexture {
233 texture: atlas_texture,
234 mip_level: 0,
235 origin: wgpu::Origin3d {
236 x: *xmin,
237 y: *ymin,
238 z: 0,
239 },
240 aspect: wgpu::TextureAspect::All,
241 },
242 unsafe {
243 slice::from_raw_parts(
244 (image.data() as *const _) as *const u8,
245 mem::size_of_val(image.data()),
246 )
247 },
248 wgpu::ImageDataLayout {
249 offset: 0,
250 bytes_per_row: Some(NonZeroU32::new(4 * 2 * image.width()).unwrap()),
251 rows_per_image: None,
252 },
253 wgpu::Extent3d {
254 width: image.width(),
255 height: image.height(),
256 depth_or_array_layers: 1,
257 },
258 );
259 }
260
261 let segments_len = segment_buffer_view.len();
263 if segments_len > 0 && width != 0 && height != 0 {
264 let segments_blocks =
265 segments_len.div_ceil_(conveyor_sort::BLOCK_SIZE.block_len as usize);
266
267 let pixel_segment_buffer = device.create_buffer(&wgpu::BufferDescriptor {
268 label: None,
269 size: (segments_len * std::mem::size_of::<u64>()) as wgpu::BufferAddress,
270 usage: wgpu::BufferUsages::STORAGE,
271 mapped_at_creation: false,
272 });
273
274 let mut data = rasterizer::Data {
275 segment_buffer_view,
276 pixel_segment_buffer,
277 };
278 self.rasterizer.encode(
279 device,
280 &mut encoder,
281 timestamp_context
282 .as_ref()
283 .map(|(query_set, _, _)| TimeStamp {
284 query_set,
285 start_index: 0,
286 end_index: 1,
287 }),
288 &mut data,
289 );
290
291 let slice_size = segments_len * std::mem::size_of::<u64>();
292 let size = slice_size as wgpu::BufferAddress;
293
294 let swap_buffer = device.create_buffer(&wgpu::BufferDescriptor {
295 label: None,
296 size,
297 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
298 mapped_at_creation: false,
299 });
300 let offset_buffer = device.create_buffer(&wgpu::BufferDescriptor {
301 label: None,
302 size: ((segments_blocks + 1) * std::mem::size_of::<u32>()) as wgpu::BufferAddress,
303 usage: wgpu::BufferUsages::STORAGE,
304 mapped_at_creation: false,
305 });
306
307 let pixel_segment_buffer = data.pixel_segment_buffer;
308 let mut data = conveyor_sort::Data {
309 segment_buffer_view,
310 pixel_segment_buffer,
311 swap_buffer,
312 offset_buffer,
313 };
314 self.sort.encode(
315 device,
316 &mut encoder,
317 timestamp_context
318 .as_ref()
319 .map(|(query_set, _, _)| TimeStamp {
320 query_set,
321 start_index: 2,
322 end_index: 3,
323 }),
324 &mut data,
325 );
326
327 let style_offset_buffer =
328 device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
329 label: None,
330 contents: bytemuck::cast_slice(self.styling_map.style_offsets()),
331 usage: wgpu::BufferUsages::STORAGE,
332 });
333 let style_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
334 label: None,
335 contents: bytemuck::cast_slice(self.styling_map.styles()),
336 usage: wgpu::BufferUsages::STORAGE,
337 });
338
339 let pixel_segment_buffer = data.pixel_segment_buffer;
340 let mut data = painter::Data {
341 segment_buffer_view,
342 pixel_segment_buffer,
343 style_buffer,
344 style_offset_buffer,
345 atlas_texture: atlas_texture.create_view(&wgpu::TextureViewDescriptor::default()),
346 output_texture,
347 width,
348 height,
349 clear_color: background_color,
350 };
351 self.paint.encode(
352 device,
353 &mut encoder,
354 timestamp_context
355 .as_ref()
356 .map(|(query_set, _, _)| TimeStamp {
357 query_set,
358 start_index: 4,
359 end_index: 5,
360 }),
361 &mut data,
362 );
363 }
364
365 if let Some((timestamp, _, _)) = timestamp_context.as_ref() {
366 encoder.write_timestamp(timestamp, 6);
367 }
368
369 {
370 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
371 label: None,
372 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
373 view,
374 resolve_target: None,
375 ops: wgpu::Operations {
376 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
377 store: true,
378 },
379 })],
380 depth_stencil_attachment: None,
381 });
382
383 rpass.set_pipeline(&self.render.pipeline);
384 rpass.set_bind_group(0, &bind_group, &[]);
385 rpass.draw(0..3, 0..1);
386 }
387
388 if let Some((timestamp, _, _)) = timestamp_context.as_ref() {
389 encoder.write_timestamp(timestamp, 7);
390 }
391
392 if let Some((timestamp, _, data_buffer)) = ×tamp_context {
393 encoder.resolve_query_set(timestamp, 0..Timings::size() as u32, data_buffer, 0);
394 }
395
396 queue.submit(Some(encoder.finish()));
397
398 let timings = timestamp_context
399 .as_ref()
400 .map(|(_, timestamp_period, data_buffer)| {
401 use bytemuck::{Pod, Zeroable};
402
403 #[repr(C)]
404 #[derive(Clone, Copy, Debug, Pod, Zeroable)]
405 struct TimestampData {
406 start: u64,
407 end: u64,
408 }
409
410 data_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ());
411
412 device.poll(wgpu::Maintain::Wait);
413
414 let view = data_buffer.slice(..).get_mapped_range();
415 let timestamps: [TimestampData; Timings::size() / 2] = *bytemuck::from_bytes(&view);
416 let durations = timestamps.map(|timestamp| {
417 let nanos = (timestamp.end - timestamp.start) as f32 * timestamp_period;
418 Duration::from_nanos(nanos as u64)
419 });
420
421 Timings {
422 rasterize: durations[0],
423 sort: durations[1],
424 paint: durations[2],
425 render: durations[3],
426 }
427 });
428
429 Ok(timings)
430 }
431
432 #[allow(clippy::too_many_arguments)]
433 pub fn render(
434 &mut self,
435 composition: &mut Composition,
436 device: &wgpu::Device,
437 queue: &wgpu::Queue,
438 surface: &wgpu::Surface,
439 width: u32,
440 height: u32,
441 clear_color: Color,
442 ) -> Option<Timings> {
443 let frame = surface.get_current_texture().unwrap();
444 let timings = self.render_to_texture(
445 composition,
446 device,
447 queue,
448 &frame
449 .texture
450 .create_view(&wgpu::TextureViewDescriptor::default()),
451 width,
452 height,
453 clear_color,
454 );
455
456 frame.present();
457
458 timings
459 }
460
461 #[allow(clippy::too_many_arguments)]
462 pub fn render_to_texture(
463 &mut self,
464 composition: &mut Composition,
465 device: &wgpu::Device,
466 queue: &wgpu::Queue,
467 view: &wgpu::TextureView,
468 width: u32,
469 height: u32,
470 clear_color: Color,
471 ) -> Option<Timings> {
472 composition.compact_geom();
473 composition
474 .shared_state
475 .borrow_mut()
476 .props_interner
477 .compact();
478
479 let layers = &composition.layers;
480 let shared_state = &mut *composition.shared_state.borrow_mut();
481 let segment_buffer = &mut shared_state.segment_buffer;
482 let geom_id_to_order = &shared_state.geom_id_to_order;
483 let segment_buffer_view = segment_buffer
484 .take()
485 .expect("segment_buffer should not be None")
486 .fill_gpu_view(|id| {
487 geom_id_to_order
488 .get(&id)
489 .copied()
490 .flatten()
491 .and_then(|order| layers.get(&order))
492 .map(|layer| layer.inner.clone())
493 });
494
495 self.styling_map.populate(layers);
496
497 let timings = self
498 .render_inner(
499 device,
500 queue,
501 view,
502 width,
503 height,
504 &segment_buffer_view,
505 painter::Color {
506 r: clear_color.r,
507 g: clear_color.g,
508 b: clear_color.b,
509 a: clear_color.a,
510 },
511 )
512 .unwrap();
513
514 *segment_buffer = Some(segment_buffer_view.recycle());
515
516 timings
517 }
518}