mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'imbris/bloom' into 'master'
Bloom See merge request veloren/veloren!2693
This commit is contained in:
commit
1bb9a9e315
90
assets/voxygen/shaders/dual-downsample-filtered-frag.glsl
Normal file
90
assets/voxygen/shaders/dual-downsample-filtered-frag.glsl
Normal file
@ -0,0 +1,90 @@
|
||||
#version 420 core
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
uniform texture2D t_src_color;
|
||||
layout(set = 0, binding = 1)
|
||||
uniform sampler s_src_color;
|
||||
layout(set = 0, binding = 2)
|
||||
|
||||
uniform u_locals {
|
||||
vec2 halfpixel;
|
||||
};
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0) out vec4 tgt_color;
|
||||
|
||||
vec4 simplefetch(ivec2 uv) {
|
||||
return texelFetch(sampler2D(t_src_color, s_src_color), uv, 0);
|
||||
}
|
||||
|
||||
// Check whether the texel color is higher than threshold, if so output as brightness color
|
||||
vec4 filterDim(vec4 color) {
|
||||
// constants from: https://learnopengl.com/Advanced-Lighting/Bloom
|
||||
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
|
||||
if(brightness > 1.00)
|
||||
return vec4(color.rgb, 1.0);
|
||||
else
|
||||
return vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec4 filteredFetch(ivec2 uv) {
|
||||
return filterDim(simplefetch(uv));
|
||||
}
|
||||
|
||||
// Derived from: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
|
||||
vec4 filteredDownsample(vec2 uv, vec2 halfpixel) {
|
||||
vec2 tex_res = 0.5 / halfpixel;
|
||||
// coordinate of the top left texel
|
||||
// _ _ _ _
|
||||
// |x|_|_|_|
|
||||
// |_|_|_|_|
|
||||
// |_|_|_|_|
|
||||
// |_|_|_|_|
|
||||
//
|
||||
ivec2 tl_coord = ivec2(uv * tex_res + vec2(-1.5, 1.5));
|
||||
|
||||
// Fetch inner square
|
||||
vec4 sum = filteredFetch(tl_coord + ivec2(1, 1));
|
||||
sum += filteredFetch(tl_coord + ivec2(2, 1));
|
||||
sum += filteredFetch(tl_coord + ivec2(1, 2));
|
||||
sum += filteredFetch(tl_coord + ivec2(2, 2));
|
||||
// Weight inner square
|
||||
sum *= 5.0;
|
||||
// Fetch border
|
||||
sum += filteredFetch(tl_coord + ivec2(0, 0));
|
||||
sum += filteredFetch(tl_coord + ivec2(1, 0));
|
||||
sum += filteredFetch(tl_coord + ivec2(2, 0));
|
||||
sum += filteredFetch(tl_coord + ivec2(3, 0));
|
||||
sum += filteredFetch(tl_coord + ivec2(0, 1));
|
||||
sum += filteredFetch(tl_coord + ivec2(3, 1));
|
||||
sum += filteredFetch(tl_coord + ivec2(0, 2));
|
||||
sum += filteredFetch(tl_coord + ivec2(3, 2));
|
||||
sum += filteredFetch(tl_coord + ivec2(0, 3));
|
||||
sum += filteredFetch(tl_coord + ivec2(1, 3));
|
||||
sum += filteredFetch(tl_coord + ivec2(2, 3));
|
||||
sum += filteredFetch(tl_coord + ivec2(3, 3));
|
||||
|
||||
return sum / 32.0;
|
||||
}
|
||||
|
||||
vec4 simplesample(vec2 uv) {
|
||||
return textureLod(sampler2D(t_src_color, s_src_color), uv, 0);
|
||||
}
|
||||
|
||||
// From: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
|
||||
vec4 downsample(vec2 uv, vec2 halfpixel) {
|
||||
vec4 sum = simplesample(uv) * 4.0;
|
||||
sum += simplesample(uv - halfpixel.xy);
|
||||
sum += simplesample(uv + halfpixel.xy);
|
||||
sum += simplesample(uv + vec2(halfpixel.x, -halfpixel.y));
|
||||
sum += simplesample(uv - vec2(halfpixel.x, -halfpixel.y));
|
||||
|
||||
return sum / 8.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Uncomment to experiment with filtering out dim pixels
|
||||
//tgt_color = filteredDownsample(uv, halfpixel);
|
||||
tgt_color = downsample(uv, halfpixel);
|
||||
}
|
34
assets/voxygen/shaders/dual-downsample-frag.glsl
Normal file
34
assets/voxygen/shaders/dual-downsample-frag.glsl
Normal file
@ -0,0 +1,34 @@
|
||||
#version 420 core
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
uniform texture2D t_src_color;
|
||||
layout(set = 0, binding = 1)
|
||||
uniform sampler s_src_color;
|
||||
layout(set = 0, binding = 2)
|
||||
|
||||
uniform u_locals {
|
||||
vec2 halfpixel;
|
||||
};
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0) out vec4 tgt_color;
|
||||
|
||||
vec4 simplesample(vec2 uv) {
|
||||
return textureLod(sampler2D(t_src_color, s_src_color), uv, 0);
|
||||
}
|
||||
|
||||
// From: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
|
||||
vec4 downsample(vec2 uv, vec2 halfpixel) {
|
||||
vec4 sum = simplesample(uv) * 4.0;
|
||||
sum += simplesample(uv - halfpixel.xy);
|
||||
sum += simplesample(uv + halfpixel.xy);
|
||||
sum += simplesample(uv + vec2(halfpixel.x, -halfpixel.y));
|
||||
sum += simplesample(uv - vec2(halfpixel.x, -halfpixel.y));
|
||||
|
||||
return sum / 8.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
tgt_color = downsample(uv, halfpixel);
|
||||
}
|
35
assets/voxygen/shaders/dual-upsample-frag.glsl
Normal file
35
assets/voxygen/shaders/dual-upsample-frag.glsl
Normal file
@ -0,0 +1,35 @@
|
||||
#version 420 core
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
uniform texture2D t_src_color;
|
||||
layout(set = 0, binding = 1)
|
||||
uniform sampler s_src_color;
|
||||
layout(set = 0, binding = 2)
|
||||
uniform u_locals {
|
||||
vec2 halfpixel;
|
||||
};
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout(location = 0) out vec4 tgt_color;
|
||||
|
||||
vec4 simplesample(vec2 uv) {
|
||||
return textureLod(sampler2D(t_src_color, s_src_color), uv, 0);
|
||||
}
|
||||
|
||||
// From: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
|
||||
vec4 upsample(vec2 uv, vec2 halfpixel) {
|
||||
vec4 sum = simplesample(uv + vec2(-halfpixel.x * 2.0, 0.0));
|
||||
sum += simplesample(uv + vec2(-halfpixel.x, halfpixel.y)) * 2.0;
|
||||
sum += simplesample(uv + vec2(0.0, halfpixel.y * 2.0));
|
||||
sum += simplesample(uv + vec2(halfpixel.x, halfpixel.y)) * 2.0;
|
||||
sum += simplesample(uv + vec2(halfpixel.x * 2.0, 0.0));
|
||||
sum += simplesample(uv + vec2(halfpixel.x, -halfpixel.y)) * 2.0;
|
||||
sum += simplesample(uv + vec2(0.0, -halfpixel.y * 2.0));
|
||||
sum += simplesample(uv + vec2(-halfpixel.x, -halfpixel.y)) * 2.0;
|
||||
return sum / 12.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
tgt_color = upsample(uv, halfpixel);
|
||||
}
|
@ -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 += 1000 * surf_color;
|
||||
emitted_light += 20 * surf_color;
|
||||
}
|
||||
|
||||
float glow_mag = length(model_glow.xyz);
|
||||
|
@ -145,7 +145,7 @@ void main() {
|
||||
nmap = mix(f_norm, normalize(nmap), min(1.0 / pow(frag_dist, 0.75), 1));
|
||||
|
||||
//float suppress_waves = max(dot(), 0);
|
||||
vec3 norm = vec3(0, 0, 1) * nmap.z + b_norm * nmap.x + c_norm * nmap.y;
|
||||
vec3 norm = normalize(vec3(0, 0, 1) * nmap.z + b_norm * nmap.x + c_norm * nmap.y);
|
||||
// vec3 norm = f_norm;
|
||||
|
||||
vec3 water_color = (1.0 - MU_WATER) * MU_SCATTER;
|
||||
|
@ -355,7 +355,7 @@ void main() {
|
||||
sin(lifetime * 2.0 + rand2) + sin(lifetime * 9.0 + rand5) * 0.3
|
||||
),
|
||||
vec3(raise),
|
||||
vec4(vec3(5, 5, 1.1), 1),
|
||||
vec4(vec3(10.3, 9, 1.5), 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5)
|
||||
);
|
||||
break;
|
||||
@ -386,7 +386,7 @@ void main() {
|
||||
attr = Attr(
|
||||
spiral_motion(vec3(0, 0, rand3 + 1), spiral_radius, lifetime, abs(rand0), rand1 * 2 * PI) + vec3(0, 0, rand2),
|
||||
vec3(6 * abs(rand4) * (1 - slow_start(2)) * pow(spiral_radius / length(inst_dir), 0.5)),
|
||||
vec4(vec3(0, 1.7, 0.7), 1),
|
||||
vec4(vec3(0, 1.7, 0.7) * 3, 1),
|
||||
spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3)
|
||||
);
|
||||
break;
|
||||
@ -397,7 +397,7 @@ void main() {
|
||||
attr = Attr(
|
||||
spiral_motion(inst_dir, 0.3 * (floor(2 * rand0 + 0.5) - 0.5) * min(linear_scale(10), 1), lifetime / inst_lifespan, 10.0, inst_time),
|
||||
vec3((1.7 - 0.7 * abs(floor(2 * rand0 - 0.5) + 0.5)) * (1.5 + 0.5 * sin(tick.x * 10 - lifetime * 4))),
|
||||
vec4(vec3(purple_col, green_col, 0.75 * purple_col), 1),
|
||||
vec4(vec3(purple_col, green_col, 0.75 * purple_col) * 3, 1),
|
||||
spin_in_axis(inst_dir, tick.z)
|
||||
);
|
||||
break;
|
||||
|
@ -27,7 +27,6 @@ uniform texture2D t_src_color;
|
||||
layout(set = 1, binding = 1)
|
||||
uniform sampler s_src_color;
|
||||
|
||||
|
||||
layout(location = 0) in vec2 uv;
|
||||
|
||||
layout (std140, set = 1, binding = 2)
|
||||
@ -36,6 +35,11 @@ uniform u_locals {
|
||||
mat4 view_mat_inv;
|
||||
};
|
||||
|
||||
#ifdef BLOOM_FACTOR
|
||||
layout(set = 1, binding = 3)
|
||||
uniform texture2D t_src_bloom;
|
||||
#endif
|
||||
|
||||
layout(location = 0) out vec4 tgt_color;
|
||||
|
||||
vec3 rgb2hsv(vec3 c) {
|
||||
@ -182,6 +186,16 @@ void main() {
|
||||
|
||||
vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy);
|
||||
|
||||
// Bloom
|
||||
#ifdef BLOOM_FACTOR
|
||||
vec4 bloom = textureLod(sampler2D(t_src_bloom, s_src_color), uv, 0);
|
||||
#if (BLOOM_UNIFORM_BLUR == false)
|
||||
// divide by 4.0 to account for adding blurred layers together
|
||||
bloom /= 4.0;
|
||||
#endif
|
||||
aa_color = mix(aa_color, bloom, BLOOM_FACTOR);
|
||||
#endif
|
||||
|
||||
// Tonemapping
|
||||
float exposure_offset = 1.0;
|
||||
// Adding an in-code offset to gamma and exposure let us have more precise control over the game's look
|
||||
|
@ -4,6 +4,8 @@
|
||||
#![deny(clippy::clone_on_ref_ptr)]
|
||||
#![feature(
|
||||
array_map,
|
||||
array_methods,
|
||||
array_zip,
|
||||
bool_to_option,
|
||||
const_generics,
|
||||
drain_filter,
|
||||
|
@ -257,6 +257,8 @@ impl PlayState for CharSelectionState {
|
||||
if let Some(mut second_pass) = drawer.second_pass() {
|
||||
second_pass.draw_clouds();
|
||||
}
|
||||
// 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();
|
||||
|
@ -265,6 +265,66 @@ impl From<PresentMode> for wgpu::PresentMode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Bloom factor
|
||||
/// Controls fraction of output image luminosity that is blurred bloom
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum BloomFactor {
|
||||
Low,
|
||||
High,
|
||||
/// Max valid value is 1.0
|
||||
Custom(f32),
|
||||
// other variant has to be placed last
|
||||
#[serde(other)]
|
||||
Standard,
|
||||
}
|
||||
|
||||
impl Default for BloomFactor {
|
||||
fn default() -> Self { Self::Standard }
|
||||
}
|
||||
|
||||
impl BloomFactor {
|
||||
/// Fraction of output image luminosity that is blurred bloom
|
||||
pub fn fraction(self) -> f32 {
|
||||
match self {
|
||||
Self::Low => 0.05,
|
||||
Self::Standard => 0.10,
|
||||
Self::High => 0.25,
|
||||
Self::Custom(val) => val.max(0.0).min(1.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bloom settings
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct BloomConfig {
|
||||
/// Controls fraction of output image luminosity that is blurred bloom
|
||||
///
|
||||
/// Defaults to `Standard`
|
||||
factor: BloomFactor,
|
||||
/// Turning this on make the bloom blur less sharply concentrated around the
|
||||
/// high intensity phenomena (removes adding in less blurred layers to the
|
||||
/// final blur)
|
||||
///
|
||||
/// Defaults to `false`
|
||||
uniform_blur: bool,
|
||||
// TODO: allow configuring the blur radius and/or the number of passes
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum BloomMode {
|
||||
On(BloomConfig),
|
||||
#[serde(other)]
|
||||
Off,
|
||||
}
|
||||
|
||||
impl Default for BloomMode {
|
||||
fn default() -> Self { Self::Off }
|
||||
}
|
||||
|
||||
impl BloomMode {
|
||||
fn is_on(&self) -> bool { matches!(self, BloomMode::On(_)) }
|
||||
}
|
||||
|
||||
/// Render modes
|
||||
#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
@ -274,7 +334,49 @@ pub struct RenderMode {
|
||||
pub fluid: FluidMode,
|
||||
pub lighting: LightingMode,
|
||||
pub shadow: ShadowMode,
|
||||
pub bloom: BloomMode,
|
||||
|
||||
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: BloomMode,
|
||||
}
|
||||
|
||||
/// Other render modes that don't effect pipelines
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
struct OtherModes {
|
||||
upscale_mode: UpscaleMode,
|
||||
present_mode: PresentMode,
|
||||
profiler_enabled: bool,
|
||||
}
|
||||
|
228
voxygen/src/render/pipelines/bloom.rs
Normal file
228
voxygen/src/render/pipelines/bloom.rs
Normal file
@ -0,0 +1,228 @@
|
||||
//! Based on: https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
|
||||
//!
|
||||
//! See additional details in the [NUM_SIZES] docs
|
||||
|
||||
use super::super::{BloomConfig, Consts};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
// TODO: auto-tune the number of passes to maintain roughly constant blur per
|
||||
// unit of FOV so changing resolution / FOV doesn't change the blur appearance
|
||||
// significantly.
|
||||
//
|
||||
/// Blurring is performed while downsampling to the smaller sizes in steps and
|
||||
/// then upsampling back up to the original resolution. Each level is half the
|
||||
/// size in both dimensions from the previous. For instance with 5 distinct
|
||||
/// sizes there is a total of 8 passes going from the largest to the smallest to
|
||||
/// the largest again:
|
||||
///
|
||||
/// 1 -> 1/2 -> 1/4 -> 1/8 -> 1/16 -> 1/8 -> 1/4 -> 1/2 -> 1
|
||||
/// ~~~~
|
||||
/// [downsampling] smallest [upsampling]
|
||||
///
|
||||
/// The textures used for downsampling are re-used when upsampling.
|
||||
///
|
||||
/// Additionally, instead of clearing them the colors are added together in an
|
||||
/// attempt to obtain a more concentrated bloom near bright areas rather than
|
||||
/// a uniform blur. In the example above, the added layers would include 1/8,
|
||||
/// 1/4, and 1/2. The smallest size is not upsampled to and the original full
|
||||
/// resolution has no blurring and we are already combining the bloom into the
|
||||
/// full resolution image in a later step, so they are not included here. The 3
|
||||
/// extra layers added in mean the total luminosity of the final blurred bloom
|
||||
/// image will be 4 times more than the input image. To account for this, we
|
||||
/// divide the bloom intensity by 4 before applying it.
|
||||
///
|
||||
/// Nevertheless, we have not fully evaluated how this visually compares to the
|
||||
/// bloom obtained without adding with the previous layers so there is the
|
||||
/// potential for further artistic investigation here.
|
||||
///
|
||||
/// NOTE: This constant includes the full resolution size and it is
|
||||
/// assumed that there will be at least one smaller image to downsample to and
|
||||
/// upsample back from (otherwise no blurring would be done). Thus, the minimum
|
||||
/// valid value is 2 and panicking indexing operations we perform assume this
|
||||
/// will be at least 2.
|
||||
pub const NUM_SIZES: usize = 5;
|
||||
|
||||
pub struct BindGroup {
|
||||
pub(in super::super) bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
|
||||
pub struct Locals {
|
||||
halfpixel: [f32; 2],
|
||||
}
|
||||
|
||||
impl Locals {
|
||||
pub fn new(source_texture_resolution: Vec2<f32>) -> Self {
|
||||
Self {
|
||||
halfpixel: source_texture_resolution.map(|e| 0.5 / e).into_array(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BloomLayout {
|
||||
pub layout: wgpu::BindGroupLayout,
|
||||
}
|
||||
|
||||
impl BloomLayout {
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
Self {
|
||||
layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: None,
|
||||
entries: &[
|
||||
// Color source
|
||||
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,
|
||||
},
|
||||
// halfpixel
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind(
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
src_color: &wgpu::TextureView,
|
||||
sampler: &wgpu::Sampler,
|
||||
half_pixel: 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),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: half_pixel.buf().as_entire_binding(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
BindGroup { bind_group }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BloomPipelines {
|
||||
pub downsample_filtered: wgpu::RenderPipeline,
|
||||
pub downsample: wgpu::RenderPipeline,
|
||||
pub upsample: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
impl BloomPipelines {
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
vs_module: &wgpu::ShaderModule,
|
||||
downsample_filtered_fs_module: &wgpu::ShaderModule,
|
||||
downsample_fs_module: &wgpu::ShaderModule,
|
||||
upsample_fs_module: &wgpu::ShaderModule,
|
||||
target_format: wgpu::TextureFormat,
|
||||
layout: &BloomLayout,
|
||||
bloom_config: &BloomConfig,
|
||||
) -> Self {
|
||||
common_base::span!(_guard, "BloomPipelines::new");
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Bloom pipelines layout"),
|
||||
push_constant_ranges: &[],
|
||||
bind_group_layouts: &[&layout.layout],
|
||||
});
|
||||
|
||||
let create_pipeline = |label, fs_module, blend| {
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some(label),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: vs_module,
|
||||
entry_point: "main",
|
||||
buffers: &[],
|
||||
},
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
clamp_depth: false,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: fs_module,
|
||||
entry_point: "main",
|
||||
targets: &[wgpu::ColorTargetState {
|
||||
format: target_format,
|
||||
blend,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}],
|
||||
}),
|
||||
})
|
||||
};
|
||||
|
||||
let downsample_filtered_pipeline = create_pipeline(
|
||||
"Bloom downsample filtered pipeline",
|
||||
downsample_filtered_fs_module,
|
||||
None,
|
||||
);
|
||||
let downsample_pipeline =
|
||||
create_pipeline("Bloom downsample pipeline", downsample_fs_module, None);
|
||||
let upsample_pipeline = create_pipeline(
|
||||
"Bloom upsample pipeline",
|
||||
upsample_fs_module,
|
||||
(!bloom_config.uniform_blur).then(|| wgpu::BlendState {
|
||||
color: wgpu::BlendComponent {
|
||||
src_factor: wgpu::BlendFactor::One,
|
||||
dst_factor: wgpu::BlendFactor::One,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
// We don't reaaly use this but we need something here..
|
||||
alpha: wgpu::BlendComponent::REPLACE,
|
||||
}),
|
||||
);
|
||||
|
||||
Self {
|
||||
downsample_filtered: downsample_filtered_pipeline,
|
||||
downsample: downsample_pipeline,
|
||||
upsample: upsample_pipeline,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
pub mod blit;
|
||||
pub mod bloom;
|
||||
pub mod clouds;
|
||||
pub mod debug;
|
||||
pub mod figure;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::super::{Consts, GlobalsLayouts};
|
||||
use super::super::{Consts, GlobalsLayouts, PipelineModes};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use vek::*;
|
||||
|
||||
@ -31,15 +31,12 @@ pub struct PostProcessLayout {
|
||||
}
|
||||
|
||||
impl PostProcessLayout {
|
||||
pub fn new(device: &wgpu::Device) -> Self {
|
||||
Self {
|
||||
layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: None,
|
||||
entries: &[
|
||||
pub fn new(device: &wgpu::Device, pipeline_modes: &PipelineModes) -> Self {
|
||||
let mut bind_entries = vec![
|
||||
// src color
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
@ -49,7 +46,7 @@ impl PostProcessLayout {
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
|
||||
visibility: wgpu::ShaderStage::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler {
|
||||
filtering: true,
|
||||
comparison: false,
|
||||
@ -67,7 +64,28 @@ impl PostProcessLayout {
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
if pipeline_modes.bloom.is_on() {
|
||||
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: &bind_entries,
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -76,13 +94,11 @@ impl PostProcessLayout {
|
||||
&self,
|
||||
device: &wgpu::Device,
|
||||
src_color: &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: &[
|
||||
let mut entries = vec![
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(src_color),
|
||||
@ -95,7 +111,25 @@ impl PostProcessLayout {
|
||||
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: 3,
|
||||
resource: wgpu::BindingResource::TextureView(src_bloom),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: None,
|
||||
layout: &self.layout,
|
||||
entries: &entries,
|
||||
});
|
||||
|
||||
BindGroup { bind_group }
|
||||
|
@ -21,11 +21,12 @@ use super::{
|
||||
mesh::Mesh,
|
||||
model::{DynamicModel, Model},
|
||||
pipelines::{
|
||||
blit, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui, GlobalsBindGroup,
|
||||
GlobalsLayouts, ShadowTexturesBindGroup,
|
||||
blit, bloom, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui,
|
||||
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,21 +46,35 @@ 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,
|
||||
|
||||
clouds: clouds::CloudsLayout,
|
||||
debug: debug::DebugLayout,
|
||||
figure: figure::FigureLayout,
|
||||
postprocess: postprocess::PostProcessLayout,
|
||||
shadow: shadow::ShadowLayout,
|
||||
sprite: sprite::SpriteLayout,
|
||||
terrain: terrain::TerrainLayout,
|
||||
clouds: clouds::CloudsLayout,
|
||||
bloom: bloom::BloomLayout,
|
||||
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
|
||||
@ -67,6 +82,8 @@ struct Views {
|
||||
|
||||
tgt_color: wgpu::TextureView,
|
||||
tgt_depth: wgpu::TextureView,
|
||||
|
||||
bloom_tgts: Option<[wgpu::TextureView; bloom::NUM_SIZES]>,
|
||||
// TODO: rename
|
||||
tgt_color_pp: wgpu::TextureView,
|
||||
}
|
||||
@ -81,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,
|
||||
@ -93,7 +111,19 @@ enum State {
|
||||
Complete {
|
||||
pipelines: Pipelines,
|
||||
shadow: Shadow,
|
||||
recreating: Option<PipelineCreation<Result<(Pipelines, ShadowPipelines), RenderError>>>,
|
||||
recreating: Option<(
|
||||
PipelineModes,
|
||||
PipelineCreation<
|
||||
Result<
|
||||
(
|
||||
Pipelines,
|
||||
ShadowPipelines,
|
||||
Arc<postprocess::PostProcessLayout>,
|
||||
),
|
||||
RenderError,
|
||||
>,
|
||||
>,
|
||||
)>,
|
||||
},
|
||||
}
|
||||
|
||||
@ -112,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,
|
||||
@ -128,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
|
||||
@ -152,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.
|
||||
//
|
||||
@ -285,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);
|
||||
@ -293,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);
|
||||
@ -305,41 +337,51 @@ impl Renderer {
|
||||
let layouts = {
|
||||
let global = GlobalsLayouts::new(&device);
|
||||
|
||||
let clouds = clouds::CloudsLayout::new(&device);
|
||||
let debug = debug::DebugLayout::new(&device);
|
||||
let figure = figure::FigureLayout::new(&device);
|
||||
let postprocess = postprocess::PostProcessLayout::new(&device);
|
||||
let shadow = shadow::ShadowLayout::new(&device);
|
||||
let sprite = sprite::SpriteLayout::new(&device);
|
||||
let terrain = terrain::TerrainLayout::new(&device);
|
||||
let clouds = clouds::CloudsLayout::new(&device);
|
||||
let bloom = bloom::BloomLayout::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,
|
||||
|
||||
clouds,
|
||||
debug,
|
||||
figure,
|
||||
postprocess,
|
||||
shadow,
|
||||
sprite,
|
||||
terrain,
|
||||
clouds,
|
||||
bloom,
|
||||
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(),
|
||||
)?;
|
||||
@ -350,7 +392,12 @@ impl Renderer {
|
||||
creating,
|
||||
};
|
||||
|
||||
let views = 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 {
|
||||
@ -389,6 +436,13 @@ impl Renderer {
|
||||
postprocess_locals,
|
||||
&views.tgt_color,
|
||||
&views.tgt_depth,
|
||||
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,
|
||||
@ -399,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 =
|
||||
@ -415,7 +469,7 @@ impl Renderer {
|
||||
sc_desc,
|
||||
|
||||
state,
|
||||
recreation_pending: false,
|
||||
recreation_pending: None,
|
||||
|
||||
layouts,
|
||||
locals,
|
||||
@ -430,7 +484,8 @@ impl Renderer {
|
||||
|
||||
shaders,
|
||||
|
||||
mode,
|
||||
pipeline_modes,
|
||||
other_modes,
|
||||
resolution: Vec2::new(dims.width, dims.height),
|
||||
|
||||
take_screenshot: None,
|
||||
@ -467,7 +522,7 @@ impl Renderer {
|
||||
/// Returns `Some((total, complete))` if in progress
|
||||
pub fn pipeline_recreation_status(&self) -> Option<(usize, usize)> {
|
||||
if let State::Complete { recreating, .. } = &self.state {
|
||||
recreating.as_ref().map(|r| r.status())
|
||||
recreating.as_ref().map(|(_, c)| c.status())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -475,37 +530,46 @@ 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();
|
||||
|
||||
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.mode.profiler_enabled &= self.profiler_features_enabled;
|
||||
self.other_modes.profiler_enabled &= self.profiler_features_enabled;
|
||||
// Enable/disable profiler
|
||||
if !self.mode.profiler_enabled {
|
||||
if !self.other_modes.profiler_enabled {
|
||||
// Clear the times if disabled
|
||||
core::mem::take(&mut self.profile_times);
|
||||
}
|
||||
self.profiler.enable_timer = self.mode.profiler_enabled;
|
||||
self.profiler.enable_debug_marker = self.mode.profiler_enabled;
|
||||
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.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 self.pipeline_modes != pipeline_modes
|
||||
|| 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
|
||||
@ -535,7 +599,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;
|
||||
@ -546,13 +610,36 @@ impl Renderer {
|
||||
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
|
||||
|
||||
// Resize other render targets
|
||||
self.views = Self::create_rt_views(&self.device, (dims.x, dims.y), &self.mode)?;
|
||||
// Rebind views to clouds/postprocess bind groups
|
||||
let (views, bloom_sizes) = Self::create_rt_views(
|
||||
&self.device,
|
||||
(dims.x, dims.y),
|
||||
&self.pipeline_modes,
|
||||
&self.other_modes,
|
||||
);
|
||||
self.views = views;
|
||||
|
||||
// appease borrow check (TODO: remove after Rust 2021)
|
||||
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,
|
||||
&self.views.tgt_color,
|
||||
&self.views.tgt_depth,
|
||||
bloom_params,
|
||||
&self.views.tgt_color_pp,
|
||||
&self.sampler,
|
||||
&self.depth_sampler,
|
||||
@ -576,7 +663,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)) => {
|
||||
@ -608,8 +695,6 @@ impl Renderer {
|
||||
} else {
|
||||
self.is_minimized = true;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn maintain(&self) {
|
||||
@ -624,12 +709,13 @@ impl Renderer {
|
||||
fn create_rt_views(
|
||||
device: &wgpu::Device,
|
||||
size: (u32, u32),
|
||||
mode: &RenderMode,
|
||||
) -> Result<Views, 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),
|
||||
@ -637,7 +723,7 @@ impl Renderer {
|
||||
};
|
||||
let levels = 1;
|
||||
|
||||
let color_view = || {
|
||||
let color_view = |width, height| {
|
||||
let tex = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
size: wgpu::Extent3d {
|
||||
@ -665,8 +751,22 @@ impl Renderer {
|
||||
})
|
||||
};
|
||||
|
||||
let tgt_color_view = color_view();
|
||||
let tgt_color_pp_view = color_view();
|
||||
let tgt_color_view = color_view(width, height);
|
||||
let tgt_color_pp_view = color_view(width, height);
|
||||
|
||||
let mut size_shift = 0;
|
||||
// TODO: skip creating bloom stuff when it is disabled
|
||||
let bloom_sizes = [(); bloom::NUM_SIZES].map(|()| {
|
||||
// .max(1) to ensure we don't create zero sized textures
|
||||
let size = Vec2::new(width, height).map(|e| (e >> size_shift).max(1));
|
||||
size_shift += 1;
|
||||
size
|
||||
});
|
||||
|
||||
let bloom_tgt_views = pipeline_modes
|
||||
.bloom
|
||||
.is_on()
|
||||
.then(|| bloom_sizes.map(|size| color_view(size.x, size.y)));
|
||||
|
||||
let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
@ -717,12 +817,16 @@ impl Renderer {
|
||||
array_layer_count: None,
|
||||
});
|
||||
|
||||
Ok(Views {
|
||||
(
|
||||
Views {
|
||||
tgt_color: tgt_color_view,
|
||||
tgt_depth: tgt_depth_view,
|
||||
bloom_tgts: bloom_tgt_views,
|
||||
tgt_color_pp: tgt_color_pp_view,
|
||||
_win_depth: win_depth_view,
|
||||
})
|
||||
},
|
||||
bloom_sizes.map(|s| s.map(|e| e as f32)),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the resolution of the render target.
|
||||
@ -796,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;
|
||||
@ -806,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,
|
||||
@ -857,11 +965,11 @@ impl Renderer {
|
||||
} else if let State::Complete {
|
||||
pipelines,
|
||||
mut shadow,
|
||||
recreating: Some(recreating),
|
||||
recreating: Some((new_pipeline_modes, pipeline_creation)),
|
||||
} = state
|
||||
{
|
||||
match recreating.try_complete() {
|
||||
Ok(Ok((pipelines, shadow_pipelines))) => {
|
||||
match pipeline_creation.try_complete() {
|
||||
Ok(Ok((pipelines, shadow_pipelines, postprocess_layout))) => {
|
||||
if let (
|
||||
Some(point_pipeline),
|
||||
Some(terrain_directed_pipeline),
|
||||
@ -877,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,
|
||||
@ -892,27 +1008,36 @@ impl Renderer {
|
||||
}
|
||||
},
|
||||
// Not complete
|
||||
Err(recreating) => State::Complete {
|
||||
Err(pipeline_creation) => State::Complete {
|
||||
pipelines,
|
||||
shadow,
|
||||
recreating: Some(recreating),
|
||||
recreating: Some((new_pipeline_modes, pipeline_creation)),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
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() {
|
||||
@ -920,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
|
||||
@ -945,29 +1071,36 @@ 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(
|
||||
*recreating = Some((
|
||||
pipeline_modes.clone(),
|
||||
pipeline_creation::recreate_pipelines(
|
||||
Arc::clone(&self.device),
|
||||
Arc::clone(&self.layouts),
|
||||
Arc::clone(&self.layouts.immutable),
|
||||
self.shaders.read().clone(),
|
||||
self.mode.clone(),
|
||||
pipeline_modes,
|
||||
// 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(),
|
||||
),
|
||||
));
|
||||
},
|
||||
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 => {},
|
||||
}
|
||||
@ -1184,7 +1317,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()
|
||||
|
@ -4,8 +4,8 @@ use super::{
|
||||
instances::Instances,
|
||||
model::{DynamicModel, Model, SubModel},
|
||||
pipelines::{
|
||||
blit, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, sprite,
|
||||
terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup,
|
||||
blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox,
|
||||
sprite, terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup,
|
||||
},
|
||||
},
|
||||
Renderer, ShadowMap, ShadowMapRenderer,
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -241,6 +241,94 @@ 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 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);
|
||||
|
||||
let mut run_bloom_pass = |bind, view, label: String, pipeline, load| {
|
||||
let pass_label = format!("bloom {} pass", label);
|
||||
let mut render_pass =
|
||||
encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor {
|
||||
label: Some(&pass_label),
|
||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||
resolve_target: None,
|
||||
view,
|
||||
ops: wgpu::Operations { store: true, load },
|
||||
}],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
render_pass.set_bind_group(0, bind, &[]);
|
||||
render_pass.set_pipeline(pipeline);
|
||||
render_pass.draw(0..3, 0..1);
|
||||
};
|
||||
|
||||
// Downsample filter passes
|
||||
(0..bloom::NUM_SIZES - 1).for_each(|index| {
|
||||
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),
|
||||
&bloom_pipelines.downsample_filtered,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
format!("downsample {}", index + 1),
|
||||
&bloom_pipelines.downsample,
|
||||
)
|
||||
};
|
||||
run_bloom_pass(
|
||||
bind,
|
||||
view,
|
||||
label,
|
||||
pipeline,
|
||||
wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
|
||||
);
|
||||
});
|
||||
|
||||
// Upsample filter passes
|
||||
(0..bloom::NUM_SIZES - 1).for_each(|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,
|
||||
&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)
|
||||
} else {
|
||||
// Add to less blurred images to get gradient of blur instead of a smudge>
|
||||
// https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/
|
||||
wgpu::LoadOp::Load
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn third_pass(&mut self) -> ThirdPassDrawer {
|
||||
let encoder = self.encoder.as_mut().unwrap();
|
||||
let device = self.borrow.device;
|
||||
@ -321,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;
|
||||
}
|
||||
|
||||
|
@ -1,15 +1,23 @@
|
||||
use super::{
|
||||
super::{
|
||||
consts::Consts,
|
||||
pipelines::{clouds, postprocess},
|
||||
pipelines::{bloom, clouds, postprocess},
|
||||
},
|
||||
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: Option<[bloom::BindGroup; bloom::NUM_SIZES]>,
|
||||
|
||||
pub postprocess: Consts<postprocess::Locals>,
|
||||
pub postprocess_bind: postprocess::BindGroup,
|
||||
}
|
||||
@ -22,6 +30,7 @@ impl Locals {
|
||||
postprocess_locals: Consts<postprocess::Locals>,
|
||||
tgt_color_view: &wgpu::TextureView,
|
||||
tgt_depth_view: &wgpu::TextureView,
|
||||
bloom: Option<BloomParams>,
|
||||
tgt_color_pp_view: &wgpu::TextureView,
|
||||
sampler: &wgpu::Sampler,
|
||||
depth_sampler: &wgpu::Sampler,
|
||||
@ -34,14 +43,26 @@ impl Locals {
|
||||
depth_sampler,
|
||||
&clouds_locals,
|
||||
);
|
||||
let postprocess_bind =
|
||||
layouts
|
||||
.postprocess
|
||||
.bind(device, tgt_color_pp_view, sampler, &postprocess_locals);
|
||||
|
||||
let postprocess_bind = layouts.postprocess.bind(
|
||||
device,
|
||||
tgt_color_pp_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,
|
||||
bloom_binds,
|
||||
postprocess: postprocess_locals,
|
||||
postprocess_bind,
|
||||
}
|
||||
@ -55,6 +76,7 @@ impl Locals {
|
||||
// e.g. resizing
|
||||
tgt_color_view: &wgpu::TextureView,
|
||||
tgt_depth_view: &wgpu::TextureView,
|
||||
bloom: Option<BloomParams>,
|
||||
tgt_color_pp_view: &wgpu::TextureView,
|
||||
sampler: &wgpu::Sampler,
|
||||
depth_sampler: &wgpu::Sampler,
|
||||
@ -67,9 +89,18 @@ impl Locals {
|
||||
depth_sampler,
|
||||
&self.clouds,
|
||||
);
|
||||
self.postprocess_bind =
|
||||
layouts
|
||||
.postprocess
|
||||
.bind(device, tgt_color_pp_view, sampler, &self.postprocess);
|
||||
self.postprocess_bind = layouts.postprocess.bind(
|
||||
device,
|
||||
tgt_color_pp_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))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
use super::{
|
||||
super::{
|
||||
pipelines::{
|
||||
blit, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox,
|
||||
sprite, terrain, ui,
|
||||
blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow,
|
||||
skybox, sprite, terrain, ui,
|
||||
},
|
||||
AaMode, CloudMode, FluidMode, LightingMode, RenderError, RenderMode, ShadowMode,
|
||||
AaMode, BloomMode, CloudMode, FluidMode, LightingMode, PipelineModes, RenderError,
|
||||
ShadowMode,
|
||||
},
|
||||
shaders::Shaders,
|
||||
Layouts,
|
||||
ImmutableLayouts, Layouts,
|
||||
};
|
||||
use common_base::prof_span;
|
||||
use std::sync::Arc;
|
||||
@ -20,6 +21,7 @@ pub struct Pipelines {
|
||||
pub lod_terrain: lod_terrain::LodTerrainPipeline,
|
||||
pub particle: particle::ParticlePipeline,
|
||||
pub clouds: clouds::CloudsPipeline,
|
||||
pub bloom: Option<bloom::BloomPipelines>,
|
||||
pub postprocess: postprocess::PostProcessPipeline,
|
||||
// Consider reenabling at some time
|
||||
// player_shadow: figure::FigurePipeline,
|
||||
@ -39,6 +41,7 @@ pub struct IngamePipelines {
|
||||
lod_terrain: lod_terrain::LodTerrainPipeline,
|
||||
particle: particle::ParticlePipeline,
|
||||
clouds: clouds::CloudsPipeline,
|
||||
pub bloom: Option<bloom::BloomPipelines>,
|
||||
postprocess: postprocess::PostProcessPipeline,
|
||||
// Consider reenabling at some time
|
||||
// player_shadow: figure::FigurePipeline,
|
||||
@ -74,6 +77,7 @@ impl Pipelines {
|
||||
lod_terrain: ingame.lod_terrain,
|
||||
particle: ingame.particle,
|
||||
clouds: ingame.clouds,
|
||||
bloom: ingame.bloom,
|
||||
postprocess: ingame.postprocess,
|
||||
//player_shadow: ingame.player_shadow,
|
||||
skybox: ingame.skybox,
|
||||
@ -107,6 +111,9 @@ struct ShaderModules {
|
||||
lod_terrain_frag: wgpu::ShaderModule,
|
||||
clouds_vert: wgpu::ShaderModule,
|
||||
clouds_frag: wgpu::ShaderModule,
|
||||
dual_downsample_filtered_frag: wgpu::ShaderModule,
|
||||
dual_downsample_frag: wgpu::ShaderModule,
|
||||
dual_upsample_frag: wgpu::ShaderModule,
|
||||
postprocess_vert: wgpu::ShaderModule,
|
||||
postprocess_frag: wgpu::ShaderModule,
|
||||
blit_vert: wgpu::ShaderModule,
|
||||
@ -120,7 +127,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");
|
||||
@ -150,11 +157,11 @@ impl ShaderModules {
|
||||
&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",
|
||||
@ -162,20 +169,38 @@ 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",
|
||||
},
|
||||
);
|
||||
|
||||
let constants = match pipeline_modes.bloom {
|
||||
BloomMode::Off => constants,
|
||||
BloomMode::On(config) => {
|
||||
format!(
|
||||
r#"
|
||||
{}
|
||||
|
||||
#define BLOOM_FACTOR {}
|
||||
#define BLOOM_UNIFORM_BLUR {}
|
||||
|
||||
"#,
|
||||
constants,
|
||||
config.factor.fraction(),
|
||||
config.uniform_blur,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
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",
|
||||
@ -185,7 +210,7 @@ impl ShaderModules {
|
||||
.unwrap();
|
||||
|
||||
let cloud = shaders
|
||||
.get(match mode.cloud {
|
||||
.get(match pipeline_modes.cloud {
|
||||
CloudMode::None => "include.cloud.none",
|
||||
_ => "include.cloud.regular",
|
||||
})
|
||||
@ -228,7 +253,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",
|
||||
}]
|
||||
@ -255,6 +280,12 @@ impl ShaderModules {
|
||||
lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?,
|
||||
clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?,
|
||||
clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?,
|
||||
dual_downsample_filtered_frag: create_shader(
|
||||
"dual-downsample-filtered-frag",
|
||||
ShaderKind::Fragment,
|
||||
)?,
|
||||
dual_downsample_frag: create_shader("dual-downsample-frag", ShaderKind::Fragment)?,
|
||||
dual_upsample_frag: create_shader("dual-upsample-frag", ShaderKind::Fragment)?,
|
||||
postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?,
|
||||
postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?,
|
||||
blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?,
|
||||
@ -305,7 +336,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,
|
||||
}
|
||||
|
||||
@ -360,7 +391,7 @@ fn create_interface_pipelines(
|
||||
fn create_ingame_and_shadow_pipelines(
|
||||
needs: PipelineNeeds,
|
||||
pool: &rayon::ThreadPool,
|
||||
tasks: [Task; 13],
|
||||
tasks: [Task; 14],
|
||||
) -> IngameAndShadowPipelines {
|
||||
prof_span!(_guard, "create_ingame_and_shadow_pipelines");
|
||||
|
||||
@ -368,7 +399,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
device,
|
||||
layouts,
|
||||
shaders,
|
||||
mode,
|
||||
pipeline_modes,
|
||||
sc_desc,
|
||||
} = needs;
|
||||
|
||||
@ -382,6 +413,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
particle_task,
|
||||
lod_terrain_task,
|
||||
clouds_task,
|
||||
bloom_task,
|
||||
postprocess_task,
|
||||
// TODO: if these are ever actually optionally done, counting them
|
||||
// as tasks to do beforehand seems kind of iffy since they will just
|
||||
@ -403,7 +435,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
&shaders.debug_frag,
|
||||
&layouts.global,
|
||||
&layouts.debug,
|
||||
mode.aa,
|
||||
pipeline_modes.aa,
|
||||
)
|
||||
},
|
||||
"debug pipeline creation",
|
||||
@ -418,7 +450,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
&shaders.skybox_vert,
|
||||
&shaders.skybox_frag,
|
||||
&layouts.global,
|
||||
mode.aa,
|
||||
pipeline_modes.aa,
|
||||
)
|
||||
},
|
||||
"skybox pipeline creation",
|
||||
@ -434,7 +466,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
&shaders.figure_frag,
|
||||
&layouts.global,
|
||||
&layouts.figure,
|
||||
mode.aa,
|
||||
pipeline_modes.aa,
|
||||
)
|
||||
},
|
||||
"figure pipeline creation",
|
||||
@ -450,7 +482,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
&shaders.terrain_frag,
|
||||
&layouts.global,
|
||||
&layouts.terrain,
|
||||
mode.aa,
|
||||
pipeline_modes.aa,
|
||||
)
|
||||
},
|
||||
"terrain pipeline creation",
|
||||
@ -466,7 +498,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
&shaders.fluid_frag,
|
||||
&layouts.global,
|
||||
&layouts.terrain,
|
||||
mode.aa,
|
||||
pipeline_modes.aa,
|
||||
)
|
||||
},
|
||||
"fluid pipeline creation",
|
||||
@ -483,7 +515,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
&layouts.global,
|
||||
&layouts.sprite,
|
||||
&layouts.terrain,
|
||||
mode.aa,
|
||||
pipeline_modes.aa,
|
||||
)
|
||||
},
|
||||
"sprite pipeline creation",
|
||||
@ -498,7 +530,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
&shaders.particle_vert,
|
||||
&shaders.particle_frag,
|
||||
&layouts.global,
|
||||
mode.aa,
|
||||
pipeline_modes.aa,
|
||||
)
|
||||
},
|
||||
"particle pipeline creation",
|
||||
@ -513,7 +545,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",
|
||||
@ -529,12 +561,36 @@ fn create_ingame_and_shadow_pipelines(
|
||||
&shaders.clouds_frag,
|
||||
&layouts.global,
|
||||
&layouts.clouds,
|
||||
mode.aa,
|
||||
pipeline_modes.aa,
|
||||
)
|
||||
},
|
||||
"clouds pipeline creation",
|
||||
)
|
||||
};
|
||||
// Pipelines for rendering our bloom
|
||||
let create_bloom = || {
|
||||
bloom_task.run(
|
||||
|| {
|
||||
match &pipeline_modes.bloom {
|
||||
BloomMode::Off => None,
|
||||
BloomMode::On(config) => Some(config),
|
||||
}
|
||||
.map(|bloom_config| {
|
||||
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_config,
|
||||
)
|
||||
})
|
||||
},
|
||||
"bloom pipelines creation",
|
||||
)
|
||||
};
|
||||
// Pipeline for rendering our post-processing
|
||||
let create_postprocess = || {
|
||||
postprocess_task.run(
|
||||
@ -584,7 +640,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",
|
||||
@ -599,7 +655,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",
|
||||
@ -614,7 +670,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",
|
||||
@ -622,7 +678,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
};
|
||||
|
||||
let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure));
|
||||
let j2 = || pool.join(create_terrain, create_fluid);
|
||||
let j2 = || pool.join(create_terrain, || pool.join(create_fluid, create_bloom));
|
||||
let j3 = || pool.join(create_sprite, create_particle);
|
||||
let j4 = || pool.join(create_lod_terrain, create_clouds);
|
||||
let j5 = || pool.join(create_postprocess, create_point_shadow);
|
||||
@ -636,7 +692,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
// Ignore this
|
||||
let (
|
||||
(
|
||||
((debug, (skybox, figure)), (terrain, fluid)),
|
||||
((debug, (skybox, figure)), (terrain, (fluid, bloom))),
|
||||
((sprite, particle), (lod_terrain, clouds)),
|
||||
),
|
||||
((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)),
|
||||
@ -653,6 +709,7 @@ fn create_ingame_and_shadow_pipelines(
|
||||
lod_terrain,
|
||||
particle,
|
||||
clouds,
|
||||
bloom,
|
||||
postprocess,
|
||||
skybox,
|
||||
sprite,
|
||||
@ -675,9 +732,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<
|
||||
@ -690,7 +747,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()
|
||||
@ -702,7 +759,7 @@ pub(super) fn initial_create_pipelines(
|
||||
device: &device,
|
||||
layouts: &layouts,
|
||||
shaders: &shader_modules,
|
||||
mode: &mode,
|
||||
pipeline_modes: &pipeline_modes,
|
||||
sc_desc: &sc_desc,
|
||||
};
|
||||
|
||||
@ -729,7 +786,7 @@ pub(super) fn initial_create_pipelines(
|
||||
device: &device,
|
||||
layouts: &layouts,
|
||||
shaders: &shader_modules,
|
||||
mode: &mode,
|
||||
pipeline_modes: &pipeline_modes,
|
||||
sc_desc: &sc_desc,
|
||||
};
|
||||
|
||||
@ -745,14 +802,24 @@ 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,
|
||||
Arc<postprocess::PostProcessLayout>,
|
||||
),
|
||||
RenderError,
|
||||
>,
|
||||
> {
|
||||
prof_span!(_guard, "recreate_pipelines");
|
||||
|
||||
// Create threadpool for parallel portion
|
||||
@ -780,7 +847,8 @@ 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) {
|
||||
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");
|
||||
@ -789,11 +857,22 @@ pub(super) fn recreate_pipelines(
|
||||
};
|
||||
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,
|
||||
};
|
||||
|
||||
@ -806,7 +885,11 @@ pub(super) fn recreate_pipelines(
|
||||
|
||||
// Send them
|
||||
result_send
|
||||
.send(Ok((Pipelines::consolidate(interface, ingame), shadow)))
|
||||
.send(Ok((
|
||||
Pipelines::consolidate(interface, ingame),
|
||||
shadow,
|
||||
layouts.postprocess,
|
||||
)))
|
||||
.expect("Channel disconnected");
|
||||
});
|
||||
|
||||
|
@ -68,6 +68,10 @@ impl assets::Compound for Shaders {
|
||||
"lod-terrain-frag",
|
||||
"clouds-vert",
|
||||
"clouds-frag",
|
||||
"dual-downsample-filtered-frag",
|
||||
"dual-downsample-frag",
|
||||
"dual-upsample-frag",
|
||||
"clouds-frag",
|
||||
"postprocess-vert",
|
||||
"postprocess-frag",
|
||||
"blit-vert",
|
||||
|
@ -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: _,
|
||||
|
@ -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() {
|
||||
|
@ -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),
|
||||
|
@ -1483,6 +1483,11 @@ impl PlayState for SessionState {
|
||||
second_pass.draw_clouds();
|
||||
}
|
||||
}
|
||||
// Bloom (call does nothing if bloom is off)
|
||||
{
|
||||
prof_span!("bloom");
|
||||
drawer.run_bloom_passes()
|
||||
}
|
||||
// PostProcess and UI
|
||||
{
|
||||
prof_span!("post-process and ui");
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user