1use std::sync::mpsc::{channel, Receiver, Sender};
2
3use crate::*;
4
5use image::Rgba;
6
7pub enum RenderPipeline<'a> {
8 User(&'a UserRenderPipeline),
9 Wgpu(&'a wgpu::RenderPipeline),
10}
11
12pub struct UserRenderPipeline {
13 pub pipeline: wgpu::RenderPipeline,
14 pub layout: wgpu::BindGroupLayout,
15 pub bind_group: wgpu::BindGroup,
16 pub buffers: HashMap<String, wgpu::Buffer>,
17}
18
19pub type PipelineMap = HashMap<String, wgpu::RenderPipeline>;
20pub type UserPipelineMap = HashMap<String, UserRenderPipeline>;
21pub type TextureMap = HashMap<TextureHandle, BindableTexture>;
22pub type RenderTargetMap = HashMap<RenderTargetId, UserRenderTarget>;
23
24#[repr(C)]
25#[derive(Copy, Clone, Debug, Default, bytemuck::Pod, bytemuck::Zeroable)]
26pub struct QuadUniform {
27 pub clip_position: [f32; 2],
28 pub size: [f32; 2],
29}
30
31pub fn shader_to_wgpu(shader: &Shader) -> wgpu::ShaderModuleDescriptor<'_> {
32 wgpu::ShaderModuleDescriptor {
33 label: Some(&shader.name),
34 source: wgpu::ShaderSource::Wgsl(shader.source.as_str().into()),
35 }
36}
37
38#[derive(Clone)]
40pub struct GraphicsContext {
41 pub surface: Arc<wgpu::Surface<'static>>,
42 pub instance: Arc<wgpu::Instance>,
43 pub adapter: Arc<wgpu::Adapter>,
44 pub device: Arc<wgpu::Device>,
45 pub queue: Arc<wgpu::Queue>,
46 pub texture_layout: Arc<wgpu::BindGroupLayout>,
48 pub config: Arc<AtomicRefCell<wgpu::SurfaceConfiguration>>,
49 pub texture_creator: Arc<AtomicRefCell<WgpuTextureCreator>>,
51 pub textures: Arc<Mutex<TextureMap>>,
53}
54
55pub struct WgpuRenderer {
56 pub context: GraphicsContext,
57
58 pub pipelines: PipelineMap,
59 pub user_pipelines: UserPipelineMap,
60 pub shaders: RefCell<ShaderMap>,
61 pub render_targets: RefCell<RenderTargetMap>,
62
63 pub text: RefCell<TextRasterizer>,
64
65 pub egui_winit: egui_winit::State,
66 pub egui_render_routine: RefCell<EguiRenderRoutine>,
67
68 pub screenshot_buffer: SizedBuffer,
69
70 pub vertex_buffer: SizedBuffer,
71 pub index_buffer: SizedBuffer,
72
73 pub quad_ubg: UniformBindGroup,
74
75 pub texture_layout: Arc<wgpu::BindGroupLayout>,
76
77 pub window: &'static Window,
78
79 pub depth_texture: Arc<Texture>,
80
81 pub first_pass_texture: BindableTexture,
82
83 pub lights_buffer: wgpu::Buffer,
84 pub global_lighting_params_buffer: wgpu::Buffer,
85
86 pub bloom: Bloom,
87 pub post_processing_effects: RefCell<Vec<PostProcessingEffect>>,
88
89 pub render_texture_format: wgpu::TextureFormat,
90
91 pub tonemapping_texture: BindableTexture,
92
93 pub camera_uniform: CameraUniform,
94 pub camera_buffer: wgpu::Buffer,
95 pub camera_bind_group: Arc<wgpu::BindGroup>,
96 pub camera_bind_group_layout: wgpu::BindGroupLayout,
97
98 pub color: Color,
99
100 pub enable_z_buffer: bool,
101
102 pub texture_creator: Arc<AtomicRefCell<WgpuTextureCreator>>,
104
105 #[cfg(not(target_arch = "wasm32"))]
106 pub thread_pool: rayon::ThreadPool,
107 pub loaded_image_recv: Receiver<LoadedImage>,
108 pub loaded_image_send: Sender<LoadedImage>,
109
110 pub textures: Arc<Mutex<TextureMap>>,
111
112 pub sprite_shader_id: ShaderId,
113 pub error_shader_id: ShaderId,
114
115 pub screenshot_params: ScreenshotParams,
116 pub screenshot_history_buffer: VecDeque<ScreenshotItem>,
117}
118
119impl WgpuRenderer {
120 pub async fn new(
121 window: &'static Window,
122 egui_winit: egui_winit::State,
123 ) -> Self {
124 let context = create_graphics_context(window).await;
125
126 trace!("Loading builtin engine textures");
127
128 {
129 let textures = &mut context.textures.lock();
130
131 macro_rules! load_engine_tex {
132 ($name: literal) => {{
133 load_texture_from_engine_bytes(
134 &context,
135 $name,
136 include_bytes!(concat!(
137 env!("CARGO_MANIFEST_DIR"),
138 "/assets/",
139 $name,
140 ".png"
141 )),
142 textures,
143 wgpu::AddressMode::Repeat,
144 );
145 }};
146 }
147
148 load_engine_tex!("error");
149 load_engine_tex!("1px");
150 load_engine_tex!("test-grid");
151 load_engine_tex!("_builtin-comfy");
152 }
153
154 let mut camera_uniform = CameraUniform::new();
155 camera_uniform.update_view_proj(&main_camera());
156
157 let camera_buffer = context.device.create_buffer_init(
158 &wgpu::util::BufferInitDescriptor {
159 label: Some("Camera Buffer"),
160 contents: bytemuck::cast_slice(&[camera_uniform]),
161 usage: wgpu::BufferUsages::UNIFORM |
162 wgpu::BufferUsages::COPY_DST,
163 },
164 );
165
166 let camera_bind_group_layout = context.device.create_bind_group_layout(
167 &wgpu::BindGroupLayoutDescriptor {
168 entries: &[
169 wgpu::BindGroupLayoutEntry {
170 binding: 0,
171 visibility: wgpu::ShaderStages::VERTEX |
172 wgpu::ShaderStages::FRAGMENT,
173 ty: wgpu::BindingType::Buffer {
174 ty: wgpu::BufferBindingType::Uniform,
175 has_dynamic_offset: false,
176 min_binding_size: None,
177 },
178 count: None,
179 },
180 wgpu::BindGroupLayoutEntry {
181 binding: 1,
182 visibility: wgpu::ShaderStages::VERTEX |
183 wgpu::ShaderStages::FRAGMENT,
184 ty: wgpu::BindingType::Buffer {
185 ty: wgpu::BufferBindingType::Uniform,
186 has_dynamic_offset: false,
187 min_binding_size: None,
188 },
189 count: None,
190 },
191 wgpu::BindGroupLayoutEntry {
192 binding: 2,
193 visibility: wgpu::ShaderStages::FRAGMENT,
194 ty: wgpu::BindingType::Buffer {
195 ty: wgpu::BufferBindingType::Uniform,
196 has_dynamic_offset: false,
197 min_binding_size: None,
198 },
199 count: None,
200 },
201 wgpu::BindGroupLayoutEntry {
202 binding: 3,
203 visibility: wgpu::ShaderStages::FRAGMENT,
204 ty: wgpu::BindingType::Texture {
205 sample_type: wgpu::TextureSampleType::Float {
206 filterable: true,
207 },
208 view_dimension: wgpu::TextureViewDimension::D2,
209 multisampled: false,
210 },
211 count: None,
212 },
213 wgpu::BindGroupLayoutEntry {
214 binding: 4,
215 visibility: wgpu::ShaderStages::FRAGMENT,
216 ty: wgpu::BindingType::Sampler(
217 wgpu::SamplerBindingType::Filtering,
218 ),
219 count: None,
220 },
221 ],
222 label: Some("camera_bind_group_layout"),
223 },
224 );
225
226 let lights_buffer = context.device.create_buffer_init(
227 &wgpu::util::BufferInitDescriptor {
228 label: Some("Lights Buffer"),
229 contents: bytemuck::cast_slice(&[LightUniform::default()]),
230 usage: wgpu::BufferUsages::UNIFORM |
231 wgpu::BufferUsages::COPY_DST,
232 },
233 );
234
235 let global_lighting_params_buffer = context.device.create_buffer_init(
236 &wgpu::util::BufferInitDescriptor {
237 label: Some("Global Lighting Params Buffer"),
238 contents: bytemuck::cast_slice(&[
239 GlobalLightingParams::default(),
240 ]),
241 usage: wgpu::BufferUsages::UNIFORM |
242 wgpu::BufferUsages::COPY_DST,
243 },
244 );
245
246 let lut_dim = 2;
247
248 let color_lut_texture = Texture::create_uninit(
249 &context.device,
250 lut_dim,
251 lut_dim,
252 Some("LUT"),
253 )
254 .unwrap();
255
256 let lut_image =
257 image::ImageBuffer::from_fn(lut_dim, lut_dim, |x, y| {
258 if x == 0 && y == 0 {
259 Rgba([255, 0, 0, 255]) } else if x == 1 && y == 0 {
261 Rgba([0, 255, 0, 255]) } else if x == 0 && y == 1 {
263 Rgba([0, 0, 255, 255]) } else {
265 Rgba([255, 255, 255, 255]) }
267 });
268
269 let lut_width = lut_image.width();
270 let lut_height = lut_image.height();
271
272 let color_lut_data: Vec<f32> = lut_image
273 .pixels()
274 .flat_map(|rgba| {
275 let r = rgba[0] as f32 / 255.0;
276 let g = rgba[1] as f32 / 255.0;
277 let b = rgba[2] as f32 / 255.0;
278 let a = rgba[3] as f32 / 255.0;
279 vec![r, g, b, a]
280 })
281 .collect();
282
283 context.queue.write_texture(
284 wgpu::ImageCopyTexture {
285 texture: &color_lut_texture.texture,
286 mip_level: 0,
287 origin: wgpu::Origin3d::ZERO,
288 aspect: wgpu::TextureAspect::All,
289 },
290 bytemuck::cast_slice(color_lut_data.as_slice()),
291 wgpu::ImageDataLayout {
292 offset: 0,
293 bytes_per_row: Some(
294 lut_width * 4 * std::mem::size_of::<f32>() as u32,
295 ),
296 rows_per_image: None,
297 },
298 wgpu::Extent3d {
299 width: lut_width,
300 height: lut_height,
301 depth_or_array_layers: 1,
302 },
303 );
304
305 let camera_bind_group =
306 context.device.create_bind_group(&wgpu::BindGroupDescriptor {
307 layout: &camera_bind_group_layout,
308 entries: &[
309 wgpu::BindGroupEntry {
310 binding: 0,
311 resource: camera_buffer.as_entire_binding(),
312 },
313 wgpu::BindGroupEntry {
314 binding: 1,
315 resource: lights_buffer.as_entire_binding(),
316 },
317 wgpu::BindGroupEntry {
318 binding: 2,
319 resource: global_lighting_params_buffer
320 .as_entire_binding(),
321 },
322 wgpu::BindGroupEntry {
323 binding: 3,
324 resource: wgpu::BindingResource::TextureView(
325 &color_lut_texture.view,
326 ),
327 },
328 wgpu::BindGroupEntry {
329 binding: 4,
330 resource: wgpu::BindingResource::Sampler(
331 &color_lut_texture.sampler,
332 ),
333 },
334 ],
335 label: Some("camera_bind_group"),
336 });
337
338 let camera_bind_group = Arc::new(camera_bind_group);
339
340 trace!("Initializing egui");
352
353 let (width, height, format) = {
354 let config = context.config.borrow();
355 (config.width, config.height, config.format)
356 };
357
358 let scale_factor = game_config()
359 .scale_factor_override
360 .unwrap_or(window.scale_factor() as f32);
361
362 let egui_render_routine = EguiRenderRoutine::new(
363 &context.device,
364 format,
365 1,
366 width,
367 height,
368 scale_factor,
369 );
370
371 let screenshot_buffer = SizedBuffer::new(
372 "screenshot_buffer",
373 &context.device,
374 (width * height) as usize * std::mem::size_of::<u32>(),
375 BufferType::Read,
376 );
377
378 info!("Initializing with scale factor: {}", window.scale_factor());
379
380 BLOOD_CANVAS
381 .set(AtomicRefCell::new(BloodCanvas::new(
382 context.texture_creator.clone(),
383 )))
384 .expect("failed to create glow blood canvas");
385
386 let (width, height) = {
387 let config = context.config.borrow();
388 (config.width, config.height)
389 };
390
391 let first_pass_texture = BindableTexture::new(
392 &context.device,
393 &context.texture_layout,
394 &TextureCreationParams {
395 label: Some("First Pass Texture"),
396 width,
397 height,
398 ..Default::default()
399 },
400 );
401
402 let tonemapping_texture = BindableTexture::new(
403 &context.device,
404 &context.texture_layout,
405 &TextureCreationParams {
406 label: Some("Tonemapping"),
407 width,
408 height,
409 ..Default::default()
410 },
411 );
412
413 let quad = QuadUniform { clip_position: [0.0, 0.0], size: [0.2, 0.2] };
414
415 let quad_ubg = UniformBindGroup::simple(
416 "Debug Quad",
417 &context.device,
418 bytemuck::cast_slice(&[quad]),
419 );
420
421 let render_texture_format = wgpu::TextureFormat::Rgba16Float;
422
423 let mut shaders = ShaderMap::new();
424
425 let bloom = Bloom::new(
426 &context,
427 &mut shaders,
428 render_texture_format,
429 camera_bind_group.clone(),
430 &camera_bind_group_layout,
431 );
432
433 let vertex_buffer = SizedBuffer::new(
434 "Mesh Vertex Buffer",
435 &context.device,
436 1024 * 1024,
437 BufferType::Vertex,
438 );
439
440 let index_buffer = SizedBuffer::new(
441 "Mesh Index Buffer",
442 &context.device,
443 1024 * 1024,
444 BufferType::Index,
445 );
446
447
448 let (tx_texture, rx_texture) = channel::<LoadedImage>();
449
450 let depth_texture = Texture::create_depth_texture(
453 &context.device,
454 &context.config.borrow(),
455 "Depth Texture",
456 );
457
458 let sprite_shader_id = create_shader(
459 &mut shaders,
460 "sprite",
461 &sprite_shader_from_fragment(engine_shader_source!("sprite")),
462 HashMap::new(),
463 )
464 .unwrap();
465
466 let error_shader_id = create_shader(
467 &mut shaders,
468 "error",
469 &sprite_shader_from_fragment(engine_shader_source!("error")),
470 HashMap::new(),
471 )
472 .unwrap();
473
474 let renderer = Self {
475 #[cfg(not(target_arch = "wasm32"))]
476 thread_pool: rayon::ThreadPoolBuilder::new().build().unwrap(),
477
478 text: RefCell::new(TextRasterizer::new(context.clone())),
479
480 sprite_shader_id,
481 error_shader_id,
482
483 loaded_image_recv: rx_texture,
484 loaded_image_send: tx_texture,
485
486 depth_texture: Arc::new(depth_texture),
487
488 pipelines: HashMap::new(),
489 user_pipelines: HashMap::new(),
490
491 shaders: RefCell::new(shaders),
492 render_targets: RefCell::new(HashMap::new()),
493
494 screenshot_buffer,
495
496 vertex_buffer,
497 index_buffer,
498
499 post_processing_effects: RefCell::new(Vec::new()),
500 bloom,
501
502 egui_winit,
503 egui_render_routine: RefCell::new(egui_render_routine),
504
505 first_pass_texture,
506
507 lights_buffer,
508
509 quad_ubg,
510
511 global_lighting_params_buffer,
512
513 texture_layout: context.texture_layout.clone(),
514
515 tonemapping_texture,
516
517 camera_uniform,
518 camera_buffer,
519 camera_bind_group,
520 camera_bind_group_layout,
521
522 textures: context.textures.clone(),
523 render_texture_format,
524
525 color: Color::new(0.1, 0.2, 0.3, 1.0),
526
527 enable_z_buffer: false,
528
529 texture_creator: context.texture_creator.clone(),
530
531 window,
532
533 context,
534 screenshot_history_buffer: VecDeque::new(),
535 screenshot_params: screenshot::ScreenshotParams::default(),
536 };
537
538 {
539 let copy_shader_id = create_shader(
540 &mut renderer.shaders.borrow_mut(),
541 "copy",
542 &post_process_shader_from_fragment(COPY_SHADER_SRC),
543 HashMap::new(),
544 )
545 .expect("copy shader creation failed");
546
547 insert_post_processing_effect(&renderer, 0, "copy", copy_shader_id);
548 }
549
550 renderer
551 }
552
553 pub fn window(&self) -> &Window {
554 &self.window
555 }
556
557 pub fn render_post_processing(
558 &mut self,
559 screen_view: &wgpu::TextureView,
560 game_config: &GameConfig,
561 ) {
562 span_with_timing!("render_post_processing");
563
564 let mut encoder =
565 self.context.device.simple_encoder("Post Processing Encoder");
566
567 let mut input_bind_group = &self.first_pass_texture.bind_group;
568
569 if game_config.bloom_enabled {
570 self.bloom.draw(
571 &self.context.device,
572 &self.texture_layout,
573 input_bind_group,
574 &mut encoder,
575 );
576 }
577
578 let post_processing_effects = self.post_processing_effects.borrow();
579 let surface_texture_format = self.context.config.borrow().format;
580
581 let (last_effect_view, last_effect_format) = if game_config
582 .tonemapping_enabled
583 {
584 (&self.tonemapping_texture.texture.view, self.render_texture_format)
585 } else {
586 (screen_view, surface_texture_format)
587 };
588
589 let enabled_effects =
590 post_processing_effects.iter().filter(|x| x.enabled).collect_vec();
591
592 for (i, effect) in enabled_effects.iter().enumerate() {
593 let (output_texture_view, output_texture_format) =
594 if i == enabled_effects.len() - 1 {
595 (last_effect_view, last_effect_format)
596 } else {
597 (&effect.render_texture.view, self.render_texture_format)
598 };
599
600 let pipeline_key =
601 format!("{}-{:?}", effect.name, output_texture_format);
602
603 let maybe_pipeline = if self.pipelines.contains_key(&pipeline_key) {
604 Some(self.pipelines.get(&pipeline_key).unwrap())
605 } else {
606 info!("Loading EFFECT: {}", effect.name);
607
608 if let Some(shader) = self.shaders.borrow().get(effect.id) {
609 let pipeline = create_post_processing_pipeline(
610 &effect.name,
611 &self.context.device,
612 output_texture_format,
613 &[&self.texture_layout, &self.camera_bind_group_layout],
614 shader.clone(),
615 wgpu::BlendState::REPLACE,
617 );
618
619 self.pipelines.insert(pipeline_key.clone(), pipeline);
620 self.pipelines.get(&pipeline_key)
621 } else {
622 warn!(
623 "NO SHADER FOR EFFECT: {} ... {}",
624 effect.name, effect.id
625 );
626 None
627 }
628 };
629
630 if let Some(pipeline) = maybe_pipeline {
631 draw_post_processing_output(
632 &effect.name,
633 &mut encoder,
634 pipeline,
635 input_bind_group,
636 &self.camera_bind_group,
637 output_texture_view,
639 true,
640 None,
641 );
642
643 input_bind_group = &effect.bind_group;
644 } else {
645 error!("Missing pipeline for {}", effect.name);
646 }
647 }
648
649 if game_config.bloom_enabled {
650 self.bloom.blit_final(
651 &mut encoder,
652 &mut self.shaders.borrow_mut(),
653 &mut self.pipelines,
654 last_effect_view,
655 last_effect_format,
656 &game_config.lighting,
657 );
658 }
659
660 if game_config.tonemapping_enabled {
661 let tonemapping_pipeline = self
662 .pipelines
663 .entry("tonemapping".into())
664 .or_insert_with(|| {
665 let shaders = &mut self.shaders.borrow_mut();
667
668 create_post_processing_pipeline(
669 "Tonemapping",
670 &self.context.device,
671 self.context.config.borrow().format,
672 &[&self.texture_layout, &self.camera_bind_group_layout],
673 create_engine_post_processing_shader!(
674 shaders,
675 "tonemapping"
676 ),
677 wgpu::BlendState::REPLACE,
678 )
679 });
680
681
682 {
683 let should_clear = false;
684
685 let mut render_pass =
686 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
687 label: Some("Tonemapping"),
688 color_attachments: &[Some(
689 wgpu::RenderPassColorAttachment {
690 view: screen_view,
691 resolve_target: None,
692 ops: wgpu::Operations {
693 load: if should_clear {
694 wgpu::LoadOp::Clear(wgpu::Color {
695 r: 0.0,
696 g: 0.0,
697 b: 0.0,
698 a: 1.0,
699 })
700 } else {
701 wgpu::LoadOp::Load
702 },
703 store: wgpu::StoreOp::Store,
704 },
705 },
706 )],
707 depth_stencil_attachment: None,
708 timestamp_writes: None,
709 occlusion_query_set: None,
710 });
711
712 render_pass.set_pipeline(tonemapping_pipeline);
713 render_pass.set_bind_group(
714 0,
715 &self.tonemapping_texture.bind_group,
716 &[],
717 );
718 render_pass.set_bind_group(1, &self.camera_bind_group, &[]);
719
720 render_pass.draw(0..3, 0..1);
721 }
722 }
723
724 self.context.queue.submit(std::iter::once(encoder.finish()));
725 }
726
727 pub fn render_egui(&self, view: &wgpu::TextureView, egui: &egui::Context) {
728 span_with_timing!("render_egui");
729
730 let mut encoder =
731 self.context.device.simple_encoder("egui Render Encoder");
732
733 let paint_jobs =
734 self.egui_render_routine.borrow_mut().end_frame_and_render(
735 egui,
736 &self.context.device,
737 &self.context.queue,
738 &mut encoder,
739 egui_scale_factor(),
740 );
741
742 let egui_render = self.egui_render_routine.borrow();
743 let egui_render = &egui_render;
744
745 {
746 let mut render_pass =
747 encoder.simple_render_pass("egui Render Pass", None, view);
748 egui_render.render_pass.render(
749 &mut render_pass,
750 &paint_jobs,
751 &egui_render.screen_descriptor,
752 );
753 }
754
755 self.context.queue.submit(std::iter::once(encoder.finish()));
756 }
757
758 pub fn on_event(
759 &mut self,
760 event: &winit::event::WindowEvent,
761 _egui_ctx: &egui::Context,
762 ) -> bool {
763 self.egui_winit.on_window_event(self.window, event).consumed
764 }
765
766 pub fn as_mut_any(&mut self) -> &mut dyn Any {
767 self
768 }
769
770 pub fn update(&mut self, params: &mut DrawParams) {
771 let _span = span!("renderer update");
772
773 let mut changed_recording_mode = false;
774
775 if is_key_pressed(KeyCode::F3) {
776 params.config.dev.recording_mode =
777 match params.config.dev.recording_mode {
778 RecordingMode::None => RecordingMode::Landscape,
779 RecordingMode::Tiktok => RecordingMode::Landscape,
780 RecordingMode::Landscape => RecordingMode::None,
781 };
782
783 changed_recording_mode = true;
784 }
785
786 if is_key_pressed(KeyCode::F9) {
787 self.enable_z_buffer = !self.enable_z_buffer;
788 info!("Z Buffer: {}", self.enable_z_buffer);
789 }
790
791 if is_key_pressed(KeyCode::F4) {
792 params.config.dev.recording_mode =
793 match params.config.dev.recording_mode {
794 RecordingMode::None => RecordingMode::Tiktok,
795 RecordingMode::Tiktok => RecordingMode::None,
796 RecordingMode::Landscape => RecordingMode::Tiktok,
797 };
798
799 changed_recording_mode = true;
800 }
801
802 {
804 let _span = span!("wgpu texture load");
805
806 while let Ok(loaded_image) = self.loaded_image_recv.try_recv() {
807 let context = self.context.clone();
808 let textures = self.textures.clone();
809 let layout = self.texture_layout.clone();
810
811 let load_image_texture = move || {
812 let texture = Texture::from_image(
813 &context.device,
814 &context.queue,
815 &loaded_image.image,
816 Some(&loaded_image.path),
817 false,
818 )
819 .unwrap();
820
821 let bind_group = context.device.simple_bind_group(
822 Some(&format!("{}_bind_group", loaded_image.path)),
823 &texture,
824 &layout,
825 );
826
827 textures.lock().insert(
828 loaded_image.handle,
829 BindableTexture { bind_group, texture },
830 );
831 };
832
833 #[cfg(target_arch = "wasm32")]
834 load_image_texture();
835
836 #[cfg(not(target_arch = "wasm32"))]
837 self.thread_pool.spawn(load_image_texture);
838 }
839 }
840
841 if changed_recording_mode {
842 info!("Recording Mode: {:?}", params.config.dev.recording_mode);
843
844 self.window.set_title(&format!(
845 "{} {}(COMFY ENGINE)",
846 params.config.game_name,
847 if params.config.dev.recording_mode == RecordingMode::Tiktok {
848 "Portrait "
849 } else {
850 ""
851 },
852 ));
853
854 let ratio = 1920.0 / 1080.0;
855
856 let size = 600.0;
857 let portrait_res = winit::dpi::PhysicalSize::new(
858 size as i32,
859 (size * ratio) as i32,
860 );
861
862 let landscape_res = winit::dpi::PhysicalSize::new(1920, 1080);
863
864 let (resolution, _ui_toggle) =
865 match params.config.dev.recording_mode {
866 RecordingMode::None => (landscape_res, true),
867 RecordingMode::Tiktok => (portrait_res, false),
868 RecordingMode::Landscape => (landscape_res, false),
869 };
870
871 self.window.request_inner_size(resolution).log_err();
872 }
874
875 #[cfg(not(any(feature = "ci-release", target_arch = "wasm32")))]
876 maybe_reload_shaders(&mut self.shaders.borrow_mut());
877
878 self.camera_uniform.update_view_proj(&main_camera());
879
880 params.config.lighting.time = get_time() as f32;
881
882 {
883 let camera = main_camera();
884 params.config.lighting.chromatic_aberration =
885 (camera.shake_amount * 200.0 * camera.shake_timer)
886 .clamp(0.0, 200.0);
887 }
888
889 self.context.queue.write_buffer(
890 &self.global_lighting_params_buffer,
891 0,
892 bytemuck::cast_slice(&[params.config.lighting]),
893 );
894
895 self.context.queue.write_buffer(
896 &self.camera_buffer,
897 0,
898 bytemuck::cast_slice(&[self.camera_uniform]),
899 );
900
901 let mut light_uniform = LightUniform::default();
902
903 for (i, light) in params.lights.iter().enumerate() {
904 if i >= 128 {
905 break;
906 }
907
908 light_uniform.lights[i] = *light;
909 light_uniform.num_lights += 1;
910 }
911
912 self.context.queue.write_buffer(
913 &self.lights_buffer,
914 0,
915 bytemuck::cast_slice(&[light_uniform]),
916 );
917 }
918
919 pub fn draw(&mut self, params: DrawParams, egui: &egui::Context) {
920 span_with_timing!("render");
921
922 let output = {
923 let _span = span!("get current surface");
924
925 match self.context.surface.get_current_texture() {
926 Ok(texture) => texture,
927 Err(_) => {
928 return;
929 }
930 }
931 };
932
933 let surface_view = {
934 let _span = span!("create surface view");
935 output.texture.create_view(&wgpu::TextureViewDescriptor::default())
936 };
937
938 let config = params.config.clone();
939
940 run_batched_render_passes(
941 self,
942 &surface_view,
943 params,
944 self.sprite_shader_id,
945 self.error_shader_id,
946 );
947
948 self.render_post_processing(&surface_view, &config);
949 self.render_egui(&surface_view, egui);
950
951 if config.dev.show_buffers {
952 span_with_timing!("render_debug");
953
954 let pp = self.post_processing_effects.borrow();
955
956 let mut bind_groups = vec![&self.first_pass_texture.bind_group];
957 bind_groups.push(&self.bloom.threshold.bind_group);
958 bind_groups.push(&self.bloom.blur_texture.bind_group);
959
960 for effect in pp.iter() {
961 if effect.enabled {
962 bind_groups.push(&effect.bind_group);
963 }
964 }
965
966 render_debug(
967 &self.context,
968 &mut self.shaders.borrow_mut(),
969 self.enable_z_buffer,
970 &self.quad_ubg,
971 &self.texture_layout,
972 &self.depth_texture,
973 bind_groups,
974 &mut self.pipelines,
975 &surface_view,
976 );
977 }
978
979 {
980 let config = self.context.config.borrow();
981 let screen = uvec2(config.width, config.height);
982
983 if self.screenshot_params.record_screenshots {
984 screenshot::record_screenshot_history(
985 screen,
986 &self.context,
987 &self.screenshot_buffer,
988 &output,
989 &mut self.screenshot_params,
990 &mut self.screenshot_history_buffer,
991 );
992 }
993
994 #[cfg(feature = "record-pngs")]
995 screenshot::record_pngs(
996 screen,
997 &self.context,
998 &self.screenshot_buffer,
999 &output,
1000 );
1001 }
1002
1003 {
1004 span_with_timing!("present");
1005 output.present();
1006 }
1007 }
1008
1009 pub fn scale_factor(&self) -> f32 {
1010 1.0
1012 }
1013
1014 pub fn resize(&mut self, new_size: UVec2) {
1015 let _span = span!("resize");
1016
1017 let scale_factor = game_config()
1018 .scale_factor_override
1019 .unwrap_or(self.window.scale_factor() as f32);
1020
1021 let size = winit::dpi::PhysicalSize::<u32>::new(new_size.x, new_size.y);
1022
1023 {
1024 let mut config = self.context.config.borrow_mut();
1025
1026 config.width = size.width;
1027 config.height = size.height;
1028
1029 self.context.surface.configure(&self.context.device, &config);
1030 }
1031
1032 self.egui_render_routine.borrow_mut().resize(
1033 size.width,
1034 size.height,
1035 scale_factor,
1036 );
1037
1038 }
1040
1041 pub fn width(&self) -> f32 {
1042 self.context.config.borrow().width as f32
1043 }
1044
1045 pub fn height(&self) -> f32 {
1046 self.context.config.borrow().height as f32
1047 }
1048
1049 pub fn end_frame(&mut self) {}
1050}
1051
1052pub fn depth_stencil_attachment(
1053 enabled: bool,
1054 view: &wgpu::TextureView,
1055 is_first: bool,
1056) -> Option<wgpu::RenderPassDepthStencilAttachment> {
1057 let clear_depth =
1058 if is_first { wgpu::LoadOp::Clear(1.0) } else { wgpu::LoadOp::Load };
1059
1060 if enabled {
1061 Some(wgpu::RenderPassDepthStencilAttachment {
1062 view,
1063 depth_ops: Some(wgpu::Operations {
1064 load: clear_depth,
1065 store: wgpu::StoreOp::Store,
1066 }),
1067 stencil_ops: None,
1068 })
1069 } else {
1070 None
1071 }
1072}