diff --git a/voxygen/src/render/pipelines/mod.rs b/voxygen/src/render/pipelines/mod.rs index babdc39638..3bff9428ee 100644 --- a/voxygen/src/render/pipelines/mod.rs +++ b/voxygen/src/render/pipelines/mod.rs @@ -16,7 +16,8 @@ use bytemuck::{Pod, Zeroable}; use common::terrain::BlockKind; use vek::*; -pub const MAX_POINT_LIGHT_COUNT: usize = 31; +// TODO: auto insert these into shaders +pub const MAX_POINT_LIGHT_COUNT: usize = 20; pub const MAX_FIGURE_SHADOW_COUNT: usize = 24; pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6; @@ -122,6 +123,7 @@ impl Globals { shadow_planes.y, ], light_shadow_count: [ + // TODO: why do we accept values greater than the max? (light_count % (MAX_POINT_LIGHT_COUNT + 1)) as u32, (shadow_count % (MAX_FIGURE_SHADOW_COUNT + 1)) as u32, (directed_light_count % (MAX_DIRECTED_LIGHT_COUNT + 1)) as u32, @@ -238,7 +240,10 @@ pub struct GlobalModel { pub struct GlobalsBindGroup { pub(super) bind_group: wgpu::BindGroup, - pub(super) shadow_textures: wgpu::BindGroup, +} + +pub struct ShadowTexturesBindGroup { + pub(super) bind_group: wgpu::BindGroup, } pub struct GlobalsLayouts { @@ -471,8 +476,6 @@ impl GlobalsLayouts { global_model: &GlobalModel, lod_data: &lod_terrain::LodData, noise: &Texture, - point_shadow_map: &Texture, - directed_shadow_map: &Texture, ) -> GlobalsBindGroup { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, @@ -537,7 +540,16 @@ impl GlobalsLayouts { ], }); - let shadow_textures = device.create_bind_group(&wgpu::BindGroupDescriptor { + GlobalsBindGroup { bind_group } + } + + pub fn bind_shadow_textures( + &self, + device: &wgpu::Device, + point_shadow_map: &Texture, + directed_shadow_map: &Texture, + ) -> ShadowTexturesBindGroup { + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, layout: &self.shadow_textures, entries: &[ @@ -560,10 +572,7 @@ impl GlobalsLayouts { ], }); - GlobalsBindGroup { - bind_group, - shadow_textures, - } + ShadowTexturesBindGroup { bind_group } } pub fn bind_col_light( diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index eee3c5cc76..f488fa1a6c 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -8,11 +8,11 @@ use super::{ model::{DynamicModel, Model}, pipelines::{ clouds, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite, terrain, - ui, GlobalsLayouts, + ui, GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup, }, texture::Texture, - AaMode, AddressMode, CloudMode, FilterMode, FluidMode, GlobalsBindGroup, LightingMode, - RenderError, RenderMode, ShadowMapMode, ShadowMode, Vertex, + AaMode, AddressMode, CloudMode, FilterMode, FluidMode, LightingMode, RenderError, RenderMode, + ShadowMapMode, ShadowMode, Vertex, }; use common::assets::{self, AssetExt, AssetHandle}; use common_base::span; @@ -117,7 +117,7 @@ impl Shaders { /// A type that holds shadow map data. Since shadow mapping may not be /// supported on all platforms, we try to keep it separate. -pub struct ShadowMapRenderer { +struct ShadowMapRenderer { // directed_encoder: gfx::Encoder, // point_encoder: gfx::Encoder, directed_depth: Texture, @@ -130,6 +130,28 @@ pub struct ShadowMapRenderer { layout: shadow::ShadowLayout, } +enum ShadowMap { + Enabled(ShadowMapRenderer), + Disabled { + dummy_point: Texture, // Cube texture + dummy_directed: Texture, + }, +} + +impl ShadowMap { + fn textures(&self) -> (&Texture, &Texture) { + match self { + Self::Enabled(renderer) => (&renderer.point_depth, &renderer.directed_depth), + Self::Disabled { + dummy_point, + dummy_directed, + } => (dummy_point, dummy_directed), + } + } + + fn is_enabled(&self) -> bool { matches!(self, Self::Enabled(_)) } +} + /// A type that stores all the layouts associated with this renderer. struct Layouts { global: GlobalsLayouts, @@ -187,6 +209,8 @@ impl Locals { &mut self, device: &wgpu::Device, layouts: &Layouts, + // Call when these are recreated and need to be rebound + // e.g. resizing tgt_color_view: &wgpu::TextureView, tgt_depth_view: &wgpu::TextureView, tgt_color_pp_view: &wgpu::TextureView, @@ -211,8 +235,6 @@ impl Locals { /// GPU, along with pipeline state objects (PSOs) needed to renderer different /// kinds of models to the screen. pub struct Renderer { - // TODO: why???? - //window: &'a winit::window::Window, device: wgpu::Device, queue: wgpu::Queue, swap_chain: wgpu::SwapChain, @@ -228,9 +250,8 @@ pub struct Renderer { sampler: wgpu::Sampler, - shadow_map: Option, - dummy_shadow_cube_tex: Texture, - dummy_shadow_tex: Texture, + shadow_map: ShadowMap, + shadow_bind: ShadowTexturesBindGroup, layouts: Layouts, @@ -273,9 +294,7 @@ impl Renderer { let dims = window.inner_size(); - let instance = wgpu::Instance::new( - wgpu::BackendBit::PRIMARY, /* | wgpu::BackendBit::SECONDARY */ - ); + let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY | wgpu::BackendBit::SECONDARY); // This is unsafe because the window handle must be valid, if you find a way to // have an invalid winit::Window then you have bigger issues @@ -290,19 +309,19 @@ impl Renderer { )) .ok_or(RenderError::CouldNotFindAdapter)?; - use wgpu::{Features, Limits}; - - let mut limits = Limits::default(); - limits.max_bind_groups = 5; - limits.max_push_constant_size = 64; + let limits = wgpu::Limits { + max_bind_groups: 5, + max_push_constant_size: 64, + ..Default::default() + }; let (device, queue) = futures::executor::block_on(adapter.request_device( &wgpu::DeviceDescriptor { // TODO label: None, - features: Features::DEPTH_CLAMPING - | Features::ADDRESS_MODE_CLAMP_TO_BORDER - | Features::PUSH_CONSTANTS, + features: wgpu::Features::DEPTH_CLAMPING + | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER + | wgpu::Features::PUSH_CONSTANTS, limits, shader_validation: true, }, @@ -341,9 +360,6 @@ impl Renderer { let shaders = Shaders::load_expect(""); - let (dummy_shadow_cube_tex, dummy_shadow_tex) = - Self::create_dummy_shadow_tex(&device, &queue); - let layouts = { let global = GlobalsLayouts::new(&device); @@ -412,11 +428,10 @@ impl Renderer { let layout = shadow::ShadowLayout::new(&device); - Some(ShadowMapRenderer { - directed_depth, - + ShadowMap::Enabled(ShadowMapRenderer { // point_encoder: factory.create_command_buffer().into(), // directed_encoder: factory.create_command_buffer().into(), + directed_depth, point_depth, point_pipeline, @@ -426,7 +441,18 @@ impl Renderer { layout, }) } else { - None + let (dummy_point, dummy_directed) = Self::create_dummy_shadow_tex(&device, &queue); + ShadowMap::Disabled { + dummy_point, + dummy_directed, + } + }; + + let shadow_bind = { + let (point, directed) = shadow_map.textures(); + layouts + .global + .bind_shadow_textures(&device, point, directed) }; let sampler = device.create_sampler(&wgpu::SamplerDescriptor { @@ -449,16 +475,10 @@ impl Renderer { Some(wgpu::AddressMode::Repeat), )?; - let clouds_locals = { - let mut consts = Consts::new(&device, 1); - consts.update(&device, &queue, &[clouds::Locals::default()], 0); - consts - }; - let postprocess_locals = { - let mut consts = Consts::new(&device, 1); - consts.update(&device, &queue, &[postprocess::Locals::default()], 0); - consts - }; + let clouds_locals = + Self::create_consts_inner(&device, &queue, &[clouds::Locals::default()]); + let postprocess_locals = + Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]); let locals = Locals::new( &device, @@ -487,8 +507,7 @@ impl Renderer { sampler, shadow_map, - dummy_shadow_cube_tex, - dummy_shadow_tex, + shadow_bind, layouts, @@ -569,14 +588,18 @@ impl Renderer { &self.sampler, ); - // TODO: rebind globals - if let (Some(shadow_map), ShadowMode::Map(mode)) = - (self.shadow_map.as_mut(), self.mode.shadow) + if let (ShadowMap::Enabled(shadow_map), ShadowMode::Map(mode)) = + (&mut self.shadow_map, self.mode.shadow) { match Self::create_shadow_views(&mut self.device, (dims.x, dims.y), &mode) { Ok((point_depth, directed_depth)) => { shadow_map.point_depth = point_depth; shadow_map.directed_depth = directed_depth; + self.shadow_bind = self.layouts.global.bind_shadow_textures( + &self.device, + &shadow_map.point_depth, + &shadow_map.directed_depth, + ); }, Err(err) => { warn!("Could not create shadow map views: {:?}", err); @@ -909,7 +932,7 @@ impl Renderer { /// Get the resolution of the shadow render target. pub fn get_shadow_resolution(&self) -> (Vec2, Vec2) { - if let Some(shadow_map) = &self.shadow_map { + if let ShadowMap::Enabled(shadow_map) = &self.shadow_map { ( shadow_map.point_depth.get_dimensions().xy(), shadow_map.directed_depth.get_dimensions().xy(), @@ -1020,7 +1043,7 @@ impl Renderer { &self.shaders.read(), &self.mode, &self.sc_desc, - self.shadow_map.is_some(), + self.shadow_map.is_enabled(), ) { Ok(( skybox_pipeline, @@ -1053,12 +1076,12 @@ impl Renderer { Some(point_pipeline), Some(terrain_directed_pipeline), Some(figure_directed_pipeline), - Some(shadow_map), + ShadowMap::Enabled(shadow_map), ) = ( point_shadow_pipeline, terrain_directed_shadow_pipeline, figure_directed_shadow_pipeline, - self.shadow_map.as_mut(), + &mut self.shadow_map, ) { shadow_map.point_pipeline = point_pipeline; shadow_map.terrain_directed_pipeline = terrain_directed_pipeline; @@ -1071,8 +1094,16 @@ impl Renderer { /// Create a new set of constants with the provided values. pub fn create_consts(&mut self, vals: &[T]) -> Consts { - let mut consts = Consts::new(&self.device, vals.len()); - consts.update(&self.device, &self.queue, vals, 0); + Self::create_consts_inner(&self.device, &self.queue, vals) + } + + pub fn create_consts_inner( + device: &wgpu::Device, + queue: &wgpu::Queue, + vals: &[T], + ) -> Consts { + let mut consts = Consts::new(device, vals.len()); + consts.update(device, queue, vals, 0); consts } diff --git a/voxygen/src/render/renderer/binding.rs b/voxygen/src/render/renderer/binding.rs index 33f4d61cb1..9bc704cda5 100644 --- a/voxygen/src/render/renderer/binding.rs +++ b/voxygen/src/render/renderer/binding.rs @@ -15,19 +15,9 @@ impl Renderer { global_model: &GlobalModel, lod_data: &lod_terrain::LodData, ) -> GlobalsBindGroup { - let (point_shadow_map, directed_shadow_map) = match &self.shadow_map { - Some(shadow_map) => (&shadow_map.point_depth, &shadow_map.directed_depth), - None => (&self.dummy_shadow_cube_tex, &self.dummy_shadow_tex), - }; - - self.layouts.global.bind( - &self.device, - global_model, - lod_data, - &self.noise_tex, - point_shadow_map, - directed_shadow_map, - ) + self.layouts + .global + .bind(&self.device, global_model, lod_data, &self.noise_tex) } pub fn create_ui_bound_locals(&mut self, vals: &[ui::Locals]) -> ui::BoundLocals { diff --git a/voxygen/src/render/renderer/drawer.rs b/voxygen/src/render/renderer/drawer.rs index 6e4ff852f1..aebd328ae1 100644 --- a/voxygen/src/render/renderer/drawer.rs +++ b/voxygen/src/render/renderer/drawer.rs @@ -9,7 +9,7 @@ use super::{ terrain, ui, ColLights, GlobalsBindGroup, Light, Shadow, }, }, - Renderer, ShadowMapRenderer, + Renderer, ShadowMap, ShadowMapRenderer, }; use core::{num::NonZeroU32, ops::Range}; use vek::Aabr; @@ -63,7 +63,7 @@ impl<'a> Drawer<'a> { }); render_pass.set_bind_group(0, &self.globals.bind_group, &[]); - render_pass.set_bind_group(1, &self.globals.shadow_textures, &[]); + render_pass.set_bind_group(1, &self.renderer.shadow_bind.bind_group, &[]); FirstPassDrawer { render_pass, @@ -72,7 +72,7 @@ impl<'a> Drawer<'a> { } pub fn shadow_pass(&mut self) -> Option { - if let Some(ref shadow_renderer) = self.renderer.shadow_map { + if let ShadowMap::Enabled(ref shadow_renderer) = self.renderer.shadow_map { let mut render_pass = self.encoder .as_mut() @@ -121,7 +121,7 @@ impl<'a> Drawer<'a> { }); render_pass.set_bind_group(0, &self.globals.bind_group, &[]); - render_pass.set_bind_group(1, &self.globals.shadow_textures, &[]); + render_pass.set_bind_group(1, &self.renderer.shadow_bind.bind_group, &[]); SecondPassDrawer { render_pass, @@ -159,7 +159,7 @@ impl<'a> Drawer<'a> { matrices: &[shadow::PointLightMatrix; 126], chunks: impl Clone + Iterator, &'b terrain::BoundLocals)>, ) { - if let Some(ref shadow_renderer) = self.renderer.shadow_map { + if let ShadowMap::Enabled(ref shadow_renderer) = self.renderer.shadow_map { const STRIDE: usize = std::mem::size_of::(); let data = bytemuck::cast_slice(matrices);