diff --git a/assets/patterned_brick_floor_02_diff_2k.png b/assets/patterned_brick_floor_02_diff_2k.png new file mode 100644 index 00000000..64300978 Binary files /dev/null and b/assets/patterned_brick_floor_02_diff_2k.png differ diff --git a/assets/solid.json b/assets/solid.json index d367c70d..9d0126e6 100644 --- a/assets/solid.json +++ b/assets/solid.json @@ -2,7 +2,7 @@ "domain": "World", "type": "Surface", "shaders": { - "Fragment": "shaders/fragment.besl" + "Fragment": "shaders/fragment" }, "variables": [ { @@ -10,6 +10,12 @@ "data_type": "vec4f", "type": "Static", "value": "Purple" + }, + { + "name": "texture", + "data_type": "Texture2D", + "type": "Static", + "value": "patterned_brick_floor_02_diff_2k" } ] } \ No newline at end of file diff --git a/docs/Design/Resource Management/assets.md b/docs/Design/Resource Management/assets.md new file mode 100644 index 00000000..820b2e55 --- /dev/null +++ b/docs/Design/Resource Management/assets.md @@ -0,0 +1,5 @@ +Assets are loaded in the following manner: +- `get()` is called by a user on the resource manager. +- The resource manager checks if the resource is already cached/processed. + - If it is, the resource document is fetched along with it's dependencies which are resolved recursivelly. In the end all resources will be provided to the user is such a manner that they are loaded in the correct order, that is all children will be loaded before their parents. + - If it is not, the resource manager will invoke all resource handlers so that they can handle the asset if needed. \ No newline at end of file diff --git a/docs/Design/Resource Management/index.yml b/docs/Design/Resource Management/index.yml new file mode 100644 index 00000000..b639e8a9 --- /dev/null +++ b/docs/Design/Resource Management/index.yml @@ -0,0 +1,2 @@ +order: 0 +icon: file \ No newline at end of file diff --git a/docs/Design/Resource Management/readme.md b/docs/Design/Resource Management/readme.md new file mode 100644 index 00000000..bcf24455 --- /dev/null +++ b/docs/Design/Resource Management/readme.md @@ -0,0 +1,6 @@ +This section details who the asset processing and resource loading is approached in the engine. From the asset loading during development to the resource handling during runtime. + +Some terminology to keep in mind: +* **Asset**: User provided file describing something that you want to consume in your application. (JPEG, PNG, GLTF, JSON, etc) +* **Resource**: An asset that has been processed by the engine. Contains useful metadata such as binary size, format, hash and the actual resource info like texture extent, mesh vertex description, etc. Resources are usually comprised of both binary data and structured information such as a document where the metadata is stored. +* **URL**: A URL is a reference to an asset or resource. The asset extension must not be named. The URL can be a filesystem path relative to the /assets folder or an internet URL. \ No newline at end of file diff --git a/src/render_domain.rs b/src/render_domain.rs index 6fc390bf..c34b14d4 100644 --- a/src/render_domain.rs +++ b/src/render_domain.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, hash::Hash}; use log::{error, trace}; use maths_rs::{prelude::MatTranslate, Mat4f}; -use crate::{resource_manager::{self, mesh_resource_handler, material_resource_handler::{Shader, Material, Variant}}, rendering::{render_system::{RenderSystem, self}, directional_light::DirectionalLight, point_light::PointLight}, Extent, orchestrator::{Entity, System, self, OrchestratorReference}, Vector3, camera::{self, Camera}, math, window_system}; +use crate::{resource_manager::{self, mesh_resource_handler, material_resource_handler::{Shader, Material, Variant}, texture_resource_handler}, rendering::{render_system::{RenderSystem, self}, directional_light::DirectionalLight, point_light::PointLight}, Extent, orchestrator::{Entity, System, self, OrchestratorReference}, Vector3, camera::{self, Camera}, math, window_system}; struct ToneMapPass { pipeline_layout: render_system::PipelineLayoutHandle, @@ -81,9 +81,11 @@ pub struct VisibilityWorldRenderDomain { material_evaluation_materials: HashMap, tone_map_pass: ToneMapPass, -debug_position: render_system::TextureHandle, -debug_normal: render_system::TextureHandle, -light_data_buffer: render_system::BufferHandle, + debug_position: render_system::TextureHandle, + debug_normal: render_system::TextureHandle, + light_data_buffer: render_system::BufferHandle, + + pending_texture_loads: Vec, } impl VisibilityWorldRenderDomain { @@ -147,11 +149,11 @@ impl VisibilityWorldRenderDomain { let indices_buffer_handle = render_system.create_buffer(Some("Visibility Index Buffer"), 1024 * 1024 * 16, render_system::Uses::Index | render_system::Uses::Storage, render_system::DeviceAccesses::CpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::STATIC); - let debug_position = render_system.create_texture(Some("debug position"), Extent::new(1920, 1080, 1), render_system::TextureFormats::RGBAu16, render_system::Uses::RenderTarget | render_system::Uses::Storage | render_system::Uses::TransferDestination, render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); - let debug_normal = render_system.create_texture(Some("debug normal"), Extent::new(1920, 1080, 1), render_system::TextureFormats::RGBAu16, render_system::Uses::RenderTarget | render_system::Uses::Storage | render_system::Uses::TransferDestination, render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); + let debug_position = render_system.create_texture(Some("debug position"), Extent::new(1920, 1080, 1), render_system::TextureFormats::RGBAu16, None, render_system::Uses::RenderTarget | render_system::Uses::Storage | render_system::Uses::TransferDestination, render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); + let debug_normal = render_system.create_texture(Some("debug normal"), Extent::new(1920, 1080, 1), render_system::TextureFormats::RGBAu16, None, render_system::Uses::RenderTarget | render_system::Uses::Storage | render_system::Uses::TransferDestination, render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); - let albedo = render_system.create_texture(Some("albedo"), Extent::new(1920, 1080, 1), render_system::TextureFormats::RGBAu16, render_system::Uses::RenderTarget | render_system::Uses::Storage | render_system::Uses::TransferDestination, render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); - let depth_target = render_system.create_texture(Some("depth_target"), Extent::new(1920, 1080, 1), render_system::TextureFormats::Depth32, render_system::Uses::DepthStencil, render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); + let albedo = render_system.create_texture(Some("albedo"), Extent::new(1920, 1080, 1), render_system::TextureFormats::RGBAu16, None, render_system::Uses::RenderTarget | render_system::Uses::Storage | render_system::Uses::TransferDestination, render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); + let depth_target = render_system.create_texture(Some("depth_target"), Extent::new(1920, 1080, 1), render_system::TextureFormats::Depth32, None, render_system::Uses::DepthStencil, render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); let render_finished_synchronizer = render_system.create_synchronizer(true); let image_ready = render_system.create_synchronizer(false); @@ -343,9 +345,9 @@ impl VisibilityWorldRenderDomain { (&visibility_pass_fragment_shader, render_system::ShaderTypes::Fragment, vec![]), ]; - let material_id = render_system.create_texture(Some("material_id"), crate::Extent::new(1920, 1080, 1), render_system::TextureFormats::U32, render_system::Uses::RenderTarget | render_system::Uses::Storage, render_system::DeviceAccesses::GpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); - let primitive_index = render_system.create_texture(Some("primitive index"), crate::Extent::new(1920, 1080, 1), render_system::TextureFormats::U32, render_system::Uses::RenderTarget | render_system::Uses::Storage, render_system::DeviceAccesses::GpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); - let instance_id = render_system.create_texture(Some("instance_id"), crate::Extent::new(1920, 1080, 1), render_system::TextureFormats::U32, render_system::Uses::RenderTarget | render_system::Uses::Storage, render_system::DeviceAccesses::GpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); + let material_id = render_system.create_texture(Some("material_id"), crate::Extent::new(1920, 1080, 1), render_system::TextureFormats::U32, None, render_system::Uses::RenderTarget | render_system::Uses::Storage, render_system::DeviceAccesses::GpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); + let primitive_index = render_system.create_texture(Some("primitive index"), crate::Extent::new(1920, 1080, 1), render_system::TextureFormats::U32, None, render_system::Uses::RenderTarget | render_system::Uses::Storage, render_system::DeviceAccesses::GpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); + let instance_id = render_system.create_texture(Some("instance_id"), crate::Extent::new(1920, 1080, 1), render_system::TextureFormats::U32, None, render_system::Uses::RenderTarget | render_system::Uses::Storage, render_system::DeviceAccesses::GpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); let attachments = [ render_system::AttachmentInformation { @@ -844,7 +846,7 @@ impl VisibilityWorldRenderDomain { } "#; - let result = render_system.create_texture(Some("result"), Extent::new(1920, 1080, 1), render_system::TextureFormats::RGBAu8, render_system::Uses::Storage | render_system::Uses::TransferDestination, render_system::DeviceAccesses::GpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); + let result = render_system.create_texture(Some("result"), Extent::new(1920, 1080, 1), render_system::TextureFormats::RGBAu8, None, render_system::Uses::Storage | render_system::Uses::TransferDestination, render_system::DeviceAccesses::GpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::DYNAMIC); let tone_map_pass = { let descriptor_set_layout = render_system.create_descriptor_set_layout(&[ @@ -983,6 +985,8 @@ impl VisibilityWorldRenderDomain { material_evaluation_materials: HashMap::new(), tone_map_pass, + + pending_texture_loads: Vec::new(), } }) // .add_post_creation_function(Box::new(Self::load_needed_assets)) @@ -998,6 +1002,23 @@ impl VisibilityWorldRenderDomain { for resource_document in &response.resources { match resource_document.class.as_str() { + "Texture" => { + let texture: &texture_resource_handler::Texture = resource_document.resource.downcast_ref().unwrap(); + + let compression = if let Some(compression) = &texture.compression { + match compression { + texture_resource_handler::CompressionSchemes::BC7 => Some(render_system::CompressionSchemes::BC7) + } + } else { + None + }; + + let new_texture = render_system.create_texture(Some(&resource_document.url), texture.extent, render_system::TextureFormats::RGBAu8, compression, render_system::Uses::Texture | render_system::Uses::TransferDestination, render_system::DeviceAccesses::CpuWrite | render_system::DeviceAccesses::GpuRead, render_system::UseCases::STATIC); + + render_system.get_texture_slice_mut(new_texture).copy_from_slice(&buffer[resource_document.offset as usize..(resource_document.offset + resource_document.size) as usize]); + + self.pending_texture_loads.push(new_texture); + } "Shader" => { let shader: &Shader = resource_document.resource.downcast_ref().unwrap(); @@ -1194,7 +1215,9 @@ impl VisibilityWorldRenderDomain { { let mut command_buffer_recording = render_system.create_command_buffer_recording(self.transfer_command_buffer, None); - // TODO: Copy the data from the CPU to the GPU + command_buffer_recording.transfer_textures(&self.pending_texture_loads); + + self.pending_texture_loads.clear(); command_buffer_recording.execute(&[], &[self.transfer_synchronizer], self.transfer_synchronizer); } diff --git a/src/rendering/render_system.rs b/src/rendering/render_system.rs index 689fce1f..ea59f3d0 100644 --- a/src/rendering/render_system.rs +++ b/src/rendering/render_system.rs @@ -183,6 +183,8 @@ pub trait CommandBufferRecording { fn clear_texture(&mut self, texture_handle: TextureHandle, clear_value: ClearValue); fn clear_buffer(&mut self, buffer_handle: BufferHandle); + fn transfer_textures(&mut self, texture_handles: &[TextureHandle]); + /// Copies texture data from a CPU accessible buffer to a GPU accessible texture. fn write_texture_data(&mut self, texture_handle: TextureHandle, data: &[RGBAu8]); @@ -264,8 +266,10 @@ pub trait RenderSystem: orchestrator::System { // Return a mutable slice to the buffer data. fn get_mut_buffer_slice(&self, buffer_handle: BufferHandle) -> &mut [u8]; + fn get_texture_slice_mut(&self, texture_handle: TextureHandle) -> &mut [u8]; + /// Creates a texture. - fn create_texture(&mut self, name: Option<&str>, extent: crate::Extent, format: TextureFormats, resource_uses: Uses, device_accesses: DeviceAccesses, use_case: UseCases) -> TextureHandle; + fn create_texture(&mut self, name: Option<&str>, extent: crate::Extent, format: TextureFormats, compression: Option, resource_uses: Uses, device_accesses: DeviceAccesses, use_case: UseCases) -> TextureHandle; fn create_sampler(&mut self) -> SamplerHandle; @@ -390,7 +394,7 @@ pub(super) mod tests { // Use and odd width to make sure there is a middle/center pixel let extent = crate::Extent { width: 1920, height: 1080, depth: 1 }; - let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::STATIC); + let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::STATIC); let attachments = [ AttachmentInformation { @@ -521,7 +525,7 @@ pub(super) mod tests { let pipeline_layout = renderer.create_pipeline_layout(&[], &[]); - let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, Uses::RenderTarget, DeviceAccesses::GpuWrite, UseCases::STATIC); + let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::GpuWrite, UseCases::STATIC); let attachments = [ AttachmentInformation { @@ -646,7 +650,7 @@ pub(super) mod tests { let pipeline_layout = renderer.create_pipeline_layout(&[], &[]); - let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, Uses::RenderTarget, DeviceAccesses::GpuWrite | DeviceAccesses::CpuRead, UseCases::DYNAMIC); + let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::GpuWrite | DeviceAccesses::CpuRead, UseCases::DYNAMIC); let attachments = [ AttachmentInformation { @@ -781,7 +785,7 @@ pub(super) mod tests { // Use and odd width to make sure there is a middle/center pixel let extent = crate::Extent { width: 1920, height: 1080, depth: 1 }; - let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::DYNAMIC); + let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::DYNAMIC); let attachments = [ AttachmentInformation { @@ -919,7 +923,7 @@ pub(super) mod tests { // Use and odd width to make sure there is a middle/center pixel let extent = crate::Extent { width: 1920, height: 1080, depth: 1 }; - let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::DYNAMIC); + let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::DYNAMIC); let attachments = [ AttachmentInformation { @@ -1083,7 +1087,7 @@ pub(super) mod tests { let buffer = renderer.create_buffer(None, 64, Uses::Uniform | Uses::Storage, DeviceAccesses::CpuWrite | DeviceAccesses::GpuRead, UseCases::DYNAMIC); - let sampled_texture = renderer.create_texture(None, crate::Extent { width: 2, height: 2, depth: 1 }, TextureFormats::RGBAu8, Uses::Texture, DeviceAccesses::CpuWrite | DeviceAccesses::GpuRead, UseCases::STATIC); + let sampled_texture = renderer.create_texture(None, crate::Extent { width: 2, height: 2, depth: 1 }, TextureFormats::RGBAu8, None, Uses::Texture, DeviceAccesses::CpuWrite | DeviceAccesses::GpuRead, UseCases::STATIC); let pixels = vec![ RGBAu8 { r: 255, g: 0, b: 0, a: 255 }, @@ -1138,7 +1142,7 @@ pub(super) mod tests { // Use and odd width to make sure there is a middle/center pixel let extent = crate::Extent { width: 1920, height: 1080, depth: 1 }; - let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::STATIC); + let render_target = renderer.create_texture(None, extent, TextureFormats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::STATIC); let attachments = [ AttachmentInformation { @@ -1271,6 +1275,11 @@ pub enum TextureFormats { U32, } +#[derive(Clone, Copy)] +pub enum CompressionSchemes { + BC7, +} + #[derive(Clone, Copy)] /// Stores the information of a memory region. pub struct Memory<'a> { @@ -1665,6 +1674,10 @@ impl RenderSystem for RenderSystemImplementation { self.pointer.get_mut_buffer_slice(buffer_handle) } + fn get_texture_slice_mut(&self, texture_handle: TextureHandle) -> &mut [u8] { + self.pointer.get_texture_slice_mut(texture_handle) + } + fn get_texture_data(&self, texture_copy_handle: TextureCopyHandle) -> &[u8] { self.pointer.get_texture_data(texture_copy_handle) } @@ -1737,7 +1750,7 @@ impl RenderSystem for RenderSystemImplementation { self.pointer.create_synchronizer(signaled) } - fn create_texture(&mut self, name: Option<&str>, extent: crate::Extent, format: TextureFormats, resource_uses: Uses, device_accesses: DeviceAccesses, use_case: UseCases) -> TextureHandle { - self.pointer.create_texture(name, extent, format, resource_uses, device_accesses, use_case) + fn create_texture(&mut self, name: Option<&str>, extent: crate::Extent, format: TextureFormats, compression: Option, resource_uses: Uses, device_accesses: DeviceAccesses, use_case: UseCases) -> TextureHandle { + self.pointer.create_texture(name, extent, format, compression, resource_uses, device_accesses, use_case) } } \ No newline at end of file diff --git a/src/rendering/vulkan_render_system.rs b/src/rendering/vulkan_render_system.rs index 9fc54066..858a8d84 100644 --- a/src/rendering/vulkan_render_system.rs +++ b/src/rendering/vulkan_render_system.rs @@ -527,12 +527,19 @@ impl render_system::RenderSystem for VulkanRenderSystem { fn get_mut_buffer_slice(&self, buffer_handle: render_system::BufferHandle) -> &mut [u8] { let buffer = self.buffers[buffer_handle.0 as usize]; unsafe { - std::slice::from_raw_parts_mut(buffer.pointer, buffer.size as usize) + std::slice::from_raw_parts_mut(buffer.pointer, buffer.size) + } + } + + fn get_texture_slice_mut(&self, texture_handle: render_system::TextureHandle) -> &mut [u8] { + let texture = &self.textures[texture_handle.0 as usize]; + unsafe { + std::slice::from_raw_parts_mut(texture.pointer as *mut u8, texture.size) } } /// Creates a texture. - fn create_texture(&mut self, name: Option<&str>, extent: crate::Extent, format: render_system::TextureFormats, resource_uses: render_system::Uses, device_accesses: render_system::DeviceAccesses, use_case: render_system::UseCases) -> render_system::TextureHandle { + fn create_texture(&mut self, name: Option<&str>, extent: crate::Extent, format: render_system::TextureFormats, compression: Option, resource_uses: render_system::Uses, device_accesses: render_system::DeviceAccesses, use_case: render_system::UseCases) -> render_system::TextureHandle { let size = (extent.width * extent.height * extent.depth * 4) as usize; let texture_handle = render_system::TextureHandle(self.textures.len() as u64); @@ -548,7 +555,7 @@ impl render_system::RenderSystem for VulkanRenderSystem { resource_uses }; - let texture_creation_result = self.create_vulkan_texture(name, extent, format, resource_uses | render_system::Uses::TransferSource, device_accesses, render_system::AccessPolicies::WRITE, 1); + let texture_creation_result = self.create_vulkan_texture(name, extent, format, compression, resource_uses | render_system::Uses::TransferSource, device_accesses, render_system::AccessPolicies::WRITE, 1); let (allocation_handle, pointer) = self.create_allocation_internal(texture_creation_result.size, device_accesses); @@ -556,9 +563,9 @@ impl render_system::RenderSystem for VulkanRenderSystem { let texture_handle = render_system::TextureHandle(self.textures.len() as u64); - let image_view = self.create_vulkan_texture_view(name, &texture_creation_result.resource, format, 0); + let image_view = self.create_vulkan_texture_view(name, &texture_creation_result.resource, format, compression, 0); - let staging_buffer = if device_accesses.contains(render_system::DeviceAccesses::CpuRead) { + let (staging_buffer, pointer) = if device_accesses.contains(render_system::DeviceAccesses::CpuRead) { let staging_buffer_creation_result = self.create_vulkan_buffer(name, size, render_system::Uses::TransferDestination); let (allocation_handle, pointer) = self.create_allocation_internal(staging_buffer_creation_result.size, render_system::DeviceAccesses::CpuRead); @@ -574,7 +581,7 @@ impl render_system::RenderSystem for VulkanRenderSystem { pointer, }); - Some(staging_buffer_handle) + (Some(staging_buffer_handle), pointer) } else if device_accesses.contains(render_system::DeviceAccesses::CpuWrite) { let staging_buffer_creation_result = self.create_vulkan_buffer(name, size, render_system::Uses::TransferSource); @@ -591,20 +598,21 @@ impl render_system::RenderSystem for VulkanRenderSystem { pointer, }); - Some(staging_buffer_handle) + (Some(staging_buffer_handle), pointer) } else { - None + (None, std::ptr::null_mut()) }; self.textures.push(Texture { next: None, + size: texture_creation_result.size, staging_buffer, allocation_handle, image: texture_creation_result.resource, image_view, pointer, extent, - format: to_format(format), + format: to_format(format, compression), format_: format, layout: vk::ImageLayout::UNDEFINED, }); @@ -825,6 +833,7 @@ pub(crate) struct Texture { format: vk::Format, format_: render_system::TextureFormats, layout: vk::ImageLayout, + size: usize, } unsafe impl Send for Texture {} @@ -915,9 +924,17 @@ fn to_store_operation(value: bool) -> vk::AttachmentStoreOp { } } -fn to_format(format: render_system::TextureFormats) -> vk::Format { +fn to_format(format: render_system::TextureFormats, compression: Option) -> vk::Format { match format { - render_system::TextureFormats::RGBAu8 => vk::Format::R8G8B8A8_UNORM, + render_system::TextureFormats::RGBAu8 => { + if let Some(compression) = compression { + match compression { + render_system::CompressionSchemes::BC7 => vk::Format::BC7_SRGB_BLOCK, + } + } else { + vk::Format::R8G8B8A8_UNORM + } + } render_system::TextureFormats::RGBAu16 => vk::Format::R16G16B16A16_SFLOAT, render_system::TextureFormats::RGBAu32 => vk::Format::R32G32B32A32_SFLOAT, render_system::TextureFormats::RGBAf16 => vk::Format::R16G16B16A16_SFLOAT, @@ -1497,7 +1514,7 @@ impl VulkanRenderSystem { .alpha_blend_op(vk::BlendOp::ADD) }).collect::>(); - let color_attachement_formats: Vec = targets.iter().filter(|a| a.format != render_system::TextureFormats::Depth32).map(|a| to_format(a.format)).collect::>(); + let color_attachement_formats: Vec = targets.iter().filter(|a| a.format != render_system::TextureFormats::Depth32).map(|a| to_format(a.format, None)).collect::>(); let color_blend_state = vk::PipelineColorBlendStateCreateInfo::default() .logic_op_enable(false) @@ -1728,7 +1745,7 @@ impl VulkanRenderSystem { unsafe { self.device.get_buffer_device_address(&vk::BufferDeviceAddressInfo::default().buffer(buffer)) } } - fn create_vulkan_texture(&self, name: Option<&str>, extent: vk::Extent3D, format: render_system::TextureFormats, resource_uses: render_system::Uses, device_accesses: render_system::DeviceAccesses, _access_policies: render_system::AccessPolicies, mip_levels: u32) -> MemoryBackedResourceCreationResult { + fn create_vulkan_texture(&self, name: Option<&str>, extent: vk::Extent3D, format: render_system::TextureFormats, compression: Option, resource_uses: render_system::Uses, device_accesses: render_system::DeviceAccesses, _access_policies: render_system::AccessPolicies, mip_levels: u32) -> MemoryBackedResourceCreationResult { let image_type_from_extent = |extent: vk::Extent3D| { if extent.depth > 1 { vk::ImageType::TYPE_3D @@ -1741,7 +1758,7 @@ impl VulkanRenderSystem { let image_create_info = vk::ImageCreateInfo::default() .image_type(image_type_from_extent(extent)) - .format(to_format(format)) + .format(to_format(format, compression)) .extent(extent) .mip_levels(mip_levels) .array_layers(1) @@ -1861,13 +1878,13 @@ impl VulkanRenderSystem { unsafe { self.device.create_semaphore(&semaphore_create_info, None).expect("No semaphore") } } - fn create_vulkan_texture_view(&self, name: Option<&str>, texture: &vk::Image, format: render_system::TextureFormats, _mip_levels: u32) -> vk::ImageView { + fn create_vulkan_texture_view(&self, name: Option<&str>, texture: &vk::Image, format: render_system::TextureFormats, compression: Option, _mip_levels: u32) -> vk::ImageView { let image_view_create_info = vk::ImageViewCreateInfo::default() .image(*texture) .view_type( vk::ImageViewType::TYPE_2D ) - .format(to_format(format)) + .format(to_format(format, compression)) .components(vk::ComponentMapping { r: vk::ComponentSwizzle::IDENTITY, g: vk::ComponentSwizzle::IDENTITY, @@ -2131,6 +2148,10 @@ impl VulkanCommandBufferRecording<'_> { // } // } + fn get_buffer(&self, buffer_handle: render_system::BufferHandle) -> (render_system::BufferHandle, &Buffer) { + (buffer_handle, &self.render_system.buffers[buffer_handle.0 as usize]) + } + fn get_texture(&self, mut texture_handle: render_system::TextureHandle) -> (render_system::TextureHandle, &Texture) { let mut i = 0; loop { @@ -2611,6 +2632,51 @@ impl render_system::CommandBufferRecording for VulkanCommandBufferRecording<'_> } } + fn transfer_textures(&mut self, texture_handles: &[render_system::TextureHandle]) { + self.consume_resources(&texture_handles.iter().map(|texture_handle| + render_system::Consumption{ + handle: render_system::Handle::Texture(*texture_handle), + stages: render_system::Stages::TRANSFER, + access: render_system::AccessPolicies::WRITE, + layout: render_system::Layouts::Transfer, + } + // r(false, (texture_format_and_resource_use_to_image_layout(attachment.format, attachment.layout, None), if attachment.format == TextureFormats::Depth32 { vk::PipelineStageFlags2::LATE_FRAGMENT_TESTS } else { vk::PipelineStageFlags2::COLOR_ATTACHMENT_OUTPUT }, if attachment.format == TextureFormats::Depth32 { vk::AccessFlags2::DEPTH_STENCIL_ATTACHMENT_WRITE } else { vk::AccessFlags2::COLOR_ATTACHMENT_WRITE }))) + ).collect::>()); + + let command_buffer = self.get_command_buffer(); + + for texture_handle in texture_handles { + let (_, texture) = self.get_texture(*texture_handle); + + let regions = [vk::BufferImageCopy2::default() + .buffer_offset(0) + .buffer_row_length(0) + .buffer_image_height(0) + .image_subresource(vk::ImageSubresourceLayers::default() + .aspect_mask(vk::ImageAspectFlags::COLOR) + .mip_level(0) + .base_array_layer(0) + .layer_count(1) + /* .build() */ + ) + .image_offset(vk::Offset3D::default().x(0).y(0).z(0)/* .build() */) + .image_extent(vk::Extent3D::default().width(texture.extent.width).height(texture.extent.height).depth(texture.extent.depth)/* .build() */)/* .build() */]; + + let (_, buffer) = self.get_buffer(texture.staging_buffer.expect("No staging buffer")); + + // Copy to images from staging buffer + let buffer_image_copy = vk::CopyBufferToImageInfo2::default() + .src_buffer(buffer.buffer) + .dst_image(texture.image) + .dst_image_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) + .regions(®ions); + + unsafe { + self.render_system.device.cmd_copy_buffer_to_image2(command_buffer.command_buffer, &buffer_image_copy); + } + } + } + fn write_texture_data(&mut self, texture_handle: render_system::TextureHandle, data: &[render_system::RGBAu8]) { let (texture_handle, texture) = self.get_texture(texture_handle); @@ -2695,7 +2761,7 @@ impl render_system::CommandBufferRecording for VulkanCommandBufferRecording<'_> unsafe { self.render_system.device.cmd_copy_buffer_to_image2(command_buffer.command_buffer, &buffer_image_copy); - } + } let image_memory_barriers = [ vk::ImageMemoryBarrier2KHR::default() diff --git a/src/resource_manager/material_resource_handler.rs b/src/resource_manager/material_resource_handler.rs index c47f00c4..d4a2ea32 100644 --- a/src/resource_manager/material_resource_handler.rs +++ b/src/resource_manager/material_resource_handler.rs @@ -6,7 +6,7 @@ use serde::{Serialize, Deserialize}; use crate::{rendering::render_system, jspd::{self}}; -use super::{ResourceHandler, ResourceManager, SerializedResourceDocument, GenericResourceSerialization, Resource}; +use super::{ResourceHandler, ResourceManager, SerializedResourceDocument, GenericResourceSerialization, Resource, ProcessedResources}; pub struct MaterialResourcerHandler { @@ -80,7 +80,7 @@ impl ResourceHandler for MaterialResourcerHandler { } } - fn process(&self, resource_manager: &ResourceManager, asset_url: &str, bytes: &[u8]) -> Result)>, String> { + fn process(&self, resource_manager: &ResourceManager, asset_url: &str, bytes: &[u8]) -> Result, String> { let asset_json = json::parse(std::str::from_utf8(&bytes).unwrap()).unwrap(); let is_material = asset_json["parent"].is_null(); @@ -100,45 +100,40 @@ impl ResourceHandler for MaterialResourcerHandler { _ => { panic!("Invalid type") } }; - let mut shaders = asset_json["shaders"].entries().filter_map(|(s_type, shader_json)| { - Self::produce_shader(&material_domain, &asset_json, &shader_json, s_type) + let mut required_resources = asset_json["shaders"].entries().filter_map(|(s_type, shader_json)| { + Self::produce_shader(resource_manager, &material_domain, &asset_json, &shader_json, s_type) }).collect::>(); - - let required_resources = shaders.iter().map(|s| s.1.clone()).collect::>(); - let material_resource_document = GenericResourceSerialization::new(asset_url.to_string(), Material { + for variable in asset_json["variables"].members() { + if variable["data_type"].as_str().unwrap() == "Texture2D" { + let texture_url = variable["value"].as_str().unwrap(); + + required_resources.push(ProcessedResources::Ref(texture_url.to_string())); + } + } + + Ok(vec![ProcessedResources::Generated((GenericResourceSerialization::new(asset_url.to_string(), Material { model: Model { name: Self::RENDER_MODEL.to_string(), pass: "MaterialEvaluation".to_string(), }, - }).required_resources(required_resources); - - shaders.push(((material_resource_document.into(), Vec::new()), "".to_string())); - - Ok(shaders.into_iter().map(|s| s.0).collect::>()) + }).required_resources(&required_resources), Vec::new()))]) } else { let variant_json = asset_json; let parent_material_url = variant_json["parent"].as_str().unwrap(); - let _ = resource_manager.load_from_cache_or_source(parent_material_url).unwrap(); - - let material_resource_document = GenericResourceSerialization { - url: asset_url.to_string(), - class: "Variant".to_string(), - required_resources: vec![parent_material_url.to_string()], - resource: Variant{ - parent: parent_material_url.to_string(), - variables: variant_json["variables"].members().map(|v| { - VariantVariable { - name: v["name"].to_string(), - value: v["value"].to_string(), - } - }).collect::>() - } - }; - - Ok(vec![(material_resource_document.into(), Vec::new())]) + let material_resource_document = GenericResourceSerialization::new(asset_url.to_string(), Variant{ + parent: parent_material_url.to_string(), + variables: variant_json["variables"].members().map(|v| { + VariantVariable { + name: v["name"].to_string(), + value: v["value"].to_string(), + } + }).collect::>() + }).required_resources(&[ProcessedResources::Ref(parent_material_url.to_string())]); + + Ok(vec![ProcessedResources::Generated((material_resource_document.into(), Vec::new()))]) } } @@ -164,7 +159,7 @@ impl ResourceHandler for MaterialResourcerHandler { impl MaterialResourcerHandler { const RENDER_MODEL: &'static str = "Visibility"; - fn treat_shader(path: &str, domain: &str, stage: &str, material: &json::JsonValue, shader_node: jspd::lexer::Node,) -> Option), String>> { + fn treat_shader(path: &str, domain: &str, stage: &str, material: &json::JsonValue, shader_node: jspd::lexer::Node,) -> Option> { let visibility = crate::rendering::visibility_shader_generator::VisibilityShaderGenerator::new(); let glsl = visibility.transform(material, &shader_node, stage)?; @@ -207,31 +202,26 @@ impl MaterialResourcerHandler { _ => { panic!("Invalid shader stage") } }; - let resource = GenericResourceSerialization { - url: path.to_string(), - class: "Shader".to_string(), - required_resources: Vec::new(), - resource: Shader { - stage: stage, - } - }; + let resource = GenericResourceSerialization::new(path.to_string(), Shader { + stage: stage, + }); - Some(Ok((resource.into(), Vec::from(result_shader_bytes)))) + Some(Ok(ProcessedResources::Generated((resource, Vec::from(result_shader_bytes))))) } - fn produce_shader(domain: &str, material: &json::JsonValue, shader_json: &json::JsonValue, stage: &str) -> Option<((SerializedResourceDocument, Vec), String)> { + fn produce_shader(resource_manager: &ResourceManager, domain: &str, material: &json::JsonValue, shader_json: &json::JsonValue, stage: &str) -> Option { let shader_option = match shader_json { json::JsonValue::Null => { None } json::JsonValue::Short(path) => { - let arlp = "assets/".to_string() + path.as_str(); + let (arlp, format) = resource_manager.read_asset_from_source(&path).ok()?; - if path.ends_with(".glsl") { - let shader_code = std::fs::read_to_string(&arlp).unwrap(); + let shader_code = std::str::from_utf8(&arlp).unwrap().to_string(); + + if format == "glsl" { Some((jspd::lexer::Node { node: jspd::lexer::Nodes::GLSL { code: shader_code }, }, path.to_string())) - } else if path.ends_with(".besl") { - let shader_code = std::fs::read_to_string(&arlp).unwrap(); + } else if format == "besl" { Some((jspd::compile_to_jspd(&shader_code).unwrap(), path.to_string())) } else { None @@ -259,7 +249,7 @@ impl MaterialResourcerHandler { }; if let Some((shader, path)) = shader_option { - Some((Self::treat_shader(&path, domain, stage, material, shader,)?.unwrap(), path)) + Some(Self::treat_shader(&path, domain, stage, material, shader,)?.unwrap()) } else { let default_shader = match stage { "Vertex" => MaterialResourcerHandler::default_vertex_shader(), @@ -271,7 +261,7 @@ impl MaterialResourcerHandler { node: jspd::lexer::Nodes::GLSL { code: default_shader.to_string() }, }; - Some((Self::treat_shader("", domain, stage, material, shader_node,)?.unwrap(), "".to_string())) + Some(Self::treat_shader("", domain, stage, material, shader_node,)?.unwrap()) } } } \ No newline at end of file diff --git a/src/resource_manager/mesh_resource_handler.rs b/src/resource_manager/mesh_resource_handler.rs index 46b694b7..3b7f28dd 100644 --- a/src/resource_manager/mesh_resource_handler.rs +++ b/src/resource_manager/mesh_resource_handler.rs @@ -4,7 +4,7 @@ use log::error; use polodb_core::bson::{Document, doc}; use serde::{Serialize, Deserialize}; -use super::{ResourceHandler, SerializedResourceDocument, GenericResourceSerialization, Resource}; +use super::{ResourceHandler, SerializedResourceDocument, GenericResourceSerialization, Resource, ProcessedResources}; pub struct MeshResourceHandler { @@ -25,7 +25,7 @@ impl ResourceHandler for MeshResourceHandler { } } - fn process(&self, _: &super::ResourceManager, asset_url: &str, bytes: &[u8]) -> Result)>, String> { + fn process(&self, _: &super::ResourceManager, asset_url: &str, bytes: &[u8]) -> Result, String> { let (gltf, buffers, _) = gltf::import_slice(bytes).unwrap(); let mut buf: Vec = Vec::with_capacity(4096 * 1024 * 3); @@ -102,14 +102,9 @@ impl ResourceHandler for MeshResourceHandler { index_type }; - let resource_document = GenericResourceSerialization { - url: asset_url.to_string(), - required_resources: Vec::new(), - class: "Mesh".to_string(), - resource: mesh, - }; + let resource_document = GenericResourceSerialization::new(asset_url.to_string(), mesh); - Ok(vec![(resource_document.into(), buf)]) + Ok(vec![ProcessedResources::Generated((resource_document, buf))]) } fn get_deserializers(&self) -> Vec<(&'static str, Box Box + Send>)> { diff --git a/src/resource_manager/mod.rs b/src/resource_manager/mod.rs index ae4c419c..300fdfcd 100644 --- a/src/resource_manager/mod.rs +++ b/src/resource_manager/mod.rs @@ -2,7 +2,7 @@ //! Handles loading assets or resources from different origins (network, local, etc.). //! It also handles caching of resources. -mod image_resource_handler; +pub mod texture_resource_handler; pub mod mesh_resource_handler; pub mod material_resource_handler; @@ -15,6 +15,12 @@ use crate::orchestrator::{System, self}; // https://www.yosoygames.com.ar/wp/2018/03/vertex-formats-part-1-compression/ +#[derive(Debug, Clone)] +enum ProcessedResources { + Generated((GenericResourceSerialization, Vec)), + Ref(String), +} + trait ResourceHandler { fn can_handle_type(&self, resource_type: &str) -> bool; @@ -29,7 +35,7 @@ trait ResourceHandler { /// - **resource**: The resource data. Can look like anything. /// - **hash**(optional): The resource hash. This is used to identify the resource data. If the resource handler wants to generate a hash for the resource it can do so else the resource manager will generate a hash for it. This is because some resources can generate hashes inteligently (EJ: code generators can output same hash for different looking code if the code is semantically identical). /// - **required_resources**(optional): A list of resources that this resource depends on. This is used to load resources that depend on other resources. - fn process(&self, resource_manager: &ResourceManager, asset_url: &str, bytes: &[u8]) -> Result)>, String>; + fn process(&self, resource_manager: &ResourceManager, asset_url: &str, bytes: &[u8]) -> Result, String>; fn get_deserializers(&self) -> Vec<(&'static str, Box Box + Send>)>; @@ -114,42 +120,37 @@ pub trait Resource { } /// This is the struct resource handlers should return when processing a resource. -#[derive(Debug, serde::Serialize, serde::Deserialize)] -pub struct GenericResourceSerialization { +#[derive(Debug, Clone)] +pub struct GenericResourceSerialization { /// The resource id. This is used to identify the resource. Needs to be meaningful and will be a public constant. url: String, /// The resource class (EJ: "Texture", "Mesh", "Material", etc.) class: String, /// List of resources that this resource depends on. - required_resources: Vec, + required_resources: Vec, /// The resource data. - resource: T, + resource: polodb_core::bson::Document, } -impl GenericResourceSerialization { - pub fn new(url: String, resource: T) -> Self { +impl GenericResourceSerialization { + pub fn new(url: String, resource: T) -> Self { GenericResourceSerialization { url, required_resources: Vec::new(), class: resource.get_class().to_string(), - resource, + resource: polodb_core::bson::to_document(&resource).unwrap(), } } - pub fn required_resources(mut self, required_resources: Vec) -> Self { - self.required_resources = required_resources; + pub fn required_resources(mut self, required_resources: &[ProcessedResources]) -> Self { + self.required_resources = required_resources.to_vec(); self } } +#[derive(Debug, Clone)] pub struct SerializedResourceDocument(polodb_core::bson::Document); -impl Into for GenericResourceSerialization { - fn into(self) -> SerializedResourceDocument { - SerializedResourceDocument(polodb_core::bson::to_document(&self).unwrap()) - } -} - pub struct Request { pub resources: Vec, } @@ -234,7 +235,7 @@ impl ResourceManager { }; let resource_handlers: Vec> = vec![ - Box::new(image_resource_handler::ImageResourceHandler::new()), + Box::new(texture_resource_handler::ImageResourceHandler::new()), Box::new(mesh_resource_handler::MeshResourceHandler::new()), Box::new(material_resource_handler::MaterialResourcerHandler::new()) ]; @@ -307,50 +308,78 @@ impl ResourceManager { } } - /// Tries to load a resource from cache.\ - fn load_from_cache_or_source(&self, url: &str) -> Option { - fn gather(db: &polodb_core::Database, url: &str) -> Option> { - let doc = db.collection::("resources").find_one(doc!{ "url": url }).unwrap()?; - + /// Recursively loads all the resources needed to load the resource at the given url. + /// **Will** load from source and cache the resources if they are not already cached. + fn gather(&self, db: &polodb_core::Database, url: &str) -> Option> { + let resource_documents = if let Some(resource_document) = db.collection::("resources").find_one(doc!{ "url": url }).unwrap() { let mut documents = vec![]; - if let Some(polodb_core::bson::Bson::Array(required_resources)) = doc.get("required_resources") { + if let Some(polodb_core::bson::Bson::Array(required_resources)) = resource_document.get("required_resources") { for required_resource in required_resources { if let polodb_core::bson::Bson::Document(required_resource) = required_resource { let resource_path = required_resource.get("url").unwrap().as_str().unwrap(); - documents.append(&mut gather(db, resource_path)?); + documents.append(&mut self.gather(db, resource_path)?); } if let polodb_core::bson::Bson::String(required_resource) = required_resource { let resource_path = required_resource.as_str(); - documents.append(&mut gather(db, resource_path)?); + documents.append(&mut self.gather(db, resource_path)?); } } } - documents.push(doc); + documents.push(resource_document); - Some(documents) - } - - let resource_descriptions = if let Some(a) = gather(&self.db, url) { - a + documents } else { let r = self.read_asset_from_source(url).unwrap(); - let mut generated_resources = Vec::new(); + let mut loaded_resource_documents = Vec::new(); + + let resource_handlers = self.resource_handlers.iter().filter(|h| h.can_handle_type(r.1.as_str())); - for resource_handler in &self.resource_handlers { - if resource_handler.can_handle_type(r.1.as_str()) { - generated_resources.append(&mut resource_handler.process(self, url, &r.0).unwrap()); + for resource_handler in resource_handlers { + let gg = resource_handler.process(self, url, &r.0).unwrap(); + + for g in gg { + match g { + ProcessedResources::Generated(g) => { + for e in &g.0.required_resources { + match e { + ProcessedResources::Generated(g) => { + loaded_resource_documents.push(self.write_resource_to_cache(&g,)?); + }, + ProcessedResources::Ref(r) => { + loaded_resource_documents.append(&mut self.gather(db, &r)?); + } + } + } + + loaded_resource_documents.push(self.write_resource_to_cache(&g,)?); + }, + ProcessedResources::Ref(r) => { + loaded_resource_documents.append(&mut self.gather(db, &r)?); + } + } } } - self.write_resource_to_cache(&generated_resources,)?; + if loaded_resource_documents.len() == 0 { + warn!("No resource handler could handle resource: {}", url); + } - gather(&self.db, url)? + loaded_resource_documents }; + + Some(resource_documents) + } + + /// Tries to load a resource from cache.\ + /// It also resolves all dependencies.\ + fn load_from_cache_or_source(&self, url: &str) -> Option { + let resource_descriptions = self.gather(&self.db, url).expect("Could not load resource"); + for r in &resource_descriptions { trace!("Loaded resource: {:#?}", r); } @@ -375,45 +404,63 @@ impl ResourceManager { /// Stores the asset as a resource. /// Returns the resource document. - fn write_resource_to_cache(&self, resource_packages: &[(SerializedResourceDocument, Vec)],) -> Option<()> { - for resource_package in resource_packages { - let mut full_resource_document = resource_package.0.0.clone(); - - let mut hasher = std::collections::hash_map::DefaultHasher::new(); + fn write_resource_to_cache(&self, resource_package: &(GenericResourceSerialization, Vec)) -> Option { + let mut resource_document = polodb_core::bson::Document::new(); - if let Some(polodb_core::bson::Bson::String(p)) = full_resource_document.get("url") { - p.hash(&mut hasher); - } else { - error!("Resource document does not have a path field."); - } + let mut hasher = std::collections::hash_map::DefaultHasher::new(); - full_resource_document.insert("id", hasher.finish() as i64); - full_resource_document.insert("size", resource_package.1.len() as i64); + resource_document.insert("id", hasher.finish() as i64); + resource_document.insert("size", resource_package.1.len() as i64); - if let None = full_resource_document.get("hash") { - let mut hasher = std::collections::hash_map::DefaultHasher::new(); + resource_document.insert("url", resource_package.0.url.clone()); + resource_package.0.url.hash(&mut hasher); - std::hash::Hasher::write(&mut hasher, resource_package.1.as_slice()); // Hash binary data + resource_document.insert("class", resource_package.0.class.clone()); - std::hash::Hasher::write(&mut hasher, &to_vec(full_resource_document.get("resource").unwrap()).unwrap()); // Hash resource metadata, since changing the resources description must also change the hash. (For caching purposes) + let mut required_resources_json = polodb_core::bson::Array::new(); - full_resource_document.insert("hash", hasher.finish() as i64); + for required_resources in &resource_package.0.required_resources { // TODO: make new type that gives a guarantee that these resources have been loaded + match required_resources { + ProcessedResources::Generated(g) => { + required_resources_json.push(polodb_core::bson::Bson::String(g.0.url.clone())); + }, + ProcessedResources::Ref(r) => { + required_resources_json.push(polodb_core::bson::Bson::String(r.clone())); + } } + } - debug!("Generated resource: {:#?}", &full_resource_document); + resource_document.insert("required_resources", required_resources_json); - let insert_result = self.db.collection::("resources").insert_one(&full_resource_document).ok()?; + let json_resource = resource_package.0.resource.clone(); - let resource_id = insert_result.inserted_id.as_object_id()?; + if let None = resource_document.get("hash") { + let mut hasher = std::collections::hash_map::DefaultHasher::new(); - let resource_path = self.resolve_resource_path(resource_id.to_string().as_str()); + std::hash::Hasher::write(&mut hasher, resource_package.1.as_slice()); // Hash binary data - let mut file = std::fs::File::create(resource_path).ok()?; + std::hash::Hasher::write(&mut hasher, &to_vec(&json_resource).unwrap()); // Hash resource metadata, since changing the resources description must also change the hash. (For caching purposes) - file.write_all(resource_package.1.as_slice()).unwrap(); + resource_document.insert("hash", hasher.finish() as i64); } - return Some(()); + resource_document.insert("resource", json_resource); + + debug!("Generated resource: {:#?}", &resource_document); + + let insert_result = self.db.collection::("resources").insert_one(&resource_document).ok()?; + + let resource_id = insert_result.inserted_id.as_object_id()?; + + let resource_path = self.resolve_resource_path(resource_id.to_string().as_str()); + + let mut file = std::fs::File::create(resource_path).ok()?; + + file.write_all(resource_package.1.as_slice()).unwrap(); + + resource_document.insert("_id", resource_id); + + return Some(resource_document); } /// Tries to load a resource from cache.\ @@ -474,13 +521,13 @@ impl ResourceManager { /// ```ignore /// let (bytes, format) = ResourceManager::read_asset_from_source("textures/concrete").unwrap(); // Path relative to .../assets /// ``` - fn read_asset_from_source(&self, path: &str) -> Result<(Vec, String), Option> { - let resource_origin = if path.starts_with("http://") || path.starts_with("https://") { "network" } else { "local" }; + fn read_asset_from_source(&self, url: &str) -> Result<(Vec, String), Option> { + let resource_origin = if url.starts_with("http://") || url.starts_with("https://") { "network" } else { "local" }; let mut source_bytes; let format; match resource_origin { "network" => { - let request = if let Ok(request) = ureq::get(path).call() { request } else { return Err(None); }; + let request = if let Ok(request) = ureq::get(url).call() { request } else { return Err(None); }; let content_type = if let Some(e) = request.header("content-type") { e.to_string() } else { return Err(None); }; format = content_type; @@ -489,8 +536,16 @@ impl ResourceManager { request.into_reader().read_to_end(&mut source_bytes); }, "local" => { - let (mut file, extension) = if let Ok(dir) = std::fs::read_dir("assets") { - let files = dir.filter(|f| if let Ok(f) = f { f.path().file_stem().unwrap().eq(&OsString::from(path)) } else { false }); + let path = std::path::Path::new("assets/"); + + let url_as_path = std::path::Path::new(url); + + let url_as_path_parent = url_as_path.parent().unwrap(); + + let path = path.join(url_as_path_parent); + + let (mut file, extension) = if let Ok(dir) = std::fs::read_dir(path) { + let files = dir.filter(|f| if let Ok(f) = f { f.path().file_stem().unwrap().eq(url_as_path.file_name().unwrap()) } else { false }); let file_path = files.last().unwrap().unwrap().path(); (std::fs::File::open(&file_path).unwrap(), file_path.extension().unwrap().to_str().unwrap().to_string()) } else { return Err(None); }; @@ -517,11 +572,13 @@ impl ResourceManager { #[cfg(test)] mod tests { - use crate::resource_manager::{image_resource_handler::Texture, mesh_resource_handler::{Mesh, IntegralTypes, VertexSemantics, Size}}; + use crate::resource_manager::{texture_resource_handler::Texture, mesh_resource_handler::{Mesh, IntegralTypes, VertexSemantics, Size}}; /// Tests for the resource manager. /// It is important to test the load twice as the first time it will be loaded from source and the second time it will be loaded from cache. + // TODO: test resource load order + use super::*; #[test] diff --git a/src/resource_manager/image_resource_handler.rs b/src/resource_manager/texture_resource_handler.rs similarity index 80% rename from src/resource_manager/image_resource_handler.rs rename to src/resource_manager/texture_resource_handler.rs index de819c84..27a8580c 100644 --- a/src/resource_manager/image_resource_handler.rs +++ b/src/resource_manager/texture_resource_handler.rs @@ -2,7 +2,7 @@ use serde::{Serialize, Deserialize}; use crate::resource_manager::GenericResourceSerialization; -use super::{ResourceHandler, SerializedResourceDocument, Resource}; +use super::{ResourceHandler, SerializedResourceDocument, Resource, ProcessedResources}; pub(crate) struct ImageResourceHandler { @@ -23,7 +23,7 @@ impl ResourceHandler for ImageResourceHandler { } } - fn process(&self, _: &super::ResourceManager, asset_url: &str, bytes: &[u8]) -> Result)>, String> { + fn process(&self, _: &super::ResourceManager, asset_url: &str, bytes: &[u8]) -> Result, String> { let mut decoder = png::Decoder::new(bytes); decoder.set_transformations(png::Transformations::EXPAND); let mut reader = decoder.read_info().unwrap(); @@ -56,17 +56,12 @@ impl ResourceHandler for ImageResourceHandler { let settings = intel_tex_2::bc7::opaque_ultra_fast_settings(); - let resource_document = GenericResourceSerialization { - url: asset_url.to_string(), - required_resources: Vec::new(), - class: "Texture".to_string(), - resource: Texture{ - extent: crate::Extent{ width: extent.width, height: extent.height, depth: extent.depth }, - compression: Some(CompressionSchemes::BC7), - } - }; + let resource_document = GenericResourceSerialization::new(asset_url.to_string(), Texture{ + extent: crate::Extent{ width: extent.width, height: extent.height, depth: extent.depth }, + compression: Some(CompressionSchemes::BC7), + }); - Ok(vec![(resource_document.into(), intel_tex_2::bc7::compress_blocks(&settings, &rgba_surface))]) + Ok(vec![ProcessedResources::Generated((resource_document, intel_tex_2::bc7::compress_blocks(&settings, &rgba_surface)))]) } fn get_deserializers(&self) -> Vec<(&'static str, Box Box + Send>)> {