1use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
6
7use crate::core::binding::GlobalBindGroupCache;
8use crate::graph::composer::ComposerContext;
9use crate::graph::core::allocator::TransientPool;
10use crate::graph::core::arena::FrameArena;
11use crate::graph::core::graph::GraphStorage;
12use crate::graph::frame::RenderLists;
13#[cfg(feature = "debug_view")]
14use crate::graph::passes::DebugViewFeature;
15use crate::graph::passes::{
16 BloomFeature, BrdfLutFeature, CasFeature, FxaaFeature, IblComputeFeature, MsaaSyncFeature,
17 OpaqueFeature, PrepassFeature, ShadowFeature, SimpleForwardFeature, SkyboxFeature, SsaoFeature,
18 SsssFeature, TaaFeature, ToneMappingFeature, TransmissionCopyFeature, TransparentFeature,
19};
20use myth_assets::AssetServer;
21use myth_core::Result;
22use myth_scene::Scene;
23use myth_scene::camera::RenderCamera;
24
25use crate::core::{ResourceManager, WgpuContext};
26use crate::graph::{FrameComposer, RenderFrame};
27use crate::pipeline::PipelineCache;
28use crate::pipeline::ShaderManager;
29use crate::settings::{RenderPath, RendererInitConfig, RendererSettings};
30
31pub struct Renderer {
46 size: (u32, u32),
47 init_config: RendererInitConfig,
48 settings: RendererSettings,
49 context: Option<RendererState>,
50}
51
52struct RendererState {
54 wgpu_ctx: WgpuContext,
55 resource_manager: ResourceManager,
56 pipeline_cache: PipelineCache,
57 shader_manager: ShaderManager,
58
59 render_frame: RenderFrame,
60 render_lists: RenderLists,
62 global_bind_group_cache: GlobalBindGroupCache,
65
66 pub(crate) graph_storage: GraphStorage,
68 pub(crate) transient_pool: TransientPool,
70 pub(crate) frame_arena: FrameArena,
71
72 pub(crate) fxaa_pass: FxaaFeature,
74 pub(crate) taa_pass: TaaFeature,
75 pub(crate) cas_pass: CasFeature,
76 pub(crate) tone_map_pass: ToneMappingFeature,
77 pub(crate) bloom_pass: BloomFeature,
78 pub(crate) ssao_pass: SsaoFeature,
79
80 pub(crate) prepass: PrepassFeature,
82 pub(crate) opaque_pass: OpaqueFeature,
83 pub(crate) skybox_pass: SkyboxFeature,
84 pub(crate) transparent_pass: TransparentFeature,
85 pub(crate) transmission_copy_pass: TransmissionCopyFeature,
86 pub(crate) simple_forward_pass: SimpleForwardFeature,
87 pub(crate) ssss_pass: SsssFeature,
88 pub(crate) msaa_sync_pass: MsaaSyncFeature,
89
90 pub(crate) shadow_pass: ShadowFeature,
92 pub(crate) brdf_pass: BrdfLutFeature,
93 pub(crate) ibl_pass: IblComputeFeature,
94
95 #[cfg(feature = "debug_view")]
97 pub(crate) debug_view_pass: DebugViewFeature,
98}
99
100#[derive(Debug, Clone, Copy, Default)]
101pub struct FrameTime {
102 pub time: f32,
103 pub delta_time: f32,
104 pub frame_count: u64,
105}
106
107impl Renderer {
108 #[must_use]
118 pub fn new(init_config: RendererInitConfig, settings: RendererSettings) -> Self {
119 Self {
120 init_config,
121 settings,
122 context: None,
123 size: (0, 0),
124 }
125 }
126
127 #[inline]
129 #[must_use]
130 pub fn size(&self) -> (u32, u32) {
131 self.size
132 }
133
134 pub async fn init<W>(&mut self, window: W, width: u32, height: u32) -> Result<()>
142 where
143 W: HasWindowHandle + HasDisplayHandle + Send + Sync + 'static,
144 {
145 if self.context.is_some() {
146 return Ok(());
147 }
148
149 self.size = (width, height);
150
151 let wgpu_ctx =
153 WgpuContext::new(window, &self.init_config, &self.settings, width, height).await?;
154
155 let resource_manager = ResourceManager::new(
157 wgpu_ctx.device.clone(),
158 wgpu_ctx.queue.clone(),
159 self.settings.anisotropy_clamp,
160 );
161
162 let render_frame = RenderFrame::new();
164
165 let global_bind_group_cache = GlobalBindGroupCache::new();
167
168 let shadow_pass = ShadowFeature::new(&wgpu_ctx.device);
172 let brdf_pass = BrdfLutFeature::new(&wgpu_ctx.device);
173 let ibl_pass = IblComputeFeature::new(&wgpu_ctx.device);
174
175 self.context = Some(RendererState {
177 wgpu_ctx,
178 resource_manager,
179 pipeline_cache: PipelineCache::new(),
180 shader_manager: ShaderManager::new(),
181
182 render_frame,
183 render_lists: RenderLists::new(),
184 global_bind_group_cache,
185
186 graph_storage: GraphStorage::new(),
188 transient_pool: TransientPool::new(),
190 frame_arena: FrameArena::new(),
191 fxaa_pass: FxaaFeature::new(),
192 taa_pass: TaaFeature::new(),
193 cas_pass: CasFeature::new(),
194 tone_map_pass: ToneMappingFeature::new(),
195 bloom_pass: BloomFeature::new(),
196 ssao_pass: SsaoFeature::new(),
197
198 prepass: PrepassFeature::new(),
200 opaque_pass: OpaqueFeature::new(),
201 skybox_pass: SkyboxFeature::new(),
202 transparent_pass: TransparentFeature::new(),
203 transmission_copy_pass: TransmissionCopyFeature::new(),
204 simple_forward_pass: SimpleForwardFeature::new(),
205 ssss_pass: SsssFeature::new(),
206 msaa_sync_pass: MsaaSyncFeature::new(),
207
208 shadow_pass,
210 brdf_pass,
211 ibl_pass,
212
213 #[cfg(feature = "debug_view")]
214 debug_view_pass: DebugViewFeature::new(),
215 });
216
217 log::info!("Renderer Initialized");
218 Ok(())
219 }
220
221 pub fn resize(&mut self, width: u32, height: u32) {
222 self.size = (width, height);
223 if let Some(state) = &mut self.context {
224 state.wgpu_ctx.resize(width, height);
225 state.global_bind_group_cache.clear();
227 }
228 }
229
230 pub fn begin_frame<'a>(
259 &'a mut self,
260 scene: &'a mut Scene,
261 camera: &'a RenderCamera,
262 assets: &'a AssetServer,
263 frame_time: FrameTime,
264 ) -> Option<FrameComposer<'a>> {
265 if self.size.0 == 0 || self.size.1 == 0 {
266 return None;
267 }
268
269 let state = self.context.as_mut()?;
270
271 state.frame_arena.reset();
275
276 state.global_bind_group_cache.begin_frame();
278
279 let surface_size = state.wgpu_ctx.size();
281 state.render_frame.extract_and_prepare(
282 &mut state.resource_manager,
283 scene,
284 camera,
285 assets,
286 frame_time,
287 &mut state.render_lists,
288 surface_size,
289 );
290
291 let requested_msaa = camera.aa_mode.msaa_sample_count();
292 if state.wgpu_ctx.msaa_samples != requested_msaa {
293 state.wgpu_ctx.msaa_samples = requested_msaa;
294 state.wgpu_ctx.pipeline_settings_version += 1;
295 }
296
297 crate::graph::culling::cull_and_sort(
299 &state.render_frame.extracted_scene,
300 &state.render_frame.render_state,
301 &state.wgpu_ctx,
302 &mut state.resource_manager,
303 &mut state.pipeline_cache,
304 &mut state.shader_manager,
305 &mut state.render_lists,
306 camera,
307 assets,
308 );
309
310 {
316 use crate::HDR_TEXTURE_FORMAT;
317 use crate::graph::core::context::ExtractContext;
318
319 let view_format = state.wgpu_ctx.surface_view_format;
320 let is_hf = state.wgpu_ctx.render_path.supports_post_processing();
321 let scene_id_val = scene.id();
322 let render_state_id = state.render_frame.render_state.id;
323 let global_state_key = (render_state_id, scene_id_val);
324
325 let ssao_enabled = scene.ssao.enabled && is_hf;
326 let needs_feature_id =
327 is_hf && (scene.screen_space.enable_sss || scene.screen_space.enable_ssr);
328 let needs_normal = ssao_enabled || needs_feature_id;
329 let needs_skybox = scene.background.needs_skybox_pass();
330 let bloom_enabled = scene.bloom.enabled && is_hf;
331
332 let mut extract_ctx = ExtractContext {
333 device: &state.wgpu_ctx.device,
334 queue: &state.wgpu_ctx.queue,
335 pipeline_cache: &mut state.pipeline_cache,
336 shader_manager: &mut state.shader_manager,
337 global_bind_group_cache: &mut state.global_bind_group_cache,
338 resource_manager: &mut state.resource_manager,
339 wgpu_ctx: &state.wgpu_ctx,
340 render_lists: &mut state.render_lists,
341 extracted_scene: &state.render_frame.extracted_scene,
342 render_state: &state.render_frame.render_state,
343 render_camera: camera,
344 assets,
345 };
346
347 state.brdf_pass.extract_and_prepare(&mut extract_ctx);
349 state.ibl_pass.extract_and_prepare(&mut extract_ctx);
350 state.shadow_pass.extract_and_prepare(&mut extract_ctx);
351
352 if needs_skybox {
354 let color_format = if is_hf {
355 HDR_TEXTURE_FORMAT
356 } else {
357 view_format
358 };
359 state.skybox_pass.extract_and_prepare(
360 &mut extract_ctx,
361 &scene.background.mode,
362 &scene.background.uniforms,
363 global_state_key,
364 color_format,
365 );
366 }
367
368 if is_hf {
369 if let Some(taa_settins) = camera.aa_mode.taa_settings() {
370 state.taa_pass.extract_and_prepare(
371 &mut extract_ctx,
372 taa_settins.feedback_weight,
373 self.size,
374 HDR_TEXTURE_FORMAT,
375 );
376
377 if taa_settins.sharpen_intensity > 0.0 {
378 state.cas_pass.extract_and_prepare(
379 &mut extract_ctx,
380 taa_settins.sharpen_intensity,
381 HDR_TEXTURE_FORMAT,
382 );
383 }
384 }
385
386 if let Some(fxaa_settings) = camera.aa_mode.fxaa_settings() {
387 state.fxaa_pass.target_quality = fxaa_settings.quality();
388 state
389 .fxaa_pass
390 .extract_and_prepare(&mut extract_ctx, view_format);
391 }
392
393 state.prepass.extract_and_prepare(
394 &mut extract_ctx,
395 needs_normal,
396 needs_feature_id,
397 camera.aa_mode.is_taa(),
398 );
399
400 if ssao_enabled {
401 state
402 .ssao_pass
403 .extract_and_prepare(&mut extract_ctx, &scene.ssao.uniforms);
404 }
405
406 state.ssss_pass.extract_and_prepare(&mut extract_ctx);
407
408 let msaa = state.wgpu_ctx.msaa_samples;
411 let needs_specular = scene.screen_space.enable_sss;
412 if msaa > 1 && needs_specular {
413 state
414 .msaa_sync_pass
415 .extract_and_prepare(&mut extract_ctx, msaa);
416 }
417
418 if bloom_enabled {
419 state.bloom_pass.extract_and_prepare(
420 &mut extract_ctx,
421 &scene.bloom.upsample_uniforms,
422 &scene.bloom.composite_uniforms,
423 );
424 }
425
426 state.tone_map_pass.extract_and_prepare(
427 &mut extract_ctx,
428 scene.tone_mapping.mode,
429 view_format,
430 global_state_key,
431 &scene.tone_mapping.uniforms,
432 scene.tone_mapping.lut_texture,
433 );
434
435 #[cfg(feature = "debug_view")]
437 {
438 use crate::graph::render_state::DebugViewTarget;
439 let target = state.render_frame.render_state.debug_view_target;
440 if target != DebugViewTarget::None {
441 state.debug_view_pass.extract_and_prepare(
442 &mut extract_ctx,
443 view_format,
444 target.view_mode(),
445 );
446 }
447 }
448 }
449 }
450
451 let ctx = ComposerContext {
453 wgpu_ctx: &mut state.wgpu_ctx,
454 resource_manager: &mut state.resource_manager,
455 pipeline_cache: &mut state.pipeline_cache,
456 shader_manager: &mut state.shader_manager,
457
458 extracted_scene: &state.render_frame.extracted_scene,
459 render_state: &state.render_frame.render_state,
460
461 global_bind_group_cache: &mut state.global_bind_group_cache,
462
463 render_lists: &mut state.render_lists,
464
465 scene,
467 camera,
468 assets,
469 frame_time,
470
471 graph_storage: &mut state.graph_storage,
472 transient_pool: &mut state.transient_pool,
473 frame_arena: &state.frame_arena,
475 fxaa_pass: &mut state.fxaa_pass,
476 taa_pass: &mut state.taa_pass,
477 cas_pass: &mut state.cas_pass,
478 tone_map_pass: &mut state.tone_map_pass,
479 bloom_pass: &mut state.bloom_pass,
480 ssao_pass: &mut state.ssao_pass,
481
482 prepass: &mut state.prepass,
483 opaque_pass: &mut state.opaque_pass,
484 skybox_pass: &mut state.skybox_pass,
485 transparent_pass: &mut state.transparent_pass,
486 transmission_copy_pass: &mut state.transmission_copy_pass,
487 simple_forward_pass: &mut state.simple_forward_pass,
488 ssss_pass: &mut state.ssss_pass,
489 msaa_sync_pass: &mut state.msaa_sync_pass,
490
491 shadow_pass: &mut state.shadow_pass,
492 brdf_pass: &mut state.brdf_pass,
493 ibl_pass: &mut state.ibl_pass,
494
495 #[cfg(feature = "debug_view")]
496 debug_view_pass: &mut state.debug_view_pass,
497 };
498
499 Some(FrameComposer::new(ctx, self.size))
501 }
502
503 pub fn maybe_prune(&mut self) {
508 if let Some(state) = &mut self.context {
509 state.render_frame.maybe_prune(&mut state.resource_manager);
510 state.global_bind_group_cache.garbage_collect();
512 }
513 }
514
515 #[inline]
519 pub fn render_path(&self) -> &RenderPath {
520 &self.settings.path
521 }
522
523 #[inline]
525 pub fn settings(&self) -> &RendererSettings {
526 &self.settings
527 }
528
529 #[inline]
531 pub fn init_config(&self) -> &RendererInitConfig {
532 &self.init_config
533 }
534
535 pub fn update_settings(&mut self, new_settings: RendererSettings) {
542 if self.settings == new_settings {
543 return;
544 }
545
546 let old = std::mem::replace(&mut self.settings, new_settings);
547
548 if let Some(state) = &mut self.context {
549 if old.vsync != self.settings.vsync {
551 state.wgpu_ctx.set_vsync(self.settings.vsync);
552 }
553
554 if old.path != self.settings.path {
556 state.wgpu_ctx.render_path = self.settings.path;
557 state.wgpu_ctx.pipeline_settings_version += 1;
558 log::info!("RenderPath changed to {:?}", self.settings.path);
559 }
560
561 if old.anisotropy_clamp != self.settings.anisotropy_clamp {
563 state
564 .resource_manager
565 .sampler_registry
566 .set_global_anisotropy(self.settings.anisotropy_clamp);
567 log::info!(
568 "Anisotropy clamp changed to {}",
569 self.settings.anisotropy_clamp
570 );
571 }
572 }
573 }
574
575 pub fn set_render_path(&mut self, path: RenderPath) {
580 if self.settings.path != path {
581 let mut new = self.settings.clone();
582 new.path = path;
583 self.update_settings(new);
584 }
585 }
586
587 #[cfg(feature = "debug_view")]
593 pub fn set_debug_view_target(&mut self, target: crate::graph::render_state::DebugViewTarget) {
594 if let Some(state) = &mut self.context {
595 state.render_frame.render_state.debug_view_target = target;
596 }
597 }
598
599 #[cfg(feature = "debug_view")]
601 pub fn debug_view_target(&self) -> crate::graph::render_state::DebugViewTarget {
602 self.context
603 .as_ref()
604 .map(|s| s.render_frame.render_state.debug_view_target)
605 .unwrap_or_default()
606 }
607
608 pub fn device(&self) -> Option<&wgpu::Device> {
614 self.context.as_ref().map(|s| &s.wgpu_ctx.device)
615 }
616
617 pub fn queue(&self) -> Option<&wgpu::Queue> {
621 self.context.as_ref().map(|s| &s.wgpu_ctx.queue)
622 }
623
624 pub fn surface_format(&self) -> Option<wgpu::TextureFormat> {
628 self.context.as_ref().map(|s| s.wgpu_ctx.config.format)
629 }
630
631 pub fn wgpu_ctx(&self) -> Option<&WgpuContext> {
636 self.context.as_ref().map(|s| &s.wgpu_ctx)
637 }
638
639 pub fn dump_graph_mermaid(&self) -> Option<String> {
640 self.context
641 .as_ref()
642 .map(|s| s.graph_storage.dump_mermaid())
643 }
644}