Implement a Render Graph #16
|
@ -959,6 +959,12 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fixedbitset"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.28"
|
version = "1.0.28"
|
||||||
|
@ -1865,6 +1871,7 @@ dependencies = [
|
||||||
"lyra-reflect",
|
"lyra-reflect",
|
||||||
"lyra-resource",
|
"lyra-resource",
|
||||||
"lyra-scene",
|
"lyra-scene",
|
||||||
|
"petgraph",
|
||||||
"quote",
|
"quote",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"syn 2.0.51",
|
"syn 2.0.51",
|
||||||
|
@ -2507,6 +2514,16 @@ version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
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]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
|
|
|
@ -35,6 +35,7 @@ itertools = "0.11.0"
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
unique = "0.9.1"
|
unique = "0.9.1"
|
||||||
rustc-hash = "1.1.0"
|
rustc-hash = "1.1.0"
|
||||||
|
petgraph = { version = "0.6.5", features = ["matrix_graph"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tracy = ["dep:tracing-tracy"]
|
tracy = ["dep:tracing-tracy"]
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
pub use pass::*;
|
pub use pass::*;
|
||||||
|
|
||||||
|
@ -19,15 +20,18 @@ mod execution_path;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use tracing::{debug_span, instrument, trace, warn};
|
use tracing::{debug_span, instrument, trace, warn};
|
||||||
|
use wgpu::ComputePass;
|
||||||
|
|
||||||
use self::execution_path::GraphExecutionPath;
|
use self::execution_path::GraphExecutionPath;
|
||||||
|
|
||||||
use super::resource::{Pipeline, RenderPipeline};
|
use super::resource::{ComputePipeline, Pipeline, RenderPipeline};
|
||||||
|
|
||||||
//#[derive(Clone)]
|
//#[derive(Clone)]
|
||||||
struct PassEntry {
|
struct PassEntry {
|
||||||
inner: Arc<RefCell<dyn RenderGraphPass>>,
|
inner: Arc<RefCell<dyn RenderGraphPass>>,
|
||||||
desc: Arc<RenderGraphPassDesc>,
|
desc: Arc<RenderGraphPassDesc>,
|
||||||
|
/// The index of the pass in the execution graph
|
||||||
|
graph_index: petgraph::matrix_graph::NodeIndex<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BindGroupEntry {
|
pub struct BindGroupEntry {
|
||||||
|
@ -73,10 +77,10 @@ pub struct RenderGraph {
|
||||||
bind_group_names: FxHashMap<String, u64>,
|
bind_group_names: FxHashMap<String, u64>,
|
||||||
// TODO: make pipelines a `type` parameter in RenderPasses,
|
// TODO: make pipelines a `type` parameter in RenderPasses,
|
||||||
// then the pipelines can be retrieved via TypeId to the pass.
|
// then the pipelines can be retrieved via TypeId to the pass.
|
||||||
///
|
|
||||||
pipelines: FxHashMap<u64, PipelineResource>,
|
pipelines: FxHashMap<u64, PipelineResource>,
|
||||||
current_id: u64,
|
current_id: u64,
|
||||||
exec_path: Option<GraphExecutionPath>,
|
exec_path: Option<GraphExecutionPath>,
|
||||||
|
new_path: petgraph::matrix_graph::DiMatrix<u64, (), Option<()>, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraph {
|
impl RenderGraph {
|
||||||
|
@ -92,6 +96,7 @@ impl RenderGraph {
|
||||||
pipelines: Default::default(),
|
pipelines: Default::default(),
|
||||||
current_id: 1,
|
current_id: 1,
|
||||||
exec_path: None,
|
exec_path: None,
|
||||||
|
new_path: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,11 +163,14 @@ impl RenderGraph {
|
||||||
self.bind_group_names.insert(name.clone(), bg_id);
|
self.bind_group_names.insert(name.clone(), bg_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let index = self.new_path.add_node(desc.id);
|
||||||
|
|
||||||
self.passes.insert(
|
self.passes.insert(
|
||||||
desc.id,
|
desc.id,
|
||||||
PassEntry {
|
PassEntry {
|
||||||
inner: Arc::new(RefCell::new(pass)),
|
inner: Arc::new(RefCell::new(pass)),
|
||||||
desc: Arc::new(desc),
|
desc: Arc::new(desc),
|
||||||
|
graph_index: index,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -172,12 +180,19 @@ impl RenderGraph {
|
||||||
pub fn setup(&mut self, device: &wgpu::Device) {
|
pub fn setup(&mut self, device: &wgpu::Device) {
|
||||||
// For all passes, create their pipelines
|
// For all passes, create their pipelines
|
||||||
for pass in self.passes.values() {
|
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 {
|
let pipeline = match pass.desc.pass_type {
|
||||||
RenderPassType::Render => {
|
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 {
|
let res = PipelineResource {
|
||||||
|
@ -233,25 +248,30 @@ impl RenderGraph {
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub fn render(&mut 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);
|
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 = self.passes.get(&pass_id).unwrap();
|
||||||
let pass_inn = pass.inner.clone();
|
let pass_inn = pass.inner.clone();
|
||||||
let pass_desc = pass.desc.clone();
|
let pass_desc = pass.desc.clone();
|
||||||
let label = format!("{} Encoder", pass_desc.name);
|
let label = format!("{} Encoder", pass_desc.name);
|
||||||
|
|
||||||
// encoders are not needed for presenter nodes.
|
// encoders are not needed for presenter nodes.
|
||||||
let encoder = if pass_desc.pass_type == RenderPassType::Presenter {
|
let encoder = if pass_desc.pass_type.should_have_pipeline() {
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(
|
Some(
|
||||||
self.device
|
self.device
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
label: Some(&label),
|
label: Some(&label),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let queue = self.queue.clone(); // clone is required to appease the borrow checker
|
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.
|
// all encoders need to be submitted before a presenter node is executed.
|
||||||
if pass_desc.pass_type == RenderPassType::Presenter {
|
if pass_desc.pass_type == RenderPassType::Presenter {
|
||||||
|
trace!("Submitting {} encoderd before presenting", encoders.len());
|
||||||
self.queue.submit(encoders.drain(..));
|
self.queue.submit(encoders.drain(..));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace!("Executing {}", pass_desc.name);
|
||||||
let mut inner = pass_inn.borrow_mut();
|
let mut inner = pass_inn.borrow_mut();
|
||||||
inner.execute(self, &*pass_desc, &mut context);
|
inner.execute(self, &*pass_desc, &mut context);
|
||||||
|
|
||||||
|
@ -320,6 +342,16 @@ impl RenderGraph {
|
||||||
pub fn bind_group_id(&self, name: &str) -> Option<u64> {
|
pub fn bind_group_id(&self, name: &str) -> Option<u64> {
|
||||||
self.bind_group_names.get(name).copied()
|
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.
|
/// 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));
|
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 lyra_ecs::World;
|
||||||
|
|
||||||
use crate::render::resource::RenderPipelineDescriptor;
|
use crate::render::resource::PipelineDescriptor;
|
||||||
|
|
||||||
use super::{RenderGraph, RenderGraphContext, RenderTarget};
|
use super::{RenderGraph, RenderGraphContext, RenderTarget};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
pub enum RenderPassType {
|
pub enum RenderPassType {
|
||||||
|
/// A node doesn't render, compute, or present anything. This likely means it injects data into the graph.
|
||||||
|
Node,
|
||||||
Compute,
|
Compute,
|
||||||
#[default]
|
#[default]
|
||||||
Render,
|
Render,
|
||||||
Presenter,
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum SlotType {
|
pub enum SlotType {
|
||||||
TextureView,
|
TextureView,
|
||||||
|
@ -150,7 +163,7 @@ pub struct RenderGraphPassDesc {
|
||||||
pub pass_type: RenderPassType,
|
pub pass_type: RenderPassType,
|
||||||
pub slots: Vec<RenderPassSlot>,
|
pub slots: Vec<RenderPassSlot>,
|
||||||
slot_names: HashMap<String, u64>,
|
slot_names: HashMap<String, u64>,
|
||||||
pub pipeline_desc: Option<RenderPipelineDescriptor>,
|
pub pipeline_desc: Option<PipelineDescriptor>,
|
||||||
pub bind_groups: Vec<(
|
pub bind_groups: Vec<(
|
||||||
String,
|
String,
|
||||||
Rc<wgpu::BindGroup>,
|
Rc<wgpu::BindGroup>,
|
||||||
|
@ -163,7 +176,7 @@ impl RenderGraphPassDesc {
|
||||||
id: u64,
|
id: u64,
|
||||||
name: &str,
|
name: &str,
|
||||||
pass_type: RenderPassType,
|
pass_type: RenderPassType,
|
||||||
pipeline_desc: Option<RenderPipelineDescriptor>,
|
pipeline_desc: Option<PipelineDescriptor>,
|
||||||
bind_groups: Vec<(&str, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>,
|
bind_groups: Vec<(&str, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -1,6 +1,20 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
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.
|
/// Supplies some basic things other passes needs.
|
||||||
///
|
///
|
||||||
|
@ -14,55 +28,87 @@ pub struct BasePass {
|
||||||
temp_render_target: Option<RenderTarget>,
|
temp_render_target: Option<RenderTarget>,
|
||||||
main_rt_id: u64,
|
main_rt_id: u64,
|
||||||
window_tv_id: u64,
|
window_tv_id: u64,
|
||||||
|
screen_size: glam::UVec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasePass {
|
impl BasePass {
|
||||||
pub fn new(surface: wgpu::Surface, surface_config: wgpu::SurfaceConfiguration) -> Self {
|
pub fn new(surface: wgpu::Surface, surface_config: wgpu::SurfaceConfiguration) -> Self {
|
||||||
|
let size = glam::UVec2::new(surface_config.width, surface_config.height);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
temp_render_target: Some(RenderTarget {
|
temp_render_target: Some(RenderTarget {
|
||||||
surface,
|
surface,
|
||||||
surface_config,
|
surface_config,
|
||||||
current_texture: None,
|
current_texture: None,
|
||||||
}),
|
}),
|
||||||
main_rt_id: 0,
|
screen_size: size,
|
||||||
window_tv_id: 0,
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPass for BasePass {
|
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(
|
let mut desc = RenderGraphPassDesc::new(
|
||||||
graph.next_id(),
|
graph.next_id(),
|
||||||
"base",
|
"base",
|
||||||
RenderPassType::Render,
|
RenderPassType::Render,
|
||||||
None,
|
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();
|
self.main_rt_id = graph.next_id();
|
||||||
let render_target = self.temp_render_target.take().unwrap();
|
desc.add_slot(RenderPassSlot {
|
||||||
desc.add_slot(
|
ty: SlotType::RenderTarget,
|
||||||
RenderPassSlot {
|
attribute: SlotAttribute::Output,
|
||||||
ty: SlotType::RenderTarget,
|
id: self.main_rt_id,
|
||||||
attribute: SlotAttribute::Output,
|
name: "main_render_target".into(),
|
||||||
id: self.main_rt_id,
|
value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new(
|
||||||
name: "main_render_target".into(),
|
render_target,
|
||||||
value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new(render_target)))),
|
)))),
|
||||||
}
|
});
|
||||||
);
|
|
||||||
self.window_tv_id = graph.next_id();
|
self.window_tv_id = graph.next_id();
|
||||||
desc.add_texture_view_slot(
|
desc.add_texture_view_slot(
|
||||||
self.window_tv_id,
|
self.window_tv_id,
|
||||||
|
@ -70,31 +116,75 @@ impl RenderGraphPass for BasePass {
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
Some(SlotValue::Lazy),
|
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
|
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) {
|
fn execute(
|
||||||
let tv_slot = graph.slot_value_mut(self.main_rt_id)
|
&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");
|
.expect("somehow the main render target slot is missing");
|
||||||
let mut rt = tv_slot.as_render_target_mut().unwrap();
|
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 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);
|
rt.current_texture = Some(surface_tex);
|
||||||
drop(rt); // must be manually dropped for borrow checker when getting texture view slot
|
drop(rt); // must be manually dropped for borrow checker when getting texture view slot
|
||||||
|
|
||||||
// store the surface texture to the 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");
|
.expect("somehow the window texture view slot is missing");
|
||||||
*tv_slot = SlotValue::TextureView(Rc::new(view));
|
*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 lyra_ecs::World;
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
use crate::render::graph::{
|
use crate::render::{
|
||||||
BufferInitDescriptor, RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType,
|
graph::{
|
||||||
SlotAttribute, SlotDescriptor, TextureDescriptor, TextureViewDescriptor,
|
RenderGraphContext, RenderGraphPass, RenderGraphPassDesc, RenderPassType, SlotAttribute,
|
||||||
|
SlotValue,
|
||||||
|
},
|
||||||
|
resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct LightCullComputePass {
|
pub struct LightCullComputePass {
|
||||||
|
@ -21,124 +25,228 @@ impl LightCullComputePass {
|
||||||
|
|
||||||
impl RenderGraphPass for LightCullComputePass {
|
impl RenderGraphPass for LightCullComputePass {
|
||||||
fn desc(
|
fn desc(
|
||||||
&self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
id: &mut u64,
|
|
||||||
) -> crate::render::graph::RenderGraphPassDesc {
|
) -> crate::render::graph::RenderGraphPassDesc {
|
||||||
let mut desc = RenderGraphPassDesc::new(*id, "LightCullCompute", RenderPassType::Compute);
|
let shader = Rc::new(Shader {
|
||||||
*id += 1;
|
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);
|
// get the size of the work group for the grid
|
||||||
*id += 1;
|
let main_rt = graph
|
||||||
desc.add_buffer_slot(*id, "camera_buffer", SlotAttribute::Input, None);
|
.slot_id("main_render_target")
|
||||||
*id += 1;
|
.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 mut contents = Vec::<u8>::new();
|
||||||
let contents_len =
|
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);
|
contents.resize(contents_len as _, 0);
|
||||||
desc.add_buffer_slot(
|
|
||||||
*id,
|
let device = graph.device();
|
||||||
"light_indices",
|
let light_indices_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
SlotAttribute::Output,
|
label: Some("light_indices_buffer"),
|
||||||
Some(SlotDescriptor::BufferInit(BufferInitDescriptor {
|
contents: &contents,
|
||||||
label: Some("B_LightIndices".to_string()),
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||||
contents,
|
});
|
||||||
|
|
||||||
|
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,
|
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 {
|
let size = wgpu::Extent3d {
|
||||||
width: self.workgroup_size.x,
|
width: self.workgroup_size.x,
|
||||||
height: self.workgroup_size.y,
|
height: self.workgroup_size.y,
|
||||||
depth_or_array_layers: 1,
|
depth_or_array_layers: 1,
|
||||||
};
|
};
|
||||||
desc.add_texture_slot(
|
let grid_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
*id,
|
label: Some("light_grid_tex"),
|
||||||
"lightgrid_texture",
|
size,
|
||||||
SlotAttribute::Output,
|
mip_level_count: 1,
|
||||||
Some(SlotDescriptor::Texture(TextureDescriptor {
|
sample_count: 1,
|
||||||
size,
|
dimension: wgpu::TextureDimension::D2,
|
||||||
mip_level_count: 1,
|
format: wgpu::TextureFormat::Rg32Uint, // vec2<uint>
|
||||||
sample_count: 1,
|
usage: wgpu::TextureUsages::STORAGE_BINDING,
|
||||||
dimension: wgpu::TextureDimension::D2,
|
view_formats: &[],
|
||||||
format: wgpu::TextureFormat::Rg32Uint, // vec2<uint>
|
});
|
||||||
usage: wgpu::TextureUsages::STORAGE_BINDING,
|
|
||||||
view_formats: vec![],
|
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(
|
desc.add_texture_view_slot(
|
||||||
*id,
|
graph.next_id(),
|
||||||
"lightgrid_texture_view",
|
"window_texture_view",
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Input,
|
||||||
Some(SlotDescriptor::TextureView(TextureViewDescriptor {
|
None,
|
||||||
texture_label: "lightgrid_texture".to_string(),
|
);
|
||||||
format: Some(wgpu::TextureFormat::Rg32Uint), // vec2<uint>
|
desc.add_buffer_slot(
|
||||||
dimension: Some(wgpu::TextureViewDimension::D2),
|
graph.next_id(),
|
||||||
aspect: wgpu::TextureAspect::All,
|
"screen_size_buffer",
|
||||||
base_mip_level: 0,
|
SlotAttribute::Input,
|
||||||
mip_level_count: None,
|
None,
|
||||||
base_array_layer: 0,
|
);
|
||||||
array_layer_count: 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
|
desc
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(&mut self, world: &mut World) {
|
fn prepare(&mut self, _world: &mut World, _context: &mut RenderGraphContext) {}
|
||||||
let _ = world;
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute(
|
fn execute(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
_: &crate::render::graph::RenderGraphPassDesc,
|
desc: &crate::render::graph::RenderGraphPassDesc,
|
||||||
context: &mut RenderGraphContext,
|
context: &mut RenderGraphContext,
|
||||||
) {
|
) {
|
||||||
let mut pass = context.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
let mut pass = context.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
||||||
label: Some("Pass_lightCull"),
|
label: Some("light_cull_pass"),
|
||||||
});
|
});
|
||||||
|
|
||||||
let pipeline = graph.compute_pipeline("main");
|
let pipeline = graph.pipeline(desc.id);
|
||||||
pass.set_pipeline(pipeline);
|
pass.set_pipeline(pipeline.as_compute());
|
||||||
|
|
||||||
let depth_tex = graph.slot_bind_group(
|
/* let depth_tex_bg = graph.bind_group(graph.bind_group_id("depth_texture").unwrap());
|
||||||
graph
|
let camera_bg = graph.bind_group(graph.bind_group_id("camera").unwrap());
|
||||||
.slot_id("depth_texture")
|
let lights_bg = graph.bind_group(graph.bind_group_id("light_buffers").unwrap());
|
||||||
.expect("Could not find depth texture slot"),
|
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());
|
||||||
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"),
|
|
||||||
);
|
|
||||||
|
|
||||||
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(1, camera_bg, &[]);
|
||||||
pass.set_bind_group(2, indices_bg, &[]);
|
pass.set_bind_group(2, lights_bg, &[]);
|
||||||
pass.set_bind_group(3, light_grid_bg, &[]);
|
pass.set_bind_group(3, grid_bg, &[]);
|
||||||
pass.set_bind_group(4, screen_size_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);
|
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::*;
|
pub use light_cull_compute::*;
|
||||||
|
|
||||||
|
/*mod depth_prepass;
|
||||||
|
|
||||||
mod depth_prepass;
|
|
||||||
pub use depth_prepass::*; */
|
pub use depth_prepass::*; */
|
||||||
|
|
||||||
/* mod simple_phong;
|
/* mod simple_phong;
|
||||||
|
@ -12,6 +10,9 @@ pub use simple_phong::*; */
|
||||||
mod base;
|
mod base;
|
||||||
pub use base::*;
|
pub use base::*;
|
||||||
|
|
||||||
|
mod light_base;
|
||||||
|
pub use light_base::*;
|
||||||
|
|
||||||
mod present_pass;
|
mod present_pass;
|
||||||
pub use present_pass::*;
|
pub use present_pass::*;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
SlotAttribute, SlotValue,
|
SlotAttribute, SlotValue,
|
||||||
},
|
},
|
||||||
render_buffer::BufferWrapper,
|
render_buffer::BufferWrapper,
|
||||||
resource::{FragmentState, RenderPipelineDescriptor, Shader, VertexState},
|
resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState},
|
||||||
},
|
},
|
||||||
DeltaTime,
|
DeltaTime,
|
||||||
};
|
};
|
||||||
|
@ -53,9 +53,9 @@ impl RenderGraphPass for TrianglePass {
|
||||||
|
|
||||||
let mut desc = RenderGraphPassDesc::new(
|
let mut desc = RenderGraphPassDesc::new(
|
||||||
graph.next_id(),
|
graph.next_id(),
|
||||||
"TrianglePass",
|
"triangle",
|
||||||
RenderPassType::Render,
|
RenderPassType::Render,
|
||||||
Some(RenderPipelineDescriptor {
|
Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
|
||||||
label: Some("triangle_pipeline".into()),
|
label: Some("triangle_pipeline".into()),
|
||||||
layouts: vec![color_bgl.clone()],
|
layouts: vec![color_bgl.clone()],
|
||||||
push_constant_ranges: vec![],
|
push_constant_ranges: vec![],
|
||||||
|
@ -77,7 +77,7 @@ impl RenderGraphPass for TrianglePass {
|
||||||
primitive: wgpu::PrimitiveState::default(),
|
primitive: wgpu::PrimitiveState::default(),
|
||||||
multisample: wgpu::MultisampleState::default(),
|
multisample: wgpu::MultisampleState::default(),
|
||||||
multiview: None,
|
multiview: None,
|
||||||
}),
|
})),
|
||||||
vec![("color_bg", color_bg, Some(color_bgl))],
|
vec![("color_bg", color_bg, Some(color_bgl))],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ impl RenderGraphPass for TrianglePass {
|
||||||
|
|
||||||
let encoder = context.encoder.as_mut().unwrap();
|
let encoder = context.encoder.as_mut().unwrap();
|
||||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
label: Some("TrianglePass"),
|
label: Some("triangle_pass"),
|
||||||
color_attachments: &[
|
color_attachments: &[
|
||||||
// This is what @location(0) in the fragment shader targets
|
// This is what @location(0) in the fragment shader targets
|
||||||
Some(wgpu::RenderPassColorAttachment {
|
Some(wgpu::RenderPassColorAttachment {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use lyra_ecs::{Entity, Tick, World, query::{Entities, TickOf}};
|
||||||
pub use point::*;
|
pub use point::*;
|
||||||
pub use spotlight::*;
|
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;
|
use crate::math::Transform;
|
||||||
|
|
||||||
|
@ -100,8 +100,10 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct LightUniformBuffers {
|
pub(crate) struct LightUniformBuffers {
|
||||||
pub buffer: wgpu::Buffer,
|
pub buffer: Rc<wgpu::Buffer>,
|
||||||
pub bind_group_pair: BindGroupPair,
|
//pub bind_group_pair: BindGroupPair,
|
||||||
|
pub bind_group: Rc<wgpu::BindGroup>,
|
||||||
|
pub bind_group_layout: Rc<wgpu::BindGroupLayout>,
|
||||||
pub light_indexes: HashMap<Entity, u32>,
|
pub light_indexes: HashMap<Entity, u32>,
|
||||||
dead_indices: VecDeque<u32>,
|
dead_indices: VecDeque<u32>,
|
||||||
pub current_light_idx: u32,
|
pub current_light_idx: u32,
|
||||||
|
@ -158,8 +160,9 @@ impl LightUniformBuffers {
|
||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
buffer,
|
buffer: Rc::new(buffer),
|
||||||
bind_group_pair: BindGroupPair::new(bindgroup, bindgroup_layout),
|
bind_group: Rc::new(bindgroup),
|
||||||
|
bind_group_layout: Rc::new(bindgroup_layout),
|
||||||
light_indexes: Default::default(),
|
light_indexes: Default::default(),
|
||||||
current_light_idx: 0,
|
current_light_idx: 0,
|
||||||
dead_indices: VecDeque::new(),
|
dead_indices: VecDeque::new(),
|
||||||
|
|
|
@ -12,6 +12,6 @@ pub mod camera;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
pub mod transform_buffer_storage;
|
pub mod transform_buffer_storage;
|
||||||
pub mod light;
|
pub mod light;
|
||||||
pub mod light_cull_compute;
|
//pub mod light_cull_compute;
|
||||||
pub mod avec;
|
pub mod avec;
|
||||||
pub mod graph;
|
pub mod graph;
|
|
@ -13,7 +13,7 @@ use wgpu::Limits;
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
use crate::math::Transform;
|
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::material::MaterialUniform;
|
||||||
use crate::render::render_buffer::BufferWrapperBuilder;
|
use crate::render::render_buffer::BufferWrapperBuilder;
|
||||||
|
|
||||||
|
@ -211,22 +211,25 @@ impl BasicRenderer {
|
||||||
|
|
||||||
let device = Rc::new(device);
|
let device = Rc::new(device);
|
||||||
let queue = Rc::new(queue);
|
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());
|
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");
|
debug!("Adding base pass");
|
||||||
g.add_pass(BasePass::new(surface, config));
|
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");
|
debug!("Adding triangle pass");
|
||||||
g.add_pass(TrianglePass::new());
|
g.add_pass(TrianglePass::new());
|
||||||
debug!("Adding present pass");
|
debug!("Adding present pass");
|
||||||
g.add_pass(PresentPass::new("main_render_target"));
|
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);
|
g.setup(&device);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
|
@ -1,9 +1,41 @@
|
||||||
use std::ops::Deref;
|
use std::{ops::Deref, rc::Rc};
|
||||||
|
|
||||||
use wgpu::PipelineLayout;
|
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 {
|
pub struct ComputePipeline {
|
||||||
layout: PipelineLayout,
|
layout: Option<PipelineLayout>,
|
||||||
wgpu_pipeline: wgpu::ComputePipeline,
|
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 {
|
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 {
|
Self {
|
||||||
layout,
|
layout,
|
||||||
wgpu_pipeline: pipeline,
|
wgpu_pipeline: pipeline,
|
||||||
|
@ -24,8 +96,8 @@ impl ComputePipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn layout(&self) -> &PipelineLayout {
|
pub fn layout(&self) -> Option<&PipelineLayout> {
|
||||||
&self.layout
|
self.layout.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
mod shader;
|
||||||
|
pub use shader::*;
|
||||||
|
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
pub use 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 {
|
pub enum Pipeline {
|
||||||
Render(RenderPipeline),
|
Render(RenderPipeline),
|
||||||
|
|
|
@ -2,44 +2,7 @@ use std::{num::NonZeroU32, ops::Deref, rc::Rc};
|
||||||
|
|
||||||
use wgpu::PipelineLayout;
|
use wgpu::PipelineLayout;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
use super::{FragmentState, VertexState};
|
||||||
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>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
//#[derive(Debug, Clone)]
|
//#[derive(Debug, Clone)]
|
||||||
pub struct RenderPipelineDescriptor {
|
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