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: "main",
275 buffers: &[Vertex::desc()],
276 },
277 primitive: wgpu::PrimitiveState {
278 topology: wgpu::PrimitiveTopology::TriangleList,
279 strip_index_format: None,
280 front_face: wgpu::FrontFace::Ccw,
281 cull_mode: Some(wgpu::Face::Back),
282 unclipped_depth: false,
283 polygon_mode: wgpu::PolygonMode::Fill,
284 conservative: false,
285 },
286 depth_stencil: None,
287 multisample: wgpu::MultisampleState {
288 count: 1,
289 mask: !0,
290 alpha_to_coverage_enabled: false,
291 },
292 fragment: Some(wgpu::FragmentState {
293 module: fs_module,
294 entry_point: "main",
295 targets: &[Some(wgpu::ColorTargetState {
296 format: surface_config.format,
297 blend: Some(wgpu::BlendState {
298 color: wgpu::BlendComponent {
299 src_factor: wgpu::BlendFactor::SrcAlpha,
300 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
301 operation: wgpu::BlendOperation::Add,
302 },
303 alpha: wgpu::BlendComponent {
304 src_factor: wgpu::BlendFactor::One,
305 dst_factor: wgpu::BlendFactor::One,
306 operation: wgpu::BlendOperation::Add,
307 },
308 }),
309 write_mask: wgpu::ColorWrites::ALL,
310 })],
311 }),
312 multiview: None,
313 });
314
315 Self {
316 pipeline: render_pipeline,
317 }
318 }
319}
320
321pub fn create_quad(
322 rect: Aabr<f32>,
323 uv_rect: Aabr<f32>,
324 color: Rgba<f32>,
325 mode: Mode,
326) -> Quad<Vertex> {
327 create_quad_vert_gradient(rect, uv_rect, color, color, mode)
328}
329
330pub fn create_quad_vert_gradient(
331 rect: Aabr<f32>,
332 uv_rect: Aabr<f32>,
333 top_color: Rgba<f32>,
334 bottom_color: Rgba<f32>,
335 mode: Mode,
336) -> Quad<Vertex> {
337 let top_color = top_color.into_array();
338 let bottom_color = bottom_color.into_array();
339
340 let center = if let Mode::ImageSourceNorth { .. } = mode {
341 uv_rect.center().into_array()
342 } else {
343 rect.center().into_array()
344 };
345 let scale = mode.scale().into_array();
346 let mode_val = mode.value();
347 let v = |pos, uv, color| Vertex {
348 pos,
349 uv,
350 center,
351 color,
352 scale,
353 mode: mode_val,
354 };
355 let aabr_to_lbrt = |aabr: Aabr<f32>| (aabr.min.x, aabr.min.y, aabr.max.x, aabr.max.y);
356
357 let (l, b, r, t) = aabr_to_lbrt(rect);
358 let (uv_l, uv_b, uv_r, uv_t) = aabr_to_lbrt(uv_rect);
359
360 match (uv_b > uv_t, uv_l > uv_r) {
361 (true, true) => Quad::new(
362 v([r, t], [uv_l, uv_b], top_color),
363 v([l, t], [uv_l, uv_t], top_color),
364 v([l, b], [uv_r, uv_t], bottom_color),
365 v([r, b], [uv_r, uv_b], bottom_color),
366 ),
367 (false, false) => Quad::new(
368 v([r, t], [uv_l, uv_b], top_color),
369 v([l, t], [uv_l, uv_t], top_color),
370 v([l, b], [uv_r, uv_t], bottom_color),
371 v([r, b], [uv_r, uv_b], bottom_color),
372 ),
373 _ => Quad::new(
374 v([r, t], [uv_r, uv_t], top_color),
375 v([l, t], [uv_l, uv_t], top_color),
376 v([l, b], [uv_l, uv_b], bottom_color),
377 v([r, b], [uv_r, uv_b], bottom_color),
378 ),
379 }
380}
381
382pub fn create_tri(
383 tri: [[f32; 2]; 3],
384 uv_tri: [[f32; 2]; 3],
385 color: Rgba<f32>,
386 mode: Mode,
387) -> Tri<Vertex> {
388 let center = [0.0, 0.0];
389 let scale = mode.scale().into_array();
390 let mode_val = mode.value();
391 let v = |pos, uv| Vertex {
392 pos,
393 uv,
394 center,
395 color: color.into_array(),
396 scale,
397 mode: mode_val,
398 };
399 Tri::new(
400 v([tri[0][0], tri[0][1]], [uv_tri[0][0], uv_tri[0][1]]),
401 v([tri[1][0], tri[1][1]], [uv_tri[1][0], uv_tri[1][1]]),
402 v([tri[2][0], tri[2][1]], [uv_tri[2][0], uv_tri[2][1]]),
403 )
404}
405
406pub struct PremultiplyAlphaLayout {
451 source_texture: wgpu::BindGroupLayout,
452}
453
454impl PremultiplyAlphaLayout {
455 pub fn new(device: &wgpu::Device) -> Self {
456 Self {
457 source_texture: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
458 label: None,
459 entries: &[
460 wgpu::BindGroupLayoutEntry {
462 binding: 0,
463 visibility: wgpu::ShaderStages::FRAGMENT,
464 ty: wgpu::BindingType::Texture {
465 sample_type: wgpu::TextureSampleType::Float { filterable: false },
466 view_dimension: wgpu::TextureViewDimension::D2,
467 multisampled: false,
468 },
469 count: None,
470 },
471 ],
472 }),
473 }
474 }
475}
476
477pub struct PremultiplyAlphaPipeline {
478 pub pipeline: wgpu::RenderPipeline,
479}
480
481impl PremultiplyAlphaPipeline {
482 pub fn new(
483 device: &wgpu::Device,
484 vs_module: &wgpu::ShaderModule,
485 fs_module: &wgpu::ShaderModule,
486 layout: &PremultiplyAlphaLayout,
487 ) -> Self {
488 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
489 label: Some("Premultiply alpha pipeline layout"),
490 bind_group_layouts: &[&layout.source_texture],
491 push_constant_ranges: &[wgpu::PushConstantRange {
492 stages: wgpu::ShaderStages::VERTEX,
493 range: 0..core::mem::size_of::<PremultiplyAlphaParams>() as u32,
494 }],
495 });
496
497 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
498 label: Some("Premultiply alpha pipeline"),
499 layout: Some(&pipeline_layout),
500 vertex: wgpu::VertexState {
501 module: vs_module,
502 entry_point: "main",
503 buffers: &[],
504 },
505 primitive: wgpu::PrimitiveState {
506 topology: wgpu::PrimitiveTopology::TriangleList,
507 strip_index_format: None,
508 front_face: wgpu::FrontFace::Ccw,
509 cull_mode: Some(wgpu::Face::Back),
510 unclipped_depth: false,
511 polygon_mode: wgpu::PolygonMode::Fill,
512 conservative: false,
513 },
514 depth_stencil: None,
515 multisample: wgpu::MultisampleState::default(),
516 fragment: Some(wgpu::FragmentState {
517 module: fs_module,
518 entry_point: "main",
519 targets: &[Some(wgpu::ColorTargetState {
520 format: UI_IMAGE_FORMAT,
521 blend: None,
522 write_mask: wgpu::ColorWrites::ALL,
523 })],
524 }),
525 multiview: None,
526 });
527
528 Self { pipeline }
529 }
530}
531
532#[repr(C)]
534#[derive(Copy, Clone, Debug, Zeroable, Pod)]
535pub struct PremultiplyAlphaParams {
536 source_size_xy: u32,
538 target_offset_xy: u32,
542 target_size_xy: u32,
544}
545
546pub(in super::super) struct PremultiplyUpload {
552 source_bg: wgpu::BindGroup,
553 source_size_xy: u32,
554 offset: Vec2<u16>,
558}
559
560impl PremultiplyUpload {
561 pub(in super::super) fn prepare(
562 device: &wgpu::Device,
563 queue: &wgpu::Queue,
564 layout: &PremultiplyAlphaLayout,
565 image: &image::RgbaImage,
566 offset: Vec2<u16>,
567 ) -> Self {
568 let image_size = wgpu::Extent3d {
574 width: image.width(),
575 height: image.height(),
576 depth_or_array_layers: 1,
577 };
578 let source_tex = device.create_texture(&wgpu::TextureDescriptor {
579 label: None,
580 size: image_size,
581 mip_level_count: 1,
582 sample_count: 1,
583 dimension: wgpu::TextureDimension::D2,
584 format: wgpu::TextureFormat::Rgba8UnormSrgb,
585 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
586 view_formats: &[],
587 });
588 queue.write_texture(
589 wgpu::ImageCopyTexture {
590 texture: &source_tex,
591 mip_level: 0,
592 origin: wgpu::Origin3d::ZERO,
593 aspect: wgpu::TextureAspect::All,
594 },
595 &(&**image)[..(image.width() as usize * image.height() as usize * 4)],
596 wgpu::ImageDataLayout {
597 offset: 0,
598 bytes_per_row: Some(image.width() * 4),
599 rows_per_image: Some(image.height()),
600 },
601 image_size,
602 );
603 let view = source_tex.create_view(&wgpu::TextureViewDescriptor {
605 label: None,
606 format: Some(wgpu::TextureFormat::Rgba8UnormSrgb),
607 dimension: Some(wgpu::TextureViewDimension::D2),
608 aspect: wgpu::TextureAspect::All,
609 base_mip_level: 0,
610 mip_level_count: None,
611 base_array_layer: 0,
612 array_layer_count: None,
613 });
614 let source_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
615 label: None,
616 layout: &layout.source_texture,
617 entries: &[wgpu::BindGroupEntry {
618 binding: 0,
619 resource: wgpu::BindingResource::TextureView(&view),
620 }],
621 });
622
623 let source_size_xy = image_size.width + (image_size.height << 16);
625
626 Self {
627 source_bg,
628 source_size_xy,
629 offset,
630 }
631 }
632
633 pub(in super::super) fn draw_data(
637 &self,
638 target: &Texture,
639 ) -> (&wgpu::BindGroup, PremultiplyAlphaParams) {
640 let target_offset_xy = u32::from(self.offset.x) + (u32::from(self.offset.y) << 16);
641 let target_dims = target.get_dimensions();
642 let target_size_xy = target_dims.x + (target_dims.y << 16);
644 (&self.source_bg, PremultiplyAlphaParams {
645 source_size_xy: self.source_size_xy,
646 target_offset_xy,
647 target_size_xy,
648 })
649 }
650}
651
652use std::sync::Arc;
653#[derive(Default)]
655pub(in super::super) struct BatchedUploads {
656 batches: Vec<(Arc<Texture>, Vec<PremultiplyUpload>)>,
657}
658#[derive(Default, Clone, Copy)]
659pub struct UploadBatchId(usize);
660
661impl BatchedUploads {
662 pub(in super::super) fn submit(
679 &mut self,
680 target_texture: &Arc<Texture>,
681 batch_id: UploadBatchId,
682 upload: PremultiplyUpload,
683 ) -> UploadBatchId {
684 if let Some(batch) = self
685 .batches
686 .get_mut(batch_id.0)
687 .filter(|b| Arc::ptr_eq(&b.0, target_texture))
688 {
689 batch.1.push(upload);
690 batch_id
691 } else {
692 let new_batch_id = UploadBatchId(self.batches.len());
693 self.batches
694 .push((Arc::clone(target_texture), vec![upload]));
695 new_batch_id
696 }
697 }
698
699 pub(in super::super) fn take(&mut self) -> Vec<(Arc<Texture>, Vec<PremultiplyUpload>)> {
700 core::mem::take(&mut self.batches)
701 }
702}