1use super::compositor;
5use crate::color::sRGB;
6use crate::component::shape::ShapeKind;
7use crate::graphics::{self, Vec2f, Vec4f};
8use crate::render::atlas::{self, Atlas};
9use crate::render::compositor::CompositorView;
10use crate::{Canonicalize, PxDim, PxPoint, SourceID, shaders};
11use core::f32;
12use guillotiere::euclid::Size2D;
13use num_traits::Zero;
14use std::collections::HashMap;
15use std::num::NonZero;
16use std::sync::Arc;
17use wgpu::BindGroupLayout;
18
19pub struct Instance<const KIND: u8> {
20 pub padding: crate::PxPerimeter,
21 pub border: f32,
22 pub blur: f32,
23 pub fill: sRGB,
24 pub outline: sRGB,
25 pub corners: [f32; 4],
26 pub id: Arc<SourceID>,
27}
28
29impl<const KIND: u8> super::Renderable for Instance<KIND> {
30 fn render(
31 &self,
32 area: crate::PxRect,
33 driver: &crate::graphics::Driver,
34 compositor: &mut CompositorView<'_>,
35 ) -> Result<(), crate::Error> {
36 let dim = area.dim() - self.padding.bottomright() - self.padding.topleft();
37
38 if dim.width <= 0.0 || dim.height <= 0.0 {
39 return Ok(());
40 }
41
42 if self.corners.iter().all(|x| x.is_zero()) && self.border.is_zero() {
45 compositor.append_data(
46 area.topleft().add_size(&self.padding.topleft()),
47 dim,
48 [0.0, 0.0].into(),
49 [0.0, 0.0].into(),
50 self.fill.as_32bit().rgba,
51 0.0,
52 u8::MAX,
53 false,
54 );
55
56 return Ok(());
57 }
58
59 let perimeter = [
60 dim.height - self.corners[0] - self.corners[3],
61 dim.width - self.corners[0] - self.corners[1],
62 dim.height - self.corners[1] - self.corners[2],
63 dim.width - self.corners[2] - self.corners[3],
64 ];
65
66 if KIND == ShapeKind::RoundRect as u8 && perimeter.iter().all(|x| *x >= 2.0) {
69 let mut corners = self.corners.map(|x| x.max(self.border));
72 let mut intcorners = corners.map(|x| x.ceil() as i32);
73
74 let intsides = [
75 intcorners[0].max(intcorners[3]),
76 intcorners[0].max(intcorners[1]),
77 intcorners[1].max(intcorners[2]),
78 intcorners[2].max(intcorners[3]),
79 ];
80
81 let inner = atlas::Size::new(
84 intsides[0] + intsides[2] + 3, intsides[1] + intsides[3] + 3, );
87
88 let (region_uv, region_index) = driver
94 .with_pipeline::<Shape<KIND>, Result<(atlas::PxBox, u8), crate::Error>>(
95 |pipeline| {
96 pipeline.reserve(
97 driver,
98 self.id.clone(),
99 inner + atlas::Size::new(4, 4),
100 Data {
101 pos: [2.0; 2].into(),
102 dim: inner.to_f32().to_array().into(),
103 border: self.border,
104 blur: self.blur,
105 corners: intcorners.map(|x| x as f32).into(),
108 fill: self.fill.as_32bit().rgba,
109 outline: self.outline.as_32bit().rgba,
110 },
111 true,
112 )
113 },
114 )?;
115
116 corners = corners.map(|x| x + 1.0);
128 intcorners = intcorners.map(|x| x + 1);
129
130 let topleft = area.topleft().add_size(&self.padding.topleft()).to_vector();
131 let uvpos = region_uv.min.add_size(&Size2D::splat(2));
133 let mut gen_corner = |pos: PxPoint, corner: f32, u: i32, v: i32| {
134 let intdim = PxDim::splat(corner.ceil());
135 compositor.append_data(
136 pos + topleft,
137 PxDim::splat(corner),
138 uvpos
139 .add_size(&Size2D::new(u, v))
140 .to_f32()
141 .to_array()
142 .into(),
143 intdim.to_array().into(),
144 0xFFFFFFFF,
145 0.0,
146 region_index,
147 true,
148 );
149 };
150
151 gen_corner(PxPoint::new(-1.0, -1.0), corners[0] + 1.0, -1, -1);
157 gen_corner(
158 PxPoint::new(dim.width - corners[1], -1.0),
159 corners[1] + 1.0,
160 inner.width - intcorners[1],
161 -1,
162 );
163 gen_corner(
164 PxPoint::new(dim.width - corners[2], dim.height - corners[2]),
165 corners[2] + 1.0,
166 inner.width - intcorners[2],
167 inner.height - intcorners[2],
168 );
169 gen_corner(
170 PxPoint::new(-1.0, dim.height - corners[3]),
171 corners[3] + 1.0,
172 -1,
173 inner.height - intcorners[3],
174 );
175
176 let sides = crate::PxRect::new(
177 corners[0].max(corners[3]),
178 corners[0].max(corners[1]),
179 corners[1].max(corners[2]),
180 corners[2].max(corners[3]),
181 );
182
183 let intsides = [
186 intcorners[0].max(intcorners[3]),
187 intcorners[0].max(intcorners[1]),
188 intcorners[1].max(intcorners[2]),
189 intcorners[2].max(intcorners[3]),
190 ];
191
192 let mut gen_side = |dim: PxDim, pos: PxPoint, u: i32, v: i32, w: i32, h: i32| {
193 compositor.append_data(
194 pos + topleft,
195 dim,
196 uvpos
197 .add_size(&Size2D::new(u, v))
198 .to_f32()
199 .to_array()
200 .into(),
201 [w as f32, h as f32].into(),
202 0xFFFFFFFF,
203 0.0,
204 region_index,
205 true,
206 );
207 };
208
209 gen_side(
214 PxDim::new(sides.left() + 1.0, dim.height - corners[0] - corners[3]),
215 PxPoint::new(-1.0, corners[0]),
216 -1,
217 intsides[1],
218 intsides[0] + 1,
219 0,
220 );
221 gen_side(
222 PxDim::new(dim.width - corners[0] - corners[1], sides.top() + 1.0),
223 PxPoint::new(corners[0], -1.0),
224 intsides[0],
225 -1,
226 0,
227 intsides[1] + 1,
228 );
229 gen_side(
230 PxDim::new(sides.right() + 1.0, dim.height - corners[1] - corners[2]),
231 PxPoint::new(dim.width - sides.right(), corners[1]),
232 inner.width - intsides[2],
233 intsides[1],
234 intsides[2] + 1,
235 0,
236 );
237 gen_side(
238 PxDim::new(dim.width - corners[3] - corners[2], sides.bottom() + 1.0),
239 PxPoint::new(corners[3], dim.height - sides.bottom()),
240 intsides[0],
241 inner.height - intsides[3],
242 0,
243 intsides[3] + 1,
244 );
245
246 compositor.append_data(
248 PxPoint::splat(corners[0]) + topleft,
249 dim - PxDim::splat(corners[0] + corners[2]),
250 [0.0, 0.0].into(),
251 [0.0, 0.0].into(),
252 self.fill.as_32bit().rgba,
253 0.0,
254 u8::MAX,
255 true,
256 );
257
258 return Ok(());
259 }
260
261 let (region_uv, region_index) = driver
270 .with_pipeline::<Shape<KIND>, Result<(atlas::PxBox, u8), crate::Error>>(|pipeline| {
271 pipeline.reserve(
272 driver,
273 self.id.clone(),
274 dim.ceil().cast(),
275 Data {
276 pos: [0.0; 2].into(),
277 dim: dim.to_array().into(),
278 border: self.border,
279 blur: self.blur,
280 corners: self.corners.into(),
281 fill: self.fill.as_32bit().rgba,
282 outline: self.outline.as_32bit().rgba,
283 },
284 false,
285 )
286 })?;
287
288 compositor.append_data(
289 area.topleft().add_size(&self.padding.topleft()),
290 dim,
291 region_uv.min.to_f32().to_array().into(),
292 region_uv.size().to_f32().to_array().into(),
293 0xFFFFFFFF,
294 0.0,
295 region_index,
296 false,
297 );
298
299 Ok(())
300 }
301}
302
303#[derive(Debug, Clone, Copy, Default, PartialEq, bytemuck::NoUninit)]
317#[repr(C)]
318pub struct Data {
319 pub corners: Vec4f,
320 pub pos: Vec2f,
321 pub dim: Vec2f,
322 pub border: f32,
323 pub blur: f32,
324 pub fill: u32,
325 pub outline: u32,
326}
327
328impl Eq for Data {}
330
331impl std::hash::Hash for Data {
332 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
333 self.corners.hash(state);
334 self.pos.hash(state);
335 self.dim.hash(state);
336 self.border.canonical_bits().hash(state);
337 self.blur.canonical_bits().hash(state);
338 self.fill.hash(state);
339 self.outline.hash(state);
340 }
341}
342
343#[derive(Debug)]
344pub struct Shape<const KIND: u8> {
345 data: HashMap<u8, Vec<Data>>,
346 buffer: wgpu::Buffer,
347 pipeline: wgpu::RenderPipeline,
348 group: wgpu::BindGroup,
349 cache: HashMap<Arc<SourceID>, (Data, atlas::PxBox, u8)>,
350 refcount: HashMap<(Data, atlas::Size), (atlas::Region, usize)>,
351}
352
353impl<const KIND: u8> Shape<KIND> {
354 fn reserve(
355 &mut self,
356 driver: &graphics::Driver,
357 id: Arc<SourceID>,
358 uvdim: atlas::Size,
359 mut data: Data,
360 clear: bool,
361 ) -> Result<(atlas::PxBox, u8), crate::Error> {
362 if let Some((cache, uv, layer)) = self.cache.get(&id) {
364 if data == *cache && uvdim == uv.size() {
367 data.pos += uv.min.to_f32().to_array();
370 self.data.entry(*layer).or_default().push(data);
371 return Ok((*uv, *layer));
372 } else if let Some((old, uv, _)) = self.cache.remove(&id) {
373 if let std::collections::hash_map::Entry::Occupied(mut v) =
376 self.refcount.entry((old, uv.size()))
377 {
378 if v.get().1 <= 1 {
379 driver.atlas.write().destroy(&mut v.get_mut().0);
380 v.remove();
381 } else {
382 v.get_mut().1 -= 1;
383 }
384 }
385 }
386 }
387
388 let (region, _) = match self.refcount.entry((data, uvdim)) {
393 std::collections::hash_map::Entry::Occupied(mut occupied_entry) => {
394 occupied_entry.get_mut().1 += 1;
395 occupied_entry.into_mut()
396 }
397 std::collections::hash_map::Entry::Vacant(vacant_entry) => vacant_entry.insert((
398 driver.atlas.write().reserve(
399 &driver.device,
400 uvdim,
401 None,
402 if clear { Some(&driver.queue) } else { None },
403 )?,
404 1,
405 )),
406 };
407
408 debug_assert_eq!(uvdim, region.uv.size());
409
410 self.cache
411 .entry(id)
412 .and_modify(|v| *v = (data, region.uv, region.index))
413 .or_insert((data, region.uv, region.index));
414
415 data.pos += region.uv.min.to_f32().to_array();
416 self.data.entry(region.index).or_default().push(data);
417 Ok((region.uv, region.index))
418 }
419}
420
421impl<const KIND: u8> super::Pipeline for Shape<KIND> {
422 fn draw(&mut self, driver: &graphics::Driver, pass: &mut wgpu::RenderPass<'_>, layer: u8) {
423 if let Some(data) = self.data.get_mut(&layer) {
424 let size = data.len() * size_of::<Data>();
425 if (self.buffer.size() as usize) < size {
426 self.buffer.destroy();
427 self.buffer = driver.device.create_buffer(&wgpu::BufferDescriptor {
428 label: Some("Shape Data"),
429 size: size.next_power_of_two() as u64,
430 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
431 mapped_at_creation: false,
432 });
433 self.group = Self::rebind(
434 &self.buffer,
435 &self.pipeline.get_bind_group_layout(0),
436 &driver.device,
437 &driver.atlas.read(),
438 );
439 }
440
441 driver
442 .queue
443 .write_buffer(&self.buffer, 0, bytemuck::cast_slice(data.as_slice()));
444
445 pass.set_pipeline(&self.pipeline);
446 pass.set_bind_group(0, &self.group, &[0]);
447 pass.draw(0..(data.len() as u32 * 6), 0..1);
448 data.clear();
449 }
450 }
451
452 fn destroy(&mut self, driver: &graphics::Driver) {
453 for (_, (mut region, _)) in self.refcount.drain() {
454 driver.atlas.write().destroy(&mut region);
455 }
456 }
457}
458
459impl<const KIND: u8> Shape<KIND> {
460 pub fn layout(device: &wgpu::Device) -> wgpu::PipelineLayout {
461 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
462 label: Some("Shape Bind Group"),
463 entries: &[
464 wgpu::BindGroupLayoutEntry {
465 binding: 0,
466 visibility: wgpu::ShaderStages::VERTEX,
467 ty: wgpu::BindingType::Buffer {
468 ty: wgpu::BufferBindingType::Uniform,
469 has_dynamic_offset: false,
470 min_binding_size: NonZero::new(size_of::<crate::Mat4x4>() as u64),
471 },
472 count: None,
473 },
474 wgpu::BindGroupLayoutEntry {
475 binding: 1,
476 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
477 ty: wgpu::BindingType::Buffer {
478 ty: wgpu::BufferBindingType::Storage { read_only: true },
479 has_dynamic_offset: true,
480 min_binding_size: None,
481 },
482 count: None,
483 },
484 wgpu::BindGroupLayoutEntry {
485 binding: 2,
486 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
487 ty: wgpu::BindingType::Buffer {
488 ty: wgpu::BufferBindingType::Uniform,
489 has_dynamic_offset: false,
490 min_binding_size: NonZero::new(size_of::<u32>() as u64),
491 },
492 count: None,
493 },
494 ],
495 });
496
497 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
498 label: Some("Shape Pipeline"),
499 bind_group_layouts: &[&bind_group_layout],
500 push_constant_ranges: &[],
501 })
502 }
503
504 pub fn shader(device: &wgpu::Device) -> wgpu::ShaderModule {
505 shaders::load_wgsl(device, "Shape", shaders::get("shape.wgsl").unwrap())
506 }
507
508 fn pipeline(
509 layout: &wgpu::PipelineLayout,
510 shader: &wgpu::ShaderModule,
511 device: &wgpu::Device,
512 entry_point: &str,
513 ) -> wgpu::RenderPipeline {
514 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
515 label: None,
516 layout: Some(layout),
517 vertex: wgpu::VertexState {
518 module: shader,
519 entry_point: Some("vs_main"),
520 buffers: &[],
521 compilation_options: Default::default(),
522 },
523 fragment: Some(wgpu::FragmentState {
524 module: shader,
525 entry_point: Some(entry_point),
526 compilation_options: Default::default(),
527 targets: &[Some(compositor::TARGET_BLEND)],
528 }),
529 primitive: wgpu::PrimitiveState {
530 front_face: wgpu::FrontFace::Cw,
531 topology: wgpu::PrimitiveTopology::TriangleList,
532 ..Default::default()
533 },
534 depth_stencil: None,
535 multisample: wgpu::MultisampleState::default(),
536 multiview: None,
537 cache: None,
538 })
539 }
540
541 fn rebind(
542 buffer: &wgpu::Buffer,
543 layout: &BindGroupLayout,
544 device: &wgpu::Device,
545 atlas: &Atlas,
546 ) -> wgpu::BindGroup {
547 let bindings = [
548 wgpu::BindGroupEntry {
549 binding: 0,
550 resource: atlas.mvp.as_entire_binding(),
551 },
552 wgpu::BindGroupEntry {
553 binding: 1,
554 resource: buffer.as_entire_binding(),
555 },
556 wgpu::BindGroupEntry {
557 binding: 2,
558 resource: atlas.extent_buf.as_entire_binding(),
559 },
560 ];
561
562 device.create_bind_group(&wgpu::BindGroupDescriptor {
563 layout,
564 entries: &bindings,
565 label: None,
566 })
567 }
568
569 fn new(
570 layout: &wgpu::PipelineLayout,
571 shader: &wgpu::ShaderModule,
572 driver: &graphics::Driver,
573 entry_point: &str,
574 ) -> Self {
575 let buffer = driver.device.create_buffer(&wgpu::BufferDescriptor {
576 label: Some("Shape Data"),
577 size: 32 * size_of::<Data>() as u64,
578 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
579 mapped_at_creation: false,
580 });
581 let pipeline = Self::pipeline(layout, shader, &driver.device, entry_point);
582
583 let group = Self::rebind(
584 &buffer,
585 &pipeline.get_bind_group_layout(0),
586 &driver.device,
587 &driver.atlas.read(),
588 );
589
590 Self {
591 data: HashMap::new(),
592 buffer,
593 pipeline,
594 group,
595 cache: HashMap::new(),
596 refcount: HashMap::new(),
597 }
598 }
599}
600
601impl Shape<0> {
602 pub fn create(
603 layout: &wgpu::PipelineLayout,
604 shader: &wgpu::ShaderModule,
605 driver: &graphics::Driver,
606 ) -> Box<dyn super::Pipeline> {
607 Box::new(Self::new(layout, shader, driver, "rectangle"))
608 }
609}
610
611impl Shape<1> {
612 pub fn create(
613 layout: &wgpu::PipelineLayout,
614 shader: &wgpu::ShaderModule,
615 driver: &graphics::Driver,
616 ) -> Box<dyn super::Pipeline> {
617 Box::new(Self::new(layout, shader, driver, "triangle"))
618 }
619}
620
621impl Shape<2> {
622 pub fn create(
623 layout: &wgpu::PipelineLayout,
624 shader: &wgpu::ShaderModule,
625 driver: &graphics::Driver,
626 ) -> Box<dyn super::Pipeline> {
627 Box::new(Self::new(layout, shader, driver, "circle"))
628 }
629}
630
631impl Shape<3> {
632 pub fn create(
633 layout: &wgpu::PipelineLayout,
634 shader: &wgpu::ShaderModule,
635 driver: &graphics::Driver,
636 ) -> Box<dyn super::Pipeline> {
637 Box::new(Self::new(layout, shader, driver, "arcs"))
638 }
639}