render: move most of the mesh processing to a MeshPrepare node

Moving that out of the MeshesPass makes the rendering meshes accessible to other passes/nodes. The shadow pass will need access to them which is why this was done now
This commit is contained in:
SeanOMik 2024-06-29 22:23:49 -04:00
parent 7ff67a194b
commit 3a80c069c9
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
20 changed files with 1075 additions and 602 deletions

View File

@ -444,6 +444,11 @@ impl World {
.and_then(|r| r.try_get_mut()) .and_then(|r| r.try_get_mut())
} }
pub fn try_get_resource_data<T: ResourceObject>(&self) -> Option<ResourceData> {
self.resources.get(&TypeId::of::<T>())
.map(|r| r.clone())
}
/// Increments the TickTracker which is used for tracking changes to components. /// Increments the TickTracker which is used for tracking changes to components.
/// ///
/// Most users wont need to call this manually, its done for you through queries and views. /// Most users wont need to call this manually, its done for you through queries and views.

View File

@ -95,9 +95,9 @@ struct NodeEntry {
struct BindGroupEntry { struct BindGroupEntry {
label: RenderGraphLabelValue, label: RenderGraphLabelValue,
/// BindGroup /// BindGroup
bg: Rc<wgpu::BindGroup>, bg: Arc<wgpu::BindGroup>,
/// BindGroupLayout /// BindGroupLayout
layout: Option<Rc<wgpu::BindGroupLayout>>, layout: Option<Arc<wgpu::BindGroupLayout>>,
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -368,22 +368,23 @@ impl RenderGraph {
} }
#[inline(always)] #[inline(always)]
pub fn try_bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroup>> { pub fn try_bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Arc<wgpu::BindGroup>> {
self.bind_groups.get(&label.into()).map(|e| &e.bg) self.bind_groups.get(&label.into()).map(|e| &e.bg)
} }
#[inline(always)] #[inline(always)]
pub fn bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroup> { pub fn bind_group<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Arc<wgpu::BindGroup> {
self.try_bind_group(label).expect("Unknown id for bind group") let l = label.into();
self.try_bind_group(l.clone()).unwrap_or_else(|| panic!("Unknown label '{:?}' for bind group layout", l.clone()))
} }
#[inline(always)] #[inline(always)]
pub fn try_bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Rc<wgpu::BindGroupLayout>> { pub fn try_bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> Option<&Arc<wgpu::BindGroupLayout>> {
self.bind_groups.get(&label.into()).and_then(|e| e.layout.as_ref()) self.bind_groups.get(&label.into()).and_then(|e| e.layout.as_ref())
} }
#[inline(always)] #[inline(always)]
pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Rc<wgpu::BindGroupLayout> { pub fn bind_group_layout<L: Into<RenderGraphLabelValue>>(&self, label: L) -> &Arc<wgpu::BindGroupLayout> {
let l = label.into(); let l = label.into();
self.try_bind_group_layout(l.clone()) self.try_bind_group_layout(l.clone())
.unwrap_or_else(|| panic!("Unknown label '{:?}' for bind group layout", l.clone())) .unwrap_or_else(|| panic!("Unknown label '{:?}' for bind group layout", l.clone()))

View File

@ -1,4 +1,4 @@
use std::{cell::{Ref, RefCell, RefMut}, num::NonZeroU32, rc::Rc}; use std::{cell::{Ref, RefCell, RefMut}, num::NonZeroU32, rc::Rc, sync::Arc};
use bind_match::bind_match; use bind_match::bind_match;
use lyra_ecs::World; use lyra_ecs::World;
@ -54,16 +54,16 @@ pub enum SlotValue {
/// The value will be set during a later phase of the render graph. To see the type of value /// The value will be set during a later phase of the render graph. To see the type of value
/// this will be set to, see the slots type. /// this will be set to, see the slots type.
Lazy, Lazy,
TextureView(Rc<wgpu::TextureView>), TextureView(Arc<wgpu::TextureView>),
Sampler(Rc<wgpu::Sampler>), Sampler(Rc<wgpu::Sampler>),
Texture(Rc<wgpu::Texture>), Texture(Rc<wgpu::Texture>),
Buffer(Rc<wgpu::Buffer>), Buffer(Arc<wgpu::Buffer>),
RenderTarget(Rc<RefCell<RenderTarget>>), RenderTarget(Rc<RefCell<RenderTarget>>),
Frame(Rc<RefCell<Option<Frame>>>), Frame(Rc<RefCell<Option<Frame>>>),
} }
impl SlotValue { impl SlotValue {
pub fn as_texture_view(&self) -> Option<&Rc<wgpu::TextureView>> { pub fn as_texture_view(&self) -> Option<&Arc<wgpu::TextureView>> {
bind_match!(self, Self::TextureView(v) => v) bind_match!(self, Self::TextureView(v) => v)
} }
@ -75,7 +75,7 @@ impl SlotValue {
bind_match!(self, Self::Texture(v) => v) bind_match!(self, Self::Texture(v) => v)
} }
pub fn as_buffer(&self) -> Option<&Rc<wgpu::Buffer>> { pub fn as_buffer(&self) -> Option<&Arc<wgpu::Buffer>> {
bind_match!(self, Self::Buffer(v) => v) bind_match!(self, Self::Buffer(v) => v)
} }
@ -189,8 +189,8 @@ pub struct NodeDesc {
/// This makes the bind groups accessible to other Nodes. /// This makes the bind groups accessible to other Nodes.
pub bind_groups: Vec<( pub bind_groups: Vec<(
RenderGraphLabelValue, RenderGraphLabelValue,
Rc<wgpu::BindGroup>, Arc<wgpu::BindGroup>,
Option<Rc<wgpu::BindGroupLayout>>, Option<Arc<wgpu::BindGroupLayout>>,
)>, )>,
} }
@ -199,7 +199,7 @@ impl NodeDesc {
pub fn new( pub fn new(
pass_type: NodeType, pass_type: NodeType,
pipeline_desc: Option<PipelineDescriptor>, pipeline_desc: Option<PipelineDescriptor>,
bind_groups: Vec<(&dyn RenderGraphLabel, Rc<wgpu::BindGroup>, Option<Rc<wgpu::BindGroupLayout>>)>, bind_groups: Vec<(&dyn RenderGraphLabel, Arc<wgpu::BindGroup>, Option<Arc<wgpu::BindGroupLayout>>)>,
) -> Self { ) -> Self {
Self { Self {
ty: pass_type, ty: pass_type,

View File

@ -1,4 +1,4 @@
use std::rc::Rc; use std::sync::Arc;
use glam::UVec2; use glam::UVec2;
use lyra_game_derive::RenderGraphLabel; use lyra_game_derive::RenderGraphLabel;
@ -56,8 +56,8 @@ impl Node for BasePass {
.buffer_dynamic_offset(false) .buffer_dynamic_offset(false)
.contents(&[self.screen_size]) .contents(&[self.screen_size])
.finish_parts(graph.device()); .finish_parts(graph.device());
let screen_size_bgl = Rc::new(screen_size_bgl); let screen_size_bgl = Arc::new(screen_size_bgl);
let screen_size_bg = Rc::new(screen_size_bg); let screen_size_bg = Arc::new(screen_size_bg);
let (camera_bgl, camera_bg, camera_buf, _) = BufferWrapper::builder() let (camera_bgl, camera_bg, camera_buf, _) = BufferWrapper::builder()
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST) .buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
@ -66,17 +66,17 @@ impl Node for BasePass {
.buffer_dynamic_offset(false) .buffer_dynamic_offset(false)
.contents(&[CameraUniform::default()]) .contents(&[CameraUniform::default()])
.finish_parts(graph.device()); .finish_parts(graph.device());
let camera_bgl = Rc::new(camera_bgl); let camera_bgl = Arc::new(camera_bgl);
let camera_bg = Rc::new(camera_bg); let camera_bg = Arc::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(), self.screen_size, "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();
let depth_texture_bg = Rc::new(dt_bg_pair.bindgroup); let depth_texture_bg = Arc::new(dt_bg_pair.bindgroup);
let depth_texture_bgl = dt_bg_pair.layout; let depth_texture_bgl = dt_bg_pair.layout;
let depth_texture_view = Rc::new(depth_texture.view); let depth_texture_view = Arc::new(depth_texture.view);
let mut desc = NodeDesc::new( let mut desc = NodeDesc::new(
NodeType::Node, NodeType::Node,
@ -102,12 +102,12 @@ impl Node for BasePass {
desc.add_buffer_slot( desc.add_buffer_slot(
BasePassSlots::ScreenSize, BasePassSlots::ScreenSize,
SlotAttribute::Output, SlotAttribute::Output,
Some(SlotValue::Buffer(Rc::new(screen_size_buf))), Some(SlotValue::Buffer(Arc::new(screen_size_buf))),
); );
desc.add_buffer_slot( desc.add_buffer_slot(
BasePassSlots::Camera, BasePassSlots::Camera,
SlotAttribute::Output, SlotAttribute::Output,
Some(SlotValue::Buffer(Rc::new(camera_buf))), Some(SlotValue::Buffer(Arc::new(camera_buf))),
); );
desc desc

View File

@ -1,4 +1,4 @@
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc, sync::Arc};
use lyra_game_derive::RenderGraphLabel; use lyra_game_derive::RenderGraphLabel;
@ -13,7 +13,7 @@ pub struct FxaaPassLabel;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct FxaaPass { pub struct FxaaPass {
target_sampler: Option<wgpu::Sampler>, target_sampler: Option<wgpu::Sampler>,
bgl: Option<Rc<wgpu::BindGroupLayout>>, bgl: Option<Arc<wgpu::BindGroupLayout>>,
/// Store bind groups for the input textures. /// Store bind groups for the input textures.
/// The texture may change due to resizes, or changes to the view target chain /// The texture may change due to resizes, or changes to the view target chain
/// from other nodes. /// from other nodes.
@ -54,7 +54,7 @@ impl Node for FxaaPass {
}, },
], ],
}); });
let bgl = Rc::new(bgl); let bgl = Arc::new(bgl);
self.bgl = Some(bgl.clone()); self.bgl = Some(bgl.clone());
self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor { self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("fxaa sampler"), label: Some("fxaa sampler"),

View File

@ -1,4 +1,4 @@
use std::{mem, rc::Rc}; use std::{mem, rc::Rc, sync::Arc};
use glam::Vec2Swizzles; use glam::Vec2Swizzles;
use lyra_ecs::World; use lyra_ecs::World;
@ -63,7 +63,7 @@ impl Node for LightCullComputePass {
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
}); });
let light_indices_bg_layout = Rc::new(device.create_bind_group_layout( let light_indices_bg_layout = Arc::new(device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor { &wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
@ -128,7 +128,7 @@ impl Node for LightCullComputePass {
array_layer_count: None, array_layer_count: None,
}); });
let light_indices_bg = Rc::new(device.create_bind_group(&wgpu::BindGroupDescriptor { let light_indices_bg = Arc::new(device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &light_indices_bg_layout, layout: &light_indices_bg_layout,
entries: &[ entries: &[
wgpu::BindGroupEntry { wgpu::BindGroupEntry {
@ -194,7 +194,7 @@ impl Node for LightCullComputePass {
desc.add_buffer_slot( desc.add_buffer_slot(
LightCullComputePassSlots::IndexCounterBuffer, LightCullComputePassSlots::IndexCounterBuffer,
SlotAttribute::Output, SlotAttribute::Output,
Some(SlotValue::Buffer(Rc::new(light_index_counter_buffer))), Some(SlotValue::Buffer(Arc::new(light_index_counter_buffer))),
); );
desc desc

View File

@ -0,0 +1,767 @@
use std::{
collections::{HashSet, VecDeque},
ops::{Deref, DerefMut},
sync::Arc,
};
use glam::{UVec2, Vec3};
use image::GenericImageView;
use itertools::izip;
use lyra_ecs::{
query::{
filter::{Has, Not, Or},
Entities, Res, ResMut, TickOf,
},
relation::{ChildOf, RelationOriginComponent},
Component, Entity, ResourceObject, World,
};
use lyra_game_derive::RenderGraphLabel;
use lyra_math::Transform;
use lyra_resource::{gltf::Mesh, ResHandle};
use lyra_scene::{SceneGraph, WorldTransform};
use rustc_hash::FxHashMap;
use tracing::{debug, instrument};
use uuid::Uuid;
use wgpu::util::DeviceExt;
use crate::{
render::{
graph::{Node, NodeDesc, NodeType},
render_buffer::BufferStorage,
render_job::RenderJob,
texture::{res_filter_to_wgpu, res_wrap_to_wgpu},
transform_buffer_storage::{TransformBuffers, TransformGroup},
vertex::Vertex,
},
DeltaTime,
};
type MeshHandle = ResHandle<Mesh>;
type SceneHandle = ResHandle<SceneGraph>;
pub struct MeshBufferStorage {
pub buffer_vertex: BufferStorage,
pub buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>,
// maybe this should just be a Uuid and the material can be retrieved though
// MeshPass's `material_buffers` field?
pub material: Option<Arc<GpuMaterial>>,
}
#[derive(Clone, Debug, Component)]
struct InterpTransform {
last_transform: Transform,
alpha: f32,
}
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
pub struct MeshPrepNodeLabel;
#[derive(Debug)]
pub struct MeshPrepNode {
pub material_bgl: Arc<wgpu::BindGroupLayout>,
}
impl MeshPrepNode {
pub fn new(device: &wgpu::Device) -> Self {
let bgl = GpuMaterial::create_bind_group_layout(device);
Self { material_bgl: bgl }
}
/// Checks if the mesh buffers in the GPU need to be updated.
#[instrument(skip(self, device, mesh_buffers, queue, mesh_han))]
fn check_mesh_buffers(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
mesh_buffers: &mut FxHashMap<uuid::Uuid, MeshBufferStorage>,
mesh_han: &ResHandle<Mesh>,
) {
let mesh_uuid = mesh_han.uuid();
if let (Some(mesh), Some(buffers)) = (mesh_han.data_ref(), mesh_buffers.get_mut(&mesh_uuid))
{
// check if the buffer sizes dont match. If they dont, completely remake the buffers
let vertices = mesh.position().unwrap();
if buffers.buffer_vertex.count() != vertices.len() {
debug!("Recreating buffers for mesh {}", mesh_uuid.to_string());
let (vert, idx) = self.create_vertex_index_buffers(device, &mesh);
// have to re-get buffers because of borrow checker
let buffers = mesh_buffers.get_mut(&mesh_uuid).unwrap();
buffers.buffer_indices = idx;
buffers.buffer_vertex = vert;
return;
}
// update vertices
let vertex_buffer = buffers.buffer_vertex.buffer();
let vertices = vertices.as_slice();
// align the vertices to 4 bytes (u32 is 4 bytes, which is wgpu::COPY_BUFFER_ALIGNMENT)
let (_, vertices, _) = bytemuck::pod_align_to::<Vec3, u32>(vertices);
queue.write_buffer(vertex_buffer, 0, bytemuck::cast_slice(vertices));
// update the indices if they're given
if let Some(index_buffer) = buffers.buffer_indices.as_ref() {
let aligned_indices = match mesh.indices.as_ref().unwrap() {
// U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size.
lyra_resource::gltf::MeshIndices::U16(v) => {
bytemuck::pod_align_to::<u16, u32>(v).1
}
lyra_resource::gltf::MeshIndices::U32(v) => {
bytemuck::pod_align_to::<u32, u32>(v).1
}
};
let index_buffer = index_buffer.1.buffer();
queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(aligned_indices));
}
}
}
#[instrument(skip(self, device, mesh))]
fn create_vertex_index_buffers(
&mut self,
device: &wgpu::Device,
mesh: &Mesh,
) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) {
let positions = mesh.position().unwrap();
let tex_coords: Vec<glam::Vec2> = mesh
.tex_coords()
.cloned()
.unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]);
let normals = mesh.normals().unwrap();
assert!(positions.len() == tex_coords.len() && positions.len() == normals.len());
let mut vertex_inputs = vec![];
for (v, t, n) in izip!(positions.iter(), tex_coords.iter(), normals.iter()) {
vertex_inputs.push(Vertex::new(*v, *t, *n));
}
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(vertex_inputs.as_slice()), //vertex_combined.as_slice(),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
});
let vertex_buffer = BufferStorage::new(vertex_buffer, 0, vertex_inputs.len());
let indices = match mesh.indices.as_ref() {
Some(indices) => {
let (idx_type, len, contents) = match indices {
lyra_resource::gltf::MeshIndices::U16(v) => {
(wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(v))
}
lyra_resource::gltf::MeshIndices::U32(v) => {
(wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(v))
}
};
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents,
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
});
let buffer_indices = BufferStorage::new(index_buffer, 0, len);
Some((idx_type, buffer_indices))
}
None => None,
};
(vertex_buffer, indices)
}
#[instrument(skip(self, device, queue, material_buffers, mesh))]
fn create_mesh_buffers(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
material_buffers: &mut RenderAssets<Arc<GpuMaterial>>,
mesh: &Mesh,
) -> MeshBufferStorage {
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(device, mesh);
let material = mesh
.material
.as_ref()
.expect("Material resource not loaded yet");
let material_ref = material.data_ref().unwrap();
let material = material_buffers.entry(material.uuid()).or_insert_with(|| {
debug!(
uuid = material.uuid().to_string(),
"Sending material to gpu"
);
Arc::new(GpuMaterial::from_resource(
device,
queue,
&self.material_bgl,
&material_ref,
))
});
MeshBufferStorage {
buffer_vertex: vertex_buffer,
buffer_indices,
material: Some(material.clone()),
}
}
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
#[instrument(skip(
self,
device,
queue,
mesh_buffers,
material_buffers,
entity_meshes,
mesh,
entity
))]
fn process_mesh(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
mesh_buffers: &mut RenderAssets<MeshBufferStorage>,
material_buffers: &mut RenderAssets<Arc<GpuMaterial>>,
entity_meshes: &mut FxHashMap<Entity, uuid::Uuid>,
entity: Entity,
mesh: &Mesh,
mesh_uuid: Uuid,
) -> bool {
#[allow(clippy::map_entry)]
if !mesh_buffers.contains_key(&mesh_uuid) {
// create the mesh's buffers
let buffers = self.create_mesh_buffers(device, queue, material_buffers, mesh);
mesh_buffers.insert(mesh_uuid, buffers);
entity_meshes.insert(entity, mesh_uuid);
true
} else {
false
}
}
/// If the resource does not exist in the world, add the default
fn try_init_resource<T: ResourceObject + Default>(world: &mut World) {
if !world.has_resource::<T>() {
world.add_resource_default::<T>();
}
}
}
impl Node for MeshPrepNode {
fn desc(
&mut self,
_: &mut crate::render::graph::RenderGraph,
) -> crate::render::graph::NodeDesc {
NodeDesc::new(NodeType::Node, None, vec![])
}
fn prepare(
&mut self,
_: &mut crate::render::graph::RenderGraph,
world: &mut lyra_ecs::World,
context: &mut crate::render::graph::RenderGraphContext,
) {
let device = &context.device;
let queue = &context.queue;
let render_limits = device.limits();
let last_epoch = world.current_tick();
let mut alive_entities = HashSet::new();
{
// prepare the world with resources
if !world.has_resource::<TransformBuffers>() {
let buffers = TransformBuffers::new(device);
world.add_resource(buffers);
}
Self::try_init_resource::<RenderMeshes>(world);
Self::try_init_resource::<RenderAssets<MeshBufferStorage>>(world);
Self::try_init_resource::<RenderAssets<Arc<GpuMaterial>>>(world);
Self::try_init_resource::<FxHashMap<Entity, uuid::Uuid>>(world);
let mut render_meshes = world.get_resource_mut::<RenderMeshes>();
render_meshes.clear();
}
let view = world.view_iter::<(
Entities,
&Transform,
TickOf<Transform>,
Or<(&MeshHandle, TickOf<MeshHandle>), (&SceneHandle, TickOf<SceneHandle>)>,
Option<&mut InterpTransform>,
Res<DeltaTime>,
ResMut<TransformBuffers>,
ResMut<RenderMeshes>,
ResMut<RenderAssets<MeshBufferStorage>>,
ResMut<RenderAssets<Arc<GpuMaterial>>>,
ResMut<FxHashMap<Entity, uuid::Uuid>>,
)>();
// used to store InterpTransform components to add to entities later
let mut component_queue: Vec<(Entity, InterpTransform)> = vec![];
for (
entity,
transform,
_transform_epoch,
(mesh_pair, scene_pair),
interp_tran,
delta_time,
mut transforms,
mut render_meshes,
mut mesh_buffers,
mut material_buffers,
mut entity_meshes,
) in view
{
alive_entities.insert(entity);
// Interpolate the transform for this entity using a component.
// If the entity does not have the component then it will be queued to be added
// to it after all the entities are prepared for rendering.
let interp_transform = match interp_tran {
Some(mut interp_transform) => {
// found in https://youtu.be/YJB1QnEmlTs?t=472
interp_transform.alpha = 1.0 - interp_transform.alpha.powf(**delta_time);
interp_transform.last_transform = interp_transform
.last_transform
.lerp(*transform, interp_transform.alpha);
interp_transform.last_transform
}
None => {
let interp = InterpTransform {
last_transform: *transform,
alpha: 0.5,
};
component_queue.push((entity, interp));
*transform
}
};
{
// expand the transform buffers if they need to be.
// this is done in its own scope to avoid multiple mutable references to self at
// once; aka, make the borrow checker happy
if transforms.needs_expand() {
debug!("Expanding transform buffers");
transforms.expand_buffers(device);
}
}
if let Some((mesh_han, mesh_epoch)) = mesh_pair {
if let Some(mesh) = mesh_han.data_ref() {
// if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers
// dont need to be resent to the gpu.
if !self.process_mesh(
device,
queue,
&mut mesh_buffers,
&mut material_buffers,
&mut entity_meshes,
entity,
&mesh,
mesh_han.uuid(),
) && mesh_epoch == last_epoch
{
self.check_mesh_buffers(device, queue, &mut mesh_buffers, &mesh_han);
}
let group = TransformGroup::EntityRes(entity, mesh_han.uuid());
let transform_id = transforms.update_or_push(
device,
queue,
&render_limits,
group,
interp_transform.calculate_mat4(),
glam::Mat3::from_quat(interp_transform.rotation),
);
let material = mesh.material.as_ref().unwrap().data_ref().unwrap();
let shader = material.shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
render_meshes.push_back(job);
}
}
if let Some((scene_han, scene_epoch)) = scene_pair {
if let Some(scene) = scene_han.data_ref() {
if scene_epoch == last_epoch {
let view = scene.world().view::<(
Entities,
&mut WorldTransform,
&Transform,
Not<Has<RelationOriginComponent<ChildOf>>>,
)>();
lyra_scene::system_update_world_transforms(scene.world(), view).unwrap();
}
for (mesh_han, pos) in
scene.world().view_iter::<(&MeshHandle, &WorldTransform)>()
{
if let Some(mesh) = mesh_han.data_ref() {
let mesh_interpo = interp_transform + **pos;
// if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers
// dont need to be resent to the gpu.
if !self.process_mesh(
device,
queue,
&mut mesh_buffers,
&mut material_buffers,
&mut entity_meshes,
entity,
&mesh,
mesh_han.uuid(),
) && scene_epoch == last_epoch
{
self.check_mesh_buffers(
device,
queue,
&mut mesh_buffers,
&mesh_han,
);
}
let scene_mesh_group =
TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
let transform_id = transforms.update_or_push(
device,
queue,
&render_limits,
group,
mesh_interpo.calculate_mat4(),
glam::Mat3::from_quat(mesh_interpo.rotation),
);
let material = mesh.material.as_ref().unwrap().data_ref().unwrap();
let shader = material.shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
render_meshes.push_back(job);
}
}
}
}
}
for (en, interp) in component_queue {
world.insert(en, interp);
}
let mut transforms = world.get_resource_mut::<TransformBuffers>();
transforms.send_to_gpu(queue);
}
fn execute(
&mut self,
_: &mut crate::render::graph::RenderGraph,
_: &crate::render::graph::NodeDesc,
_: &mut crate::render::graph::RenderGraphContext,
) {
}
}
#[repr(transparent)]
pub struct RenderAssets<T>(FxHashMap<Uuid, T>);
impl<T> Deref for RenderAssets<T> {
type Target = FxHashMap<Uuid, T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for RenderAssets<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> Default for RenderAssets<T> {
fn default() -> Self {
Self(Default::default())
}
}
impl<T> RenderAssets<T> {
pub fn new() -> Self {
Self::default()
}
}
#[allow(dead_code)]
pub struct GpuMaterial {
pub bind_group: Arc<wgpu::BindGroup>,
bind_group_layout: Arc<wgpu::BindGroupLayout>,
material_properties_buffer: wgpu::Buffer,
diffuse_texture: wgpu::Texture,
diffuse_texture_sampler: wgpu::Sampler,
/* specular_texture: wgpu::Texture,
specular_texture_sampler: wgpu::Sampler, */
}
impl GpuMaterial {
fn create_bind_group_layout(device: &wgpu::Device) -> Arc<wgpu::BindGroupLayout> {
Arc::new(
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("bgl_material"),
entries: &[
// material properties
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None, /* Some(
NonZeroU64::new(mem::size_of::<MaterialPropertiesUniform>() as _)
.unwrap(),
) */
},
count: None,
},
// diffuse texture
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
// diffuse texture sampler
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
// specular texture
/* wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: false },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
// specular texture sampler
wgpu::BindGroupLayoutEntry {
binding: 4,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
count: None,
}, */
],
}),
)
}
fn texture_desc(label: &str, size: UVec2) -> wgpu::TextureDescriptor {
//debug!("Texture desc size: {:?}", size);
wgpu::TextureDescriptor {
label: Some(label),
size: wgpu::Extent3d {
width: size.x,
height: size.y,
depth_or_array_layers: 1,
},
mip_level_count: 1, // TODO
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
}
}
fn write_texture(queue: &wgpu::Queue, texture: &wgpu::Texture, img: &lyra_resource::Image) {
let dim = img.dimensions();
//debug!("Write texture size: {:?}", dim);
queue.write_texture(
wgpu::ImageCopyTexture {
aspect: wgpu::TextureAspect::All,
texture: &texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
&img.to_rgba8(),
wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: std::num::NonZeroU32::new(4 * dim.0),
rows_per_image: std::num::NonZeroU32::new(dim.1),
},
wgpu::Extent3d {
width: dim.0,
height: dim.1,
depth_or_array_layers: 1,
},
);
}
fn from_resource(
device: &wgpu::Device,
queue: &wgpu::Queue,
layout: &Arc<wgpu::BindGroupLayout>,
mat: &lyra_resource::gltf::Material,
) -> Self {
//let specular = mat.specular.as_ref().unwrap_or_default();
//let specular_
let prop = MaterialPropertiesUniform {
ambient: Vec3::ONE,
_padding1: 0,
diffuse: Vec3::ONE,
shininess: 32.0,
specular_factor: 0.0,
_padding2: [0; 3],
specular_color_factor: Vec3::ZERO,
_padding3: 0,
};
let properties_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("buffer_material"),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
contents: bytemuck::bytes_of(&prop),
});
let diffuse_tex = mat.base_color_texture.as_ref().unwrap();
let diffuse_tex = diffuse_tex.data_ref().unwrap();
let diffuse_tex_img = diffuse_tex.image.data_ref().unwrap();
let diffuse_tex_dim = diffuse_tex_img.dimensions();
let diffuse_texture = device.create_texture(&Self::texture_desc(
"material_diffuse_texture",
UVec2::new(diffuse_tex_dim.0, diffuse_tex_dim.1),
));
let diffuse_tex_view = diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler_desc = match &diffuse_tex.sampler {
Some(sampler) => {
let magf = res_filter_to_wgpu(
sampler
.mag_filter
.unwrap_or(lyra_resource::FilterMode::Linear),
);
let minf = res_filter_to_wgpu(
sampler
.min_filter
.unwrap_or(lyra_resource::FilterMode::Nearest),
);
let mipf = res_filter_to_wgpu(
sampler
.mipmap_filter
.unwrap_or(lyra_resource::FilterMode::Nearest),
);
let wrap_u = res_wrap_to_wgpu(sampler.wrap_u);
let wrap_v = res_wrap_to_wgpu(sampler.wrap_v);
let wrap_w = res_wrap_to_wgpu(sampler.wrap_w);
wgpu::SamplerDescriptor {
address_mode_u: wrap_u,
address_mode_v: wrap_v,
address_mode_w: wrap_w,
mag_filter: magf,
min_filter: minf,
mipmap_filter: mipf,
..Default::default()
}
}
None => wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
},
};
let diffuse_sampler = device.create_sampler(&sampler_desc);
Self::write_texture(queue, &diffuse_texture, &diffuse_tex_img);
debug!("TODO: specular texture");
let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("bg_material"),
layout: &layout,
entries: &[
// material properties
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: &properties_buffer,
offset: 0,
size: None,
}),
},
// diffuse texture
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&diffuse_tex_view),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::Sampler(&diffuse_sampler),
},
// TODO: specular textures
],
});
Self {
bind_group: Arc::new(bg),
bind_group_layout: layout.clone(),
material_properties_buffer: properties_buffer,
diffuse_texture,
diffuse_texture_sampler: diffuse_sampler,
}
}
}
/// Uniform for MaterialProperties in a shader
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct MaterialPropertiesUniform {
ambient: glam::Vec3,
_padding1: u32,
diffuse: glam::Vec3,
shininess: f32,
specular_factor: f32,
_padding2: [u32; 3],
specular_color_factor: glam::Vec3,
_padding3: u32,
}
#[derive(Default)]
pub struct RenderMeshes(VecDeque<RenderJob>);
impl Deref for RenderMeshes {
type Target = VecDeque<RenderJob>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for RenderMeshes {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

View File

@ -1,497 +1,175 @@
use std::{collections::{HashSet, VecDeque}, rc::Rc}; use std::{rc::Rc, sync::Arc};
use glam::Vec3; use lyra_ecs::{AtomicRef, ResourceData};
use itertools::izip;
use lyra_ecs::{query::{filter::{Has, Not, Or}, Entities, Res, TickOf}, relation::{ChildOf, RelationOriginComponent}, Component, Entity};
use lyra_game_derive::RenderGraphLabel; use lyra_game_derive::RenderGraphLabel;
use lyra_math::Transform; use tracing::{instrument, warn};
use lyra_resource::{gltf::Mesh, ResHandle};
use lyra_scene::{SceneGraph, WorldTransform};
use rustc_hash::FxHashMap;
use tracing::{debug, instrument, warn};
use uuid::Uuid;
use wgpu::util::DeviceExt;
use crate::{ use crate::render::{
render::{ desc_buf_lay::DescVertexBufferLayout,
desc_buf_lay::DescVertexBufferLayout, graph::{ graph::{Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext},
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState},
}, material::{Material, MaterialUniform}, render_buffer::{BufferStorage, BufferWrapper}, render_job::RenderJob, resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState}, texture::RenderTexture, transform_buffer_storage::{TransformBuffers, TransformGroup}, vertex::Vertex texture::RenderTexture,
}, transform_buffer_storage::TransformBuffers,
DeltaTime, vertex::Vertex,
}; };
use super::{BasePassSlots, LightBasePassSlots, LightCullComputePassSlots}; use super::{
BasePassSlots, LightBasePassSlots, LightCullComputePassSlots, MeshBufferStorage, RenderAssets,
type MeshHandle = ResHandle<Mesh>; RenderMeshes,
type SceneHandle = ResHandle<SceneGraph>; };
#[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)] #[derive(Debug, Hash, Clone, Default, PartialEq, RenderGraphLabel)]
pub struct MeshesPassLabel; pub struct MeshesPassLabel;
#[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)] #[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)]
pub enum MeshesPassSlots { pub enum MeshesPassSlots {
Material Material,
} }
struct MeshBufferStorage { //#[derive(Default)]
buffer_vertex: BufferStorage, #[allow(dead_code)]
buffer_indices: Option<(wgpu::IndexFormat, BufferStorage)>,
// maybe this should just be a Uuid and the material can be retrieved though
// MeshPass's `material_buffers` field?
material: Option<Rc<Material>>,
}
#[derive(Clone, Debug, Component)]
struct InterpTransform {
last_transform: Transform,
alpha: f32,
}
#[derive(Default)]
pub struct MeshPass { pub struct MeshPass {
transforms: Option<TransformBuffers>,
mesh_buffers: FxHashMap<uuid::Uuid, MeshBufferStorage>,
render_jobs: VecDeque<RenderJob>,
texture_bind_group_layout: Option<Rc<wgpu::BindGroupLayout>>,
material_buffer: Option<wgpu::Buffer>,
material_buffers: FxHashMap<uuid::Uuid, Rc<Material>>,
entity_meshes: FxHashMap<Entity, uuid::Uuid>,
default_texture: Option<RenderTexture>, default_texture: Option<RenderTexture>,
pipeline: Option<RenderPipeline>, pipeline: Option<RenderPipeline>,
material_bgl: Option<Rc<wgpu::BindGroupLayout>>, material_bgl: Arc<wgpu::BindGroupLayout>,
transform_buffers: Option<ResourceData>,
// TODO: find a better way to extract these resources from the main world to be used in the
// render stage.
render_meshes: Option<ResourceData>,
mesh_buffers: Option<ResourceData>,
} }
impl MeshPass { impl MeshPass {
pub fn new() -> Self { pub fn new(material_bgl: Arc<wgpu::BindGroupLayout>) -> Self {
Self::default() Self {
} default_texture: None,
pipeline: None,
/// Checks if the mesh buffers in the GPU need to be updated. material_bgl,
#[instrument(skip(self, device, queue, mesh_han))] transform_buffers: None,
fn check_mesh_buffers(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, mesh_han: &ResHandle<Mesh>) { render_meshes: None,
let mesh_uuid = mesh_han.uuid(); mesh_buffers: None,
if let (Some(mesh), Some(buffers)) = (mesh_han.data_ref(), self.mesh_buffers.get_mut(&mesh_uuid)) {
// check if the buffer sizes dont match. If they dont, completely remake the buffers
let vertices = mesh.position().unwrap();
if buffers.buffer_vertex.count() != vertices.len() {
debug!("Recreating buffers for mesh {}", mesh_uuid.to_string());
let (vert, idx) = self.create_vertex_index_buffers(device, &mesh);
// have to re-get buffers because of borrow checker
let buffers = self.mesh_buffers.get_mut(&mesh_uuid).unwrap();
buffers.buffer_indices = idx;
buffers.buffer_vertex = vert;
return;
}
// update vertices
let vertex_buffer = buffers.buffer_vertex.buffer();
let vertices = vertices.as_slice();
// align the vertices to 4 bytes (u32 is 4 bytes, which is wgpu::COPY_BUFFER_ALIGNMENT)
let (_, vertices, _) = bytemuck::pod_align_to::<Vec3, u32>(vertices);
queue.write_buffer(vertex_buffer, 0, bytemuck::cast_slice(vertices));
// update the indices if they're given
if let Some(index_buffer) = buffers.buffer_indices.as_ref() {
let aligned_indices = match mesh.indices.as_ref().unwrap() {
// U16 indices need to be aligned to u32, for wpgu, which are 4-bytes in size.
lyra_resource::gltf::MeshIndices::U16(v) => bytemuck::pod_align_to::<u16, u32>(v).1,
lyra_resource::gltf::MeshIndices::U32(v) => bytemuck::pod_align_to::<u32, u32>(v).1,
};
let index_buffer = index_buffer.1.buffer();
queue.write_buffer(index_buffer, 0, bytemuck::cast_slice(aligned_indices));
}
} }
} }
#[instrument(skip(self, device, mesh))] fn transform_buffers(&self) -> AtomicRef<TransformBuffers> {
fn create_vertex_index_buffers(&mut self, device: &wgpu::Device, mesh: &Mesh) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) { self.transform_buffers
let positions = mesh.position().unwrap(); .as_ref()
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned() .unwrap()
.unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]); .get()
let normals = mesh.normals().unwrap();
assert!(positions.len() == tex_coords.len() && positions.len() == normals.len());
let mut vertex_inputs = vec![];
for (v, t, n) in izip!(positions.iter(), tex_coords.iter(), normals.iter()) {
vertex_inputs.push(Vertex::new(*v, *t, *n));
}
let vertex_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(vertex_inputs.as_slice()),//vertex_combined.as_slice(),
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages:: COPY_DST,
}
);
let vertex_buffer = BufferStorage::new(vertex_buffer, 0, vertex_inputs.len());
let indices = match mesh.indices.as_ref() {
Some(indices) => {
let (idx_type, len, contents) = match indices {
lyra_resource::gltf::MeshIndices::U16(v) => (wgpu::IndexFormat::Uint16, v.len(), bytemuck::cast_slice(v)),
lyra_resource::gltf::MeshIndices::U32(v) => (wgpu::IndexFormat::Uint32, v.len(), bytemuck::cast_slice(v)),
};
let index_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents,
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages:: COPY_DST,
}
);
let buffer_indices = BufferStorage::new(index_buffer, 0, len);
Some((idx_type, buffer_indices))
},
None => {
None
}
};
( vertex_buffer, indices )
} }
#[instrument(skip(self, device, queue, mesh))] fn render_meshes(&self) -> AtomicRef<RenderMeshes> {
fn create_mesh_buffers(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, mesh: &Mesh) -> MeshBufferStorage { self.render_meshes
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(device, mesh); .as_ref()
.unwrap()
let material = mesh.material.as_ref() .get()
.expect("Material resource not loaded yet");
let material_ref = material.data_ref()
.unwrap();
let material = self.material_buffers.entry(material.uuid())
.or_insert_with(|| {
debug!(uuid=material.uuid().to_string(), "Sending material to gpu");
Rc::new(Material::from_resource(device, queue, self.texture_bind_group_layout.clone().unwrap(), &material_ref))
});
// TODO: support material uniforms from multiple uniforms
let uni = MaterialUniform::from(&**material);
queue.write_buffer(self.material_buffer.as_ref().unwrap(), 0, bytemuck::bytes_of(&uni));
MeshBufferStorage {
buffer_vertex: vertex_buffer,
buffer_indices,
material: Some(material.clone()),
}
} }
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed. fn mesh_buffers(&self) -> AtomicRef<RenderAssets<MeshBufferStorage>> {
#[instrument(skip(self, device, queue, mesh, entity))] self.mesh_buffers
fn process_mesh(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, entity: Entity, mesh: &Mesh, mesh_uuid: Uuid) -> bool { .as_ref()
#[allow(clippy::map_entry)] .unwrap()
if !self.mesh_buffers.contains_key(&mesh_uuid) { .get()
// create the mesh's buffers
let buffers = self.create_mesh_buffers(device, queue, mesh);
self.mesh_buffers.insert(mesh_uuid, buffers);
self.entity_meshes.insert(entity, mesh_uuid);
true
} else { false }
} }
} }
impl Node for MeshPass { impl Node for MeshPass {
fn desc( fn desc(
&mut self, &mut self,
graph: &mut crate::render::graph::RenderGraph, _: &mut crate::render::graph::RenderGraph,
) -> crate::render::graph::NodeDesc { ) -> crate::render::graph::NodeDesc {
let device = graph.device();
let transforms = TransformBuffers::new(device);
//let transform_bgl = transforms.bindgroup_layout.clone();
self.transforms = Some(transforms);
let texture_bind_group_layout = Rc::new(RenderTexture::create_layout(device));
self.texture_bind_group_layout = Some(texture_bind_group_layout.clone());
let (material_bgl, material_bg, material_buf, _) = BufferWrapper::builder()
.label_prefix("material")
.visibility(wgpu::ShaderStages::FRAGMENT)
.buffer_usage(wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST)
.contents(&[MaterialUniform::default()])
.finish_parts(device);
let material_bgl = Rc::new(material_bgl);
self.material_bgl = Some(material_bgl.clone());
let material_bg = Rc::new(material_bg);
self.material_buffer = Some(material_buf);
// load the default texture // load the default texture
let bytes = include_bytes!("../../default_texture.png"); //let bytes = include_bytes!("../../default_texture.png");
self.default_texture = Some(RenderTexture::from_bytes(device, &graph.queue, texture_bind_group_layout.clone(), bytes, "default_texture").unwrap()); //self.default_texture = Some(RenderTexture::from_bytes(device, &graph.queue, texture_bind_group_layout.clone(), bytes, "default_texture").unwrap());
// 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 camera_bgl = graph.bind_group_layout(BasePassSlots::Camera);
let lights_bgl = graph.bind_group_layout(LightBasePassSlots::Lights);
let light_grid_bgl = graph
.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
let shader = Rc::new(Shader {
label: Some("base_shader".into()),
source: include_str!("../../shaders/base.wgsl").to_string(),
}); */
NodeDesc::new( NodeDesc::new(
NodeType::Render, NodeType::Render,
None, None,
/* Some(PipelineDescriptor::Render(RenderPipelineDescriptor {
label: Some("meshes".into()),
layouts: vec![
texture_bind_group_layout.clone(),
transform_bgl,
camera_bgl.clone(),
lights_bgl.clone(),
material_bgl.clone(),
texture_bind_group_layout,
light_grid_bgl.clone(),
],
push_constant_ranges: vec![],
vertex: VertexState {
module: shader.clone(),
entry_point: "vs_main".into(),
buffers: vec![
Vertex::desc().into(),
],
},
fragment: Some(FragmentState {
module: shader,
entry_point: "fs_main".into(),
targets: vec![Some(wgpu::ColorTargetState {
format: surface_config_format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
depth_stencil: Some(wgpu::DepthStencilState {
format: RenderTexture::DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(), // TODO: stencil buffer
bias: wgpu::DepthBiasState::default(),
}),
primitive: wgpu::PrimitiveState::default(),
multisample: wgpu::MultisampleState::default(),
multiview: None,
})), */
vec![ vec![
(&MeshesPassSlots::Material, material_bg, Some(material_bgl)), //(&MeshesPassSlots::Material, material_bg, Some(material_bgl)),
], ],
) )
} }
#[instrument(skip(self, graph, world, context))] #[instrument(skip(self, graph, world))]
fn prepare(&mut self, graph: &mut RenderGraph, world: &mut lyra_ecs::World, context: &mut RenderGraphContext) { fn prepare(
let device = &context.device; &mut self,
let queue = &context.queue; graph: &mut RenderGraph,
let render_limits = device.limits(); world: &mut lyra_ecs::World,
_: &mut RenderGraphContext,
let last_epoch = world.current_tick(); ) {
let mut alive_entities = HashSet::new();
let view = world.view_iter::<(
Entities,
&Transform,
TickOf<Transform>,
Or<
(&MeshHandle, TickOf<MeshHandle>),
(&SceneHandle, TickOf<SceneHandle>)
>,
Option<&mut InterpTransform>,
Res<DeltaTime>,
)>();
// used to store InterpTransform components to add to entities later
let mut component_queue: Vec<(Entity, InterpTransform)> = vec![];
for (
entity,
transform,
_transform_epoch,
(
mesh_pair,
scene_pair
),
interp_tran,
delta_time,
) in view
{
alive_entities.insert(entity);
// Interpolate the transform for this entity using a component.
// If the entity does not have the component then it will be queued to be added
// to it after all the entities are prepared for rendering.
let interp_transform = match interp_tran {
Some(mut interp_transform) => {
// found in https://youtu.be/YJB1QnEmlTs?t=472
interp_transform.alpha = 1.0 - interp_transform.alpha.powf(**delta_time);
interp_transform.last_transform = interp_transform.last_transform.lerp(*transform, interp_transform.alpha);
interp_transform.last_transform
},
None => {
let interp = InterpTransform {
last_transform: *transform,
alpha: 0.5,
};
component_queue.push((entity, interp));
*transform
}
};
{
// expand the transform buffers if they need to be.
// this is done in its own scope to avoid multiple mutable references to self at
// once; aka, make the borrow checker happy
let transforms = self.transforms.as_mut().unwrap();
if transforms.needs_expand() {
debug!("Expanding transform buffers");
transforms.expand_buffers(device);
}
}
if let Some((mesh_han, mesh_epoch)) = mesh_pair {
if let Some(mesh) = mesh_han.data_ref() {
// if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers
// dont need to be resent to the gpu.
if !self.process_mesh(device, queue, entity, &mesh, mesh_han.uuid())
&& mesh_epoch == last_epoch {
self.check_mesh_buffers(device, queue, &mesh_han);
}
let transforms = self.transforms.as_mut().unwrap();
let group = TransformGroup::EntityRes(entity, mesh_han.uuid());
let transform_id = transforms.update_or_push(device, queue, &render_limits,
group, interp_transform.calculate_mat4(), glam::Mat3::from_quat(interp_transform.rotation));
let material = mesh.material.as_ref().unwrap()
.data_ref().unwrap();
let shader = material.shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
self.render_jobs.push_back(job);
}
}
if let Some((scene_han, scene_epoch)) = scene_pair {
if let Some(scene) = scene_han.data_ref() {
if scene_epoch == last_epoch {
let view = scene.world().view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
lyra_scene::system_update_world_transforms(scene.world(), view).unwrap();
}
for (mesh_han, pos) in scene.world().view_iter::<(&MeshHandle, &WorldTransform)>() {
if let Some(mesh) = mesh_han.data_ref() {
let mesh_interpo = interp_transform + **pos;
// if process mesh did not just create a new mesh, and the epoch
// shows that the scene has changed, verify that the mesh buffers
// dont need to be resent to the gpu.
if !self.process_mesh(device, queue, entity, &mesh, mesh_han.uuid())
&& scene_epoch == last_epoch {
self.check_mesh_buffers(device, queue, &mesh_han);
}
let transforms = self.transforms.as_mut().unwrap();
let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
let transform_id = transforms.update_or_push(device, queue, &render_limits,
group, mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) );
let material = mesh.material.as_ref().unwrap()
.data_ref().unwrap();
let shader = material.shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh_han.uuid(), transform_id);
self.render_jobs.push_back(job);
}
}
}
}
}
for (en, interp) in component_queue {
world.insert(en, interp);
}
let transforms = self.transforms.as_mut().unwrap();
transforms.send_to_gpu(queue);
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 surface_config_format = graph.view_target().format();
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);
let light_grid_bgl = graph let light_grid_bgl =
.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup); graph.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
let shader = Rc::new(Shader { let shader = Rc::new(Shader {
label: Some("base_shader".into()), label: Some("base_shader".into()),
source: include_str!("../../shaders/base.wgsl").to_string(), source: include_str!("../../shaders/base.wgsl").to_string(),
}); });
self.pipeline = Some(RenderPipeline::create(device, &RenderPipelineDescriptor {
label: Some("meshes".into()), let transforms = world
layouts: vec![ .try_get_resource_data::<TransformBuffers>()
self.texture_bind_group_layout.as_ref().unwrap().clone(), .expect("Missing transform buffers");
//transform_bgl self.transform_buffers = Some(transforms.clone());
self.transforms.as_ref().unwrap().bindgroup_layout.clone(),
camera_bgl.clone(), let render_meshes = world
lights_bgl.clone(), .try_get_resource_data::<RenderMeshes>()
self.material_bgl.as_ref().unwrap().clone(), .expect("Missing transform buffers");
self.texture_bind_group_layout.as_ref().unwrap().clone(), self.render_meshes = Some(render_meshes.clone());
light_grid_bgl.clone(),
], let mesh_buffers = world
push_constant_ranges: vec![], .try_get_resource_data::<RenderAssets<MeshBufferStorage>>()
vertex: VertexState { .expect("Missing render meshes");
module: shader.clone(), self.mesh_buffers = Some(mesh_buffers.clone());
entry_point: "vs_main".into(),
buffers: vec![
Vertex::desc().into(), let transforms = transforms.get::<TransformBuffers>();
self.pipeline = Some(RenderPipeline::create(
device,
&RenderPipelineDescriptor {
label: Some("meshes".into()),
layouts: vec![
self.material_bgl.clone(),
transforms.bindgroup_layout.clone(),
camera_bgl.clone(),
lights_bgl.clone(),
light_grid_bgl.clone(),
], ],
push_constant_ranges: vec![],
vertex: VertexState {
module: shader.clone(),
entry_point: "vs_main".into(),
buffers: vec![Vertex::desc().into()],
},
fragment: Some(FragmentState {
module: shader,
entry_point: "fs_main".into(),
targets: vec![Some(wgpu::ColorTargetState {
format: surface_config_format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
depth_stencil: Some(wgpu::DepthStencilState {
format: RenderTexture::DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(), // TODO: stencil buffer
bias: wgpu::DepthBiasState::default(),
}),
primitive: wgpu::PrimitiveState::default(),
multisample: wgpu::MultisampleState::default(),
multiview: None,
}, },
fragment: Some(FragmentState { ));
module: shader,
entry_point: "fs_main".into(),
targets: vec![Some(wgpu::ColorTargetState {
format: surface_config_format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
depth_stencil: Some(wgpu::DepthStencilState {
format: RenderTexture::DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(), // TODO: stencil buffer
bias: wgpu::DepthBiasState::default(),
}),
primitive: wgpu::PrimitiveState::default(),
multisample: wgpu::MultisampleState::default(),
multiview: None,
}));
} }
} }
@ -504,10 +182,10 @@ 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 vt = graph.view_target();
let view = vt.render_view(); let view = vt.render_view();
@ -518,102 +196,113 @@ impl Node for MeshPass {
.as_texture_view() .as_texture_view()
.expect("BasePassSlots::DepthTextureView was not a TextureView slot"); .expect("BasePassSlots::DepthTextureView was not a TextureView slot");
let camera_bg = graph let camera_bg = graph.bind_group(BasePassSlots::Camera);
.bind_group(BasePassSlots::Camera);
let lights_bg = graph let lights_bg = graph.bind_group(LightBasePassSlots::Lights);
.bind_group(LightBasePassSlots::Lights);
let light_grid_bg = graph let light_grid_bg = graph.bind_group(LightCullComputePassSlots::LightIndicesGridGroup);
.bind_group(LightCullComputePassSlots::LightIndicesGridGroup);
let material_bg = graph //let material_bg = graph.bind_group(MeshesPassSlots::Material);
.bind_group(MeshesPassSlots::Material);
/* let pipeline = graph.pipeline(context.label.clone()) /* let pipeline = graph.pipeline(context.label.clone())
.expect("Failed to find pipeline for MeshPass"); */ .expect("Failed to find pipeline for MeshPass"); */
let pipeline = self.pipeline.as_ref().unwrap(); let pipeline = self.pipeline.as_ref().unwrap();
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let transforms = self.transform_buffers();
label: Some("Render Pass"), let render_meshes = self.render_meshes();
color_attachments: &[Some(wgpu::RenderPassColorAttachment { let mesh_buffers = self.mesh_buffers();
view,
resolve_target: None, {
ops: wgpu::Operations { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
load: wgpu::LoadOp::Clear(wgpu::Color { label: Some("Render Pass"),
r: 0.1, color_attachments: &[Some(wgpu::RenderPassColorAttachment {
g: 0.2, view,
b: 0.3, resolve_target: None,
a: 1.0, ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.1,
g: 0.2,
b: 0.3,
a: 1.0,
}),
store: true,
},
})],
// enable depth buffer
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}), }),
store: true, stencil_ops: None,
},
})],
// enable depth buffer
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}), }),
stencil_ops: None, });
}),
});
pass.set_pipeline(pipeline); pass.set_pipeline(pipeline);
//let material_buffer_bg = self.material_buffer.as_ref().unwrap().bindgroup(); //let default_texture = self.default_texture.as_ref().unwrap();
let default_texture = self.default_texture.as_ref().unwrap();
let transforms = self.transforms.as_mut().unwrap();
while let Some(job) = self.render_jobs.pop_front() { for job in render_meshes.iter() {
// get the mesh (containing vertices) and the buffers from storage // get the mesh (containing vertices) and the buffers from storage
let buffers = self.mesh_buffers.get(&job.mesh_uuid); let buffers = mesh_buffers.get(&job.mesh_uuid);
if buffers.is_none() { if buffers.is_none() {
warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid); warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid);
continue; continue;
} }
let buffers = buffers.unwrap(); let buffers = buffers.unwrap();
// Bind the optional texture // Bind the optional texture
if let Some(tex) = buffers.material.as_ref() /* if let Some(tex) = buffers.material.as_ref()
.and_then(|m| m.diffuse_texture.as_ref()) { .and_then(|m| m.diffuse_texture.as_ref()) {
pass.set_bind_group(0, tex.bind_group(), &[]); pass.set_bind_group(0, tex.bind_group(), &[]);
} else { } else {
pass.set_bind_group(0, default_texture.bind_group(), &[]); pass.set_bind_group(0, default_texture.bind_group(), &[]);
} }
if let Some(tex) = buffers.material.as_ref() if let Some(tex) = buffers.material.as_ref()
.and_then(|m| m.specular.as_ref()) .and_then(|m| m.specular.as_ref())
.and_then(|s| s.texture.as_ref().or(s.color_texture.as_ref())) { .and_then(|s| s.texture.as_ref().or(s.color_texture.as_ref())) {
pass.set_bind_group(5, tex.bind_group(), &[]); pass.set_bind_group(5, tex.bind_group(), &[]);
} else { } else {
pass.set_bind_group(5, default_texture.bind_group(), &[]); pass.set_bind_group(5, default_texture.bind_group(), &[]);
} } */
if let Some(mat) = buffers.material.as_ref() {
pass.set_bind_group(0, &mat.bind_group, &[]);
} else {
todo!("cannot render mesh without material");
}
// Get the bindgroup for job's transform and bind to it using an offset. // Get the bindgroup for job's transform and bind to it using an offset.
let bindgroup = transforms.bind_group(job.transform_id); let bindgroup = transforms.bind_group(job.transform_id);
let offset = transforms.buffer_offset(job.transform_id); let offset = transforms.buffer_offset(job.transform_id);
pass.set_bind_group(1, bindgroup, &[ offset, ]); pass.set_bind_group(1, bindgroup, &[offset]);
pass.set_bind_group(2, camera_bg, &[]); pass.set_bind_group(2, camera_bg, &[]);
pass.set_bind_group(3, lights_bg, &[]); pass.set_bind_group(3, lights_bg, &[]);
pass.set_bind_group(4, material_bg, &[]); //pass.set_bind_group(4, material_bg, &[]);
pass.set_bind_group(6, light_grid_bg, &[]); pass.set_bind_group(4, light_grid_bg, &[]);
// if this mesh uses indices, use them to draw the mesh // if this mesh uses indices, use them to draw the mesh
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() { if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
let indices_len = indices.count() as u32; let indices_len = indices.count() as u32;
pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..)); pass.set_vertex_buffer(
pass.set_index_buffer(indices.buffer().slice(..), *idx_type); buffers.buffer_vertex.slot(),
pass.draw_indexed(0..indices_len, 0, 0..1); buffers.buffer_vertex.buffer().slice(..),
} else { );
let vertex_count = buffers.buffer_vertex.count(); pass.set_index_buffer(indices.buffer().slice(..), *idx_type);
pass.draw_indexed(0..indices_len, 0, 0..1);
} else {
let vertex_count = buffers.buffer_vertex.count();
pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..)); pass.set_vertex_buffer(
pass.draw(0..vertex_count as u32, 0..1); buffers.buffer_vertex.slot(),
buffers.buffer_vertex.buffer().slice(..),
);
pass.draw(0..vertex_count as u32, 0..1);
}
} }
} }
} }

View File

@ -21,3 +21,9 @@ pub use tint::*;
mod fxaa; mod fxaa;
pub use fxaa::*; pub use fxaa::*;
/* mod shadow_maps;
pub use shadow_maps::*; */
mod mesh_prepare;
pub use mesh_prepare::*;

View File

@ -1,4 +1,4 @@
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc, sync::Arc};
use lyra_game_derive::RenderGraphLabel; use lyra_game_derive::RenderGraphLabel;
@ -13,7 +13,7 @@ pub struct TintPassLabel;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TintPass { pub struct TintPass {
target_sampler: Option<wgpu::Sampler>, target_sampler: Option<wgpu::Sampler>,
bgl: Option<Rc<wgpu::BindGroupLayout>>, bgl: Option<Arc<wgpu::BindGroupLayout>>,
/// Store bind groups for the input textures. /// Store bind groups for the input textures.
/// The texture may change due to resizes, or changes to the view target chain /// The texture may change due to resizes, or changes to the view target chain
/// from other nodes. /// from other nodes.
@ -54,7 +54,7 @@ impl Node for TintPass {
}, },
], ],
}); });
let bgl = Rc::new(bgl); let bgl = Arc::new(bgl);
self.bgl = Some(bgl.clone()); self.bgl = Some(bgl.clone());
self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor::default())); self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor::default()));

View File

@ -6,7 +6,7 @@ use lyra_ecs::{Entity, Tick, World};
pub use point::*; pub use point::*;
pub use spotlight::*; pub use spotlight::*;
use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem, rc::Rc}; use std::{collections::{HashMap, VecDeque}, marker::PhantomData, mem, sync::Arc};
use crate::math::Transform; use crate::math::Transform;
@ -98,9 +98,9 @@ impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
} }
pub(crate) struct LightUniformBuffers { pub(crate) struct LightUniformBuffers {
pub buffer: Rc<wgpu::Buffer>, pub buffer: Arc<wgpu::Buffer>,
pub bind_group: Rc<wgpu::BindGroup>, pub bind_group: Arc<wgpu::BindGroup>,
pub bind_group_layout: Rc<wgpu::BindGroupLayout>, pub bind_group_layout: Arc<wgpu::BindGroupLayout>,
max_light_count: u64, max_light_count: u64,
} }
@ -155,9 +155,9 @@ impl LightUniformBuffers {
}); });
Self { Self {
buffer: Rc::new(buffer), buffer: Arc::new(buffer),
bind_group: Rc::new(bindgroup), bind_group: Arc::new(bindgroup),
bind_group_layout: Rc::new(bindgroup_layout), bind_group_layout: Arc::new(bindgroup_layout),
max_light_count: max_buffer_sizes / mem::size_of::<LightUniform>() as u64, max_light_count: max_buffer_sizes / mem::size_of::<LightUniform>() as u64,
} }
} }

View File

@ -1,9 +1,10 @@
use std::rc::Rc; use std::sync::Arc;
use lyra_resource::{ResHandle, Texture}; use lyra_resource::{ResHandle, Texture};
use super::texture::RenderTexture; use super::texture::RenderTexture;
#[derive(Default)]
pub struct MaterialSpecular { pub struct MaterialSpecular {
pub factor: f32, pub factor: f32,
pub color_factor: glam::Vec3, pub color_factor: glam::Vec3,
@ -11,7 +12,7 @@ pub struct MaterialSpecular {
pub color_texture: Option<RenderTexture>, pub color_texture: Option<RenderTexture>,
} }
fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Rc<wgpu::BindGroupLayout>, i: &Option<ResHandle<Texture>>) -> Option<RenderTexture> { fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Arc<wgpu::BindGroupLayout>, i: &Option<ResHandle<Texture>>) -> Option<RenderTexture> {
if let Some(tex) = i { if let Some(tex) = i {
RenderTexture::from_resource(device, queue, bg_layout.clone(), tex, None).ok() RenderTexture::from_resource(device, queue, bg_layout.clone(), tex, None).ok()
} else { } else {
@ -20,7 +21,7 @@ fn texture_to_render(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: &Rc<
} }
impl MaterialSpecular { impl MaterialSpecular {
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Specular) -> Self { pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Specular) -> Self {
let tex = texture_to_render(device, queue, &bg_layout, &value.texture); let tex = texture_to_render(device, queue, &bg_layout, &value.texture);
let color_tex = texture_to_render(device, queue, &bg_layout, &value.color_texture); let color_tex = texture_to_render(device, queue, &bg_layout, &value.color_texture);
@ -45,7 +46,7 @@ pub struct Material {
} }
impl Material { impl Material {
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Material) -> Self { pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, value: &lyra_resource::gltf::Material) -> Self {
let diffuse_texture = texture_to_render(device, queue, &bg_layout, &value.base_color_texture); let diffuse_texture = texture_to_render(device, queue, &bg_layout, &value.base_color_texture);
let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s)); let specular = value.specular.as_ref().map(|s| MaterialSpecular::from_resource(device, queue, bg_layout.clone(), s));

View File

@ -1,4 +1,4 @@
use std::{num::NonZeroU32, rc::Rc}; use std::{num::NonZeroU32, sync::Arc};
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
@ -23,11 +23,11 @@ impl RenderBuffer {
pub struct BindGroupPair { pub struct BindGroupPair {
pub bindgroup: wgpu::BindGroup, pub bindgroup: wgpu::BindGroup,
pub layout: Rc<wgpu::BindGroupLayout>, pub layout: Arc<wgpu::BindGroupLayout>,
} }
impl BindGroupPair { impl BindGroupPair {
pub fn create_bind_group(device: &wgpu::Device, layout: Rc<wgpu::BindGroupLayout>, entries: &[wgpu::BindGroupEntry<'_>]) -> Self { pub fn create_bind_group(device: &wgpu::Device, layout: Arc<wgpu::BindGroupLayout>, entries: &[wgpu::BindGroupEntry<'_>]) -> Self {
let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor { let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout, layout: &layout,
entries, entries,
@ -43,7 +43,7 @@ impl BindGroupPair {
pub fn new(bindgroup: wgpu::BindGroup, layout: wgpu::BindGroupLayout) -> Self { pub fn new(bindgroup: wgpu::BindGroup, layout: wgpu::BindGroupLayout) -> Self {
Self { Self {
bindgroup, bindgroup,
layout: Rc::new(layout), layout: Arc::new(layout),
} }
} }
} }
@ -136,7 +136,7 @@ impl BufferWrapper {
} }
/// Take the bind group layout, the bind group, and the buffer out of the wrapper. /// Take the bind group layout, the bind group, and the buffer out of the wrapper.
pub fn parts(self) -> (Option<Rc<wgpu::BindGroupLayout>>, Option<wgpu::BindGroup>, wgpu::Buffer) { pub fn parts(self) -> (Option<Arc<wgpu::BindGroupLayout>>, Option<wgpu::BindGroup>, wgpu::Buffer) {
if let Some(pair) = self.bindgroup_pair { if let Some(pair) = self.bindgroup_pair {
(Some(pair.layout), Some(pair.bindgroup), self.inner_buf) (Some(pair.layout), Some(pair.bindgroup), self.inner_buf)
} else { } else {
@ -297,7 +297,7 @@ impl BufferWrapperBuilder {
BindGroupPair { BindGroupPair {
bindgroup: bg, bindgroup: bg,
layout: Rc::new(bg_layout), layout: Arc::new(bg_layout),
} }
} }
}; };
@ -308,7 +308,7 @@ impl BufferWrapperBuilder {
len: Some(self.count.unwrap_or_default() as usize), len: Some(self.count.unwrap_or_default() as usize),
} */ } */
(Rc::try_unwrap(bg_pair.layout).unwrap(), bg_pair.bindgroup, buffer, self.count.unwrap_or_default() as usize) (Arc::try_unwrap(bg_pair.layout).unwrap(), bg_pair.bindgroup, buffer, self.count.unwrap_or_default() as usize)
} }
pub fn finish(self, device: &wgpu::Device) -> BufferWrapper { pub fn finish(self, device: &wgpu::Device) -> BufferWrapper {
@ -316,7 +316,7 @@ impl BufferWrapperBuilder {
BufferWrapper { BufferWrapper {
bindgroup_pair: Some(BindGroupPair { bindgroup_pair: Some(BindGroupPair {
layout: Rc::new(bgl), layout: Arc::new(bgl),
bindgroup: bg bindgroup: bg
}), }),
inner_buf: buff, inner_buf: buff,

View File

@ -9,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, FxaaPass, FxaaPassLabel, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, SubGraphNode, ViewTarget}; use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, FxaaPass, FxaaPassLabel, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshPrepNode, MeshPrepNodeLabel, 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};
@ -147,9 +147,13 @@ impl BasicRenderer {
forward_plus_graph.add_node(LightCullComputePassLabel, LightCullComputePass::new(size)); forward_plus_graph.add_node(LightCullComputePassLabel, LightCullComputePass::new(size));
debug!("Adding mesh pass"); debug!("Adding mesh pass");
forward_plus_graph.add_node(MeshesPassLabel, MeshPass::new()); let mesh_prep = MeshPrepNode::new(&device);
let material_bgl = mesh_prep.material_bgl.clone();
forward_plus_graph.add_node(MeshPrepNodeLabel, mesh_prep);
forward_plus_graph.add_node(MeshesPassLabel, MeshPass::new(material_bgl));
forward_plus_graph.add_edge(LightBasePassLabel, LightCullComputePassLabel); forward_plus_graph.add_edge(LightBasePassLabel, LightCullComputePassLabel);
forward_plus_graph.add_edge(MeshPrepNodeLabel, MeshesPassLabel);
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,

View File

@ -1,4 +1,4 @@
use std::{ops::Deref, rc::Rc}; use std::{ops::Deref, rc::Rc, sync::Arc};
use wgpu::PipelineLayout; use wgpu::PipelineLayout;
@ -7,7 +7,7 @@ use super::Shader;
//#[derive(Debug, Clone)] //#[derive(Debug, Clone)]
pub struct ComputePipelineDescriptor { pub struct ComputePipelineDescriptor {
pub label: Option<String>, pub label: Option<String>,
pub layouts: Vec<Rc<wgpu::BindGroupLayout>>, pub layouts: Vec<Arc<wgpu::BindGroupLayout>>,
pub push_constant_ranges: Vec<wgpu::PushConstantRange>, pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
// TODO: make this a ResHandle<Shader> // TODO: make this a ResHandle<Shader>
/// The compiled shader module for the stage. /// The compiled shader module for the stage.

View File

@ -1,4 +1,4 @@
use std::{num::NonZeroU32, ops::Deref, rc::Rc}; use std::{num::NonZeroU32, ops::Deref, sync::Arc};
use wgpu::PipelineLayout; use wgpu::PipelineLayout;
@ -7,7 +7,7 @@ use super::{FragmentState, VertexState};
//#[derive(Debug, Clone)] //#[derive(Debug, Clone)]
pub struct RenderPipelineDescriptor { pub struct RenderPipelineDescriptor {
pub label: Option<String>, pub label: Option<String>,
pub layouts: Vec<Rc<wgpu::BindGroupLayout>>, pub layouts: Vec<Arc<wgpu::BindGroupLayout>>,
pub push_constant_ranges: Vec<wgpu::PushConstantRange>, pub push_constant_ranges: Vec<wgpu::PushConstantRange>,
pub vertex: VertexState, pub vertex: VertexState,
pub fragment: Option<FragmentState>, pub fragment: Option<FragmentState>,
@ -87,7 +87,7 @@ impl RenderPipeline {
// an Rc was used here so that this shader could be reused by the fragment stage if // an Rc was used here so that this shader could be reused by the fragment stage if
// they share the same shader. I tried to do it without an Rc but couldn't get past // they share the same shader. I tried to do it without an Rc but couldn't get past
// the borrow checker // the borrow checker
let vrtx_shad = Rc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor { let vrtx_shad = Arc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: desc.vertex.module.label.as_deref(), label: desc.vertex.module.label.as_deref(),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
&desc.vertex.module.source, &desc.vertex.module.source,
@ -103,7 +103,7 @@ impl RenderPipeline {
if f.module == desc.vertex.module { if f.module == desc.vertex.module {
vrtx_shad.clone() vrtx_shad.clone()
} else { } else {
Rc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor { Arc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: f.module.label.as_deref(), label: f.module.label.as_deref(),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&f.module.source)), source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&f.module.source)),
})) }))

View File

@ -87,28 +87,31 @@ fn vs_main(
// Fragment shader // Fragment shader
struct Material { struct Material {
ambient: vec4<f32>, ambient: vec3<f32>,
diffuse: vec4<f32>, diffuse: vec3<f32>,
specular: vec4<f32>,
shininess: f32, shininess: f32,
specular_factor: f32,
specular_color: vec3<f32>,
} }
@group(0) @binding(0) @group(0) @binding(0)
var t_diffuse: texture_2d<f32>; var<uniform> u_material: Material;
@group(0) @binding(1) @group(0) @binding(1)
var t_diffuse: texture_2d<f32>;
@group(0) @binding(2)
var s_diffuse: sampler; var s_diffuse: sampler;
@group(4) @binding(0) /*@group(4) @binding(0)
var<uniform> u_material: Material; var<uniform> u_material: Material;
@group(5) @binding(0) @group(5) @binding(0)
var t_specular: texture_2d<f32>; var t_specular: texture_2d<f32>;
@group(5) @binding(1) @group(5) @binding(1)
var s_specular: sampler; var s_specular: sampler;*/
@group(6) @binding(0) @group(4) @binding(0)
var<storage, read_write> u_light_indices: array<u32>; var<storage, read_write> u_light_indices: array<u32>;
@group(6) @binding(1) @group(4) @binding(1)
var t_light_grid: texture_storage_2d<rg32uint, read_write>; // vec2<u32> var t_light_grid: texture_storage_2d<rg32uint, read_write>; // vec2<u32>
@fragment @fragment
@ -118,7 +121,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
} }
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords); let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
let specular_color: vec3<f32> = textureSample(t_specular, s_specular, in.tex_coords).xyz; let specular_color: vec3<f32> = vec3<f32>(0.0); //textureSample(t_specular, s_specular, in.tex_coords).xyz;
var light_res = vec3<f32>(0.0); var light_res = vec3<f32>(0.0);
if (object_color.a < ALPHA_CUTOFF) { if (object_color.a < ALPHA_CUTOFF) {

View File

@ -1,4 +1,4 @@
use std::rc::Rc; use std::sync::Arc;
use image::GenericImageView; use image::GenericImageView;
use lyra_resource::{FilterMode, ResHandle, Texture, WrappingMode}; use lyra_resource::{FilterMode, ResHandle, Texture, WrappingMode};
@ -44,7 +44,7 @@ impl RenderTexture {
}) })
} }
fn create_bind_group_pair(device: &wgpu::Device, layout: Rc<wgpu::BindGroupLayout>, view: &wgpu::TextureView, sampler: &wgpu::Sampler) -> BindGroupPair { fn create_bind_group_pair(device: &wgpu::Device, layout: Arc<wgpu::BindGroupLayout>, view: &wgpu::TextureView, sampler: &wgpu::Sampler) -> BindGroupPair {
let bg = device.create_bind_group( let bg = device.create_bind_group(
&wgpu::BindGroupDescriptor { &wgpu::BindGroupDescriptor {
layout: &layout, layout: &layout,
@ -68,12 +68,12 @@ impl RenderTexture {
} }
} }
pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, bytes: &[u8], label: &str) -> anyhow::Result<Self> { pub fn from_bytes(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, bytes: &[u8], label: &str) -> anyhow::Result<Self> {
let img = image::load_from_memory(bytes)?; let img = image::load_from_memory(bytes)?;
Self::from_image(device, queue, bg_layout, &img, Some(label)) Self::from_image(device, queue, bg_layout, &img, Some(label))
} }
pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, img: &image::DynamicImage, label: Option<&str>) -> anyhow::Result<Self> { pub fn from_image(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, img: &image::DynamicImage, label: Option<&str>) -> anyhow::Result<Self> {
let rgba = img.to_rgba8(); let rgba = img.to_rgba8();
let dimensions = img.dimensions(); let dimensions = img.dimensions();
@ -134,7 +134,7 @@ impl RenderTexture {
}) })
} }
pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Rc<wgpu::BindGroupLayout>, texture_res: &ResHandle<Texture>, label: Option<&str>) -> anyhow::Result<Self> { pub fn from_resource(device: &wgpu::Device, queue: &wgpu::Queue, bg_layout: Arc<wgpu::BindGroupLayout>, texture_res: &ResHandle<Texture>, label: Option<&str>) -> anyhow::Result<Self> {
let texture_ref = texture_res.data_ref().unwrap(); let texture_ref = texture_res.data_ref().unwrap();
let img = texture_ref.image.data_ref().unwrap(); let img = texture_ref.image.data_ref().unwrap();
@ -371,7 +371,7 @@ impl RenderTexture {
/// Convert [`lyra_resource::WrappingMode`] to [`wgpu::AddressMode`] /// Convert [`lyra_resource::WrappingMode`] to [`wgpu::AddressMode`]
#[inline(always)] #[inline(always)]
fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode { pub(crate) fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode {
match wmode { match wmode {
WrappingMode::ClampToEdge => wgpu::AddressMode::ClampToEdge, WrappingMode::ClampToEdge => wgpu::AddressMode::ClampToEdge,
WrappingMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat, WrappingMode::MirroredRepeat => wgpu::AddressMode::MirrorRepeat,
@ -381,7 +381,7 @@ fn res_wrap_to_wgpu(wmode: WrappingMode) -> wgpu::AddressMode {
/// Convert [`lyra_resource::FilterMode`] to [`wgpu::FilterMode`] /// Convert [`lyra_resource::FilterMode`] to [`wgpu::FilterMode`]
#[inline(always)] #[inline(always)]
fn res_filter_to_wgpu(fmode: FilterMode) -> wgpu::FilterMode { pub(crate) fn res_filter_to_wgpu(fmode: FilterMode) -> wgpu::FilterMode {
match fmode { match fmode {
FilterMode::Nearest => wgpu::FilterMode::Nearest, FilterMode::Nearest => wgpu::FilterMode::Nearest,
FilterMode::Linear => wgpu::FilterMode::Linear, FilterMode::Linear => wgpu::FilterMode::Linear,

View File

@ -1,4 +1,4 @@
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64, rc::Rc}; use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64, sync::Arc};
use lyra_ecs::Entity; use lyra_ecs::Entity;
use tracing::instrument; use tracing::instrument;
@ -165,7 +165,7 @@ impl<K: Hash + Eq + PartialEq + Clone, V: Clone, S: BuildHasher> CachedValMap<K,
/// [`TransformGroup`]s are used to represent entries in the buffer. They are used to insert, /// [`TransformGroup`]s are used to represent entries in the buffer. They are used to insert,
/// update, and retrieve the transforms. /// update, and retrieve the transforms.
pub struct TransformBuffers { pub struct TransformBuffers {
pub bindgroup_layout: Rc<wgpu::BindGroupLayout>, pub bindgroup_layout: Arc<wgpu::BindGroupLayout>,
//groups: CachedValMap<TransformGroupId, TransformIndex>, //groups: CachedValMap<TransformGroupId, TransformIndex>,
//groups: SlotMap<TransformGroupId, TransformIndex>, //groups: SlotMap<TransformGroupId, TransformIndex>,
entries: Vec<BufferEntry>, entries: Vec<BufferEntry>,
@ -195,7 +195,7 @@ impl TransformBuffers {
}); });
let mut s = Self { let mut s = Self {
bindgroup_layout: Rc::new(bindgroup_layout), bindgroup_layout: Arc::new(bindgroup_layout),
entries: Default::default(), entries: Default::default(),
max_transform_count: (limits.max_uniform_buffer_binding_size) as usize / (limits.min_uniform_buffer_offset_alignment as usize), //(mem::size_of::<glam::Mat4>()), max_transform_count: (limits.max_uniform_buffer_binding_size) as usize / (limits.min_uniform_buffer_offset_alignment as usize), //(mem::size_of::<glam::Mat4>()),
limits, limits,
@ -345,9 +345,6 @@ impl TransformBuffers {
/// Returns a boolean indicating if the buffers need to be expanded /// Returns a boolean indicating if the buffers need to be expanded
pub fn needs_expand(&self) -> bool { pub fn needs_expand(&self) -> bool {
false false
/* self.entries.last()
.map(|entry| entry.len >= self.max_transform_count)
.unwrap_or(false) */
} }
} }

View File

@ -95,7 +95,7 @@ impl From<gltf::material::AlphaMode> for AlphaMode {
} }
} }
#[derive(Clone, Reflect)] #[derive(Clone, Reflect, Default)]
pub struct Specular { pub struct Specular {
/// The strength of the specular reflection, default of 1.0 /// The strength of the specular reflection, default of 1.0
pub factor: f32, pub factor: f32,