tessera_ui_basic_components/pipelines/image_vector/
pipeline.rs

1use std::{collections::HashMap, sync::Arc};
2
3use encase::{ShaderType, UniformBuffer};
4use glam::{Vec2, Vec4};
5use tessera_ui::{
6    Color, PxPosition, PxSize,
7    px::PxRect,
8    renderer::drawer::DrawablePipeline,
9    wgpu::{self, util::DeviceExt},
10};
11
12use super::command::{ImageVectorCommand, ImageVectorData, ImageVectorVertex};
13
14const DEFAULT_ATLAS_SIZE: u32 = 2048;
15const MIN_ATLAS_SIZE: u32 = 256;
16const ATLAS_PADDING: u32 = 1;
17const ATLAS_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
18
19struct GeometryResources {
20    vertex_buffer: wgpu::Buffer,
21    index_buffer: wgpu::Buffer,
22    index_count: u32,
23}
24
25#[derive(Hash, PartialEq, Eq, Clone)]
26struct AtlasKey {
27    data: ImageVectorData,
28    width: u32,
29    height: u32,
30}
31
32impl AtlasKey {
33    fn new(data: &Arc<ImageVectorData>, width: u32, height: u32) -> Self {
34        Self {
35            data: (**data).clone(),
36            width,
37            height,
38        }
39    }
40}
41
42struct AtlasCacheEntry {
43    uv_origin: [f32; 2],
44    uv_scale: [f32; 2],
45    uniform_buffer: wgpu::Buffer,
46    bind_group: wgpu::BindGroup,
47}
48
49#[derive(ShaderType, Clone, Copy)]
50struct ImageVectorUniforms {
51    origin: Vec2,
52    scale: Vec2,
53    tint: Vec4,
54}
55
56#[derive(ShaderType, Clone, Copy)]
57struct AtlasSampleUniforms {
58    origin: Vec2,
59    scale: Vec2,
60    uv_origin: Vec2,
61    uv_scale: Vec2,
62    tint: Vec4,
63}
64
65pub struct ImageVectorPipeline {
66    raster_pipeline: wgpu::RenderPipeline,
67    raster_bind_group: wgpu::BindGroup,
68    sample_pipeline: wgpu::RenderPipeline,
69    sample_bind_group_layout: wgpu::BindGroupLayout,
70    atlas_sampler: wgpu::Sampler,
71    atlas: VectorAtlas,
72    resources: HashMap<ImageVectorData, GeometryResources>,
73    cache: HashMap<AtlasKey, AtlasCacheEntry>,
74    raster_sample_count: u32,
75}
76
77impl ImageVectorPipeline {
78    pub fn new(
79        device: &wgpu::Device,
80        config: &wgpu::SurfaceConfiguration,
81        sample_count: u32,
82    ) -> Self {
83        let raster_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
84            label: Some("Image Vector Shader"),
85            source: wgpu::ShaderSource::Wgsl(include_str!("image_vector.wgsl").into()),
86        });
87        let sample_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
88            label: Some("Image Vector Atlas Sample Shader"),
89            source: wgpu::ShaderSource::Wgsl(include_str!("atlas_sample.wgsl").into()),
90        });
91
92        let raster_bind_group_layout =
93            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
94                entries: &[wgpu::BindGroupLayoutEntry {
95                    binding: 0,
96                    visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
97                    ty: wgpu::BindingType::Buffer {
98                        ty: wgpu::BufferBindingType::Uniform,
99                        has_dynamic_offset: false,
100                        min_binding_size: Some(ImageVectorUniforms::min_size()),
101                    },
102                    count: None,
103                }],
104                label: Some("image_vector_raster_bind_group_layout"),
105            });
106
107        let sample_bind_group_layout =
108            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
109                entries: &[
110                    wgpu::BindGroupLayoutEntry {
111                        binding: 0,
112                        visibility: wgpu::ShaderStages::FRAGMENT,
113                        ty: wgpu::BindingType::Texture {
114                            multisampled: false,
115                            view_dimension: wgpu::TextureViewDimension::D2,
116                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
117                        },
118                        count: None,
119                    },
120                    wgpu::BindGroupLayoutEntry {
121                        binding: 1,
122                        visibility: wgpu::ShaderStages::FRAGMENT,
123                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
124                        count: None,
125                    },
126                    wgpu::BindGroupLayoutEntry {
127                        binding: 2,
128                        visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
129                        ty: wgpu::BindingType::Buffer {
130                            ty: wgpu::BufferBindingType::Uniform,
131                            has_dynamic_offset: false,
132                            min_binding_size: Some(AtlasSampleUniforms::min_size()),
133                        },
134                        count: None,
135                    },
136                ],
137                label: Some("image_vector_sample_bind_group_layout"),
138            });
139
140        let raster_uniforms = raster_uniforms();
141        let mut uniform_data = UniformBuffer::new(Vec::new());
142        uniform_data
143            .write(&raster_uniforms)
144            .expect("raster uniform serialization failed");
145        let raster_uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
146            label: Some("image_vector_raster_uniform_buffer"),
147            contents: &uniform_data.into_inner(),
148            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
149        });
150
151        let raster_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
152            layout: &raster_bind_group_layout,
153            entries: &[wgpu::BindGroupEntry {
154                binding: 0,
155                resource: raster_uniform_buffer.as_entire_binding(),
156            }],
157            label: Some("image_vector_raster_bind_group"),
158        });
159
160        let vertex_layout = wgpu::VertexBufferLayout {
161            array_stride: std::mem::size_of::<ImageVectorVertex>() as wgpu::BufferAddress,
162            step_mode: wgpu::VertexStepMode::Vertex,
163            attributes: &[
164                wgpu::VertexAttribute {
165                    offset: 0,
166                    shader_location: 0,
167                    format: wgpu::VertexFormat::Float32x2,
168                },
169                wgpu::VertexAttribute {
170                    offset: 8,
171                    shader_location: 1,
172                    format: wgpu::VertexFormat::Float32x4,
173                },
174            ],
175        };
176        let vertex_layouts = [vertex_layout];
177
178        let raster_sample_count = desired_raster_sample_count(sample_count);
179
180        let raster_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
181            label: Some("Image Vector Raster Pipeline"),
182            layout: Some(
183                &device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
184                    label: Some("image_vector_raster_pipeline_layout"),
185                    bind_group_layouts: &[&raster_bind_group_layout],
186                    push_constant_ranges: &[],
187                }),
188            ),
189            vertex: wgpu::VertexState {
190                module: &raster_shader,
191                entry_point: Some("vs_main"),
192                buffers: &vertex_layouts,
193                compilation_options: Default::default(),
194            },
195            fragment: Some(wgpu::FragmentState {
196                module: &raster_shader,
197                entry_point: Some("fs_main"),
198                targets: &[Some(wgpu::ColorTargetState {
199                    format: ATLAS_FORMAT,
200                    blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
201                    write_mask: wgpu::ColorWrites::ALL,
202                })],
203                compilation_options: Default::default(),
204            }),
205            primitive: wgpu::PrimitiveState::default(),
206            depth_stencil: None,
207            multisample: wgpu::MultisampleState {
208                count: raster_sample_count,
209                mask: !0,
210                alpha_to_coverage_enabled: false,
211            },
212            multiview: None,
213            cache: None,
214        });
215
216        let sample_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
217            label: Some("Image Vector Atlas Sample Pipeline"),
218            layout: Some(
219                &device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
220                    label: Some("image_vector_sample_pipeline_layout"),
221                    bind_group_layouts: &[&sample_bind_group_layout],
222                    push_constant_ranges: &[],
223                }),
224            ),
225            vertex: wgpu::VertexState {
226                module: &sample_shader,
227                entry_point: Some("vs_main"),
228                buffers: &[],
229                compilation_options: Default::default(),
230            },
231            fragment: Some(wgpu::FragmentState {
232                module: &sample_shader,
233                entry_point: Some("fs_main"),
234                targets: &[Some(wgpu::ColorTargetState {
235                    format: config.format,
236                    blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
237                    write_mask: wgpu::ColorWrites::ALL,
238                })],
239                compilation_options: Default::default(),
240            }),
241            primitive: wgpu::PrimitiveState::default(),
242            depth_stencil: None,
243            multisample: wgpu::MultisampleState {
244                count: sample_count,
245                mask: !0,
246                alpha_to_coverage_enabled: false,
247            },
248            multiview: None,
249            cache: None,
250        });
251
252        let atlas_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
253            label: Some("image_vector_atlas_sampler"),
254            address_mode_u: wgpu::AddressMode::ClampToEdge,
255            address_mode_v: wgpu::AddressMode::ClampToEdge,
256            address_mode_w: wgpu::AddressMode::ClampToEdge,
257            mag_filter: wgpu::FilterMode::Linear,
258            min_filter: wgpu::FilterMode::Linear,
259            mipmap_filter: wgpu::FilterMode::Linear,
260            ..Default::default()
261        });
262
263        Self {
264            raster_pipeline,
265            raster_bind_group,
266            sample_pipeline,
267            sample_bind_group_layout,
268            atlas_sampler,
269            atlas: VectorAtlas::new(device),
270            resources: HashMap::new(),
271            cache: HashMap::new(),
272            raster_sample_count,
273        }
274    }
275
276    fn create_resources(device: &wgpu::Device, data: &Arc<ImageVectorData>) -> GeometryResources {
277        let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
278            label: Some("image_vector_vertex_buffer"),
279            contents: bytemuck::cast_slice(data.vertices.as_slice()),
280            usage: wgpu::BufferUsages::VERTEX,
281        });
282
283        let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
284            label: Some("image_vector_index_buffer"),
285            contents: bytemuck::cast_slice(data.indices.as_slice()),
286            usage: wgpu::BufferUsages::INDEX,
287        });
288
289        GeometryResources {
290            vertex_buffer,
291            index_buffer,
292            index_count: data.indices.len() as u32,
293        }
294    }
295
296    fn ensure_cached_entry(
297        &mut self,
298        device: &wgpu::Device,
299        queue: &wgpu::Queue,
300        data: &Arc<ImageVectorData>,
301        width: u32,
302        height: u32,
303    ) {
304        let key = AtlasKey::new(data, width, height);
305        if self.cache.contains_key(&key) {
306            return;
307        }
308
309        let geometry_key = (**data).clone();
310        self.resources
311            .entry(geometry_key.clone())
312            .or_insert_with(|| Self::create_resources(device, data));
313
314        let allocation = self.atlas.allocate(device, queue, width, height);
315        let geometry = self
316            .resources
317            .get(&geometry_key)
318            .expect("geometry must exist");
319        self.rasterize_into_allocation(device, queue, geometry, &allocation, width, height);
320
321        let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
322            label: Some("image_vector_sample_uniform_buffer"),
323            size: AtlasSampleUniforms::min_size().get(),
324            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
325            mapped_at_creation: false,
326        });
327
328        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
329            layout: &self.sample_bind_group_layout,
330            entries: &[
331                wgpu::BindGroupEntry {
332                    binding: 0,
333                    resource: wgpu::BindingResource::TextureView(
334                        self.atlas.page_view(allocation.page_index),
335                    ),
336                },
337                wgpu::BindGroupEntry {
338                    binding: 1,
339                    resource: wgpu::BindingResource::Sampler(&self.atlas_sampler),
340                },
341                wgpu::BindGroupEntry {
342                    binding: 2,
343                    resource: uniform_buffer.as_entire_binding(),
344                },
345            ],
346            label: Some("image_vector_sample_bind_group"),
347        });
348
349        let uv_origin = [
350            allocation.inner_rect.x as f32 / allocation.page_size.0 as f32,
351            allocation.inner_rect.y as f32 / allocation.page_size.1 as f32,
352        ];
353        let uv_scale = [
354            allocation.inner_rect.width as f32 / allocation.page_size.0 as f32,
355            allocation.inner_rect.height as f32 / allocation.page_size.1 as f32,
356        ];
357
358        self.cache.insert(
359            key,
360            AtlasCacheEntry {
361                uv_origin,
362                uv_scale,
363                uniform_buffer,
364                bind_group,
365            },
366        );
367    }
368
369    fn rasterize_into_allocation(
370        &self,
371        device: &wgpu::Device,
372        queue: &wgpu::Queue,
373        geometry: &GeometryResources,
374        allocation: &AtlasAllocation,
375        width: u32,
376        height: u32,
377    ) {
378        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
379            label: Some("image_vector_atlas_encoder"),
380        });
381
382        let resolved_texture = device.create_texture(&wgpu::TextureDescriptor {
383            label: Some("image_vector_resolved_texture"),
384            size: wgpu::Extent3d {
385                width,
386                height,
387                depth_or_array_layers: 1,
388            },
389            mip_level_count: 1,
390            sample_count: 1,
391            dimension: wgpu::TextureDimension::D2,
392            format: ATLAS_FORMAT,
393            usage: wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::RENDER_ATTACHMENT,
394            view_formats: &[],
395        });
396        let resolved_view = resolved_texture.create_view(&wgpu::TextureViewDescriptor::default());
397
398        let (color_view, resolve_target, _msaa_texture) = if self.raster_sample_count > 1 {
399            let msaa_texture = device.create_texture(&wgpu::TextureDescriptor {
400                label: Some("image_vector_msaa_texture"),
401                size: wgpu::Extent3d {
402                    width,
403                    height,
404                    depth_or_array_layers: 1,
405                },
406                mip_level_count: 1,
407                sample_count: self.raster_sample_count,
408                dimension: wgpu::TextureDimension::D2,
409                format: ATLAS_FORMAT,
410                usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
411                view_formats: &[],
412            });
413            let msaa_view = msaa_texture.create_view(&wgpu::TextureViewDescriptor::default());
414            (msaa_view, Some(resolved_view.clone()), Some(msaa_texture))
415        } else {
416            (resolved_view.clone(), None, None)
417        };
418
419        {
420            let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
421                label: Some("image_vector_raster_pass"),
422                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
423                    view: &color_view,
424                    depth_slice: None,
425                    resolve_target: resolve_target.as_ref(),
426                    ops: wgpu::Operations {
427                        load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
428                        store: wgpu::StoreOp::Store,
429                    },
430                })],
431                depth_stencil_attachment: None,
432                occlusion_query_set: None,
433                timestamp_writes: None,
434            });
435            pass.set_pipeline(&self.raster_pipeline);
436            pass.set_bind_group(0, &self.raster_bind_group, &[]);
437            pass.set_vertex_buffer(0, geometry.vertex_buffer.slice(..));
438            pass.set_index_buffer(geometry.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
439            pass.draw_indexed(0..geometry.index_count, 0, 0..1);
440        }
441
442        encoder.copy_texture_to_texture(
443            wgpu::TexelCopyTextureInfo {
444                texture: &resolved_texture,
445                mip_level: 0,
446                origin: wgpu::Origin3d::ZERO,
447                aspect: wgpu::TextureAspect::All,
448            },
449            wgpu::TexelCopyTextureInfo {
450                texture: self.atlas.page_texture(allocation.page_index),
451                mip_level: 0,
452                origin: wgpu::Origin3d {
453                    x: allocation.inner_rect.x,
454                    y: allocation.inner_rect.y,
455                    z: 0,
456                },
457                aspect: wgpu::TextureAspect::All,
458            },
459            wgpu::Extent3d {
460                width: allocation.inner_rect.width,
461                height: allocation.inner_rect.height,
462                depth_or_array_layers: 1,
463            },
464        );
465
466        queue.submit(std::iter::once(encoder.finish()));
467    }
468}
469
470impl DrawablePipeline<ImageVectorCommand> for ImageVectorPipeline {
471    fn draw(
472        &mut self,
473        device: &wgpu::Device,
474        queue: &wgpu::Queue,
475        config: &wgpu::SurfaceConfiguration,
476        render_pass: &mut wgpu::RenderPass<'_>,
477        commands: &[(&ImageVectorCommand, PxSize, PxPosition)],
478        _scene_texture_view: &wgpu::TextureView,
479        _clip_rect: Option<PxRect>,
480    ) {
481        if commands.is_empty() {
482            return;
483        }
484
485        for (command, size, _) in commands {
486            if let Some((width, height)) = physical_dimensions(*size) {
487                self.ensure_cached_entry(device, queue, &command.data, width, height);
488            }
489        }
490
491        render_pass.set_pipeline(&self.sample_pipeline);
492
493        for (command, size, start_pos) in commands {
494            let Some((width, height)) = physical_dimensions(*size) else {
495                continue;
496            };
497            let key = AtlasKey::new(&command.data, width, height);
498            let entry = match self.cache.get_mut(&key) {
499                Some(entry) => entry,
500                None => continue,
501            };
502
503            let uniforms = compute_sample_uniforms(
504                *start_pos,
505                *size,
506                command.tint,
507                entry.uv_origin,
508                entry.uv_scale,
509                config,
510            );
511            let mut buffer = UniformBuffer::new(Vec::new());
512            buffer
513                .write(&uniforms)
514                .expect("sample uniform serialization failed");
515            queue.write_buffer(&entry.uniform_buffer, 0, &buffer.into_inner());
516
517            render_pass.set_bind_group(0, &entry.bind_group, &[]);
518            render_pass.draw(0..6, 0..1);
519        }
520    }
521}
522
523fn desired_raster_sample_count(global_sample_count: u32) -> u32 {
524    if global_sample_count > 1 {
525        global_sample_count
526    } else {
527        4
528    }
529}
530
531fn raster_uniforms() -> ImageVectorUniforms {
532    ImageVectorUniforms {
533        origin: Vec2::new(-1.0, 1.0),
534        scale: Vec2::new(2.0, -2.0),
535        tint: Vec4::new(1.0, 1.0, 1.0, 1.0),
536    }
537}
538
539fn compute_sample_uniforms(
540    start_pos: PxPosition,
541    size: PxSize,
542    tint: Color,
543    uv_origin: [f32; 2],
544    uv_scale: [f32; 2],
545    config: &wgpu::SurfaceConfiguration,
546) -> AtlasSampleUniforms {
547    let left = (start_pos.x.0 as f32 / config.width as f32) * 2.0 - 1.0;
548    let right = ((start_pos.x.0 + size.width.0) as f32 / config.width as f32) * 2.0 - 1.0;
549    let top = 1.0 - (start_pos.y.0 as f32 / config.height as f32) * 2.0;
550    let bottom = 1.0 - ((start_pos.y.0 + size.height.0) as f32 / config.height as f32) * 2.0;
551
552    AtlasSampleUniforms {
553        origin: Vec2::new(left, top),
554        scale: Vec2::new(right - left, bottom - top),
555        uv_origin: Vec2::from_array(uv_origin),
556        uv_scale: Vec2::from_array(uv_scale),
557        tint: Vec4::new(tint.r, tint.g, tint.b, tint.a),
558    }
559}
560
561fn physical_dimensions(size: PxSize) -> Option<(u32, u32)> {
562    if size.width.0 <= 0 || size.height.0 <= 0 {
563        None
564    } else {
565        Some((size.width.0 as u32, size.height.0 as u32))
566    }
567}
568
569struct AtlasAllocation {
570    page_index: usize,
571    inner_rect: AtlasRect,
572    page_size: (u32, u32),
573}
574
575#[derive(Clone, Copy)]
576struct AtlasRect {
577    x: u32,
578    y: u32,
579    width: u32,
580    height: u32,
581}
582
583struct VectorAtlas {
584    pages: Vec<AtlasPage>,
585    default_size: u32,
586    max_dimension: u32,
587}
588
589impl VectorAtlas {
590    fn new(device: &wgpu::Device) -> Self {
591        let max_dimension = device.limits().max_texture_dimension_2d;
592        let default_size = DEFAULT_ATLAS_SIZE.min(max_dimension);
593        Self {
594            pages: Vec::new(),
595            default_size: default_size.max(MIN_ATLAS_SIZE),
596            max_dimension,
597        }
598    }
599
600    fn allocate(
601        &mut self,
602        device: &wgpu::Device,
603        queue: &wgpu::Queue,
604        width: u32,
605        height: u32,
606    ) -> AtlasAllocation {
607        let padded_width = width.saturating_add(ATLAS_PADDING * 2).max(1);
608        let padded_height = height.saturating_add(ATLAS_PADDING * 2).max(1);
609
610        if padded_width > self.max_dimension || padded_height > self.max_dimension {
611            panic!(
612                "Image vector target {}x{} exceeds GPU atlas limit {}",
613                width, height, self.max_dimension
614            );
615        }
616
617        for (index, page) in self.pages.iter_mut().enumerate() {
618            if let Some(rect) = page.allocate(padded_width, padded_height) {
619                return AtlasAllocation {
620                    page_index: index,
621                    inner_rect: AtlasRect {
622                        x: rect.x + ATLAS_PADDING,
623                        y: rect.y + ATLAS_PADDING,
624                        width,
625                        height,
626                    },
627                    page_size: (page.width, page.height),
628                };
629            }
630        }
631
632        let needed = padded_width.max(padded_height);
633        let mut page_size = self.default_size.max(needed).max(MIN_ATLAS_SIZE);
634        page_size = page_size.min(self.max_dimension);
635        let page_index = self.add_page(device, queue, page_size);
636        let page = self
637            .pages
638            .get_mut(page_index)
639            .expect("new page should exist");
640        let rect = page
641            .allocate(padded_width, padded_height)
642            .expect("allocation should fit in a new page");
643        AtlasAllocation {
644            page_index,
645            inner_rect: AtlasRect {
646                x: rect.x + ATLAS_PADDING,
647                y: rect.y + ATLAS_PADDING,
648                width,
649                height,
650            },
651            page_size: (page.width, page.height),
652        }
653    }
654
655    fn add_page(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, size: u32) -> usize {
656        let page = AtlasPage::new(device, queue, size, size);
657        self.pages.push(page);
658        self.pages.len() - 1
659    }
660
661    fn page_view(&self, index: usize) -> &wgpu::TextureView {
662        &self.pages[index].view
663    }
664
665    fn page_texture(&self, index: usize) -> &wgpu::Texture {
666        &self.pages[index].texture
667    }
668}
669
670struct ShelfRow {
671    y: u32,
672    height: u32,
673    cursor_x: u32,
674}
675
676struct AtlasPage {
677    texture: wgpu::Texture,
678    view: wgpu::TextureView,
679    width: u32,
680    height: u32,
681    rows: Vec<ShelfRow>,
682    next_y: u32,
683}
684
685impl AtlasPage {
686    fn new(device: &wgpu::Device, queue: &wgpu::Queue, width: u32, height: u32) -> Self {
687        let texture = device.create_texture(&wgpu::TextureDescriptor {
688            label: Some("image_vector_atlas_page"),
689            size: wgpu::Extent3d {
690                width,
691                height,
692                depth_or_array_layers: 1,
693            },
694            mip_level_count: 1,
695            sample_count: 1,
696            dimension: wgpu::TextureDimension::D2,
697            format: ATLAS_FORMAT,
698            usage: wgpu::TextureUsages::TEXTURE_BINDING
699                | wgpu::TextureUsages::COPY_DST
700                | wgpu::TextureUsages::COPY_SRC,
701            view_formats: &[],
702        });
703
704        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
705            label: Some("image_vector_atlas_clear_encoder"),
706        });
707        encoder.clear_texture(
708            &texture,
709            &wgpu::ImageSubresourceRange {
710                aspect: wgpu::TextureAspect::All,
711                base_mip_level: 0,
712                mip_level_count: Some(1),
713                base_array_layer: 0,
714                array_layer_count: Some(1),
715            },
716        );
717        queue.submit(std::iter::once(encoder.finish()));
718
719        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
720        Self {
721            texture,
722            view,
723            width,
724            height,
725            rows: Vec::new(),
726            next_y: 0,
727        }
728    }
729
730    fn allocate(&mut self, width: u32, height: u32) -> Option<AtlasRect> {
731        for row in &mut self.rows {
732            if height <= row.height && row.cursor_x + width <= self.width {
733                let rect = AtlasRect {
734                    x: row.cursor_x,
735                    y: row.y,
736                    width,
737                    height,
738                };
739                row.cursor_x += width;
740                return Some(rect);
741            }
742        }
743
744        if self.next_y + height > self.height {
745            return None;
746        }
747
748        let rect = AtlasRect {
749            x: 0,
750            y: self.next_y,
751            width,
752            height,
753        };
754        self.rows.push(ShelfRow {
755            y: self.next_y,
756            height,
757            cursor_x: width,
758        });
759        self.next_y += height;
760        Some(rect)
761    }
762}