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}