Compare commits

...

2 Commits

10 changed files with 500 additions and 181 deletions

View File

@ -2,7 +2,7 @@ use std::{collections::VecDeque, sync::Arc};
use tracing::instrument; use tracing::instrument;
use super::{Frame, RenderGraphLabel, RenderGraphLabelValue, RenderTarget}; use super::{RenderGraphLabel, RenderGraphLabelValue};
/// A queued write to a GPU buffer targeting a graph slot. /// A queued write to a GPU buffer targeting a graph slot.
pub(crate) struct GraphBufferWrite { pub(crate) struct GraphBufferWrite {
@ -87,14 +87,15 @@ impl<'a> RenderGraphContext<'a> {
self.queue_buffer_write(target_slot, offset, bytemuck::bytes_of(&bytes)); self.queue_buffer_write(target_slot, offset, bytemuck::bytes_of(&bytes));
} }
pub fn get_frame(&self, render_target: &RenderTarget) -> super::Frame { /// Submit the encoder to the gpu queue.
let texture = render_target.frame_texture() ///
.expect("failed to create frame texture"); // should this be returned to the user? /// The `encoder` of this context will be `None` until the next node is executed, then another
/// one will be made. You likely don't need to run this yourself until you are manually
Frame { /// presenting a surface texture.
device: self.device.clone(), pub fn submit_encoder(&mut self) {
queue: self.queue.clone(), let en = self.encoder.take()
texture, .unwrap()
} .finish();
self.queue.submit(std::iter::once(en));
} }
} }

View File

@ -1,6 +1,6 @@
mod node; mod node;
use std::{ use std::{
cell::{Ref, RefCell}, collections::{HashMap, VecDeque}, fmt::Debug, hash::Hash, rc::Rc, sync::Arc cell::{Ref, RefCell, RefMut}, collections::{HashMap, VecDeque}, fmt::Debug, hash::Hash, rc::Rc, sync::Arc
}; };
use lyra_ecs::World; use lyra_ecs::World;
@ -125,10 +125,11 @@ pub struct RenderGraph {
bind_groups: FxHashMap<RenderGraphLabelValue, BindGroupEntry>, bind_groups: FxHashMap<RenderGraphLabelValue, BindGroupEntry>,
/// A directed graph used to determine dependencies of nodes. /// A directed graph used to determine dependencies of nodes.
node_graph: petgraph::matrix_graph::DiMatrix<RenderGraphLabelValue, (), Option<()>, usize>, node_graph: petgraph::matrix_graph::DiMatrix<RenderGraphLabelValue, (), Option<()>, usize>,
view_target: Rc<RefCell<ViewTarget>>,
} }
impl RenderGraph { impl RenderGraph {
pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>) -> Self { pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>, view_target: Rc<RefCell<ViewTarget>>) -> Self {
Self { Self {
device, device,
queue, queue,
@ -137,6 +138,7 @@ impl RenderGraph {
sub_graphs: Default::default(), sub_graphs: Default::default(),
bind_groups: Default::default(), bind_groups: Default::default(),
node_graph: Default::default(), node_graph: Default::default(),
view_target,
} }
} }
@ -344,28 +346,18 @@ impl RenderGraph {
let device = self.device.clone(); let device = self.device.clone();
let queue = self.queue.clone(); let queue = self.queue.clone();
// create a new encoder since the last one was presented // create a new encoder if the last one was submitted
if encoder.is_none() { if encoder.is_none() {
encoder = Some(self.create_encoder()); encoder = Some(self.create_encoder());
} }
// all encoders need to be submitted before a presenter node is executed.
if pass_desc.ty == NodeType::Presenter {
trace!("Submitting encoder before presenting");
let finished = encoder.take().unwrap().finish();
self.queue.submit(std::iter::once(finished));
// Do not create a new encoder yet since this node may be the last node.
// A new encoder can be made on the next iteration of this loop.
}
let mut context = RenderGraphContext::new(device, queue, encoder.take(), pass_label.clone()); let mut context = RenderGraphContext::new(device, queue, encoder.take(), pass_label.clone());
trace!("Executing {:?}", pass_label.0); trace!("Executing {:?}", pass_label.0);
let mut inner = pass_inn.borrow_mut(); let mut inner = pass_inn.borrow_mut();
inner.execute(self, &pass_desc, &mut context); inner.execute(self, &pass_desc, &mut context);
// take back the encoder from the context
encoder = context.encoder; encoder = context.encoder;
} }
@ -531,6 +523,14 @@ impl RenderGraph {
sg.bind_groups.insert(bg.label.clone(), bg); sg.bind_groups.insert(bg.label.clone(), bg);
} }
} }
pub fn view_target(&self) -> Ref<ViewTarget> {
self.view_target.borrow()
}
pub fn view_target_mut(&self) -> RefMut<ViewTarget> {
self.view_target.borrow_mut()
}
} }
pub struct SubGraphNode { pub struct SubGraphNode {

View File

@ -1,5 +1,6 @@
use std::{cell::RefCell, rc::Rc}; use std::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,7 @@ use crate::{
render::{ render::{
camera::{CameraUniform, RenderCamera}, camera::{CameraUniform, RenderCamera},
graph::{ graph::{
Node, NodeDesc, NodeSlot, NodeType, RenderGraph, RenderGraphContext, RenderTarget, SlotAttribute, SlotType, SlotValue Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue
}, },
render_buffer::BufferWrapper, texture::RenderTexture, render_buffer::BufferWrapper, texture::RenderTexture,
}, },
@ -23,10 +24,7 @@ pub enum BasePassSlots {
DepthTexture, DepthTexture,
ScreenSize, ScreenSize,
Camera, Camera,
MainRenderTarget,
WindowTextureView,
DepthTextureView, DepthTextureView,
Frame,
} }
/// Supplies some basic things other passes needs. /// Supplies some basic things other passes needs.
@ -34,23 +32,12 @@ pub enum BasePassSlots {
/// screen size buffer, camera buffer, /// screen size buffer, camera buffer,
#[derive(Default)] #[derive(Default)]
pub struct BasePass { pub struct BasePass {
/// Temporary storage for the main render target screen_size: UVec2,
///
/// This should be Some when the pass is first created then after its added to
/// the render graph it will be None and stay None.
temp_render_target: Option<RenderTarget>,
screen_size: glam::UVec2,
} }
impl BasePass { impl BasePass {
pub fn new(render_target: RenderTarget) -> Self { pub fn new() -> Self {
let size = render_target.size(); Self::default()
Self {
temp_render_target: Some(render_target),
screen_size: size,
..Default::default()
}
} }
} }
@ -59,8 +46,8 @@ impl Node for BasePass {
&mut self, &mut self,
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 vt = graph.view_target();
self.screen_size = render_target.size(); self.screen_size = vt.size();
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)
@ -107,25 +94,6 @@ impl Node for BasePass {
], ],
); );
desc.add_slot(NodeSlot {
ty: SlotType::RenderTarget,
attribute: SlotAttribute::Output,
label: BasePassSlots::MainRenderTarget.into(),
value: Some(SlotValue::RenderTarget(Rc::new(RefCell::new(
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(
BasePassSlots::WindowTextureView,
SlotAttribute::Output,
Some(SlotValue::Lazy),
);
desc.add_texture_view_slot( desc.add_texture_view_slot(
BasePassSlots::DepthTextureView, BasePassSlots::DepthTextureView,
SlotAttribute::Output, SlotAttribute::Output,
@ -145,10 +113,12 @@ 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, graph: &mut RenderGraph, 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 screen_size = graph.view_target().size();
let mut render_cam = let mut render_cam =
RenderCamera::new(PhysicalSize::new(self.screen_size.x, self.screen_size.y)); RenderCamera::new(PhysicalSize::new(screen_size.x, screen_size.y));
let uniform = render_cam.calc_view_projection(&camera); let uniform = render_cam.calc_view_projection(&camera);
context.queue_buffer_write_with(BasePassSlots::Camera, 0, uniform) context.queue_buffer_write_with(BasePassSlots::Camera, 0, uniform)
@ -163,38 +133,20 @@ impl Node for BasePass {
_desc: &crate::render::graph::NodeDesc, _desc: &crate::render::graph::NodeDesc,
context: &mut crate::render::graph::RenderGraphContext, context: &mut crate::render::graph::RenderGraphContext,
) { ) {
let tv_slot = graph let mut vt = graph.view_target_mut();
.slot_value_mut(BasePassSlots::MainRenderTarget) vt.primary.create_frame();
.expect("somehow the main render target slot is missing"); vt.next_chain(context.encoder.as_mut().unwrap());
let rt = tv_slot.as_render_target().unwrap(); vt.primary.create_frame_view();
let rt_size = rt.size();
let frame = context.get_frame(&rt);
/* debug_assert!( /* 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.
let rt_size = vt.size();
if rt_size != self.screen_size { if rt_size != self.screen_size {
self.screen_size = rt_size; self.screen_size = rt_size;
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 view = surface_tex
.create_view(&wgpu::TextureViewDescriptor::default());
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
let tv_slot = graph
.slot_value_mut(BasePassSlots::WindowTextureView)
.expect("somehow the window texture view slot is missing");
*tv_slot = SlotValue::TextureView(Rc::new(view));
} }
} }

View File

@ -440,12 +440,7 @@ impl Node for MeshPass {
if self.pipeline.is_none() { if self.pipeline.is_none() {
let device = graph.device(); let device = graph.device();
let surface_config_format = graph.view_target().format();
let main_rt = graph.slot_value(BasePassSlots::MainRenderTarget)
.and_then(|s| s.as_render_target())
.expect("missing main render target");
let surface_config_format = main_rt.format();
drop(main_rt);
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera); let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights); let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
@ -508,11 +503,14 @@ impl Node for MeshPass {
) { ) {
let encoder = context.encoder.as_mut().unwrap(); let encoder = context.encoder.as_mut().unwrap();
let view = graph /* let view = graph
.slot_value(BasePassSlots::WindowTextureView) .slot_value(BasePassSlots::WindowTextureView)
.unwrap() .unwrap()
.as_texture_view() .as_texture_view()
.expect("BasePassSlots::WindowTextureView was not a TextureView slot"); .expect("BasePassSlots::WindowTextureView was not a TextureView slot"); */
let vt = graph.view_target();
let view = vt.render_view();
let depth_view = graph let depth_view = graph
.slot_value(BasePassSlots::DepthTextureView) .slot_value(BasePassSlots::DepthTextureView)

View File

@ -16,5 +16,5 @@ pub use present_pass::*;
mod init; mod init;
pub use init::*; pub use init::*;
mod tint; /* mod tint;
pub use tint::*; pub use tint::*; */

View File

@ -2,72 +2,42 @@ 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::{Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext};
#[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)] #[derive(Debug, Clone, Hash, PartialEq, RenderGraphLabel)]
pub struct PresentPassLabel(RenderGraphLabelValue); pub struct PresentPassLabel;
impl PresentPassLabel {
pub fn new(frame_label: impl RenderGraphLabel) -> Self {
Self(frame_label.into())
}
}
/// Supplies some basic things other passes needs. /// Supplies some basic things other passes needs.
/// ///
/// screen size buffer, camera buffer, /// screen size buffer, camera buffer,
pub struct PresentPass { #[derive(Default, Debug)]
/// Label of this pass pub struct PresentPass;
pub label: PresentPassLabel,
}
impl PresentPass { impl PresentPass {
/// Create a new PresentNode, presenting the frame at `frame_label` pub fn new() -> Self {
pub fn new(frame_label: impl RenderGraphLabel) -> Self { Self::default()
Self {
//render_target_slot: render_target_slot.rc_clone(),
label: PresentPassLabel::new(frame_label),
}
}
pub fn from_node_label(present_pass_label: PresentPassLabel) -> Self {
Self {
label: present_pass_label,
}
} }
} }
impl Node for PresentPass { impl Node for PresentPass {
fn desc(&mut self, _graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::NodeDesc { fn desc(&mut self, _graph: &mut crate::render::graph::RenderGraph) -> crate::render::graph::NodeDesc {
let mut desc = NodeDesc::new( NodeDesc::new(
NodeType::Presenter, NodeType::Presenter,
None, None,
vec![], vec![],
); )
desc.add_slot(
NodeSlot {
ty: SlotType::Frame,
attribute: SlotAttribute::Input,
label: self.label.0.clone(),
value: None,
}
);
desc
} }
fn prepare(&mut self, _graph: &mut RenderGraph, _world: &mut lyra_ecs::World, _context: &mut RenderGraphContext) { fn prepare(&mut self, _graph: &mut RenderGraph, _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 mut vt = graph.view_target_mut();
let slot = graph.slot_value_mut(self.label.0.clone()) vt.copy_to_primary(context.encoder.as_mut().unwrap());
.unwrap_or_else(|| panic!("frame slot '{:?}' for PresentPass is missing", self.label.0)) context.submit_encoder();
.as_frame_mut()
.unwrap() let frame = vt.primary.frame.take()
.take() .expect("ViewTarget.primary was already presented");
.unwrap_or_else(|| panic!("frame '{:?}' was already presented", self.label.0)); frame.present();
slot.present();
} }
} }

View File

@ -0,0 +1,190 @@
use std::{cell::RefCell, rc::Rc};
use lyra_game_derive::RenderGraphLabel;
use crate::render::{
graph::{Node, NodeDesc, NodeSlot, NodeType, RenderTarget, SlotAttribute, SlotType, SlotValue},
resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState},
};
use super::BasePassSlots;
#[derive(Debug, Clone, Copy, Hash, RenderGraphLabel)]
pub enum TintPassSlots {
InputRenderTarget,
InputTextureView,
TextureViewBindGroup,
Frame
}
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
pub struct TintPassLabel;
pub struct TintPass {
render_target: Option<RenderTarget>,
}
impl TintPass {
pub fn new(render_target: RenderTarget) -> Self {
Self {
render_target: Some(render_target)
}
}
}
impl Node for TintPass {
fn desc<'a, 'b>(
&'a mut self,
graph: &'b mut crate::render::graph::RenderGraph,
) -> crate::render::graph::NodeDesc {
let device = &graph.device;
// get surface config format
/* let main_rt = graph
.slot_value(BasePassSlots::MainRenderTarget)
.and_then(|s| s.as_render_target())
.expect("missing main render target");
let surface_config_format = main_rt.format();
drop(main_rt); */
let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("tint_bgl"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: false },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
count: None,
},
],
});
let bgl = Rc::new(bgl);
let input_view = graph
.slot_value(BasePassSlots::WindowTextureView)
.and_then(|s| s.as_texture_view())
.expect("missing input texture view");
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("tint_bg"),
layout: &*bgl,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(input_view),
}],
});
let shader = Rc::new(Shader {
label: Some("tint_shader".into()),
source: include_str!("../../shaders/tint.wgsl").to_string(),
});
let mut desc = NodeDesc::new(
NodeType::Render,
Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
label: Some("tint_pass".into()),
layouts: vec![bgl.clone()],
push_constant_ranges: vec![],
vertex: VertexState {
module: shader.clone(),
entry_point: "vs_main".into(),
buffers: vec![],
},
fragment: Some(FragmentState {
module: shader,
entry_point: "fs_main".into(),
targets: vec![Some(wgpu::ColorTargetState {
format: self.render_target.as_ref().unwrap().format(),
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
depth_stencil: None,
primitive: wgpu::PrimitiveState::default(),
multisample: wgpu::MultisampleState::default(),
multiview: None,
})),
vec![(&TintPassSlots::TextureViewBindGroup, bg.into(), Some(bgl))],
);
// desc.add_buffer_slot(
// FxaaPassSlots::Lights,
// SlotAttribute::Output,
// Some(SlotValue::Buffer(light_buffers.buffer.clone())),
// );
desc.add_slot(NodeSlot {
ty: SlotType::Frame,
attribute: SlotAttribute::Output,
label: TintPassSlots::Frame.into(),
value: Some(SlotValue::Lazy),
});
desc
}
fn prepare(
&mut self,
_: &mut crate::render::graph::RenderGraph,
_: &mut lyra_ecs::World,
_: &mut crate::render::graph::RenderGraphContext,
) {
//todo!()
}
fn execute(
&mut self,
graph: &mut crate::render::graph::RenderGraph,
_: &crate::render::graph::NodeDesc,
context: &mut crate::render::graph::RenderGraphContext,
) {
let rt = self.render_target.as_ref().unwrap();
let frame = rt.create_frame();
let view = frame.texture()
.create_view(&wgpu::TextureViewDescriptor::default());
let frame_slot = graph
.slot_value_mut(TintPassSlots::Frame)
.expect("somehow the frame slot is missing");
*frame_slot = SlotValue::Frame(Rc::new(RefCell::new(Some(frame))));
let bg = graph.bind_group(TintPassSlots::TextureViewBindGroup);
/* let view = graph
.slot_value(BasePassSlots::WindowTextureView)
.unwrap()
.as_texture_view()
.expect("BasePassSlots::WindowTextureView was not a TextureView slot"); */
{
let encoder = context.encoder.as_mut().unwrap();
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("tint_pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: true,
},
})],
depth_stencil_attachment: None,
});
pass.set_bind_group(0, bg, &[]);
pass.draw(0..4, 0..1);
}
}
}

View File

@ -39,7 +39,7 @@ impl RenderTarget {
sample_count: 1, sample_count: 1,
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format, format,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST, usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::COPY_SRC,
view_formats: &[], view_formats: &[],
}); });
@ -89,6 +89,21 @@ impl RenderTarget {
}, },
} }
} }
/// Create the frame 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 create_frame(&self) -> Frame {
let texture = self.frame_texture()
.expect("failed to create frame texture"); // TODO: should be returned to the user
let size = self.size();
Frame {
size,
texture,
}
}
} }
pub enum FrameTexture { pub enum FrameTexture {
@ -96,10 +111,12 @@ pub enum FrameTexture {
Texture(Arc<wgpu::Texture>), Texture(Arc<wgpu::Texture>),
} }
#[allow(dead_code)] /// Represents the current frame that is being rendered to.
//#[allow(dead_code)]
pub struct Frame { pub struct Frame {
pub(crate) device: Arc<wgpu::Device>, /* pub(crate) device: Arc<wgpu::Device>,
pub(crate) queue: Arc<wgpu::Queue>, pub(crate) queue: Arc<wgpu::Queue>, */
pub(crate) size: math::UVec2,
pub(crate) texture: FrameTexture, pub(crate) texture: FrameTexture,
} }
@ -120,4 +137,178 @@ impl Frame {
FrameTexture::Texture(_) => {}, FrameTexture::Texture(_) => {},
} }
} }
/// The size of the frame
pub fn size(&self) -> math::UVec2 {
self.size
}
}
/// Stores the current frame, and the render target it came from.
pub struct FrameTarget {
pub render_target: RenderTarget,
/// None when a frame has not been created yet
pub frame: Option<Frame>,
/// The view to use to render to the frame.
pub frame_view: Option<wgpu::TextureView>,
}
impl FrameTarget {
pub fn new(render_target: RenderTarget) -> Self {
Self {
render_target,
frame: None,
frame_view: None,
}
}
/// Returns the size of the [`RenderTarget`].
pub fn size(&self) -> math::UVec2 {
self.render_target.size()
}
/// Returns the [`wgpu::TextureFormat`] of the [`RenderTarget`].
pub fn format(&self) -> wgpu::TextureFormat {
self.render_target.format()
}
/// Create the frame using the inner [`RenderTarget`].
pub fn create_frame(&mut self) -> &mut Frame {
self.frame = Some(self.render_target.create_frame());
self.frame.as_mut().unwrap()
}
/// Create the [`wgpu::TextureView`] for the [`Frame`], storing it in self and returning a reference to it.
pub fn create_frame_view(&mut self) -> &wgpu::TextureView {
let frame = self.frame.as_ref().expect("frame was not created, cannot create view");
self.frame_view = Some(frame.texture().create_view(&wgpu::TextureViewDescriptor::default()));
self.frame_view.as_ref().unwrap()
}
}
pub struct TargetViewChain {
source: FrameTarget,
dest: FrameTarget,
}
impl TargetViewChain {
pub fn next(&mut self, encoder: &mut wgpu::CommandEncoder) {
let size = self.source.size();
let size = wgpu::Extent3d {
width: size.x,
height: size.y,
depth_or_array_layers: 1,
};
let source_tex = self.source.frame.as_ref().unwrap().texture();
let dest_tex = self.dest.frame.as_ref().unwrap().texture();
let source_ict = wgpu::ImageCopyTexture {
texture: source_tex,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
};
let dest_ict = wgpu::ImageCopyTexture {
texture: dest_tex,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
};
encoder.copy_texture_to_texture(source_ict, dest_ict, size);
std::mem::swap(&mut self.source, &mut self.dest);
}
}
pub struct ViewTarget {
device: Arc<wgpu::Device>,
/// The primary RenderTarget, likely a Surface
pub primary: FrameTarget,
pub chain: Option<TargetViewChain>,
}
impl ViewTarget {
pub fn new(device: Arc<wgpu::Device>, primary: RenderTarget) -> Self {
Self {
device,
primary: FrameTarget::new(primary),
chain: None
}
}
pub fn size(&self) -> math::UVec2 {
self.primary.size()
}
pub fn format(&self) -> wgpu::TextureFormat {
self.primary.format()
}
pub fn resize(&mut self, device: &wgpu::Device, size: math::UVec2) {
self.primary.render_target.resize(device, size);
}
/// Cycle the target view chain, storing it in self, and returning a mutable borrow to it.
pub fn next_chain(&mut self, encoder: &mut wgpu::CommandEncoder) -> &mut TargetViewChain {
// TODO: check if chain has already been made. If it has, use next on it.
let _ = encoder;
let format = self.primary.format();
let size = self.primary.size();
let mut source = FrameTarget::new(RenderTarget::new_texture(&self.device, format, size));
source.create_frame();
source.create_frame_view();
let mut dest = FrameTarget::new(RenderTarget::new_texture(&self.device, format, size));
dest.create_frame();
dest.create_frame_view();
self.chain = Some(TargetViewChain {
source,
dest,
});
//self.reset_chain(encoder);
self.chain.as_mut().unwrap()
}
/// Get the [`wgpu::TextureView`] to render to
pub fn render_view(&self) -> &wgpu::TextureView {
let chain = self.chain.as_ref().unwrap();
chain.source.frame_view.as_ref().unwrap()
}
/// Copy the chain target to the primary target
///
/// The primary target must have `wgpu::TextureUsages::COPY_DST`.
pub fn copy_to_primary(&self, encoder: &mut wgpu::CommandEncoder) {
let chain = self.chain.as_ref().unwrap();
let chain_tex = chain.source.frame.as_ref().unwrap().texture();
let source_ict = wgpu::ImageCopyTexture {
texture: chain_tex,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
};
let dest_tex = self.primary.frame.as_ref().unwrap().texture();
let dest_ict = wgpu::ImageCopyTexture {
texture: dest_tex,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
};
let size = self.primary.size();
let size = wgpu::Extent3d {
width: size.x,
height: size.y,
depth_or_array_layers: 1,
};
encoder.copy_texture_to_texture(source_ict, dest_ict, size);
}
} }

View File

@ -1,5 +1,7 @@
use std::cell::RefCell;
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;
@ -7,7 +9,7 @@ use lyra_game_derive::RenderGraphLabel;
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, RenderGraphLabelValue, RenderTarget, SubGraphNode, TintPass, TintPassLabel, TintPassSlots}; use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, SubGraphNode, ViewTarget};
use super::graph::RenderGraph; use super::graph::RenderGraph;
use super::{resource::RenderPipeline, render_job::RenderJob}; use super::{resource::RenderPipeline, render_job::RenderJob};
@ -114,7 +116,7 @@ impl BasicRenderer {
.find(|f| f.describe().srgb) .find(|f| f.describe().srgb)
.unwrap_or(surface_caps.formats[0]); .unwrap_or(surface_caps.formats[0]);
let config = wgpu::SurfaceConfiguration { let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST,
format: surface_format, format: surface_format,
width: size.width, width: size.width,
height: size.height, height: size.height,
@ -127,35 +129,16 @@ impl BasicRenderer {
let device = Arc::new(device); let device = Arc::new(device);
let queue = Arc::new(queue); let queue = Arc::new(queue);
let surface_size = wgpu::Extent3d {
width: config.width,
height: config.height,
depth_or_array_layers: 1
};
let surface_target = RenderTarget::from_surface(surface, config); let surface_target = RenderTarget::from_surface(surface, config);
let view_target = Rc::new(RefCell::new(ViewTarget::new(device.clone(), surface_target)));
let headless_texture = device.create_texture(&wgpu::TextureDescriptor { let mut main_graph = RenderGraph::new(device.clone(), queue.clone(), view_target.clone());
label: Some("headless_texture"),
size: surface_size,
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: surface_format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT // we'll be rendering to it
| wgpu::TextureUsages::TEXTURE_BINDING,
view_formats: &[],
});
let headless_target = RenderTarget::from(headless_texture);
let mut main_graph = RenderGraph::new(device.clone(), queue.clone());
debug!("Adding base pass"); debug!("Adding base pass");
main_graph.add_node(BasePassLabel, BasePass::new(surface_target)); main_graph.add_node(BasePassLabel, BasePass::new());
{ {
let mut forward_plus_graph = RenderGraph::new(device.clone(), queue.clone()); let mut forward_plus_graph = RenderGraph::new(device.clone(), queue.clone(), view_target.clone());
debug!("Adding light base pass"); debug!("Adding light base pass");
forward_plus_graph.add_node(LightBasePassLabel, LightBasePass::new()); forward_plus_graph.add_node(LightBasePassLabel, LightBasePass::new());
@ -171,8 +154,8 @@ impl BasicRenderer {
main_graph.add_sub_graph(TestSubGraphLabel, forward_plus_graph); main_graph.add_sub_graph(TestSubGraphLabel, forward_plus_graph);
main_graph.add_node(TestSubGraphLabel, SubGraphNode::new(TestSubGraphLabel, main_graph.add_node(TestSubGraphLabel, SubGraphNode::new(TestSubGraphLabel,
vec![ vec![
RenderGraphLabelValue::from(BasePassSlots::WindowTextureView), /* RenderGraphLabelValue::from(BasePassSlots::WindowTextureView),
RenderGraphLabelValue::from(BasePassSlots::MainRenderTarget), RenderGraphLabelValue::from(BasePassSlots::MainRenderTarget), */
RenderGraphLabelValue::from(BasePassSlots::DepthTexture), RenderGraphLabelValue::from(BasePassSlots::DepthTexture),
RenderGraphLabelValue::from(BasePassSlots::DepthTextureView), RenderGraphLabelValue::from(BasePassSlots::DepthTextureView),
RenderGraphLabelValue::from(BasePassSlots::Camera), RenderGraphLabelValue::from(BasePassSlots::Camera),
@ -181,12 +164,12 @@ impl BasicRenderer {
)); ));
} }
let present_pass_label = PresentPassLabel::new(BasePassSlots::Frame);//TintPassSlots::Frame); //let present_pass_label = PresentPassLabel::new(BasePassSlots::Frame);//TintPassSlots::Frame);
let p = PresentPass::from_node_label(present_pass_label.clone()); let p = PresentPass::default();
main_graph.add_node(p.label.clone(), p); main_graph.add_node(PresentPassLabel, p);
main_graph.add_edge(BasePassLabel, TestSubGraphLabel); main_graph.add_edge(BasePassLabel, TestSubGraphLabel);
main_graph.add_edge(TestSubGraphLabel, present_pass_label); main_graph.add_edge(TestSubGraphLabel, PresentPassLabel);
/* debug!("Adding base pass"); /* debug!("Adding base pass");
g.add_node(BasePassLabel, BasePass::new(surface_target)); g.add_node(BasePassLabel, BasePass::new(surface_target));
@ -259,9 +242,10 @@ impl Renderer for BasicRenderer {
self.size = new_size; self.size = new_size;
// 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.resize(&self.device, math::UVec2::new(new_size.width, new_size.height)); */
self.graph.view_target_mut().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); */
@ -269,6 +253,8 @@ impl Renderer for BasicRenderer {
// 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>();
world_ss.0 = glam::UVec2::new(new_size.width, new_size.height); world_ss.0 = glam::UVec2::new(new_size.width, new_size.height);
} }
} }

View File

@ -0,0 +1,31 @@
@group(0) @binding(0)
var t_screen: texture_2d<f32>;
@group(0) @binding(1)
var s_screen: sampler;
struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,
@location(0) tex_coords: vec2<f32>,
}
@vertex
fn vs_main(
@builtin(vertex_index) vertex_index: u32,
) -> VertexOutput {
const vertices = array<vec4<f32>, 4>(vec4<f32>(-1.0, -1.0, 0.0, 1.0), vec4<f32>(1.0, -1.0, 0.0, 1.0), vec4<f32>(-1.0, 1.0, 0.0, 1.0), vec4<f32>(1.0, 1.0, 0.0, 1.0));
const tex_coords = array<vec2<f32>, 4>(vec2<f32>(0.0, 0.0), vec2<f32>(1.0, 0.0), vec2<f32>(0.0, 1.0), vec2<f32>(1.0, 1.0));
var out: VertexOutput;
out.clip_position = vertices[vertex_index];
out.tex_coords = tex_coords[vertex_index];
return out;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let rgb: vec3<f32> = textureSample(t_screen, s_screen, in.tex_coords).xyz;
rgb *= vec3<f32>(0.8);
return vec4<f32>(rgb, 1.0);
}