kludgine_core/sprite/
pipeline.rs1use 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
12pub struct Pipeline<T> {
14 core: PipelineCore,
15 _phantom: PhantomData<T>,
16}
17
18#[repr(C)]
19#[derive(Copy, Clone, Pod, Zeroable)]
20pub struct Uniforms {
22 pub ortho: [f32; 16],
24 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
137pub trait VertexShaderSource {
139 type Lyon: easygpu_lyon::VertexShaderSource + Send + Sync;
142
143 #[must_use]
145 fn shader() -> &'static [u8];
146
147 #[must_use]
149 fn texture_format() -> TextureFormat;
150
151 #[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}