Skip to main content

kludgine_core/sprite/
pipeline.rs

1use std::marker::PhantomData;
2use std::ops::Deref;
3
4use bytemuck::{Pod, Zeroable};
5use easygpu::prelude::*;
6use easygpu::wgpu::TextureFormat;
7use figures::Vectorlike;
8
9use super::{Normal, Srgb};
10use crate::math::{Angle, Pixels, Point};
11
12/// A pipeline for rendering shapes.
13pub struct Pipeline<T> {
14    core: PipelineCore,
15    _phantom: PhantomData<T>,
16}
17
18#[repr(C)]
19#[derive(Copy, Clone, Pod, Zeroable)]
20/// The uniforms for the shader.
21pub struct Uniforms {
22    /// The orthographic projection matrix
23    pub ortho: [f32; 16],
24    /// The transformation matrix
25    pub transform: [f32; 16],
26}
27#[repr(C)]
28#[derive(Copy, Clone, Debug, Pod, Zeroable)]
29pub struct Vertex {
30    pub position: [f32; 3],
31    pub uv: [f32; 2],
32    pub color: Rgba8,
33    pub alpha: f32,
34}
35
36impl Vertex {
37    pub fn rotate_by(mut self, angle: Option<Angle>, origin: Point<f32, Pixels>) -> Self {
38        if let Some(angle) = angle {
39            let origin = origin.to_vector();
40            let position = Point::new(self.position[0], self.position[1]);
41            let relative_position = position - origin;
42            let rotated = angle.transform_point(relative_position) + origin;
43
44            self.position[0] = rotated.x;
45            self.position[1] = rotated.y;
46        }
47
48        self
49    }
50}
51
52impl<T> Pipeline<T> {
53    pub fn binding(
54        &self,
55        renderer: &Renderer,
56        texture: &Texture,
57        sampler: &Sampler,
58    ) -> BindingGroup {
59        renderer
60            .device
61            .create_binding_group(&self.pipeline.layout.sets[1], &[texture, sampler])
62    }
63}
64
65impl<'a, T> AbstractPipeline<'a> for Pipeline<T>
66where
67    T: VertexShaderSource,
68{
69    type PrepareContext = ScreenTransformation<f32>;
70    type Uniforms = self::Uniforms;
71
72    fn description() -> PipelineDescription<'a> {
73        PipelineDescription {
74            vertex_layout: &[
75                VertexFormat::Float3,
76                VertexFormat::Float2,
77                VertexFormat::UByte4,
78                VertexFormat::Float,
79            ],
80            pipeline_layout: &[
81                Set(&[Binding {
82                    binding: BindingType::UniformBuffer,
83                    stage: ShaderStages::VERTEX,
84                }]),
85                Set(&[
86                    Binding {
87                        binding: BindingType::SampledTexture {
88                            multisampled: false,
89                        },
90                        stage: ShaderStages::FRAGMENT,
91                    },
92                    Binding {
93                        binding: BindingType::Sampler,
94                        stage: ShaderStages::FRAGMENT,
95                    },
96                ]),
97            ],
98            vertex_shader: T::shader(),
99            fragment_shader: include_bytes!("shaders/sprite.frag.spv"),
100        }
101    }
102
103    fn setup(pipeline: easygpu::pipeline::Pipeline, dev: &Device) -> Self {
104        let transform = ScreenTransformation::identity().to_array();
105        let ortho = ScreenTransformation::identity().to_array();
106        let uniforms = dev.create_uniform_buffer(&[self::Uniforms { ortho, transform }]);
107        let bindings = dev.create_binding_group(&pipeline.layout.sets[0], &[&uniforms]);
108
109        Self {
110            core: PipelineCore {
111                pipeline,
112                bindings,
113                uniforms,
114            },
115            _phantom: PhantomData::default(),
116        }
117    }
118
119    fn prepare(
120        &'a self,
121        ortho: ScreenTransformation<f32>,
122    ) -> Option<(&'a UniformBuffer, Vec<self::Uniforms>)> {
123        let ortho = ortho.to_array();
124        let transform = ScreenTransformation::identity().to_array();
125        Some((&self.uniforms, vec![self::Uniforms { ortho, transform }]))
126    }
127}
128
129impl<T> Deref for Pipeline<T> {
130    type Target = PipelineCore;
131
132    fn deref(&self) -> &Self::Target {
133        &self.core
134    }
135}
136
137/// Defines a shader source for sprites.
138pub trait VertexShaderSource {
139    /// The corresponding shader source type in `easygpu_lyon` for shape
140    /// rendering.
141    type Lyon: easygpu_lyon::VertexShaderSource + Send + Sync;
142
143    /// The shader executable.
144    #[must_use]
145    fn shader() -> &'static [u8];
146
147    /// The texture format expected.
148    #[must_use]
149    fn texture_format() -> TextureFormat;
150
151    /// The sampler format expected.
152    #[must_use]
153    fn sampler_format() -> TextureFormat {
154        <Self::Lyon as easygpu_lyon::VertexShaderSource>::sampler_format()
155    }
156}
157
158impl VertexShaderSource for Srgb {
159    type Lyon = easygpu_lyon::Srgb;
160
161    fn shader() -> &'static [u8] {
162        include_bytes!("shaders/sprite-srgb.vert.spv")
163    }
164
165    fn texture_format() -> TextureFormat {
166        TextureFormat::Rgba8UnormSrgb
167    }
168}
169
170impl VertexShaderSource for Normal {
171    type Lyon = easygpu_lyon::Normal;
172
173    fn shader() -> &'static [u8] {
174        include_bytes!("shaders/sprite.vert.spv")
175    }
176
177    fn texture_format() -> TextureFormat {
178        TextureFormat::Rgba8Unorm
179    }
180}