forma_render/gpu/renderer/
mod.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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        // Number of segments to be generated, out of the prefix sum.
262        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)) = &timestamp_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}