use arrayvec::ArrayVec;
#[allow(unused_imports)]
use rend3::format_sso;
use rend3::{resources::MaterialManager, ModeData, RendererMode};
use wgpu::{
BindGroupLayout, BlendState, ColorTargetState, ColorWrites, CompareFunction, DepthBiasState, DepthStencilState,
Device, Face, FragmentState, FrontFace, MultisampleState, PipelineLayoutDescriptor, PolygonMode, PrimitiveState,
PrimitiveTopology, RenderPipeline, RenderPipelineDescriptor, StencilState, TextureFormat, VertexState,
};
use crate::{
common::{interfaces::ShaderInterfaces, shaders::mode_safe_shader},
material::{PbrMaterial, TransparencyType},
vertex::{cpu_vertex_buffers, gpu_vertex_buffers},
SampleCount,
};
#[derive(Debug, Clone)]
pub enum Baking {
Enabled,
Disabled,
}
#[derive(Clone)]
pub struct BuildForwardPassShaderArgs<'a> {
pub mode: RendererMode,
pub device: &'a Device,
pub interfaces: &'a ShaderInterfaces,
pub directional_light_bgl: &'a BindGroupLayout,
pub texture_bgl: ModeData<(), &'a BindGroupLayout>,
pub materials: &'a MaterialManager,
pub samples: SampleCount,
pub transparency: TransparencyType,
pub baking: Baking,
}
pub fn build_forward_pass_pipeline(args: BuildForwardPassShaderArgs<'_>) -> RenderPipeline {
profiling::scope!(
"build forward pass pipeline",
&format_sso!("{:?}:samp {:?}:bake {:?}", args.transparency, args.samples, args.baking)
);
let forward_pass_vert = unsafe {
mode_safe_shader(
args.device,
args.mode,
"forward pass vert",
match args.baking {
Baking::Disabled => "opaque.vert.cpu.spv",
Baking::Enabled => "opaque-baking.vert.cpu.spv",
},
match args.baking {
Baking::Disabled => "opaque.vert.gpu.spv",
Baking::Enabled => "opaque-baking.vert.gpu.spv",
},
false,
)
};
let forward_pass_frag = unsafe {
mode_safe_shader(
args.device,
args.mode,
"forward pass frag",
"opaque.frag.cpu.spv",
"opaque.frag.gpu.spv",
false,
)
};
let mut bgls: ArrayVec<&BindGroupLayout, 6> = ArrayVec::new();
bgls.push(&args.interfaces.samplers_bgl);
bgls.push(&args.interfaces.culled_object_bgl);
bgls.push(args.directional_light_bgl);
bgls.push(&args.interfaces.uniform_bgl);
bgls.push(args.materials.get_bind_group_layout::<PbrMaterial>());
if args.mode == RendererMode::GPUPowered {
bgls.push(args.texture_bgl.as_gpu())
}
let pll = args.device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: Some("opaque pass"),
bind_group_layouts: &bgls,
push_constant_ranges: &[],
});
build_forward_pass_inner(args, pll, forward_pass_vert, forward_pass_frag)
}
fn build_forward_pass_inner(
args: BuildForwardPassShaderArgs,
pll: wgpu::PipelineLayout,
forward_pass_vert: wgpu::ShaderModule,
forward_pass_frag: wgpu::ShaderModule,
) -> RenderPipeline {
let cpu_vertex_buffers = cpu_vertex_buffers();
let gpu_vertex_buffers = gpu_vertex_buffers();
args.device.create_render_pipeline(&RenderPipelineDescriptor {
label: Some(match args.transparency {
TransparencyType::Opaque => "opaque pass",
TransparencyType::Cutout => "cutout pass",
TransparencyType::Blend => "blend forward pass",
}),
layout: Some(&pll),
vertex: VertexState {
module: &forward_pass_vert,
entry_point: "main",
buffers: match args.mode {
RendererMode::CPUPowered => &cpu_vertex_buffers,
RendererMode::GPUPowered => &gpu_vertex_buffers,
},
},
primitive: PrimitiveState {
topology: PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: FrontFace::Cw,
cull_mode: match args.transparency {
TransparencyType::Opaque | TransparencyType::Cutout => Some(Face::Back),
TransparencyType::Blend => None,
},
clamp_depth: false,
polygon_mode: PolygonMode::Fill,
conservative: false,
},
depth_stencil: match args.baking {
Baking::Enabled => None,
Baking::Disabled => Some(DepthStencilState {
format: TextureFormat::Depth32Float,
depth_write_enabled: matches!(args.transparency, TransparencyType::Opaque | TransparencyType::Cutout),
depth_compare: match args.transparency {
TransparencyType::Opaque | TransparencyType::Cutout => CompareFunction::Equal,
TransparencyType::Blend => CompareFunction::GreaterEqual,
},
stencil: StencilState::default(),
bias: DepthBiasState::default(),
}),
},
multisample: MultisampleState {
count: args.samples as u32,
..Default::default()
},
fragment: Some(FragmentState {
module: &forward_pass_frag,
entry_point: "main",
targets: &[ColorTargetState {
format: TextureFormat::Rgba16Float,
blend: match args.transparency {
TransparencyType::Opaque | TransparencyType::Cutout => None,
TransparencyType::Blend => Some(BlendState::ALPHA_BLENDING),
},
write_mask: ColorWrites::all(),
}],
}),
})
}