1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::error::Error;

use crate::math::{PhysicalSizeI32, Point, ScaleFactor};

pub type PrimitiveID = u32;

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct CustomPrimitive {
    pub id: PrimitiveID,
    pub offset: Point,
    pub pipeline_index: u8,
}

pub trait CustomPipeline {
    fn needs_preparing(&self) -> bool;

    fn prepare(
        &mut self,
        device: &wgpu::Device,
        queue: &wgpu::Queue,
        screen_size: PhysicalSizeI32,
        scale_factor: ScaleFactor,
        primitives: &[QueuedCustomPrimitive],
    ) -> Result<(), Box<dyn Error>>;

    fn render_primitives<'pass>(
        &'pass self,
        primitives: &[QueuedCustomPrimitive],
        render_pass: &mut wgpu::RenderPass<'pass>,
    ) -> Result<(), Box<dyn Error>>;
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct QueuedCustomPrimitive {
    pub id: PrimitiveID,
    pub offset: Point,
}

/// A default shader uniform struct containing the scale factor and a scaling vector
/// used to convert from screen space to clip space.
#[repr(C)]
#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
pub struct DefaultConstantUniforms {
    pub screen_to_clip_scale: [f32; 2],
    pub scale_factor: f32,
    pub _padding: f32,
}

impl DefaultConstantUniforms {
    pub fn new(screen_size: PhysicalSizeI32, scale_factor: ScaleFactor) -> Self {
        Self {
            screen_to_clip_scale: crate::math::screen_to_clip_scale(screen_size, scale_factor),
            scale_factor: scale_factor.0,
            _padding: 0.0,
        }
    }

    pub fn layout_buffer_and_bind_group(
        device: &wgpu::Device,
    ) -> (wgpu::BindGroupLayout, wgpu::Buffer, wgpu::BindGroup) {
        let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
            label: Some("rootvg-core constants layout"),
            entries: &[Self::entry(0)],
        });

        let buffer = device.create_buffer(&wgpu::BufferDescriptor {
            label: Some("rootvg-core constants buffer"),
            size: std::mem::size_of::<Self>() as wgpu::BufferAddress,
            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
            mapped_at_creation: false,
        });

        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
            label: Some("rootvg-core constants bind group"),
            layout: &layout,
            entries: &[wgpu::BindGroupEntry {
                binding: 0,
                resource: buffer.as_entire_binding(),
            }],
        });

        (layout, buffer, bind_group)
    }

    pub fn prepare_buffer(
        buffer: &wgpu::Buffer,
        screen_size: PhysicalSizeI32,
        scale_factor: ScaleFactor,
        queue: &wgpu::Queue,
    ) {
        let uniforms = Self::new(screen_size, scale_factor);
        queue.write_buffer(buffer, 0, bytemuck::bytes_of(&uniforms));
    }

    pub fn entry(binding: u32) -> wgpu::BindGroupLayoutEntry {
        wgpu::BindGroupLayoutEntry {
            binding,
            visibility: wgpu::ShaderStages::VERTEX,
            ty: wgpu::BindingType::Buffer {
                ty: wgpu::BufferBindingType::Uniform,
                has_dynamic_offset: false,
                min_binding_size: wgpu::BufferSize::new(
                    std::mem::size_of::<Self>() as wgpu::BufferAddress
                ),
            },
            count: None,
        }
    }
}