Properly rebind shadow textures when they are changed

This commit is contained in:
Imbris 2020-12-20 16:03:05 -05:00 committed by Avi Weinstock
parent 057ba79b57
commit 863ed7fb80
4 changed files with 105 additions and 75 deletions

View File

@ -16,7 +16,8 @@ use bytemuck::{Pod, Zeroable};
use common::terrain::BlockKind; use common::terrain::BlockKind;
use vek::*; 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_FIGURE_SHADOW_COUNT: usize = 24;
pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6; pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6;
@ -122,6 +123,7 @@ impl Globals {
shadow_planes.y, shadow_planes.y,
], ],
light_shadow_count: [ light_shadow_count: [
// TODO: why do we accept values greater than the max?
(light_count % (MAX_POINT_LIGHT_COUNT + 1)) as u32, (light_count % (MAX_POINT_LIGHT_COUNT + 1)) as u32,
(shadow_count % (MAX_FIGURE_SHADOW_COUNT + 1)) as u32, (shadow_count % (MAX_FIGURE_SHADOW_COUNT + 1)) as u32,
(directed_light_count % (MAX_DIRECTED_LIGHT_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 struct GlobalsBindGroup {
pub(super) bind_group: wgpu::BindGroup, pub(super) bind_group: wgpu::BindGroup,
pub(super) shadow_textures: wgpu::BindGroup, }
pub struct ShadowTexturesBindGroup {
pub(super) bind_group: wgpu::BindGroup,
} }
pub struct GlobalsLayouts { pub struct GlobalsLayouts {
@ -471,8 +476,6 @@ impl GlobalsLayouts {
global_model: &GlobalModel, global_model: &GlobalModel,
lod_data: &lod_terrain::LodData, lod_data: &lod_terrain::LodData,
noise: &Texture, noise: &Texture,
point_shadow_map: &Texture,
directed_shadow_map: &Texture,
) -> GlobalsBindGroup { ) -> GlobalsBindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None, 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, label: None,
layout: &self.shadow_textures, layout: &self.shadow_textures,
entries: &[ entries: &[
@ -560,10 +572,7 @@ impl GlobalsLayouts {
], ],
}); });
GlobalsBindGroup { ShadowTexturesBindGroup { bind_group }
bind_group,
shadow_textures,
}
} }
pub fn bind_col_light<Locals>( pub fn bind_col_light<Locals>(

View File

@ -8,11 +8,11 @@ use super::{
model::{DynamicModel, Model}, model::{DynamicModel, Model},
pipelines::{ pipelines::{
clouds, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite, terrain, clouds, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite, terrain,
ui, GlobalsLayouts, ui, GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup,
}, },
texture::Texture, texture::Texture,
AaMode, AddressMode, CloudMode, FilterMode, FluidMode, GlobalsBindGroup, LightingMode, AaMode, AddressMode, CloudMode, FilterMode, FluidMode, LightingMode, RenderError, RenderMode,
RenderError, RenderMode, ShadowMapMode, ShadowMode, Vertex, ShadowMapMode, ShadowMode, Vertex,
}; };
use common::assets::{self, AssetExt, AssetHandle}; use common::assets::{self, AssetExt, AssetHandle};
use common_base::span; use common_base::span;
@ -117,7 +117,7 @@ impl Shaders {
/// A type that holds shadow map data. Since shadow mapping may not be /// A type that holds shadow map data. Since shadow mapping may not be
/// supported on all platforms, we try to keep it separate. /// supported on all platforms, we try to keep it separate.
pub struct ShadowMapRenderer { struct ShadowMapRenderer {
// directed_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>, // directed_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
// point_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>, // point_encoder: gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
directed_depth: Texture, directed_depth: Texture,
@ -130,6 +130,28 @@ pub struct ShadowMapRenderer {
layout: shadow::ShadowLayout, 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. /// A type that stores all the layouts associated with this renderer.
struct Layouts { struct Layouts {
global: GlobalsLayouts, global: GlobalsLayouts,
@ -187,6 +209,8 @@ impl Locals {
&mut self, &mut self,
device: &wgpu::Device, device: &wgpu::Device,
layouts: &Layouts, layouts: &Layouts,
// Call when these are recreated and need to be rebound
// e.g. resizing
tgt_color_view: &wgpu::TextureView, tgt_color_view: &wgpu::TextureView,
tgt_depth_view: &wgpu::TextureView, tgt_depth_view: &wgpu::TextureView,
tgt_color_pp_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 /// GPU, along with pipeline state objects (PSOs) needed to renderer different
/// kinds of models to the screen. /// kinds of models to the screen.
pub struct Renderer { pub struct Renderer {
// TODO: why????
//window: &'a winit::window::Window,
device: wgpu::Device, device: wgpu::Device,
queue: wgpu::Queue, queue: wgpu::Queue,
swap_chain: wgpu::SwapChain, swap_chain: wgpu::SwapChain,
@ -228,9 +250,8 @@ pub struct Renderer {
sampler: wgpu::Sampler, sampler: wgpu::Sampler,
shadow_map: Option<ShadowMapRenderer>, shadow_map: ShadowMap,
dummy_shadow_cube_tex: Texture, shadow_bind: ShadowTexturesBindGroup,
dummy_shadow_tex: Texture,
layouts: Layouts, layouts: Layouts,
@ -273,9 +294,7 @@ impl Renderer {
let dims = window.inner_size(); let dims = window.inner_size();
let instance = wgpu::Instance::new( let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY | wgpu::BackendBit::SECONDARY);
wgpu::BackendBit::PRIMARY, /* | wgpu::BackendBit::SECONDARY */
);
// This is unsafe because the window handle must be valid, if you find a way to // 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 // have an invalid winit::Window then you have bigger issues
@ -290,19 +309,19 @@ impl Renderer {
)) ))
.ok_or(RenderError::CouldNotFindAdapter)?; .ok_or(RenderError::CouldNotFindAdapter)?;
use wgpu::{Features, Limits}; let limits = wgpu::Limits {
max_bind_groups: 5,
let mut limits = Limits::default(); max_push_constant_size: 64,
limits.max_bind_groups = 5; ..Default::default()
limits.max_push_constant_size = 64; };
let (device, queue) = futures::executor::block_on(adapter.request_device( let (device, queue) = futures::executor::block_on(adapter.request_device(
&wgpu::DeviceDescriptor { &wgpu::DeviceDescriptor {
// TODO // TODO
label: None, label: None,
features: Features::DEPTH_CLAMPING features: wgpu::Features::DEPTH_CLAMPING
| Features::ADDRESS_MODE_CLAMP_TO_BORDER | wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER
| Features::PUSH_CONSTANTS, | wgpu::Features::PUSH_CONSTANTS,
limits, limits,
shader_validation: true, shader_validation: true,
}, },
@ -341,9 +360,6 @@ impl Renderer {
let shaders = Shaders::load_expect(""); let shaders = Shaders::load_expect("");
let (dummy_shadow_cube_tex, dummy_shadow_tex) =
Self::create_dummy_shadow_tex(&device, &queue);
let layouts = { let layouts = {
let global = GlobalsLayouts::new(&device); let global = GlobalsLayouts::new(&device);
@ -412,11 +428,10 @@ impl Renderer {
let layout = shadow::ShadowLayout::new(&device); let layout = shadow::ShadowLayout::new(&device);
Some(ShadowMapRenderer { ShadowMap::Enabled(ShadowMapRenderer {
directed_depth,
// point_encoder: factory.create_command_buffer().into(), // point_encoder: factory.create_command_buffer().into(),
// directed_encoder: factory.create_command_buffer().into(), // directed_encoder: factory.create_command_buffer().into(),
directed_depth,
point_depth, point_depth,
point_pipeline, point_pipeline,
@ -426,7 +441,18 @@ impl Renderer {
layout, layout,
}) })
} else { } 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 { let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
@ -449,16 +475,10 @@ impl Renderer {
Some(wgpu::AddressMode::Repeat), Some(wgpu::AddressMode::Repeat),
)?; )?;
let clouds_locals = { let clouds_locals =
let mut consts = Consts::new(&device, 1); Self::create_consts_inner(&device, &queue, &[clouds::Locals::default()]);
consts.update(&device, &queue, &[clouds::Locals::default()], 0); let postprocess_locals =
consts Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]);
};
let postprocess_locals = {
let mut consts = Consts::new(&device, 1);
consts.update(&device, &queue, &[postprocess::Locals::default()], 0);
consts
};
let locals = Locals::new( let locals = Locals::new(
&device, &device,
@ -487,8 +507,7 @@ impl Renderer {
sampler, sampler,
shadow_map, shadow_map,
dummy_shadow_cube_tex, shadow_bind,
dummy_shadow_tex,
layouts, layouts,
@ -569,14 +588,18 @@ impl Renderer {
&self.sampler, &self.sampler,
); );
// TODO: rebind globals if let (ShadowMap::Enabled(shadow_map), ShadowMode::Map(mode)) =
if let (Some(shadow_map), ShadowMode::Map(mode)) = (&mut self.shadow_map, self.mode.shadow)
(self.shadow_map.as_mut(), self.mode.shadow)
{ {
match Self::create_shadow_views(&mut self.device, (dims.x, dims.y), &mode) { match Self::create_shadow_views(&mut self.device, (dims.x, dims.y), &mode) {
Ok((point_depth, directed_depth)) => { Ok((point_depth, directed_depth)) => {
shadow_map.point_depth = point_depth; shadow_map.point_depth = point_depth;
shadow_map.directed_depth = directed_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) => { Err(err) => {
warn!("Could not create shadow map views: {:?}", err); warn!("Could not create shadow map views: {:?}", err);
@ -909,7 +932,7 @@ impl Renderer {
/// Get the resolution of the shadow render target. /// Get the resolution of the shadow render target.
pub fn get_shadow_resolution(&self) -> (Vec2<u32>, Vec2<u32>) { pub fn get_shadow_resolution(&self) -> (Vec2<u32>, Vec2<u32>) {
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.point_depth.get_dimensions().xy(),
shadow_map.directed_depth.get_dimensions().xy(), shadow_map.directed_depth.get_dimensions().xy(),
@ -1020,7 +1043,7 @@ impl Renderer {
&self.shaders.read(), &self.shaders.read(),
&self.mode, &self.mode,
&self.sc_desc, &self.sc_desc,
self.shadow_map.is_some(), self.shadow_map.is_enabled(),
) { ) {
Ok(( Ok((
skybox_pipeline, skybox_pipeline,
@ -1053,12 +1076,12 @@ impl Renderer {
Some(point_pipeline), Some(point_pipeline),
Some(terrain_directed_pipeline), Some(terrain_directed_pipeline),
Some(figure_directed_pipeline), Some(figure_directed_pipeline),
Some(shadow_map), ShadowMap::Enabled(shadow_map),
) = ( ) = (
point_shadow_pipeline, point_shadow_pipeline,
terrain_directed_shadow_pipeline, terrain_directed_shadow_pipeline,
figure_directed_shadow_pipeline, figure_directed_shadow_pipeline,
self.shadow_map.as_mut(), &mut self.shadow_map,
) { ) {
shadow_map.point_pipeline = point_pipeline; shadow_map.point_pipeline = point_pipeline;
shadow_map.terrain_directed_pipeline = terrain_directed_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. /// Create a new set of constants with the provided values.
pub fn create_consts<T: Copy + bytemuck::Pod>(&mut self, vals: &[T]) -> Consts<T> { pub fn create_consts<T: Copy + bytemuck::Pod>(&mut self, vals: &[T]) -> Consts<T> {
let mut consts = Consts::new(&self.device, vals.len()); Self::create_consts_inner(&self.device, &self.queue, vals)
consts.update(&self.device, &self.queue, vals, 0); }
pub fn create_consts_inner<T: Copy + bytemuck::Pod>(
device: &wgpu::Device,
queue: &wgpu::Queue,
vals: &[T],
) -> Consts<T> {
let mut consts = Consts::new(device, vals.len());
consts.update(device, queue, vals, 0);
consts consts
} }

View File

@ -15,19 +15,9 @@ impl Renderer {
global_model: &GlobalModel, global_model: &GlobalModel,
lod_data: &lod_terrain::LodData, lod_data: &lod_terrain::LodData,
) -> GlobalsBindGroup { ) -> GlobalsBindGroup {
let (point_shadow_map, directed_shadow_map) = match &self.shadow_map { self.layouts
Some(shadow_map) => (&shadow_map.point_depth, &shadow_map.directed_depth), .global
None => (&self.dummy_shadow_cube_tex, &self.dummy_shadow_tex), .bind(&self.device, global_model, lod_data, &self.noise_tex)
};
self.layouts.global.bind(
&self.device,
global_model,
lod_data,
&self.noise_tex,
point_shadow_map,
directed_shadow_map,
)
} }
pub fn create_ui_bound_locals(&mut self, vals: &[ui::Locals]) -> ui::BoundLocals { pub fn create_ui_bound_locals(&mut self, vals: &[ui::Locals]) -> ui::BoundLocals {

View File

@ -9,7 +9,7 @@ use super::{
terrain, ui, ColLights, GlobalsBindGroup, Light, Shadow, terrain, ui, ColLights, GlobalsBindGroup, Light, Shadow,
}, },
}, },
Renderer, ShadowMapRenderer, Renderer, ShadowMap, ShadowMapRenderer,
}; };
use core::{num::NonZeroU32, ops::Range}; use core::{num::NonZeroU32, ops::Range};
use vek::Aabr; 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(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 { FirstPassDrawer {
render_pass, render_pass,
@ -72,7 +72,7 @@ impl<'a> Drawer<'a> {
} }
pub fn shadow_pass(&mut self) -> Option<ShadowDrawer> { pub fn shadow_pass(&mut self) -> Option<ShadowDrawer> {
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 = let mut render_pass =
self.encoder self.encoder
.as_mut() .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(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 { SecondPassDrawer {
render_pass, render_pass,
@ -159,7 +159,7 @@ impl<'a> Drawer<'a> {
matrices: &[shadow::PointLightMatrix; 126], matrices: &[shadow::PointLightMatrix; 126],
chunks: impl Clone + Iterator<Item = (&'b Model<terrain::Vertex>, &'b terrain::BoundLocals)>, chunks: impl Clone + Iterator<Item = (&'b Model<terrain::Vertex>, &'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::<shadow::PointLightMatrix>(); const STRIDE: usize = std::mem::size_of::<shadow::PointLightMatrix>();
let data = bytemuck::cast_slice(matrices); let data = bytemuck::cast_slice(matrices);