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)
uniform texture2D t_src_color;
layout(set = 1, binding = 1)
uniform texture2D t_src_bloom;
layout(set = 1, binding = 2)
uniform sampler s_src_color;
@ -182,6 +184,10 @@ void main() {
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
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

View File

@ -4,6 +4,8 @@
#![deny(clippy::clone_on_ref_ptr)]
#![feature(
array_map,
array_methods,
array_zip,
bool_to_option,
const_generics,
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 bloom;
pub mod clouds;
pub mod debug;
pub mod figure;

View File

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

View File

@ -21,8 +21,8 @@ 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,
@ -49,13 +49,14 @@ const QUAD_INDEX_BUFFER_U32_START_VERT_LEN: u32 = 3000;
struct Layouts {
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,
postprocess: postprocess::PostProcessLayout,
ui: ui::UiLayout,
blit: blit::BlitLayout,
}
@ -67,6 +68,8 @@ struct Views {
tgt_color: wgpu::TextureView,
tgt_depth: wgpu::TextureView,
bloom_tgts: [wgpu::TextureView; bloom::NUM_SIZES],
// TODO: rename
tgt_color_pp: wgpu::TextureView,
}
@ -305,26 +308,28 @@ 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 = postprocess::PostProcessLayout::new(&device);
let ui = ui::UiLayout::new(&device);
let blit = blit::BlitLayout::new(&device);
Layouts {
global,
clouds,
debug,
figure,
postprocess,
shadow,
sprite,
terrain,
clouds,
bloom,
postprocess,
ui,
blit,
}
@ -350,7 +355,8 @@ 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), &mode)?;
let create_sampler = |filter| {
device.create_sampler(&wgpu::SamplerDescriptor {
@ -379,6 +385,8 @@ impl Renderer {
let clouds_locals =
Self::create_consts_inner(&device, &queue, &[clouds::Locals::default()]);
let bloom_locals = bloom_sizes
.map(|size| Self::create_consts_inner(&device, &queue, &[bloom::HalfPixel::new(size)]));
let postprocess_locals =
Self::create_consts_inner(&device, &queue, &[postprocess::Locals::default()]);
@ -386,9 +394,18 @@ impl Renderer {
&device,
&layouts,
clouds_locals,
bloom_locals,
postprocess_locals,
&views.tgt_color,
&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,
&sampler,
&depth_sampler,
@ -546,13 +563,26 @@ 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)?;
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
self.locals.rebind(
&self.device,
&self.layouts,
bloom_locals,
&self.views.tgt_color,
&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.sampler,
&self.depth_sampler,
@ -625,7 +655,7 @@ impl Renderer {
device: &wgpu::Device,
size: (u32, u32),
mode: &RenderMode,
) -> Result<Views, RenderError> {
) -> Result<(Views, [Vec2<f32>; bloom::NUM_SIZES]), RenderError> {
let upscaled = Vec2::<u32>::from(size)
.map(|e| (e as f32 * mode.upscale_mode.factor) as u32)
.into_tuple();
@ -637,7 +667,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 +695,19 @@ 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 = bloom_sizes.map(|size| color_view(size.x, size.y));
let tgt_depth_tex = device.create_texture(&wgpu::TextureDescriptor {
label: None,
@ -717,12 +758,16 @@ impl Renderer {
array_layer_count: None,
});
Ok(Views {
Ok((
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.

View File

@ -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,
@ -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 {
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;

View File

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

View File

@ -1,8 +1,8 @@
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,
},
@ -20,6 +20,7 @@ pub struct Pipelines {
pub lod_terrain: lod_terrain::LodTerrainPipeline,
pub particle: particle::ParticlePipeline,
pub clouds: clouds::CloudsPipeline,
pub bloom: bloom::BloomPipelines,
pub postprocess: postprocess::PostProcessPipeline,
// Consider reenabling at some time
// player_shadow: figure::FigurePipeline,
@ -39,6 +40,7 @@ pub struct IngamePipelines {
lod_terrain: lod_terrain::LodTerrainPipeline,
particle: particle::ParticlePipeline,
clouds: clouds::CloudsPipeline,
bloom: bloom::BloomPipelines,
postprocess: postprocess::PostProcessPipeline,
// Consider reenabling at some time
// player_shadow: figure::FigurePipeline,
@ -74,6 +76,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 +110,8 @@ struct ShaderModules {
lod_terrain_frag: wgpu::ShaderModule,
clouds_vert: wgpu::ShaderModule,
clouds_frag: wgpu::ShaderModule,
duel_downsample_frag: wgpu::ShaderModule,
duel_upsample_frag: wgpu::ShaderModule,
postprocess_vert: wgpu::ShaderModule,
postprocess_frag: wgpu::ShaderModule,
blit_vert: wgpu::ShaderModule,
@ -255,6 +260,8 @@ 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)?,
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_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?,
blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?,
@ -360,7 +367,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");
@ -382,6 +389,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
@ -535,6 +543,22 @@ fn create_ingame_and_shadow_pipelines(
"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
let create_postprocess = || {
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 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 +660,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 +677,7 @@ fn create_ingame_and_shadow_pipelines(
lod_terrain,
particle,
clouds,
bloom,
postprocess,
skybox,
sprite,

View File

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