1use crate::*;
2
3pub fn run_batched_render_passes(
4 c: &mut WgpuRenderer,
5 surface_view: &wgpu::TextureView,
6 params: DrawParams,
7 sprite_shader_id: ShaderId,
8 error_shader_id: ShaderId,
9) {
10 span_with_timing!("run_batched_render_passes");
11
12 let mut is_first = true;
13
14 let queues = consume_render_queues();
15
16 for (key, mut meshes) in
37 queues.into_iter().sorted_by_key(|(k, _)| k.z_index)
38 {
39 let _span = span!("blend/shader/target group");
40
41 if get_y_sort(key.z_index) {
43 meshes.sort_by_key(|mesh| {
44 OrderedFloat::<f32>(-(mesh.origin.y + mesh.y_sort_offset))
45 });
46 }
47
48 perf_counter_inc("render passes", 1);
49 perf_counter_inc("meshes", meshes.len() as u64);
50
51 render_meshes(
52 c,
53 is_first,
54 params.clear_color,
55 MeshDrawData {
56 blend_mode: key.blend_mode,
57 texture: key.texture_id,
58 shader: key.shader,
59 render_target: key.render_target,
60 data: meshes,
61 },
62 surface_view,
63 sprite_shader_id,
64 error_shader_id,
65 );
66
67
68 is_first = false;
69 }
70
71 {
72 span_with_timing!("prepare_particles");
73
74 for (key, queue) in params.particle_queues.into_iter() {
75 render_particles(
76 c,
77 is_first,
78 ParticleDrawData {
79 blend_mode: key.blend_mode,
80 texture: key.texture_id,
81 data: queue,
82 },
83 params.clear_color,
84 surface_view,
85 sprite_shader_id,
86 );
87
88 perf_counter("particle draws", 1);
89 is_first = false;
90 }
91
92 }
128
129 if is_first {
130 render_meshes(
131 c,
132 is_first,
133 params.clear_color,
134 MeshDrawData {
135 blend_mode: BlendMode::Alpha,
136 texture: TextureHandle::from_path("1px"),
137 shader: ShaderInstanceId::default(),
138 render_target: RenderTargetId::default(),
139 data: Default::default(),
140 },
141 surface_view,
142 sprite_shader_id,
143 error_shader_id,
144 );
145
146 }
162}
163
164pub fn render_meshes(
166 c: &mut WgpuRenderer,
167 is_first: bool,
168 clear_color: Color,
169 pass_data: MeshDrawData,
170 surface_view: &wgpu::TextureView,
171 sprite_shader_id: ShaderId,
172 _error_shader_id: ShaderId,
173) {
174 let _span = span!("render_meshes");
175
176 let pipeline_name = ensure_pipeline_exists(c, &pass_data, sprite_shader_id);
177
178 perf_counter_inc("batch-count", 1);
179
180 let tex_handle = pass_data.texture;
181 let _span = span!("texture");
182
183 let mut all_vertices: Vec<SpriteVertex> = vec![];
184 let mut all_indices = vec![];
185
186 for mesh in pass_data.data.into_iter() {
187 let offset = all_vertices.len() as u32;
188 all_vertices.extend(&mesh.vertices);
189 all_indices.extend(mesh.indices.iter().map(|x| *x + offset));
190 }
191
192 c.vertex_buffer.ensure_size_and_copy(
193 &c.context.device,
194 &c.context.queue,
195 bytemuck::cast_slice(all_vertices.as_slice()),
196 );
197
198 c.index_buffer.ensure_size_and_copy(
199 &c.context.device,
200 &c.context.queue,
201 bytemuck::cast_slice(all_indices.as_slice()),
202 );
203
204 let textures = c.textures.lock();
205 let render_targets = c.render_targets.borrow();
206
207 let mut encoder = c.context.device.simple_encoder("Mesh Render Encoder");
208
209 {
210 let clear_color = if is_first { Some(clear_color) } else { None };
211
212 let target_view = if pass_data.render_target.0 > 0 {
213 &render_targets
214 .get(&pass_data.render_target)
215 .expect("user render target must exist when used")
216 .view
217 } else if c.post_processing_effects.borrow().iter().any(|x| x.enabled) {
218 &c.first_pass_texture.texture.view
219 } else {
220 surface_view
221 };
222
223 let mut render_pass =
224 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
225 label: Some("Mesh Render Pass"),
226 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
227 view: target_view,
228 resolve_target: None,
229 ops: wgpu::Operations {
230 load: color_to_clear_op(clear_color),
231 store: wgpu::StoreOp::Store,
232 },
233 })],
234 depth_stencil_attachment: depth_stencil_attachment(
235 c.enable_z_buffer,
236 &c.depth_texture.view,
237 is_first,
238 ),
239 timestamp_writes: None,
240 occlusion_query_set: None,
241 });
242
243 let mesh_pipeline = c
244 .user_pipelines
245 .get(&pipeline_name)
246 .map(RenderPipeline::User)
247 .or_else(|| {
248 c.pipelines.get(&pipeline_name).map(RenderPipeline::Wgpu)
249 })
250 .expect("ensured pipeline must exist within the same frame");
251
252
253 match &mesh_pipeline {
254 RenderPipeline::User(pipeline) => {
255 render_pass.set_pipeline(&pipeline.pipeline);
256 }
257 RenderPipeline::Wgpu(pipeline) => {
258 render_pass.set_pipeline(pipeline);
259 }
260 }
261
262 render_pass.set_vertex_buffer(0, c.vertex_buffer.buffer.slice(..));
263
264 if !all_indices.is_empty() {
265 render_pass.set_index_buffer(
266 c.index_buffer.buffer.slice(..),
267 wgpu::IndexFormat::Uint32,
268 );
269 }
270
271 let tex_bind_group = match tex_handle {
272 TextureHandle::RenderTarget(render_target_id) => {
273 &render_targets.get(&render_target_id).unwrap().bind_group
274 }
275 _ => {
276 &textures
277 .get(&tex_handle)
278 .unwrap_or_else(|| {
279 textures
280 .get(&texture_id("error"))
281 .expect("error texture must exist")
282 })
283 .bind_group
284 }
285 };
286
287 render_pass.set_bind_group(0, tex_bind_group, &[]);
288 render_pass.set_bind_group(1, &c.camera_bind_group, &[]);
289
290 match &mesh_pipeline {
291 RenderPipeline::User(pipeline) => {
292 render_pass.set_bind_group(2, &pipeline.bind_group, &[]);
293 }
294 RenderPipeline::Wgpu(_) => {}
295 }
296
297 if all_indices.is_empty() {
298 render_pass.draw(0..all_vertices.len() as u32, 0..1);
299 } else {
300 render_pass.draw_indexed(0..all_indices.len() as u32, 0, 0..1);
301 }
302 }
303
304 c.context.queue.submit(std::iter::once(encoder.finish()));
305}
306
307pub fn render_particles(
308 c: &mut WgpuRenderer,
309 is_first: bool,
310 pass_data: ParticleDrawData,
311 clear_color: Color,
312 surface_view: &wgpu::TextureView,
313 sprite_shader_id: ShaderId,
314) {
315 let _span = span!("render_particles");
316
317 let target_view =
318 if c.post_processing_effects.borrow().iter().any(|x| x.enabled) {
319 &c.first_pass_texture.texture.view
320 } else {
321 surface_view
322 };
323
324 let textures = c.textures.lock();
325
326 let particle_pipeline = {
327 let name = format!(
328 "Particle {:?} {:?}",
329 pass_data.blend_mode, c.enable_z_buffer
330 );
331
332 c.pipelines.entry(name.clone()).or_insert_with(|| {
333 create_render_pipeline_with_layout(
334 &name,
335 &c.context.device,
336 wgpu::TextureFormat::Rgba16Float,
338 &[&c.texture_layout, &c.camera_bind_group_layout],
339 &[SpriteVertex::desc()],
340 &c.shaders.borrow().get(sprite_shader_id).unwrap().clone(),
341 pass_data.blend_mode,
342 c.enable_z_buffer,
343 )
344 .expect("particle pipeline creation failed")
345 })
346 };
347
348 let mut all_vertices: Vec<SpriteVertex> = vec![];
349 let mut all_indices: Vec<u32> = vec![];
350
351 for draw in pass_data.data {
352 let size = draw.size;
353
354 let tex_size = ASSETS
355 .borrow()
356 .texture_image_map
357 .lock()
358 .get(&pass_data.texture)
359 .map(|image| vec2(image.width() as f32, image.height() as f32))
360 .unwrap_or(Vec2::ONE);
361
362 let tex_width = tex_size.x;
363 let tex_height = tex_size.y;
364
365 let vertices = rotated_rectangle(
366 draw.position,
368 RawDrawParams {
369 dest_size: Some(size),
370 rotation: draw.rotation,
371 source_rect: draw.source_rect,
372 ..Default::default()
373 },
374 tex_width,
375 tex_height,
376 draw.color,
377 Vec2::ZERO,
379 );
380
381 let len = all_vertices.len() as u32;
382 all_indices.extend_from_slice(&[
383 len,
384 2 + len,
385 1 + len,
386 len,
387 3 + len,
388 2 + len,
389 ]);
390
391 all_vertices.extend(vertices);
392 }
393
394 let mut encoder =
395 c.context.device.simple_encoder("Particle Render Encoder");
396
397 c.vertex_buffer.ensure_size_and_copy(
398 &c.context.device,
399 &c.context.queue,
400 bytemuck::cast_slice(all_vertices.as_slice()),
401 );
402
403 c.index_buffer.ensure_size_and_copy(
404 &c.context.device,
405 &c.context.queue,
406 bytemuck::cast_slice(all_indices.as_slice()),
407 );
408
409 {
410 let clear_color = if is_first { Some(clear_color) } else { None };
411
412 let mut render_pass =
413 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
414 label: Some("Particle Render Pass"),
415 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
416 view: target_view,
417 resolve_target: None,
418 ops: wgpu::Operations {
419 load: color_to_clear_op(clear_color),
420 store: wgpu::StoreOp::Store,
421 },
422 })],
423 depth_stencil_attachment: depth_stencil_attachment(
434 c.enable_z_buffer,
435 &c.depth_texture.view,
436 is_first,
437 ),
438 timestamp_writes: None,
439 occlusion_query_set: None,
440 });
441
442 render_pass.set_pipeline(particle_pipeline);
443 render_pass.set_vertex_buffer(0, c.vertex_buffer.buffer.slice(..));
444
445 if !all_indices.is_empty() {
446 render_pass.set_index_buffer(
447 c.index_buffer.buffer.slice(..),
448 wgpu::IndexFormat::Uint32,
449 );
450 }
451
452 let tex_bind_group = &textures
453 .get(&pass_data.texture)
454 .unwrap_or_else(|| textures.get(&texture_id("error")).unwrap())
455 .bind_group;
456
457 render_pass.set_bind_group(0, tex_bind_group, &[]);
458 render_pass.set_bind_group(1, &c.camera_bind_group, &[]);
459
460 if all_indices.is_empty() {
461 render_pass.draw(0..all_vertices.len() as u32, 0..1);
462 } else {
463 render_pass.draw_indexed(0..all_indices.len() as u32, 0, 0..1);
464 }
465 }
466
467 c.context.queue.submit(std::iter::once(encoder.finish()));
468}