Skip to main content

zenthra_render/
image_pipeline.rs

1use bytemuck::{Pod, Zeroable};
2
3#[repr(C)]
4#[derive(Copy, Clone, Debug, Pod, Zeroable)]
5pub struct ImageInstance {
6    pub pos: [f32; 2],
7    pub size: [f32; 2],
8    pub radius: [f32; 4],
9    pub border_width: f32,
10    pub border_color: [f32; 4],
11    pub shadow_color: [f32; 4],
12    pub shadow_offset: [f32; 2],
13    pub shadow_blur: f32,
14    pub clip_rect: [f32; 4],
15    pub grayscale: f32,
16    pub brightness: f32,
17    pub opacity: f32,
18    pub uv_rect: [f32; 4],
19    pub bg_color: [f32; 4],
20    pub rotation: [f32; 3], // (x, y, z) in radians
21    pub flip: [f32; 2],     // (horizontal, vertical) - 1.0 for normal, -1.0 for flipped
22}
23
24impl Default for ImageInstance {
25    fn default() -> Self {
26        Self {
27            pos: [0.0; 2],
28            size: [100.0, 100.0],
29            radius: [0.0; 4],
30            border_width: 0.0,
31            border_color: [0.0; 4],
32            shadow_color: [0.0; 4],
33            shadow_offset: [0.0; 2],
34            shadow_blur: 0.0,
35            clip_rect: [0.0, 0.0, 9999.0, 9999.0],
36            grayscale: 0.0,
37            brightness: 1.0,
38            opacity: 1.0,
39            uv_rect: [0.0, 0.0, 1.0, 1.0],
40            bg_color: [0.0; 4],
41            rotation: [0.0; 3],
42            flip: [1.0, 1.0],
43        }
44    }
45}
46
47impl ImageInstance {
48    pub const fn desc() -> wgpu::VertexBufferLayout<'static> {
49        wgpu::VertexBufferLayout {
50            array_stride: std::mem::size_of::<ImageInstance>() as wgpu::BufferAddress,
51            step_mode: wgpu::VertexStepMode::Instance,
52            attributes: &[
53                wgpu::VertexAttribute { offset: 0, shader_location: 0, format: wgpu::VertexFormat::Float32x2 },
54                wgpu::VertexAttribute { offset: 8, shader_location: 1, format: wgpu::VertexFormat::Float32x2 },
55                wgpu::VertexAttribute { offset: 16, shader_location: 2, format: wgpu::VertexFormat::Float32x4 },
56                wgpu::VertexAttribute { offset: 32, shader_location: 3, format: wgpu::VertexFormat::Float32 },
57                wgpu::VertexAttribute { offset: 36, shader_location: 4, format: wgpu::VertexFormat::Float32x4 },
58                wgpu::VertexAttribute { offset: 52, shader_location: 5, format: wgpu::VertexFormat::Float32x4 },
59                wgpu::VertexAttribute { offset: 68, shader_location: 6, format: wgpu::VertexFormat::Float32x2 },
60                wgpu::VertexAttribute { offset: 76, shader_location: 7, format: wgpu::VertexFormat::Float32 },
61                wgpu::VertexAttribute { offset: 80, shader_location: 8, format: wgpu::VertexFormat::Float32x4 },
62                wgpu::VertexAttribute { offset: 96, shader_location: 9, format: wgpu::VertexFormat::Float32 },
63                wgpu::VertexAttribute { offset: 100, shader_location: 10, format: wgpu::VertexFormat::Float32 },
64                wgpu::VertexAttribute { offset: 104, shader_location: 11, format: wgpu::VertexFormat::Float32 },
65                wgpu::VertexAttribute { offset: 108, shader_location: 12, format: wgpu::VertexFormat::Float32x4 },
66                wgpu::VertexAttribute { offset: 124, shader_location: 13, format: wgpu::VertexFormat::Float32x4 },
67                wgpu::VertexAttribute { offset: 140, shader_location: 14, format: wgpu::VertexFormat::Float32x3 },
68                wgpu::VertexAttribute { offset: 152, shader_location: 15, format: wgpu::VertexFormat::Float32x2 },
69            ],
70        }
71    }
72}
73
74#[repr(C)]
75#[derive(Copy, Clone, Pod, Zeroable)]
76struct ScreenUniforms {
77    screen_size: [f32; 2],
78    _padding: [f32; 2],
79}
80
81pub struct ImagePipeline {
82    pipeline: wgpu::RenderPipeline,
83    uniform_buffer: wgpu::Buffer,
84    uniform_bg: wgpu::BindGroup,
85    pub texture_bgl: wgpu::BindGroupLayout,
86}
87
88impl ImagePipeline {
89    pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
90        let shader = device.create_shader_module(wgpu::include_wgsl!("shaders/image.wgsl"));
91
92        let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
93            label: Some("Image Uniform Buffer"),
94            size: std::mem::size_of::<ScreenUniforms>() as u64,
95            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
96            mapped_at_creation: false,
97        });
98
99        let uniform_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
100            label: Some("Image Uniform BGL"),
101            entries: &[wgpu::BindGroupLayoutEntry {
102                binding: 0,
103                visibility: wgpu::ShaderStages::VERTEX,
104                ty: wgpu::BindingType::Buffer {
105                    ty: wgpu::BufferBindingType::Uniform,
106                    has_dynamic_offset: false,
107                    min_binding_size: None,
108                },
109                count: None,
110            }],
111        });
112
113        let uniform_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
114            label: Some("Image Uniform BG"),
115            layout: &uniform_bgl,
116            entries: &[wgpu::BindGroupEntry {
117                binding: 0,
118                resource: uniform_buffer.as_entire_binding(),
119            }],
120        });
121
122        let texture_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
123            label: Some("Image Texture BGL"),
124            entries: &[
125                wgpu::BindGroupLayoutEntry {
126                    binding: 0,
127                    visibility: wgpu::ShaderStages::FRAGMENT,
128                    ty: wgpu::BindingType::Texture {
129                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
130                        view_dimension: wgpu::TextureViewDimension::D2,
131                        multisampled: false,
132                    },
133                    count: None,
134                },
135                wgpu::BindGroupLayoutEntry {
136                    binding: 1,
137                    visibility: wgpu::ShaderStages::FRAGMENT,
138                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
139                    count: None,
140                },
141            ],
142        });
143
144        let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
145            label: Some("Image Pipeline Layout"),
146            bind_group_layouts: &[Some(&uniform_bgl), Some(&texture_bgl)],
147            immediate_size: 0,
148        });
149
150        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
151            label: Some("Image Pipeline"),
152            layout: Some(&layout),
153            vertex: wgpu::VertexState {
154                module: &shader,
155                entry_point: Some("vs_main"),
156                buffers: &[ImageInstance::desc()],
157                compilation_options: Default::default(),
158            },
159            fragment: Some(wgpu::FragmentState {
160                module: &shader,
161                entry_point: Some("fs_main"),
162                targets: &[Some(wgpu::ColorTargetState {
163                    format,
164                    blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
165                    write_mask: wgpu::ColorWrites::ALL,
166                })],
167                compilation_options: Default::default(),
168            }),
169            primitive: wgpu::PrimitiveState::default(),
170            depth_stencil: None,
171            multisample: wgpu::MultisampleState::default(),
172            multiview_mask: None,
173            cache: None,
174        });
175
176        Self {
177            pipeline,
178            uniform_buffer,
179            uniform_bg,
180            texture_bgl,
181        }
182    }
183
184    pub fn prepare(
185        &self,
186        queue: &wgpu::Queue,
187        width: u32,
188        height: u32,
189    ) {
190        queue.write_buffer(
191            &self.uniform_buffer,
192            0,
193            bytemuck::bytes_of(&ScreenUniforms {
194                screen_size: [width as f32, height as f32],
195                _padding: [0.0; 2],
196            }),
197        );
198    }
199
200    pub fn draw<'a>(
201        &'a self,
202        pass: &mut wgpu::RenderPass<'a>,
203        texture_bind_group: &'a wgpu::BindGroup,
204        instance_buffer: &'a wgpu::Buffer,
205        instance_count: u32,
206    ) {
207        if instance_count == 0 {
208            return;
209        }
210        pass.set_pipeline(&self.pipeline);
211        pass.set_bind_group(0, &self.uniform_bg, &[]);
212        pass.set_bind_group(1, texture_bind_group, &[]);
213        pass.set_vertex_buffer(0, instance_buffer.slice(..));
214        pass.draw(0..6, 0..instance_count);
215    }
216}