1use super::*;
2
3#[derive(Default)]
7pub(crate) struct SpriteResources {
8 pub(crate) pipeline: Option<DualPipeline>,
10 pub(crate) pipeline_depth_write: Option<DualPipeline>,
11 pub(crate) pipeline_additive: Option<DualPipeline>,
12 pub(crate) pipeline_additive_depth_write: Option<DualPipeline>,
13 pub(crate) pipeline_premultiplied: Option<DualPipeline>,
14 pub(crate) pipeline_premultiplied_depth_write: Option<DualPipeline>,
15 pub(crate) refraction_pipeline: Option<wgpu::RenderPipeline>,
17 pub(crate) refraction_bgl: Option<wgpu::BindGroupLayout>,
19 pub(crate) refraction_sampler: Option<wgpu::Sampler>,
21 pub(crate) bgl: Option<wgpu::BindGroupLayout>,
23 pub(crate) soft_bgl: Option<wgpu::BindGroupLayout>,
25 pub(crate) soft_fallback_bg: Option<wgpu::BindGroup>,
27 pub(crate) soft_sampler: Option<wgpu::Sampler>,
29 pub(crate) soft_fallback_tex: Option<wgpu::Texture>,
31 pub(crate) lit_pipeline: Option<DualPipeline>,
33 pub(crate) lit_pipeline_depth_write: Option<DualPipeline>,
34 pub(crate) lit_pipeline_additive: Option<DualPipeline>,
35 pub(crate) lit_pipeline_additive_depth_write: Option<DualPipeline>,
36 pub(crate) lit_pipeline_premultiplied: Option<DualPipeline>,
37 pub(crate) lit_pipeline_premultiplied_depth_write: Option<DualPipeline>,
38 pub(crate) lit_bgl: Option<wgpu::BindGroupLayout>,
40 pub(crate) lit_fallback_bg: Option<wgpu::BindGroup>,
42 pub(crate) lit_fallback_tex: Option<wgpu::Texture>,
44 pub(crate) outline_mask_pipeline: Option<wgpu::RenderPipeline>,
46}
47
48impl DeviceResources {
49 pub(crate) fn ensure_sprite_pipelines(&mut self, device: &wgpu::Device) {
58 if self.sprite.bgl.is_some() {
59 return;
60 }
61
62 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
63 label: Some("sprite_bgl"),
64 entries: &[
65 wgpu::BindGroupLayoutEntry {
67 binding: 0,
68 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
69 ty: wgpu::BindingType::Buffer {
70 ty: wgpu::BufferBindingType::Uniform,
71 has_dynamic_offset: false,
72 min_binding_size: None,
73 },
74 count: None,
75 },
76 wgpu::BindGroupLayoutEntry {
78 binding: 1,
79 visibility: wgpu::ShaderStages::FRAGMENT,
80 ty: wgpu::BindingType::Texture {
81 sample_type: wgpu::TextureSampleType::Float { filterable: true },
82 view_dimension: wgpu::TextureViewDimension::D2,
83 multisampled: false,
84 },
85 count: None,
86 },
87 wgpu::BindGroupLayoutEntry {
89 binding: 2,
90 visibility: wgpu::ShaderStages::FRAGMENT,
91 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
92 count: None,
93 },
94 wgpu::BindGroupLayoutEntry {
96 binding: 3,
97 visibility: wgpu::ShaderStages::VERTEX,
98 ty: wgpu::BindingType::Buffer {
99 ty: wgpu::BufferBindingType::Storage { read_only: true },
100 has_dynamic_offset: false,
101 min_binding_size: None,
102 },
103 count: None,
104 },
105 ],
106 });
107
108 let soft_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
112 label: Some("sprite_soft_bgl"),
113 entries: &[
114 wgpu::BindGroupLayoutEntry {
115 binding: 0,
116 visibility: wgpu::ShaderStages::FRAGMENT,
117 ty: wgpu::BindingType::Texture {
118 sample_type: wgpu::TextureSampleType::Depth,
119 view_dimension: wgpu::TextureViewDimension::D2,
120 multisampled: false,
121 },
122 count: None,
123 },
124 wgpu::BindGroupLayoutEntry {
125 binding: 1,
126 visibility: wgpu::ShaderStages::FRAGMENT,
127 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
128 count: None,
129 },
130 ],
131 });
132
133 let soft_sampler =
134 crate::resources::builders::clamp_nearest_sampler(device, "sprite_soft_sampler");
135
136 let fallback_tex = device.create_texture(&wgpu::TextureDescriptor {
137 label: Some("sprite_soft_fallback_tex"),
138 size: wgpu::Extent3d {
139 width: 1,
140 height: 1,
141 depth_or_array_layers: 1,
142 },
143 mip_level_count: 1,
144 sample_count: 1,
145 dimension: wgpu::TextureDimension::D2,
146 format: wgpu::TextureFormat::Depth32Float,
147 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
148 view_formats: &[],
149 });
150 let fallback_view = fallback_tex.create_view(&wgpu::TextureViewDescriptor {
151 aspect: wgpu::TextureAspect::DepthOnly,
152 ..Default::default()
153 });
154 let fallback_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
155 label: Some("sprite_soft_fallback_bg"),
156 layout: &soft_bgl,
157 entries: &[
158 wgpu::BindGroupEntry {
159 binding: 0,
160 resource: wgpu::BindingResource::TextureView(&fallback_view),
161 },
162 wgpu::BindGroupEntry {
163 binding: 1,
164 resource: wgpu::BindingResource::Sampler(&soft_sampler),
165 },
166 ],
167 });
168
169 let shader = crate::resources::builders::wgsl_module(
170 device,
171 "sprite_shader",
172 crate::resources::builders::wgsl_source!("sprite"),
173 );
174
175 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
176 label: Some("sprite_pipeline_layout"),
177 bind_group_layouts: &[&self.camera_bind_group_layout, &bgl, &soft_bgl],
178 push_constant_ranges: &[],
179 });
180
181 let vert_attrs = [wgpu::VertexAttribute {
184 offset: 0,
185 shader_location: 0,
186 format: wgpu::VertexFormat::Float32x3,
187 }];
188 let vertex_buffers = [wgpu::VertexBufferLayout {
189 array_stride: 12,
190 step_mode: wgpu::VertexStepMode::Instance,
191 attributes: &vert_attrs,
192 }];
193
194 let sample_count = self.sample_count;
195 let ldr_format = self.target_format;
196 let make_sprite = |depth_write: bool, blend: wgpu::BlendState, label: &str| {
199 crate::resources::builders::build_dual_pipeline(
200 device,
201 &crate::resources::builders::DualPipelineDesc {
202 label,
203 layout: &layout,
204 shader: &shader,
205 vertex_entry: "vs_main",
206 fragment_entry: "fs_main",
207 vertex_buffers: &vertex_buffers,
208 blend: Some(blend),
209 topology: wgpu::PrimitiveTopology::TriangleList,
210 cull_mode: None,
211 depth_write,
212 depth_compare: wgpu::CompareFunction::Less,
213 sample_count,
214 ldr_format,
215 },
216 )
217 };
218
219 let lit_bgl = crate::resources::builders::texture_sampler_bgl(
223 device,
224 "sprite_lit_bgl",
225 wgpu::ShaderStages::FRAGMENT,
226 );
227
228 let alpha = wgpu::BlendState::ALPHA_BLENDING;
229 let additive = crate::resources::builders::ADDITIVE_BLEND;
230 let premultiplied = crate::resources::builders::PREMULTIPLIED_BLEND;
231 self.sprite.bgl = Some(bgl);
232 self.sprite.soft_bgl = Some(soft_bgl);
233 self.sprite.soft_sampler = Some(soft_sampler);
234 self.sprite.soft_fallback_tex = Some(fallback_tex);
235 self.sprite.soft_fallback_bg = Some(fallback_bg);
236 self.sprite.pipeline = Some(make_sprite(false, alpha, "sprite_pipeline"));
237 self.sprite.pipeline_depth_write =
238 Some(make_sprite(true, alpha, "sprite_pipeline_depth_write"));
239 self.sprite.pipeline_additive =
240 Some(make_sprite(false, additive, "sprite_pipeline_additive"));
241 self.sprite.pipeline_additive_depth_write = Some(make_sprite(
242 true,
243 additive,
244 "sprite_pipeline_additive_depth_write",
245 ));
246 self.sprite.pipeline_premultiplied = Some(make_sprite(
247 false,
248 premultiplied,
249 "sprite_pipeline_premultiplied",
250 ));
251 self.sprite.pipeline_premultiplied_depth_write = Some(make_sprite(
252 true,
253 premultiplied,
254 "sprite_pipeline_premultiplied_depth_write",
255 ));
256
257 let refraction_bgl = crate::resources::builders::texture_sampler_bgl(
268 device,
269 "sprite_refraction_bgl",
270 wgpu::ShaderStages::FRAGMENT,
271 );
272
273 let refraction_sampler =
274 crate::resources::builders::clamp_linear_sampler(device, "sprite_refraction_sampler");
275
276 let refraction_shader = crate::resources::builders::wgsl_module(
277 device,
278 "sprite_refraction_shader",
279 crate::resources::builders::wgsl_source!("sprite_refraction"),
280 );
281
282 let bgl_ref = self.sprite.bgl.as_ref().unwrap();
283 let refraction_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
284 label: Some("sprite_refraction_pipeline_layout"),
285 bind_group_layouts: &[&self.camera_bind_group_layout, bgl_ref, &refraction_bgl],
286 push_constant_ranges: &[],
287 });
288
289 let refraction_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
290 label: Some("sprite_refraction_pipeline"),
291 layout: Some(&refraction_layout),
292 vertex: wgpu::VertexState {
293 module: &refraction_shader,
294 entry_point: Some("vs_main"),
295 buffers: &vertex_buffers,
296 compilation_options: wgpu::PipelineCompilationOptions::default(),
297 },
298 fragment: Some(wgpu::FragmentState {
299 module: &refraction_shader,
300 entry_point: Some("fs_main"),
301 targets: &[Some(wgpu::ColorTargetState {
302 format: wgpu::TextureFormat::Rgba16Float,
303 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
304 write_mask: wgpu::ColorWrites::ALL,
305 })],
306 compilation_options: wgpu::PipelineCompilationOptions::default(),
307 }),
308 primitive: wgpu::PrimitiveState {
309 topology: wgpu::PrimitiveTopology::TriangleList,
310 cull_mode: None,
311 ..Default::default()
312 },
313 depth_stencil: Some(wgpu::DepthStencilState {
314 format: wgpu::TextureFormat::Depth24PlusStencil8,
315 depth_write_enabled: false,
316 depth_compare: wgpu::CompareFunction::Less,
317 stencil: wgpu::StencilState::default(),
318 bias: wgpu::DepthBiasState::default(),
319 }),
320 multisample: wgpu::MultisampleState {
321 count: sample_count,
322 ..Default::default()
323 },
324 multiview: None,
325 cache: None,
326 });
327
328 self.sprite.refraction_bgl = Some(refraction_bgl);
329 self.sprite.refraction_sampler = Some(refraction_sampler);
330 self.sprite.refraction_pipeline = Some(refraction_pipeline);
331
332 let lit_shader = crate::resources::builders::wgsl_module(
344 device,
345 "sprite_lit_shader",
346 crate::resources::builders::wgsl_source!("sprite_lit"),
347 );
348
349 let sprite_bgl_ref = self.sprite.bgl.as_ref().unwrap();
350 let soft_bgl_ref = self.sprite.soft_bgl.as_ref().unwrap();
351 let lit_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
352 label: Some("sprite_lit_pipeline_layout"),
353 bind_group_layouts: &[
354 &self.camera_bind_group_layout,
355 sprite_bgl_ref,
356 soft_bgl_ref,
357 &lit_bgl,
358 ],
359 push_constant_ranges: &[],
360 });
361
362 let make_lit = |depth_write: bool, blend: wgpu::BlendState, label: &str| {
363 crate::resources::builders::build_dual_pipeline(
364 device,
365 &crate::resources::builders::DualPipelineDesc {
366 label,
367 layout: &lit_layout,
368 shader: &lit_shader,
369 vertex_entry: "vs_main",
370 fragment_entry: "fs_main",
371 vertex_buffers: &vertex_buffers,
372 blend: Some(blend),
373 topology: wgpu::PrimitiveTopology::TriangleList,
374 cull_mode: None,
375 depth_write,
376 depth_compare: wgpu::CompareFunction::Less,
377 sample_count,
378 ldr_format,
379 },
380 )
381 };
382
383 self.sprite.lit_pipeline = Some(make_lit(false, alpha, "sprite_lit_pipeline"));
384 self.sprite.lit_pipeline_depth_write =
385 Some(make_lit(true, alpha, "sprite_lit_pipeline_depth_write"));
386 self.sprite.lit_pipeline_additive =
387 Some(make_lit(false, additive, "sprite_lit_pipeline_additive"));
388 self.sprite.lit_pipeline_additive_depth_write = Some(make_lit(
389 true,
390 additive,
391 "sprite_lit_pipeline_additive_depth_write",
392 ));
393 self.sprite.lit_pipeline_premultiplied = Some(make_lit(
394 false,
395 premultiplied,
396 "sprite_lit_pipeline_premultiplied",
397 ));
398 self.sprite.lit_pipeline_premultiplied_depth_write = Some(make_lit(
399 true,
400 premultiplied,
401 "sprite_lit_pipeline_premultiplied_depth_write",
402 ));
403
404 let lit_fallback_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
407 label: Some("sprite_lit_fallback_bg"),
408 layout: &lit_bgl,
409 entries: &[
410 wgpu::BindGroupEntry {
411 binding: 0,
412 resource: wgpu::BindingResource::TextureView(&self.fallback_normal_map_view),
413 },
414 wgpu::BindGroupEntry {
415 binding: 1,
416 resource: wgpu::BindingResource::Sampler(&self.material_sampler),
417 },
418 ],
419 });
420
421 self.sprite.lit_bgl = Some(lit_bgl);
422 self.sprite.lit_fallback_bg = Some(lit_fallback_bg);
423 }
424
425 pub(crate) fn upload_sprite(
429 &mut self,
430 device: &wgpu::Device,
431 queue: &wgpu::Queue,
432 item: &crate::renderer::SpriteItem,
433 ) -> SpriteGpuData {
434 let count = item.positions.len() as u32;
435
436 let pos_bytes: Vec<u8> = item
438 .positions
439 .iter()
440 .flat_map(|p| bytemuck::bytes_of(p).iter().copied())
441 .collect();
442 let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
443 label: Some("sprite_vertex_buf"),
444 size: pos_bytes.len().max(12) as u64,
445 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
446 mapped_at_creation: false,
447 });
448 queue.write_buffer(&vertex_buffer, 0, &pos_bytes);
449
450 #[repr(C)]
453 #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
454 struct GpuSpriteInstance {
455 colour: [f32; 4],
456 size: f32,
457 rotation: f32,
458 soft_distance: f32,
459 _pad1: f32,
460 uv_rect: [f32; 4],
461 velocity: [f32; 3],
462 _pad2: f32,
463 }
464
465 let instances: Vec<GpuSpriteInstance> = (0..item.positions.len())
466 .map(|i| GpuSpriteInstance {
467 colour: if i < item.colours.len() {
468 item.colours[i]
469 } else {
470 item.default_colour
471 },
472 size: if i < item.sizes.len() {
473 item.sizes[i]
474 } else {
475 item.default_size
476 },
477 rotation: if i < item.rotations.len() {
478 item.rotations[i]
479 } else {
480 0.0
481 },
482 soft_distance: if i < item.soft_particle_distances.len() {
483 item.soft_particle_distances[i].max(0.0)
484 } else {
485 0.0
486 },
487 _pad1: 0.0,
488 uv_rect: if i < item.uv_rects.len() {
489 item.uv_rects[i]
490 } else {
491 [0.0, 0.0, 1.0, 1.0]
492 },
493 velocity: if i < item.velocities.len() {
494 item.velocities[i]
495 } else {
496 [0.0, 0.0, 0.0]
497 },
498 _pad2: 0.0,
499 })
500 .collect();
501
502 let instance_bytes = bytemuck::cast_slice(&instances);
503 let instance_buf = device.create_buffer(&wgpu::BufferDescriptor {
504 label: Some("sprite_instance_buf"),
505 size: instance_bytes.len().max(48) as u64,
506 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
507 mapped_at_creation: false,
508 });
509 queue.write_buffer(&instance_buf, 0, instance_bytes);
510
511 #[repr(C)]
516 #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
517 struct SpriteUniformData {
518 model: [[f32; 4]; 4],
519 world_space: u32,
520 has_texture: u32,
521 soft_particle_distance: f32,
522 orientation: u32,
523 axis: [f32; 3],
524 refraction_strength: f32,
525 lit: u32,
526 normal_mode: u32,
527 has_normal_map: u32,
528 ambient_scale: f32,
529 roughness: f32,
530 receive_shadows: u32,
531 _pad_lit_b: u32,
532 _pad_lit_c: u32,
533 }
534
535 let (texture_view, has_texture): (&wgpu::TextureView, u32) =
536 if let Some(id) = item.texture_id {
537 if let Some(tex) = self.content.textures.get(id) {
538 (&tex.view, 1)
539 } else {
540 (&self.content.fallback_lut_view, 0)
541 }
542 } else {
543 (&self.content.fallback_lut_view, 0)
544 };
545
546 let orientation = match item.orientation {
547 crate::renderer::SpriteOrientation::CameraFacing => 0u32,
548 crate::renderer::SpriteOrientation::VelocityStretched => 1u32,
549 crate::renderer::SpriteOrientation::AxisLocked => 2u32,
550 };
551
552 let normal_mode = match item.lit_params.normal_mode {
553 crate::renderer::SpriteNormalMode::Spherical => 0u32,
554 crate::renderer::SpriteNormalMode::Flat => 1u32,
555 crate::renderer::SpriteNormalMode::NormalMap => 2u32,
556 };
557
558 let (normal_view, has_normal_map): (&wgpu::TextureView, u32) =
559 if let Some(id) = item.normal_texture_id {
560 if let Some(tex) = self.content.textures.get(id) {
561 (&tex.view, 1)
562 } else {
563 (&self.fallback_normal_map_view, 0)
564 }
565 } else {
566 (&self.fallback_normal_map_view, 0)
567 };
568
569 let uniform_data = SpriteUniformData {
570 model: item.model,
571 world_space: if item.size_mode == crate::renderer::SpriteSizeMode::WorldSpace {
572 1
573 } else {
574 0
575 },
576 has_texture,
577 soft_particle_distance: item
578 .soft_particle_distance
579 .filter(|d| *d > 0.0)
580 .unwrap_or(0.0),
581 orientation,
582 axis: item.axis,
583 refraction_strength: item.refraction_strength.filter(|s| *s > 0.0).unwrap_or(0.0),
584 lit: item.lit as u32,
585 normal_mode,
586 has_normal_map,
587 ambient_scale: item.lit_params.ambient_scale,
588 roughness: item.lit_params.roughness,
589 receive_shadows: item.lit_params.receive_shadows as u32,
590 _pad_lit_b: 0,
591 _pad_lit_c: 0,
592 };
593 let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
594 label: Some("sprite_uniform_buf"),
595 size: std::mem::size_of::<SpriteUniformData>() as u64,
596 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
597 mapped_at_creation: false,
598 });
599 queue.write_buffer(&uniform_buf, 0, bytemuck::bytes_of(&uniform_data));
600
601 let bgl = self
602 .sprite
603 .bgl
604 .as_ref()
605 .expect("ensure_sprite_pipelines not called");
606
607 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
608 label: Some("sprite_bind_group"),
609 layout: bgl,
610 entries: &[
611 wgpu::BindGroupEntry {
612 binding: 0,
613 resource: uniform_buf.as_entire_binding(),
614 },
615 wgpu::BindGroupEntry {
616 binding: 1,
617 resource: wgpu::BindingResource::TextureView(texture_view),
618 },
619 wgpu::BindGroupEntry {
620 binding: 2,
621 resource: wgpu::BindingResource::Sampler(&self.material_sampler),
622 },
623 wgpu::BindGroupEntry {
624 binding: 3,
625 resource: instance_buf.as_entire_binding(),
626 },
627 ],
628 });
629
630 let lit_normal_bg = if item.lit {
631 self.sprite.lit_bgl.as_ref().map(|lit_bgl| {
632 device.create_bind_group(&wgpu::BindGroupDescriptor {
633 label: Some("sprite_lit_normal_bg"),
634 layout: lit_bgl,
635 entries: &[
636 wgpu::BindGroupEntry {
637 binding: 0,
638 resource: wgpu::BindingResource::TextureView(normal_view),
639 },
640 wgpu::BindGroupEntry {
641 binding: 1,
642 resource: wgpu::BindingResource::Sampler(&self.material_sampler),
643 },
644 ],
645 })
646 })
647 } else {
648 None
649 };
650
651 SpriteGpuData {
652 vertex_buffer,
653 sprite_count: count,
654 bind_group,
655 depth_write: item.depth_write,
656 blend: item.blend,
657 wireframe: false,
658 refraction_strength: item.refraction_strength.filter(|s| *s > 0.0).unwrap_or(0.0),
659 lit: item.lit,
660 lit_normal_bg,
661 _uniform_buf: uniform_buf,
662 _instance_buf: instance_buf,
663 }
664 }
665
666 pub(crate) fn ensure_sprite_outline_mask_pipeline(&mut self, device: &wgpu::Device) {
671 if self.sprite.outline_mask_pipeline.is_some() {
672 return;
673 }
674 let bgl = self
675 .sprite
676 .bgl
677 .as_ref()
678 .expect("ensure_sprite_pipelines must be called first");
679
680 let shader = crate::resources::builders::wgsl_module(
681 device,
682 "sprite_outline_mask_shader",
683 crate::resources::builders::wgsl_source!("sprite_outline_mask"),
684 );
685
686 let layout = crate::resources::builders::standard_scene_layout(
687 device,
688 "sprite_outline_mask_pipeline_layout",
689 &self.camera_bind_group_layout,
690 bgl,
691 );
692
693 let vert_attrs = [wgpu::VertexAttribute {
694 offset: 0,
695 shader_location: 0,
696 format: wgpu::VertexFormat::Float32x3,
697 }];
698 let vertex_buffers = [wgpu::VertexBufferLayout {
699 array_stride: 12,
700 step_mode: wgpu::VertexStepMode::Instance,
701 attributes: &vert_attrs,
702 }];
703
704 self.sprite.outline_mask_pipeline =
705 Some(crate::resources::builders::build_outline_mask_pipeline(
706 device,
707 "sprite_outline_mask_pipeline",
708 &layout,
709 &shader,
710 wgpu::TextureFormat::R8Unorm,
711 &vertex_buffers,
712 None,
713 false,
714 wgpu::CompareFunction::Less,
715 ));
716 }
717
718 pub fn upload_sprite_set(
725 &mut self,
726 device: &wgpu::Device,
727 queue: &wgpu::Queue,
728 item: &crate::renderer::SpriteItem,
729 ) -> crate::resources::SpriteSetId {
730 self.ensure_sprite_pipelines(device);
731 let gpu = self.upload_sprite(device, queue, item);
732 self.content.sprite_set_store.insert(gpu)
733 }
734
735 pub fn drop_sprite_set(&mut self, id: crate::resources::SpriteSetId) -> bool {
737 self.content.sprite_set_store.remove(id)
738 }
739
740 pub fn replace_sprite_set(
742 &mut self,
743 device: &wgpu::Device,
744 queue: &wgpu::Queue,
745 id: crate::resources::SpriteSetId,
746 item: &crate::renderer::SpriteItem,
747 ) -> bool {
748 if !self.content.sprite_set_store.contains(id) {
749 return false;
750 }
751 self.ensure_sprite_pipelines(device);
752 let gpu = self.upload_sprite(device, queue, item);
753 self.content.sprite_set_store.replace(id, gpu)
754 }
755
756 pub fn begin_upload_sprite_set(
758 &mut self,
759 device: &wgpu::Device,
760 queue: &wgpu::Queue,
761 item: crate::renderer::SpriteItem,
762 ) -> crate::resources::JobId {
763 let slot = crate::resources::ResultSlot::<crate::resources::SpriteSetId>::new();
764 let slot_for_apply = slot.clone();
765 let device_for_apply = device.clone();
766 let queue_for_apply = queue.clone();
767 let id = {
768 let mut runner = self.jobs.lock().expect("upload job runner poisoned");
769 runner.submit_cpu(move |progress| {
770 progress.set(0.9);
771 Ok(crate::resources::upload_jobs::JobProduct::with_apply(
772 Box::new(move |resources: &mut DeviceResources| {
773 let sid =
774 resources.upload_sprite_set(&device_for_apply, &queue_for_apply, &item);
775 slot_for_apply.set(sid);
776 }),
777 ))
778 })
779 };
780 self.job_results
781 .sprite_set
782 .lock()
783 .expect("sprite set result map poisoned")
784 .insert(id, slot);
785 id
786 }
787
788 pub fn upload_result_sprite_set(
791 &mut self,
792 id: crate::resources::JobId,
793 ) -> crate::error::ViewportResult<crate::resources::SpriteSetId> {
794 let mut map = self
795 .job_results
796 .sprite_set
797 .lock()
798 .expect("sprite set result map poisoned");
799 let slot = match map.get(&id) {
800 Some(s) => s.clone(),
801 None => {
802 return Err(crate::error::ViewportError::JobResultMissing {
803 reason: "unknown id or wrong upload type",
804 });
805 }
806 };
807 match slot.take() {
808 Some(sid) => {
809 map.remove(&id);
810 Ok(sid)
811 }
812 None => Err(crate::error::ViewportError::JobNotReady),
813 }
814 }
815
816 pub fn upload_sprite_instance_set(
828 &mut self,
829 device: &wgpu::Device,
830 queue: &wgpu::Queue,
831 item: &crate::renderer::SpriteItem,
832 ) -> crate::resources::SpriteInstanceSetId {
833 self.ensure_sprite_pipelines(device);
834 let gpu = self.upload_sprite(device, queue, item);
835 self.content.sprite_instance_set_store.insert(gpu)
836 }
837
838 pub fn drop_sprite_instance_set(&mut self, id: crate::resources::SpriteInstanceSetId) -> bool {
840 self.content.sprite_instance_set_store.remove(id)
841 }
842
843 pub fn replace_sprite_instance_set(
846 &mut self,
847 device: &wgpu::Device,
848 queue: &wgpu::Queue,
849 id: crate::resources::SpriteInstanceSetId,
850 item: &crate::renderer::SpriteItem,
851 ) -> bool {
852 if !self.content.sprite_instance_set_store.contains(id) {
853 return false;
854 }
855 self.ensure_sprite_pipelines(device);
856 let gpu = self.upload_sprite(device, queue, item);
857 self.content.sprite_instance_set_store.replace(id, gpu)
858 }
859
860 pub fn begin_upload_sprite_instance_set(
862 &mut self,
863 device: &wgpu::Device,
864 queue: &wgpu::Queue,
865 item: crate::renderer::SpriteItem,
866 ) -> crate::resources::JobId {
867 let slot = crate::resources::ResultSlot::<crate::resources::SpriteInstanceSetId>::new();
868 let slot_for_apply = slot.clone();
869 let device_for_apply = device.clone();
870 let queue_for_apply = queue.clone();
871 let id = {
872 let mut runner = self.jobs.lock().expect("upload job runner poisoned");
873 runner.submit_cpu(move |progress| {
874 progress.set(0.9);
875 Ok(crate::resources::upload_jobs::JobProduct::with_apply(
876 Box::new(move |resources: &mut DeviceResources| {
877 let sid = resources.upload_sprite_instance_set(
878 &device_for_apply,
879 &queue_for_apply,
880 &item,
881 );
882 slot_for_apply.set(sid);
883 }),
884 ))
885 })
886 };
887 self.job_results
888 .sprite_instance_set
889 .lock()
890 .expect("sprite instance set result map poisoned")
891 .insert(id, slot);
892 id
893 }
894
895 pub fn upload_result_sprite_instance_set(
898 &mut self,
899 id: crate::resources::JobId,
900 ) -> crate::error::ViewportResult<crate::resources::SpriteInstanceSetId> {
901 let mut map = self
902 .job_results
903 .sprite_instance_set
904 .lock()
905 .expect("sprite instance set result map poisoned");
906 let slot = match map.get(&id) {
907 Some(s) => s.clone(),
908 None => {
909 return Err(crate::error::ViewportError::JobResultMissing {
910 reason: "unknown id or wrong upload type",
911 });
912 }
913 };
914 match slot.take() {
915 Some(sid) => {
916 map.remove(&id);
917 Ok(sid)
918 }
919 None => Err(crate::error::ViewportError::JobNotReady),
920 }
921 }
922}
923
924#[derive(Clone)]
926pub struct SpriteGpuData {
927 pub(crate) vertex_buffer: wgpu::Buffer,
929 pub(crate) sprite_count: u32,
931 pub(crate) bind_group: wgpu::BindGroup,
933 pub(crate) depth_write: bool,
935 pub(crate) blend: crate::renderer::SpriteBlend,
937 pub(crate) wireframe: bool,
939 pub(crate) refraction_strength: f32,
943 pub(crate) lit: bool,
946 pub(crate) lit_normal_bg: Option<wgpu::BindGroup>,
950 pub(crate) _uniform_buf: wgpu::Buffer,
952 pub(crate) _instance_buf: wgpu::Buffer,
953}