From 6cabd74871e090801b63d9d7b4875c3d4d2bf340 Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 29 Jul 2021 02:06:56 -0400 Subject: [PATCH] Make bloom optional with a config option that is not exposed in the UI (to give artists time to refine bloom before exposing the option) --- .../dual-downsample-filtered-frag.glsl | 2 +- .../voxygen/shaders/dual-downsample-frag.glsl | 2 +- assets/voxygen/shaders/figure-frag.glsl | 2 +- assets/voxygen/shaders/include/constants.glsl | 3 + assets/voxygen/shaders/postprocess-frag.glsl | 14 +- voxygen/src/menu/char_selection/mod.rs | 7 +- voxygen/src/render/mod.rs | 42 +++ voxygen/src/render/pipelines/bloom.rs | 12 +- voxygen/src/render/pipelines/postprocess.rs | 145 +++++---- voxygen/src/render/renderer.rs | 290 +++++++++++------- voxygen/src/render/renderer/drawer.rs | 45 +-- voxygen/src/render/renderer/locals.rs | 39 ++- .../src/render/renderer/pipeline_creation.rs | 140 +++++---- voxygen/src/scene/figure/mod.rs | 2 +- voxygen/src/scene/mod.rs | 4 +- voxygen/src/scene/terrain.rs | 2 +- voxygen/src/session/mod.rs | 3 +- voxygen/src/settings/graphics.rs | 2 + voxygen/src/window.rs | 3 +- 19 files changed, 480 insertions(+), 279 deletions(-) diff --git a/assets/voxygen/shaders/dual-downsample-filtered-frag.glsl b/assets/voxygen/shaders/dual-downsample-filtered-frag.glsl index 1c381e166b..9ce06098ae 100644 --- a/assets/voxygen/shaders/dual-downsample-filtered-frag.glsl +++ b/assets/voxygen/shaders/dual-downsample-filtered-frag.glsl @@ -5,7 +5,7 @@ uniform texture2D t_src_color; layout(set = 0, binding = 1) uniform sampler s_src_color; layout(set = 0, binding = 2) -// TODO: refactor in rust + uniform u_locals { vec2 halfpixel; }; diff --git a/assets/voxygen/shaders/dual-downsample-frag.glsl b/assets/voxygen/shaders/dual-downsample-frag.glsl index fb8cfdf9dc..af9fa46c09 100644 --- a/assets/voxygen/shaders/dual-downsample-frag.glsl +++ b/assets/voxygen/shaders/dual-downsample-frag.glsl @@ -5,7 +5,7 @@ uniform texture2D t_src_color; layout(set = 0, binding = 1) uniform sampler s_src_color; layout(set = 0, binding = 2) -// TODO: refactor in rust + uniform u_locals { vec2 halfpixel; }; diff --git a/assets/voxygen/shaders/figure-frag.glsl b/assets/voxygen/shaders/figure-frag.glsl index ebd636fea7..0c1948023c 100644 --- a/assets/voxygen/shaders/figure-frag.glsl +++ b/assets/voxygen/shaders/figure-frag.glsl @@ -198,7 +198,7 @@ void main() { // For now, just make glowing material light be the same colour as the surface // TODO: Add a way to control this better outside the shaders if ((material & (1u << 0u)) > 0u) { - emitted_light += 10 * surf_color; + emitted_light += 20 * surf_color; } float glow_mag = length(model_glow.xyz); diff --git a/assets/voxygen/shaders/include/constants.glsl b/assets/voxygen/shaders/include/constants.glsl index f1bc2a8aa9..8cd90ca072 100644 --- a/assets/voxygen/shaders/include/constants.glsl +++ b/assets/voxygen/shaders/include/constants.glsl @@ -24,6 +24,9 @@ #define SHADOW_MODE_CHEAP 1 #define SHADOW_MODE_MAP 2 +#define BLOOM_DISABLED 0 +#define BLOOM_ENABLED 1 + /* Unlike the other flags (for now anyway), these are bitmask values */ #define LIGHTING_TYPE_REFLECTION 0x01 #define LIGHTING_TYPE_TRANSMISSION 0x02 diff --git a/assets/voxygen/shaders/postprocess-frag.glsl b/assets/voxygen/shaders/postprocess-frag.glsl index 6308d73d16..57df871073 100644 --- a/assets/voxygen/shaders/postprocess-frag.glsl +++ b/assets/voxygen/shaders/postprocess-frag.glsl @@ -25,19 +25,21 @@ layout(set = 1, binding = 0) uniform texture2D t_src_color; layout(set = 1, binding = 1) -uniform texture2D t_src_bloom; -layout(set = 1, binding = 2) uniform sampler s_src_color; - layout(location = 0) in vec2 uv; -layout (std140, set = 1, binding = 3) +layout (std140, set = 1, binding = 2) uniform u_locals { mat4 proj_mat_inv; mat4 view_mat_inv; }; +#if (BLOOM == BLOOM_ENABLED) +layout(set = 1, binding = 3) +uniform texture2D t_src_bloom; +#endif + layout(location = 0) out vec4 tgt_color; vec3 rgb2hsv(vec3 c) { @@ -185,10 +187,12 @@ void main() { vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy); // Bloom + #if (BLOOM == BLOOM_ENABLED) // divide by 4.0 to account for adding blurred layers together vec4 bloom = textureLod(sampler2D(t_src_bloom, s_src_color), uv, 0) / 4.0; float bloom_factor = 0.10; - aa_color = aa_color * (1.0 - bloom_factor) + bloom * bloom_factor; + aa_color = mix(aa_color, bloom, bloom_factor); + #endif // Tonemapping float exposure_offset = 1.0; diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 688d53f465..a5e96f3c20 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -257,11 +257,8 @@ impl PlayState for CharSelectionState { if let Some(mut second_pass) = drawer.second_pass() { second_pass.draw_clouds(); } - // Bloom - // TODO: make optional - { - drawer.run_bloom_passes() - } + // Bloom (does nothing if bloom is disabled) + drawer.run_bloom_passes(); // PostProcess and UI let mut third_pass = drawer.third_pass(); third_pass.draw_postprocess(); diff --git a/voxygen/src/render/mod.rs b/voxygen/src/render/mod.rs index d7598d5508..a2a8786c01 100644 --- a/voxygen/src/render/mod.rs +++ b/voxygen/src/render/mod.rs @@ -274,7 +274,49 @@ pub struct RenderMode { pub fluid: FluidMode, pub lighting: LightingMode, pub shadow: ShadowMode, + pub bloom: bool, + pub upscale_mode: UpscaleMode, pub present_mode: PresentMode, pub profiler_enabled: bool, } + +impl RenderMode { + fn split(self) -> (PipelineModes, OtherModes) { + ( + PipelineModes { + aa: self.aa, + cloud: self.cloud, + fluid: self.fluid, + lighting: self.lighting, + shadow: self.shadow, + bloom: self.bloom, + }, + OtherModes { + upscale_mode: self.upscale_mode, + present_mode: self.present_mode, + profiler_enabled: self.profiler_enabled, + }, + ) + } +} + +/// Render modes that require pipeline recreation (e.g. shader recompilation) +/// when changed +#[derive(PartialEq, Clone, Debug)] +pub struct PipelineModes { + aa: AaMode, + cloud: CloudMode, + fluid: FluidMode, + lighting: LightingMode, + pub shadow: ShadowMode, + bloom: bool, +} + +/// Other render modes that don't effect pipelines +#[derive(PartialEq, Clone, Debug)] +struct OtherModes { + upscale_mode: UpscaleMode, + present_mode: PresentMode, + profiler_enabled: bool, +} diff --git a/voxygen/src/render/pipelines/bloom.rs b/voxygen/src/render/pipelines/bloom.rs index 061ef0871f..64463278e2 100644 --- a/voxygen/src/render/pipelines/bloom.rs +++ b/voxygen/src/render/pipelines/bloom.rs @@ -18,11 +18,15 @@ pub struct BindGroup { #[repr(C)] #[derive(Copy, Clone, Debug, Zeroable, Pod)] -pub struct HalfPixel([f32; 2]); +pub struct Locals { + halfpixel: [f32; 2], +} -impl HalfPixel { +impl Locals { pub fn new(source_texture_resolution: Vec2) -> Self { - Self(source_texture_resolution.map(|e| 0.5 / e).into_array()) + Self { + halfpixel: source_texture_resolution.map(|e| 0.5 / e).into_array(), + } } } @@ -77,7 +81,7 @@ impl BloomLayout { device: &wgpu::Device, src_color: &wgpu::TextureView, sampler: &wgpu::Sampler, - half_pixel: Consts, + half_pixel: Consts, ) -> BindGroup { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: None, diff --git a/voxygen/src/render/pipelines/postprocess.rs b/voxygen/src/render/pipelines/postprocess.rs index 4efac906f1..ccb83b52c1 100644 --- a/voxygen/src/render/pipelines/postprocess.rs +++ b/voxygen/src/render/pipelines/postprocess.rs @@ -1,4 +1,4 @@ -use super::super::{Consts, GlobalsLayouts}; +use super::super::{Consts, GlobalsLayouts, PipelineModes}; use bytemuck::{Pod, Zeroable}; use vek::*; @@ -31,55 +31,61 @@ pub struct PostProcessLayout { } impl PostProcessLayout { - pub fn new(device: &wgpu::Device) -> Self { + pub fn new(device: &wgpu::Device, pipeline_modes: &PipelineModes) -> Self { + let mut bind_entries = vec![ + // src color + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler { + filtering: true, + comparison: false, + }, + count: None, + }, + // Locals + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + ]; + + if pipeline_modes.bloom { + bind_entries.push( + // src bloom + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + ); + } + Self { layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: None, - entries: &[ - // src color - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - // TODO: make optional - // src bloom - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Texture { - sample_type: wgpu::TextureSampleType::Float { filterable: true }, - view_dimension: wgpu::TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { - filtering: true, - comparison: false, - }, - count: None, - }, - // Locals - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }, - ], + entries: &bind_entries, }), } } @@ -88,35 +94,42 @@ impl PostProcessLayout { &self, device: &wgpu::Device, src_color: &wgpu::TextureView, - src_bloom: &wgpu::TextureView, + src_bloom: Option<&wgpu::TextureView>, sampler: &wgpu::Sampler, locals: &Consts, ) -> BindGroup { - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &self.layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(src_color), - }, + let mut entries = vec![ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(src_color), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(sampler), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: locals.buf().as_entire_binding(), + }, + ]; + // Optional bloom source + if let Some(src_bloom) = src_bloom { + entries.push( // TODO: might be cheaper to premix bloom at lower resolution if we are doing // extensive upscaling // TODO: if there is no upscaling we can do the last bloom upsampling in post // process to save a pass and the need for the final full size bloom render target wgpu::BindGroupEntry { - binding: 1, + binding: 3, resource: wgpu::BindingResource::TextureView(src_bloom), }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::Sampler(sampler), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: locals.buf().as_entire_binding(), - }, - ], + ); + } + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &self.layout, + entries: &entries, }); BindGroup { bind_group } diff --git a/voxygen/src/render/renderer.rs b/voxygen/src/render/renderer.rs index 9e923adf3d..a7f2f78d48 100644 --- a/voxygen/src/render/renderer.rs +++ b/voxygen/src/render/renderer.rs @@ -25,7 +25,8 @@ use super::{ GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup, }, texture::Texture, - AaMode, AddressMode, FilterMode, RenderError, RenderMode, ShadowMapMode, ShadowMode, Vertex, + AaMode, AddressMode, FilterMode, OtherModes, PipelineModes, RenderError, RenderMode, + ShadowMapMode, ShadowMode, Vertex, }; use common::assets::{self, AssetExt, AssetHandle}; use common_base::span; @@ -45,8 +46,9 @@ pub type ColLightInfo = (Vec<[u8; 4]>, Vec2); const QUAD_INDEX_BUFFER_U16_START_VERT_LEN: u16 = 3000; const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000; -/// A type that stores all the layouts associated with this renderer. -struct Layouts { +/// A type that stores all the layouts associated with this renderer that never +/// change when the RenderMode is modified. +struct ImmutableLayouts { global: GlobalsLayouts, debug: debug::DebugLayout, @@ -56,11 +58,23 @@ struct Layouts { terrain: terrain::TerrainLayout, clouds: clouds::CloudsLayout, bloom: bloom::BloomLayout, - postprocess: postprocess::PostProcessLayout, ui: ui::UiLayout, blit: blit::BlitLayout, } +/// A type that stores all the layouts associated with this renderer. +struct Layouts { + immutable: Arc, + + postprocess: Arc, +} + +impl core::ops::Deref for Layouts { + type Target = ImmutableLayouts; + + fn deref(&self) -> &Self::Target { &self.immutable } +} + /// Render target views struct Views { // NOTE: unused for now, maybe... we will want it for something @@ -69,7 +83,7 @@ struct Views { tgt_color: wgpu::TextureView, tgt_depth: wgpu::TextureView, - bloom_tgts: [wgpu::TextureView; bloom::NUM_SIZES], + bloom_tgts: Option<[wgpu::TextureView; bloom::NUM_SIZES]>, // TODO: rename tgt_color_pp: wgpu::TextureView, } @@ -84,6 +98,7 @@ struct Shadow { /// 1. Only interface pipelines created /// 2. All of the pipelines have been created #[allow(clippy::large_enum_variant)] // They are both pretty large +#[allow(clippy::type_complexity)] enum State { // NOTE: this is used as a transient placeholder for moving things out of State temporarily Nothing, @@ -96,7 +111,19 @@ enum State { Complete { pipelines: Pipelines, shadow: Shadow, - recreating: Option>>, + recreating: Option< + PipelineCreation< + Result< + ( + Pipelines, + ShadowPipelines, + PipelineModes, + Arc, + ), + RenderError, + >, + >, + >, }, } @@ -115,11 +142,11 @@ pub struct Renderer { depth_sampler: wgpu::Sampler, state: State, - // true if there is a pending need to recreate the pipelines (e.g. RenderMode change or shader + // Some if there is a pending need to recreate the pipelines (e.g. RenderMode change or shader // hotloading) - recreation_pending: bool, + recreation_pending: Option, - layouts: Arc, + layouts: Layouts, // Note: we keep these here since their bind groups need to be updated if we resize the // color/depth textures locals: Locals, @@ -131,7 +158,8 @@ pub struct Renderer { shaders: AssetHandle, - mode: RenderMode, + pipeline_modes: PipelineModes, + other_modes: OtherModes, resolution: Vec2, // If this is Some then a screenshot will be taken and passed to the handler here @@ -155,7 +183,8 @@ pub struct Renderer { impl Renderer { /// Create a new `Renderer` from a variety of backend-specific components /// and the window targets. - pub fn new(window: &winit::window::Window, mut mode: RenderMode) -> Result { + pub fn new(window: &winit::window::Window, mode: RenderMode) -> Result { + let (pipeline_modes, mut other_modes) = mode.split(); // Enable seamless cubemaps globally, where available--they are essentially a // strict improvement on regular cube maps. // @@ -288,7 +317,7 @@ impl Renderer { format, width: dims.width, height: dims.height, - present_mode: mode.present_mode.into(), + present_mode: other_modes.present_mode.into(), }; let swap_chain = device.create_swap_chain(&surface, &sc_desc); @@ -296,7 +325,7 @@ impl Renderer { let shadow_views = ShadowMap::create_shadow_views( &device, (dims.width, dims.height), - &ShadowMapMode::try_from(mode.shadow).unwrap_or_default(), + &ShadowMapMode::try_from(pipeline_modes.shadow).unwrap_or_default(), ) .map_err(|err| { warn!("Could not create shadow map views: {:?}", err); @@ -315,11 +344,14 @@ impl Renderer { let terrain = terrain::TerrainLayout::new(&device); let clouds = clouds::CloudsLayout::new(&device); let bloom = bloom::BloomLayout::new(&device); - let postprocess = postprocess::PostProcessLayout::new(&device); + let postprocess = Arc::new(postprocess::PostProcessLayout::new( + &device, + &pipeline_modes, + )); let ui = ui::UiLayout::new(&device); let blit = blit::BlitLayout::new(&device); - Layouts { + let immutable = Arc::new(ImmutableLayouts { global, debug, @@ -329,22 +361,27 @@ impl Renderer { terrain, clouds, bloom, - postprocess, ui, blit, + }); + + Layouts { + immutable, + postprocess, } }; - // Arcify the device and layouts + // Arcify the device let device = Arc::new(device); - let layouts = Arc::new(layouts); let (interface_pipelines, creating) = pipeline_creation::initial_create_pipelines( - // TODO: combine Arcs? Arc::clone(&device), - Arc::clone(&layouts), + Layouts { + immutable: Arc::clone(&layouts.immutable), + postprocess: Arc::clone(&layouts.postprocess), + }, shaders.read().clone(), - mode.clone(), + pipeline_modes.clone(), sc_desc.clone(), // Note: cheap clone shadow_views.is_some(), )?; @@ -355,8 +392,12 @@ impl Renderer { creating, }; - let (views, bloom_sizes) = - Self::create_rt_views(&device, (dims.width, dims.height), &mode)?; + let (views, bloom_sizes) = Self::create_rt_views( + &device, + (dims.width, dims.height), + &pipeline_modes, + &other_modes, + ); let create_sampler = |filter| { device.create_sampler(&wgpu::SamplerDescriptor { @@ -385,8 +426,6 @@ impl Renderer { let clouds_locals = Self::create_consts_inner(&device, &queue, &[clouds::Locals::default()]); - let bloom_locals = bloom_sizes - .map(|size| Self::create_consts_inner(&device, &queue, &[bloom::HalfPixel::new(size)])); let postprocess_locals = Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]); @@ -394,18 +433,16 @@ impl Renderer { &device, &layouts, clouds_locals, - bloom_locals, postprocess_locals, &views.tgt_color, &views.tgt_depth, - [ - &views.tgt_color_pp, - &views.bloom_tgts[1], - &views.bloom_tgts[2], - &views.bloom_tgts[3], - &views.bloom_tgts[4], - ], - &views.bloom_tgts[0], + views.bloom_tgts.as_ref().map(|tgts| locals::BloomParams { + locals: bloom_sizes.map(|size| { + Self::create_consts_inner(&device, &queue, &[bloom::Locals::new(size)]) + }), + src_views: [&views.tgt_color_pp, &tgts[1], &tgts[2], &tgts[3], &tgts[4]], + final_tgt_view: &tgts[0], + }), &views.tgt_color_pp, &sampler, &depth_sampler, @@ -416,9 +453,9 @@ impl Renderer { let quad_index_buffer_u32 = create_quad_index_buffer_u32(&device, QUAD_INDEX_BUFFER_U32_START_VERT_LEN as usize); let mut profiler = wgpu_profiler::GpuProfiler::new(4, queue.get_timestamp_period()); - mode.profiler_enabled &= profiler_features_enabled; - profiler.enable_timer = mode.profiler_enabled; - profiler.enable_debug_marker = mode.profiler_enabled; + other_modes.profiler_enabled &= profiler_features_enabled; + profiler.enable_timer = other_modes.profiler_enabled; + profiler.enable_debug_marker = other_modes.profiler_enabled; #[cfg(feature = "egui-ui")] let egui_renderpass = @@ -432,7 +469,7 @@ impl Renderer { sc_desc, state, - recreation_pending: false, + recreation_pending: None, layouts, locals, @@ -447,7 +484,8 @@ impl Renderer { shaders, - mode, + pipeline_modes, + other_modes, resolution: Vec2::new(dims.width, dims.height), take_screenshot: None, @@ -492,37 +530,47 @@ impl Renderer { /// Change the render mode. pub fn set_render_mode(&mut self, mode: RenderMode) -> Result<(), RenderError> { - // TODO: are there actually any issues with the current mode not matching the - // pipelines (since we could previously have inconsistencies from - // pipelines failing to build due to shader editing)? - // TODO: FIXME: defer mode changing until pipelines are rebuilt to prevent - // incompatibilities as pipelines are now rebuilt in a deferred mannder in the - // background TODO: consider separating changes that don't require - // rebuilding pipelines - self.mode = mode; - self.sc_desc.present_mode = self.mode.present_mode.into(); + let (pipeline_modes, other_modes) = mode.split(); - // Only enable profiling if the wgpu features are enabled - self.mode.profiler_enabled &= self.profiler_features_enabled; - // Enable/disable profiler - if !self.mode.profiler_enabled { - // Clear the times if disabled - core::mem::take(&mut self.profile_times); + if self.other_modes != other_modes { + self.other_modes = other_modes; + + // Update present mode in swap chain descriptor + self.sc_desc.present_mode = self.other_modes.present_mode.into(); + + // Only enable profiling if the wgpu features are enabled + self.other_modes.profiler_enabled &= self.profiler_features_enabled; + // Enable/disable profiler + if !self.other_modes.profiler_enabled { + // Clear the times if disabled + core::mem::take(&mut self.profile_times); + } + self.profiler.enable_timer = self.other_modes.profiler_enabled; + self.profiler.enable_debug_marker = self.other_modes.profiler_enabled; + + // Recreate render target + self.on_resize(self.resolution); } - self.profiler.enable_timer = self.mode.profiler_enabled; - self.profiler.enable_debug_marker = self.mode.profiler_enabled; - // Recreate render target - self.on_resize(self.resolution)?; - - // Recreate pipelines with the new AA mode - self.recreate_pipelines(); + // We can't cancel the pending recreation even if the new settings are equal + // to the current ones becuase the recreation could be triggered by something + // else like shader hotloading + if dbg!(self.pipeline_modes != pipeline_modes) + || dbg!( + self.recreation_pending + .as_ref() + .map_or(false, |modes| modes != &pipeline_modes) + ) + { + // Recreate pipelines with new modes + self.recreate_pipelines(pipeline_modes); + } Ok(()) } - /// Get the render mode. - pub fn render_mode(&self) -> &RenderMode { &self.mode } + /// Get the pipelines mode. + pub fn pipeline_modes(&self) -> &PipelineModes { &self.pipeline_modes } /// Get the current profiling times /// Nested timings immediately follow their parent @@ -552,7 +600,7 @@ impl Renderer { } /// Resize internal render targets to match window render target dimensions. - pub fn on_resize(&mut self, dims: Vec2) -> Result<(), RenderError> { + pub fn on_resize(&mut self, dims: Vec2) { // Avoid panics when creating texture with w,h of 0,0. if dims.x != 0 && dims.y != 0 { self.is_minimized = false; @@ -563,26 +611,36 @@ impl Renderer { self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); // Resize other render targets - let (views, bloom_sizes) = - Self::create_rt_views(&self.device, (dims.x, dims.y), &self.mode)?; + let (views, bloom_sizes) = Self::create_rt_views( + &self.device, + (dims.x, dims.y), + &self.pipeline_modes, + &self.other_modes, + ); self.views = views; - let bloom_locals = - bloom_sizes.map(|size| self.create_consts(&[bloom::HalfPixel::new(size)])); - // Rebind views to clouds/postprocess bind groups + + // appease borrow check + let device = &self.device; + let queue = &self.queue; + let views = &self.views; + let bloom_params = self + .views + .bloom_tgts + .as_ref() + .map(|tgts| locals::BloomParams { + locals: bloom_sizes.map(|size| { + Self::create_consts_inner(device, queue, &[bloom::Locals::new(size)]) + }), + src_views: [&views.tgt_color_pp, &tgts[1], &tgts[2], &tgts[3], &tgts[4]], + final_tgt_view: &tgts[0], + }); + self.locals.rebind( &self.device, &self.layouts, - bloom_locals, &self.views.tgt_color, &self.views.tgt_depth, - [ - &self.views.tgt_color_pp, - &self.views.bloom_tgts[1], - &self.views.bloom_tgts[2], - &self.views.bloom_tgts[3], - &self.views.bloom_tgts[4], - ], - &self.views.bloom_tgts[0], + bloom_params, &self.views.tgt_color_pp, &self.sampler, &self.depth_sampler, @@ -606,7 +664,7 @@ impl Renderer { }; if let (Some((point_depth, directed_depth)), ShadowMode::Map(mode)) = - (shadow_views, self.mode.shadow) + (shadow_views, self.pipeline_modes.shadow) { match ShadowMap::create_shadow_views(&self.device, (dims.x, dims.y), &mode) { Ok((new_point_depth, new_directed_depth)) => { @@ -638,8 +696,6 @@ impl Renderer { } else { self.is_minimized = true; } - - Ok(()) } pub fn maintain(&self) { @@ -654,12 +710,13 @@ impl Renderer { fn create_rt_views( device: &wgpu::Device, size: (u32, u32), - mode: &RenderMode, - ) -> Result<(Views, [Vec2; bloom::NUM_SIZES]), RenderError> { + pipeline_modes: &PipelineModes, + other_modes: &OtherModes, + ) -> (Views, [Vec2; bloom::NUM_SIZES]) { let upscaled = Vec2::::from(size) - .map(|e| (e as f32 * mode.upscale_mode.factor) as u32) + .map(|e| (e as f32 * other_modes.upscale_mode.factor) as u32) .into_tuple(); - let (width, height, sample_count) = match mode.aa { + let (width, height, sample_count) = match pipeline_modes.aa { AaMode::None | AaMode::Fxaa => (upscaled.0, upscaled.1, 1), AaMode::MsaaX4 => (upscaled.0, upscaled.1, 4), AaMode::MsaaX8 => (upscaled.0, upscaled.1, 8), @@ -707,7 +764,9 @@ impl Renderer { size }); - let bloom_tgt_views = bloom_sizes.map(|size| color_view(size.x, size.y)); + let bloom_tgt_views = pipeline_modes + .bloom + .then(|| bloom_sizes.map(|size| color_view(size.x, size.y))); let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor { label: None, @@ -758,7 +817,7 @@ impl Renderer { array_layer_count: None, }); - Ok(( + ( Views { tgt_color: tgt_color_view, tgt_depth: tgt_depth_view, @@ -767,7 +826,7 @@ impl Renderer { _win_depth: win_depth_view, }, bloom_sizes.map(|s| s.map(|e| e as f32)), - )) + ) } /// Get the resolution of the render target. @@ -841,7 +900,7 @@ impl Renderer { } // Try to get the latest profiling results - if self.mode.profiler_enabled { + if self.other_modes.profiler_enabled { // Note: this lags a few frames behind if let Some(profile_times) = self.profiler.process_finished_frame() { self.profile_times = profile_times; @@ -851,6 +910,10 @@ impl Renderer { // Handle polling background pipeline creation/recreation // Temporarily set to nothing and then replace in the statement below let state = core::mem::replace(&mut self.state, State::Nothing); + // Indicator for if pipeline recreation finished and we need to recreate bind + // groups / render targets (handling defered so that State will be valid + // when calling Self::on_resize) + let mut trigger_on_resize = false; // If still creating initial pipelines, check if complete self.state = if let State::Interface { pipelines: interface, @@ -906,7 +969,7 @@ impl Renderer { } = state { match recreating.try_complete() { - Ok(Ok((pipelines, shadow_pipelines))) => { + Ok(Ok((pipelines, shadow_pipelines, new_pipeline_modes, postprocess_layout))) => { if let ( Some(point_pipeline), Some(terrain_directed_pipeline), @@ -922,6 +985,14 @@ impl Renderer { shadow_map.terrain_directed_pipeline = terrain_directed_pipeline; shadow_map.figure_directed_pipeline = figure_directed_pipeline; } + + self.pipeline_modes = new_pipeline_modes; + self.layouts.postprocess = postprocess_layout; + // TODO: we have the potential to skip recreating bindings / render targets on + // pipeline recreation trigged by shader reloading (would need to ensure new + // postprocess_layout is not created...) + trigger_on_resize = true; + State::Complete { pipelines, shadow, @@ -947,17 +1018,26 @@ impl Renderer { state }; + // Call on_resize to recreate render targets and their bind groups if the + // pipelines were recreated with a new postprocess layout and or changes in the + // render modes + if trigger_on_resize { + self.on_resize(self.resolution); + } + // If the shaders files were changed attempt to recreate the shaders if self.shaders.reloaded() { - self.recreate_pipelines(); + self.recreate_pipelines(self.pipeline_modes.clone()); } // Or if we have a recreation pending - if self.recreation_pending - && matches!(&self.state, State::Complete { recreating, .. } if recreating.is_none()) - { - self.recreation_pending = false; - self.recreate_pipelines(); + if matches!(&self.state, State::Complete { + recreating: None, + .. + }) { + if let Some(new_pipeline_modes) = self.recreation_pending.take() { + self.recreate_pipelines(new_pipeline_modes); + } } let tex = match self.swap_chain.get_current_frame() { @@ -965,7 +1045,8 @@ impl Renderer { // If lost recreate the swap chain Err(err @ wgpu::SwapChainError::Lost) => { warn!("{}. Recreating swap chain. A frame will be missed", err); - return self.on_resize(self.resolution).map(|()| None); + self.on_resize(self.resolution); + return Ok(None); }, Err(wgpu::SwapChainError::Timeout) => { // This will probably be resolved on the next frame @@ -990,21 +1071,24 @@ impl Renderer { } /// Recreate the pipelines - fn recreate_pipelines(&mut self) { + fn recreate_pipelines(&mut self, pipeline_modes: PipelineModes) { match &mut self.state { State::Complete { recreating, .. } if recreating.is_some() => { // Defer recreation so that we are not building multiple sets of pipelines in // the background at once - self.recreation_pending = true; + self.recreation_pending = Some(pipeline_modes); }, State::Complete { recreating, shadow, .. } => { *recreating = Some(pipeline_creation::recreate_pipelines( Arc::clone(&self.device), - Arc::clone(&self.layouts), + Arc::clone(&self.layouts.immutable), self.shaders.read().clone(), - self.mode.clone(), + self.pipeline_modes.clone(), + // NOTE: if present_mode starts to be used to configure pipelines then it needs + // to become a part of the pipeline modes (note here since the present mode is + // accessible through the swap chain descriptor) self.sc_desc.clone(), // Note: cheap clone shadow.map.is_enabled(), )); @@ -1012,7 +1096,7 @@ impl Renderer { State::Interface { .. } => { // Defer recreation so that we are not building multiple sets of pipelines in // the background at once - self.recreation_pending = true; + self.recreation_pending = Some(pipeline_modes); }, State::Nothing => {}, } @@ -1229,7 +1313,7 @@ impl Renderer { // Queue screenshot self.take_screenshot = Some(Box::new(screenshot_handler)); // Take profiler snapshot - if self.mode.profiler_enabled { + if self.other_modes.profiler_enabled { let file_name = format!( "frame-trace_{}.json", std::time::SystemTime::now() diff --git a/voxygen/src/render/renderer/drawer.rs b/voxygen/src/render/renderer/drawer.rs index 4c5bc22887..16f67b76f2 100644 --- a/voxygen/src/render/renderer/drawer.rs +++ b/voxygen/src/render/renderer/drawer.rs @@ -61,7 +61,7 @@ struct RendererBorrow<'frame> { pipelines: Pipelines<'frame>, locals: &'frame super::locals::Locals, views: &'frame super::Views, - mode: &'frame super::super::RenderMode, + pipeline_modes: &'frame super::super::PipelineModes, quad_index_buffer_u16: &'frame Buffer, quad_index_buffer_u32: &'frame Buffer, #[cfg(feature = "egui-ui")] @@ -112,7 +112,7 @@ impl<'frame> Drawer<'frame> { pipelines, locals: &renderer.locals, views: &renderer.views, - mode: &renderer.mode, + pipeline_modes: &renderer.pipeline_modes, quad_index_buffer_u16: &renderer.quad_index_buffer_u16, quad_index_buffer_u32: &renderer.quad_index_buffer_u32, #[cfg(feature = "egui-ui")] @@ -131,13 +131,13 @@ impl<'frame> Drawer<'frame> { } } - /// Get the render mode. - pub fn render_mode(&self) -> &super::super::RenderMode { self.borrow.mode } + /// Get the pipeline modes. + pub fn pipeline_modes(&self) -> &super::super::PipelineModes { self.borrow.pipeline_modes } /// Returns None if the shadow renderer is not enabled at some level or the /// pipelines are not available yet pub fn shadow_pass(&mut self) -> Option { - if !self.borrow.mode.shadow.is_map() { + if !self.borrow.pipeline_modes.shadow.is_map() { return None; } @@ -243,15 +243,24 @@ impl<'frame> Drawer<'frame> { /// To be ran between the second pass and the third pass /// does nothing if the ingame pipelines are not yet ready + /// does nothing if bloom is disabled pub fn run_bloom_passes(&mut self) { - let pipelines = match self.borrow.pipelines.all() { - Some(p) => p, - None => return, - }; - let locals = &self.borrow.locals; let views = &self.borrow.views; + let bloom_pipelines = match self.borrow.pipelines.all() { + Some(super::Pipelines { bloom: Some(p), .. }) => p, + _ => return, + }; + + // TODO: consider consolidating optional bloom bind groups and optional pipeline + // into a single structure? + let (bloom_tgts, bloom_binds) = + match views.bloom_tgts.as_ref().zip(locals.bloom_binds.as_ref()) { + Some((t, b)) => (t, b), + None => return, + }; + let device = self.borrow.device; let mut encoder = self.encoder.as_mut().unwrap().scope("bloom", device); @@ -275,18 +284,18 @@ impl<'frame> Drawer<'frame> { // Downsample filter passes (0..bloom::NUM_SIZES - 1).for_each(|index| { - let bind = &locals.bloom_binds[index].bind_group; - let view = &views.bloom_tgts[index + 1]; + let bind = &bloom_binds[index].bind_group; + let view = &bloom_tgts[index + 1]; // Do filtering out of non-bright things during the first downsample let (label, pipeline) = if index == 0 { ( format!("downsample filtered {}", index + 1), - &pipelines.bloom.downsample_filtered, + &bloom_pipelines.downsample_filtered, ) } else { ( format!("downsample {}", index + 1), - &pipelines.bloom.downsample, + &bloom_pipelines.downsample, ) }; run_bloom_pass( @@ -300,14 +309,14 @@ impl<'frame> Drawer<'frame> { // Upsample filter passes (0..bloom::NUM_SIZES - 1).for_each(|index| { - let bind = &locals.bloom_binds[bloom::NUM_SIZES - 1 - index].bind_group; - let view = &views.bloom_tgts[bloom::NUM_SIZES - 2 - index]; + let bind = &bloom_binds[bloom::NUM_SIZES - 1 - index].bind_group; + let view = &bloom_tgts[bloom::NUM_SIZES - 2 - index]; let label = format!("upsample {}", index + 1); run_bloom_pass( bind, view, label, - &pipelines.bloom.upsample, + &bloom_pipelines.upsample, if index + 2 == bloom::NUM_SIZES { // Clear for the final image since that is just stuff from the pervious frame. wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT) @@ -400,7 +409,7 @@ impl<'frame> Drawer<'frame> { chunks: impl Clone + Iterator, &'data terrain::BoundLocals)>, ) { - if !self.borrow.mode.shadow.is_map() { + if !self.borrow.pipeline_modes.shadow.is_map() { return; } diff --git a/voxygen/src/render/renderer/locals.rs b/voxygen/src/render/renderer/locals.rs index 8a3c549604..fb3b0301ac 100644 --- a/voxygen/src/render/renderer/locals.rs +++ b/voxygen/src/render/renderer/locals.rs @@ -6,11 +6,17 @@ use super::{ Layouts, }; +pub struct BloomParams<'a> { + pub locals: [Consts; bloom::NUM_SIZES], + pub src_views: [&'a wgpu::TextureView; bloom::NUM_SIZES], + pub final_tgt_view: &'a wgpu::TextureView, +} + pub struct Locals { pub clouds: Consts, pub clouds_bind: clouds::BindGroup, - pub bloom_binds: [bloom::BindGroup; bloom::NUM_SIZES], + pub bloom_binds: Option<[bloom::BindGroup; bloom::NUM_SIZES]>, pub postprocess: Consts, pub postprocess_bind: postprocess::BindGroup, @@ -21,12 +27,10 @@ impl Locals { device: &wgpu::Device, layouts: &Layouts, clouds_locals: Consts, - bloom_locals: [Consts; bloom::NUM_SIZES], postprocess_locals: Consts, tgt_color_view: &wgpu::TextureView, tgt_depth_view: &wgpu::TextureView, - bloom_src_views: [&wgpu::TextureView; bloom::NUM_SIZES], - bloom_final_tgt_view: &wgpu::TextureView, + bloom: Option, tgt_color_pp_view: &wgpu::TextureView, sampler: &wgpu::Sampler, depth_sampler: &wgpu::Sampler, @@ -39,18 +43,22 @@ impl Locals { depth_sampler, &clouds_locals, ); - let bloom_binds = bloom_src_views - .zip(bloom_locals) - .map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals)); let postprocess_bind = layouts.postprocess.bind( device, tgt_color_pp_view, - bloom_final_tgt_view, + bloom.as_ref().map(|b| b.final_tgt_view), sampler, &postprocess_locals, ); + let bloom_binds = bloom.map(|bloom| { + bloom + .src_views + .zip(bloom.locals) // zip arrays + .map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals)) + }); + Self { clouds: clouds_locals, clouds_bind, @@ -66,11 +74,9 @@ impl Locals { layouts: &Layouts, // Call when these are recreated and need to be rebound // e.g. resizing - bloom_locals: [Consts; bloom::NUM_SIZES], tgt_color_view: &wgpu::TextureView, tgt_depth_view: &wgpu::TextureView, - bloom_src_views: [&wgpu::TextureView; bloom::NUM_SIZES], - bloom_final_tgt_view: &wgpu::TextureView, + bloom: Option, tgt_color_pp_view: &wgpu::TextureView, sampler: &wgpu::Sampler, depth_sampler: &wgpu::Sampler, @@ -83,15 +89,18 @@ impl Locals { depth_sampler, &self.clouds, ); - self.bloom_binds = bloom_src_views - .zip(bloom_locals) - .map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals)); self.postprocess_bind = layouts.postprocess.bind( device, tgt_color_pp_view, - bloom_final_tgt_view, + bloom.as_ref().map(|b| b.final_tgt_view), sampler, &self.postprocess, ); + self.bloom_binds = bloom.map(|bloom| { + bloom + .src_views + .zip(bloom.locals) // zip arrays + .map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals)) + }); } } diff --git a/voxygen/src/render/renderer/pipeline_creation.rs b/voxygen/src/render/renderer/pipeline_creation.rs index 10d02ba298..7331504271 100644 --- a/voxygen/src/render/renderer/pipeline_creation.rs +++ b/voxygen/src/render/renderer/pipeline_creation.rs @@ -4,10 +4,10 @@ use super::{ blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, sprite, terrain, ui, }, - AaMode, CloudMode, FluidMode, LightingMode, RenderError, RenderMode, ShadowMode, + AaMode, CloudMode, FluidMode, LightingMode, PipelineModes, RenderError, ShadowMode, }, shaders::Shaders, - Layouts, + ImmutableLayouts, Layouts, }; use common_base::prof_span; use std::sync::Arc; @@ -20,7 +20,7 @@ pub struct Pipelines { pub lod_terrain: lod_terrain::LodTerrainPipeline, pub particle: particle::ParticlePipeline, pub clouds: clouds::CloudsPipeline, - pub bloom: bloom::BloomPipelines, + pub bloom: Option, pub postprocess: postprocess::PostProcessPipeline, // Consider reenabling at some time // player_shadow: figure::FigurePipeline, @@ -40,7 +40,7 @@ pub struct IngamePipelines { lod_terrain: lod_terrain::LodTerrainPipeline, particle: particle::ParticlePipeline, clouds: clouds::CloudsPipeline, - bloom: bloom::BloomPipelines, + pub bloom: Option, postprocess: postprocess::PostProcessPipeline, // Consider reenabling at some time // player_shadow: figure::FigurePipeline, @@ -126,7 +126,7 @@ impl ShaderModules { pub fn new( device: &wgpu::Device, shaders: &Shaders, - mode: &RenderMode, + pipeline_modes: &PipelineModes, has_shadow_views: bool, ) -> Result { prof_span!(_guard, "ShaderModules::new"); @@ -151,16 +151,17 @@ impl ShaderModules { #define CLOUD_MODE {} #define LIGHTING_ALGORITHM {} #define SHADOW_MODE {} +#define BLOOM {} "#, &constants.0, // TODO: Configurable vertex/fragment shader preference. "VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT", - match mode.fluid { + match pipeline_modes.fluid { FluidMode::Cheap => "FLUID_MODE_CHEAP", FluidMode::Shiny => "FLUID_MODE_SHINY", }, - match mode.cloud { + match pipeline_modes.cloud { CloudMode::None => "CLOUD_MODE_NONE", CloudMode::Minimal => "CLOUD_MODE_MINIMAL", CloudMode::Low => "CLOUD_MODE_LOW", @@ -168,20 +169,25 @@ impl ShaderModules { CloudMode::High => "CLOUD_MODE_HIGH", CloudMode::Ultra => "CLOUD_MODE_ULTRA", }, - match mode.lighting { + match pipeline_modes.lighting { LightingMode::Ashikhmin => "LIGHTING_ALGORITHM_ASHIKHMIN", LightingMode::BlinnPhong => "LIGHTING_ALGORITHM_BLINN_PHONG", LightingMode::Lambertian => "LIGHTING_ALGORITHM_LAMBERTIAN", }, - match mode.shadow { + match pipeline_modes.shadow { ShadowMode::None => "SHADOW_MODE_NONE", ShadowMode::Map(_) if has_shadow_views => "SHADOW_MODE_MAP", ShadowMode::Cheap | ShadowMode::Map(_) => "SHADOW_MODE_CHEAP", }, + if dbg!(pipeline_modes.bloom) { + "BLOOM_ENABLED" + } else { + "BLOOM_DISABLED" + }, ); let anti_alias = shaders - .get(match mode.aa { + .get(match pipeline_modes.aa { AaMode::None => "antialias.none", AaMode::Fxaa => "antialias.fxaa", AaMode::MsaaX4 => "antialias.msaa-x4", @@ -191,7 +197,7 @@ impl ShaderModules { .unwrap(); let cloud = shaders - .get(match mode.cloud { + .get(match pipeline_modes.cloud { CloudMode::None => "include.cloud.none", _ => "include.cloud.regular", }) @@ -234,7 +240,7 @@ impl ShaderModules { create_shader_module(device, &mut compiler, glsl, kind, &file_name, &options) }; - let selected_fluid_shader = ["fluid-frag.", match mode.fluid { + let selected_fluid_shader = ["fluid-frag.", match pipeline_modes.fluid { FluidMode::Cheap => "cheap", FluidMode::Shiny => "shiny", }] @@ -317,7 +323,7 @@ struct PipelineNeeds<'a> { device: &'a wgpu::Device, layouts: &'a Layouts, shaders: &'a ShaderModules, - mode: &'a RenderMode, + pipeline_modes: &'a PipelineModes, sc_desc: &'a wgpu::SwapChainDescriptor, } @@ -380,7 +386,7 @@ fn create_ingame_and_shadow_pipelines( device, layouts, shaders, - mode, + pipeline_modes, sc_desc, } = needs; @@ -416,7 +422,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.debug_frag, &layouts.global, &layouts.debug, - mode.aa, + pipeline_modes.aa, ) }, "debug pipeline creation", @@ -431,7 +437,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.skybox_vert, &shaders.skybox_frag, &layouts.global, - mode.aa, + pipeline_modes.aa, ) }, "skybox pipeline creation", @@ -447,7 +453,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.figure_frag, &layouts.global, &layouts.figure, - mode.aa, + pipeline_modes.aa, ) }, "figure pipeline creation", @@ -463,7 +469,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.terrain_frag, &layouts.global, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "terrain pipeline creation", @@ -479,7 +485,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.fluid_frag, &layouts.global, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "fluid pipeline creation", @@ -496,7 +502,7 @@ fn create_ingame_and_shadow_pipelines( &layouts.global, &layouts.sprite, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "sprite pipeline creation", @@ -511,7 +517,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.particle_vert, &shaders.particle_frag, &layouts.global, - mode.aa, + pipeline_modes.aa, ) }, "particle pipeline creation", @@ -526,7 +532,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.lod_terrain_vert, &shaders.lod_terrain_frag, &layouts.global, - mode.aa, + pipeline_modes.aa, ) }, "lod terrain pipeline creation", @@ -542,7 +548,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.clouds_frag, &layouts.global, &layouts.clouds, - mode.aa, + pipeline_modes.aa, ) }, "clouds pipeline creation", @@ -552,15 +558,17 @@ fn create_ingame_and_shadow_pipelines( let create_bloom = || { bloom_task.run( || { - bloom::BloomPipelines::new( - device, - &shaders.blit_vert, - &shaders.dual_downsample_filtered_frag, - &shaders.dual_downsample_frag, - &shaders.dual_upsample_frag, - wgpu::TextureFormat::Rgba16Float, - &layouts.bloom, - ) + pipeline_modes.bloom.then(|| { + bloom::BloomPipelines::new( + device, + &shaders.blit_vert, + &shaders.dual_downsample_filtered_frag, + &shaders.dual_downsample_frag, + &shaders.dual_upsample_frag, + wgpu::TextureFormat::Rgba16Float, + &layouts.bloom, + ) + }) }, "bloom pipelines creation", ) @@ -614,7 +622,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.point_light_shadows_vert, &layouts.global, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "point shadow pipeline creation", @@ -629,7 +637,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.light_shadows_directed_vert, &layouts.global, &layouts.terrain, - mode.aa, + pipeline_modes.aa, ) }, "terrain directed shadow pipeline creation", @@ -644,7 +652,7 @@ fn create_ingame_and_shadow_pipelines( &shaders.light_shadows_figure_vert, &layouts.global, &layouts.figure, - mode.aa, + pipeline_modes.aa, ) }, "figure directed shadow pipeline creation", @@ -706,9 +714,9 @@ fn create_ingame_and_shadow_pipelines( /// NOTE: this tries to use all the CPU cores to complete as soon as possible pub(super) fn initial_create_pipelines( device: Arc, - layouts: Arc, + layouts: Layouts, shaders: Shaders, - mode: RenderMode, + pipeline_modes: PipelineModes, sc_desc: wgpu::SwapChainDescriptor, has_shadow_views: bool, ) -> Result< @@ -721,7 +729,7 @@ pub(super) fn initial_create_pipelines( prof_span!(_guard, "initial_create_pipelines"); // Process shaders into modules - let shader_modules = ShaderModules::new(&device, &shaders, &mode, has_shadow_views)?; + let shader_modules = ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views)?; // Create threadpool for parallel portion let pool = rayon::ThreadPoolBuilder::new() @@ -733,7 +741,7 @@ pub(super) fn initial_create_pipelines( device: &device, layouts: &layouts, shaders: &shader_modules, - mode: &mode, + pipeline_modes: &pipeline_modes, sc_desc: &sc_desc, }; @@ -760,7 +768,7 @@ pub(super) fn initial_create_pipelines( device: &device, layouts: &layouts, shaders: &shader_modules, - mode: &mode, + pipeline_modes: &pipeline_modes, sc_desc: &sc_desc, }; @@ -776,14 +784,25 @@ pub(super) fn initial_create_pipelines( /// Use this to recreate all the pipelines in the background. /// TODO: report progress /// NOTE: this tries to use all the CPU cores to complete as soon as possible +#[allow(clippy::type_complexity)] pub(super) fn recreate_pipelines( device: Arc, - layouts: Arc, + immutable_layouts: Arc, shaders: Shaders, - mode: RenderMode, + pipeline_modes: PipelineModes, sc_desc: wgpu::SwapChainDescriptor, has_shadow_views: bool, -) -> PipelineCreation> { +) -> PipelineCreation< + Result< + ( + Pipelines, + ShadowPipelines, + PipelineModes, + Arc, + ), + RenderError, + >, +> { prof_span!(_guard, "recreate_pipelines"); // Create threadpool for parallel portion @@ -811,20 +830,32 @@ pub(super) fn recreate_pipelines( // Process shaders into modules let guard = shader_task.start("process shaders"); - let shader_modules = match ShaderModules::new(&device, &shaders, &mode, has_shadow_views) { - Ok(modules) => modules, - Err(err) => { - result_send.send(Err(err)).expect("Channel disconnected"); - return; - }, - }; + let shader_modules = + match ShaderModules::new(&device, &shaders, &pipeline_modes, has_shadow_views) { + Ok(modules) => modules, + Err(err) => { + result_send.send(Err(err)).expect("Channel disconnected"); + return; + }, + }; drop(guard); + // Create new postprocess layouts + let postprocess_layouts = Arc::new(postprocess::PostProcessLayout::new( + &device, + &pipeline_modes, + )); + + let layouts = Layouts { + immutable: immutable_layouts, + postprocess: postprocess_layouts, + }; + let needs = PipelineNeeds { device: &device, layouts: &layouts, shaders: &shader_modules, - mode: &mode, + pipeline_modes: &pipeline_modes, sc_desc: &sc_desc, }; @@ -837,7 +868,12 @@ pub(super) fn recreate_pipelines( // Send them result_send - .send(Ok((Pipelines::consolidate(interface, ingame), shadow))) + .send(Ok(( + Pipelines::consolidate(interface, ingame), + shadow, + pipeline_modes, + layouts.postprocess, + ))) .expect("Channel disconnected"); }); diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 9fac2b5fff..6ec10087d4 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -518,7 +518,7 @@ impl FigureMgr { let ray_direction = scene_data.get_sun_dir(); let is_daylight = ray_direction.z < 0.0/*0.6*/; // Are shadows enabled at all? - let can_shadow_sun = renderer.render_mode().shadow.is_map() && is_daylight; + let can_shadow_sun = renderer.pipeline_modes().shadow.is_map() && is_daylight; let Dependents { proj_mat: _, view_mat: _, diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index c7659e18ca..4f6d0af4d5 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -681,7 +681,7 @@ impl Scene { let sun_dir = scene_data.get_sun_dir(); let is_daylight = sun_dir.z < 0.0; - if renderer.render_mode().shadow.is_map() && (is_daylight || !lights.is_empty()) { + if renderer.pipeline_modes().shadow.is_map() && (is_daylight || !lights.is_empty()) { let fov = self.camera.get_fov(); let aspect_ratio = self.camera.get_aspect_ratio(); @@ -1062,7 +1062,7 @@ impl Scene { let camera_data = (&self.camera, scene_data.figure_lod_render_distance); // would instead have this as an extension. - if drawer.render_mode().shadow.is_map() && (is_daylight || !self.light_data.is_empty()) { + if drawer.pipeline_modes().shadow.is_map() && (is_daylight || !self.light_data.is_empty()) { if is_daylight { prof_span!("directed shadows"); if let Some(mut shadow_pass) = drawer.shadow_pass() { diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index dae0847392..31d06afd4e 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -1244,7 +1244,7 @@ impl Terrain { return min.partial_cmple(&max).reduce_and(); }; let (visible_light_volume, visible_psr_bounds) = if ray_direction.z < 0.0 - && renderer.render_mode().shadow.is_map() + && renderer.pipeline_modes().shadow.is_map() { let visible_bounding_box = math::Aabb:: { min: math::Vec3::from(visible_bounding_box.min - focus_off), diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index eaf2469d95..23c8bed695 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -1483,8 +1483,7 @@ impl PlayState for SessionState { second_pass.draw_clouds(); } } - // Bloom - // TODO: make optional + // Bloom (call does nothing if bloom is off) { prof_span!("bloom"); drawer.run_bloom_passes() diff --git a/voxygen/src/settings/graphics.rs b/voxygen/src/settings/graphics.rs index 1155a7a131..55cc52ba6b 100644 --- a/voxygen/src/settings/graphics.rs +++ b/voxygen/src/settings/graphics.rs @@ -43,6 +43,7 @@ pub struct GraphicsSettings { pub window_size: [u16; 2], pub fullscreen: FullScreenSettings, pub lod_detail: u32, + pub bloom_enabled: bool, } impl Default for GraphicsSettings { @@ -62,6 +63,7 @@ impl Default for GraphicsSettings { window_size: [1280, 720], fullscreen: FullScreenSettings::default(), lod_detail: 250, + bloom_enabled: false, } } } diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 4f2c3da9d4..f29c197fb6 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -791,8 +791,7 @@ impl Window { let physical = self.window.inner_size(); self.renderer - .on_resize(Vec2::new(physical.width, physical.height)) - .unwrap(); + .on_resize(Vec2::new(physical.width, physical.height)); // TODO: update users of this event with the fact that it is now the physical // size let winit::dpi::PhysicalSize { width, height } = physical;