use crate::{
components::ConfigChanges,
config::{Config, RenderConfig},
};
use crate::forward_renderer::render_passes::{prepass::PrePass, shadow_pass::ShadowPass, upload_pass::UploadPass};
use crate::{camera::Camera, scene::Scene};
use easy_wgpu::{
framebuffer::{FrameBuffer, FrameBufferBuilder},
gpu::Gpu,
texture::{TexParams, Texture},
};
use enum_map::Enum;
use log::debug;
use super::main_pass::MainPass;
pub const ENTITY_ID_PASS_SCALE_FACTOR: u32 = 8;
#[derive(Debug, Enum)]
pub enum OffscreenTarget {
Color, MSAAColor, Depth, EntityID, SingleDepth, }
pub struct RenderData {
pub framebuffer: FrameBuffer<OffscreenTarget>,
}
impl RenderData {
pub fn new(gpu: &Gpu, params: &RenderConfig, surface_format: Option<wgpu::TextureFormat>) -> Self {
let frambuffer_builder = FrameBufferBuilder::<OffscreenTarget>::new(128, 128);
let depth_texture_usage = if cfg!(target_arch = "wasm32") {
wgpu::TextureUsages::RENDER_ATTACHMENT
} else {
wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC
};
let mut offscreen_color_format = wgpu::TextureFormat::Rgba8Unorm;
if params.offscreen_color_float_tex {
offscreen_color_format = wgpu::TextureFormat::Rgba32Float;
}
let framebuffer = frambuffer_builder
.add_render_target(
gpu.device(),
OffscreenTarget::Color,
surface_format.unwrap_or(offscreen_color_format),
wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
TexParams::default(),
)
.add_render_target(
gpu.device(),
OffscreenTarget::MSAAColor,
surface_format.unwrap_or(wgpu::TextureFormat::Rgba8Unorm),
wgpu::TextureUsages::RENDER_ATTACHMENT,
TexParams {
sample_count: params.msaa_nr_samples,
..Default::default()
},
)
.add_render_target(
gpu.device(),
OffscreenTarget::Depth,
wgpu::TextureFormat::Depth32FloatStencil8,
depth_texture_usage,
TexParams {
sample_count: params.msaa_nr_samples,
..Default::default()
},
)
.add_render_target(
gpu.device(),
OffscreenTarget::SingleDepth,
wgpu::TextureFormat::Depth16Unorm,
depth_texture_usage,
TexParams {
scale_factor: ENTITY_ID_PASS_SCALE_FACTOR,
..Default::default()
},
)
.add_render_target(
gpu.device(),
OffscreenTarget::EntityID,
wgpu::TextureFormat::Rgba8Unorm,
wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC | wgpu::TextureUsages::COPY_DST,
TexParams {
scale_factor: ENTITY_ID_PASS_SCALE_FACTOR,
..Default::default()
},
)
.build(gpu.device());
Self { framebuffer }
}
}
pub struct RenderPasses {
pub upload_pass: UploadPass, shadow_pass: ShadowPass, main_pass: MainPass,
}
impl RenderPasses {
pub fn new(gpu: &Gpu, params: &RenderConfig, color_target_format: wgpu::TextureFormat, depth_target_format: wgpu::TextureFormat) -> Self {
let upload_pass = UploadPass::new(gpu, params);
let shadow_pass = ShadowPass::new(gpu);
let main_pass = MainPass::new(gpu, params, color_target_format, depth_target_format);
Self {
upload_pass,
shadow_pass,
main_pass,
}
}
#[allow(clippy::too_many_arguments)]
pub fn run(&mut self, out_view: &wgpu::TextureView, data: &RenderData, gpu: &Gpu, camera: &mut Camera, scene: &mut Scene, config: &mut Config) {
let global_uniforms = self.upload_pass.run(gpu, camera, scene, &config.render);
self.shadow_pass.run(gpu, global_uniforms, scene);
self.main_pass
.run(gpu, global_uniforms, &data.framebuffer, out_view, scene, &config.render);
}
}
pub struct Renderer {
pub data: RenderData,
prepass: PrePass,
pub passes: RenderPasses,
}
impl Renderer {
pub fn new(gpu: &Gpu, params: &RenderConfig, surface_format: Option<wgpu::TextureFormat>) -> Self {
let data = RenderData::new(gpu, params, surface_format);
let prepass = PrePass::new();
let color_target_format = data.framebuffer.get(OffscreenTarget::Color).unwrap().texture.format();
let depth_target_format = data.framebuffer.get(OffscreenTarget::Depth).unwrap().texture.format();
let passes = RenderPasses::new(gpu, params, color_target_format, depth_target_format);
Self { data, prepass, passes }
}
#[allow(clippy::too_many_arguments)]
pub fn render_to_view(
&mut self,
out_view: &wgpu::TextureView,
gpu: &Gpu,
camera: &mut Camera,
scene: &mut Scene,
config: &mut Config,
_dt: core::time::Duration,
) {
self.begin_frame(gpu, camera, scene, config);
self.passes.run(out_view, &self.data, gpu, camera, scene, config);
self.end_frame(scene);
}
#[allow(clippy::too_many_arguments)]
pub fn render_to_texture(
&mut self,
gpu: &Gpu,
camera: &mut Camera,
scene: &mut Scene,
config: &mut Config,
_dt: core::time::Duration,
) {
self.begin_frame(gpu, camera, scene, config);
let out_view = &self.data.framebuffer.get(OffscreenTarget::Color).unwrap().view;
self.passes.run(out_view, &self.data, gpu, camera, scene, config);
self.end_frame(scene);
}
fn prepare_for_rendering(&mut self, gpu: &Gpu, camera: &mut Camera, scene: &mut Scene, config: &mut Config) {
if let Ok(delta) = scene.get_resource::<&ConfigChanges>() {
config.apply_deltas(&delta);
}
self.prepass.run(gpu, camera, scene, config);
}
fn begin_frame(&mut self, gpu: &Gpu, camera: &mut Camera, scene: &mut Scene, config: &mut Config) {
let (width, height) = camera.get_target_res(scene);
self.resize_if_necesary(width, height, gpu);
self.prepare_for_rendering(gpu, camera, scene, config);
}
fn end_frame(&self, scene: &mut Scene) {
scene.world_mut().clear_trackers();
}
pub fn rendered_tex(&self) -> &Texture {
self.data.framebuffer.get(OffscreenTarget::Color).unwrap()
}
pub fn rendered_tex_mut(&mut self) -> &mut Texture {
self.data.framebuffer.get_mut(OffscreenTarget::Color).unwrap()
}
pub fn depth_buffer(&self) -> &Texture {
self.data.framebuffer.get(OffscreenTarget::Depth).unwrap()
}
pub fn entity_id_buffer(&self) -> &Texture {
self.data.framebuffer.get(OffscreenTarget::EntityID).unwrap()
}
fn resize_if_necesary(&mut self, width: u32, height: u32, gpu: &Gpu) {
if self.data.framebuffer.width != width || self.data.framebuffer.height != height {
debug!(
"resizing framebuffer because it is size {}, {}",
self.data.framebuffer.width, self.data.framebuffer.height
);
self.data.framebuffer.resize(gpu.device(), width, height);
}
}
}