1use super::super::{Bound, Consts, GlobalsLayouts, Quad, Texture, Tri, Vertex as VertexTrait};
2use bytemuck::{Pod, Zeroable};
3use std::mem;
4use vek::*;
5
6const UI_IMAGE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
15
16#[repr(C)]
17#[derive(Copy, Clone, Debug, Zeroable, Pod)]
18pub struct Vertex {
19 pos: [f32; 2],
20 uv: [f32; 2],
21 color: [f32; 4],
22 center: [f32; 2],
23 scale: [f32; 2],
25 mode: u32,
26}
27
28impl Vertex {
29 fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
30 const ATTRIBUTES: [wgpu::VertexAttribute; 6] = wgpu::vertex_attr_array![
31 0 => Float32x2, 1 => Float32x2, 2 => Float32x4,
32 3 => Float32x2, 4 => Float32x2, 5 => Uint32,
33 ];
34 wgpu::VertexBufferLayout {
35 array_stride: Self::STRIDE,
36 step_mode: wgpu::VertexStepMode::Vertex,
37 attributes: &ATTRIBUTES,
38 }
39 }
40}
41
42impl VertexTrait for Vertex {
43 const QUADS_INDEX: Option<wgpu::IndexFormat> = None;
44 const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
45}
46
47#[repr(C)]
48#[derive(Copy, Clone, Debug, Zeroable, Pod)]
49pub struct Locals {
50 pos: [f32; 4],
51}
52
53impl From<Vec4<f32>> for Locals {
54 fn from(pos: Vec4<f32>) -> Self {
55 Self {
56 pos: pos.into_array(),
57 }
58 }
59}
60
61impl Default for Locals {
62 fn default() -> Self { Self { pos: [0.0; 4] } }
63}
64
65#[repr(C)]
66#[derive(Copy, Clone, Debug, Zeroable, Pod)]
67pub struct TexLocals {
68 texture_size: [u32; 2],
69}
70
71impl From<Vec2<u32>> for TexLocals {
72 fn from(texture_size: Vec2<u32>) -> Self {
73 Self {
74 texture_size: texture_size.into_array(),
75 }
76 }
77}
78
79pub const MODE_TEXT: u32 = 0;
81pub const MODE_IMAGE: u32 = 1;
83pub const MODE_GEOMETRY: u32 = 2;
85pub const MODE_IMAGE_SOURCE_NORTH: u32 = 3;
90pub const MODE_IMAGE_TARGET_NORTH: u32 = 5;
95
96#[derive(Clone, Copy)]
97pub enum Mode {
98 Text,
99 Image {
100 scale: Vec2<f32>,
101 },
102 Geometry,
103 ImageSourceNorth {
107 scale: Vec2<f32>,
108 },
109 ImageTargetNorth {
113 scale: Vec2<f32>,
114 },
115}
116
117impl Mode {
118 fn value(self) -> u32 {
119 match self {
120 Mode::Text => MODE_TEXT,
121 Mode::Image { .. } => MODE_IMAGE,
122 Mode::Geometry => MODE_GEOMETRY,
123 Mode::ImageSourceNorth { .. } => MODE_IMAGE_SOURCE_NORTH,
124 Mode::ImageTargetNorth { .. } => MODE_IMAGE_TARGET_NORTH,
125 }
126 }
127
128 fn scale(self) -> Vec2<f32> {
130 match self {
131 Mode::ImageSourceNorth { scale } | Mode::ImageTargetNorth { scale } => scale,
132 Mode::Image { scale } => scale,
133 Mode::Text | Mode::Geometry => Vec2::one(),
134 }
135 }
136}
137
138pub type BoundLocals = Bound<Consts<Locals>>;
139
140pub struct TextureBindGroup {
141 pub(in super::super) bind_group: wgpu::BindGroup,
142}
143
144pub struct UiLayout {
145 locals: wgpu::BindGroupLayout,
146 texture: wgpu::BindGroupLayout,
147}
148
149impl UiLayout {
150 pub fn new(device: &wgpu::Device) -> Self {
151 Self {
152 locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
153 label: None,
154 entries: &[
155 wgpu::BindGroupLayoutEntry {
157 binding: 0,
158 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
159 ty: wgpu::BindingType::Buffer {
160 ty: wgpu::BufferBindingType::Uniform,
161 has_dynamic_offset: false,
162 min_binding_size: None,
163 },
164 count: None,
165 },
166 ],
167 }),
168 texture: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
169 label: None,
170 entries: &[
171 wgpu::BindGroupLayoutEntry {
173 binding: 0,
174 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
175 ty: wgpu::BindingType::Texture {
176 sample_type: wgpu::TextureSampleType::Float { filterable: true },
177 view_dimension: wgpu::TextureViewDimension::D2,
178 multisampled: false,
179 },
180 count: None,
181 },
182 wgpu::BindGroupLayoutEntry {
183 binding: 1,
184 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
185 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
186 count: None,
187 },
188 wgpu::BindGroupLayoutEntry {
190 binding: 2,
191 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
192 ty: wgpu::BindingType::Buffer {
193 ty: wgpu::BufferBindingType::Uniform,
194 has_dynamic_offset: false,
195 min_binding_size: None,
196 },
197 count: None,
198 },
199 ],
200 }),
201 }
202 }
203
204 pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts<Locals>) -> BoundLocals {
205 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
206 label: None,
207 layout: &self.locals,
208 entries: &[wgpu::BindGroupEntry {
209 binding: 0,
210 resource: locals.buf().as_entire_binding(),
211 }],
212 });
213
214 BoundLocals {
215 bind_group,
216 with: locals,
217 }
218 }
219
220 pub fn bind_texture(
221 &self,
222 device: &wgpu::Device,
223 texture: &Texture,
224 tex_locals: Consts<TexLocals>,
225 ) -> TextureBindGroup {
226 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
227 label: None,
228 layout: &self.texture,
229 entries: &[
230 wgpu::BindGroupEntry {
231 binding: 0,
232 resource: wgpu::BindingResource::TextureView(&texture.view),
233 },
234 wgpu::BindGroupEntry {
235 binding: 1,
236 resource: wgpu::BindingResource::Sampler(&texture.sampler),
237 },
238 wgpu::BindGroupEntry {
239 binding: 2,
240 resource: tex_locals.buf().as_entire_binding(),
241 },
242 ],
243 });
244
245 TextureBindGroup { bind_group }
246 }
247}
248
249pub struct UiPipeline {
250 pub pipeline: wgpu::RenderPipeline,
251}
252
253impl UiPipeline {
254 pub fn new(
255 device: &wgpu::Device,
256 vs_module: &wgpu::ShaderModule,
257 fs_module: &wgpu::ShaderModule,
258 surface_config: &wgpu::SurfaceConfiguration,
259 global_layout: &GlobalsLayouts,
260 layout: &UiLayout,
261 ) -> Self {
262 let render_pipeline_layout =
263 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
264 label: Some("Ui pipeline layout"),
265 push_constant_ranges: &[],
266 bind_group_layouts: &[&global_layout.globals, &layout.locals, &layout.texture],
267 });
268
269 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
270 label: Some("UI pipeline"),
271 layout: Some(&render_pipeline_layout),
272 vertex: wgpu::VertexState {
273 module: vs_module,
274 entry_point: Some("main"),
275 buffers: &[Vertex::desc()],
276 compilation_options: Default::default(),
277 },
278 primitive: wgpu::PrimitiveState {
279 topology: wgpu::PrimitiveTopology::TriangleList,
280 strip_index_format: None,
281 front_face: wgpu::FrontFace::Ccw,
282 cull_mode: Some(wgpu::Face::Back),
283 unclipped_depth: false,
284 polygon_mode: wgpu::PolygonMode::Fill,
285 conservative: false,
286 },
287 depth_stencil: None,
288 multisample: wgpu::MultisampleState {
289 count: 1,
290 mask: !0,
291 alpha_to_coverage_enabled: false,
292 },
293 fragment: Some(wgpu::FragmentState {
294 module: fs_module,
295 entry_point: Some("main"),
296 targets: &[Some(wgpu::ColorTargetState {
297 format: surface_config.format,
298 blend: Some(wgpu::BlendState {
299 color: wgpu::BlendComponent {
300 src_factor: wgpu::BlendFactor::SrcAlpha,
301 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
302 operation: wgpu::BlendOperation::Add,
303 },
304 alpha: wgpu::BlendComponent {
305 src_factor: wgpu::BlendFactor::One,
306 dst_factor: wgpu::BlendFactor::One,
307 operation: wgpu::BlendOperation::Add,
308 },
309 }),
310 write_mask: wgpu::ColorWrites::ALL,
311 })],
312 compilation_options: Default::default(),
313 }),
314 multiview: None,
315 cache: None,
316 });
317
318 Self {
319 pipeline: render_pipeline,
320 }
321 }
322}
323
324pub fn create_quad(
325 rect: Aabr<f32>,
326 uv_rect: Aabr<f32>,
327 color: Rgba<f32>,
328 mode: Mode,
329) -> Quad<Vertex> {
330 create_quad_vert_gradient(rect, uv_rect, color, color, mode)
331}
332
333pub fn create_quad_vert_gradient(
334 rect: Aabr<f32>,
335 uv_rect: Aabr<f32>,
336 top_color: Rgba<f32>,
337 bottom_color: Rgba<f32>,
338 mode: Mode,
339) -> Quad<Vertex> {
340 let top_color = top_color.into_array();
341 let bottom_color = bottom_color.into_array();
342
343 let center = if let Mode::ImageSourceNorth { .. } = mode {
344 uv_rect.center().into_array()
345 } else {
346 rect.center().into_array()
347 };
348 let scale = mode.scale().into_array();
349 let mode_val = mode.value();
350 let v = |pos, uv, color| Vertex {
351 pos,
352 uv,
353 center,
354 color,
355 scale,
356 mode: mode_val,
357 };
358 let aabr_to_lbrt = |aabr: Aabr<f32>| (aabr.min.x, aabr.min.y, aabr.max.x, aabr.max.y);
359
360 let (l, b, r, t) = aabr_to_lbrt(rect);
361 let (uv_l, uv_b, uv_r, uv_t) = aabr_to_lbrt(uv_rect);
362
363 match (uv_b > uv_t, uv_l > uv_r) {
364 (true, true) => Quad::new(
365 v([r, t], [uv_l, uv_b], top_color),
366 v([l, t], [uv_l, uv_t], top_color),
367 v([l, b], [uv_r, uv_t], bottom_color),
368 v([r, b], [uv_r, uv_b], bottom_color),
369 ),
370 (false, false) => Quad::new(
371 v([r, t], [uv_l, uv_b], top_color),
372 v([l, t], [uv_l, uv_t], top_color),
373 v([l, b], [uv_r, uv_t], bottom_color),
374 v([r, b], [uv_r, uv_b], bottom_color),
375 ),
376 _ => Quad::new(
377 v([r, t], [uv_r, uv_t], top_color),
378 v([l, t], [uv_l, uv_t], top_color),
379 v([l, b], [uv_l, uv_b], bottom_color),
380 v([r, b], [uv_r, uv_b], bottom_color),
381 ),
382 }
383}
384
385pub fn create_tri(
386 tri: [[f32; 2]; 3],
387 uv_tri: [[f32; 2]; 3],
388 color: Rgba<f32>,
389 mode: Mode,
390) -> Tri<Vertex> {
391 let center = [0.0, 0.0];
392 let scale = mode.scale().into_array();
393 let mode_val = mode.value();
394 let v = |pos, uv| Vertex {
395 pos,
396 uv,
397 center,
398 color: color.into_array(),
399 scale,
400 mode: mode_val,
401 };
402 Tri::new(
403 v([tri[0][0], tri[0][1]], [uv_tri[0][0], uv_tri[0][1]]),
404 v([tri[1][0], tri[1][1]], [uv_tri[1][0], uv_tri[1][1]]),
405 v([tri[2][0], tri[2][1]], [uv_tri[2][0], uv_tri[2][1]]),
406 )
407}
408
409pub struct PremultiplyAlphaLayout {
454 source_texture: wgpu::BindGroupLayout,
455}
456
457impl PremultiplyAlphaLayout {
458 pub fn new(device: &wgpu::Device) -> Self {
459 Self {
460 source_texture: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
461 label: None,
462 entries: &[
463 wgpu::BindGroupLayoutEntry {
465 binding: 0,
466 visibility: wgpu::ShaderStages::FRAGMENT,
467 ty: wgpu::BindingType::Texture {
468 sample_type: wgpu::TextureSampleType::Float { filterable: false },
469 view_dimension: wgpu::TextureViewDimension::D2,
470 multisampled: false,
471 },
472 count: None,
473 },
474 ],
475 }),
476 }
477 }
478}
479
480pub struct PremultiplyAlphaPipeline {
481 pub pipeline: wgpu::RenderPipeline,
482}
483
484impl PremultiplyAlphaPipeline {
485 pub fn new(
486 device: &wgpu::Device,
487 vs_module: &wgpu::ShaderModule,
488 fs_module: &wgpu::ShaderModule,
489 layout: &PremultiplyAlphaLayout,
490 ) -> Self {
491 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
492 label: Some("Premultiply alpha pipeline layout"),
493 bind_group_layouts: &[&layout.source_texture],
494 push_constant_ranges: &[wgpu::PushConstantRange {
495 stages: wgpu::ShaderStages::VERTEX,
496 range: 0..core::mem::size_of::<PremultiplyAlphaParams>() as u32,
497 }],
498 });
499
500 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
501 label: Some("Premultiply alpha pipeline"),
502 layout: Some(&pipeline_layout),
503 vertex: wgpu::VertexState {
504 module: vs_module,
505 entry_point: Some("main"),
506 buffers: &[],
507 compilation_options: Default::default(),
508 },
509 primitive: wgpu::PrimitiveState {
510 topology: wgpu::PrimitiveTopology::TriangleList,
511 strip_index_format: None,
512 front_face: wgpu::FrontFace::Ccw,
513 cull_mode: Some(wgpu::Face::Back),
514 unclipped_depth: false,
515 polygon_mode: wgpu::PolygonMode::Fill,
516 conservative: false,
517 },
518 depth_stencil: None,
519 multisample: wgpu::MultisampleState::default(),
520 fragment: Some(wgpu::FragmentState {
521 module: fs_module,
522 entry_point: Some("main"),
523 targets: &[Some(wgpu::ColorTargetState {
524 format: UI_IMAGE_FORMAT,
525 blend: None,
526 write_mask: wgpu::ColorWrites::ALL,
527 })],
528 compilation_options: Default::default(),
529 }),
530 multiview: None,
531 cache: None,
532 });
533
534 Self { pipeline }
535 }
536}
537
538#[repr(C)]
540#[derive(Copy, Clone, Debug, Zeroable, Pod)]
541pub struct PremultiplyAlphaParams {
542 source_size_xy: u32,
544 target_offset_xy: u32,
548 target_size_xy: u32,
550}
551
552pub(in super::super) struct PremultiplyUpload {
558 source_bg: wgpu::BindGroup,
559 source_size_xy: u32,
560 offset: Vec2<u16>,
564}
565
566impl PremultiplyUpload {
567 pub(in super::super) fn prepare(
568 device: &wgpu::Device,
569 queue: &wgpu::Queue,
570 layout: &PremultiplyAlphaLayout,
571 image: &image::RgbaImage,
572 offset: Vec2<u16>,
573 ) -> Self {
574 let image_size = wgpu::Extent3d {
580 width: image.width(),
581 height: image.height(),
582 depth_or_array_layers: 1,
583 };
584 let source_tex = device.create_texture(&wgpu::TextureDescriptor {
585 label: None,
586 size: image_size,
587 mip_level_count: 1,
588 sample_count: 1,
589 dimension: wgpu::TextureDimension::D2,
590 format: wgpu::TextureFormat::Rgba8UnormSrgb,
591 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
592 view_formats: &[],
593 });
594 queue.write_texture(
595 wgpu::TexelCopyTextureInfo {
596 texture: &source_tex,
597 mip_level: 0,
598 origin: wgpu::Origin3d::ZERO,
599 aspect: wgpu::TextureAspect::All,
600 },
601 &(&**image)[..(image.width() as usize * image.height() as usize * 4)],
602 wgpu::TexelCopyBufferLayout {
603 offset: 0,
604 bytes_per_row: Some(image.width() * 4),
605 rows_per_image: Some(image.height()),
606 },
607 image_size,
608 );
609 let view = source_tex.create_view(&wgpu::TextureViewDescriptor {
611 label: None,
612 format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
613 dimension: Some(wgpu::TextureViewDimension::D2),
614 usage: None,
615 aspect: wgpu::TextureAspect::All,
616 base_mip_level: 0,
617 mip_level_count: None,
618 base_array_layer: 0,
619 array_layer_count: None,
620 });
621 let source_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
622 label: None,
623 layout: &layout.source_texture,
624 entries: &[wgpu::BindGroupEntry {
625 binding: 0,
626 resource: wgpu::BindingResource::TextureView(&view),
627 }],
628 });
629
630 let source_size_xy = image_size.width + (image_size.height << 16);
632
633 Self {
634 source_bg,
635 source_size_xy,
636 offset,
637 }
638 }
639
640 pub(in super::super) fn draw_data(
644 &self,
645 target: &Texture,
646 ) -> (&wgpu::BindGroup, PremultiplyAlphaParams) {
647 let target_offset_xy = u32::from(self.offset.x) + (u32::from(self.offset.y) << 16);
648 let target_dims = target.get_dimensions();
649 let target_size_xy = target_dims.x + (target_dims.y << 16);
651 (&self.source_bg, PremultiplyAlphaParams {
652 source_size_xy: self.source_size_xy,
653 target_offset_xy,
654 target_size_xy,
655 })
656 }
657}
658
659use std::sync::Arc;
660#[derive(Default)]
662pub(in super::super) struct BatchedUploads {
663 batches: Vec<(Arc<Texture>, Vec<PremultiplyUpload>)>,
664}
665#[derive(Default, Clone, Copy)]
666pub struct UploadBatchId(usize);
667
668impl BatchedUploads {
669 pub(in super::super) fn submit(
686 &mut self,
687 target_texture: &Arc<Texture>,
688 batch_id: UploadBatchId,
689 upload: PremultiplyUpload,
690 ) -> UploadBatchId {
691 if let Some(batch) = self
692 .batches
693 .get_mut(batch_id.0)
694 .filter(|b| Arc::ptr_eq(&b.0, target_texture))
695 {
696 batch.1.push(upload);
697 batch_id
698 } else {
699 let new_batch_id = UploadBatchId(self.batches.len());
700 self.batches
701 .push((Arc::clone(target_texture), vec![upload]));
702 new_batch_id
703 }
704 }
705
706 pub(in super::super) fn take(&mut self) -> Vec<(Arc<Texture>, Vec<PremultiplyUpload>)> {
707 core::mem::take(&mut self.batches)
708 }
709}