Initial bloom impl

This commit is contained in:
Imbris
2021-07-21 04:58:03 -04:00
parent b8716e03a6
commit 2d83ef1c0e
12 changed files with 478 additions and 38 deletions

View File

@ -0,0 +1,31 @@
#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 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);
}

View File

@ -0,0 +1,33 @@
#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 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);
}

View File

@ -25,6 +25,8 @@
layout(set = 1, binding = 0) layout(set = 1, binding = 0)
uniform texture2D t_src_color; uniform texture2D t_src_color;
layout(set = 1, binding = 1) layout(set = 1, binding = 1)
uniform texture2D t_src_bloom;
layout(set = 1, binding = 2)
uniform sampler s_src_color; uniform sampler s_src_color;
@ -182,6 +184,10 @@ void main() {
vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy); vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy);
// Bloom
vec4 bloom = textureLod(sampler2D(t_src_bloom, s_src_color), uv, 0) * 0.05;
aa_color += bloom;
// Tonemapping // Tonemapping
float exposure_offset = 1.0; 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 // Adding an in-code offset to gamma and exposure let us have more precise control over the game's look

View File

@ -4,6 +4,8 @@
#![deny(clippy::clone_on_ref_ptr)] #![deny(clippy::clone_on_ref_ptr)]
#![feature( #![feature(
array_map, array_map,
array_methods,
array_zip,
bool_to_option, bool_to_option,
const_generics, const_generics,
drain_filter, drain_filter,

View File

@ -0,0 +1,197 @@
use super::super::Consts;
use bytemuck::{Pod, Zeroable};
use vek::*;
/// Each level is a multiple of 2 smaller in both dimensions.
/// For a total of 8 passes from the largest to the smallest to the largest
/// again.
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 HalfPixel([f32; 2]);
impl HalfPixel {
pub fn new(source_texture_resolution: Vec2<f32>) -> Self {
Self(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<HalfPixel>,
) -> 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,
) -> 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 downsample_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Bloom downsample pipeline"),
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: downsample_fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: target_format,
blend: None,
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
let upsample_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Bloom upsample pipeline"),
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: upsample_fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: target_format,
blend: None,
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
downsample: downsample_pipeline,
upsample: upsample_pipeline,
}
}
}

View File

@ -1,4 +1,5 @@
pub mod blit; pub mod blit;
pub mod bloom;
pub mod clouds; pub mod clouds;
pub mod debug; pub mod debug;
pub mod figure; pub mod figure;

View File

@ -39,7 +39,19 @@ impl PostProcessLayout {
// src color // src color
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, 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,
multisampled: false,
},
count: None,
},
// TODO: make optional
// src bloom
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture { ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true }, sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2, view_dimension: wgpu::TextureViewDimension::D2,
@ -49,7 +61,7 @@ impl PostProcessLayout {
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler {
filtering: true, filtering: true,
comparison: false, comparison: false,
@ -76,6 +88,7 @@ impl PostProcessLayout {
&self, &self,
device: &wgpu::Device, device: &wgpu::Device,
src_color: &wgpu::TextureView, src_color: &wgpu::TextureView,
src_bloom: &wgpu::TextureView,
sampler: &wgpu::Sampler, sampler: &wgpu::Sampler,
locals: &Consts<Locals>, locals: &Consts<Locals>,
) -> BindGroup { ) -> BindGroup {
@ -87,6 +100,14 @@ impl PostProcessLayout {
binding: 0, binding: 0,
resource: wgpu::BindingResource::TextureView(src_color), resource: wgpu::BindingResource::TextureView(src_color),
}, },
// 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: 0,
resource: wgpu::BindingResource::TextureView(src_bloom),
},
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
binding: 1, binding: 1,
resource: wgpu::BindingResource::Sampler(sampler), resource: wgpu::BindingResource::Sampler(sampler),

View File

@ -21,8 +21,8 @@ use super::{
mesh::Mesh, mesh::Mesh,
model::{DynamicModel, Model}, model::{DynamicModel, Model},
pipelines::{ pipelines::{
blit, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui, GlobalsBindGroup, blit, bloom, clouds, debug, figure, postprocess, shadow, sprite, terrain, ui,
GlobalsLayouts, ShadowTexturesBindGroup, GlobalsBindGroup, GlobalsLayouts, ShadowTexturesBindGroup,
}, },
texture::Texture, texture::Texture,
AaMode, AddressMode, FilterMode, RenderError, RenderMode, ShadowMapMode, ShadowMode, Vertex, AaMode, AddressMode, FilterMode, RenderError, RenderMode, ShadowMapMode, ShadowMode, Vertex,
@ -49,13 +49,14 @@ const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000;
struct Layouts { struct Layouts {
global: GlobalsLayouts, global: GlobalsLayouts,
clouds: clouds::CloudsLayout,
debug: debug::DebugLayout, debug: debug::DebugLayout,
figure: figure::FigureLayout, figure: figure::FigureLayout,
postprocess: postprocess::PostProcessLayout,
shadow: shadow::ShadowLayout, shadow: shadow::ShadowLayout,
sprite: sprite::SpriteLayout, sprite: sprite::SpriteLayout,
terrain: terrain::TerrainLayout, terrain: terrain::TerrainLayout,
clouds: clouds::CloudsLayout,
bloom: bloom::BloomLayout,
postprocess: postprocess::PostProcessLayout,
ui: ui::UiLayout, ui: ui::UiLayout,
blit: blit::BlitLayout, blit: blit::BlitLayout,
} }
@ -67,6 +68,8 @@ struct Views {
tgt_color: wgpu::TextureView, tgt_color: wgpu::TextureView,
tgt_depth: wgpu::TextureView, tgt_depth: wgpu::TextureView,
bloom_tgts: [wgpu::TextureView; bloom::NUM_SIZES],
// TODO: rename // TODO: rename
tgt_color_pp: wgpu::TextureView, tgt_color_pp: wgpu::TextureView,
} }
@ -305,26 +308,28 @@ impl Renderer {
let layouts = { let layouts = {
let global = GlobalsLayouts::new(&device); let global = GlobalsLayouts::new(&device);
let clouds = clouds::CloudsLayout::new(&device);
let debug = debug::DebugLayout::new(&device); let debug = debug::DebugLayout::new(&device);
let figure = figure::FigureLayout::new(&device); let figure = figure::FigureLayout::new(&device);
let postprocess = postprocess::PostProcessLayout::new(&device);
let shadow = shadow::ShadowLayout::new(&device); let shadow = shadow::ShadowLayout::new(&device);
let sprite = sprite::SpriteLayout::new(&device); let sprite = sprite::SpriteLayout::new(&device);
let terrain = terrain::TerrainLayout::new(&device); 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 ui = ui::UiLayout::new(&device); let ui = ui::UiLayout::new(&device);
let blit = blit::BlitLayout::new(&device); let blit = blit::BlitLayout::new(&device);
Layouts { Layouts {
global, global,
clouds,
debug, debug,
figure, figure,
postprocess,
shadow, shadow,
sprite, sprite,
terrain, terrain,
clouds,
bloom,
postprocess,
ui, ui,
blit, blit,
} }
@ -350,7 +355,8 @@ impl Renderer {
creating, 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), &mode)?;
let create_sampler = |filter| { let create_sampler = |filter| {
device.create_sampler(&wgpu::SamplerDescriptor { device.create_sampler(&wgpu::SamplerDescriptor {
@ -379,6 +385,8 @@ impl Renderer {
let clouds_locals = let clouds_locals =
Self::create_consts_inner(&device, &queue, &[clouds::Locals::default()]); 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 = let postprocess_locals =
Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]); Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]);
@ -386,9 +394,18 @@ impl Renderer {
&device, &device,
&layouts, &layouts,
clouds_locals, clouds_locals,
bloom_locals,
postprocess_locals, postprocess_locals,
&views.tgt_color, &views.tgt_color,
&views.tgt_depth, &views.tgt_depth,
[
&views.tgt_color,
&views.bloom_tgts[1],
&views.bloom_tgts[2],
&views.bloom_tgts[3],
&views.bloom_tgts[4],
],
&views.bloom_tgts[0],
&views.tgt_color_pp, &views.tgt_color_pp,
&sampler, &sampler,
&depth_sampler, &depth_sampler,
@ -546,13 +563,26 @@ impl Renderer {
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc); self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
// Resize other render targets // Resize other render targets
self.views = 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.mode)?;
self.views = views;
let bloom_locals =
bloom_sizes.map(|size| self.create_consts(&[bloom::HalfPixel::new(size)]));
// Rebind views to clouds/postprocess bind groups // Rebind views to clouds/postprocess bind groups
self.locals.rebind( self.locals.rebind(
&self.device, &self.device,
&self.layouts, &self.layouts,
bloom_locals,
&self.views.tgt_color, &self.views.tgt_color,
&self.views.tgt_depth, &self.views.tgt_depth,
[
&self.views.tgt_color,
&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],
&self.views.tgt_color_pp, &self.views.tgt_color_pp,
&self.sampler, &self.sampler,
&self.depth_sampler, &self.depth_sampler,
@ -625,7 +655,7 @@ impl Renderer {
device: &wgpu::Device, device: &wgpu::Device,
size: (u32, u32), size: (u32, u32),
mode: &RenderMode, mode: &RenderMode,
) -> Result<Views, RenderError> { ) -> Result<(Views, [Vec2<f32>; bloom::NUM_SIZES]), RenderError> {
let upscaled = Vec2::<u32>::from(size) let upscaled = Vec2::<u32>::from(size)
.map(|e| (e as f32 * mode.upscale_mode.factor) as u32) .map(|e| (e as f32 * mode.upscale_mode.factor) as u32)
.into_tuple(); .into_tuple();
@ -637,7 +667,7 @@ impl Renderer {
}; };
let levels = 1; let levels = 1;
let color_view = || { let color_view = |width, height| {
let tex = device.create_texture(&wgpu::TextureDescriptor { let tex = device.create_texture(&wgpu::TextureDescriptor {
label: None, label: None,
size: wgpu::Extent3d { size: wgpu::Extent3d {
@ -665,8 +695,19 @@ impl Renderer {
}) })
}; };
let tgt_color_view = color_view(); let tgt_color_view = color_view(width, height);
let tgt_color_pp_view = color_view(); 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 = bloom_sizes.map(|size| color_view(size.x, size.y));
let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor { let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor {
label: None, label: None,
@ -717,12 +758,16 @@ impl Renderer {
array_layer_count: None, array_layer_count: None,
}); });
Ok(Views { Ok((
tgt_color: tgt_color_view, Views {
tgt_depth: tgt_depth_view, tgt_color: tgt_color_view,
tgt_color_pp: tgt_color_pp_view, tgt_depth: tgt_depth_view,
_win_depth: win_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. /// Get the resolution of the render target.

View File

@ -4,8 +4,8 @@ use super::{
instances::Instances, instances::Instances,
model::{DynamicModel, Model, SubModel}, model::{DynamicModel, Model, SubModel},
pipelines::{ pipelines::{
blit, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, sprite, blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox,
terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup, sprite, terrain, ui, ColLights, GlobalsBindGroup, ShadowTexturesBindGroup,
}, },
}, },
Renderer, ShadowMap, ShadowMapRenderer, Renderer, ShadowMap, ShadowMapRenderer,
@ -241,6 +241,57 @@ 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
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 encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut run_bloom_pass = |bind, view, label: String, pipeline| {
let mut render_pass =
encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor {
label: Some(&label),
color_attachments: &[wgpu::RenderPassColorAttachment {
resolve_target: None,
view,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
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 = &locals.bloom_binds[index].bind_group;
let view = &views.bloom_tgts[index + 1];
let label = format!("bloom downsample pass {}", index + 1);
run_bloom_pass(bind, view, label, &pipelines.bloom.downsample);
});
// 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 label = format!("bloom upsample pass {}", index + 1);
run_bloom_pass(bind, view, label, &pipelines.bloom.upsample);
});
}
pub fn third_pass(&mut self) -> ThirdPassDrawer { pub fn third_pass(&mut self) -> ThirdPassDrawer {
let encoder = self.encoder.as_mut().unwrap(); let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device; let device = self.borrow.device;

View File

@ -1,7 +1,7 @@
use super::{ use super::{
super::{ super::{
consts::Consts, consts::Consts,
pipelines::{clouds, postprocess}, pipelines::{bloom, clouds, postprocess},
}, },
Layouts, Layouts,
}; };
@ -10,6 +10,8 @@ pub struct Locals {
pub clouds: Consts<clouds::Locals>, pub clouds: Consts<clouds::Locals>,
pub clouds_bind: clouds::BindGroup, pub clouds_bind: clouds::BindGroup,
pub bloom_binds: [bloom::BindGroup; bloom::NUM_SIZES],
pub postprocess: Consts<postprocess::Locals>, pub postprocess: Consts<postprocess::Locals>,
pub postprocess_bind: postprocess::BindGroup, pub postprocess_bind: postprocess::BindGroup,
} }
@ -19,9 +21,12 @@ impl Locals {
device: &wgpu::Device, device: &wgpu::Device,
layouts: &Layouts, layouts: &Layouts,
clouds_locals: Consts<clouds::Locals>, clouds_locals: Consts<clouds::Locals>,
bloom_locals: [Consts<bloom::HalfPixel>; bloom::NUM_SIZES],
postprocess_locals: Consts<postprocess::Locals>, postprocess_locals: Consts<postprocess::Locals>,
tgt_color_view: &wgpu::TextureView, tgt_color_view: &wgpu::TextureView,
tgt_depth_view: &wgpu::TextureView, tgt_depth_view: &wgpu::TextureView,
bloom_src_views: [&wgpu::TextureView; bloom::NUM_SIZES],
bloom_final_tgt_view: &wgpu::TextureView,
tgt_color_pp_view: &wgpu::TextureView, tgt_color_pp_view: &wgpu::TextureView,
sampler: &wgpu::Sampler, sampler: &wgpu::Sampler,
depth_sampler: &wgpu::Sampler, depth_sampler: &wgpu::Sampler,
@ -34,14 +39,22 @@ impl Locals {
depth_sampler, depth_sampler,
&clouds_locals, &clouds_locals,
); );
let postprocess_bind = let bloom_binds = bloom_src_views
layouts .zip(bloom_locals)
.postprocess .map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals));
.bind(device, tgt_color_pp_view, sampler, &postprocess_locals);
let postprocess_bind = layouts.postprocess.bind(
device,
tgt_color_pp_view,
bloom_final_tgt_view,
sampler,
&postprocess_locals,
);
Self { Self {
clouds: clouds_locals, clouds: clouds_locals,
clouds_bind, clouds_bind,
bloom_binds,
postprocess: postprocess_locals, postprocess: postprocess_locals,
postprocess_bind, postprocess_bind,
} }
@ -53,8 +66,11 @@ impl Locals {
layouts: &Layouts, layouts: &Layouts,
// Call when these are recreated and need to be rebound // Call when these are recreated and need to be rebound
// e.g. resizing // e.g. resizing
bloom_locals: [Consts<bloom::HalfPixel>; bloom::NUM_SIZES],
tgt_color_view: &wgpu::TextureView, tgt_color_view: &wgpu::TextureView,
tgt_depth_view: &wgpu::TextureView, tgt_depth_view: &wgpu::TextureView,
bloom_src_views: [&wgpu::TextureView; bloom::NUM_SIZES],
bloom_final_tgt_view: &wgpu::TextureView,
tgt_color_pp_view: &wgpu::TextureView, tgt_color_pp_view: &wgpu::TextureView,
sampler: &wgpu::Sampler, sampler: &wgpu::Sampler,
depth_sampler: &wgpu::Sampler, depth_sampler: &wgpu::Sampler,
@ -67,9 +83,15 @@ impl Locals {
depth_sampler, depth_sampler,
&self.clouds, &self.clouds,
); );
self.postprocess_bind = self.bloom_binds = bloom_src_views
layouts .zip(bloom_locals)
.postprocess .map(|(view, locals)| layouts.bloom.bind(device, view, sampler, locals));
.bind(device, tgt_color_pp_view, sampler, &self.postprocess); self.postprocess_bind = layouts.postprocess.bind(
device,
tgt_color_pp_view,
bloom_final_tgt_view,
sampler,
&self.postprocess,
);
} }
} }

View File

@ -1,8 +1,8 @@
use super::{ use super::{
super::{ super::{
pipelines::{ pipelines::{
blit, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox, blit, bloom, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow,
sprite, terrain, ui, skybox, sprite, terrain, ui,
}, },
AaMode, CloudMode, FluidMode, LightingMode, RenderError, RenderMode, ShadowMode, AaMode, CloudMode, FluidMode, LightingMode, RenderError, RenderMode, ShadowMode,
}, },
@ -20,6 +20,7 @@ pub struct Pipelines {
pub lod_terrain: lod_terrain::LodTerrainPipeline, pub lod_terrain: lod_terrain::LodTerrainPipeline,
pub particle: particle::ParticlePipeline, pub particle: particle::ParticlePipeline,
pub clouds: clouds::CloudsPipeline, pub clouds: clouds::CloudsPipeline,
pub bloom: bloom::BloomPipelines,
pub postprocess: postprocess::PostProcessPipeline, pub postprocess: postprocess::PostProcessPipeline,
// Consider reenabling at some time // Consider reenabling at some time
// player_shadow: figure::FigurePipeline, // player_shadow: figure::FigurePipeline,
@ -39,6 +40,7 @@ pub struct IngamePipelines {
lod_terrain: lod_terrain::LodTerrainPipeline, lod_terrain: lod_terrain::LodTerrainPipeline,
particle: particle::ParticlePipeline, particle: particle::ParticlePipeline,
clouds: clouds::CloudsPipeline, clouds: clouds::CloudsPipeline,
bloom: bloom::BloomPipelines,
postprocess: postprocess::PostProcessPipeline, postprocess: postprocess::PostProcessPipeline,
// Consider reenabling at some time // Consider reenabling at some time
// player_shadow: figure::FigurePipeline, // player_shadow: figure::FigurePipeline,
@ -74,6 +76,7 @@ impl Pipelines {
lod_terrain: ingame.lod_terrain, lod_terrain: ingame.lod_terrain,
particle: ingame.particle, particle: ingame.particle,
clouds: ingame.clouds, clouds: ingame.clouds,
bloom: ingame.bloom,
postprocess: ingame.postprocess, postprocess: ingame.postprocess,
//player_shadow: ingame.player_shadow, //player_shadow: ingame.player_shadow,
skybox: ingame.skybox, skybox: ingame.skybox,
@ -107,6 +110,8 @@ struct ShaderModules {
lod_terrain_frag: wgpu::ShaderModule, lod_terrain_frag: wgpu::ShaderModule,
clouds_vert: wgpu::ShaderModule, clouds_vert: wgpu::ShaderModule,
clouds_frag: wgpu::ShaderModule, clouds_frag: wgpu::ShaderModule,
duel_downsample_frag: wgpu::ShaderModule,
duel_upsample_frag: wgpu::ShaderModule,
postprocess_vert: wgpu::ShaderModule, postprocess_vert: wgpu::ShaderModule,
postprocess_frag: wgpu::ShaderModule, postprocess_frag: wgpu::ShaderModule,
blit_vert: wgpu::ShaderModule, blit_vert: wgpu::ShaderModule,
@ -255,6 +260,8 @@ impl ShaderModules {
lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?, lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?,
clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?, clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?,
clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?, clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?,
duel_downsample_frag: create_shader("duel-downsample-frag", ShaderKind::Fragment)?,
duel_upsample_frag: create_shader("duel-upsample-frag", ShaderKind::Fragment)?,
postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?, postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?,
postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?, postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?,
blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?, blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?,
@ -360,7 +367,7 @@ fn create_interface_pipelines(
fn create_ingame_and_shadow_pipelines( fn create_ingame_and_shadow_pipelines(
needs: PipelineNeeds, needs: PipelineNeeds,
pool: &rayon::ThreadPool, pool: &rayon::ThreadPool,
tasks: [Task; 13], tasks: [Task; 14],
) -> IngameAndShadowPipelines { ) -> IngameAndShadowPipelines {
prof_span!(_guard, "create_ingame_and_shadow_pipelines"); prof_span!(_guard, "create_ingame_and_shadow_pipelines");
@ -382,6 +389,7 @@ fn create_ingame_and_shadow_pipelines(
particle_task, particle_task,
lod_terrain_task, lod_terrain_task,
clouds_task, clouds_task,
bloom_task,
postprocess_task, postprocess_task,
// TODO: if these are ever actually optionally done, counting them // TODO: if these are ever actually optionally done, counting them
// as tasks to do beforehand seems kind of iffy since they will just // as tasks to do beforehand seems kind of iffy since they will just
@ -535,6 +543,22 @@ fn create_ingame_and_shadow_pipelines(
"clouds pipeline creation", "clouds pipeline creation",
) )
}; };
// Pipelines for rendering our bloom
let create_bloom = || {
bloom_task.run(
|| {
bloom::BloomPipelines::new(
device,
&shaders.blit_vert,
&shaders.duel_downsample_frag,
&shaders.duel_upsample_frag,
wgpu::TextureFormat::Rgba16Float,
&layouts.bloom,
)
},
"bloom pipelines creation",
)
};
// Pipeline for rendering our post-processing // Pipeline for rendering our post-processing
let create_postprocess = || { let create_postprocess = || {
postprocess_task.run( postprocess_task.run(
@ -622,7 +646,7 @@ fn create_ingame_and_shadow_pipelines(
}; };
let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure)); 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 j3 = || pool.join(create_sprite, create_particle);
let j4 = || pool.join(create_lod_terrain, create_clouds); let j4 = || pool.join(create_lod_terrain, create_clouds);
let j5 = || pool.join(create_postprocess, create_point_shadow); let j5 = || pool.join(create_postprocess, create_point_shadow);
@ -636,7 +660,7 @@ fn create_ingame_and_shadow_pipelines(
// Ignore this // Ignore this
let ( let (
( (
((debug, (skybox, figure)), (terrain, fluid)), ((debug, (skybox, figure)), (terrain, (fluid, bloom))),
((sprite, particle), (lod_terrain, clouds)), ((sprite, particle), (lod_terrain, clouds)),
), ),
((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)), ((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)),
@ -653,6 +677,7 @@ fn create_ingame_and_shadow_pipelines(
lod_terrain, lod_terrain,
particle, particle,
clouds, clouds,
bloom,
postprocess, postprocess,
skybox, skybox,
sprite, sprite,

View File

@ -1483,6 +1483,12 @@ impl PlayState for SessionState {
second_pass.draw_clouds(); second_pass.draw_clouds();
} }
} }
// Bloom
// TODO: make optional
{
prof_span!("bloom");
drawer.run_bloom_passes()
}
// PostProcess and UI // PostProcess and UI
{ {
prof_span!("post-process and ui"); prof_span!("post-process and ui");