extern crate nalgebra as na;
use crate::components::EnvironmentMapGpu;
use crate::components::ShadowCaster;
use crate::components::ShadowMap;
use crate::forward_renderer::render_passes::upload_pass::PerFrameUniforms;
use crate::forward_renderer::renderer::GTarget;
use crate::forward_renderer::renderer::RenderParams;
use crate::light::Light;
use crate::scene::Scene;
use easy_wgpu::bind_group::BindGroupBuilder;
use easy_wgpu::bind_group::BindGroupDesc;
use easy_wgpu::bind_group::BindGroupWrapper;
use easy_wgpu::bind_group_layout::BindGroupLayoutBuilder;
use easy_wgpu::bind_group_layout::BindGroupLayoutDesc;
use easy_wgpu::gbuffer::Gbuffer;
use easy_wgpu::gpu::Gpu;
use easy_wgpu::pipeline::RenderPipelineDescBuilder;
use easy_wgpu::texture::Texture;
use super::upload_pass::MAX_NUM_SHADOWS;
#[allow(clippy::approx_constant)]
#[include_wgsl_oil::include_wgsl_oil("../../../shaders/compose.wgsl")]
mod shader_code {}
pub struct ComposePass {
render_pipeline: wgpu::RenderPipeline,
pub out_texture: Texture,
input_layout: wgpu::BindGroupLayout,
input_bind_group: Option<BindGroupWrapper>,
local_scene: Scene,
passthrough_light: Light,
}
impl ComposePass {
pub fn new(gpu: &Gpu) -> Self {
let input_layout_desc = Self::input_layout_desc();
let input_layout = input_layout_desc
.clone()
.into_bind_group_layout(gpu.device());
let out_texture = Texture::new(
gpu.device(),
4,
4,
wgpu::TextureFormat::Rgba8Unorm,
wgpu::TextureUsages::RENDER_ATTACHMENT
| wgpu::TextureUsages::TEXTURE_BINDING
| wgpu::TextureUsages::COPY_SRC,
);
let render_pipeline = RenderPipelineDescBuilder::new()
.label("compose pipeline")
.shader_code(shader_code::SOURCE)
.shader_label("compose_shader")
.add_bind_group_layout_desc(PerFrameUniforms::layout_desc())
.add_bind_group_layout_desc(input_layout_desc) .add_render_target(wgpu::ColorTargetState {
format: out_texture.texture.format(),
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})
.multisample(wgpu::MultisampleState::default())
.build_pipeline(gpu.device());
let mut local_scene = Scene::new();
let passthrough_tex = Texture::new(
gpu.device(),
4,
4,
wgpu::TextureFormat::Depth32Float,
wgpu::TextureUsages::TEXTURE_BINDING,
);
let passthrough_light = Light::new("compose_pass_passthrough_light", &mut local_scene);
let _ = local_scene.world.insert_one(
passthrough_light.entity,
ShadowMap {
tex_depth: passthrough_tex,
},
);
Self {
render_pipeline,
out_texture,
input_layout,
input_bind_group: None,
local_scene,
passthrough_light,
}
}
pub fn run(
&mut self,
gpu: &Gpu,
per_frame_uniforms: &PerFrameUniforms,
gbuffer: &Gbuffer<GTarget>,
scene: &mut Scene,
render_params: &RenderParams,
) {
self.begin_pass(gpu, gbuffer);
let mut encoder = gpu
.device()
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Compose Encoder"),
});
{
self.update_bind_group(gpu, scene, gbuffer, per_frame_uniforms);
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Compose Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &self.out_texture.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: f64::from(render_params.bg_color.x),
g: f64::from(render_params.bg_color.y),
b: f64::from(render_params.bg_color.z),
a: f64::from(render_params.bg_color.w),
}),
store: true,
},
})],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&self.render_pipeline);
render_pass.set_bind_group(0, &per_frame_uniforms.bind_group, &[]);
render_pass.set_bind_group(1, self.input_bind_group.as_ref().unwrap().bg(), &[]);
render_pass.draw(0..4, 0..1);
}
}
gpu.queue().submit(Some(encoder.finish()));
self.end_pass();
}
fn begin_pass(&mut self, gpu: &Gpu, gbuffer: &Gbuffer<GTarget>) {
if gbuffer.width != self.out_texture.width() || gbuffer.height != self.out_texture.height()
{
self.out_texture
.resize(gpu.device(), gbuffer.width, gbuffer.height);
}
}
fn end_pass(&mut self) {}
fn input_layout_desc() -> BindGroupLayoutDesc {
BindGroupLayoutBuilder::new()
.label("compose_input_layout")
.add_entry_tex(
wgpu::ShaderStages::FRAGMENT,
wgpu::TextureSampleType::Float { filterable: true },
)
.add_entry_tex(
wgpu::ShaderStages::FRAGMENT,
wgpu::TextureSampleType::Float { filterable: false },
)
.add_entry_tex(
wgpu::ShaderStages::FRAGMENT,
wgpu::TextureSampleType::Float { filterable: false },
)
.add_entry_tex(
wgpu::ShaderStages::FRAGMENT,
wgpu::TextureSampleType::Float { filterable: false },
)
.add_entry_tex(
wgpu::ShaderStages::FRAGMENT,
wgpu::TextureSampleType::Float { filterable: false },
)
.add_entry_cubemap(
wgpu::ShaderStages::FRAGMENT,
wgpu::TextureSampleType::Float { filterable: true },
)
.add_entry_cubemap(
wgpu::ShaderStages::FRAGMENT,
wgpu::TextureSampleType::Float { filterable: true },
)
.add_entries_tex(
wgpu::ShaderStages::FRAGMENT,
wgpu::TextureSampleType::Depth, MAX_NUM_SHADOWS,
)
.build()
}
fn update_bind_group(
&mut self,
gpu: &Gpu,
scene: &Scene,
gbuffer: &Gbuffer<GTarget>,
per_frame_uniforms: &PerFrameUniforms,
) {
let env_map = scene.get_resource::<&EnvironmentMapGpu>().unwrap();
let mut shadow_maps = Vec::new();
for i in 0..MAX_NUM_SHADOWS {
let is_within_valid_lights: bool = i < per_frame_uniforms.idx_ubo2light.len();
if is_within_valid_lights
&& scene
.world
.has::<ShadowCaster>(per_frame_uniforms.idx_ubo2light[i])
.unwrap()
{
let entity = per_frame_uniforms.idx_ubo2light[i];
let shadow: gloss_hecs::Ref<'_, ShadowMap> = scene.get_comp::<&ShadowMap>(&entity);
shadow_maps.push(shadow);
} else {
let shadow: gloss_hecs::Ref<'_, ShadowMap> = self
.local_scene
.get_comp::<&ShadowMap>(&self.passthrough_light.entity);
shadow_maps.push(shadow);
}
}
let entries = BindGroupBuilder::new()
.add_entry_tex(gbuffer.get(GTarget::Albedo).unwrap())
.add_entry_tex(gbuffer.get(GTarget::Position).unwrap())
.add_entry_tex(gbuffer.get(GTarget::Normal).unwrap())
.add_entry_tex(gbuffer.get(GTarget::MetalnessRoughness).unwrap())
.add_entry_tex(gbuffer.get(GTarget::Depth).unwrap())
.add_entry_tex(&env_map.diffuse_tex)
.add_entry_tex(&env_map.specular_tex)
.add_entry_tex(&shadow_maps[0].tex_depth)
.add_entry_tex(&shadow_maps[1].tex_depth)
.add_entry_tex(&shadow_maps[2].tex_depth)
.add_entry_tex(&shadow_maps[3].tex_depth)
.add_entry_tex(&shadow_maps[4].tex_depth)
.add_entry_tex(&shadow_maps[5].tex_depth)
.add_entry_tex(&shadow_maps[6].tex_depth)
.add_entry_tex(&shadow_maps[7].tex_depth)
.build_entries();
let stale = self
.input_bind_group
.as_ref()
.map_or(true, |b| b.is_stale(&entries)); if stale {
debug!("compose input bind group is stale, recreating");
self.input_bind_group = Some(
BindGroupDesc::new("compose_input_bg", entries)
.into_bind_group_wrapper(gpu.device(), &self.input_layout),
);
}
}
}