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)

This commit is contained in:
Imbris 2021-07-29 02:06:56 -04:00
parent bfdee903dd
commit 6cabd74871
19 changed files with 480 additions and 279 deletions

View File

@ -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;
};

View File

@ -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;
};

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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();

View File

@ -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,
}

View File

@ -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<f32>) -> 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<HalfPixel>,
half_pixel: Consts<Locals>,
) -> BindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,

View File

@ -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<Locals>,
) -> 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 }

View File

@ -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<u16>);
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<ImmutableLayouts>,
postprocess: Arc<postprocess::PostProcessLayout>,
}
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<PipelineCreation<Result<(Pipelines, ShadowPipelines), RenderError>>>,
recreating: Option<
PipelineCreation<
Result<
(
Pipelines,
ShadowPipelines,
PipelineModes,
Arc<postprocess::PostProcessLayout>,
),
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<PipelineModes>,
layouts: Arc<Layouts>,
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<Shaders>,
mode: RenderMode,
pipeline_modes: PipelineModes,
other_modes: OtherModes,
resolution: Vec2<u32>,
// 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<Self, RenderError> {
pub fn new(window: &winit::window::Window, mode: RenderMode) -> Result<Self, RenderError> {
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<u32>) -> Result<(), RenderError> {
pub fn on_resize(&mut self, dims: Vec2<u32>) {
// 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<f32>; bloom::NUM_SIZES]), RenderError> {
pipeline_modes: &PipelineModes,
other_modes: &OtherModes,
) -> (Views, [Vec2<f32>; bloom::NUM_SIZES]) {
let upscaled = Vec2::<u32>::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()

View File

@ -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<u16>,
quad_index_buffer_u32: &'frame Buffer<u32>,
#[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<ShadowPassDrawer> {
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<Item = (&'data Model<terrain::Vertex>, &'data terrain::BoundLocals)>,
) {
if !self.borrow.mode.shadow.is_map() {
if !self.borrow.pipeline_modes.shadow.is_map() {
return;
}

View File

@ -6,11 +6,17 @@ use super::{
Layouts,
};
pub struct BloomParams<'a> {
pub locals: [Consts<bloom::Locals>; 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<clouds::Locals>,
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<postprocess::Locals>,
pub postprocess_bind: postprocess::BindGroup,
@ -21,12 +27,10 @@ impl Locals {
device: &wgpu::Device,
layouts: &Layouts,
clouds_locals: Consts<clouds::Locals>,
bloom_locals: [Consts<bloom::HalfPixel>; bloom::NUM_SIZES],
postprocess_locals: Consts<postprocess::Locals>,
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<BloomParams>,
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::HalfPixel>; 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<BloomParams>,
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))
});
}
}

View File

@ -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<bloom::BloomPipelines>,
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<bloom::BloomPipelines>,
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<Self, RenderError> {
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<wgpu::Device>,
layouts: Arc<Layouts>,
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<wgpu::Device>,
layouts: Arc<Layouts>,
immutable_layouts: Arc<ImmutableLayouts>,
shaders: Shaders,
mode: RenderMode,
pipeline_modes: PipelineModes,
sc_desc: wgpu::SwapChainDescriptor,
has_shadow_views: bool,
) -> PipelineCreation<Result<(Pipelines, ShadowPipelines), RenderError>> {
) -> PipelineCreation<
Result<
(
Pipelines,
ShadowPipelines,
PipelineModes,
Arc<postprocess::PostProcessLayout>,
),
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");
});

View File

@ -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: _,

View File

@ -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() {

View File

@ -1244,7 +1244,7 @@ impl<V: RectRasterableVol> Terrain<V> {
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::<f32> {
min: math::Vec3::from(visible_bounding_box.min - focus_off),

View File

@ -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()

View File

@ -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,
}
}
}

View File

@ -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;