1use std::sync::Arc;
2use wgpu::{util::DeviceExt, Device, Queue, Surface, SurfaceConfiguration};
3use winit::window::Window;
4
5pub use crate::gpu_types::{
6 InstanceRaw, LightData, PostProcessUniforms, SceneUniforms, ShadowVsUniform, Vertex,
7};
8pub use crate::pipeline::SceneState;
9pub use crate::post_process::PostProcessState;
10
11pub struct RenderContext<'a> {
25 pub(crate) encoder: &'a mut wgpu::CommandEncoder,
26 pub(crate) view: &'a wgpu::TextureView,
27 pub(crate) renderer: &'a mut Renderer,
28 pub(crate) light_time: f32,
29}
30
31impl<'a> RenderContext<'a> {
32 pub fn new(
34 encoder: &'a mut wgpu::CommandEncoder,
35 view: &'a wgpu::TextureView,
36 renderer: &'a mut Renderer,
37 light_time: f32,
38 ) -> Self {
39 Self {
40 encoder,
41 view,
42 renderer,
43 light_time,
44 }
45 }
46
47 pub fn disable_gpu_compute(&mut self) {
50 self.renderer.gpu_fluid = None;
51 self.renderer.gpu_particles = None;
52 self.renderer.gpu_physics = None;
53 }
54
55 pub fn light_time(&self) -> f32 {
57 self.light_time
58 }
59
60 pub fn renderer(&self) -> &Renderer {
62 self.renderer
63 }
64
65 pub fn renderer_mut(&mut self) -> &mut Renderer {
67 self.renderer
68 }
69
70 pub fn encoder(&mut self) -> &mut wgpu::CommandEncoder {
72 self.encoder
73 }
74
75 pub fn output_view(&self) -> &wgpu::TextureView {
77 self.view
78 }
79
80 pub fn parts_mut(&mut self) -> (&mut wgpu::CommandEncoder, &wgpu::TextureView, &mut Renderer) {
83 (self.encoder, self.view, self.renderer)
84 }
85}
86
87pub struct Renderer {
88 pub surface: Surface<'static>,
90 pub device: Device,
91 pub queue: Queue,
92 pub config: SurfaceConfiguration,
93 pub size: winit::dpi::PhysicalSize<u32>,
94 pub depth_texture_view: wgpu::TextureView,
95
96 pub scene: SceneState,
98
99 pub post: PostProcessState,
101
102 pub gpu_particles: Option<crate::gpu_particles::GpuParticleSystem>,
104
105 pub gpu_physics: Option<crate::gpu_physics::GpuPhysicsSystem>,
106
107 pub gpu_fluid: Option<crate::gpu_fluid::GpuFluidSystem>,
109
110 pub deferred: Option<crate::deferred::DeferredState>,
112
113 pub gpu_cull: Option<crate::gpu_cull::GpuCullState>,
115
116 pub ssao: Option<crate::ssao::SsaoState>,
118
119 pub ssr: Option<crate::ssr::SsrState>,
121
122 pub ssgi: Option<crate::ssgi::SsgiState>,
124
125 pub volumetric: Option<crate::volumetric::VolumetricState>,
127
128 pub decal: Option<crate::decal::DecalState>,
130
131 pub taa: Option<crate::taa::TaaState>,
133
134 pub fxaa: Option<crate::fxaa::FxaaState>,
136
137 pub debug_renderer: Option<crate::debug_renderer::GizmoRendererSystem>,
139
140 pub asset_manager: std::sync::RwLock<crate::asset::AssetManager>,
142
143 pub web_profile: crate::web_profile::WebProfile,
145}
146
147impl Renderer {
148 pub fn load_shader(
149 device: &wgpu::Device,
150 file_path: &str,
151 fallback_src: &str,
152 label: &str,
153 ) -> wgpu::ShaderModule {
154 let source =
155 std::fs::read_to_string(file_path).unwrap_or_else(|_| fallback_src.to_string());
156 device.create_shader_module(wgpu::ShaderModuleDescriptor {
157 label: Some(label),
158 source: wgpu::ShaderSource::Wgsl(source.into()),
159 })
160 }
161
162 pub async fn new(window: Arc<Window>) -> Self {
163 let mut size = window.inner_size();
164 if size.width == 0 || size.height == 0 {
166 size = winit::dpi::PhysicalSize::new(1280, 720);
167 }
168
169 #[cfg(target_arch = "wasm32")]
170 {
171 if size.width > 640 || size.height > 360 {
174 let aspect = size.width as f32 / size.height as f32;
175 if aspect > 1.0 {
176 size.width = 640;
177 size.height = (640.0 / aspect) as u32;
178 } else {
179 size.height = 360;
180 size.width = (360.0 * aspect) as u32;
181 }
182 }
183 }
184
185 #[cfg(target_arch = "wasm32")]
186 let backends = wgpu::Backends::BROWSER_WEBGPU | wgpu::Backends::GL;
187 #[cfg(not(target_arch = "wasm32"))]
188 let backends = wgpu::Backends::all();
189
190 log::info!("[Renderer] Window size: {}x{}", size.width, size.height);
191 log::info!("[Renderer] Backends: {:?}", backends);
192
193 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
194 backends,
195 ..Default::default()
196 });
197
198 #[cfg(not(target_arch = "wasm32"))]
200 {
201 let adapters = instance.enumerate_adapters(backends);
202 log::info!("[Renderer] {} adapter bulundu", adapters.len());
203 for (i, a) in adapters.iter().enumerate() {
204 let info = a.get_info();
205 log::info!(
206 "[Renderer] Adapter {}: {} ({:?}, {:?})",
207 i,
208 info.name,
209 info.backend,
210 info.device_type
211 );
212 }
213 }
214
215 let surface = instance
216 .create_surface(window.clone())
217 .expect("Surface oluşturulamadı!");
218
219 log::info!("[Renderer] Surface oluşturuldu, adapter aranıyor...");
220
221 let adapter = instance
222 .request_adapter(&wgpu::RequestAdapterOptions {
223 power_preference: wgpu::PowerPreference::default(),
224 compatible_surface: Some(&surface),
225 force_fallback_adapter: false,
226 })
227 .await;
228
229 let adapter = match adapter {
230 Some(a) => {
231 let info = a.get_info();
232 log::info!(
233 "[Renderer] Adapter bulundu: {} ({:?})",
234 info.name,
235 info.backend
236 );
237 a
238 }
239 None => {
240 log::warn!(
241 "[Renderer] Surface uyumlu adapter bulunamadı, surface'siz deneniyor..."
242 );
243 match instance
245 .request_adapter(&wgpu::RequestAdapterOptions {
246 power_preference: wgpu::PowerPreference::default(),
247 compatible_surface: None,
248 force_fallback_adapter: false,
249 })
250 .await
251 {
252 Some(a) => {
253 let info = a.get_info();
254 log::info!(
255 "[Renderer] Surface'siz adapter bulundu: {} ({:?})",
256 info.name,
257 info.backend
258 );
259 a
260 }
261 None => {
262 log::error!(
263 "[Renderer] Hiçbir adapter bulunamadı! Backends: {:?}",
264 backends
265 );
266 panic!(
267 "GPU adapter bulunamadı! Backends: {:?}, Window size: {}x{}",
268 backends, size.width, size.height
269 );
270 }
271 }
272 }
273 };
274
275 #[cfg(not(target_arch = "wasm32"))]
276 let (device, queue) = adapter
277 .request_device(
278 &wgpu::DeviceDescriptor {
279 required_features: wgpu::Features::POLYGON_MODE_LINE,
280 required_limits: wgpu::Limits {
281 max_bind_groups: 6,
282 max_storage_buffers_per_shader_stage: 8,
283 max_storage_buffer_binding_size: 256 << 20, ..wgpu::Limits::default()
285 },
286 label: None,
287 },
288 None,
289 )
290 .await
291 .unwrap();
292
293 #[cfg(target_arch = "wasm32")]
294 let (device, queue) = adapter
295 .request_device(
296 &wgpu::DeviceDescriptor {
297 required_features: wgpu::Features::empty(),
298 required_limits: wgpu::Limits {
299 max_bind_groups: 4,
300 max_storage_buffers_per_shader_stage: 8,
301 max_storage_buffer_binding_size: 128 << 20, ..wgpu::Limits::downlevel_webgl2_defaults()
303 .using_resolution(adapter.limits())
304 },
305 label: None,
306 },
307 None,
308 )
309 .await
310 .unwrap();
311
312 let surface_caps = surface.get_capabilities(&adapter);
313 let surface_format = surface_caps
314 .formats
315 .iter()
316 .copied()
317 .find(|f| f.is_srgb())
318 .unwrap_or(surface_caps.formats[0]);
319
320 let config = wgpu::SurfaceConfiguration {
321 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
322 format: surface_format,
323 width: size.width,
324 height: size.height,
325 present_mode: wgpu::PresentMode::AutoNoVsync,
327 alpha_mode: surface_caps.alpha_modes[0],
328 view_formats: vec![],
329 desired_maximum_frame_latency: 2,
330 };
331 surface.configure(&device, &config);
332
333 let depth_texture_view = Self::create_depth_texture(&device, config.width, config.height);
334
335 let scene = crate::pipeline::build_scene_pipelines(&device);
336 let post_res = crate::post_process::build_post_process_resources(
337 &device,
338 surface_format,
339 config.width,
340 config.height,
341 &depth_texture_view,
342 );
343
344 #[cfg(not(target_arch = "wasm32"))]
346 let gpu_particles = {
347 let max_particles: u32 = 100_000;
348 Some(crate::gpu_particles::GpuParticleSystem::new(
349 &device,
350 max_particles,
351 &scene.global_bind_group_layout,
352 wgpu::TextureFormat::Rgba16Float,
353 ))
354 };
355 #[cfg(target_arch = "wasm32")]
356 let gpu_particles: Option<crate::gpu_particles::GpuParticleSystem> = None;
357
358 #[cfg(not(target_arch = "wasm32"))]
359 let gpu_physics = {
360 let max_physics_spheres: u32 = 50_000;
361 let mut physics = crate::gpu_physics::GpuPhysicsSystem::new(
362 &device,
363 max_physics_spheres,
364 &scene.global_bind_group_layout,
365 wgpu::TextureFormat::Rgba16Float,
366 wgpu::TextureFormat::Depth32Float,
367 );
368 physics.enable_debug(&device, 0);
369 Some(physics)
370 };
371 #[cfg(target_arch = "wasm32")]
372 let gpu_physics: Option<crate::gpu_physics::GpuPhysicsSystem> = None;
373
374 #[cfg(not(target_arch = "wasm32"))]
375 let gpu_fluid = Some(crate::gpu_fluid::GpuFluidSystem::new(
376 &device,
377 &queue,
378 100_000,
379 &scene.global_bind_group_layout,
380 post_res.hdr_texture.format(),
381 config.width,
382 config.height,
383 ));
384 #[cfg(target_arch = "wasm32")]
385 let gpu_fluid: Option<crate::gpu_fluid::GpuFluidSystem> = None;
386 let debug_renderer = Some(crate::debug_renderer::GizmoRendererSystem::new(
387 &device,
388 &scene.global_bind_group_layout,
389 wgpu::TextureFormat::Rgba16Float,
390 wgpu::TextureFormat::Depth32Float,
391 ));
392
393 let scene_state = SceneState {
394 render_pipeline: scene.render_pipeline,
395 render_double_sided_pipeline: scene.render_double_sided_pipeline,
396 wireframe_pipeline: scene.wireframe_pipeline,
397 unlit_pipeline: scene.unlit_pipeline,
398 sky_pipeline: scene.sky_pipeline,
399 water_pipeline: scene.water_pipeline,
400 shadow_pipeline: scene.shadow_pipeline,
401 transparent_pipeline: scene.transparent_pipeline,
402 grid_pipeline: scene.grid_pipeline,
403 shadow_texture_view: scene.shadow_texture_view,
404 shadow_cascade_layer_views: scene.shadow_cascade_layer_views,
405 shadow_depth_texture: scene.shadow_depth_texture,
406 shadow_pass_bind_group_layout: scene.shadow_pass_bind_group_layout,
407 shadow_cascade_uniform_buffers: scene.shadow_cascade_uniform_buffers,
408 shadow_pass_bind_groups: scene.shadow_pass_bind_groups,
409 global_uniform_buffer: scene.global_uniform_buffer,
410 global_bind_group_layout: scene.global_bind_group_layout,
411 global_bind_group: scene.global_bind_group,
412 shadow_bind_group_layout: scene.shadow_bind_group_layout,
413 shadow_bind_group: scene.shadow_bind_group,
414 texture_bind_group_layout: scene.texture_bind_group_layout,
415 skeleton_bind_group_layout: scene.skeleton_bind_group_layout,
416 dummy_skeleton_bind_group: scene.dummy_skeleton_bind_group,
417 instance_bind_group_layout: scene.instance_bind_group_layout,
418 instance_buffer: scene.instance_buffer,
419 instance_bind_group: scene.instance_bind_group,
420 instance_capacity: scene.instance_capacity,
421 };
422
423 #[cfg(not(target_arch = "wasm32"))]
424 let deferred = Some(crate::deferred::DeferredState::new(
425 &device,
426 &scene_state,
427 size.width,
428 size.height,
429 ));
430 #[cfg(target_arch = "wasm32")]
431 let deferred: Option<crate::deferred::DeferredState> = None;
432
433 #[cfg(not(target_arch = "wasm32"))]
434 let gpu_cull = Some(crate::gpu_cull::GpuCullState::new(
435 &device,
436 &scene_state,
437 scene_state.instance_capacity as u32,
438 ));
439 #[cfg(target_arch = "wasm32")]
440 let gpu_cull: Option<crate::gpu_cull::GpuCullState> = None;
441
442 let ssao = deferred.as_ref().map(|def| {
443 crate::ssao::SsaoState::new(&device, &queue, &scene_state, def, size.width, size.height)
444 });
445
446 let ssr = deferred.as_ref().map(|def| {
447 crate::ssr::SsrState::new(
448 &device,
449 &scene_state,
450 def,
451 &post_res.hdr_texture_view,
452 size.width,
453 size.height,
454 )
455 });
456
457 let ssgi = deferred.as_ref().map(|def| {
458 crate::ssgi::SsgiState::new(
459 &device,
460 &scene_state,
461 def,
462 &post_res.hdr_texture_view,
463 size.width,
464 size.height,
465 )
466 });
467
468 let volumetric = deferred.as_ref().map(|def| {
469 crate::volumetric::VolumetricState::new(
470 &device,
471 &scene_state,
472 def,
473 size.width,
474 size.height,
475 )
476 });
477
478 let decal = deferred
479 .as_ref()
480 .map(|def| crate::decal::DecalState::new(&device, &scene_state, def));
481
482 let taa = if let Some(ref def) = deferred {
483 Some(crate::taa::TaaState::new(
484 &device,
485 &post_res.hdr_texture_view,
486 &def.world_position_view,
487 size.width,
488 size.height,
489 ))
490 } else {
491 None
492 };
493
494 let post_state = PostProcessState {
495 hdr_texture: post_res.hdr_texture,
496 hdr_texture_view: post_res.hdr_texture_view,
497 hdr_bind_group: post_res.hdr_bind_group,
498 bloom_extract_texture_view: post_res.bloom_extract_texture_view,
499 bloom_extract_bind_group: post_res.bloom_extract_bind_group,
500 bloom_blur_texture_view: post_res.bloom_blur_texture_view,
501 bloom_blur_bind_group: post_res.bloom_blur_bind_group,
502 post_bind_group_layout: post_res.post_bind_group_layout,
503 bloom_extract_pipeline: post_res.bloom_extract_pipeline,
504 bloom_blur_pipeline: post_res.bloom_blur_pipeline,
505 composite_pipeline: post_res.composite_pipeline,
506 blur_params_buffer: post_res.blur_params_buffer,
507 blur_params_bind_group_layout: post_res.blur_params_bind_group_layout,
508 blur_h_bind_group: post_res.blur_h_bind_group,
509 blur_v_bind_group: post_res.blur_v_bind_group,
510 composite_bloom_bind_group_layout: post_res.composite_bloom_bind_group_layout,
511 composite_bloom_bind_group: post_res.composite_bloom_bind_group,
512 post_params_buffer: post_res.post_params_buffer,
513 post_params_bind_group_layout: post_res.post_params_bind_group_layout,
514 post_params_bind_group: post_res.post_params_bind_group,
515 };
516
517 let fxaa = Some(crate::fxaa::FxaaState::new(
519 &device,
520 config.format,
521 size.width,
522 size.height,
523 ));
524
525 Self {
526 surface,
527 device,
528 queue,
529 config,
530 size,
531 depth_texture_view,
532 scene: scene_state,
533 post: post_state,
534 deferred,
535 gpu_cull,
536 ssao,
537 ssr,
538 ssgi,
539 volumetric,
540 decal,
541 taa,
542 fxaa,
543 gpu_particles,
544 gpu_physics,
545 gpu_fluid,
546 debug_renderer,
547 asset_manager: std::sync::RwLock::new(crate::asset::AssetManager::new()),
548 web_profile: crate::web_profile::WebProfile::auto(),
549 }
550 }
551
552 pub fn rebuild_shaders(&mut self) {
553 tracing::info!("🚀 Rebuilding Shaders Pipeline...");
554 crate::pipeline::rebuild_pipelines(self);
555 }
556
557 pub fn ensure_instance_capacity(&mut self, needed: usize) -> bool {
558 self.scene.ensure_instance_capacity(&self.device, needed)
559 }
560
561 pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
562 if new_size.width > 0 && new_size.height > 0 {
563 self.size = new_size;
564 self.config.width = new_size.width;
565 self.config.height = new_size.height;
566 self.surface.configure(&self.device, &self.config);
567
568 self.depth_texture_view =
569 Self::create_depth_texture(&self.device, new_size.width, new_size.height);
570
571 if let Some(ref mut def) = self.deferred {
572 def.resize(&self.device, new_size.width, new_size.height);
573 if let Some(ref mut decal) = self.decal {
574 decal.resize(&self.device, def);
575 }
576 }
577
578 let sampler = self.device.create_sampler(&wgpu::SamplerDescriptor {
579 address_mode_u: wgpu::AddressMode::ClampToEdge,
580 address_mode_v: wgpu::AddressMode::ClampToEdge,
581 mag_filter: wgpu::FilterMode::Linear,
582 min_filter: wgpu::FilterMode::Linear,
583 ..Default::default()
584 });
585 let (hdr_t, hdr_tv, hdr_bg, be_tv, be_bg, bb_tv, bb_bg, cb_bg) =
586 crate::post_process::create_post_textures(
587 &self.device,
588 &self.post.post_bind_group_layout,
589 &self.post.composite_bloom_bind_group_layout,
590 &sampler,
591 new_size.width,
592 new_size.height,
593 &self.depth_texture_view,
594 );
595 self.post.hdr_texture = hdr_t;
596 self.post.hdr_texture_view = hdr_tv;
597 self.post.hdr_bind_group = hdr_bg;
598 self.post.bloom_extract_texture_view = be_tv;
599 self.post.bloom_extract_bind_group = be_bg;
600 self.post.bloom_blur_texture_view = bb_tv;
601 self.post.bloom_blur_bind_group = bb_bg;
602 self.post.composite_bloom_bind_group = cb_bg;
603
604 let (buf, h_bg, v_bg) = crate::post_process::create_blur_buffers(
605 &self.device,
606 &self.post.blur_params_bind_group_layout,
607 new_size.width,
608 new_size.height,
609 );
610 self.post.blur_params_buffer = buf;
611 self.post.blur_h_bind_group = h_bg;
612 self.post.blur_v_bind_group = v_bg;
613
614 if let (Some(ref mut taa), Some(ref def)) = (&mut self.taa, &self.deferred) {
616 taa.resize(
617 &self.device,
618 &self.post.hdr_texture_view,
619 &def.world_position_view,
620 new_size.width,
621 new_size.height,
622 );
623 }
624 if let (Some(ref mut ssgi), Some(ref def)) = (&mut self.ssgi, &self.deferred) {
625 ssgi.resize(
626 &self.device,
627 def,
628 &self.post.hdr_texture_view,
629 new_size.width,
630 new_size.height,
631 );
632 }
633 if let (Some(ref mut ssao), Some(ref def)) = (&mut self.ssao, &self.deferred) {
634 ssao.resize(
635 &self.device,
636 def,
637 new_size.width,
638 new_size.height,
639 );
640 }
641 if let (Some(ref mut vol), Some(ref def)) = (&mut self.volumetric, &self.deferred) {
642 vol.resize(
643 &self.device,
644 def,
645 new_size.width,
646 new_size.height,
647 );
648 }
649 if let Some(ref mut fxaa) = self.fxaa {
651 fxaa.resize(&self.device, &self.queue, self.config.format, new_size.width, new_size.height);
652 }
653 }
654 }
655
656 pub fn create_cube(&self) -> crate::components::Mesh {
664 crate::asset::AssetManager::create_cube(&self.device)
665 }
666
667 pub fn create_sphere(&self, radius: f32, stacks: u32, slices: u32) -> crate::components::Mesh {
669 crate::asset::AssetManager::create_sphere(&self.device, radius, stacks, slices)
670 }
671
672 pub fn create_plane(&self, size: f32) -> crate::components::Mesh {
674 crate::asset::AssetManager::create_plane(&self.device, size)
675 }
676
677 pub fn create_checkerboard_texture(&self) -> Arc<wgpu::BindGroup> {
680 self.asset_manager
681 .write()
682 .unwrap()
683 .create_checkerboard_texture(
684 &self.device,
685 &self.queue,
686 &self.scene.texture_bind_group_layout,
687 )
688 }
689
690 pub fn create_white_texture(&self) -> Arc<wgpu::BindGroup> {
693 self.asset_manager.write().unwrap().create_white_texture(
694 &self.device,
695 &self.queue,
696 &self.scene.texture_bind_group_layout,
697 )
698 }
699
700 pub fn load_texture(&self, path: &str) -> Result<Arc<wgpu::BindGroup>, String> {
703 self.asset_manager.write().unwrap().load_material_texture(
704 &self.device,
705 &self.queue,
706 &self.scene.texture_bind_group_layout,
707 path,
708 )
709 }
710
711 pub fn load_gltf(&self, path: &str) -> Result<crate::asset::loaders::GltfSceneAsset, String> {
713 let white_tex = self.create_white_texture();
714 self.asset_manager.write().unwrap().load_gltf_scene(
715 &self.device,
716 &self.queue,
717 &self.scene.texture_bind_group_layout,
718 white_tex,
719 path,
720 )
721 }
722
723 pub fn create_skeleton(
724 &self,
725 hierarchy: std::sync::Arc<crate::animation::SkeletonHierarchy>,
726 ) -> crate::components::Skeleton {
727 use wgpu::util::DeviceExt;
728
729 let local_poses: Vec<gizmo_math::Mat4> = hierarchy
731 .joints
732 .iter()
733 .map(|j| j.local_bind_transform)
734 .collect();
735
736 let global_matrices = hierarchy.calculate_global_matrices(&local_poses);
738 let mut joint_matrices = vec![gizmo_math::Mat4::IDENTITY; 128];
739 for (i, joint) in hierarchy.joints.iter().enumerate() {
740 if i < 128 {
741 joint_matrices[i] = global_matrices[i] * joint.inverse_bind_matrix;
742 }
743 }
744
745 let buffer = self
746 .device
747 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
748 label: Some("Skeleton Joint Buffer"),
749 contents: bytemuck::cast_slice(&joint_matrices),
750 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
751 });
752
753 let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
754 label: Some("Skeleton Bind Group"),
755 layout: &self.scene.skeleton_bind_group_layout,
756 entries: &[wgpu::BindGroupEntry {
757 binding: 0,
758 resource: buffer.as_entire_binding(),
759 }],
760 });
761
762 crate::components::Skeleton::new(
763 std::sync::Arc::new(bind_group),
764 std::sync::Arc::new(buffer),
765 hierarchy,
766 local_poses,
767 )
768 }
769
770 pub fn run_post_processing(
771 &self,
772 encoder: &mut wgpu::CommandEncoder,
773 output_view: &wgpu::TextureView,
774 ) {
775 if let Some(ref fxaa) = self.fxaa {
776 if fxaa.enabled {
777 crate::post_process::run_post_processing(self, encoder, &fxaa.input_texture_view);
779 crate::fxaa::run_fxaa_pass(fxaa, encoder, output_view);
780 return;
781 }
782 }
783 crate::post_process::run_post_processing(self, encoder, output_view);
785 }
786
787 pub fn update_post_process(&self, queue: &wgpu::Queue, params: PostProcessUniforms) {
788 queue.write_buffer(
789 &self.post.post_params_buffer,
790 0,
791 bytemuck::cast_slice(&[params]),
792 );
793 }
794
795 pub fn create_mesh(&self, vertices: &[Vertex]) -> wgpu::Buffer {
796 self.device
797 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
798 label: Some("Mesh Vertex Buffer"),
799 contents: bytemuck::cast_slice(vertices),
800 usage: wgpu::BufferUsages::VERTEX,
801 })
802 }
803
804 pub fn create_texture(&self, rgba_bytes: &[u8], width: u32, height: u32) -> wgpu::BindGroup {
805 let mip_level_count = width.max(height).ilog2() + 1;
806 let size = wgpu::Extent3d {
807 width,
808 height,
809 depth_or_array_layers: 1,
810 };
811 let texture = self.device.create_texture(&wgpu::TextureDescriptor {
812 label: Some("Game Texture"),
813 size,
814 mip_level_count,
815 sample_count: 1,
816 dimension: wgpu::TextureDimension::D2,
817 format: wgpu::TextureFormat::Rgba8UnormSrgb,
818 usage: wgpu::TextureUsages::TEXTURE_BINDING
819 | wgpu::TextureUsages::COPY_DST
820 | wgpu::TextureUsages::RENDER_ATTACHMENT,
821 view_formats: &[],
822 });
823 self.queue.write_texture(
824 wgpu::ImageCopyTexture {
825 texture: &texture,
826 mip_level: 0,
827 origin: wgpu::Origin3d::ZERO,
828 aspect: wgpu::TextureAspect::All,
829 },
830 rgba_bytes,
831 wgpu::ImageDataLayout {
832 offset: 0,
833 bytes_per_row: Some(4 * width),
834 rows_per_image: Some(height),
835 },
836 size,
837 );
838
839 Self::generate_mipmaps(
840 &self.device,
841 &self.queue,
842 &texture,
843 wgpu::TextureFormat::Rgba8UnormSrgb,
844 mip_level_count,
845 );
846 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
847 let sampler = self.device.create_sampler(&wgpu::SamplerDescriptor {
848 address_mode_u: wgpu::AddressMode::Repeat,
849 address_mode_v: wgpu::AddressMode::Repeat,
850 address_mode_w: wgpu::AddressMode::Repeat,
851 mag_filter: wgpu::FilterMode::Linear,
852 min_filter: wgpu::FilterMode::Linear,
853 mipmap_filter: wgpu::FilterMode::Linear,
854 ..Default::default()
855 });
856 self.device.create_bind_group(&wgpu::BindGroupDescriptor {
857 layout: &self.scene.texture_bind_group_layout,
858 entries: &[
859 wgpu::BindGroupEntry {
860 binding: 0,
861 resource: wgpu::BindingResource::TextureView(&view),
862 },
863 wgpu::BindGroupEntry {
864 binding: 1,
865 resource: wgpu::BindingResource::Sampler(&sampler),
866 },
867 ],
868 label: Some("texture_bind_group"),
869 })
870 }
871
872 fn create_depth_texture(device: &wgpu::Device, width: u32, height: u32) -> wgpu::TextureView {
873 let tex = device.create_texture(&wgpu::TextureDescriptor {
874 label: Some("Depth Texture"),
875 size: wgpu::Extent3d {
876 width,
877 height,
878 depth_or_array_layers: 1,
879 },
880 mip_level_count: 1,
881 sample_count: 1,
882 dimension: wgpu::TextureDimension::D2,
883 format: wgpu::TextureFormat::Depth32Float,
884 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
885 view_formats: &[],
886 });
887 tex.create_view(&wgpu::TextureViewDescriptor::default())
888 }
889
890 fn generate_mipmaps(
891 device: &wgpu::Device,
892 queue: &wgpu::Queue,
893 texture: &wgpu::Texture,
894 format: wgpu::TextureFormat,
895 mip_level_count: u32,
896 ) {
897 if mip_level_count <= 1 {
898 return;
899 }
900
901 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
902 label: Some("Mipmap Blit Shader"),
903 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/mipmap.wgsl").into()),
904 });
905
906 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
907 label: Some("Mipmap Blit Pipeline"),
908 layout: None,
909 vertex: wgpu::VertexState {
910 module: &shader,
911 entry_point: "vs_main",
912 compilation_options: Default::default(),
913 buffers: &[],
914 },
915 fragment: Some(wgpu::FragmentState {
916 module: &shader,
917 entry_point: "fs_main",
918 compilation_options: Default::default(),
919 targets: &[Some(wgpu::ColorTargetState {
920 format,
921 blend: None,
922 write_mask: wgpu::ColorWrites::ALL,
923 })],
924 }),
925 primitive: wgpu::PrimitiveState {
926 topology: wgpu::PrimitiveTopology::TriangleList,
927 ..Default::default()
928 },
929 depth_stencil: None,
930 multisample: wgpu::MultisampleState::default(),
931 multiview: None,
932 });
933
934 let bind_group_layout = pipeline.get_bind_group_layout(0);
935 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
936 address_mode_u: wgpu::AddressMode::ClampToEdge,
937 address_mode_v: wgpu::AddressMode::ClampToEdge,
938 address_mode_w: wgpu::AddressMode::ClampToEdge,
939 mag_filter: wgpu::FilterMode::Linear,
940 min_filter: wgpu::FilterMode::Linear,
941 mipmap_filter: wgpu::FilterMode::Nearest,
942 ..Default::default()
943 });
944
945 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
946 label: Some("Mipmap Encoder"),
947 });
948
949 let views: Vec<wgpu::TextureView> = (0..mip_level_count)
950 .map(|mip| {
951 texture.create_view(&wgpu::TextureViewDescriptor {
952 label: Some(&format!("Mip {}", mip)),
953 format: None,
954 dimension: None,
955 aspect: wgpu::TextureAspect::All,
956 base_mip_level: mip,
957 mip_level_count: Some(1),
958 base_array_layer: 0,
959 array_layer_count: None,
960 })
961 })
962 .collect();
963
964 for target_mip in 1..mip_level_count as usize {
965 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
966 layout: &bind_group_layout,
967 entries: &[
968 wgpu::BindGroupEntry {
969 binding: 0,
970 resource: wgpu::BindingResource::TextureView(&views[target_mip - 1]),
971 },
972 wgpu::BindGroupEntry {
973 binding: 1,
974 resource: wgpu::BindingResource::Sampler(&sampler),
975 },
976 ],
977 label: None,
978 });
979
980 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
981 label: None,
982 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
983 view: &views[target_mip],
984 resolve_target: None,
985 ops: wgpu::Operations {
986 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
987 store: wgpu::StoreOp::Store,
988 },
989 })],
990 depth_stencil_attachment: None,
991 timestamp_writes: None,
992 occlusion_query_set: None,
993 });
994 pass.set_pipeline(&pipeline);
995 pass.set_bind_group(0, &bind_group, &[]);
996 pass.draw(0..3, 0..1);
997 }
998
999 queue.submit(Some(encoder.finish()));
1000 }
1001}
1002
1003#[cfg(test)]
1004mod tests {
1005 use super::*;
1006
1007 #[test]
1008 fn test_mipmap_level_calculation() {
1009 let width = 4096u32;
1010 let height = 2048u32;
1011 let mip_level_count = width.max(height).ilog2() + 1;
1012 assert_eq!(mip_level_count, 13); let width2 = 512u32;
1015 let height2 = 512u32;
1016 assert_eq!(width2.max(height2).ilog2() + 1, 10);
1017 }
1018
1019 #[test]
1020 fn test_headless_mipmap_generation() {
1021 pollster::block_on(async {
1022 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
1023 backends: wgpu::Backends::all(),
1024 ..Default::default()
1025 });
1026
1027 let adapter = instance
1028 .request_adapter(&wgpu::RequestAdapterOptions {
1029 power_preference: wgpu::PowerPreference::LowPower,
1030 compatible_surface: None,
1031 force_fallback_adapter: false,
1032 })
1033 .await;
1034
1035 let adapter = match adapter {
1036 Some(a) => a,
1037 None => {
1038 tracing::info!(
1039 "No suitable GPU adapter found for headless test. Skipping wgpu test."
1040 );
1041 return;
1042 }
1043 };
1044
1045 let (device, queue) = adapter
1046 .request_device(
1047 &wgpu::DeviceDescriptor {
1048 required_features: wgpu::Features::empty(),
1049 required_limits: wgpu::Limits::downlevel_defaults(),
1050 label: None,
1051 },
1052 None,
1053 )
1054 .await
1055 .unwrap();
1056
1057 let width = 256u32;
1058 let height = 256u32;
1059 let mip_level_count = width.max(height).ilog2() + 1;
1060
1061 let texture = device.create_texture(&wgpu::TextureDescriptor {
1062 label: Some("Test Texture"),
1063 size: wgpu::Extent3d {
1064 width,
1065 height,
1066 depth_or_array_layers: 1,
1067 },
1068 mip_level_count,
1069 sample_count: 1,
1070 dimension: wgpu::TextureDimension::D2,
1071 format: wgpu::TextureFormat::Rgba8UnormSrgb,
1072 usage: wgpu::TextureUsages::TEXTURE_BINDING
1073 | wgpu::TextureUsages::COPY_DST
1074 | wgpu::TextureUsages::RENDER_ATTACHMENT,
1075 view_formats: &[],
1076 });
1077
1078 Renderer::generate_mipmaps(
1080 &device,
1081 &queue,
1082 &texture,
1083 wgpu::TextureFormat::Rgba8UnormSrgb,
1084 mip_level_count,
1085 );
1086
1087 device.poll(wgpu::Maintain::Wait);
1088 });
1089 }
1090}