1use crate::*;
2
3pub const FRAG_SHADER_PREFIX: &str = include_str!(concat!(
4 env!("CARGO_MANIFEST_DIR"),
5 "/shaders/frag-shader-prefix.wgsl"
6));
7
8pub const CAMERA_BIND_GROUP_PREFIX: &str = include_str!(concat!(
9 env!("CARGO_MANIFEST_DIR"),
10 "/shaders/camera-bind-group.wgsl"
11));
12
13pub const SHADER_POST_PROCESSING_VERTEX: &str = include_str!(concat!(
14 env!("CARGO_MANIFEST_DIR"),
15 "/shaders/post_processing_vertex.wgsl"
16));
17
18pub const COPY_SHADER_SRC: &str =
19 include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/copy.wgsl"));
20
21#[macro_export]
22macro_rules! engine_shader_source {
23 ($name:literal) => {{
24 let shader = include_str!(concat!(
25 env!("CARGO_MANIFEST_DIR"),
26 "/shaders/",
27 $name,
28 ".wgsl"
29 ));
30
31 shader
32 }};
33}
34
35pub fn sprite_shader_from_fragment(source: &str) -> String {
36 format!("{}{}{}", CAMERA_BIND_GROUP_PREFIX, FRAG_SHADER_PREFIX, source)
37}
38
39pub fn post_process_shader_from_fragment(source: &str) -> String {
40 format!(
41 "{}{}{}",
42 CAMERA_BIND_GROUP_PREFIX, SHADER_POST_PROCESSING_VERTEX, source
43 )
44}
45
46#[macro_export]
47macro_rules! create_engine_post_processing_shader {
48 ($shaders:expr, $name:literal) => {{
49 let full_shader =
50 post_process_shader_from_fragment(engine_shader_source!($name));
51
52 let shader_id =
53 create_shader($shaders, $name, &full_shader, HashMap::new())
54 .unwrap();
55
56 $shaders.get(shader_id).unwrap().clone()
57 }};
58}
59
60pub fn load_texture_from_engine_bytes(
61 context: &GraphicsContext,
62 name: &str,
63 bytes: &[u8],
64 textures: &mut TextureMap,
65 address_mode: wgpu::AddressMode,
66) {
67 let img = image::load_from_memory(bytes).expect("must be valid image");
68 let texture = Texture::from_image_ex(
69 &context.device,
70 &context.queue,
71 &img,
72 Some(name),
73 false,
74 address_mode,
75 )
76 .unwrap();
77
78 load_texture_with_image(context, name, img, texture, textures);
79}
80
81pub fn load_texture_with_image(
87 context: &GraphicsContext,
88 name: &str,
89 img: DynamicImage,
90 texture: Texture,
91 textures: &mut TextureMap,
92) {
93 let handle = texture_path(name);
94
95 let bind_group = context.device.simple_bind_group(
96 Some(&format!("{}_bind_group", name)),
97 &texture,
98 &context.texture_layout,
99 );
100
101 ASSETS.borrow_mut().insert_handle(name, handle);
102 ASSETS
103 .borrow_mut()
104 .texture_image_map
105 .lock()
106 .insert(handle, Arc::new(img.to_rgba8()));
107 textures.insert(handle, BindableTexture { bind_group, texture });
108}
109
110pub struct MipmapGenerator {
111 pub format: wgpu::TextureFormat,
112 pub blit_pipeline: wgpu::RenderPipeline,
113 pub blit_layout: wgpu::BindGroupLayout,
114}
115
116impl MipmapGenerator {
117 pub fn new(device: &wgpu::Device, format: wgpu::TextureFormat) -> Self {
118 let blit_pipeline = {
119 let shader =
121 device.create_shader_module(wgpu::ShaderModuleDescriptor {
122 label: Some("Blit Shader"),
123 source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(
124 engine_shader_source!("blit"),
125 )),
126 });
127
128 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
129 label: Some("Blit Render Pipeline"),
130 layout: None,
131 vertex: wgpu::VertexState {
132 module: &shader,
133 entry_point: "vs_main",
134 buffers: &[],
135 },
136 fragment: Some(wgpu::FragmentState {
137 module: &shader,
138 entry_point: "fs_main",
139 targets: &[Some(format.into())],
140 }),
141 primitive: wgpu::PrimitiveState {
142 topology: wgpu::PrimitiveTopology::TriangleList,
143 ..Default::default()
144 },
145 depth_stencil: None,
146 multisample: wgpu::MultisampleState::default(),
147 multiview: None,
148 })
149 };
150
151 let blit_layout = blit_pipeline.get_bind_group_layout(0);
152
153 Self { format, blit_pipeline, blit_layout }
154 }
155
156 pub fn generate_mipmaps(
157 &self,
158 encoder: &mut wgpu::CommandEncoder,
159 device: &wgpu::Device,
160 texture: &wgpu::Texture,
161 mip_count: u32,
162 ) {
163 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
164 label: Some("Mip Sampler"),
165 address_mode_u: wgpu::AddressMode::ClampToEdge,
166 address_mode_v: wgpu::AddressMode::ClampToEdge,
167 address_mode_w: wgpu::AddressMode::ClampToEdge,
168 mag_filter: wgpu::FilterMode::Linear,
169 min_filter: wgpu::FilterMode::Linear,
170 mipmap_filter: wgpu::FilterMode::Nearest,
171 ..Default::default()
172 });
173
174 let views = (0..mip_count)
175 .map(|mip| {
176 texture.create_view(&wgpu::TextureViewDescriptor {
177 label: Some("Mip View"),
178 format: None,
179 dimension: None,
180 aspect: wgpu::TextureAspect::All,
181 base_mip_level: mip,
182 mip_level_count: Some(1),
183 base_array_layer: 0,
184 array_layer_count: None,
185 })
186 })
187 .collect::<Vec<_>>();
188
189 for target_mip in 1..mip_count as usize {
190 let bind_group =
191 device.create_bind_group(&wgpu::BindGroupDescriptor {
192 layout: &self.blit_layout,
193 entries: &[
194 wgpu::BindGroupEntry {
195 binding: 0,
196 resource: wgpu::BindingResource::TextureView(
197 &views[target_mip - 1],
198 ),
199 },
200 wgpu::BindGroupEntry {
201 binding: 1,
202 resource: wgpu::BindingResource::Sampler(&sampler),
203 },
204 ],
205 label: None,
206 });
207
208 let mut rpass =
209 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
210 label: None,
211 color_attachments: &[Some(
212 wgpu::RenderPassColorAttachment {
213 view: &views[target_mip],
214 resolve_target: None,
215 ops: wgpu::Operations {
216 load: wgpu::LoadOp::Clear(wgpu::Color::WHITE),
217 store: wgpu::StoreOp::Store,
218 },
219 },
220 )],
221 depth_stencil_attachment: None,
222 timestamp_writes: None,
223 occlusion_query_set: None,
224 });
225
226 rpass.set_pipeline(&self.blit_pipeline);
227 rpass.set_bind_group(0, &bind_group, &[]);
228 rpass.draw(0..3, 0..1);
229 }
230 }
231}