render: change to manual creation of render graph exeuction path, rewrite light cull compute pass into the render graph
This commit is contained in:
parent
fc57777a45
commit
9a48075f07
|
@ -959,6 +959,12 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
|
@ -1865,6 +1871,7 @@ dependencies = [
|
|||
"lyra-reflect",
|
||||
"lyra-resource",
|
||||
"lyra-scene",
|
||||
"petgraph",
|
||||
"quote",
|
||||
"rustc-hash",
|
||||
"syn 2.0.51",
|
||||
|
@ -2507,6 +2514,16 @@ version = "2.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap 2.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.13"
|
||||
|
|
|
@ -35,6 +35,7 @@ itertools = "0.11.0"
|
|||
thiserror = "1.0.56"
|
||||
unique = "0.9.1"
|
||||
rustc-hash = "1.1.0"
|
||||
petgraph = { version = "0.6.5", features = ["matrix_graph"] }
|
||||
|
||||
[features]
|
||||
tracy = ["dep:tracing-tracy"]
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use itertools::Itertools;
|
||||
use lyra_ecs::World;
|
||||
pub use pass::*;
|
||||
|
||||
|
@ -19,15 +20,18 @@ mod execution_path;
|
|||
|
||||
use rustc_hash::FxHashMap;
|
||||
use tracing::{debug_span, instrument, trace, warn};
|
||||
use wgpu::ComputePass;
|
||||
|
||||
use self::execution_path::GraphExecutionPath;
|
||||
|
||||
use super::resource::{Pipeline, RenderPipeline};
|
||||
use super::resource::{ComputePipeline, Pipeline, RenderPipeline};
|
||||
|
||||
//#[derive(Clone)]
|
||||
struct PassEntry {
|
||||
inner: Arc<RefCell<dyn RenderGraphPass>>,
|
||||
desc: Arc<RenderGraphPassDesc>,
|
||||
/// The index of the pass in the execution graph
|
||||
graph_index: petgraph::matrix_graph::NodeIndex<usize>,
|
||||
}
|
||||
|
||||
pub struct BindGroupEntry {
|
||||
|
@ -73,10 +77,10 @@ pub struct RenderGraph {
|
|||
bind_group_names: FxHashMap<String, u64>,
|
||||
// TODO: make pipelines a `type` parameter in RenderPasses,
|
||||
// then the pipelines can be retrieved via TypeId to the pass.
|
||||
///
|
||||
pipelines: FxHashMap<u64, PipelineResource>,
|
||||
current_id: u64,
|
||||
exec_path: Option<GraphExecutionPath>,
|
||||
new_path: petgraph::matrix_graph::DiMatrix<u64, (), Option<()>, usize>,
|
||||
}
|
||||
|
||||
impl RenderGraph {
|
||||
|
@ -92,6 +96,7 @@ impl RenderGraph {
|
|||
pipelines: Default::default(),
|
||||
current_id: 1,
|
||||
exec_path: None,
|
||||
new_path: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,11 +163,14 @@ impl RenderGraph {
|
|||
self.bind_group_names.insert(name.clone(), bg_id);
|
||||
}
|
||||
|
||||
let index = self.new_path.add_node(desc.id);
|
||||
|
||||
self.passes.insert(
|
||||
desc.id,
|
||||
PassEntry {
|
||||
inner: Arc::new(RefCell::new(pass)),
|
||||
desc: Arc::new(desc),
|
||||
graph_index: index,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -172,12 +180,19 @@ impl RenderGraph {
|
|||
pub fn setup(&mut self, device: &wgpu::Device) {
|
||||
// For all passes, create their pipelines
|
||||
for pass in self.passes.values() {
|
||||
if let Some(pipei) = &pass.desc.pipeline_desc {
|
||||
if let Some(pipeline_desc) = &pass.desc.pipeline_desc {
|
||||
let pipeline = match pass.desc.pass_type {
|
||||
RenderPassType::Render => {
|
||||
Pipeline::Render(RenderPipeline::create(device, pipei))
|
||||
Pipeline::Render(RenderPipeline::create(device, pipeline_desc.as_render_pipeline_descriptor()
|
||||
.expect("got compute pipeline descriptor in a render pass")))
|
||||
},
|
||||
RenderPassType::Compute => {
|
||||
Pipeline::Compute(ComputePipeline::create(device, pipeline_desc.as_compute_pipeline_descriptor()
|
||||
.expect("got render pipeline descriptor in a compute pass")))
|
||||
},
|
||||
RenderPassType::Presenter | RenderPassType::Node => {
|
||||
panic!("Present or Node RenderGraph passes should not have a pipeline descriptor!");
|
||||
}
|
||||
_ => todo!(),
|
||||
};
|
||||
|
||||
let res = PipelineResource {
|
||||
|
@ -233,25 +248,30 @@ impl RenderGraph {
|
|||
|
||||
#[instrument(skip(self))]
|
||||
pub fn render(&mut self) {
|
||||
let mut path = self.exec_path.take().unwrap();
|
||||
let mut sorted: VecDeque<u64> = petgraph::algo::toposort(&self.new_path, None)
|
||||
.expect("RenderGraph had cycled!")
|
||||
.iter().map(|i| self.new_path[i.clone()])
|
||||
.collect();
|
||||
let path_names = sorted.iter().map(|i| self.pass(*i).unwrap().name.clone()).collect_vec();
|
||||
trace!("Render graph execution order: {:?}", path_names);
|
||||
|
||||
let mut encoders = Vec::with_capacity(self.passes.len() / 2);
|
||||
while let Some(pass_id) = path.queue.pop_front() {
|
||||
while let Some(pass_id) = sorted.pop_front() {
|
||||
let pass = self.passes.get(&pass_id).unwrap();
|
||||
let pass_inn = pass.inner.clone();
|
||||
let pass_desc = pass.desc.clone();
|
||||
let label = format!("{} Encoder", pass_desc.name);
|
||||
|
||||
// encoders are not needed for presenter nodes.
|
||||
let encoder = if pass_desc.pass_type == RenderPassType::Presenter {
|
||||
None
|
||||
} else {
|
||||
let encoder = if pass_desc.pass_type.should_have_pipeline() {
|
||||
Some(
|
||||
self.device
|
||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some(&label),
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let queue = self.queue.clone(); // clone is required to appease the borrow checker
|
||||
|
@ -259,9 +279,11 @@ impl RenderGraph {
|
|||
|
||||
// all encoders need to be submitted before a presenter node is executed.
|
||||
if pass_desc.pass_type == RenderPassType::Presenter {
|
||||
trace!("Submitting {} encoderd before presenting", encoders.len());
|
||||
self.queue.submit(encoders.drain(..));
|
||||
}
|
||||
|
||||
trace!("Executing {}", pass_desc.name);
|
||||
let mut inner = pass_inn.borrow_mut();
|
||||
inner.execute(self, &*pass_desc, &mut context);
|
||||
|
||||
|
@ -320,6 +342,16 @@ impl RenderGraph {
|
|||
pub fn bind_group_id(&self, name: &str) -> Option<u64> {
|
||||
self.bind_group_names.get(name).copied()
|
||||
}
|
||||
|
||||
pub fn add_edge(&mut self, from: &str, to: &str) {
|
||||
let from_idx = self.passes.iter().find(|p| p.1.desc.name == from).map(|p| p.1.graph_index)
|
||||
.expect("Failed to find from pass");
|
||||
let to_idx = self.passes.iter().find(|p| p.1.desc.name == to).map(|p| p.1.graph_index)
|
||||
.expect("Failed to find to pass");
|
||||
|
||||
self.new_path.add_edge(from_idx, to_idx, ());
|
||||
//self.new_path.add_edge(NodeIndex::new(from_id as usize), NodeIndex::new(to_id as usize), ());
|
||||
}
|
||||
}
|
||||
|
||||
/// A queued write to a GPU buffer targeting a graph slot.
|
||||
|
@ -396,4 +428,21 @@ impl<'a> RenderGraphContext<'a> {
|
|||
) {
|
||||
self.queue_buffer_write(target_slot, offset, bytemuck::bytes_of(&bytes));
|
||||
}
|
||||
|
||||
pub fn get_bind_groups<'b>(&self, graph: &'b RenderGraph, bind_group_names: &[&str]) -> Vec<Option<&'b Rc<wgpu::BindGroup>>> {
|
||||
bind_group_names
|
||||
.iter()
|
||||
.map(|name| graph.bind_group_id(name))
|
||||
.map(|bgi| bgi.map(|bgi| graph.bind_group(bgi)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn set_bind_groups<'b, 'c>(graph: &'b RenderGraph, pass: &'c mut ComputePass<'b>, bind_groups: &[(&str, u32)]) {
|
||||
for (name, index) in bind_groups {
|
||||
let bg = graph.bind_group_id(name).map(|bgi| graph.bind_group(bgi))
|
||||
.expect(&format!("Could not find bind group '{}'", name));
|
||||
|
||||
pass.set_bind_group(*index, bg, &[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,18 +2,31 @@ use std::{cell::{Ref, RefCell, RefMut}, collections::HashMap, num::NonZeroU32, r
|
|||
|
||||
use lyra_ecs::World;
|
||||
|
||||
use crate::render::resource::RenderPipelineDescriptor;
|
||||
use crate::render::resource::PipelineDescriptor;
|
||||
|
||||
use super::{RenderGraph, RenderGraphContext, RenderTarget};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub enum RenderPassType {
|
||||
/// A node doesn't render, compute, or present anything. This likely means it injects data into the graph.
|
||||
Node,
|
||||
Compute,
|
||||
#[default]
|
||||
Render,
|
||||
Presenter,
|
||||
}
|
||||
|
||||
impl RenderPassType {
|
||||
pub fn should_have_pipeline(&self) -> bool {
|
||||
match self {
|
||||
RenderPassType::Node => false,
|
||||
RenderPassType::Compute => true,
|
||||
RenderPassType::Render => true,
|
||||
RenderPassType::Presenter => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum SlotType {
|
||||
TextureView,
|
||||
|
@ -150,7 +163,7 @@ pub struct RenderGraphPassDesc {
|
|||
pub pass_type: RenderPassType,
|
||||
pub slots: Vec<RenderPassSlot>,
|
||||
slot_names: HashMap<String, u64>,
|
||||
pub pipeline_desc: Option<RenderPipelineDescriptor>,
|
||||
pub pipeline_desc: Option<PipelineDescriptor>,
|
||||
pub bind_groups: Vec<(
|
||||
String,
|
||||
Rc<wgpu::BindGroup>,
|
||||
|
@ -163,7 +176,7 @@ impl RenderGraphPassDesc {
|
|||
id: u64,
|
||||
name: &str,
|
||||
pass_type: RenderPassType,
|
||||
pipeline_desc: Option<RenderPipelineDescriptor>,
|
||||
pipeline_desc: Option<PipelineDescriptor>,
|
||||
bind_groups: Vec<(&str, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -1,68 +1,114 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::render::graph::{RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot, RenderPassType, RenderTarget, SlotAttribute, SlotType, SlotValue};
|
||||
use glam::UVec2;
|
||||
use tracing::warn;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use crate::{
|
||||
render::{
|
||||
camera::{CameraUniform, RenderCamera},
|
||||
graph::{
|
||||
RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassSlot,
|
||||
RenderPassType, RenderTarget, SlotAttribute, SlotType, SlotValue,
|
||||
},
|
||||
render_buffer::BufferWrapper, texture::RenderTexture,
|
||||
},
|
||||
scene::CameraComponent,
|
||||
};
|
||||
|
||||
/// Supplies some basic things other passes needs.
|
||||
///
|
||||
///
|
||||
/// screen size buffer, camera buffer,
|
||||
#[derive(Default)]
|
||||
pub struct BasePass {
|
||||
/// Temporary storage for the main render target
|
||||
///
|
||||
///
|
||||
/// This should be Some when the pass is first created then after its added to
|
||||
/// the render graph it will be None and stay None.
|
||||
temp_render_target: Option<RenderTarget>,
|
||||
main_rt_id: u64,
|
||||
window_tv_id: u64,
|
||||
screen_size: glam::UVec2,
|
||||
}
|
||||
|
||||
impl BasePass {
|
||||
pub fn new(surface: wgpu::Surface, surface_config: wgpu::SurfaceConfiguration) -> Self {
|
||||
let size = glam::UVec2::new(surface_config.width, surface_config.height);
|
||||
|
||||
Self {
|
||||
temp_render_target: Some(RenderTarget {
|
||||
surface,
|
||||
surface_config,
|
||||
current_texture: None,
|
||||
}),
|
||||
main_rt_id: 0,
|
||||
window_tv_id: 0,
|
||||
screen_size: size,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderGraphPass for BasePass {
|
||||
fn desc(&mut self, graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::RenderGraphPassDesc {
|
||||
fn desc(
|
||||
&mut self,
|
||||
graph: &mut crate::render::graph::RenderGraph,
|
||||
) -> crate::render::graph::RenderGraphPassDesc {
|
||||
let render_target = self.temp_render_target.take().unwrap();
|
||||
self.screen_size = UVec2::new(
|
||||
render_target.surface_config.width,
|
||||
render_target.surface_config.height,
|
||||
);
|
||||
|
||||
let (screen_size_bgl, screen_size_bg, screen_size_buf, _) = BufferWrapper::builder()
|
||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||
.label_prefix("ScreenSize")
|
||||
.visibility(wgpu::ShaderStages::COMPUTE)
|
||||
.buffer_dynamic_offset(false)
|
||||
.contents(&[self.screen_size])
|
||||
.finish_parts(&graph.device());
|
||||
let screen_size_bgl = Rc::new(screen_size_bgl);
|
||||
let screen_size_bg = Rc::new(screen_size_bg);
|
||||
|
||||
let (camera_bgl, camera_bg, camera_buf, _) = BufferWrapper::builder()
|
||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||
.label_prefix("camera")
|
||||
.visibility(wgpu::ShaderStages::all())
|
||||
.buffer_dynamic_offset(false)
|
||||
.contents(&[CameraUniform::default()])
|
||||
.finish_parts(&graph.device());
|
||||
let camera_bgl = Rc::new(camera_bgl);
|
||||
let camera_bg = Rc::new(camera_bg);
|
||||
|
||||
// create the depth texture using the utility struct, then take all the required fields
|
||||
let mut depth_texture = RenderTexture::create_depth_texture(&graph.device(), &render_target.surface_config, "depth_texture");
|
||||
depth_texture.create_bind_group(&graph.device);
|
||||
|
||||
let dt_bg_pair = depth_texture.bindgroup_pair.unwrap();
|
||||
let depth_texture_bg = Rc::new(dt_bg_pair.bindgroup);
|
||||
let depth_texture_bgl = dt_bg_pair.layout;
|
||||
let depth_texture_view = Rc::new(depth_texture.view);
|
||||
|
||||
let mut desc = RenderGraphPassDesc::new(
|
||||
graph.next_id(),
|
||||
"base",
|
||||
RenderPassType::Render,
|
||||
None,
|
||||
vec![],
|
||||
vec![
|
||||
("depth_texture", depth_texture_bg, Some(depth_texture_bgl)),
|
||||
("screen_size", screen_size_bg, Some(screen_size_bgl)),
|
||||
("camera", camera_bg, Some(camera_bgl)),
|
||||
],
|
||||
);
|
||||
|
||||
/* desc.add_buffer_slot(*id, "screen_size_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor {
|
||||
label: Some("B_ScreenSize".to_string()),
|
||||
contents: bytemuck::bytes_of(&UVec2::new(800, 600)).to_vec(),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
})));
|
||||
desc.add_buffer_slot(*id, "camera_buffer", SlotAttribute::Output, Some(SlotDescriptor::BufferInit(BufferInitDescriptor {
|
||||
label: Some("B_Camera".to_string()),
|
||||
contents: bytemuck::bytes_of(&CameraUniform::default()).to_vec(),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
})));
|
||||
*id += 1; */
|
||||
|
||||
self.main_rt_id = graph.next_id();
|
||||
let render_target = self.temp_render_target.take().unwrap();
|
||||
desc.add_slot(
|
||||
RenderPassSlot {
|
||||
ty: SlotType::RenderTarget,
|
||||
attribute: SlotAttribute::Output,
|
||||
id: self.main_rt_id,
|
||||
name: "main_render_target".into(),
|
||||
value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new(render_target)))),
|
||||
}
|
||||
);
|
||||
desc.add_slot(RenderPassSlot {
|
||||
ty: SlotType::RenderTarget,
|
||||
attribute: SlotAttribute::Output,
|
||||
id: self.main_rt_id,
|
||||
name: "main_render_target".into(),
|
||||
value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new(
|
||||
render_target,
|
||||
)))),
|
||||
});
|
||||
self.window_tv_id = graph.next_id();
|
||||
desc.add_texture_view_slot(
|
||||
self.window_tv_id,
|
||||
|
@ -70,31 +116,75 @@ impl RenderGraphPass for BasePass {
|
|||
SlotAttribute::Output,
|
||||
Some(SlotValue::Lazy),
|
||||
);
|
||||
desc.add_texture_view_slot(
|
||||
self.window_tv_id,
|
||||
"depth_texture_view",
|
||||
SlotAttribute::Output,
|
||||
Some(SlotValue::TextureView(depth_texture_view)),
|
||||
);
|
||||
desc.add_buffer_slot(
|
||||
graph.next_id(),
|
||||
"screen_size_buffer",
|
||||
SlotAttribute::Output,
|
||||
Some(SlotValue::Buffer(Rc::new(screen_size_buf))),
|
||||
);
|
||||
desc.add_buffer_slot(
|
||||
graph.next_id(),
|
||||
"camera_buffer",
|
||||
SlotAttribute::Output,
|
||||
Some(SlotValue::Buffer(Rc::new(camera_buf))),
|
||||
);
|
||||
|
||||
desc
|
||||
}
|
||||
|
||||
fn prepare(&mut self, _world: &mut lyra_ecs::World, _context: &mut RenderGraphContext) {
|
||||
|
||||
fn prepare(&mut self, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
|
||||
if let Some(camera) = world.view_iter::<&mut CameraComponent>().next() {
|
||||
let mut render_cam =
|
||||
RenderCamera::new(PhysicalSize::new(self.screen_size.x, self.screen_size.y));
|
||||
let uniform = render_cam.calc_view_projection(&camera);
|
||||
|
||||
context.queue_buffer_write_with("camera_buffer", 0, uniform)
|
||||
} else {
|
||||
warn!("Missing camera!");
|
||||
}
|
||||
}
|
||||
|
||||
fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::RenderGraphPassDesc, _context: &mut crate::render::graph::RenderGraphContext) {
|
||||
let tv_slot = graph.slot_value_mut(self.main_rt_id)
|
||||
fn execute(
|
||||
&mut self,
|
||||
graph: &mut crate::render::graph::RenderGraph,
|
||||
_desc: &crate::render::graph::RenderGraphPassDesc,
|
||||
context: &mut crate::render::graph::RenderGraphContext,
|
||||
) {
|
||||
let tv_slot = graph
|
||||
.slot_value_mut(self.main_rt_id)
|
||||
.expect("somehow the main render target slot is missing");
|
||||
let mut rt = tv_slot.as_render_target_mut().unwrap();
|
||||
debug_assert!(!rt.current_texture.is_some(), "main render target surface was not presented!");
|
||||
|
||||
debug_assert!(
|
||||
!rt.current_texture.is_some(),
|
||||
"main render target surface was not presented!"
|
||||
);
|
||||
|
||||
// update the screen size buffer if the size changed.
|
||||
if rt.surface_config.width != self.screen_size.x
|
||||
|| rt.surface_config.height != self.screen_size.y
|
||||
{
|
||||
self.screen_size = UVec2::new(rt.surface_config.width, rt.surface_config.height);
|
||||
context.queue_buffer_write_with("screen_size_buffer", 0, self.screen_size)
|
||||
}
|
||||
|
||||
let surface_tex = rt.surface.get_current_texture().unwrap();
|
||||
let view = surface_tex.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
let view = surface_tex
|
||||
.texture
|
||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
|
||||
rt.current_texture = Some(surface_tex);
|
||||
drop(rt); // must be manually dropped for borrow checker when getting texture view slot
|
||||
|
||||
// store the surface texture to the slot
|
||||
let tv_slot = graph.slot_value_mut(self.window_tv_id)
|
||||
let tv_slot = graph
|
||||
.slot_value_mut(self.window_tv_id)
|
||||
.expect("somehow the window texture view slot is missing");
|
||||
*tv_slot = SlotValue::TextureView(Rc::new(view));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
use crate::render::{
|
||||
graph::{
|
||||
RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SlotAttribute,
|
||||
SlotValue,
|
||||
},
|
||||
light::LightUniformBuffers,
|
||||
};
|
||||
|
||||
/// Supplies some basic things other passes needs.
|
||||
///
|
||||
/// screen size buffer, camera buffer,
|
||||
#[derive(Default)]
|
||||
pub struct LightBasePass {
|
||||
light_buffers: Option<LightUniformBuffers>,
|
||||
}
|
||||
|
||||
impl LightBasePass {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderGraphPass for LightBasePass {
|
||||
fn desc(
|
||||
&mut self,
|
||||
graph: &mut crate::render::graph::RenderGraph,
|
||||
) -> crate::render::graph::RenderGraphPassDesc {
|
||||
let device = &graph.device;
|
||||
self.light_buffers = Some(LightUniformBuffers::new(device));
|
||||
let light_buffers = self.light_buffers.as_ref().unwrap();
|
||||
|
||||
let mut desc = RenderGraphPassDesc::new(
|
||||
graph.next_id(),
|
||||
"light_base",
|
||||
RenderPassType::Node,
|
||||
None,
|
||||
vec![(
|
||||
"light_buffers",
|
||||
light_buffers.bind_group.clone(),
|
||||
Some(light_buffers.bind_group_layout.clone()),
|
||||
)],
|
||||
);
|
||||
|
||||
desc.add_buffer_slot(
|
||||
graph.next_id(),
|
||||
"light_buffers",
|
||||
SlotAttribute::Output,
|
||||
Some(SlotValue::Buffer(light_buffers.buffer.clone())),
|
||||
);
|
||||
|
||||
desc
|
||||
}
|
||||
|
||||
fn prepare(&mut self, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
|
||||
let tick = world.current_tick();
|
||||
let lights = self.light_buffers.as_mut().unwrap();
|
||||
lights.update_lights(context.queue, tick, world);
|
||||
}
|
||||
|
||||
fn execute(
|
||||
&mut self,
|
||||
_graph: &mut crate::render::graph::RenderGraph,
|
||||
_desc: &crate::render::graph::RenderGraphPassDesc,
|
||||
_context: &mut crate::render::graph::RenderGraphContext,
|
||||
) {
|
||||
}
|
||||
}
|
|
@ -1,10 +1,14 @@
|
|||
use std::mem;
|
||||
use std::{mem, rc::Rc};
|
||||
|
||||
use lyra_ecs::World;
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
use crate::render::graph::{
|
||||
BufferInitDescriptor, RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType,
|
||||
SlotAttribute, SlotDescriptor, TextureDescriptor, TextureViewDescriptor,
|
||||
use crate::render::{
|
||||
graph::{
|
||||
RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SlotAttribute,
|
||||
SlotValue,
|
||||
},
|
||||
resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader},
|
||||
};
|
||||
|
||||
pub struct LightCullComputePass {
|
||||
|
@ -21,124 +25,228 @@ impl LightCullComputePass {
|
|||
|
||||
impl RenderGraphPass for LightCullComputePass {
|
||||
fn desc(
|
||||
&self,
|
||||
&mut self,
|
||||
graph: &mut crate::render::graph::RenderGraph,
|
||||
id: &mut u64,
|
||||
) -> crate::render::graph::RenderGraphPassDesc {
|
||||
let mut desc = RenderGraphPassDesc::new(*id, "LightCullCompute", RenderPassType::Compute);
|
||||
*id += 1;
|
||||
let shader = Rc::new(Shader {
|
||||
label: Some("light_cull_comp_shader".into()),
|
||||
source: include_str!("../../shaders/light_cull.comp.wgsl").to_string(),
|
||||
});
|
||||
|
||||
desc.add_buffer_slot(*id, "screen_size_buffer", SlotAttribute::Input, None);
|
||||
*id += 1;
|
||||
desc.add_buffer_slot(*id, "camera_buffer", SlotAttribute::Input, None);
|
||||
*id += 1;
|
||||
// get the size of the work group for the grid
|
||||
let main_rt = graph
|
||||
.slot_id("main_render_target")
|
||||
.and_then(|s| graph.slot_value(s))
|
||||
.and_then(|s| s.as_render_target())
|
||||
.expect("missing main render target");
|
||||
self.workgroup_size =
|
||||
glam::UVec2::new(main_rt.surface_config.width, main_rt.surface_config.height);
|
||||
|
||||
// initialize some buffers with empty data
|
||||
let mut contents = Vec::<u8>::new();
|
||||
let contents_len =
|
||||
self.workgroup_size.x * self.workgroup_size.y * 200 * mem::size_of::<u32>() as u32;
|
||||
self.workgroup_size.x * self.workgroup_size.y * mem::size_of::<u32>() as u32;
|
||||
contents.resize(contents_len as _, 0);
|
||||
desc.add_buffer_slot(
|
||||
*id,
|
||||
"light_indices",
|
||||
SlotAttribute::Output,
|
||||
Some(SlotDescriptor::BufferInit(BufferInitDescriptor {
|
||||
label: Some("B_LightIndices".to_string()),
|
||||
contents,
|
||||
|
||||
let device = graph.device();
|
||||
let light_indices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("light_indices_buffer"),
|
||||
contents: &contents,
|
||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
|
||||
let light_index_counter_buffer =
|
||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("light_index_counter_buffer"),
|
||||
contents: &bytemuck::cast_slice(&[0]),
|
||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
})),
|
||||
);
|
||||
*id += 1;
|
||||
});
|
||||
|
||||
let light_indices_bg_layout = Rc::new(device.create_bind_group_layout(
|
||||
&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Storage { read_only: false },
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::COMPUTE | wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::StorageTexture {
|
||||
access: wgpu::StorageTextureAccess::ReadWrite,
|
||||
format: wgpu::TextureFormat::Rg32Uint, // vec2<uint>
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 2,
|
||||
visibility: wgpu::ShaderStages::COMPUTE,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Storage { read_only: false },
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: None,
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: Some("light_indices_grid_bgl"),
|
||||
},
|
||||
));
|
||||
|
||||
let size = wgpu::Extent3d {
|
||||
width: self.workgroup_size.x,
|
||||
height: self.workgroup_size.y,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
desc.add_texture_slot(
|
||||
*id,
|
||||
"lightgrid_texture",
|
||||
SlotAttribute::Output,
|
||||
Some(SlotDescriptor::Texture(TextureDescriptor {
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rg32Uint, // vec2<uint>
|
||||
usage: wgpu::TextureUsages::STORAGE_BINDING,
|
||||
view_formats: vec![],
|
||||
let grid_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("light_grid_tex"),
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rg32Uint, // vec2<uint>
|
||||
usage: wgpu::TextureUsages::STORAGE_BINDING,
|
||||
view_formats: &[],
|
||||
});
|
||||
|
||||
let grid_texture_view = grid_texture.create_view(&wgpu::TextureViewDescriptor {
|
||||
label: Some("light_grid_texview"),
|
||||
format: Some(wgpu::TextureFormat::Rg32Uint), // vec2<uint>
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
base_mip_level: 0,
|
||||
mip_level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
});
|
||||
|
||||
let light_indices_bg = Rc::new(device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &light_indices_bg_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||
buffer: &light_indices_buffer,
|
||||
offset: 0,
|
||||
size: None, // the entire light buffer is needed
|
||||
}),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&grid_texture_view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||
buffer: &light_index_counter_buffer,
|
||||
offset: 0,
|
||||
size: None, // the entire light buffer is needed
|
||||
}),
|
||||
},
|
||||
],
|
||||
label: Some("light_indices_grid_bind_group"),
|
||||
}));
|
||||
|
||||
drop(main_rt);
|
||||
let pass_id = graph.next_id();
|
||||
|
||||
let depth_tex_bgl = graph.bind_group_layout(graph.bind_group_id("depth_texture").unwrap());
|
||||
let camera_bgl = graph.bind_group_layout(graph.bind_group_id("camera").unwrap());
|
||||
let lights_bgl = graph.bind_group_layout(graph.bind_group_id("light_buffers").unwrap());
|
||||
let screen_size_bgl = graph.bind_group_layout(graph.bind_group_id("screen_size").unwrap());
|
||||
|
||||
let mut desc = RenderGraphPassDesc::new(
|
||||
pass_id,
|
||||
"light_cull_compute",
|
||||
RenderPassType::Compute,
|
||||
Some(PipelineDescriptor::Compute(ComputePipelineDescriptor {
|
||||
label: Some("light_cull_pipeline".into()),
|
||||
push_constant_ranges: vec![],
|
||||
layouts: vec![
|
||||
depth_tex_bgl.clone(),
|
||||
camera_bgl.clone(),
|
||||
lights_bgl.clone(),
|
||||
light_indices_bg_layout.clone(),
|
||||
screen_size_bgl.clone(),
|
||||
],
|
||||
shader,
|
||||
shader_entry_point: "cs_main".into(),
|
||||
})),
|
||||
vec![(
|
||||
"light_indices_grid",
|
||||
light_indices_bg,
|
||||
Some(light_indices_bg_layout),
|
||||
)],
|
||||
);
|
||||
*id += 1;
|
||||
|
||||
desc.add_texture_view_slot(
|
||||
*id,
|
||||
"lightgrid_texture_view",
|
||||
SlotAttribute::Output,
|
||||
Some(SlotDescriptor::TextureView(TextureViewDescriptor {
|
||||
texture_label: "lightgrid_texture".to_string(),
|
||||
format: Some(wgpu::TextureFormat::Rg32Uint), // vec2<uint>
|
||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
base_mip_level: 0,
|
||||
mip_level_count: None,
|
||||
base_array_layer: 0,
|
||||
array_layer_count: None,
|
||||
})),
|
||||
graph.next_id(),
|
||||
"window_texture_view",
|
||||
SlotAttribute::Input,
|
||||
None,
|
||||
);
|
||||
desc.add_buffer_slot(
|
||||
graph.next_id(),
|
||||
"screen_size_buffer",
|
||||
SlotAttribute::Input,
|
||||
None,
|
||||
);
|
||||
desc.add_buffer_slot(graph.next_id(), "camera_buffer", SlotAttribute::Input, None);
|
||||
desc.add_buffer_slot(
|
||||
graph.next_id(),
|
||||
"index_counter_buffer",
|
||||
SlotAttribute::Output,
|
||||
Some(SlotValue::Buffer(Rc::new(light_index_counter_buffer))),
|
||||
);
|
||||
*id += 1;
|
||||
|
||||
desc
|
||||
}
|
||||
|
||||
fn prepare(&mut self, world: &mut World) {
|
||||
let _ = world;
|
||||
todo!()
|
||||
}
|
||||
fn prepare(&mut self, _world: &mut World, _context: &mut RenderGraphContext) {}
|
||||
|
||||
fn execute(
|
||||
&mut self,
|
||||
graph: &mut crate::render::graph::RenderGraph,
|
||||
_: &crate::render::graph::RenderGraphPassDesc,
|
||||
desc: &crate::render::graph::RenderGraphPassDesc,
|
||||
context: &mut RenderGraphContext,
|
||||
) {
|
||||
let mut pass = context.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
||||
label: Some("Pass_lightCull"),
|
||||
label: Some("light_cull_pass"),
|
||||
});
|
||||
|
||||
let pipeline = graph.compute_pipeline("main");
|
||||
pass.set_pipeline(pipeline);
|
||||
let pipeline = graph.pipeline(desc.id);
|
||||
pass.set_pipeline(pipeline.as_compute());
|
||||
|
||||
let depth_tex = graph.slot_bind_group(
|
||||
graph
|
||||
.slot_id("depth_texture")
|
||||
.expect("Could not find depth texture slot"),
|
||||
);
|
||||
let camera_bg = graph.slot_bind_group(
|
||||
graph
|
||||
.slot_id("camera_buffer")
|
||||
.expect("Could not find camera buffers"),
|
||||
);
|
||||
let screen_size_bg = graph.slot_bind_group(
|
||||
graph
|
||||
.slot_id("screen_size_buffer")
|
||||
.expect("Could not find screen size buffer slot"),
|
||||
);
|
||||
let indices_bg = graph.slot_bind_group(
|
||||
graph
|
||||
.slot_id("light_indices")
|
||||
.expect("Could not find light index buffer slot"),
|
||||
);
|
||||
let light_grid_bg = graph.slot_bind_group(
|
||||
graph
|
||||
.slot_id("grid_texture")
|
||||
.expect("Could not find light grid buffer slot"),
|
||||
);
|
||||
/* let depth_tex_bg = graph.bind_group(graph.bind_group_id("depth_texture").unwrap());
|
||||
let camera_bg = graph.bind_group(graph.bind_group_id("camera").unwrap());
|
||||
let lights_bg = graph.bind_group(graph.bind_group_id("light_buffers").unwrap());
|
||||
let grid_bg = graph.bind_group(graph.bind_group_id("light_indices_grid").unwrap());
|
||||
let screen_size_bg = graph.bind_group(graph.bind_group_id("screen_size").unwrap());
|
||||
|
||||
pass.set_bind_group(0, depth_tex, &[]);
|
||||
pass.set_bind_group(0, depth_tex_bg, &[]);
|
||||
pass.set_bind_group(1, camera_bg, &[]);
|
||||
pass.set_bind_group(2, indices_bg, &[]);
|
||||
pass.set_bind_group(3, light_grid_bg, &[]);
|
||||
pass.set_bind_group(4, screen_size_bg, &[]);
|
||||
pass.set_bind_group(2, lights_bg, &[]);
|
||||
pass.set_bind_group(3, grid_bg, &[]);
|
||||
pass.set_bind_group(4, screen_size_bg, &[]); */
|
||||
|
||||
RenderGraphContext::set_bind_groups(
|
||||
graph,
|
||||
&mut pass,
|
||||
&[
|
||||
("depth_texture", 0),
|
||||
("camera", 1),
|
||||
("light_buffers", 2),
|
||||
("light_indices_grid", 3),
|
||||
("screen_size", 4),
|
||||
],
|
||||
);
|
||||
|
||||
pass.dispatch_workgroups(self.workgroup_size.x, self.workgroup_size.y, 1);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
/* mod light_cull_compute;
|
||||
mod light_cull_compute;
|
||||
pub use light_cull_compute::*;
|
||||
|
||||
|
||||
|
||||
mod depth_prepass;
|
||||
/*mod depth_prepass;
|
||||
pub use depth_prepass::*; */
|
||||
|
||||
/* mod simple_phong;
|
||||
|
@ -12,6 +10,9 @@ pub use simple_phong::*; */
|
|||
mod base;
|
||||
pub use base::*;
|
||||
|
||||
mod light_base;
|
||||
pub use light_base::*;
|
||||
|
||||
mod present_pass;
|
||||
pub use present_pass::*;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
SlotAttribute, SlotValue,
|
||||
},
|
||||
render_buffer::BufferWrapper,
|
||||
resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState},
|
||||
resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState},
|
||||
},
|
||||
DeltaTime,
|
||||
};
|
||||
|
@ -53,9 +53,9 @@ impl RenderGraphPass for TrianglePass {
|
|||
|
||||
let mut desc = RenderGraphPassDesc::new(
|
||||
graph.next_id(),
|
||||
"TrianglePass",
|
||||
"triangle",
|
||||
RenderPassType::Render,
|
||||
Some(RenderPipelineDescriptor {
|
||||
Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
|
||||
label: Some("triangle_pipeline".into()),
|
||||
layouts: vec![color_bgl.clone()],
|
||||
push_constant_ranges: vec![],
|
||||
|
@ -77,7 +77,7 @@ impl RenderGraphPass for TrianglePass {
|
|||
primitive: wgpu::PrimitiveState::default(),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview: None,
|
||||
}),
|
||||
})),
|
||||
vec![("color_bg", color_bg, Some(color_bgl))],
|
||||
);
|
||||
|
||||
|
@ -125,7 +125,7 @@ impl RenderGraphPass for TrianglePass {
|
|||
|
||||
let encoder = context.encoder.as_mut().unwrap();
|
||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("TrianglePass"),
|
||||
label: Some("triangle_pass"),
|
||||
color_attachments: &[
|
||||
// This is what @location(0) in the fragment shader targets
|
||||
Some(wgpu::RenderPassColorAttachment {
|
||||
|
|
|
@ -6,7 +6,7 @@ use lyra_ecs::{Entity, Tick, World, query::{Entities, TickOf}};
|
|||
pub use point::*;
|
||||
pub use spotlight::*;
|
||||
|
||||
use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem};
|
||||
use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem, rc::Rc};
|
||||
|
||||
use crate::math::Transform;
|
||||
|
||||
|
@ -100,8 +100,10 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
|
|||
}
|
||||
|
||||
pub(crate) struct LightUniformBuffers {
|
||||
pub buffer: wgpu::Buffer,
|
||||
pub bind_group_pair: BindGroupPair,
|
||||
pub buffer: Rc<wgpu::Buffer>,
|
||||
//pub bind_group_pair: BindGroupPair,
|
||||
pub bind_group: Rc<wgpu::BindGroup>,
|
||||
pub bind_group_layout: Rc<wgpu::BindGroupLayout>,
|
||||
pub light_indexes: HashMap<Entity, u32>,
|
||||
dead_indices: VecDeque<u32>,
|
||||
pub current_light_idx: u32,
|
||||
|
@ -158,8 +160,9 @@ impl LightUniformBuffers {
|
|||
});
|
||||
|
||||
Self {
|
||||
buffer,
|
||||
bind_group_pair: BindGroupPair::new(bindgroup, bindgroup_layout),
|
||||
buffer: Rc::new(buffer),
|
||||
bind_group: Rc::new(bindgroup),
|
||||
bind_group_layout: Rc::new(bindgroup_layout),
|
||||
light_indexes: Default::default(),
|
||||
current_light_idx: 0,
|
||||
dead_indices: VecDeque::new(),
|
||||
|
|
|
@ -12,6 +12,6 @@ pub mod camera;
|
|||
pub mod window;
|
||||
pub mod transform_buffer_storage;
|
||||
pub mod light;
|
||||
pub mod light_cull_compute;
|
||||
//pub mod light_cull_compute;
|
||||
pub mod avec;
|
||||
pub mod graph;
|
|
@ -13,7 +13,7 @@ use wgpu::Limits;
|
|||
use winit::window::Window;
|
||||
|
||||
use crate::math::Transform;
|
||||
use crate::render::graph::{BasePass, PresentPass, TrianglePass};
|
||||
use crate::render::graph::{BasePass, LightBasePass, LightCullComputePass, PresentPass, TrianglePass};
|
||||
use crate::render::material::MaterialUniform;
|
||||
use crate::render::render_buffer::BufferWrapperBuilder;
|
||||
|
||||
|
@ -211,22 +211,25 @@ impl BasicRenderer {
|
|||
|
||||
let device = Rc::new(device);
|
||||
let queue = Rc::new(queue);
|
||||
//let light_cull_compute = LightCullCompute::new(device.clone(), queue.clone(), size, &light_uniform_buffers, &camera_buffer, &mut depth_texture);
|
||||
|
||||
let mut g = RenderGraph::new(device.clone(), queue.clone());
|
||||
/* debug!("Adding base pass");
|
||||
g.add_pass(TrianglePass::new());
|
||||
debug!("Adding depth pre-pass");
|
||||
g.add_pass(DepthPrePass::new());
|
||||
debug!("Adding light cull compute pass");
|
||||
g.add_pass(LightCullComputePass::new(size)); */
|
||||
|
||||
debug!("Adding base pass");
|
||||
g.add_pass(BasePass::new(surface, config));
|
||||
debug!("Adding light base pass");
|
||||
g.add_pass(LightBasePass::new());
|
||||
debug!("Adding light cull compute pass");
|
||||
g.add_pass(LightCullComputePass::new(size));
|
||||
debug!("Adding triangle pass");
|
||||
g.add_pass(TrianglePass::new());
|
||||
debug!("Adding present pass");
|
||||
g.add_pass(PresentPass::new("main_render_target"));
|
||||
|
||||
g.add_edge("base", "light_base");
|
||||
g.add_edge("light_base", "light_cull_compute");
|
||||
g.add_edge("base", "triangle");
|
||||
g.add_edge("base", "present_main_render_target");
|
||||
|
||||
g.setup(&device);
|
||||
|
||||
Self {
|
||||
|
|
|
@ -1,9 +1,41 @@
|
|||
use std::ops::Deref;
|
||||
use std::{ops::Deref, rc::Rc};
|
||||
|
||||
use wgpu::PipelineLayout;
|
||||
|
||||
use super::Shader;
|
||||
|
||||
//#[derive(Debug, Clone)]
|
||||
pub struct ComputePipelineDescriptor {
|
||||
pub label: Option<String>,
|
||||
pub layouts: Vec<Rc<wgpu::BindGroupLayout>>,
|
||||
pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
|
||||
// TODO: make this a ResHandle<Shader>
|
||||
/// The compiled shader module for the stage.
|
||||
pub shader: Rc<Shader>,
|
||||
/// The entry point in the compiled shader.
|
||||
/// There must be a function in the shader with the same name.
|
||||
pub shader_entry_point: String,
|
||||
}
|
||||
|
||||
impl ComputePipelineDescriptor {
|
||||
/// Create the [`wgpu::PipelineLayout`] for this pipeline
|
||||
pub(crate) fn create_layout(&self, device: &wgpu::Device) -> wgpu::PipelineLayout {
|
||||
let bgs = self
|
||||
.layouts
|
||||
.iter()
|
||||
.map(|bg| bg.as_ref())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: None, //self.label.as_ref().map(|s| format!("{}Layout", s)),
|
||||
bind_group_layouts: &bgs,
|
||||
push_constant_ranges: &self.push_constant_ranges,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ComputePipeline {
|
||||
layout: PipelineLayout,
|
||||
layout: Option<PipelineLayout>,
|
||||
wgpu_pipeline: wgpu::ComputePipeline,
|
||||
}
|
||||
|
||||
|
@ -15,8 +47,48 @@ impl Deref for ComputePipeline {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<wgpu::ComputePipeline> for ComputePipeline {
|
||||
fn from(value: wgpu::ComputePipeline) -> Self {
|
||||
Self {
|
||||
layout: None,
|
||||
wgpu_pipeline: value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputePipeline {
|
||||
pub fn new(layout: PipelineLayout, pipeline: wgpu::ComputePipeline) -> Self {
|
||||
/// Creates a new compute pipeline on the `device`.
|
||||
///
|
||||
/// Parameters:
|
||||
/// * `device` - The device to create the pipeline on.
|
||||
/// * `desc` - The discriptor of the compute pipeline
|
||||
pub fn create(device: &wgpu::Device, desc: &ComputePipelineDescriptor) -> ComputePipeline {
|
||||
// create the layout only if bind groups layouts were specified
|
||||
let layout = if !desc.layouts.is_empty() {
|
||||
Some(desc.create_layout(device))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// an Rc was used here so that this shader could be reused by the fragment stage if
|
||||
// they share the same shader. I tried to do it without an Rc but couldn't get past
|
||||
// the borrow checker
|
||||
let compiled_shader = Rc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: desc.shader.label.as_ref().map(|s| s.as_str()),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
||||
&desc.shader.source,
|
||||
)),
|
||||
}));
|
||||
|
||||
let desc = wgpu::ComputePipelineDescriptor {
|
||||
label: desc.label.as_deref(),
|
||||
layout: layout.as_ref(),
|
||||
module: &compiled_shader,
|
||||
entry_point: &desc.shader_entry_point,
|
||||
};
|
||||
|
||||
let pipeline = device.create_compute_pipeline(&desc);
|
||||
|
||||
Self {
|
||||
layout,
|
||||
wgpu_pipeline: pipeline,
|
||||
|
@ -24,12 +96,12 @@ impl ComputePipeline {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn layout(&self) -> &PipelineLayout {
|
||||
&self.layout
|
||||
pub fn layout(&self) -> Option<&PipelineLayout> {
|
||||
self.layout.as_ref()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn wgpu_pipeline(&self) -> &wgpu::ComputePipeline {
|
||||
&self.wgpu_pipeline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
mod shader;
|
||||
pub use shader::*;
|
||||
|
||||
mod pipeline;
|
||||
pub use pipeline::*;
|
||||
|
||||
|
|
|
@ -1,4 +1,25 @@
|
|||
use super::{compute_pipeline::ComputePipeline, render_pipeline::RenderPipeline};
|
||||
use super::{compute_pipeline::ComputePipeline, render_pipeline::RenderPipeline, ComputePipelineDescriptor, RenderPipelineDescriptor};
|
||||
|
||||
pub enum PipelineDescriptor {
|
||||
Render(RenderPipelineDescriptor),
|
||||
Compute(ComputePipelineDescriptor),
|
||||
}
|
||||
|
||||
impl PipelineDescriptor {
|
||||
pub fn as_render_pipeline_descriptor(&self) -> Option<&RenderPipelineDescriptor> {
|
||||
match self {
|
||||
Self::Render(r) => Some(r),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_compute_pipeline_descriptor(&self) -> Option<&ComputePipelineDescriptor> {
|
||||
match self {
|
||||
Self::Compute(c) => Some(c),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Pipeline {
|
||||
Render(RenderPipeline),
|
||||
|
|
|
@ -2,44 +2,7 @@ use std::{num::NonZeroU32, ops::Deref, rc::Rc};
|
|||
|
||||
use wgpu::PipelineLayout;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct VertexBufferLayout {
|
||||
pub array_stride: wgpu::BufferAddress,
|
||||
pub step_mode: wgpu::VertexStepMode,
|
||||
pub attributes: Vec<wgpu::VertexAttribute>,
|
||||
}
|
||||
|
||||
/// Describes the vertex stage in a render pipeline.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VertexState {
|
||||
// TODO: make this a ResHandle<Shader>
|
||||
/// The compiled shader module for the stage.
|
||||
pub module: Rc<Shader>,
|
||||
/// The entry point in the compiled shader.
|
||||
/// There must be a function in the shader with the same name.
|
||||
pub entry_point: String,
|
||||
/// The format of the vertex buffers used with this pipeline.
|
||||
pub buffers: Vec<VertexBufferLayout>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Shader {
|
||||
pub label: Option<String>,
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
/// Describes the fragment stage in the render pipeline.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FragmentState {
|
||||
// TODO: make this a ResHandle<Shader>
|
||||
/// The compiled shader module for the stage.
|
||||
pub module: Rc<Shader>,
|
||||
/// The entry point in the compiled shader.
|
||||
/// There must be a function in the shader with the same name.
|
||||
pub entry_point: String,
|
||||
/// The color state of the render targets.
|
||||
pub targets: Vec<Option<wgpu::ColorTargetState>>,
|
||||
}
|
||||
use super::{FragmentState, VertexState};
|
||||
|
||||
//#[derive(Debug, Clone)]
|
||||
pub struct RenderPipelineDescriptor {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct VertexBufferLayout {
|
||||
pub array_stride: wgpu::BufferAddress,
|
||||
pub step_mode: wgpu::VertexStepMode,
|
||||
pub attributes: Vec<wgpu::VertexAttribute>,
|
||||
}
|
||||
|
||||
/// Describes the vertex stage in a render pipeline.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VertexState {
|
||||
// TODO: make this a ResHandle<Shader>
|
||||
/// The compiled shader module for the stage.
|
||||
pub module: Rc<Shader>,
|
||||
/// The entry point in the compiled shader.
|
||||
/// There must be a function in the shader with the same name.
|
||||
pub entry_point: String,
|
||||
/// The format of the vertex buffers used with this pipeline.
|
||||
pub buffers: Vec<VertexBufferLayout>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Shader {
|
||||
pub label: Option<String>,
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
/// Describes the fragment stage in the render pipeline.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FragmentState {
|
||||
// TODO: make this a ResHandle<Shader>
|
||||
/// The compiled shader module for the stage.
|
||||
pub module: Rc<Shader>,
|
||||
/// The entry point in the compiled shader.
|
||||
/// There must be a function in the shader with the same name.
|
||||
pub entry_point: String,
|
||||
/// The color state of the render targets.
|
||||
pub targets: Vec<Option<wgpu::ColorTargetState>>,
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
// Vertex shader
|
||||
|
||||
const max_light_count: u32 = 16u;
|
||||
|
||||
const LIGHT_TY_DIRECTIONAL = 0u;
|
||||
const LIGHT_TY_POINT = 1u;
|
||||
const LIGHT_TY_SPOT = 2u;
|
||||
|
||||
const ALPHA_CUTOFF = 0.1;
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) tex_coords: vec2<f32>,
|
||||
@location(2) normal: vec3<f32>,
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) tex_coords: vec2<f32>,
|
||||
@location(1) world_position: vec3<f32>,
|
||||
@location(2) world_normal: vec3<f32>,
|
||||
}
|
||||
|
||||
struct TransformData {
|
||||
transform: mat4x4<f32>,
|
||||
normal_matrix: mat4x4<f32>,
|
||||
}
|
||||
|
||||
struct CameraUniform {
|
||||
view: mat4x4<f32>,
|
||||
inverse_projection: mat4x4<f32>,
|
||||
view_projection: mat4x4<f32>,
|
||||
projection: mat4x4<f32>,
|
||||
position: vec3<f32>,
|
||||
tile_debug: u32,
|
||||
};
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> u_model_transform_data: TransformData;
|
||||
|
||||
@group(2) @binding(0)
|
||||
var<uniform> u_camera: CameraUniform;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
model: VertexInput,
|
||||
) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
|
||||
out.tex_coords = model.tex_coords;
|
||||
out.clip_position = u_camera.view_projection * u_model_transform_data.transform * vec4<f32>(model.position, 1.0);
|
||||
|
||||
// the normal mat is actually only a mat3x3, but there's a bug in wgpu: https://github.com/gfx-rs/wgpu-rs/issues/36
|
||||
let normal_mat4 = u_model_transform_data.normal_matrix;
|
||||
let normal_mat = mat3x3(normal_mat4[0].xyz, normal_mat4[1].xyz, normal_mat4[2].xyz);
|
||||
out.world_normal = normalize(normal_mat * model.normal, );
|
||||
|
||||
var world_position: vec4<f32> = u_model_transform_data.transform * vec4<f32>(model.position, 1.0);
|
||||
out.world_position = world_position.xyz;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Fragment shader
|
||||
|
||||
struct Material {
|
||||
ambient: vec4<f32>,
|
||||
diffuse: vec4<f32>,
|
||||
specular: vec4<f32>,
|
||||
shininess: f32,
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var t_diffuse: texture_2d<f32>;
|
||||
@group(0) @binding(1)
|
||||
var s_diffuse: sampler;
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||
|
||||
if (object_color.a < ALPHA_CUTOFF) {
|
||||
discard;
|
||||
}
|
||||
|
||||
return object_color;
|
||||
}
|
Loading…
Reference in New Issue