Compare commits
No commits in common. "5f1a61ef52a651be32cb98998714c3e0e8ad6584" and "5fc1a0f134a901a5b4e3bdd44ae085404d30a8ff" have entirely different histories.
5f1a61ef52
...
5fc1a0f134
|
@ -29,8 +29,8 @@ tracy = ["lyra-game/tracy"]
|
||||||
lyra-game = { path = "lyra-game" }
|
lyra-game = { path = "lyra-game" }
|
||||||
lyra-scripting = { path = "lyra-scripting", optional = true }
|
lyra-scripting = { path = "lyra-scripting", optional = true }
|
||||||
|
|
||||||
#[profile.dev]
|
[profile.dev]
|
||||||
#opt-level = 1
|
opt-level = 1
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
|
@ -1,100 +0,0 @@
|
||||||
use std::{collections::VecDeque, sync::Arc};
|
|
||||||
|
|
||||||
use tracing::instrument;
|
|
||||||
|
|
||||||
use super::{Frame, RenderGraphLabel, RenderGraphLabelValue, RenderTarget};
|
|
||||||
|
|
||||||
/// A queued write to a GPU buffer targeting a graph slot.
|
|
||||||
pub(crate) struct GraphBufferWrite {
|
|
||||||
/// The name of the slot that has the resource that will be written
|
|
||||||
pub(crate) target_slot: RenderGraphLabelValue,
|
|
||||||
pub(crate) offset: u64,
|
|
||||||
pub(crate) bytes: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct RenderGraphContext<'a> {
|
|
||||||
/// The [`wgpu::CommandEncoder`] used to encode GPU operations.
|
|
||||||
///
|
|
||||||
/// This is `None` during the `prepare` stage.
|
|
||||||
pub encoder: Option<wgpu::CommandEncoder>,
|
|
||||||
/// The gpu device that is being used.
|
|
||||||
pub device: Arc<wgpu::Device>,
|
|
||||||
pub queue: Arc<wgpu::Queue>,
|
|
||||||
pub(crate) buffer_writes: VecDeque<GraphBufferWrite>,
|
|
||||||
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
|
|
||||||
/// The label of this Node.
|
|
||||||
pub label: RenderGraphLabelValue,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> RenderGraphContext<'a> {
|
|
||||||
pub(crate) fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>, encoder: Option<wgpu::CommandEncoder>, label: RenderGraphLabelValue) -> Self {
|
|
||||||
Self {
|
|
||||||
encoder,
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
buffer_writes: Default::default(),
|
|
||||||
renderpass_desc: vec![],
|
|
||||||
label,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn begin_render_pass(
|
|
||||||
&'a mut self,
|
|
||||||
desc: wgpu::RenderPassDescriptor<'a, 'a>,
|
|
||||||
) -> wgpu::RenderPass {
|
|
||||||
self.encoder
|
|
||||||
.as_mut()
|
|
||||||
.expect(
|
|
||||||
"RenderGraphContext is missing a command encoder. This is likely \
|
|
||||||
because you are trying to run render commands in the prepare stage.",
|
|
||||||
)
|
|
||||||
.begin_render_pass(&desc)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn begin_compute_pass(&mut self, desc: &wgpu::ComputePassDescriptor) -> wgpu::ComputePass {
|
|
||||||
self.encoder
|
|
||||||
.as_mut()
|
|
||||||
.expect(
|
|
||||||
"RenderGraphContext is missing a command encoder. This is likely \
|
|
||||||
because you are trying to run render commands in the prepare stage.",
|
|
||||||
)
|
|
||||||
.begin_compute_pass(desc)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Queue a data write to a buffer at that is contained in `target_slot`.
|
|
||||||
///
|
|
||||||
/// This does not submit the data to the GPU immediately, or add it to the `wgpu::Queue`. The
|
|
||||||
/// data will be submitted to the GPU queue right after the prepare stage for all passes
|
|
||||||
/// is ran.
|
|
||||||
#[instrument(skip(self, bytes), level="trace", fields(size = bytes.len()))]
|
|
||||||
pub fn queue_buffer_write(&mut self, target_slot: impl RenderGraphLabel, offset: u64, bytes: &[u8]) {
|
|
||||||
self.buffer_writes.push_back(GraphBufferWrite {
|
|
||||||
target_slot: target_slot.into(),
|
|
||||||
offset,
|
|
||||||
bytes: bytes.to_vec(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Queue a data write of a type that to a buffer at that is contained in `target_slot`.
|
|
||||||
#[instrument(skip(self, bytes), level="trace", fields(size = std::mem::size_of::<T>()))]
|
|
||||||
pub fn queue_buffer_write_with<T: bytemuck::NoUninit>(
|
|
||||||
&mut self,
|
|
||||||
target_slot: impl RenderGraphLabel,
|
|
||||||
offset: u64,
|
|
||||||
bytes: T,
|
|
||||||
) {
|
|
||||||
self.queue_buffer_write(target_slot, offset, bytemuck::bytes_of(&bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_frame(&self, render_target: &RenderTarget) -> super::Frame {
|
|
||||||
let texture = render_target.frame_texture()
|
|
||||||
.expect("failed to create frame texture"); // should this be returned to the user?
|
|
||||||
|
|
||||||
Frame {
|
|
||||||
device: self.device.clone(),
|
|
||||||
queue: self.queue.clone(),
|
|
||||||
texture,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,12 +12,6 @@ pub use passes::*;
|
||||||
mod slot_desc;
|
mod slot_desc;
|
||||||
pub use slot_desc::*;
|
pub use slot_desc::*;
|
||||||
|
|
||||||
mod context;
|
|
||||||
pub use context::*;
|
|
||||||
|
|
||||||
mod render_target;
|
|
||||||
pub use render_target::*;
|
|
||||||
|
|
||||||
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 wgpu::ComputePass;
|
||||||
|
@ -114,27 +108,32 @@ pub struct PipelineResource {
|
||||||
pub bg_layout_name_lookup: HashMap<String, u32>,
|
pub bg_layout_name_lookup: HashMap<String, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct RenderTarget {
|
||||||
|
pub surface: wgpu::Surface,
|
||||||
|
pub surface_config: wgpu::SurfaceConfiguration,
|
||||||
|
pub current_texture: Option<wgpu::SurfaceTexture>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RenderGraph {
|
pub struct RenderGraph {
|
||||||
device: Arc<wgpu::Device>,
|
device: Rc<wgpu::Device>,
|
||||||
queue: Arc<wgpu::Queue>,
|
queue: Rc<wgpu::Queue>,
|
||||||
slots: FxHashMap<RenderGraphLabelValue, ResourcedSlot>,
|
slots: FxHashMap<RenderGraphLabelValue, ResourcedSlot>,
|
||||||
nodes: FxHashMap<RenderGraphLabelValue, PassEntry>,
|
nodes: FxHashMap<RenderGraphLabelValue, PassEntry>,
|
||||||
sub_graphs: FxHashMap<RenderGraphLabelValue, RenderGraph>,
|
|
||||||
bind_groups: FxHashMap<RenderGraphLabelValue, BindGroupEntry>,
|
bind_groups: FxHashMap<RenderGraphLabelValue, BindGroupEntry>,
|
||||||
/// A directed graph used to determine dependencies of nodes.
|
/// A directed graph describing the execution path of the RenderGraph
|
||||||
node_graph: petgraph::matrix_graph::DiMatrix<RenderGraphLabelValue, (), Option<()>, usize>,
|
execution_graph: petgraph::matrix_graph::DiMatrix<RenderGraphLabelValue, (), Option<()>, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraph {
|
impl RenderGraph {
|
||||||
pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>) -> Self {
|
pub fn new(device: Rc<wgpu::Device>, queue: Rc<wgpu::Queue>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
slots: Default::default(),
|
slots: Default::default(),
|
||||||
nodes: Default::default(),
|
nodes: Default::default(),
|
||||||
sub_graphs: Default::default(),
|
|
||||||
bind_groups: Default::default(),
|
bind_groups: Default::default(),
|
||||||
node_graph: Default::default(),
|
execution_graph: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +152,7 @@ impl RenderGraph {
|
||||||
/// not change. The IDs of output slots do stay the same.
|
/// not change. The IDs of output slots do stay the same.
|
||||||
/// 3. Ensuring that no two slots share the same ID when the names do not match.
|
/// 3. Ensuring that no two slots share the same ID when the names do not match.
|
||||||
#[instrument(skip(self, pass), level = "debug")]
|
#[instrument(skip(self, pass), level = "debug")]
|
||||||
pub fn add_node<P: Node>(&mut self, label: impl RenderGraphLabel, mut pass: P) {
|
pub fn add_pass<P: Node>(&mut self, label: impl RenderGraphLabel, mut pass: P) {
|
||||||
let mut desc = pass.desc(self);
|
let mut desc = pass.desc(self);
|
||||||
|
|
||||||
// collect all the slots of the pass
|
// collect all the slots of the pass
|
||||||
|
@ -204,7 +203,7 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
let label: RenderGraphLabelValue = label.into();
|
let label: RenderGraphLabelValue = label.into();
|
||||||
let index = self.node_graph.add_node(label.clone());
|
let index = self.execution_graph.add_node(label.clone());
|
||||||
|
|
||||||
self.nodes.insert(
|
self.nodes.insert(
|
||||||
label,
|
label,
|
||||||
|
@ -240,9 +239,9 @@ impl RenderGraph {
|
||||||
.as_compute_pipeline_descriptor()
|
.as_compute_pipeline_descriptor()
|
||||||
.expect("got render pipeline descriptor in a compute pass"),
|
.expect("got render pipeline descriptor in a compute pass"),
|
||||||
)),
|
)),
|
||||||
NodeType::Presenter | NodeType::Node | NodeType::Graph => {
|
NodeType::Presenter | NodeType::Node => {
|
||||||
panic!("Present or Node RenderGraph passes should not have a pipeline descriptor!");
|
panic!("Present or Node RenderGraph passes should not have a pipeline descriptor!");
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
drop(desc);
|
drop(desc);
|
||||||
|
@ -259,27 +258,16 @@ impl RenderGraph {
|
||||||
|
|
||||||
#[instrument(skip(self, world))]
|
#[instrument(skip(self, world))]
|
||||||
pub fn prepare(&mut self, world: &mut World) {
|
pub fn prepare(&mut self, world: &mut World) {
|
||||||
|
// prepare all passes
|
||||||
let mut buffer_writes = VecDeque::<GraphBufferWrite>::new();
|
let mut buffer_writes = VecDeque::<GraphBufferWrite>::new();
|
||||||
// reserve some buffer writes. not all nodes write so half the amount of them is probably
|
// reserve some buffer writes. not all nodes write so half the amount of them is probably
|
||||||
// fine.
|
// fine.
|
||||||
buffer_writes.reserve(self.nodes.len() / 2);
|
buffer_writes.reserve(self.nodes.len() / 2);
|
||||||
|
|
||||||
let mut sorted: VecDeque<RenderGraphLabelValue> = petgraph::algo::toposort(&self.node_graph, None)
|
for (label, pass) in &mut self.nodes {
|
||||||
.expect("RenderGraph had cycled!")
|
let mut context = RenderGraphContext::new(&self.device, &self.queue, None, label.clone());
|
||||||
.iter()
|
let mut inner = pass.inner.borrow_mut();
|
||||||
.map(|i| self.node_graph[i.clone()].clone())
|
inner.prepare(world, &mut context);
|
||||||
.collect();
|
|
||||||
|
|
||||||
while let Some(pass_label) = sorted.pop_front() {
|
|
||||||
let pass = self.nodes.get(&pass_label).unwrap();
|
|
||||||
let device = self.device.clone();
|
|
||||||
let queue = self.queue.clone();
|
|
||||||
|
|
||||||
let inner = pass.inner.clone();
|
|
||||||
let mut inner = inner.borrow_mut();
|
|
||||||
|
|
||||||
let mut context = RenderGraphContext::new(device, queue, None, pass_label.clone());
|
|
||||||
inner.prepare(self, world, &mut context);
|
|
||||||
buffer_writes.append(&mut context.buffer_writes);
|
buffer_writes.append(&mut context.buffer_writes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,10 +296,10 @@ impl RenderGraph {
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
pub fn render(&mut self) {
|
pub fn render(&mut self) {
|
||||||
let mut sorted: VecDeque<RenderGraphLabelValue> = petgraph::algo::toposort(&self.node_graph, None)
|
let mut sorted: VecDeque<RenderGraphLabelValue> = petgraph::algo::toposort(&self.execution_graph, None)
|
||||||
.expect("RenderGraph had cycled!")
|
.expect("RenderGraph had cycled!")
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| self.node_graph[i.clone()].clone())
|
.map(|i| self.execution_graph[i.clone()].clone())
|
||||||
.collect();
|
.collect();
|
||||||
//debug!("Render graph execution order: {:?}", sorted);
|
//debug!("Render graph execution order: {:?}", sorted);
|
||||||
|
|
||||||
|
@ -340,7 +328,7 @@ impl RenderGraph {
|
||||||
// clone of the Rc's is required to appease the borrow checker
|
// clone of the Rc's is required to appease the borrow checker
|
||||||
let device = self.device.clone();
|
let device = self.device.clone();
|
||||||
let queue = self.queue.clone();
|
let queue = self.queue.clone();
|
||||||
let mut context = RenderGraphContext::new(device, queue, encoder, pass_label.clone());
|
let mut context = RenderGraphContext::new(&device, &queue, encoder, pass_label.clone());
|
||||||
|
|
||||||
// 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.ty == NodeType::Presenter {
|
if pass_desc.ty == NodeType::Presenter {
|
||||||
|
@ -424,23 +412,23 @@ impl RenderGraph {
|
||||||
.iter()
|
.iter()
|
||||||
.find(|p| *p.0 == from)
|
.find(|p| *p.0 == from)
|
||||||
.map(|p| p.1.graph_index)
|
.map(|p| p.1.graph_index)
|
||||||
.expect("Failed to find from node");
|
.expect("Failed to find from pass");
|
||||||
let to_idx = self
|
let to_idx = self
|
||||||
.nodes
|
.nodes
|
||||||
.iter()
|
.iter()
|
||||||
.find(|p| *p.0 == to)
|
.find(|p| *p.0 == to)
|
||||||
.map(|p| p.1.graph_index)
|
.map(|p| p.1.graph_index)
|
||||||
.expect("Failed to find to node");
|
.expect("Failed to find to pass");
|
||||||
|
|
||||||
debug_assert_ne!(from_idx, to_idx, "cannot add edges between the same node");
|
debug_assert_ne!(from_idx, to_idx, "cannot add edges between the same node");
|
||||||
|
|
||||||
self.node_graph.add_edge(from_idx, to_idx, ());
|
self.execution_graph.add_edge(from_idx, to_idx, ());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Utility method for setting the bind groups for a node.
|
/// Utility method for setting the bind groups for a pass.
|
||||||
///
|
///
|
||||||
/// The parameter `bind_groups` can be used to specify the labels of a bind group, and the
|
/// The parameter `bind_groups` can be used to specify the labels of a bind group, and the
|
||||||
/// index of the bind group in the pipeline for the node. If a bind group of the provided
|
/// index of the bind group in the pipeline for the pass. If a bind group of the provided
|
||||||
/// name is not found in the graph, a panic will occur.
|
/// name is not found in the graph, a panic will occur.
|
||||||
///
|
///
|
||||||
/// # Example:
|
/// # Example:
|
||||||
|
@ -474,33 +462,88 @@ impl RenderGraph {
|
||||||
pass.set_bind_group(*index, bg, &[]);
|
pass.set_bind_group(*index, bg, &[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn sub_graph_mut<L: Into<RenderGraphLabelValue>>(&mut self, label: L) -> Option<&mut RenderGraph> {
|
/// A queued write to a GPU buffer targeting a graph slot.
|
||||||
self.sub_graphs.get_mut(&label.into())
|
pub(crate) struct GraphBufferWrite {
|
||||||
|
/// The name of the slot that has the resource that will be written
|
||||||
|
target_slot: RenderGraphLabelValue,
|
||||||
|
offset: u64,
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct RenderGraphContext<'a> {
|
||||||
|
/// The [`wgpu::CommandEncoder`] used to encode GPU operations.
|
||||||
|
///
|
||||||
|
/// This is `None` during the `prepare` stage.
|
||||||
|
pub encoder: Option<wgpu::CommandEncoder>,
|
||||||
|
/// The gpu device that is being used.
|
||||||
|
pub device: &'a wgpu::Device,
|
||||||
|
pub queue: &'a wgpu::Queue,
|
||||||
|
pub(crate) buffer_writes: VecDeque<GraphBufferWrite>,
|
||||||
|
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
|
||||||
|
/// The label of this Node.
|
||||||
|
pub label: RenderGraphLabelValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> RenderGraphContext<'a> {
|
||||||
|
pub(crate) fn new(device: &'a wgpu::Device, queue: &'a wgpu::Queue, encoder: Option<wgpu::CommandEncoder>, label: RenderGraphLabelValue) -> Self {
|
||||||
|
Self {
|
||||||
|
encoder,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
buffer_writes: Default::default(),
|
||||||
|
renderpass_desc: vec![],
|
||||||
|
label,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SubGraphNode(RenderGraphLabelValue);
|
pub fn begin_render_pass(
|
||||||
|
&'a mut self,
|
||||||
impl Node for SubGraphNode {
|
desc: wgpu::RenderPassDescriptor<'a, 'a>,
|
||||||
fn desc<'a, 'b>(&'a mut self, _: &'b mut RenderGraph) -> NodeDesc {
|
) -> wgpu::RenderPass {
|
||||||
NodeDesc::new(NodeType::Graph, None, vec![])
|
self.encoder
|
||||||
|
.as_mut()
|
||||||
|
.expect(
|
||||||
|
"RenderGraphContext is missing a command encoder. This is likely \
|
||||||
|
because you are trying to run render commands in the prepare stage.",
|
||||||
|
)
|
||||||
|
.begin_render_pass(&desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(&mut self, graph: &mut RenderGraph, world: &mut World, _: &mut RenderGraphContext) {
|
pub fn begin_compute_pass(&mut self, desc: &wgpu::ComputePassDescriptor) -> wgpu::ComputePass {
|
||||||
let sg = graph.sub_graph_mut(self.0.clone())
|
self.encoder
|
||||||
.unwrap_or_else(|| panic!("failed to find sub graph for SubGraphNode: {:?}", self.0));
|
.as_mut()
|
||||||
sg.prepare(world);
|
.expect(
|
||||||
|
"RenderGraphContext is missing a command encoder. This is likely \
|
||||||
|
because you are trying to run render commands in the prepare stage.",
|
||||||
|
)
|
||||||
|
.begin_compute_pass(desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(
|
/// Queue a data write to a buffer at that is contained in `target_slot`.
|
||||||
|
///
|
||||||
|
/// This does not submit the data to the GPU immediately, or add it to the `wgpu::Queue`. The
|
||||||
|
/// data will be submitted to the GPU queue right after the prepare stage for all passes
|
||||||
|
/// is ran.
|
||||||
|
#[instrument(skip(self, bytes), level="trace", fields(size = bytes.len()))]
|
||||||
|
pub fn queue_buffer_write(&mut self, target_slot: impl RenderGraphLabel, offset: u64, bytes: &[u8]) {
|
||||||
|
self.buffer_writes.push_back(GraphBufferWrite {
|
||||||
|
target_slot: target_slot.into(),
|
||||||
|
offset,
|
||||||
|
bytes: bytes.to_vec(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Queue a data write of a type that to a buffer at that is contained in `target_slot`.
|
||||||
|
#[instrument(skip(self, bytes), level="trace", fields(size = std::mem::size_of::<T>()))]
|
||||||
|
pub fn queue_buffer_write_with<T: bytemuck::NoUninit>(
|
||||||
&mut self,
|
&mut self,
|
||||||
graph: &mut RenderGraph,
|
target_slot: impl RenderGraphLabel,
|
||||||
_: &NodeDesc,
|
offset: u64,
|
||||||
_: &mut RenderGraphContext,
|
bytes: T,
|
||||||
) {
|
) {
|
||||||
let sg = graph.sub_graph_mut(self.0.clone())
|
self.queue_buffer_write(target_slot, offset, bytemuck::bytes_of(&bytes));
|
||||||
.unwrap_or_else(|| panic!("failed to find sub graph for SubGraphNode: {:?}", self.0));
|
|
||||||
sg.render();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use lyra_ecs::World;
|
||||||
|
|
||||||
use crate::render::resource::PipelineDescriptor;
|
use crate::render::resource::PipelineDescriptor;
|
||||||
|
|
||||||
use super::{Frame, RenderGraph, RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, RenderTarget};
|
use super::{RenderGraph, RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, RenderTarget};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||||
pub enum NodeType {
|
pub enum NodeType {
|
||||||
|
@ -18,8 +18,6 @@ pub enum NodeType {
|
||||||
Render,
|
Render,
|
||||||
/// A node that presents render results to a render target.
|
/// A node that presents render results to a render target.
|
||||||
Presenter,
|
Presenter,
|
||||||
/// A node that represents a sub-graph.
|
|
||||||
Graph,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeType {
|
impl NodeType {
|
||||||
|
@ -30,7 +28,6 @@ impl NodeType {
|
||||||
NodeType::Compute => true,
|
NodeType::Compute => true,
|
||||||
NodeType::Render => true,
|
NodeType::Render => true,
|
||||||
NodeType::Presenter => false,
|
NodeType::Presenter => false,
|
||||||
NodeType::Graph => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,11 +40,10 @@ pub enum SlotType {
|
||||||
Texture,
|
Texture,
|
||||||
Buffer,
|
Buffer,
|
||||||
RenderTarget,
|
RenderTarget,
|
||||||
Frame,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The value of a slot in a [`Node`].
|
/// The value of a slot in a [`Node`].
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum SlotValue {
|
pub enum SlotValue {
|
||||||
/// This slot doesn't have any value
|
/// This slot doesn't have any value
|
||||||
None,
|
None,
|
||||||
|
@ -59,7 +55,6 @@ pub enum SlotValue {
|
||||||
Texture(Rc<wgpu::Texture>),
|
Texture(Rc<wgpu::Texture>),
|
||||||
Buffer(Rc<wgpu::Buffer>),
|
Buffer(Rc<wgpu::Buffer>),
|
||||||
RenderTarget(Rc<RefCell<RenderTarget>>),
|
RenderTarget(Rc<RefCell<RenderTarget>>),
|
||||||
Frame(Rc<RefCell<Option<Frame>>>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SlotValue {
|
impl SlotValue {
|
||||||
|
@ -86,14 +81,6 @@ impl SlotValue {
|
||||||
pub fn as_render_target_mut(&mut self) -> Option<RefMut<RenderTarget>> {
|
pub fn as_render_target_mut(&mut self) -> Option<RefMut<RenderTarget>> {
|
||||||
bind_match!(self, Self::RenderTarget(v) => v.borrow_mut())
|
bind_match!(self, Self::RenderTarget(v) => v.borrow_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_frame(&self) -> Option<Ref<Option<Frame>>> {
|
|
||||||
bind_match!(self, Self::Frame(v) => v.borrow())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_frame_mut(&mut self) -> Option<RefMut<Option<Frame>>> {
|
|
||||||
bind_match!(self, Self::Frame(v) => v.borrow_mut())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum SlotAttribute {
|
pub enum SlotAttribute {
|
||||||
|
@ -361,7 +348,7 @@ pub trait Node: 'static {
|
||||||
///
|
///
|
||||||
/// This phase runs before `execute` and is meant to be used to collect data from the World
|
/// This phase runs before `execute` and is meant to be used to collect data from the World
|
||||||
/// and write to GPU buffers.
|
/// and write to GPU buffers.
|
||||||
fn prepare(&mut self, graph: &mut RenderGraph, world: &mut World, context: &mut RenderGraphContext);
|
fn prepare(&mut self, world: &mut World, context: &mut RenderGraphContext);
|
||||||
|
|
||||||
/// Execute the node.
|
/// Execute the node.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use glam::UVec2;
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
|
@ -8,7 +9,8 @@ use crate::{
|
||||||
render::{
|
render::{
|
||||||
camera::{CameraUniform, RenderCamera},
|
camera::{CameraUniform, RenderCamera},
|
||||||
graph::{
|
graph::{
|
||||||
Node, NodeDesc, NodeSlot, NodeType, RenderGraph, RenderGraphContext, RenderTarget, SlotAttribute, SlotType, SlotValue
|
RenderGraphContext, Node, NodeDesc, NodeSlot,
|
||||||
|
NodeType, RenderTarget, SlotAttribute, SlotType, SlotValue,
|
||||||
},
|
},
|
||||||
render_buffer::BufferWrapper, texture::RenderTexture,
|
render_buffer::BufferWrapper, texture::RenderTexture,
|
||||||
},
|
},
|
||||||
|
@ -26,7 +28,6 @@ pub enum BasePassSlots {
|
||||||
MainRenderTarget,
|
MainRenderTarget,
|
||||||
WindowTextureView,
|
WindowTextureView,
|
||||||
DepthTextureView,
|
DepthTextureView,
|
||||||
Frame,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Supplies some basic things other passes needs.
|
/// Supplies some basic things other passes needs.
|
||||||
|
@ -43,11 +44,15 @@ pub struct BasePass {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasePass {
|
impl BasePass {
|
||||||
pub fn new(render_target: RenderTarget) -> Self {
|
pub fn new(surface: wgpu::Surface, surface_config: wgpu::SurfaceConfiguration) -> Self {
|
||||||
let size = render_target.size();
|
let size = glam::UVec2::new(surface_config.width, surface_config.height);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
temp_render_target: Some(render_target),
|
temp_render_target: Some(RenderTarget {
|
||||||
|
surface,
|
||||||
|
surface_config,
|
||||||
|
current_texture: None,
|
||||||
|
}),
|
||||||
screen_size: size,
|
screen_size: size,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
@ -60,7 +65,10 @@ impl Node for BasePass {
|
||||||
graph: &mut crate::render::graph::RenderGraph,
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
) -> crate::render::graph::NodeDesc {
|
) -> crate::render::graph::NodeDesc {
|
||||||
let render_target = self.temp_render_target.take().unwrap();
|
let render_target = self.temp_render_target.take().unwrap();
|
||||||
self.screen_size = render_target.size();
|
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()
|
let (screen_size_bgl, screen_size_bg, screen_size_buf, _) = BufferWrapper::builder()
|
||||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||||
|
@ -83,7 +91,7 @@ impl Node for BasePass {
|
||||||
let camera_bg = Rc::new(camera_bg);
|
let camera_bg = Rc::new(camera_bg);
|
||||||
|
|
||||||
// create the depth texture using the utility struct, then take all the required fields
|
// create the depth texture using the utility struct, then take all the required fields
|
||||||
let mut depth_texture = RenderTexture::create_depth_texture(&graph.device(), self.screen_size, "depth_texture");
|
let mut depth_texture = RenderTexture::create_depth_texture(&graph.device(), &render_target.surface_config, "depth_texture");
|
||||||
depth_texture.create_bind_group(&graph.device);
|
depth_texture.create_bind_group(&graph.device);
|
||||||
|
|
||||||
let dt_bg_pair = depth_texture.bindgroup_pair.unwrap();
|
let dt_bg_pair = depth_texture.bindgroup_pair.unwrap();
|
||||||
|
@ -115,12 +123,6 @@ impl Node for BasePass {
|
||||||
render_target,
|
render_target,
|
||||||
)))),
|
)))),
|
||||||
});
|
});
|
||||||
desc.add_slot(NodeSlot {
|
|
||||||
ty: SlotType::Frame,
|
|
||||||
attribute: SlotAttribute::Output,
|
|
||||||
label: BasePassSlots::Frame.into(),
|
|
||||||
value: Some(SlotValue::Lazy),
|
|
||||||
});
|
|
||||||
desc.add_texture_view_slot(
|
desc.add_texture_view_slot(
|
||||||
BasePassSlots::WindowTextureView,
|
BasePassSlots::WindowTextureView,
|
||||||
SlotAttribute::Output,
|
SlotAttribute::Output,
|
||||||
|
@ -145,7 +147,7 @@ impl Node for BasePass {
|
||||||
desc
|
desc
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(&mut self, _graph: &mut RenderGraph, 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() {
|
if let Some(camera) = world.view_iter::<&mut CameraComponent>().next() {
|
||||||
let mut render_cam =
|
let mut render_cam =
|
||||||
RenderCamera::new(PhysicalSize::new(self.screen_size.x, self.screen_size.y));
|
RenderCamera::new(PhysicalSize::new(self.screen_size.x, self.screen_size.y));
|
||||||
|
@ -166,31 +168,28 @@ impl Node for BasePass {
|
||||||
let tv_slot = graph
|
let tv_slot = graph
|
||||||
.slot_value_mut(BasePassSlots::MainRenderTarget)
|
.slot_value_mut(BasePassSlots::MainRenderTarget)
|
||||||
.expect("somehow the main render target slot is missing");
|
.expect("somehow the main render target slot is missing");
|
||||||
let rt = tv_slot.as_render_target().unwrap();
|
let mut rt = tv_slot.as_render_target_mut().unwrap();
|
||||||
let rt_size = rt.size();
|
debug_assert!(
|
||||||
let frame = context.get_frame(&rt);
|
|
||||||
/* debug_assert!(
|
|
||||||
!rt.current_texture.is_some(),
|
!rt.current_texture.is_some(),
|
||||||
"main render target surface was not presented!"
|
"main render target surface was not presented!"
|
||||||
); */
|
);
|
||||||
|
|
||||||
// update the screen size buffer if the size changed.
|
// update the screen size buffer if the size changed.
|
||||||
if rt_size != self.screen_size {
|
if rt.surface_config.width != self.screen_size.x
|
||||||
self.screen_size = rt_size;
|
|| 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(BasePassSlots::ScreenSize, 0, self.screen_size)
|
context.queue_buffer_write_with(BasePassSlots::ScreenSize, 0, self.screen_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
let surface_tex = frame.texture();
|
let surface_tex = rt.surface.get_current_texture().unwrap();
|
||||||
let view = surface_tex
|
let view = surface_tex
|
||||||
|
.texture
|
||||||
.create_view(&wgpu::TextureViewDescriptor::default());
|
.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
|
drop(rt); // must be manually dropped for borrow checker when getting texture view slot
|
||||||
|
|
||||||
let frame_slot = graph
|
|
||||||
.slot_value_mut(BasePassSlots::Frame)
|
|
||||||
.expect("somehow the frame slot is missing");
|
|
||||||
*frame_slot = SlotValue::Frame(Rc::new(RefCell::new(Some(frame))));
|
|
||||||
|
|
||||||
// store the surface texture to the slot
|
// store the surface texture to the slot
|
||||||
let tv_slot = graph
|
let tv_slot = graph
|
||||||
.slot_value_mut(BasePassSlots::WindowTextureView)
|
.slot_value_mut(BasePassSlots::WindowTextureView)
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
|
||||||
|
|
||||||
use crate::render::graph::{Node, NodeDesc, NodeSlot, NodeType};
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, Hash, RenderGraphLabel)]
|
|
||||||
pub struct InitNodeLabel;
|
|
||||||
|
|
||||||
pub struct InitNode {
|
|
||||||
slots: Vec<NodeSlot>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Node for InitNode {
|
|
||||||
fn desc<'a, 'b>(&'a mut self, _: &'b mut crate::render::graph::RenderGraph) -> crate::render::graph::NodeDesc {
|
|
||||||
let mut desc = NodeDesc::new(NodeType::Node, None, vec![]);
|
|
||||||
// the slots can just be cloned since the slot attribute doesn't really matter much.
|
|
||||||
desc.slots = self.slots.clone();
|
|
||||||
|
|
||||||
desc
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prepare(&mut self, _: &mut crate::render::graph::RenderGraph, _: &mut lyra_ecs::World, _: &mut crate::render::graph::RenderGraphContext) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute(
|
|
||||||
&mut self,
|
|
||||||
_: &mut crate::render::graph::RenderGraph,
|
|
||||||
_: &crate::render::graph::NodeDesc,
|
|
||||||
_: &mut crate::render::graph::RenderGraphContext,
|
|
||||||
) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,8 @@ use lyra_game_derive::RenderGraphLabel;
|
||||||
|
|
||||||
use crate::render::{
|
use crate::render::{
|
||||||
graph::{
|
graph::{
|
||||||
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue
|
RenderGraphContext, Node, NodeDesc, NodeType, SlotAttribute,
|
||||||
|
SlotValue,
|
||||||
},
|
},
|
||||||
light::LightUniformBuffers,
|
light::LightUniformBuffers,
|
||||||
};
|
};
|
||||||
|
@ -57,10 +58,10 @@ impl Node for LightBasePass {
|
||||||
desc
|
desc
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(&mut self, _graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
|
fn prepare(&mut self, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
|
||||||
let tick = world.current_tick();
|
let tick = world.current_tick();
|
||||||
let lights = self.light_buffers.as_mut().unwrap();
|
let lights = self.light_buffers.as_mut().unwrap();
|
||||||
lights.update_lights(&context.queue, tick, world);
|
lights.update_lights(context.queue, tick, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(
|
fn execute(
|
||||||
|
|
|
@ -7,7 +7,7 @@ use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
use crate::render::{
|
use crate::render::{
|
||||||
graph::{
|
graph::{
|
||||||
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue
|
Node, NodeDesc, NodeType, RenderGraphContext, SlotAttribute, SlotValue
|
||||||
}, renderer::ScreenSize, resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader}
|
}, renderer::ScreenSize, resource::{ComputePipelineDescriptor, PipelineDescriptor, Shader}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,7 +51,8 @@ impl Node for LightCullComputePass {
|
||||||
.slot_value(BasePassSlots::MainRenderTarget)
|
.slot_value(BasePassSlots::MainRenderTarget)
|
||||||
.and_then(|s| s.as_render_target())
|
.and_then(|s| s.as_render_target())
|
||||||
.expect("missing main render target");
|
.expect("missing main render target");
|
||||||
self.workgroup_size = main_rt.size();
|
self.workgroup_size =
|
||||||
|
glam::UVec2::new(main_rt.surface_config.width, main_rt.surface_config.height);
|
||||||
|
|
||||||
// initialize some buffers with empty data
|
// initialize some buffers with empty data
|
||||||
let mut contents = Vec::<u8>::new();
|
let mut contents = Vec::<u8>::new();
|
||||||
|
@ -214,7 +215,7 @@ impl Node for LightCullComputePass {
|
||||||
desc
|
desc
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(&mut self, _graph: &mut RenderGraph, world: &mut World, context: &mut RenderGraphContext) {
|
fn prepare(&mut self, world: &mut World, context: &mut RenderGraphContext) {
|
||||||
context.queue_buffer_write_with(LightCullComputePassSlots::IndexCounterBuffer, 0, 0);
|
context.queue_buffer_write_with(LightCullComputePassSlots::IndexCounterBuffer, 0, 0);
|
||||||
|
|
||||||
let screen_size = world.get_resource::<ScreenSize>();
|
let screen_size = world.get_resource::<ScreenSize>();
|
||||||
|
|
|
@ -15,7 +15,8 @@ use wgpu::util::DeviceExt;
|
||||||
use crate::{
|
use crate::{
|
||||||
render::{
|
render::{
|
||||||
desc_buf_lay::DescVertexBufferLayout, graph::{
|
desc_buf_lay::DescVertexBufferLayout, graph::{
|
||||||
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext
|
RenderGraphContext, Node, NodeDesc,
|
||||||
|
NodeType,
|
||||||
}, material::{Material, MaterialUniform}, render_buffer::{BufferStorage, BufferWrapper}, render_job::RenderJob, resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState}, texture::RenderTexture, transform_buffer_storage::{TransformBuffers, TransformGroup}, vertex::Vertex
|
}, material::{Material, MaterialUniform}, render_buffer::{BufferStorage, BufferWrapper}, render_job::RenderJob, resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState}, texture::RenderTexture, transform_buffer_storage::{TransformBuffers, TransformGroup}, vertex::Vertex
|
||||||
},
|
},
|
||||||
DeltaTime,
|
DeltaTime,
|
||||||
|
@ -234,7 +235,7 @@ impl Node for MeshPass {
|
||||||
let main_rt = graph.slot_value(BasePassSlots::MainRenderTarget)
|
let main_rt = graph.slot_value(BasePassSlots::MainRenderTarget)
|
||||||
.and_then(|s| s.as_render_target())
|
.and_then(|s| s.as_render_target())
|
||||||
.expect("missing main render target");
|
.expect("missing main render target");
|
||||||
let surface_config_format = main_rt.format();
|
let surface_config_format = main_rt.surface_config.format;
|
||||||
drop(main_rt);
|
drop(main_rt);
|
||||||
|
|
||||||
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
|
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
|
||||||
|
@ -296,10 +297,10 @@ impl Node for MeshPass {
|
||||||
desc
|
desc
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, _graph, world, context))]
|
#[instrument(skip(self, world, context))]
|
||||||
fn prepare(&mut self, _graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
|
fn prepare(&mut self, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) {
|
||||||
let device = &context.device;
|
let device = context.device;
|
||||||
let queue = &context.queue;
|
let queue = context.queue;
|
||||||
let render_limits = device.limits();
|
let render_limits = device.limits();
|
||||||
|
|
||||||
let last_epoch = world.current_tick();
|
let last_epoch = world.current_tick();
|
||||||
|
@ -363,7 +364,7 @@ impl Node for MeshPass {
|
||||||
let transforms = self.transforms.as_mut().unwrap();
|
let transforms = self.transforms.as_mut().unwrap();
|
||||||
if transforms.needs_expand() {
|
if transforms.needs_expand() {
|
||||||
debug!("Expanding transform buffers");
|
debug!("Expanding transform buffers");
|
||||||
transforms.expand_buffers(&device);
|
transforms.expand_buffers(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,14 +373,14 @@ impl Node for MeshPass {
|
||||||
// if process mesh did not just create a new mesh, and the epoch
|
// if process mesh did not just create a new mesh, and the epoch
|
||||||
// shows that the scene has changed, verify that the mesh buffers
|
// shows that the scene has changed, verify that the mesh buffers
|
||||||
// dont need to be resent to the gpu.
|
// dont need to be resent to the gpu.
|
||||||
if !self.process_mesh(&device, &queue, entity, &*mesh, mesh_han.uuid())
|
if !self.process_mesh(device, queue, entity, &*mesh, mesh_han.uuid())
|
||||||
&& mesh_epoch == last_epoch {
|
&& mesh_epoch == last_epoch {
|
||||||
self.check_mesh_buffers(&device, &queue, &mesh_han);
|
self.check_mesh_buffers(device, queue, &mesh_han);
|
||||||
}
|
}
|
||||||
|
|
||||||
let transforms = self.transforms.as_mut().unwrap();
|
let transforms = self.transforms.as_mut().unwrap();
|
||||||
let group = TransformGroup::EntityRes(entity, mesh_han.uuid());
|
let group = TransformGroup::EntityRes(entity, mesh_han.uuid());
|
||||||
let transform_id = transforms.update_or_push(&device, &queue, &render_limits,
|
let transform_id = transforms.update_or_push(device, queue, &render_limits,
|
||||||
group, interp_transform.calculate_mat4(), glam::Mat3::from_quat(interp_transform.rotation));
|
group, interp_transform.calculate_mat4(), glam::Mat3::from_quat(interp_transform.rotation));
|
||||||
|
|
||||||
let material = mesh.material.as_ref().unwrap()
|
let material = mesh.material.as_ref().unwrap()
|
||||||
|
@ -404,15 +405,15 @@ impl Node for MeshPass {
|
||||||
// if process mesh did not just create a new mesh, and the epoch
|
// if process mesh did not just create a new mesh, and the epoch
|
||||||
// shows that the scene has changed, verify that the mesh buffers
|
// shows that the scene has changed, verify that the mesh buffers
|
||||||
// dont need to be resent to the gpu.
|
// dont need to be resent to the gpu.
|
||||||
if !self.process_mesh(&device, &queue, entity, &*mesh, mesh_han.uuid())
|
if !self.process_mesh(device, queue, entity, &*mesh, mesh_han.uuid())
|
||||||
&& scene_epoch == last_epoch {
|
&& scene_epoch == last_epoch {
|
||||||
self.check_mesh_buffers(&device, &queue, &mesh_han);
|
self.check_mesh_buffers(device, queue, &mesh_han);
|
||||||
}
|
}
|
||||||
|
|
||||||
let transforms = self.transforms.as_mut().unwrap();
|
let transforms = self.transforms.as_mut().unwrap();
|
||||||
let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
|
let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
|
||||||
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
|
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
|
||||||
let transform_id = transforms.update_or_push(&device, &queue, &render_limits,
|
let transform_id = transforms.update_or_push(device, queue, &render_limits,
|
||||||
group, mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) );
|
group, mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) );
|
||||||
|
|
||||||
let material = mesh.material.as_ref().unwrap()
|
let material = mesh.material.as_ref().unwrap()
|
||||||
|
@ -431,7 +432,7 @@ impl Node for MeshPass {
|
||||||
}
|
}
|
||||||
|
|
||||||
let transforms = self.transforms.as_mut().unwrap();
|
let transforms = self.transforms.as_mut().unwrap();
|
||||||
transforms.send_to_gpu(&queue);
|
transforms.send_to_gpu(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(
|
fn execute(
|
||||||
|
|
|
@ -12,6 +12,3 @@ pub use light_base::*;
|
||||||
|
|
||||||
mod present_pass;
|
mod present_pass;
|
||||||
pub use present_pass::*;
|
pub use present_pass::*;
|
||||||
|
|
||||||
mod init;
|
|
||||||
pub use init::*;
|
|
|
@ -2,14 +2,14 @@ use std::hash::Hash;
|
||||||
|
|
||||||
use lyra_game_derive::RenderGraphLabel;
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
|
|
||||||
use crate::render::graph::{Node, NodeDesc, NodeSlot, NodeType, RenderGraph, RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, SlotAttribute, SlotType};
|
use crate::render::graph::{RenderGraphContext, RenderGraphLabel, RenderGraphLabelValue, Node, NodeDesc, NodeSlot, NodeType, SlotAttribute, SlotType};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)]
|
#[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)]
|
||||||
pub struct PresentPassLabel(RenderGraphLabelValue);
|
pub struct PresentPassLabel(RenderGraphLabelValue);
|
||||||
|
|
||||||
impl PresentPassLabel {
|
impl PresentPassLabel {
|
||||||
pub fn new(frame_label: impl RenderGraphLabel) -> Self {
|
pub fn new(label: impl RenderGraphLabel) -> Self {
|
||||||
Self(frame_label.into())
|
Self(label.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,17 +22,10 @@ pub struct PresentPass {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PresentPass {
|
impl PresentPass {
|
||||||
/// Create a new PresentNode, presenting the frame at `frame_label`
|
pub fn new(render_target_slot: impl RenderGraphLabel) -> Self {
|
||||||
pub fn new(frame_label: impl RenderGraphLabel) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
//render_target_slot: render_target_slot.rc_clone(),
|
//render_target_slot: render_target_slot.rc_clone(),
|
||||||
label: PresentPassLabel::new(frame_label),
|
label: PresentPassLabel::new(render_target_slot),
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_node_label(present_pass_label: PresentPassLabel) -> Self {
|
|
||||||
Self {
|
|
||||||
label: present_pass_label,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +40,7 @@ impl Node for PresentPass {
|
||||||
|
|
||||||
desc.add_slot(
|
desc.add_slot(
|
||||||
NodeSlot {
|
NodeSlot {
|
||||||
ty: SlotType::Frame,
|
ty: SlotType::RenderTarget,
|
||||||
attribute: SlotAttribute::Input,
|
attribute: SlotAttribute::Input,
|
||||||
label: self.label.0.clone(),
|
label: self.label.0.clone(),
|
||||||
value: None,
|
value: None,
|
||||||
|
@ -57,17 +50,15 @@ impl Node for PresentPass {
|
||||||
desc
|
desc
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare(&mut self, _graph: &mut RenderGraph, _world: &mut lyra_ecs::World, _context: &mut RenderGraphContext) {
|
fn prepare(&mut self, _world: &mut lyra_ecs::World, _context: &mut RenderGraphContext) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::NodeDesc, _context: &mut crate::render::graph::RenderGraphContext) {
|
fn execute(&mut self, graph: &mut crate::render::graph::RenderGraph, _desc: &crate::render::graph::NodeDesc, _context: &mut crate::render::graph::RenderGraphContext) {
|
||||||
let slot = graph.slot_value_mut(self.label.0.clone())
|
let mut slot = graph.slot_value_mut(self.label.0.clone())
|
||||||
.unwrap_or_else(|| panic!("frame slot '{:?}' for PresentPass is missing", self.label.0))
|
.expect(&format!("render target slot '{:?}' for PresentPass is missing", self.label.0))
|
||||||
.as_frame_mut()
|
.as_render_target_mut().unwrap();
|
||||||
.unwrap()
|
let surf_tex = slot.current_texture.take().unwrap();
|
||||||
.take()
|
surf_tex.present();
|
||||||
.unwrap_or_else(|| panic!("frame '{:?}' was already presented", self.label.0));
|
|
||||||
slot.present();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,102 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::math;
|
|
||||||
|
|
||||||
enum RenderTargetInner {
|
|
||||||
Surface {
|
|
||||||
surface: wgpu::Surface,
|
|
||||||
config: wgpu::SurfaceConfiguration,
|
|
||||||
},
|
|
||||||
Texture {
|
|
||||||
texture: Arc<wgpu::Texture>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A render target that is a surface or a texture.
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct RenderTarget(RenderTargetInner);
|
|
||||||
|
|
||||||
impl From<wgpu::Texture> for RenderTarget {
|
|
||||||
fn from(value: wgpu::Texture) -> Self {
|
|
||||||
Self(RenderTargetInner::Texture { texture: Arc::new(value) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderTarget {
|
|
||||||
pub fn from_surface(surface: wgpu::Surface, config: wgpu::SurfaceConfiguration) -> Self {
|
|
||||||
Self(RenderTargetInner::Surface { surface, config })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn format(&self) -> wgpu::TextureFormat {
|
|
||||||
match &self.0 {
|
|
||||||
RenderTargetInner::Surface { config, .. } => config.format,
|
|
||||||
RenderTargetInner::Texture { texture } => texture.format(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size(&self) -> math::UVec2 {
|
|
||||||
match &self.0 {
|
|
||||||
RenderTargetInner::Surface { config, .. } => math::UVec2::new(config.width, config.height),
|
|
||||||
RenderTargetInner::Texture { texture } => {
|
|
||||||
let s = texture.size();
|
|
||||||
math::UVec2::new(s.width, s.height)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the frame texture of the [`RenderTarget`]
|
|
||||||
///
|
|
||||||
/// If this is target is a surface and the frame texture was already retrieved from the swap
|
|
||||||
/// chain, a [`wgpu::SurfaceError`] error will be returned.
|
|
||||||
pub fn frame_texture(&self) -> Result<FrameTexture, wgpu::SurfaceError> {
|
|
||||||
match &self.0 {
|
|
||||||
RenderTargetInner::Surface { surface, .. } => Ok(FrameTexture::Surface(surface.get_current_texture()?)),
|
|
||||||
RenderTargetInner::Texture { texture } => Ok(FrameTexture::Texture(texture.clone())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(&mut self, device: &wgpu::Device, new_size: math::UVec2) {
|
|
||||||
match &mut self.0 {
|
|
||||||
RenderTargetInner::Surface { surface, config } => {
|
|
||||||
config.width = new_size.x;
|
|
||||||
config.height = new_size.y;
|
|
||||||
surface.configure(device, config);
|
|
||||||
},
|
|
||||||
RenderTargetInner::Texture { texture } => {
|
|
||||||
let _ = texture;
|
|
||||||
todo!()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum FrameTexture {
|
|
||||||
Surface(wgpu::SurfaceTexture),
|
|
||||||
Texture(Arc<wgpu::Texture>),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct Frame {
|
|
||||||
pub(crate) device: Arc<wgpu::Device>,
|
|
||||||
pub(crate) queue: Arc<wgpu::Queue>,
|
|
||||||
pub(crate) texture: FrameTexture,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Frame {
|
|
||||||
pub fn texture(&self) -> &wgpu::Texture {
|
|
||||||
match &self.texture {
|
|
||||||
FrameTexture::Surface(s) => &s.texture,
|
|
||||||
FrameTexture::Texture(t) => t,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Present the frame
|
|
||||||
///
|
|
||||||
/// If this frame is from a surface, it will be present, else nothing will happen.
|
|
||||||
pub fn present(self) {
|
|
||||||
match self.texture {
|
|
||||||
FrameTexture::Surface(s) => s.present(),
|
|
||||||
FrameTexture::Texture(_) => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -17,8 +17,8 @@ pub(crate) struct LightIndicesGridBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct LightCullCompute {
|
pub(crate) struct LightCullCompute {
|
||||||
device: Arc<wgpu::Device>,
|
device: Rc<wgpu::Device>,
|
||||||
queue: Arc<wgpu::Queue>,
|
queue: Rc<wgpu::Queue>,
|
||||||
pipeline: ComputePipeline,
|
pipeline: ComputePipeline,
|
||||||
pub light_indices_grid: LightIndicesGridBuffer,
|
pub light_indices_grid: LightIndicesGridBuffer,
|
||||||
screen_size_buffer: BufferWrapper,
|
screen_size_buffer: BufferWrapper,
|
||||||
|
@ -153,7 +153,7 @@ impl LightCullCompute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>, screen_size: PhysicalSize<u32>, lights_buffers: &LightUniformBuffers, camera_buffers: &BufferWrapper, depth_texture: &mut RenderTexture) -> Self {
|
pub fn new(device: Rc<wgpu::Device>, queue: Rc<wgpu::Queue>, screen_size: PhysicalSize<u32>, lights_buffers: &LightUniformBuffers, camera_buffers: &BufferWrapper, depth_texture: &mut RenderTexture) -> Self {
|
||||||
let screen_size_buffer = BufferWrapper::builder()
|
let screen_size_buffer = BufferWrapper::builder()
|
||||||
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
|
||||||
.label_prefix("ScreenSize")
|
.label_prefix("ScreenSize")
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
use tracing::{debug, instrument, warn};
|
use tracing::{debug, instrument, warn};
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshesPassLabel, PresentPass, PresentPassLabel, RenderTarget};
|
use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshesPassLabel, PresentPass, PresentPassLabel};
|
||||||
|
|
||||||
use super::graph::RenderGraph;
|
use super::graph::RenderGraph;
|
||||||
use super::{resource::RenderPipeline, render_job::RenderJob};
|
use super::{resource::RenderPipeline, render_job::RenderJob};
|
||||||
|
|
||||||
use crate::math;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct ScreenSize(glam::UVec2);
|
pub struct ScreenSize(glam::UVec2);
|
||||||
|
|
||||||
|
@ -46,8 +45,8 @@ pub trait RenderPass {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BasicRenderer {
|
pub struct BasicRenderer {
|
||||||
pub device: Arc<wgpu::Device>, // device does not need to be mutable, no need for refcell
|
pub device: Rc<wgpu::Device>, // device does not need to be mutable, no need for refcell
|
||||||
pub queue: Arc<wgpu::Queue>,
|
pub queue: Rc<wgpu::Queue>,
|
||||||
pub size: winit::dpi::PhysicalSize<u32>,
|
pub size: winit::dpi::PhysicalSize<u32>,
|
||||||
pub window: Arc<Window>,
|
pub window: Arc<Window>,
|
||||||
|
|
||||||
|
@ -120,38 +119,35 @@ impl BasicRenderer {
|
||||||
};
|
};
|
||||||
surface.configure(&device, &config);
|
surface.configure(&device, &config);
|
||||||
|
|
||||||
let device = Arc::new(device);
|
let device = Rc::new(device);
|
||||||
let queue = Arc::new(queue);
|
let queue = Rc::new(queue);
|
||||||
|
|
||||||
let mut g = RenderGraph::new(device.clone(), queue.clone());
|
let mut g = RenderGraph::new(device.clone(), queue.clone());
|
||||||
|
|
||||||
let surface_target = RenderTarget::from_surface(surface, config);
|
|
||||||
|
|
||||||
debug!("Adding base pass");
|
debug!("Adding base pass");
|
||||||
g.add_node(BasePassLabel, BasePass::new(surface_target));
|
g.add_pass(BasePassLabel, BasePass::new(surface, config));
|
||||||
debug!("Adding light base pass");
|
debug!("Adding light base pass");
|
||||||
g.add_node(LightBasePassLabel, LightBasePass::new());
|
g.add_pass(LightBasePassLabel, LightBasePass::new());
|
||||||
debug!("Adding light cull compute pass");
|
debug!("Adding light cull compute pass");
|
||||||
g.add_node(LightCullComputePassLabel, LightCullComputePass::new(size));
|
g.add_pass(LightCullComputePassLabel, LightCullComputePass::new(size));
|
||||||
//debug!("Adding triangle pass");
|
//debug!("Adding triangle pass");
|
||||||
//g.add_node(TrianglePass::new());
|
//g.add_pass(TrianglePass::new());
|
||||||
|
|
||||||
debug!("Adding mesh pass");
|
debug!("Adding mesh pass");
|
||||||
g.add_node(MeshesPassLabel, MeshPass::new());
|
g.add_pass(MeshesPassLabel, MeshPass::new());
|
||||||
|
|
||||||
debug!("Adding present pass");
|
debug!("Adding present pass");
|
||||||
let present_pass_label = PresentPassLabel::new(BasePassSlots::Frame);
|
let p = PresentPass::new(BasePassSlots::MainRenderTarget);
|
||||||
let p = PresentPass::from_node_label(present_pass_label.clone());
|
g.add_pass(p.label.clone(), p);
|
||||||
g.add_node(p.label.clone(), p);
|
|
||||||
|
|
||||||
g.add_edge(BasePassLabel, LightBasePassLabel);
|
g.add_edge(BasePassLabel, LightBasePassLabel);
|
||||||
g.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
g.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
||||||
g.add_edge(BasePassLabel, MeshesPassLabel);
|
g.add_edge(BasePassLabel, MeshesPassLabel);
|
||||||
|
|
||||||
// make sure that present runs last
|
// make sure that present runs last
|
||||||
g.add_edge(BasePassLabel, present_pass_label.clone());
|
g.add_edge(BasePassLabel, PresentPassLabel::new(BasePassSlots::MainRenderTarget));
|
||||||
g.add_edge(LightCullComputePassLabel, present_pass_label.clone());
|
g.add_edge(LightCullComputePassLabel, PresentPassLabel::new(BasePassSlots::MainRenderTarget));
|
||||||
g.add_edge(MeshesPassLabel, present_pass_label.clone());
|
g.add_edge(MeshesPassLabel, PresentPassLabel::new(BasePassSlots::MainRenderTarget));
|
||||||
|
|
||||||
g.setup(&device);
|
g.setup(&device);
|
||||||
|
|
||||||
|
@ -195,10 +191,9 @@ impl Renderer for BasicRenderer {
|
||||||
// update surface config and the surface
|
// update surface config and the surface
|
||||||
let mut rt = self.graph.slot_value_mut(BasePassSlots::MainRenderTarget)
|
let mut rt = self.graph.slot_value_mut(BasePassSlots::MainRenderTarget)
|
||||||
.unwrap().as_render_target_mut().unwrap();
|
.unwrap().as_render_target_mut().unwrap();
|
||||||
rt.resize(&self.device, math::UVec2::new(new_size.width, new_size.height));
|
rt.surface_config.width = new_size.width;
|
||||||
/* rt.surface_config.width = new_size.width;
|
|
||||||
rt.surface_config.height = new_size.height;
|
rt.surface_config.height = new_size.height;
|
||||||
rt.surface.configure(&self.device, &rt.surface_config); */
|
rt.surface.configure(&self.device, &rt.surface_config);
|
||||||
|
|
||||||
// update screen size resource in ecs
|
// update screen size resource in ecs
|
||||||
let mut world_ss = world.get_resource_mut::<ScreenSize>();
|
let mut world_ss = world.get_resource_mut::<ScreenSize>();
|
||||||
|
|
|
@ -254,10 +254,10 @@ impl RenderTexture {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_depth_texture(device: &wgpu::Device, size: crate::math::UVec2, label: &str) -> Self {
|
pub fn create_depth_texture(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, label: &str) -> Self {
|
||||||
let size = wgpu::Extent3d {
|
let size = wgpu::Extent3d {
|
||||||
width: size.x,
|
width: config.width,
|
||||||
height: size.y,
|
height: config.height,
|
||||||
depth_or_array_layers: 1,
|
depth_or_array_layers: 1,
|
||||||
};
|
};
|
||||||
let desc = wgpu::TextureDescriptor {
|
let desc = wgpu::TextureDescriptor {
|
||||||
|
|
Loading…
Reference in New Issue