Implement a Render Graph #16
|
@ -0,0 +1,29 @@
|
||||||
|
# The run config is used for both run mode and debug mode
|
||||||
|
|
||||||
|
[[configs]]
|
||||||
|
# the name of this task
|
||||||
|
name = "Example 'simple_scene'"
|
||||||
|
|
||||||
|
# the type of the debugger. If not set, it can't be debugged but can still be run
|
||||||
|
type = "lldb"
|
||||||
|
|
||||||
|
# the program to run
|
||||||
|
program = "../../target/debug/simple_scene"
|
||||||
|
|
||||||
|
# the program arguments, e.g. args = ["arg1", "arg2"], optional
|
||||||
|
# args = []
|
||||||
|
|
||||||
|
# current working directory, optional
|
||||||
|
cwd = "${workspace}/examples/simple_scene"
|
||||||
|
|
||||||
|
# environment variables, optional
|
||||||
|
# [configs.env]
|
||||||
|
# VAR1 = "VAL1"
|
||||||
|
# VAR2 = "VAL2"
|
||||||
|
|
||||||
|
# task to run before the run/debug session is started, optional
|
||||||
|
[configs.prelaunch]
|
||||||
|
program = "cargo"
|
||||||
|
args = [
|
||||||
|
"build",
|
||||||
|
]
|
|
@ -3065,6 +3065,16 @@ version = "0.3.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple_scene"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"async-std",
|
||||||
|
"lyra-engine",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "simple_scene"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
lyra-engine = { path = "../../", features = ["tracy"] }
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
async-std = "1.12.0"
|
||||||
|
tracing = "0.1.37"
|
|
@ -0,0 +1,59 @@
|
||||||
|
---Return the userdata's name from its metatable
|
||||||
|
---@param val userdata
|
||||||
|
---@return string
|
||||||
|
function udname(val)
|
||||||
|
return getmetatable(val).__name
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_init()
|
||||||
|
local cube = world:request_res("../assets/cube-texture-embedded.gltf")
|
||||||
|
print("Loaded textured cube (" .. udname(cube) .. ")")
|
||||||
|
|
||||||
|
cube:wait_until_loaded()
|
||||||
|
local scenes = cube:scenes()
|
||||||
|
local cube_scene = scenes[1]
|
||||||
|
|
||||||
|
local pos = Transform.from_translation(Vec3.new(0, 0, -8.0))
|
||||||
|
|
||||||
|
local e = world:spawn(pos, cube_scene)
|
||||||
|
print("spawned entity " .. tostring(e))
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[ function on_first()
|
||||||
|
print("Lua's first function was called")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_pre_update()
|
||||||
|
print("Lua's pre-update function was called")
|
||||||
|
end ]]
|
||||||
|
|
||||||
|
function on_update()
|
||||||
|
--[[ ---@type number
|
||||||
|
local dt = world:resource(DeltaTime)
|
||||||
|
local act = world:resource(ActionHandler)
|
||||||
|
---@type number
|
||||||
|
local move_objs = act:get_axis("ObjectsMoveUpDown")
|
||||||
|
|
||||||
|
world:view(function (t)
|
||||||
|
if move_objs ~= nil then
|
||||||
|
t:translate(0, move_objs * 0.35 * dt, 0)
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
end, Transform) ]]
|
||||||
|
|
||||||
|
---@type number
|
||||||
|
local dt = world:resource(DeltaTime)
|
||||||
|
|
||||||
|
world:view(function (t)
|
||||||
|
t:translate(0, 0.15 * dt, 0)
|
||||||
|
return t
|
||||||
|
end, Transform)
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[ function on_post_update()
|
||||||
|
print("Lua's post-update function was called")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_last()
|
||||||
|
print("Lua's last function was called")
|
||||||
|
end ]]
|
|
@ -0,0 +1,149 @@
|
||||||
|
use lyra_engine::{
|
||||||
|
assets::{gltf::Gltf, ResourceManager},
|
||||||
|
game::Game,
|
||||||
|
input::{
|
||||||
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
|
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
||||||
|
},
|
||||||
|
math::{self, Transform, Vec3},
|
||||||
|
render::light::directional::DirectionalLight,
|
||||||
|
scene::{
|
||||||
|
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
||||||
|
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
|
||||||
|
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() {
|
||||||
|
let action_handler_plugin = |game: &mut Game| {
|
||||||
|
let action_handler = ActionHandler::builder()
|
||||||
|
.add_layout(LayoutId::from(0))
|
||||||
|
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_LOOK_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis))
|
||||||
|
.add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis))
|
||||||
|
.add_action("Debug", Action::new(ActionKind::Button))
|
||||||
|
.add_mapping(
|
||||||
|
ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
||||||
|
.bind(
|
||||||
|
ACTLBL_MOVE_FORWARD_BACKWARD,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_MOVE_LEFT_RIGHT,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_MOVE_UP_DOWN,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_LOOK_LEFT_RIGHT,
|
||||||
|
&[
|
||||||
|
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||||
|
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_LOOK_UP_DOWN,
|
||||||
|
&[
|
||||||
|
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||||
|
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
ACTLBL_LOOK_ROLL,
|
||||||
|
&[
|
||||||
|
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
|
||||||
|
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.bind(
|
||||||
|
"Debug",
|
||||||
|
&[ActionSource::Keyboard(KeyCode::B).into_binding()],
|
||||||
|
)
|
||||||
|
.finish(),
|
||||||
|
)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
let world = game.world_mut();
|
||||||
|
world.add_resource(action_handler);
|
||||||
|
game.with_plugin(InputActionPlugin);
|
||||||
|
};
|
||||||
|
|
||||||
|
Game::initialize()
|
||||||
|
.await
|
||||||
|
.with_plugin(lyra_engine::DefaultPlugins)
|
||||||
|
.with_plugin(setup_scene_plugin)
|
||||||
|
.with_plugin(action_handler_plugin)
|
||||||
|
//.with_plugin(camera_debug_plugin)
|
||||||
|
.with_plugin(FreeFlyCameraPlugin)
|
||||||
|
.run()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_scene_plugin(game: &mut Game) {
|
||||||
|
let world = game.world_mut();
|
||||||
|
let resman = world.get_resource_mut::<ResourceManager>();
|
||||||
|
|
||||||
|
/* let camera_gltf = resman
|
||||||
|
.request::<Gltf>("../assets/AntiqueCamera.glb")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
camera_gltf.wait_recurse_dependencies_load();
|
||||||
|
let camera_mesh = &camera_gltf.data_ref().unwrap().scenes[0];
|
||||||
|
drop(resman);
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
camera_mesh.clone(),
|
||||||
|
WorldTransform::default(),
|
||||||
|
Transform::from_xyz(0.0, -5.0, -2.0),
|
||||||
|
)); */
|
||||||
|
|
||||||
|
let cube_gltf = resman
|
||||||
|
.request::<Gltf>("../assets/cube-texture-embedded.gltf")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
cube_gltf.wait_recurse_dependencies_load();
|
||||||
|
let cube_mesh = &cube_gltf.data_ref().unwrap().scenes[0];
|
||||||
|
drop(resman);
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
cube_mesh.clone(),
|
||||||
|
WorldTransform::default(),
|
||||||
|
Transform::from_xyz(0.0, -5.0, -2.0),
|
||||||
|
));
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
||||||
|
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
||||||
|
light_tran.rotate_x(math::Angle::Degrees(-45.0));
|
||||||
|
light_tran.rotate_y(math::Angle::Degrees(25.0));
|
||||||
|
world.spawn((
|
||||||
|
DirectionalLight {
|
||||||
|
enabled: true,
|
||||||
|
color: Vec3::ONE,
|
||||||
|
intensity: 0.15, //..Default::default()
|
||||||
|
},
|
||||||
|
light_tran,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut camera = CameraComponent::new_3d();
|
||||||
|
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
|
||||||
|
world.spawn((camera, FreeFlyCamera::default()));
|
||||||
|
}
|
|
@ -57,10 +57,12 @@ struct GameLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameLoop {
|
impl GameLoop {
|
||||||
pub async fn new(window: Arc<Window>, world: World, staged_exec: StagedExecutor) -> GameLoop {
|
pub async fn new(window: Arc<Window>, mut world: World, staged_exec: StagedExecutor) -> Self {
|
||||||
|
let renderer = BasicRenderer::create_with_window(&mut world, window.clone()).await;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
window: Arc::clone(&window),
|
window: Arc::clone(&window),
|
||||||
renderer: Box::new(BasicRenderer::create_with_window(window).await),
|
renderer: Box::new(renderer),
|
||||||
|
|
||||||
world,
|
world,
|
||||||
staged_exec,
|
staged_exec,
|
||||||
|
@ -68,7 +70,7 @@ impl GameLoop {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
pub async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
||||||
self.renderer.on_resize(new_size);
|
self.renderer.on_resize(&mut self.world, new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn on_init(&mut self) {
|
pub async fn on_init(&mut self) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#![feature(hash_extract_if)]
|
#![feature(hash_extract_if)]
|
||||||
#![feature(lint_reasons)]
|
#![feature(lint_reasons)]
|
||||||
#![feature(trait_alias)]
|
#![feature(trait_alias)]
|
||||||
|
#![feature(map_many_mut)]
|
||||||
|
|
||||||
extern crate self as lyra_engine;
|
extern crate self as lyra_engine;
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ pub struct GraphExecutionPath {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GraphExecutionPath {
|
impl GraphExecutionPath {
|
||||||
pub fn new(pass_descriptions: Vec<&RenderGraphPassDesc>) -> Self {
|
pub fn new(built_in_slots: FxHashSet<u64>, pass_descriptions: Vec<&RenderGraphPassDesc>) -> Self {
|
||||||
// collect all the output slots
|
// collect all the output slots
|
||||||
let mut total_outputs = HashMap::new();
|
let mut total_outputs = HashMap::new();
|
||||||
for desc in pass_descriptions.iter() {
|
for desc in pass_descriptions.iter() {
|
||||||
|
@ -28,6 +28,9 @@ impl GraphExecutionPath {
|
||||||
// find the node inputs
|
// find the node inputs
|
||||||
let mut inputs = vec![];
|
let mut inputs = vec![];
|
||||||
for slot in desc.input_slots() {
|
for slot in desc.input_slots() {
|
||||||
|
// If the slot is built in to the graph, no need to care about the sorting.
|
||||||
|
if built_in_slots.contains(&slot.id) { continue; }
|
||||||
|
|
||||||
let inp = total_outputs.get(&slot.name)
|
let inp = total_outputs.get(&slot.name)
|
||||||
.expect(&format!("failed to find slot: '{}', ensure that there is a pass outputting it", slot.name));
|
.expect(&format!("failed to find slot: '{}', ensure that there is a pass outputting it", slot.name));
|
||||||
inputs.push(*inp);
|
inputs.push(*inp);
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
mod pass;
|
mod pass;
|
||||||
use std::collections::HashMap;
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
collections::{HashMap, VecDeque},
|
||||||
|
ptr::NonNull,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
pub use pass::*;
|
pub use pass::*;
|
||||||
|
|
||||||
|
@ -11,14 +16,22 @@ pub use slot_desc::*;
|
||||||
|
|
||||||
mod execution_path;
|
mod execution_path;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use tracing::debug;
|
use tracing::{debug, debug_span, instrument};
|
||||||
use wgpu::{util::DeviceExt, RenderPass};
|
use wgpu::{util::DeviceExt, RenderPass};
|
||||||
|
|
||||||
use super::{compute_pipeline::ComputePipeline, pipeline::Pipeline, render_pipeline::RenderPipeline, renderer::{BasicRenderer, Renderer}};
|
use self::execution_path::GraphExecutionPath;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
compute_pipeline::ComputePipeline,
|
||||||
|
pipeline::Pipeline,
|
||||||
|
render_pipeline::RenderPipeline,
|
||||||
|
renderer::{BasicRenderer, Renderer},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct PassEntry {
|
struct PassEntry {
|
||||||
inner: Box<dyn RenderGraphPass>,
|
inner: Arc<RefCell<dyn RenderGraphPass>>,
|
||||||
desc: RenderGraphPassDesc,
|
desc: RenderGraphPassDesc,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,80 +59,137 @@ pub struct PipelineResource {
|
||||||
pub struct RenderGraph {
|
pub struct RenderGraph {
|
||||||
slots: FxHashMap<u64, ResourcedSlot>,
|
slots: FxHashMap<u64, ResourcedSlot>,
|
||||||
slot_names: HashMap<String, u64>,
|
slot_names: HashMap<String, u64>,
|
||||||
// slots with same name
|
|
||||||
slot_mutlikey: FxHashMap<u64, u64>,
|
|
||||||
passes: FxHashMap<u64, PassEntry>,
|
passes: FxHashMap<u64, PassEntry>,
|
||||||
// TODO: Use a SlotMap
|
// TODO: Use a SlotMap
|
||||||
bind_groups: FxHashMap<u64, wgpu::BindGroup>,
|
bind_groups: FxHashMap<u64, wgpu::BindGroup>,
|
||||||
// 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: HashMap<String, PipelineResource>,
|
///
|
||||||
|
pipelines: FxHashMap<u64, PipelineResource>,
|
||||||
current_id: u64,
|
current_id: u64,
|
||||||
|
exec_path: Option<GraphExecutionPath>,
|
||||||
|
|
||||||
pub(crate) surface_config: wgpu::SurfaceConfiguration,
|
pub(crate) surface_config: wgpu::SurfaceConfiguration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraph {
|
impl RenderGraph {
|
||||||
pub fn new(surface_config: wgpu::SurfaceConfiguration) -> Self {
|
pub fn new(surface_config: wgpu::SurfaceConfiguration) -> Self {
|
||||||
|
let mut slots = FxHashMap::default();
|
||||||
|
let mut slot_names = HashMap::default();
|
||||||
|
|
||||||
|
slots.insert(
|
||||||
|
0,
|
||||||
|
ResourcedSlot {
|
||||||
|
name: "window_texture_view".to_string(),
|
||||||
|
ty: SlotType::TextureView,
|
||||||
|
value: SlotValue::None,
|
||||||
|
bind_group_id: None,
|
||||||
|
create_desc: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
slot_names.insert("window_texture_view".to_string(), 0u64);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
slots: Default::default(),
|
slots,
|
||||||
slot_names: Default::default(),
|
slot_names,
|
||||||
slot_mutlikey: Default::default(),
|
|
||||||
passes: Default::default(),
|
passes: Default::default(),
|
||||||
bind_groups: Default::default(),
|
bind_groups: Default::default(),
|
||||||
pipelines: Default::default(),
|
pipelines: Default::default(),
|
||||||
current_id: 0,
|
current_id: 1,
|
||||||
|
exec_path: None,
|
||||||
surface_config,
|
surface_config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn next_id(&mut self) -> u64 {
|
||||||
|
self.current_id += 1;
|
||||||
|
self.current_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn slot_id(&self, name: &str) -> Option<u64> {
|
pub fn slot_id(&self, name: &str) -> Option<u64> {
|
||||||
self.slot_names.get(name).cloned()
|
self.slot_names.get(name).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn slot_value(&self, id: u64) -> Option<&SlotValue> {
|
||||||
|
self.slots.get(&id).map(|s| &s.value)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pass(&self, id: u64) -> Option<&RenderGraphPassDesc> {
|
pub fn pass(&self, id: u64) -> Option<&RenderGraphPassDesc> {
|
||||||
self.passes.get(&id)
|
self.passes.get(&id).map(|s| &s.desc)
|
||||||
.map(|s| &s.desc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_pass<P: RenderGraphPass>(&mut self, pass: P) {
|
pub fn add_pass<P: RenderGraphPass>(&mut self, pass: P) {
|
||||||
let mut desc = pass.desc(self, &mut self.current_id);
|
let mut desc = pass.desc(self);
|
||||||
|
|
||||||
for slot in &mut desc.slots {
|
for slot in &mut desc.slots {
|
||||||
if let Some((id, other)) = self.slot_names.get(&slot.name)
|
if let Some((id, other)) = self
|
||||||
|
.slot_names
|
||||||
|
.get(&slot.name)
|
||||||
.and_then(|id| self.slots.get_mut(id).map(|s| (id, s)))
|
.and_then(|id| self.slots.get_mut(id).map(|s| (id, s)))
|
||||||
{
|
{
|
||||||
slot.id = *id;
|
slot.id = *id;
|
||||||
|
|
||||||
if slot.desc.is_some() && other.create_desc.is_none() {
|
if slot.desc.is_some() && other.create_desc.is_none() {
|
||||||
other.create_desc = slot.desc;
|
other.create_desc = slot.desc.clone();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let res_slot = ResourcedSlot {
|
let res_slot = ResourcedSlot {
|
||||||
name: slot.name,
|
name: slot.name.clone(),
|
||||||
ty: slot.ty,
|
ty: slot.ty,
|
||||||
value: SlotValue::None,
|
value: SlotValue::None,
|
||||||
bind_group_id: None,
|
bind_group_id: None,
|
||||||
create_desc: slot.desc,
|
create_desc: slot.desc.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.slots.insert(slot.id, res_slot);
|
self.slots.insert(slot.id, res_slot);
|
||||||
self.slot_names.insert(slot.name, slot.id);
|
self.slot_names.insert(slot.name.clone(), slot.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.passes.insert(desc.id, PassEntry {
|
self.passes.insert(
|
||||||
inner: Box::new(pass),
|
desc.id,
|
||||||
|
PassEntry {
|
||||||
|
inner: Arc::new(RefCell::new(pass)),
|
||||||
desc,
|
desc,
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates all buffers required for the passes, also creates an internal execution path.
|
/// Creates all buffers required for the passes, also creates an internal execution path.
|
||||||
|
#[instrument(skip(self, device))]
|
||||||
pub fn setup(&mut self, device: &wgpu::Device) {
|
pub fn setup(&mut self, device: &wgpu::Device) {
|
||||||
|
let mut later_slots = VecDeque::new();
|
||||||
|
|
||||||
for slot in self.slots.values_mut() {
|
// For all passes, create their pipelines
|
||||||
if slot.bind_group_id.is_none() {
|
for pass in self.passes.values() {
|
||||||
match slot.create_desc {
|
if let Some(pipei) = &pass.desc.pipeline_info {
|
||||||
|
let pipeline = match pass.desc.pass_type {
|
||||||
|
RenderPassType::Render => {
|
||||||
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: Some(pass.desc.name.as_str()),
|
||||||
|
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&pipei.source)),
|
||||||
|
});
|
||||||
|
Pipeline::Render(RenderPipeline::new(device, &self.surface_config, &shader, vec![], vec![]))
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = PipelineResource {
|
||||||
|
pipeline,
|
||||||
|
bg_layout_name_lookup: Default::default(),
|
||||||
|
};
|
||||||
|
self.pipelines.insert(pass.desc.id, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (slot_id, slot) in &mut self.slots {
|
||||||
|
if slot.bind_group_id.is_none() && slot.create_desc.is_some() {
|
||||||
|
let s = debug_span!("res_creation", slot = slot.name);
|
||||||
|
let _e = s.enter();
|
||||||
|
|
||||||
|
debug!("Creating bind group for slot");
|
||||||
|
|
||||||
|
match &slot.create_desc {
|
||||||
Some(SlotDescriptor::BufferInit(bi)) => {
|
Some(SlotDescriptor::BufferInit(bi)) => {
|
||||||
let label = format!("B_{}", slot.name);
|
let label = format!("B_{}", slot.name);
|
||||||
let wb = bi.as_wgpu(Some(&label));
|
let wb = bi.as_wgpu(Some(&label));
|
||||||
|
@ -127,8 +197,8 @@ impl RenderGraph {
|
||||||
let buf = device.create_buffer_init(&wb);
|
let buf = device.create_buffer_init(&wb);
|
||||||
slot.value = SlotValue::Buffer(buf);
|
slot.value = SlotValue::Buffer(buf);
|
||||||
|
|
||||||
debug!(slot=slot.name, "Created and initialized buffer for slot");
|
debug!("Created and initialized buffer for slot");
|
||||||
},
|
}
|
||||||
Some(SlotDescriptor::Buffer(b)) => {
|
Some(SlotDescriptor::Buffer(b)) => {
|
||||||
let label = format!("B_{}", slot.name);
|
let label = format!("B_{}", slot.name);
|
||||||
let wb = b.as_wgpu(Some(&label));
|
let wb = b.as_wgpu(Some(&label));
|
||||||
|
@ -136,89 +206,116 @@ impl RenderGraph {
|
||||||
let buf = device.create_buffer(&wb);
|
let buf = device.create_buffer(&wb);
|
||||||
slot.value = SlotValue::Buffer(buf);
|
slot.value = SlotValue::Buffer(buf);
|
||||||
|
|
||||||
debug!(slot=slot.name, "Created buffer");
|
debug!("Created buffer");
|
||||||
}
|
}
|
||||||
|
Some(SlotDescriptor::Texture(t)) => {
|
||||||
|
let label = format!("Tex_{}", slot.name);
|
||||||
|
let wt = t.as_wgpu(Some(&label));
|
||||||
|
|
||||||
|
let tex = device.create_texture(&wt);
|
||||||
|
slot.value = SlotValue::Texture(tex);
|
||||||
|
|
||||||
|
debug!("Created texture");
|
||||||
|
}
|
||||||
|
Some(SlotDescriptor::TextureView(tv)) => {
|
||||||
|
// texture views cannot be done in this step. Not only would we run into a
|
||||||
|
// borrow checker error when trying to get the texture for the view, we
|
||||||
|
// can also not guarantee that the texture was actually created.
|
||||||
|
let tex_slot = self
|
||||||
|
.slot_names
|
||||||
|
.get(&tv.texture_label)
|
||||||
|
.expect("Failed to find texture for texture view slot");
|
||||||
|
later_slots.push_back((*slot_id, *tex_slot));
|
||||||
|
|
||||||
|
debug!("Queuing slot resources for later creation");
|
||||||
|
}
|
||||||
|
|
||||||
//Some(SlotDescriptor::Sampler(b)) => {},
|
//Some(SlotDescriptor::Sampler(b)) => {},
|
||||||
//Some(SlotDescriptor::Texture(b)) => {},
|
//Some(SlotDescriptor::Texture(b)) => {},
|
||||||
//Some(SlotDescriptor::TextureView(b)) => {},
|
Some(SlotDescriptor::None) => {}
|
||||||
Some(SlotDescriptor::None) => {},
|
None => {}
|
||||||
None => {},
|
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
todo!()
|
// create buffers for the queued slots
|
||||||
|
while let Some((lower_id, upper_id)) = later_slots.pop_front() {
|
||||||
|
let many = self.slots.get_many_mut([&lower_id, &upper_id]).unwrap();
|
||||||
|
let (lower_slot, upper_slot) = match many {
|
||||||
|
[a, b] => (a, b),
|
||||||
|
};
|
||||||
|
|
||||||
|
let s = debug_span!("deferred_res_creation", slot = lower_slot.name);
|
||||||
|
let _e = s.enter();
|
||||||
|
|
||||||
|
match &lower_slot.create_desc {
|
||||||
|
Some(SlotDescriptor::TextureView(tv)) => {
|
||||||
|
let label = format!("TexView_{}", lower_slot.name);
|
||||||
|
|
||||||
|
let wt = tv.as_wgpu(Some(&label));
|
||||||
|
let tex = upper_slot.value.as_texture();
|
||||||
|
|
||||||
|
let texview = tex.create_view(&wt);
|
||||||
|
lower_slot.value = SlotValue::TextureView(texview);
|
||||||
|
|
||||||
|
debug!(slot = lower_slot.name, "Created texture view");
|
||||||
|
}
|
||||||
|
Some(SlotDescriptor::None) => {}
|
||||||
|
None => {}
|
||||||
|
_ => unreachable!("this slot should not have been put into the do later list"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare(&mut self) {
|
pub fn prepare(&mut self) {
|
||||||
todo!()
|
let builtin = {
|
||||||
|
let mut h = FxHashSet::default();
|
||||||
|
h.insert(0u64);
|
||||||
|
h
|
||||||
|
};
|
||||||
|
let descs = self.passes.values().map(|p| &p.desc).collect();
|
||||||
|
self.exec_path = Some(GraphExecutionPath::new(builtin, descs));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self, renderer: &mut BasicRenderer) {
|
pub fn render(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, surface: &wgpu::Surface) {
|
||||||
todo!()
|
let mut path = self.exec_path.take().unwrap();
|
||||||
|
|
||||||
|
let output = surface.get_current_texture().unwrap();
|
||||||
|
let view = output
|
||||||
|
.texture
|
||||||
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
// update the window texture view slot.
|
||||||
|
let window_tv_slot = self.slots.get_mut(&0).unwrap(); // 0 is window_texture_view
|
||||||
|
debug_assert_eq!(
|
||||||
|
window_tv_slot.name.as_str(),
|
||||||
|
"window_texture_view",
|
||||||
|
"unexpected slot where 'window_texture_view' should be"
|
||||||
|
);
|
||||||
|
window_tv_slot.value = SlotValue::TextureView(view);
|
||||||
|
|
||||||
|
while let Some(pass_id) = path.queue.pop_front() {
|
||||||
|
let pass = self.passes.get(&pass_id).unwrap().clone();
|
||||||
|
let label = format!("{} Encoder", pass.desc.name);
|
||||||
|
|
||||||
|
let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
|
label: Some(&label),
|
||||||
|
});
|
||||||
|
let mut context = RenderGraphContext::new(encoder, queue);
|
||||||
|
|
||||||
|
let mut inner = pass.inner.borrow_mut();
|
||||||
|
inner.execute(self, &pass.desc, &mut context);
|
||||||
|
|
||||||
|
queue.submit(std::iter::once(context.encoder.finish()));
|
||||||
|
}
|
||||||
|
|
||||||
|
output.present();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a pipeline by name from the graph.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if the pipeline was not found by name.
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn pipeline(&self, name: &str) -> &Pipeline {
|
pub fn pipeline(&self, id: u64) -> &Pipeline {
|
||||||
&self.pipelines.get(name)
|
&self.pipelines.get(&id).unwrap().pipeline
|
||||||
.unwrap()
|
|
||||||
.pipeline
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to get a pipeline by name from the graph.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the pipeline was not found by name,
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn try_pipeline(&self, name: &str) -> Option<&Pipeline> {
|
|
||||||
self.pipelines.get(name)
|
|
||||||
.map(|p| &p.pipeline)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a [`RenderPipeline`] by name from the graph.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if the pipeline was not found by name, or if the pipeline is not a render pipeline.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn render_pipeline(&self, name: &str) -> &RenderPipeline {
|
|
||||||
self.pipelines.get(name)
|
|
||||||
.unwrap()
|
|
||||||
.pipeline
|
|
||||||
.as_render()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to get a [`RenderPipeline`] by name from the graph.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the pipeline was not found by name, or if the pipeline is not a render pipeline.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn try_render_pipeline(&self, name: &str) -> Option<&RenderPipeline> {
|
|
||||||
self.pipelines.get(name)
|
|
||||||
.and_then(|p| p.pipeline.try_as_render())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a [`ComputePipeline`] by name from the graph.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// Panics if the pipeline was not found by name, or if the pipeline is not a compute pipeline.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn compute_pipeline(&self, name: &str) -> &ComputePipeline {
|
|
||||||
&self.pipelines.get(name)
|
|
||||||
.unwrap()
|
|
||||||
.pipeline
|
|
||||||
.as_compute()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to get a [`ComputePipeline`] by name from the graph.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the pipeline was not found by name, or if the pipeline is not a render pipeline.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn try_compute_pipeline(&self, name: &str) -> Option<&ComputePipeline> {
|
|
||||||
self.pipelines.get(name)
|
|
||||||
.and_then(|p| p.pipeline.try_as_compute())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -228,19 +325,24 @@ impl RenderGraph {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn bind_group(&self, id: u64) -> &wgpu::BindGroup {
|
pub fn bind_group(&self, id: u64) -> &wgpu::BindGroup {
|
||||||
self.try_bind_group(id)
|
self.try_bind_group(id).expect("Unknown id for bind group")
|
||||||
.expect("Unknown id for bind group")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn try_slot_bind_group(&self, id: u64) -> Option<&wgpu::BindGroup> {
|
pub fn try_slot_bind_group(&self, id: u64) -> Option<&wgpu::BindGroup> {
|
||||||
self.slots.get(&id)
|
self.slots.get(&id).and_then(|s| {
|
||||||
.and_then(|s| self.bind_groups.get(&s.bind_group_id.expect("Slot bind group has not been created yet")))
|
self.bind_groups.get(
|
||||||
|
&s.bind_group_id
|
||||||
|
.expect("Slot bind group has not been created yet"),
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn slot_bind_group(&self, id: u64) -> &wgpu::BindGroup {
|
pub fn slot_bind_group(&self, id: u64) -> &wgpu::BindGroup {
|
||||||
let bg_id = self.slots.get(&id)
|
let bg_id = self
|
||||||
|
.slots
|
||||||
|
.get(&id)
|
||||||
.expect("unknown slot id")
|
.expect("unknown slot id")
|
||||||
.bind_group_id
|
.bind_group_id
|
||||||
.expect("Slot bind group has not been created yet");
|
.expect("Slot bind group has not been created yet");
|
||||||
|
@ -248,17 +350,50 @@ impl RenderGraph {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct BufferWrite {
|
||||||
|
/// The name of the slot that has the resource that will be written
|
||||||
|
target_slot: String,
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RenderGraphContext<'a> {
|
pub struct RenderGraphContext<'a> {
|
||||||
encoder: wgpu::CommandEncoder,
|
/// Becomes None when the encoder is submitted
|
||||||
queue: &'a wgpu::Queue,
|
pub(crate) encoder: wgpu::CommandEncoder,
|
||||||
|
pub(crate) queue: &'a wgpu::Queue,
|
||||||
|
pub(crate) buffer_writes: Vec<BufferWrite>,
|
||||||
|
renderpass_desc: Vec<wgpu::RenderPassDescriptor<'a, 'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RenderGraphContext<'a> {
|
impl<'a> RenderGraphContext<'a> {
|
||||||
pub fn begin_render_pass(&mut self, desc: &wgpu::RenderPassDescriptor) -> wgpu::RenderPass {
|
pub fn new(encoder: wgpu::CommandEncoder, queue: &'a wgpu::Queue) -> Self {
|
||||||
todo!()
|
Self {
|
||||||
|
encoder,
|
||||||
|
queue,
|
||||||
|
buffer_writes: vec![],
|
||||||
|
renderpass_desc: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn begin_render_pass(
|
||||||
|
&'a mut self,
|
||||||
|
desc: wgpu::RenderPassDescriptor<'a, 'a>,
|
||||||
|
) -> wgpu::RenderPass {
|
||||||
|
self.encoder.begin_render_pass(&desc)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn begin_compute_pass(&mut self, desc: &wgpu::ComputePassDescriptor) -> wgpu::ComputePass {
|
pub fn begin_compute_pass(&mut self, desc: &wgpu::ComputePassDescriptor) -> wgpu::ComputePass {
|
||||||
todo!()
|
self.encoder.begin_compute_pass(desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_buffer(&mut self, target_slot: &str, bytes: &[u8]) {
|
||||||
|
//self.queue.write_buffer(buffer, offset, data)
|
||||||
|
self.buffer_writes.push(BufferWrite {
|
||||||
|
target_slot: target_slot.to_string(),
|
||||||
|
bytes: bytes.to_vec(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_buffer_muck<T: bytemuck::NoUninit>(&mut self, target_slot: &str, bytes: T) {
|
||||||
|
self.write_buffer(target_slot, bytemuck::bytes_of(&bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -28,6 +28,23 @@ pub enum SlotValue {
|
||||||
Buffer(wgpu::Buffer),
|
Buffer(wgpu::Buffer),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SlotValue {
|
||||||
|
/// Gets `self` as a texture, panics if self is not a instance of [`SlotValue::Texture`].
|
||||||
|
pub fn as_texture(&self) -> &wgpu::Texture {
|
||||||
|
match self {
|
||||||
|
Self::Texture(t) => t,
|
||||||
|
_ => panic!("self is not an instance of SlotValue::Texture"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_texture_view(&self) -> &wgpu::TextureView {
|
||||||
|
match self {
|
||||||
|
Self::TextureView(t) => t,
|
||||||
|
_ => panic!("self is not an instance of SlotValue::TextureView"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum SlotDescriptor {
|
pub enum SlotDescriptor {
|
||||||
/// Most likely this slot is an input, so it doesn't need to specify the descriptor.
|
/// Most likely this slot is an input, so it doesn't need to specify the descriptor.
|
||||||
|
@ -57,6 +74,21 @@ pub struct RenderPassSlot {
|
||||||
pub desc: Option<SlotDescriptor>,
|
pub desc: Option<SlotDescriptor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RenderGraphPipelineInfo {
|
||||||
|
pub label: String,
|
||||||
|
pub source: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderGraphPipelineInfo {
|
||||||
|
pub fn new(label: &str, source: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
label: label.to_string(),
|
||||||
|
source: source.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RenderGraphPassDesc {
|
pub struct RenderGraphPassDesc {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
|
@ -64,16 +96,18 @@ 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_info: Option<RenderGraphPipelineInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderGraphPassDesc {
|
impl RenderGraphPassDesc {
|
||||||
pub fn new(id: u64, name: &str, pass_type: RenderPassType) -> Self {
|
pub fn new(id: u64, name: &str, pass_type: RenderPassType, pipeline_info: Option<RenderGraphPipelineInfo>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
pass_type,
|
pass_type,
|
||||||
slots: vec![],
|
slots: vec![],
|
||||||
slot_names: HashMap::default(),
|
slot_names: HashMap::default(),
|
||||||
|
pipeline_info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +118,7 @@ impl RenderGraphPassDesc {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_buffer_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
pub fn add_buffer_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
||||||
debug_assert!(matches!(desc, Some(SlotDescriptor::Buffer(_)) | Some(SlotDescriptor::BufferInit(_)) ),
|
debug_assert!(matches!(desc, None | Some(SlotDescriptor::Buffer(_)) | Some(SlotDescriptor::BufferInit(_)) ),
|
||||||
"slot descriptor does not match the type of slot");
|
"slot descriptor does not match the type of slot");
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = RenderPassSlot {
|
||||||
|
@ -99,7 +133,7 @@ impl RenderGraphPassDesc {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_texture_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
pub fn add_texture_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
||||||
debug_assert!(matches!(desc, Some(SlotDescriptor::Texture(_))),
|
debug_assert!(matches!(desc, None | Some(SlotDescriptor::Texture(_))),
|
||||||
"slot descriptor does not match the type of slot");
|
"slot descriptor does not match the type of slot");
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = RenderPassSlot {
|
||||||
|
@ -114,7 +148,7 @@ impl RenderGraphPassDesc {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_texture_view_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
pub fn add_texture_view_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
||||||
debug_assert!(matches!(desc, Some(SlotDescriptor::TextureView(_))),
|
debug_assert!(matches!(desc, None | Some(SlotDescriptor::TextureView(_))),
|
||||||
"slot descriptor does not match the type of slot");
|
"slot descriptor does not match the type of slot");
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = RenderPassSlot {
|
||||||
|
@ -128,8 +162,8 @@ impl RenderGraphPassDesc {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn add_texture_sampler_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
pub fn add_sampler_slot(&mut self, id: u64, name: &str, attribute: SlotAttribute, desc: Option<SlotDescriptor>) {
|
||||||
debug_assert!(matches!(desc, Some(SlotDescriptor::Sampler(_))),
|
debug_assert!(matches!(desc, None | Some(SlotDescriptor::Sampler(_))),
|
||||||
"slot descriptor does not match the type of slot");
|
"slot descriptor does not match the type of slot");
|
||||||
|
|
||||||
let slot = RenderPassSlot {
|
let slot = RenderPassSlot {
|
||||||
|
@ -161,8 +195,8 @@ pub trait RenderGraphPass: 'static {
|
||||||
/// Create a render pass describer.
|
/// Create a render pass describer.
|
||||||
///
|
///
|
||||||
/// The `id` argument is passed as mutable so you can increment it as you use it for new slots.
|
/// The `id` argument is passed as mutable so you can increment it as you use it for new slots.
|
||||||
fn desc(&self, graph: &mut RenderGraph, id: &mut u64) -> RenderGraphPassDesc;
|
fn desc<'a, 'b>(&'a self, graph: &'b mut RenderGraph) -> RenderGraphPassDesc;
|
||||||
|
|
||||||
fn prepare(&mut self, world: &mut World);
|
fn prepare(&mut self, world: &mut World, context: &mut RenderGraphContext);
|
||||||
fn execute(&mut self, graph: &mut RenderGraph, desc: &RenderGraphPassDesc, context: &mut RenderGraphContext);
|
fn execute(&mut self, graph: &mut RenderGraph, desc: &RenderGraphPassDesc, context: &mut RenderGraphContext);
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
mod light_cull_compute;
|
/* mod light_cull_compute;
|
||||||
pub use light_cull_compute::*;
|
pub use light_cull_compute::*;
|
||||||
|
|
||||||
mod base;
|
mod base;
|
||||||
|
@ -6,3 +6,9 @@ pub use base::*;
|
||||||
|
|
||||||
mod depth_prepass;
|
mod depth_prepass;
|
||||||
pub use depth_prepass::*;
|
pub use depth_prepass::*;
|
||||||
|
|
||||||
|
mod simple_phong;
|
||||||
|
pub use simple_phong::*; */
|
||||||
|
|
||||||
|
mod triangle;
|
||||||
|
pub use triangle::*;
|
|
@ -0,0 +1,95 @@
|
||||||
|
use glam::UVec2;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
render::{
|
||||||
|
camera::{CameraUniform, RenderCamera},
|
||||||
|
graph::{
|
||||||
|
BufferInitDescriptor, RenderGraphContext, RenderGraphPass, RenderGraphPassDesc,
|
||||||
|
RenderGraphPipelineInfo, RenderPassType, SlotAttribute, SlotDescriptor,
|
||||||
|
},
|
||||||
|
renderer::ScreenSize,
|
||||||
|
},
|
||||||
|
scene::CameraComponent,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Supplies some basic things other passes needs.
|
||||||
|
///
|
||||||
|
/// screen size buffer, camera buffer,
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TrianglePass;
|
||||||
|
|
||||||
|
impl TrianglePass {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderGraphPass for TrianglePass {
|
||||||
|
fn desc(
|
||||||
|
&self,
|
||||||
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
|
) -> crate::render::graph::RenderGraphPassDesc {
|
||||||
|
let mut desc = RenderGraphPassDesc::new(
|
||||||
|
graph.next_id(),
|
||||||
|
"TrianglePass",
|
||||||
|
RenderPassType::Render,
|
||||||
|
Some(RenderGraphPipelineInfo::new(
|
||||||
|
"TriangleShader",
|
||||||
|
include_str!("../../shaders/triangle.wgsl"),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
desc.add_texture_view_slot(graph.next_id(), "window_texture_view", SlotAttribute::Input, None);
|
||||||
|
|
||||||
|
/* desc.add_buffer_slot(graph.next_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(graph.next_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,
|
||||||
|
}))); */
|
||||||
|
|
||||||
|
desc
|
||||||
|
}
|
||||||
|
|
||||||
|
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::RenderGraphPassDesc,
|
||||||
|
context: &mut crate::render::graph::RenderGraphContext,
|
||||||
|
) {
|
||||||
|
let view = graph.slot_value(graph.slot_id("window_texture_view").unwrap()).unwrap().as_texture_view();
|
||||||
|
let encoder = &mut context.encoder;
|
||||||
|
|
||||||
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("TrianglePass"),
|
||||||
|
color_attachments: &[
|
||||||
|
// This is what @location(0) in the fragment shader targets
|
||||||
|
Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
|
r: 0.1,
|
||||||
|
g: 0.2,
|
||||||
|
b: 0.3,
|
||||||
|
a: 1.0,
|
||||||
|
}),
|
||||||
|
store: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let pipeline = graph.pipeline(desc.id);
|
||||||
|
pass.set_pipeline(&pipeline.as_render());
|
||||||
|
pass.draw(0..3, 0..1);
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,6 +40,19 @@ impl TextureViewDescriptor {
|
||||||
array_layer_count: d.array_layer_count,
|
array_layer_count: d.array_layer_count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_wgpu<'a>(&self, label: Option<&'a str>) -> wgpu::TextureViewDescriptor<'a> {
|
||||||
|
wgpu::TextureViewDescriptor {
|
||||||
|
label,
|
||||||
|
format: self.format,
|
||||||
|
dimension: self.dimension,
|
||||||
|
aspect: self.aspect,
|
||||||
|
base_mip_level: self.base_mip_level,
|
||||||
|
mip_level_count: self.mip_level_count,
|
||||||
|
base_array_layer: self.base_array_layer,
|
||||||
|
array_layer_count: self.array_layer_count,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,6 +130,21 @@ pub struct TextureDescriptor {
|
||||||
pub view_formats: Vec<wgpu::TextureFormat>,
|
pub view_formats: Vec<wgpu::TextureFormat>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TextureDescriptor {
|
||||||
|
pub fn as_wgpu<'a>(&'a self, label: Option<&'a str>) -> wgpu::TextureDescriptor<'a> {
|
||||||
|
wgpu::TextureDescriptor {
|
||||||
|
label,
|
||||||
|
size: self.size,
|
||||||
|
mip_level_count: self.mip_level_count,
|
||||||
|
sample_count: self.sample_count,
|
||||||
|
dimension: self.dimension,
|
||||||
|
format: self.format,
|
||||||
|
usage: self.usage,
|
||||||
|
view_formats: &self.view_formats,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct BufferDescriptor {
|
pub struct BufferDescriptor {
|
||||||
|
@ -134,7 +162,7 @@ pub struct BufferDescriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferDescriptor {
|
impl BufferDescriptor {
|
||||||
pub fn as_wgpu(&self, label: Option<&str>) -> wgpu::BufferDescriptor {
|
pub fn as_wgpu<'a>(&self, label: Option<&'a str>) -> wgpu::BufferDescriptor<'a> {
|
||||||
wgpu::BufferDescriptor {
|
wgpu::BufferDescriptor {
|
||||||
label,
|
label,
|
||||||
size: self.size,
|
size: self.size,
|
||||||
|
@ -156,7 +184,7 @@ pub struct BufferInitDescriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BufferInitDescriptor {
|
impl BufferInitDescriptor {
|
||||||
pub fn as_wgpu(&self, label: Option<&str>) -> wgpu::util::BufferInitDescriptor {
|
pub fn as_wgpu<'a>(&'a self, label: Option<&'a str>) -> wgpu::util::BufferInitDescriptor<'a> {
|
||||||
wgpu::util::BufferInitDescriptor {
|
wgpu::util::BufferInitDescriptor {
|
||||||
label,
|
label,
|
||||||
contents: &self.contents,
|
contents: &self.contents,
|
||||||
|
|
|
@ -56,13 +56,14 @@ impl RenderPipeline {
|
||||||
// Requires Features::CONSERVATIVE_RASTERIZATION
|
// Requires Features::CONSERVATIVE_RASTERIZATION
|
||||||
conservative: false,
|
conservative: false,
|
||||||
},
|
},
|
||||||
depth_stencil: Some(wgpu::DepthStencilState {
|
/*depth_stencil: Some(wgpu::DepthStencilState {
|
||||||
format: RenderTexture::DEPTH_FORMAT,
|
format: RenderTexture::DEPTH_FORMAT,
|
||||||
depth_write_enabled: true,
|
depth_write_enabled: true,
|
||||||
depth_compare: wgpu::CompareFunction::Less,
|
depth_compare: wgpu::CompareFunction::Less,
|
||||||
stencil: wgpu::StencilState::default(), // TODO: stencil buffer
|
stencil: wgpu::StencilState::default(), // TODO: stencil buffer
|
||||||
bias: wgpu::DepthBiasState::default(),
|
bias: wgpu::DepthBiasState::default(),
|
||||||
}),
|
}),*/
|
||||||
|
depth_stencil: None,
|
||||||
multisample: wgpu::MultisampleState {
|
multisample: wgpu::MultisampleState {
|
||||||
count: 1,
|
count: 1,
|
||||||
mask: !0,
|
mask: !0,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::collections::{VecDeque, HashSet};
|
use std::collections::{VecDeque, HashSet};
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
@ -18,7 +19,7 @@ use wgpu::util::DeviceExt;
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
use crate::math::Transform;
|
use crate::math::Transform;
|
||||||
use crate::render::graph::{BasePass, DepthPrePass, LightCullComputePass};
|
use crate::render::graph::TrianglePass;
|
||||||
use crate::render::material::MaterialUniform;
|
use crate::render::material::MaterialUniform;
|
||||||
use crate::render::render_buffer::BufferWrapperBuilder;
|
use crate::render::render_buffer::BufferWrapperBuilder;
|
||||||
use crate::scene::CameraComponent;
|
use crate::scene::CameraComponent;
|
||||||
|
@ -44,6 +45,20 @@ type SceneHandle = ResHandle<SceneGraph>;
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct ScreenSize(glam::UVec2);
|
pub struct ScreenSize(glam::UVec2);
|
||||||
|
|
||||||
|
impl Deref for ScreenSize {
|
||||||
|
type Target = glam::UVec2;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for ScreenSize {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Renderer {
|
pub trait Renderer {
|
||||||
fn prepare(&mut self, main_world: &mut World);
|
fn prepare(&mut self, main_world: &mut World);
|
||||||
fn render(&mut self) -> Result<(), wgpu::SurfaceError>;
|
fn render(&mut self) -> Result<(), wgpu::SurfaceError>;
|
||||||
|
@ -212,9 +227,15 @@ impl BasicRenderer {
|
||||||
//let light_cull_compute = LightCullCompute::new(device.clone(), queue.clone(), size, &light_uniform_buffers, &camera_buffer, &mut depth_texture);
|
//let light_cull_compute = LightCullCompute::new(device.clone(), queue.clone(), size, &light_uniform_buffers, &camera_buffer, &mut depth_texture);
|
||||||
|
|
||||||
let mut g = RenderGraph::new(config.clone());
|
let mut g = RenderGraph::new(config.clone());
|
||||||
g.add_pass(BasePass::new());
|
/* debug!("Adding base pass");
|
||||||
|
g.add_pass(TrianglePass::new());
|
||||||
|
debug!("Adding depth pre-pass");
|
||||||
g.add_pass(DepthPrePass::new());
|
g.add_pass(DepthPrePass::new());
|
||||||
g.add_pass(LightCullComputePass::new(size));
|
debug!("Adding light cull compute pass");
|
||||||
|
g.add_pass(LightCullComputePass::new(size)); */
|
||||||
|
|
||||||
|
debug!("Adding triangle pass");
|
||||||
|
g.add_pass(TrianglePass::new());
|
||||||
g.setup(&device);
|
g.setup(&device);
|
||||||
|
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
|
@ -261,7 +282,7 @@ impl Renderer for BasicRenderer {
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
#[instrument(skip(self))]
|
||||||
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
||||||
self.graph.render(self);
|
self.graph.render(&self.device, &self.queue, &self.surface);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -276,7 +297,7 @@ impl Renderer for BasicRenderer {
|
||||||
// tell other things of updated resize
|
// tell other things of updated resize
|
||||||
self.surface.configure(&self.device, &self.config);
|
self.surface.configure(&self.device, &self.config);
|
||||||
|
|
||||||
let mut world_ss = world.get_resource::<ScreenSize>();
|
let mut world_ss = world.get_resource_mut::<ScreenSize>();
|
||||||
world_ss.0 = glam::UVec2::new(new_size.width, new_size.height);
|
world_ss.0 = glam::UVec2::new(new_size.width, new_size.height);
|
||||||
self.graph.surface_config = self.config.clone();
|
self.graph.surface_config = self.config.clone();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
@builtin(vertex_index) in_vertex_index: u32,
|
||||||
|
) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
let x = f32(1 - i32(in_vertex_index)) * 0.5;
|
||||||
|
let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5;
|
||||||
|
out.clip_position = vec4<f32>(x, y, 0.0, 1.0);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
return vec4<f32>(0.3, 0.2, 0.1, 1.0);
|
||||||
|
}
|
Loading…
Reference in New Issue