Return Result for asset handle wait_for_load, create shader asset loader that uses the preprocessor
This commit is contained in:
parent
8d54c42d74
commit
95b01613fe
|
@ -4,6 +4,7 @@ use lyra_gltf::GltfLoader;
|
|||
use lyra_resource::ResourceManager;
|
||||
|
||||
use crate::game::App;
|
||||
use crate::render::PreprocessShaderLoader;
|
||||
use crate::winit::{WinitPlugin, WindowPlugin};
|
||||
use crate::DeltaTimePlugin;
|
||||
use crate::input::InputPlugin;
|
||||
|
@ -101,7 +102,10 @@ pub struct ResourceManagerPlugin;
|
|||
|
||||
impl Plugin for ResourceManagerPlugin {
|
||||
fn setup(&mut self, app: &mut App) {
|
||||
app.world.add_resource(ResourceManager::new());
|
||||
let rm = ResourceManager::new();
|
||||
rm.register_loader::<PreprocessShaderLoader>();
|
||||
|
||||
app.world.add_resource(rm);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,12 +115,12 @@ pub struct DefaultPlugins;
|
|||
|
||||
impl Plugin for DefaultPlugins {
|
||||
fn setup(&mut self, app: &mut App) {
|
||||
WinitPlugin::default().setup(app);
|
||||
CommandQueuePlugin.setup(app);
|
||||
InputPlugin.setup(app);
|
||||
ResourceManagerPlugin.setup(app);
|
||||
GltfPlugin.setup(app);
|
||||
WinitPlugin::default().setup(app);
|
||||
WindowPlugin::default().setup(app);
|
||||
CommandQueuePlugin.setup(app);
|
||||
InputPlugin.setup(app);
|
||||
DeltaTimePlugin.setup(app);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::{
|
|||
};
|
||||
|
||||
use lyra_ecs::World;
|
||||
use lyra_resource::{RequestError, ResHandle, ResourceManager};
|
||||
pub use node::*;
|
||||
|
||||
mod passes;
|
||||
|
@ -22,7 +23,7 @@ use rustc_hash::FxHashMap;
|
|||
use tracing::{debug_span, instrument, trace, warn};
|
||||
use wgpu::CommandEncoder;
|
||||
|
||||
use super::resource::{ComputePipeline, Pass, Pipeline, RenderPipeline};
|
||||
use super::{resource::{ComputePipeline, Pass, Pipeline, RenderPipeline}, Shader};
|
||||
|
||||
/// A trait that represents the label of a resource, slot, or node in the [`RenderGraph`].
|
||||
pub trait RenderGraphLabel: Debug + 'static {
|
||||
|
@ -118,11 +119,15 @@ pub struct RenderGraph {
|
|||
/// A directed graph used to determine dependencies of nodes.
|
||||
node_graph: petgraph::matrix_graph::DiMatrix<RenderGraphLabelValue, (), Option<()>, usize>,
|
||||
view_target: Rc<RefCell<ViewTarget>>,
|
||||
shader_prepoc: wgsl_preprocessor::Processor,
|
||||
/// Type erased data of ResourceManager ecs resource
|
||||
resource_manager: lyra_ecs::ResourceData,
|
||||
}
|
||||
|
||||
impl RenderGraph {
|
||||
pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>, view_target: Rc<RefCell<ViewTarget>>) -> Self {
|
||||
pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>, view_target: Rc<RefCell<ViewTarget>>, world: &World) -> Self {
|
||||
let rm = world.get_resource_data::<ResourceManager>()
|
||||
.expect("RenderGraph requires ResourceManager ECS resource");
|
||||
|
||||
Self {
|
||||
device,
|
||||
queue,
|
||||
|
@ -132,7 +137,7 @@ impl RenderGraph {
|
|||
bind_groups: Default::default(),
|
||||
node_graph: Default::default(),
|
||||
view_target,
|
||||
shader_prepoc: wgsl_preprocessor::Processor::new(),
|
||||
resource_manager: rm,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -518,7 +523,7 @@ impl RenderGraph {
|
|||
/// This step also parses the shader and will return errors if it failed to parse.
|
||||
///
|
||||
/// Returns: The shader module import path if the module specified one.
|
||||
#[inline(always)]
|
||||
/* #[inline(always)]
|
||||
pub fn register_shader(&mut self, shader_src: &str) -> Result<Option<String>, wgsl_preprocessor::Error> {
|
||||
self.shader_prepoc.parse_module(shader_src)
|
||||
}
|
||||
|
@ -527,6 +532,16 @@ impl RenderGraph {
|
|||
#[inline(always)]
|
||||
pub fn preprocess_shader(&mut self, shader_path: &str) -> Result<String, wgsl_preprocessor::Error> {
|
||||
self.shader_prepoc.preprocess_module(shader_path)
|
||||
} */
|
||||
|
||||
/// Load a shader from a `str`
|
||||
///
|
||||
/// This will also wait until the shader is loaded before returning.
|
||||
pub fn load_shader_str(&self, shader_name: &str, shader_src: &str) -> Result<ResHandle<Shader>, RequestError> {
|
||||
let rm = self.resource_manager.get::<ResourceManager>();
|
||||
let shader = rm.load_str(shader_name, "text/wgsl", shader_src)?;
|
||||
shader.wait_for_load()?;
|
||||
Ok(shader)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use lyra_game_derive::RenderGraphLabel;
|
||||
|
||||
use crate::render::{
|
||||
graph::{Node, NodeDesc, NodeType},
|
||||
resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState},
|
||||
resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, VertexState},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
||||
|
@ -64,10 +64,8 @@ impl Node for FxaaPass {
|
|||
..Default::default()
|
||||
}));
|
||||
|
||||
let shader = Rc::new(Shader {
|
||||
label: Some("fxaa_shader".into()),
|
||||
source: include_str!("../../shaders/fxaa.wgsl").to_string(),
|
||||
});
|
||||
let shader = graph.load_shader_str("fxaa_shader", include_str!("../../shaders/fxaa.wgsl"))
|
||||
.expect("failed to load wgsl shader from manager");
|
||||
|
||||
let vt = graph.view_target();
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{mem, rc::Rc, sync::Arc};
|
||||
use std::{mem, sync::Arc};
|
||||
|
||||
use glam::Vec2Swizzles;
|
||||
use lyra_ecs::World;
|
||||
|
@ -8,7 +8,7 @@ use wgpu::util::DeviceExt;
|
|||
use crate::render::{
|
||||
graph::{
|
||||
Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext, SlotAttribute, SlotValue
|
||||
}, renderer::ScreenSize, resource::{ComputePipeline, ComputePipelineDescriptor, Shader}
|
||||
}, renderer::ScreenSize, resource::{ComputePipeline, ComputePipelineDescriptor}
|
||||
};
|
||||
|
||||
use super::{BasePassSlots, LightBasePassSlots};
|
||||
|
@ -219,10 +219,8 @@ impl Node for LightCullComputePass {
|
|||
let screen_size_bgl = graph.bind_group_layout(BasePassSlots::ScreenSize);
|
||||
let light_indices_bg_layout = graph.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
|
||||
|
||||
let shader = Rc::new(Shader {
|
||||
label: Some("light_cull_comp_shader".into()),
|
||||
source: include_str!("../../shaders/light_cull.comp.wgsl").to_string(),
|
||||
});
|
||||
let shader = graph.load_shader_str("light_cull_comp_shader", include_str!("../../shaders/light_cull.comp.wgsl"))
|
||||
.expect("failed to load light cull compute shader");
|
||||
|
||||
let pipeline = ComputePipeline::create(device, &ComputePipelineDescriptor {
|
||||
label: Some("light_cull_pipeline".into()),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{rc::Rc, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use lyra_ecs::{AtomicRef, ResourceData};
|
||||
use lyra_game_derive::RenderGraphLabel;
|
||||
|
@ -7,7 +7,7 @@ use tracing::{instrument, warn};
|
|||
use crate::render::{
|
||||
desc_buf_lay::DescVertexBufferLayout,
|
||||
graph::{Node, NodeDesc, NodeType, RenderGraph, RenderGraphContext},
|
||||
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState},
|
||||
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, VertexState},
|
||||
texture::RenderTexture,
|
||||
transform_buffer_storage::TransformBuffers,
|
||||
vertex::Vertex,
|
||||
|
@ -102,10 +102,8 @@ impl Node for MeshPass {
|
|||
_: &mut RenderGraphContext,
|
||||
) {
|
||||
if self.pipeline.is_none() {
|
||||
let shader_mod = graph.register_shader(include_str!("../../shaders/base.wgsl"))
|
||||
.expect("failed to register shader").expect("base shader missing module");
|
||||
let shader_src = graph.preprocess_shader(&shader_mod)
|
||||
.expect("failed to preprocess shader");
|
||||
let shader = graph.load_shader_str("mesh_base", include_str!("../../shaders/base.wgsl"))
|
||||
.expect("failed to load base mesh shader");
|
||||
|
||||
let device = graph.device();
|
||||
let surface_config_format = graph.view_target().format();
|
||||
|
@ -299,11 +297,6 @@ impl Node for MeshPass {
|
|||
graph.bind_group_layout(LightCullComputePassSlots::LightIndicesGridGroup);
|
||||
let atlas_bgl = self.shadows_atlas.as_ref().unwrap().layout.clone();
|
||||
|
||||
let shader = Rc::new(Shader {
|
||||
label: Some(shader_mod.into()),
|
||||
source: shader_src,
|
||||
});
|
||||
|
||||
let transforms = world
|
||||
.get_resource_data::<TransformBuffers>()
|
||||
.expect("Missing transform buffers");
|
||||
|
|
|
@ -14,17 +14,13 @@ use lyra_ecs::{
|
|||
};
|
||||
use lyra_game_derive::RenderGraphLabel;
|
||||
use lyra_math::{Angle, Transform};
|
||||
use lyra_resource::{RequestError, ResHandle};
|
||||
use rustc_hash::FxHashMap;
|
||||
use tracing::{debug, warn};
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
use crate::render::{
|
||||
graph::{Node, NodeDesc, NodeType, RenderGraph, SlotAttribute, SlotValue},
|
||||
light::{directional::DirectionalLight, LightType, PointLight, SpotLight},
|
||||
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState},
|
||||
transform_buffer_storage::TransformBuffers,
|
||||
vertex::Vertex,
|
||||
AtlasFrame, GpuSlotBuffer, TextureAtlas,
|
||||
graph::{Node, NodeDesc, NodeType, RenderGraph, SlotAttribute, SlotValue}, light::{directional::DirectionalLight, LightType, PointLight, SpotLight}, resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, VertexState}, transform_buffer_storage::TransformBuffers, vertex::Vertex, AtlasFrame, GpuSlotBuffer, Shader, TextureAtlas
|
||||
};
|
||||
|
||||
use super::{MeshBufferStorage, RenderAssets, RenderMeshes};
|
||||
|
@ -74,7 +70,7 @@ pub struct ShadowMapsPass {
|
|||
transform_buffers: Option<ResourceData>,
|
||||
render_meshes: Option<ResourceData>,
|
||||
mesh_buffers: Option<ResourceData>,
|
||||
shader: Option<String>,
|
||||
shader: ResHandle<Shader>,
|
||||
pipeline: Option<RenderPipeline>,
|
||||
point_light_pipeline: Option<RenderPipeline>,
|
||||
|
||||
|
@ -171,7 +167,7 @@ impl ShadowMapsPass {
|
|||
transform_buffers: None,
|
||||
render_meshes: None,
|
||||
mesh_buffers: None,
|
||||
shader: None,
|
||||
shader: Default::default(),
|
||||
pipeline: None,
|
||||
point_light_pipeline: None,
|
||||
|
||||
|
@ -503,21 +499,14 @@ impl ShadowMapsPass {
|
|||
queue.write_buffer(buffer, 0, bytemuck::cast_slice(points.as_slice()));
|
||||
}
|
||||
|
||||
/// Register all the shaders, returning the module of the
|
||||
fn register_shaders(&self, graph: &mut RenderGraph) -> Result<(), wgsl_preprocessor::Error> {
|
||||
let src = include_str!("../../shaders/shadows/shadows_structs.wgsl");
|
||||
graph.register_shader(src)?;
|
||||
/// Load all shadow related registers and return the shader for the shadow depth pass.
|
||||
fn loader_shaders(&self, graph: &mut RenderGraph) -> Result<ResHandle<Shader>, RequestError> {
|
||||
graph.load_shader_str("shadows_structs", include_str!("../../shaders/shadows/shadows_structs.wgsl"))?;
|
||||
graph.load_shader_str("shadows_bindings", include_str!("../../shaders/shadows/shadows_bindings.wgsl"))?;
|
||||
graph.load_shader_str("shadows_calc", include_str!("../../shaders/shadows/shadows_calc.wgsl"))?;
|
||||
let depth = graph.load_shader_str("shadows_depth", include_str!("../../shaders/shadows/shadows_depth.wgsl"))?;
|
||||
|
||||
let src = include_str!("../../shaders/shadows/shadows_bindings.wgsl");
|
||||
graph.register_shader(src)?;
|
||||
|
||||
let src = include_str!("../../shaders/shadows/shadows_calc.wgsl");
|
||||
graph.register_shader(src)?;
|
||||
|
||||
let src = include_str!("../../shaders/shadows/shadows_depth.wgsl");
|
||||
graph.register_shader(src)?;
|
||||
|
||||
Ok(())
|
||||
Ok(depth)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -526,11 +515,8 @@ impl Node for ShadowMapsPass {
|
|||
&mut self,
|
||||
graph: &mut crate::render::graph::RenderGraph,
|
||||
) -> crate::render::graph::NodeDesc {
|
||||
self.register_shaders(graph)
|
||||
.expect("failed to register shaders");
|
||||
self.shader = Some(graph.preprocess_shader("lyra::shadows::depth_pass")
|
||||
.expect("failed to preprocess depth shadow shaders"));
|
||||
println!("{}", self.shader.as_ref().unwrap());
|
||||
self.shader = self.loader_shaders(graph)
|
||||
.expect("failed to load depth shadow shader");
|
||||
|
||||
let mut node = NodeDesc::new(NodeType::Render, None, vec![]);
|
||||
|
||||
|
@ -773,11 +759,6 @@ impl Node for ShadowMapsPass {
|
|||
}
|
||||
|
||||
if self.pipeline.is_none() {
|
||||
let shader = Rc::new(Shader {
|
||||
label: Some("lyra::shadows::depth_pass".into()),
|
||||
source: self.shader.clone().unwrap(),
|
||||
});
|
||||
|
||||
let bgl = self.bgl.clone();
|
||||
let transforms = self.transform_buffers().bindgroup_layout.clone();
|
||||
|
||||
|
@ -788,7 +769,7 @@ impl Node for ShadowMapsPass {
|
|||
layouts: vec![bgl.clone(), transforms.clone()],
|
||||
push_constant_ranges: vec![],
|
||||
vertex: VertexState {
|
||||
module: shader.clone(),
|
||||
module: self.shader.clone(),
|
||||
entry_point: "vs_main".into(),
|
||||
buffers: vec![Vertex::position_desc().into()],
|
||||
},
|
||||
|
@ -817,12 +798,12 @@ impl Node for ShadowMapsPass {
|
|||
layouts: vec![bgl, transforms],
|
||||
push_constant_ranges: vec![],
|
||||
vertex: VertexState {
|
||||
module: shader.clone(),
|
||||
module: self.shader.clone(),
|
||||
entry_point: "vs_main".into(),
|
||||
buffers: vec![Vertex::position_desc().into()],
|
||||
},
|
||||
fragment: Some(FragmentState {
|
||||
module: shader,
|
||||
module: self.shader.clone(),
|
||||
entry_point: "fs_point_light_main".into(),
|
||||
targets: vec![],
|
||||
}),
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
use std::{
|
||||
cell::RefMut,
|
||||
collections::{HashMap, VecDeque},
|
||||
rc::Rc,
|
||||
collections::VecDeque,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use glam::{Vec2, Vec3};
|
||||
use image::GenericImageView;
|
||||
use lyra_ecs::{
|
||||
query::{Entities, ResMut}, AtomicRef, Entity, ResourceData
|
||||
query::{Entities, ResMut}, AtomicRef, ResourceData
|
||||
};
|
||||
use lyra_game_derive::RenderGraphLabel;
|
||||
use lyra_resource::{Image, Texture};
|
||||
use lyra_resource::Image;
|
||||
use tracing::{info, instrument, warn};
|
||||
use uuid::Uuid;
|
||||
use wgpu::util::DeviceExt;
|
||||
|
@ -21,10 +19,9 @@ use crate::{
|
|||
graph::{Node, NodeDesc, NodeType, SlotAttribute},
|
||||
render_job::RenderJob,
|
||||
resource::{
|
||||
FragmentState, PipelineDescriptor, RenderPipeline, RenderPipelineDescriptor, Shader,
|
||||
FragmentState, RenderPipeline, RenderPipelineDescriptor,
|
||||
VertexState,
|
||||
},
|
||||
texture::{res_filter_to_wgpu, res_wrap_to_wgpu},
|
||||
transform_buffer_storage::{TransformBuffers, TransformIndex},
|
||||
vertex::Vertex2D,
|
||||
},
|
||||
|
@ -44,8 +41,12 @@ pub enum SpritePassSlots {
|
|||
}
|
||||
|
||||
struct SpriteTexture {
|
||||
// this field is actually read, its given to the bind group
|
||||
#[allow(dead_code)]
|
||||
texture: wgpu::Texture,
|
||||
texture_sampler: wgpu::Sampler,
|
||||
// this field is actually read, its given to the bind group
|
||||
#[allow(dead_code)]
|
||||
sampler: wgpu::Sampler,
|
||||
texture_bg: Arc<wgpu::BindGroup>,
|
||||
|
||||
vertex_buffers: wgpu::Buffer,
|
||||
|
@ -217,7 +218,7 @@ impl Node for SpritePass {
|
|||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
|
@ -241,10 +242,8 @@ impl Node for SpritePass {
|
|||
let vt = graph.view_target();
|
||||
|
||||
if self.pipeline.is_none() {
|
||||
let shader = Rc::new(Shader {
|
||||
label: Some("sprite_shader".into()),
|
||||
source: include_str!("../../shaders/2d/sprite_main.wgsl").to_string(),
|
||||
});
|
||||
let shader = graph.load_shader_str("sprite_shader", include_str!("../../shaders/2d/sprite_main.wgsl"))
|
||||
.expect("failed to load wgsl shader from manager");
|
||||
|
||||
let diffuse_bgl = self.texture_bgl.clone().unwrap();
|
||||
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera).clone();
|
||||
|
@ -310,7 +309,7 @@ impl Node for SpritePass {
|
|||
let texture_uuid = sprite.texture.uuid();
|
||||
if !sprite_store.contains_key(&texture_uuid) {
|
||||
// returns `None` if the Texture image is not loaded.
|
||||
if let Some((tex, samp, tex_bg)) =
|
||||
if let Some((texture, sampler, tex_bg)) =
|
||||
self.load_sprite_texture(device, queue, &texture_uuid, &image)
|
||||
{
|
||||
let (vertex, index) = self.create_vertex_index_buffers(device, &sprite);
|
||||
|
@ -318,8 +317,8 @@ impl Node for SpritePass {
|
|||
sprite_store.insert(
|
||||
texture_uuid,
|
||||
SpriteTexture {
|
||||
texture: tex,
|
||||
texture_sampler: samp,
|
||||
texture,
|
||||
sampler,
|
||||
texture_bg: Arc::new(tex_bg),
|
||||
vertex_buffers: vertex,
|
||||
index_buffers: index,
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use lyra_game_derive::RenderGraphLabel;
|
||||
|
||||
use crate::render::{
|
||||
graph::{Node, NodeDesc, NodeType},
|
||||
resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, Shader, VertexState},
|
||||
resource::{FragmentState, PipelineDescriptor, RenderPipelineDescriptor, VertexState},
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
||||
|
@ -58,13 +58,9 @@ impl Node for TintPass {
|
|||
self.bgl = Some(bgl.clone());
|
||||
self.target_sampler = Some(device.create_sampler(&wgpu::SamplerDescriptor::default()));
|
||||
|
||||
let shader = Rc::new(Shader {
|
||||
label: Some("tint_shader".into()),
|
||||
source: include_str!("../../shaders/tint.wgsl").to_string(),
|
||||
});
|
||||
|
||||
let vt = graph.view_target();
|
||||
|
||||
let shader = graph.load_shader_str("tint_shader", include_str!("../../shaders/tint.wgsl"))
|
||||
.expect("failed to load tint shader");
|
||||
|
||||
NodeDesc::new(
|
||||
NodeType::Render,
|
||||
|
|
|
@ -6,7 +6,6 @@ pub mod render_buffer;
|
|||
pub mod render_job;
|
||||
pub mod mesh;
|
||||
pub mod texture;
|
||||
pub mod shader_loader;
|
||||
pub mod material;
|
||||
pub mod camera;
|
||||
pub mod transform_buffer_storage;
|
||||
|
@ -19,4 +18,7 @@ mod texture_atlas;
|
|||
pub use texture_atlas::*;
|
||||
|
||||
mod slot_buffer;
|
||||
pub use slot_buffer::*;
|
||||
pub use slot_buffer::*;
|
||||
|
||||
mod shader_loader;
|
||||
pub use shader_loader::*;
|
|
@ -135,13 +135,13 @@ impl BasicRenderer {
|
|||
let surface_target = RenderTarget::from_surface(surface, config);
|
||||
let view_target = Rc::new(RefCell::new(ViewTarget::new(device.clone(), surface_target)));
|
||||
|
||||
let mut main_graph = RenderGraph::new(device.clone(), queue.clone(), view_target.clone());
|
||||
let mut main_graph = RenderGraph::new(device.clone(), queue.clone(), view_target.clone(), &world);
|
||||
|
||||
debug!("Adding base pass");
|
||||
main_graph.add_node(BasePassLabel, BasePass::new());
|
||||
|
||||
{
|
||||
let mut forward_plus_graph = RenderGraph::new(device.clone(), queue.clone(), view_target.clone());
|
||||
let mut forward_plus_graph = RenderGraph::new(device.clone(), queue.clone(), view_target.clone(), &world);
|
||||
|
||||
debug!("Adding light base pass");
|
||||
forward_plus_graph.add_node(LightBasePassLabel, LightBasePass::new());
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
use std::{ops::Deref, rc::Rc, sync::Arc};
|
||||
|
||||
use lyra_resource::ResHandle;
|
||||
use wgpu::PipelineLayout;
|
||||
|
||||
use super::{PipelineCompilationOptions, Shader};
|
||||
use crate::render::Shader;
|
||||
|
||||
use super::PipelineCompilationOptions;
|
||||
|
||||
//#[derive(Debug, Clone)]
|
||||
pub struct ComputePipelineDescriptor {
|
||||
pub label: Option<String>,
|
||||
pub layouts: Vec<Arc<wgpu::BindGroupLayout>>,
|
||||
// TODO: make this a ResHandle<Shader>
|
||||
/// The compiled shader module for the stage.
|
||||
pub shader: Rc<Shader>,
|
||||
pub shader: ResHandle<Shader>,
|
||||
/// The entry point in the compiled shader.
|
||||
/// There must be a function in the shader with the same name.
|
||||
pub shader_entry_point: String,
|
||||
|
@ -76,13 +78,16 @@ impl ComputePipeline {
|
|||
None
|
||||
};
|
||||
|
||||
let shader = desc.shader.data_ref()
|
||||
.expect("shader is not loaded, ensure its loaded before creating a pipeline!");
|
||||
|
||||
// an Rc was used here so that this shader could be reused by the fragment stage if
|
||||
// they share the same shader. I tried to do it without an Rc but couldn't get past
|
||||
// the borrow checker
|
||||
let compiled_shader = Rc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: desc.shader.label.as_deref(),
|
||||
label: shader.label.as_deref(),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
||||
&desc.shader.source,
|
||||
&shader.source,
|
||||
)),
|
||||
}));
|
||||
|
||||
|
|
|
@ -84,13 +84,16 @@ impl RenderPipeline {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let vertex_shader = desc.vertex.module.data_ref()
|
||||
.expect("vertex shader is not loaded, ensure its loaded before creating a pipeline!");
|
||||
|
||||
// an Rc was used here so that this shader could be reused by the fragment stage if
|
||||
// they share the same shader. I tried to do it without an Rc but couldn't get past
|
||||
// the borrow checker
|
||||
let vrtx_shad = Arc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: desc.vertex.module.label.as_deref(),
|
||||
label: vertex_shader.label.as_deref(),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
|
||||
&desc.vertex.module.source,
|
||||
&vertex_shader.source,
|
||||
)),
|
||||
}));
|
||||
let vrtx_state = wgpu::VertexState {
|
||||
|
@ -101,12 +104,14 @@ impl RenderPipeline {
|
|||
};
|
||||
|
||||
let frag_module = desc.fragment.as_ref().map(|f| {
|
||||
if f.module == desc.vertex.module {
|
||||
if f.module.uuid() == desc.vertex.module.uuid() {
|
||||
vrtx_shad.clone()
|
||||
} else {
|
||||
let frag_shader = f.module.data_ref()
|
||||
.expect("vertex shader is not loaded, ensure its loaded before creating a pipeline!");
|
||||
Arc::new(device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: f.module.label.as_deref(),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&f.module.source)),
|
||||
label: frag_shader.label.as_deref(),
|
||||
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(&frag_shader.source)),
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use std::rc::Rc;
|
||||
use lyra_resource::ResHandle;
|
||||
|
||||
use crate::render::Shader;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct VertexBufferLayout {
|
||||
|
@ -18,11 +20,10 @@ impl<'a> From<wgpu::VertexBufferLayout<'a>> for VertexBufferLayout {
|
|||
}
|
||||
|
||||
/// Describes the vertex stage in a render pipeline.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct VertexState {
|
||||
// TODO: make this a ResHandle<Shader>
|
||||
/// The compiled shader module for the stage.
|
||||
pub module: Rc<Shader>,
|
||||
pub module: ResHandle<Shader>,
|
||||
/// The entry point in the compiled shader.
|
||||
/// There must be a function in the shader with the same name.
|
||||
pub entry_point: String,
|
||||
|
@ -30,18 +31,11 @@ pub struct VertexState {
|
|||
pub buffers: Vec<VertexBufferLayout>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Shader {
|
||||
pub label: Option<String>,
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
/// Describes the fragment stage in the render pipeline.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct FragmentState {
|
||||
// TODO: make this a ResHandle<Shader>
|
||||
/// The compiled shader module for the stage.
|
||||
pub module: Rc<Shader>,
|
||||
pub module: ResHandle<Shader>,
|
||||
/// The entry point in the compiled shader.
|
||||
/// There must be a function in the shader with the same name.
|
||||
pub entry_point: String,
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use async_std::sync::RwLock;
|
||||
use lyra_resource::{loader::{LoaderError, ResourceLoader}, ResHandle, ResourceData};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Shader {
|
||||
pub label: Option<String>,
|
||||
pub source: String,
|
||||
pub dependent_modules: Option<String>,
|
||||
}
|
||||
|
||||
impl ResourceData for Shader {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn dependencies(&self) -> Vec<lyra_resource::UntypedResHandle> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
/// A WGSL shader module loader with a preprocessor.
|
||||
///
|
||||
/// This will load resources with mimetype of `text/wgsl` and `.wgsl` file extensions.
|
||||
#[derive(Default)]
|
||||
pub struct PreprocessShaderLoader {
|
||||
processor: Arc<RwLock<wgsl_preprocessor::Processor>>,
|
||||
}
|
||||
|
||||
async fn load_shader_str(processor: Arc<RwLock<wgsl_preprocessor::Processor>>, content: &str) -> Result<Shader, LoaderError> {
|
||||
let mut processor = processor.write().await;
|
||||
|
||||
let mod_path = processor.parse_module(content)
|
||||
.map_err(|e| LoaderError::DecodingError(Arc::new(e.into())))?;
|
||||
|
||||
let content = match &mod_path {
|
||||
Some(path) => processor.preprocess_module(&path)
|
||||
.map_err(|e| LoaderError::DecodingError(Arc::new(e.into())))?,
|
||||
None => content.to_owned(),
|
||||
};
|
||||
|
||||
Ok(Shader {
|
||||
label: mod_path,
|
||||
source: content,
|
||||
dependent_modules: None,
|
||||
})
|
||||
}
|
||||
|
||||
impl ResourceLoader for PreprocessShaderLoader {
|
||||
fn extensions(&self) -> &[&str] {
|
||||
&[
|
||||
"wgsl"
|
||||
]
|
||||
}
|
||||
|
||||
fn mime_types(&self) -> &[&str] {
|
||||
&[
|
||||
"text/wgsl"
|
||||
]
|
||||
}
|
||||
|
||||
fn load(&self, _: lyra_resource::ResourceManager, path: &str) -> lyra_resource::loader::PinedBoxLoaderFuture {
|
||||
// cant use &str across async
|
||||
let path = path.to_string();
|
||||
let processor = self.processor.clone();
|
||||
|
||||
Box::pin(async move {
|
||||
let module_str = std::fs::read_to_string(&path)
|
||||
.map_err(|e| LoaderError::DecodingError(Arc::new(e.into())))?;
|
||||
let shader = load_shader_str(processor, &module_str).await?;
|
||||
|
||||
Ok(Box::new(shader) as Box<dyn ResourceData>)
|
||||
})
|
||||
}
|
||||
|
||||
fn load_bytes(&self, _: lyra_resource::ResourceManager, bytes: Vec<u8>, offset: usize, length: usize) -> lyra_resource::loader::PinedBoxLoaderFuture {
|
||||
let processor = self.processor.clone();
|
||||
|
||||
Box::pin(async move {
|
||||
let end = offset + length;
|
||||
let contents = std::str::from_utf8(&bytes[offset..end])
|
||||
.map_err(|e| LoaderError::DecodingError(Arc::new(e.into())))?;
|
||||
let shader = load_shader_str(processor, &contents).await?;
|
||||
|
||||
Ok(Box::new(shader) as Box<dyn ResourceData>)
|
||||
})
|
||||
}
|
||||
|
||||
fn create_erased_handle(&self) -> std::sync::Arc<dyn lyra_resource::ResourceStorage> {
|
||||
Arc::from(ResHandle::<Shader>::new_loading(None))
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ struct CameraUniform {
|
|||
projection: mat4x4<f32>,
|
||||
position: vec3f,
|
||||
tile_debug: u32,
|
||||
};
|
||||
}
|
||||
|
||||
struct Light {
|
||||
position: vec3f,
|
||||
|
@ -32,12 +32,12 @@ struct Light {
|
|||
spot_cutoff: f32,
|
||||
spot_outer_cutoff: f32,
|
||||
light_shadow_uniform_index: array<i32, 6>,
|
||||
};
|
||||
}
|
||||
|
||||
struct Lights {
|
||||
light_count: u32,
|
||||
data: array<Light>,
|
||||
};
|
||||
}
|
||||
|
||||
struct Cone {
|
||||
tip: vec3f,
|
||||
|
|
|
@ -24,7 +24,7 @@ enum ModelLoaderError {
|
|||
|
||||
impl From<ModelLoaderError> for LoaderError {
|
||||
fn from(value: ModelLoaderError) -> Self {
|
||||
LoaderError::DecodingError(value.into())
|
||||
LoaderError::DecodingError(Arc::new(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ impl ResourceLoader for GltfLoader {
|
|||
let parent_path = parent_path.display().to_string();
|
||||
|
||||
let gltf = gltf::Gltf::open(&path)
|
||||
.map_err(|ge| LoaderError::DecodingError(ge.into()))?;
|
||||
.map_err(|ge| LoaderError::DecodingError(Arc::new(ge.into())))?;
|
||||
|
||||
let mut use_bin = false;
|
||||
let buffers: Vec<Vec<u8>> = gltf.buffers().flat_map(|b| match b.source() {
|
||||
|
@ -290,7 +290,7 @@ mod tests {
|
|||
|
||||
let manager = ResourceManager::new();
|
||||
let gltf = manager.request::<Gltf>(&path).unwrap();
|
||||
assert!(gltf.wait_for_load_timeout(Duration::from_secs(10)), "failed to load gltf, hit 10 second timeout");
|
||||
assert!(gltf.wait_for_load_timeout(Duration::from_secs(10)).is_ok_and(|r| r), "failed to load gltf, hit 10 second timeout");
|
||||
let gltf = gltf.data_ref().unwrap();
|
||||
|
||||
assert_eq!(gltf.scenes.len(), 1);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{loader::LoaderError, ResourceState, UntypedResHandle};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -9,7 +7,7 @@ pub enum DependencyState {
|
|||
/// The resource that had the error.
|
||||
handle: UntypedResHandle,
|
||||
/// The error that the resource ran into when loading.
|
||||
error: Arc<LoaderError>,
|
||||
error: LoaderError,
|
||||
},
|
||||
Ready,
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@ pub mod loader;
|
|||
mod world_ext;
|
||||
pub use world_ext::*;
|
||||
|
||||
pub(crate) mod util;
|
||||
|
||||
pub use crossbeam::channel as channel;
|
||||
pub use notify;
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use super::{LoaderError, PinedBoxLoaderFuture, ResourceLoader};
|
|||
|
||||
impl From<ImageError> for LoaderError {
|
||||
fn from(value: ImageError) -> Self {
|
||||
LoaderError::DecodingError(value.into())
|
||||
LoaderError::DecodingError(Arc::new(value.into()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,8 +75,8 @@ impl ResourceLoader for ImageLoader {
|
|||
// load the image and construct Resource<Texture>
|
||||
let image = image::load_from_memory(&buf)
|
||||
.map_err(|e| match e {
|
||||
ImageError::IoError(e) => LoaderError::IoError(e),
|
||||
_ => LoaderError::DecodingError(e.into()),
|
||||
ImageError::IoError(e) => LoaderError::IoError(Arc::new(e)),
|
||||
_ => LoaderError::DecodingError(Arc::new(e.into())),
|
||||
})?;
|
||||
let image = Image::from(image);
|
||||
let image = Box::new(image) as Box<dyn ResourceData>;
|
||||
|
@ -89,8 +89,8 @@ impl ResourceLoader for ImageLoader {
|
|||
Box::pin(async move {
|
||||
let image = image::load_from_memory(&bytes[offset..(length-offset)])
|
||||
.map_err(|e| match e {
|
||||
ImageError::IoError(e) => LoaderError::IoError(e),
|
||||
_ => LoaderError::DecodingError(e.into()),
|
||||
ImageError::IoError(e) => LoaderError::IoError(Arc::new(e)),
|
||||
_ => LoaderError::DecodingError(Arc::new(e.into())),
|
||||
})?;
|
||||
let image = Image::from(image);
|
||||
debug!("Finished loading image ({} bytes)", length);
|
||||
|
|
|
@ -7,7 +7,7 @@ use thiserror::Error;
|
|||
|
||||
use crate::{resource_manager::ResourceStorage, ResourceData, ResourceManager};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[derive(Error, Debug, Clone)]
|
||||
pub enum LoaderError {
|
||||
#[error("A malformed path was given: '{0}'")]
|
||||
InvalidPath(String),
|
||||
|
@ -16,16 +16,22 @@ pub enum LoaderError {
|
|||
UnsupportedExtension(String),
|
||||
|
||||
#[error("IOError: '{0}'")]
|
||||
IoError(io::Error),
|
||||
IoError(Arc<io::Error>),
|
||||
|
||||
// From is implemented for this field in each loader module
|
||||
#[error("Decoding error: '{0}'")]
|
||||
DecodingError(anyhow::Error),
|
||||
DecodingError(Arc<anyhow::Error>),
|
||||
}
|
||||
|
||||
impl From<io::Error> for LoaderError {
|
||||
fn from(value: io::Error) -> Self {
|
||||
LoaderError::IoError(value)
|
||||
LoaderError::IoError(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for LoaderError {
|
||||
fn from(value: anyhow::Error) -> Self {
|
||||
LoaderError::DecodingError(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ pub trait ResourceData: Send + Sync + Any + 'static {
|
|||
|
||||
pub enum ResourceState {
|
||||
Loading,
|
||||
Error(Arc<LoaderError>),
|
||||
Error(LoaderError),
|
||||
Ready(Box<dyn ResourceData>),
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ impl UntypedResHandle {
|
|||
matches!(d.state, ResourceState::Ready(_))
|
||||
}
|
||||
|
||||
pub fn get_error(&self) -> Option<Arc<LoaderError>> {
|
||||
pub fn get_error(&self) -> Option<LoaderError> {
|
||||
let d = self.read();
|
||||
|
||||
match &d.state {
|
||||
|
@ -197,14 +197,15 @@ impl UntypedResHandle {
|
|||
///
|
||||
/// This blocks the thread without consuming CPU time; its backed by a
|
||||
/// [`Condvar`](std::sync::Condvar).
|
||||
pub fn wait_for_load(&self) {
|
||||
self.wait_for_load_timeout_option_impl(None);
|
||||
pub fn wait_for_load(&self) -> Result<(), LoaderError> {
|
||||
self.wait_for_load_timeout_option_impl(None)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Does the same as [`UntypedResHandle::wait_for_load`] but has a timeout.
|
||||
///
|
||||
/// Returns true if the resource was loaded before hitting the timeout.
|
||||
pub fn wait_for_load_timeout(&self, timeout: Duration) -> bool {
|
||||
pub fn wait_for_load_timeout(&self, timeout: Duration) -> Result<bool, LoaderError> {
|
||||
self.wait_for_load_timeout_option_impl(Some(timeout))
|
||||
}
|
||||
|
||||
|
@ -212,41 +213,48 @@ impl UntypedResHandle {
|
|||
///
|
||||
/// This blocks the thread without consuming CPU time; its backed by a
|
||||
/// [`Condvar`](std::sync::Condvar).
|
||||
pub fn wait_recurse_dependencies_load(&self) {
|
||||
self.wait_recurse_dependencies_load_timeout_option_impl(None);
|
||||
pub fn wait_recurse_dependencies_load(&self) -> Result<(), LoaderError> {
|
||||
self.wait_recurse_dependencies_load_timeout_option_impl(None)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Does the same as [`UntypedResHandle::wait_recurse_dependencies_load`] but has a timeout.
|
||||
///
|
||||
/// Returns true if the resource was loaded before hitting the timeout.
|
||||
pub fn wait_recurse_dependencies_load_timeout(&self, timeout: Duration) -> bool {
|
||||
pub fn wait_recurse_dependencies_load_timeout(&self, timeout: Duration) -> Result<bool, LoaderError> {
|
||||
self.wait_recurse_dependencies_load_timeout_option_impl(Some(timeout))
|
||||
}
|
||||
|
||||
fn wait_for_load_timeout_option_impl(&self, timeout: Option<Duration>) -> bool {
|
||||
fn wait_for_load_timeout_option_impl(&self, timeout: Option<Duration>) -> Result<bool, LoaderError> {
|
||||
let d = self.read();
|
||||
|
||||
if matches!(d.state, ResourceState::Ready(_)) {
|
||||
return true;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
let cv = d.condvar.clone();
|
||||
// MUST DROP to avoid deadlock
|
||||
drop(d);
|
||||
|
||||
let l = cv.0.lock().unwrap();
|
||||
|
||||
if let Some(timeout) = timeout {
|
||||
let (_unused, timeout) = cv.1.wait_timeout(l, timeout).unwrap();
|
||||
!timeout.timed_out()
|
||||
let (_unused, _) = cv.1.wait_timeout(l, timeout).unwrap();
|
||||
} else {
|
||||
let _unused = cv.1.wait(l).unwrap();
|
||||
true
|
||||
}
|
||||
|
||||
let d = self.read();
|
||||
match &d.state {
|
||||
// it will still be loading if the timeout is exceeded
|
||||
ResourceState::Loading => Ok(false),
|
||||
ResourceState::Error(e) => Err(e.clone()),
|
||||
ResourceState::Ready(_) => Ok(true),
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_recurse_dependencies_load_timeout_option_impl(&self, timeout: Option<Duration>) -> bool {
|
||||
if !self.wait_for_load_timeout_option_impl(timeout) {
|
||||
return false;
|
||||
fn wait_recurse_dependencies_load_timeout_option_impl(&self, timeout: Option<Duration>) -> Result<bool, LoaderError> {
|
||||
if !self.wait_for_load_timeout_option_impl(timeout)? {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let res = self.read();
|
||||
|
@ -257,13 +265,13 @@ impl UntypedResHandle {
|
|||
// waiting for some resources and finish early.
|
||||
while self.recurse_dependency_state().is_loading() {
|
||||
for dep in data.recur_dependencies() {
|
||||
if !dep.wait_for_load_timeout_option_impl(timeout) {
|
||||
return false;
|
||||
if !dep.wait_for_load_timeout_option_impl(timeout)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
Ok(true)
|
||||
},
|
||||
// self.wait_for_load at the start ensures that the state is ready
|
||||
_ => unreachable!()
|
||||
|
@ -315,6 +323,12 @@ pub struct ResHandle<T: ResourceData> {
|
|||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: ResourceData> Default for ResHandle<T> {
|
||||
fn default() -> Self {
|
||||
Self::new_loading(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ResourceData> Clone for ResHandle<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
|
@ -591,7 +605,7 @@ mod tests {
|
|||
assert!(state.is_loading());
|
||||
|
||||
// this will take a bit
|
||||
res.wait_recurse_dependencies_load();
|
||||
res.wait_recurse_dependencies_load().unwrap();
|
||||
|
||||
let state = res.recurse_dependency_state();
|
||||
assert!(!state.is_loading());
|
||||
|
|
|
@ -166,7 +166,8 @@ impl ResourceManager {
|
|||
}
|
||||
Err(err) => {
|
||||
let mut d = untyped.write();
|
||||
d.state = ResourceState::Error(Arc::new(err));
|
||||
d.state = ResourceState::Error(err);
|
||||
d.condvar.1.notify_all();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -236,10 +237,12 @@ impl ResourceManager {
|
|||
Ok(data) => {
|
||||
let mut d = thand.write();
|
||||
d.state = ResourceState::Ready(data);
|
||||
d.condvar.1.notify_all();
|
||||
}
|
||||
Err(err) => {
|
||||
let mut d = thand.write();
|
||||
d.state = ResourceState::Error(Arc::new(err));
|
||||
d.state = ResourceState::Error(err);
|
||||
d.condvar.1.notify_all();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -273,6 +276,15 @@ impl ResourceManager {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_str<T>(&self, ident: &str, mime_type: &str, text: &str) -> Result<ResHandle<T>, RequestError>
|
||||
where
|
||||
T: ResourceData
|
||||
{
|
||||
let bytes = text.as_bytes().to_vec();
|
||||
let len = bytes.len();
|
||||
self.load_bytes(ident, mime_type, bytes, 0, len)
|
||||
}
|
||||
|
||||
/// Start watching a path for changes. Returns a mspc channel that will send events
|
||||
pub fn watch(&self, path: &str, recursive: bool) -> notify::Result<Receiver<Result<Vec<DebouncedEvent>, Vec<notify::Error>>>> {
|
||||
let (send, recv) = crossbeam::channel::bounded(15);
|
||||
|
@ -358,7 +370,7 @@ impl ResourceManager {
|
|||
}
|
||||
Err(err) => {
|
||||
let mut d = thand.write();
|
||||
d.state = ResourceState::Error(Arc::new(err));
|
||||
d.state = ResourceState::Error(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -370,7 +382,7 @@ impl ResourceManager {
|
|||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use std::{io, ops::Deref};
|
||||
use std::io;
|
||||
|
||||
use instant::Instant;
|
||||
|
||||
|
@ -420,8 +432,7 @@ pub mod tests {
|
|||
let res = man.request::<Image>(&get_image("squiggles.png")).unwrap();
|
||||
assert!(!res.is_loaded());
|
||||
|
||||
res.wait_for_load();
|
||||
//busy_wait_resource(&res, 10.0);
|
||||
res.wait_for_load().unwrap();
|
||||
|
||||
// shouldn't panic because of the loop
|
||||
res.data_ref().unwrap();
|
||||
|
@ -455,7 +466,7 @@ pub mod tests {
|
|||
// make sure the error is NotFound
|
||||
//RequestError::Loader(LoaderError::IoError(e)) if e.kind() == io::ErrorKind::NotFound => true,
|
||||
ResourceState::Error(err) => {
|
||||
match err.deref() {
|
||||
match err {
|
||||
LoaderError::IoError(e) if e.kind() == io::ErrorKind::NotFound => true,
|
||||
_ => false,
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
use base64::Engine;
|
|
@ -121,11 +121,11 @@ fn setup_scene_plugin(app: &mut App) {
|
|||
.request::<Gltf>("../assets/cube-texture-embedded.gltf")
|
||||
.unwrap();
|
||||
|
||||
cube_gltf.wait_recurse_dependencies_load();
|
||||
cube_gltf.wait_recurse_dependencies_load().unwrap();
|
||||
let cube_mesh = &cube_gltf.data_ref().unwrap().scenes[0];
|
||||
|
||||
let image = resman.request::<Image>("../assets/Egg_item.png").unwrap();
|
||||
image.wait_recurse_dependencies_load();
|
||||
image.wait_recurse_dependencies_load().unwrap();
|
||||
|
||||
drop(resman);
|
||||
world.spawn((
|
||||
|
|
Loading…
Reference in New Issue