diff --git a/src/render_domain.rs b/src/render_domain.rs index cfad7507..a102191d 100644 --- a/src/render_domain.rs +++ b/src/render_domain.rs @@ -101,7 +101,7 @@ pub struct VisibilityWorldRenderDomain { pending_texture_loads: Vec, - top_level_acceleration_structure: render_system::AccelerationStructureHandle, + top_level_acceleration_structure: render_system::TopLevelAccelerationStructureHandle, } const VERTEX_COUNT: u32 = 64; @@ -954,8 +954,7 @@ void main() {{ let _instance_buffer = render_system.create_acceleration_structure_instance_buffer(Some("Scene Instance Buffer"), MAX_INSTANCES as u32); - let buffer = render_system.create_buffer(None, 65565, render_system::Uses::AccelerationStructure, render_system::DeviceAccesses::GpuWrite, render_system::UseCases::STATIC); - let top_level_acceleration_structure = render_system.create_acceleration_structure(Some("Top Level Acceleration Structure"), render_system::AccelerationStructureTypes::TopLevel{ instance_count: 16 }, render_system::BufferDescriptor { buffer: buffer, offset: 0, range: 4096, slot: 0 }); + let top_level_acceleration_structure = render_system.create_top_level_acceleration_structure(Some("Top Level Acceleration Structure")); let rt_pass_descriptor_set_layout = render_system.create_descriptor_set_layout(Some("RT Pass Set Layout"), &[ render_system::DescriptorSetLayoutBinding { diff --git a/src/rendering/render_system.rs b/src/rendering/render_system.rs index e8cbdd3f..fd79ea4e 100644 --- a/src/rendering/render_system.rs +++ b/src/rendering/render_system.rs @@ -61,7 +61,10 @@ pub struct BaseBufferHandle(pub(super) u64); pub struct BufferHandle(pub(super) u64, std::marker::PhantomData); #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] -pub struct AccelerationStructureHandle(pub(super) u64); +pub struct TopLevelAccelerationStructureHandle(pub(super) u64); + +#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] +pub struct BottomLevelAccelerationStructureHandle(pub(super) u64); #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub struct CommandBufferHandle(pub(super) u64); @@ -108,7 +111,7 @@ pub struct TextureCopyHandle(pub(crate) u64); #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum Handle { Buffer(BaseBufferHandle), - AccelerationStructure(AccelerationStructureHandle), + // AccelerationStructure(AccelerationStructureHandle), CommandBuffer(CommandBufferHandle), Shader(ShaderHandle), Pipeline(PipelineHandle), @@ -133,34 +136,39 @@ pub struct Consumption { pub layout: Layouts, } -pub enum AccelerationStructureBuildAA { +pub enum BottomLevelAccelerationStructureBuildDescriptions { Mesh { - vertex_buffer: BaseBufferHandle, - index_buffer: BaseBufferHandle, - transform_buffer: BaseBufferHandle, - vertex_format: Formats, - vertex_stride: u32, - index_format: DataTypes, - index_count: u32, - transform_count: u32, + vertex_buffer: BufferStridedRange, vertex_count: u32, + vertex_position_encoding: Encodings, + index_buffer: BufferStridedRange, + triangle_count: u32, + index_format: DataTypes, }, AABB { aabb_buffer: BaseBufferHandle, transform_buffer: BaseBufferHandle, transform_count: u32, }, +} + +pub enum TopLevelAccelerationStructureBuildDescriptions { Instance { - acceleration_structure: AccelerationStructureHandle, - transform_buffer: BaseBufferHandle, - transform_count: u32, + instances_buffer: BaseBufferHandle, + instance_count: u32, }, } -pub struct AccelerationStructureBuild { - pub acceleration_structure: AccelerationStructureHandle, - pub scratch_buffer: BaseBufferHandle, - pub acceleration_structure_build_type: AccelerationStructureBuildAA, +pub struct BottomLevelAccelerationStructureBuild { + pub acceleration_structure: BottomLevelAccelerationStructureHandle, + pub scratch_buffer: BufferDescriptor, + pub description: BottomLevelAccelerationStructureBuildDescriptions, +} + +pub struct TopLevelAccelerationStructureBuild { + pub acceleration_structure: TopLevelAccelerationStructureHandle, + pub scratch_buffer: BufferDescriptor, + pub description: TopLevelAccelerationStructureBuildDescriptions, } pub struct BufferStridedRange { @@ -174,7 +182,7 @@ pub struct BindingTables { pub raygen: BufferStridedRange, pub hit: BufferStridedRange, pub miss: BufferStridedRange, - pub callable: BufferStridedRange, + pub callable: Option, } pub struct DispatchExtent { @@ -182,11 +190,28 @@ pub struct DispatchExtent { pub dispatch_extent: Extent, } +pub enum BottomLevelAccelerationStructureDescriptions { + Mesh { + vertex_count: u32, + vertex_position_encoding: Encodings, + triangle_count: u32, + index_format: DataTypes, + }, + AABB { + transform_count: u32, + }, +} + +pub struct BottomLevelAccelerationStructure { + pub description: BottomLevelAccelerationStructureDescriptions, +} + pub trait CommandBufferRecording { /// Enables recording on the command buffer. fn begin(&self); - fn build_acceleration_structures(&mut self, acceleration_structure_builds: &[AccelerationStructureBuild]); + fn build_top_level_acceleration_structure(&mut self, acceleration_structure_build: &TopLevelAccelerationStructureBuild); + fn build_bottom_level_acceleration_structures(&mut self, acceleration_structure_builds: &[BottomLevelAccelerationStructureBuild]); /// Starts a render pass on the GPU. /// A render pass is a particular configuration of render targets which will be used simultaneously to render certain imagery. @@ -270,7 +295,7 @@ pub enum Descriptor { layout: Layouts, }, AccelerationStructure { - handle: AccelerationStructureHandle, + handle: TopLevelAccelerationStructureHandle, }, Swapchain(SwapchainHandle), Sampler(SamplerHandle), @@ -341,7 +366,11 @@ pub trait RenderSystem: orchestrator::System { fn create_sampler(&mut self) -> SamplerHandle; fn create_acceleration_structure_instance_buffer(&mut self, name: Option<&str>, max_instance_count: u32) -> BaseBufferHandle; - fn create_acceleration_structure(&mut self, name: Option<&str>, r#type: AccelerationStructureTypes, buffer_descriptor: BufferDescriptor,) -> AccelerationStructureHandle; + + fn create_top_level_acceleration_structure(&mut self, name: Option<&str>,) -> TopLevelAccelerationStructureHandle; + fn create_bottom_level_acceleration_structure(&mut self, description: &BottomLevelAccelerationStructure) -> BottomLevelAccelerationStructureHandle; + + fn write_instance(&mut self, instances_buffer_handle: BaseBufferHandle, transform: [[f32; 4]; 3], custom_index: u16, mask: u8, sbt_record_offset: usize, acceleration_structure: BottomLevelAccelerationStructureHandle); fn bind_to_window(&mut self, window_os_handles: &window_system::WindowOsHandles) -> SwapchainHandle; @@ -380,170 +409,626 @@ pub struct RGBAu8 { a: u8, } -#[cfg(test)] -pub(super) mod tests { - use super::*; - - fn check_triangle(pixels: &[RGBAu8], extent: Extent) { - assert_eq!(pixels.len(), (extent.width * extent.height) as usize); - - let pixel = pixels[0]; // top left - assert_eq!(pixel, RGBAu8 { r: 0, g: 0, b: 0, a: 255 }); - - if extent.width % 2 != 0 { - let pixel = pixels[(extent.width / 2) as usize]; // middle top center - assert_eq!(pixel, RGBAu8 { r: 255, g: 0, b: 0, a: 255 }); - } - - let pixel = pixels[(extent.width - 1) as usize]; // top right - assert_eq!(pixel, RGBAu8 { r: 0, g: 0, b: 0, a: 255 }); - - let pixel = pixels[(extent.width * (extent.height - 1)) as usize]; // bottom left - assert_eq!(pixel, RGBAu8 { r: 0, g: 0, b: 255, a: 255 }); - - let pixel = pixels[(extent.width * extent.height - (extent.width / 2)) as usize]; // middle bottom center - assert!(pixel == RGBAu8 { r: 0, g: 127, b: 127, a: 255 } || pixel == RGBAu8 { r: 0, g: 128, b: 127, a: 255 }); // FIX: workaround for CI, TODO: make near equal function - - let pixel = pixels[(extent.width * extent.height - 1) as usize]; // bottom right - assert_eq!(pixel, RGBAu8 { r: 0, g: 255, b: 0, a: 255 }); - } - - pub(crate) fn render_triangle(renderer: &mut dyn RenderSystem) { - let signal = renderer.create_synchronizer(false); - - let floats: [f32;21] = [ - 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, - 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 1.0, - -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0 - ]; +/// Enumerates the types of command buffers that can be created. +pub enum CommandBufferType { + /// A command buffer that can perform graphics operations. Draws, blits, presentations, etc. + GRAPHICS, + /// A command buffer that can perform compute operations. Dispatches, etc. + COMPUTE, + /// A command buffer that is optimized for transfer operations. Copies, etc. + TRANSFER +} - let vertex_layout = [ - VertexElement{ name: "POSITION".to_string(), format: DataTypes::Float3, binding: 0 }, - VertexElement{ name: "COLOR".to_string(), format: DataTypes::Float4, binding: 0 }, - ]; +/// Enumerates the types of buffers that can be created. +pub enum BufferType { + /// A buffer that can be used as a vertex buffer. + VERTEX, + /// A buffer that can be used as an index buffer. + INDEX, + /// A buffer that can be used as a uniform buffer. + UNIFORM, + /// A buffer that can be used as a storage buffer. + STORAGE, + /// A buffer that can be used as an indirect buffer. + INDIRECT +} - let mesh = unsafe { renderer.add_mesh_from_vertices_and_indices(3, 3, - std::slice::from_raw_parts(floats.as_ptr() as *const u8, (3*4 + 4*4) * 3), - std::slice::from_raw_parts([0u16, 1u16, 2u16].as_ptr() as *const u8, 3 * 2), - &vertex_layout - ) }; +/// Enumerates the types of shaders that can be created. +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] +pub enum ShaderTypes { + /// A vertex shader. + Vertex, + /// A fragment shader. + Fragment, + /// A compute shader. + Compute, + Task, + Mesh, + Raygen, + ClosestHit, + AnyHit, + Intersection, + Miss, + Callable, +} - let vertex_shader_code = " - #version 450 - #pragma shader_stage(vertex) +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum Encodings { + IEEE754, + UnsignedNormalized, + SignedNormalized, +} - layout(location = 0) in vec3 in_position; - layout(location = 1) in vec4 in_color; +#[derive(PartialEq, Eq, Clone, Copy)] +/// Enumerates the formats that textures can have. +pub enum Formats { + /// 8 bit unsigned per component normalized RGBA. + RGBAu8, + /// 16 bit unsigned per component normalized RGBA. + RGBAu16, + /// 32 bit unsigned per component normalized RGBA. + RGBAu32, + /// 16 bit float per component RGBA. + RGBAf16, + /// 32 bit float per component RGBA. + RGBAf32, + /// 10 bit unsigned for R, G and 11 bit unsigned for B normalized RGB. + RGBu10u10u11, + /// 8 bit unsigned per component normalized BGRA. + BGRAu8, + /// 32 bit float depth. + Depth32, + U32, +} - layout(location = 0) out vec4 out_color; +#[derive(Clone, Copy)] +pub enum CompressionSchemes { + BC7, +} - void main() { - out_color = in_color; - gl_Position = vec4(in_position, 1.0); - } - "; +#[derive(Clone, Copy)] +/// Stores the information of a memory region. +pub struct Memory<'a> { + /// The allocation that the memory region is associated with. + allocation: &'a AllocationHandle, + /// The offset of the memory region. + offset: usize, + /// The size of the memory region. + size: usize, +} - let fragment_shader_code = " - #version 450 - #pragma shader_stage(fragment) +#[derive(Clone, Copy)] +pub enum ClearValue { + None, + Color(crate::RGBA), + Integer(u32, u32, u32, u32), + Depth(f32), +} - layout(location = 0) in vec4 in_color; +#[derive(Clone, Copy)] +/// Stores the information of an attachment. +pub struct AttachmentInformation { + /// The image view of the attachment. + pub image: ImageHandle, + /// The format of the attachment. + pub format: Formats, + /// The layout of the attachment. + pub layout: Layouts, + /// The clear color of the attachment. + pub clear: ClearValue, + /// Whether to load the contents of the attchment when starting a render pass. + pub load: bool, + /// Whether to store the contents of the attachment when ending a render pass. + pub store: bool, +} - layout(location = 0) out vec4 out_color; +#[derive(Clone, Copy)] +/// Stores the information of a image copy. +pub struct ImageCopy { + /// The source image. + pub(super) source: ImageHandle, + pub(super) source_format: Formats, + /// The destination image. + pub(super) destination: ImageHandle, + pub(super) destination_format: Formats, + /// The images extent. + pub(super) extent: crate::Extent, +} - void main() { - out_color = in_color; - } - "; +#[derive(Clone, Copy)] +/// Stores the information of a buffer copy. +pub struct BufferCopy { + /// The source buffer. + pub(super) source: BaseBufferHandle, + /// The destination buffer. + pub(super) destination: BaseBufferHandle, + /// The size of the copy. + pub(super) size: usize, +} - let vertex_shader = renderer.create_shader(ShaderSourceType::GLSL, ShaderTypes::Vertex, vertex_shader_code.as_bytes()); - let fragment_shader = renderer.create_shader(ShaderSourceType::GLSL, ShaderTypes::Fragment, fragment_shader_code.as_bytes()); +use serde::{Serialize, Deserialize}; - let pipeline_layout = renderer.create_pipeline_layout(&[], &[]); +bitflags::bitflags! { + #[derive(Clone, Copy, PartialEq, Eq)] + /// Bit flags for the available access policies. + pub struct AccessPolicies : u8 { + /// Will perform read access. + const READ = 0b00000001; + /// Will perform write access. + const WRITE = 0b00000010; + } +} - // Use and odd width to make sure there is a middle/center pixel - let extent = crate::Extent { width: 1920, height: 1080, depth: 1 }; +#[derive(Clone, Copy)] +pub struct TextureState { + /// The layout of the resource. + pub layout: Layouts, +} - let render_target = renderer.create_image(None, extent, Formats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::STATIC); +#[derive(Clone, Copy)] +/// Stores the information of a barrier. +pub enum Barrier { + /// An image barrier. + Image(ImageHandle), + /// A buffer barrier. + Buffer(BaseBufferHandle), + /// A memory barrier. + Memory, +} - let attachments = [ - AttachmentInformation { - image: render_target, - layout: Layouts::RenderTarget, - format: Formats::RGBAu8, - clear: ClearValue::Color(crate::RGBA { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }), - load: false, - store: true, - } - ]; +bitflags::bitflags! { + #[derive(Clone, Copy, PartialEq, Eq)] + /// Bit flags for the available pipeline stages. + pub struct Stages : u64 { + /// No stage. + const NONE = 0b0; + /// The vertex stage. + const VERTEX = 0b1; + /// The mesh shader execution stage. + const MESH = 0b10; + /// The fragment stage. + const FRAGMENT = 0b100; + /// The compute stage. + const COMPUTE = 0b1000; + /// The transfer stage. + const TRANSFER = 0b10000; + /// The acceleration structure stage. + const ACCELERATION_STRUCTURE = 0b100000; + /// The presentation stage. + const PRESENTATION = 0b1000000; + /// The host stage. + const HOST = 0b10000000; + /// The shader write stage. + const SHADER_WRITE = 0b1000000000; + /// The indirect commands evaluation stage. + const INDIRECT = 0b10000000000; + } +} - let pipeline = renderer.create_raster_pipeline(&[ - PipelineConfigurationBlocks::Layout { layout: &pipeline_layout }, - PipelineConfigurationBlocks::Shaders { shaders: &[(&vertex_shader, ShaderTypes::Vertex, vec![]), (&fragment_shader, ShaderTypes::Fragment, vec![])], }, - PipelineConfigurationBlocks::VertexInput { vertex_elements: &vertex_layout, }, - PipelineConfigurationBlocks::RenderTargets { targets: &attachments }, - ]); +#[derive(Clone, Copy)] +/// Stores the information of a transition state. +pub struct TransitionState { + /// The stages this transition will either wait or block on. + pub stage: Stages, + /// The type of access that will be done on the resource by the process the operation that requires this transition. + pub access: AccessPolicies, + pub layout: Layouts, +} - let command_buffer_handle = renderer.create_command_buffer(); +/// Stores the information of a barrier descriptor. +#[derive(Clone, Copy)] +pub struct BarrierDescriptor { + /// The barrier. + pub barrier: Barrier, + /// The state of the resource previous to the barrier. If None, the resource state will be discarded. + pub source: Option, + /// The state of the resource after the barrier. + pub destination: TransitionState, +} - renderer.start_frame_capture(); +bitflags::bitflags! { + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] + /// Bit flags for the available resource uses. + pub struct Uses : u32 { + /// Resource will be used as a vertex buffer. + const Vertex = 1 << 0; + /// Resource will be used as an index buffer. + const Index = 1 << 1; + /// Resource will be used as a uniform buffer. + const Uniform = 1 << 2; + /// Resource will be used as a storage buffer. + const Storage = 1 << 3; + /// Resource will be used as an indirect buffer. + const Indirect = 1 << 4; + /// Resource will be used as an image. + const Image = 1 << 5; + /// Resource will be used as a render target. + const RenderTarget = 1 << 6; + /// Resource will be used as a depth stencil. + const DepthStencil = 1 << 7; + /// Resource will be used as an acceleration structure. + const AccelerationStructure = 1 << 8; + /// Resource will be used as a transfer source. + const TransferSource = 1 << 9; + /// Resource will be used as a transfer destination. + const TransferDestination = 1 << 10; + /// Resource will be used as a shader binding table. + const ShaderBindingTable = 1 << 11; + /// Resource will be used as a acceleration structure build scratch buffer. + const AccelerationStructureBuildScratch = 1 << 12; + } +} - let mut command_buffer_recording = renderer.create_command_buffer_recording(command_buffer_handle, None); +#[derive(Clone, Copy, PartialEq, Eq)] +/// Enumerates the available layouts. +pub enum Layouts { + /// The layout is undefined. We don't mind what the layout is. + Undefined, + /// The image will be used as render target. + RenderTarget, + /// The resource will be used in a transfer operation. + Transfer, + /// The resource will be used as a presentation source. + Present, + /// The resource will be used as a read only sample source. + Read, + /// The resource will be used as a read/write storage. + General, +} - let attachments = [ - AttachmentInformation { - image: render_target, - layout: Layouts::RenderTarget, - format: Formats::RGBAu8, - clear: ClearValue::Color(crate::RGBA { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }), - load: false, - store: true, - } - ]; +#[derive(Clone, Copy)] +/// Enumerates the available descriptor types. +pub enum DescriptorType { + /// A uniform buffer. + UniformBuffer, + /// A storage buffer. + StorageBuffer, + /// An image. + SampledImage, + /// A combined image sampler. + CombinedImageSampler, + /// A storage image. + StorageImage, + /// A sampler. + Sampler, +AccelerationStructure, +} - command_buffer_recording.start_render_pass(extent, &attachments); +/// Stores the information of a descriptor set layout binding. +pub struct DescriptorSetLayoutBinding { + pub name: &'static str, + /// The binding of the descriptor set layout binding. + pub binding: u32, + /// The descriptor type of the descriptor set layout binding. + pub descriptor_type: DescriptorType, + /// The number of descriptors in the descriptor set layout binding. + pub descriptor_count: u32, + /// The stages the descriptor set layout binding will be used in. + pub stages: Stages, + /// The immutable samplers of the descriptor set layout binding. + pub immutable_samplers: Option>, +} - command_buffer_recording.bind_raster_pipeline(&pipeline); +/// Stores the information of a descriptor. +pub enum DescriptorInfo { + /// A buffer descriptor. + Buffer { + /// The buffer of the descriptor. + buffer: BaseBufferHandle, + /// The offset to start reading from inside the buffer. + offset: usize, + /// How much to read from the buffer after `offset`. + range: usize, + }, + /// An image descriptor. + Image { + /// The image of the descriptor. + image: ImageHandle, + /// The format of the texture. + format: Formats, + /// The layout of the texture. + layout: Layouts, + }, + /// A sampler descriptor. + Sampler { + /// The sampler of the descriptor. + sampler: u32, + } +} - command_buffer_recording.draw_mesh(&mesh); +/// Stores the information of a descriptor set write. +pub struct DescriptorWrite { + /// The descriptor set to write to. + pub descriptor_set: DescriptorSetHandle, + /// The binding to write to. + pub binding: u32, + /// The index of the array element to write to in the binding(if the binding is an array). + pub array_element: u32, + /// Information describing the descriptor. + pub descriptor: Descriptor, +} - command_buffer_recording.end_render_pass(); +/// Describes the details of the memory layout of a particular image. +pub struct ImageSubresourceLayout { + /// The offset inside a memory region where the texture will read it's first texel from. + pub(super) offset: u64, + /// The size of the texture in bytes. + pub(super) size: u64, + /// The row pitch of the texture. + pub(super) row_pitch: u64, + /// The array pitch of the texture. + pub(super) array_pitch: u64, + /// The depth pitch of the texture. + pub(super) depth_pitch: u64, +} - let texure_copy_handles = command_buffer_recording.sync_textures(&[render_target]); +/// Describes the properties of a particular surface. +pub struct SurfaceProperties { + /// The current extent of the surface. + pub(super) extent: crate::Extent, +} - command_buffer_recording.execute(&[], &[], signal); +#[derive(Clone, Copy, PartialEq, Eq)] +/// Enumerates the states of a swapchain's validity for presentation. +pub enum SwapchainStates { + /// The swapchain is valid for presentation. + Ok, + /// The swapchain is suboptimal for presentation. + Suboptimal, + /// The swapchain can't be used for presentation. + Invalid, +} - renderer.end_frame_capture(); +pub struct BufferDescriptor { + pub buffer: BaseBufferHandle, + pub offset: u64, + pub range: u64, + pub slot: u32, +} - renderer.wait(signal); // Wait for the render to finish before accessing the image data +pub trait SpecializationMapEntry { + fn get_constant_id(&self) -> u32; + fn get_size(&self) -> usize; + fn get_data(&self) -> &[u8]; + fn get_type(&self) -> String; +} - assert!(!renderer.has_errors()); +pub struct GenericSpecializationMapEntry { + pub r#type: String, + pub constant_id: u32, + pub value: T, +} - // Get image data and cast u8 slice to rgbau8 - let pixels = unsafe { std::slice::from_raw_parts(renderer.get_image_data(texure_copy_handles[0]).as_ptr() as *const RGBAu8, (extent.width * extent.height) as usize) }; +impl SpecializationMapEntry for GenericSpecializationMapEntry { + fn get_constant_id(&self) -> u32 { + self.constant_id + } - check_triangle(pixels, extent); + fn get_type(&self) -> String { + self.r#type.clone() + } - // let mut file = std::fs::File::create("test.png").unwrap(); + fn get_size(&self) -> usize { + std::mem::size_of::() + } - // let mut encoder = png::Encoder::new(&mut file, extent.width, extent.height); + fn get_data(&self) -> &[u8] { + unsafe { std::slice::from_raw_parts(&self.value as *const T as *const u8, std::mem::size_of::()) } + } +} - // encoder.set_color(png::ColorType::Rgba); - // encoder.set_depth(png::BitDepth::Eight); +pub type ShaderParameter<'a> = (&'a ShaderHandle, ShaderTypes, Vec>); - // let mut writer = encoder.write_header().unwrap(); - // writer.write_image_data(unsafe { std::slice::from_raw_parts(pixels.as_ptr() as *const u8, pixels.len() * 4) }).unwrap(); +pub enum PipelineConfigurationBlocks<'a> { + VertexInput { + vertex_elements: &'a [VertexElement] + }, + InputAssembly { + + }, + RenderTargets { + targets: &'a [AttachmentInformation], + }, + Shaders { + shaders: &'a [(&'a ShaderHandle, ShaderTypes, Vec>)], + }, + Layout { + layout: &'a PipelineLayoutHandle, } +} - pub(crate) fn present(renderer: &mut dyn RenderSystem) { - let mut window_system = window_system::WindowSystem::new(); +pub struct PushConstantRange { + pub offset: u32, + pub size: u32, +} - // Use and odd width to make sure there is a middle/center pixel - let extent = crate::Extent { width: 1920, height: 1080, depth: 1 }; +pub enum AccelerationStructureTypes { + TopLevel { + instance_count: u32, + }, + BottomLevel { + vertex_count: u32, + triangle_count: u32, + vertex_position_format: DataTypes, + index_format: DataTypes, + }, +} - let window_handle = window_system.create_window("Renderer Test", extent, "test"); +pub struct RenderSystemImplementation { + pointer: Box, +} - let swapchain = renderer.bind_to_window(&window_system.get_os_handles_2(&window_handle)); +impl RenderSystemImplementation { + pub fn new(pointer: Box) -> Self { + Self { + pointer: pointer, + } + } +} + +impl orchestrator::Entity for RenderSystemImplementation {} +impl orchestrator::System for RenderSystemImplementation {} + +impl RenderSystem for RenderSystemImplementation { + fn has_errors(&self) -> bool { + self.pointer.has_errors() + } + + fn add_mesh_from_vertices_and_indices(&mut self, vertex_count: u32, index_count: u32, vertices: &[u8], indices: &[u8], vertex_layout: &[VertexElement]) -> MeshHandle { + self.pointer.add_mesh_from_vertices_and_indices(vertex_count, index_count, vertices, indices, vertex_layout) + } + + fn create_shader(&mut self, shader_source_type: ShaderSourceType, stage: ShaderTypes, shader: &[u8]) -> ShaderHandle { + self.pointer.create_shader(shader_source_type, stage, shader) + } + + fn get_buffer_address(&self, buffer_handle: BaseBufferHandle) -> u64 { + self.pointer.get_buffer_address(buffer_handle) + } + + fn write(&self, descriptor_set_writes: &[DescriptorWrite]) { + self.pointer.write(descriptor_set_writes) + } + + fn get_buffer_slice(&mut self, buffer_handle: BaseBufferHandle) -> &[u8] { + self.pointer.get_buffer_slice(buffer_handle) + } + + fn get_mut_buffer_slice(&self, buffer_handle: BaseBufferHandle) -> &mut [u8] { + self.pointer.get_mut_buffer_slice(buffer_handle) + } + + fn get_texture_slice_mut(&self, texture_handle: ImageHandle) -> &mut [u8] { + self.pointer.get_texture_slice_mut(texture_handle) + } + + fn get_image_data(&self, texture_copy_handle: TextureCopyHandle) -> &[u8] { + self.pointer.get_image_data(texture_copy_handle) + } + + fn bind_to_window(&mut self, window_os_handles: &window_system::WindowOsHandles) -> SwapchainHandle { + self.pointer.bind_to_window(window_os_handles) + } + + fn present(&self, image_index: u32, swapchains: &[SwapchainHandle], synchronizer_handle: SynchronizerHandle) { + self.pointer.present(image_index, swapchains, synchronizer_handle) + } + + fn wait(&self, synchronizer_handle: SynchronizerHandle) { + self.pointer.wait(synchronizer_handle) + } + + fn start_frame_capture(&self) { + self.pointer.start_frame_capture() + } + + fn end_frame_capture(&self) { + self.pointer.end_frame_capture() + } + + fn acquire_swapchain_image(&self, swapchain_handle: SwapchainHandle, synchronizer_handle: SynchronizerHandle) -> u32 { + self.pointer.acquire_swapchain_image(swapchain_handle, synchronizer_handle) + } + + fn create_buffer(&mut self, name: Option<&str>, size: usize, uses: Uses, accesses: DeviceAccesses, use_case: UseCases) -> BaseBufferHandle { + self.pointer.create_buffer(name, size, uses, accesses, use_case) + } + + fn create_allocation(&mut self, size: usize, _resource_uses: Uses, resource_device_accesses: DeviceAccesses) -> AllocationHandle { + self.pointer.create_allocation(size, _resource_uses, resource_device_accesses) + } + + fn create_command_buffer(&mut self) -> CommandBufferHandle { + self.pointer.create_command_buffer() + } + + fn create_command_buffer_recording<'a>(&'a self, command_buffer_handle: CommandBufferHandle, frame: Option) -> Box { + self.pointer.create_command_buffer_recording(command_buffer_handle, frame) + } + + fn create_descriptor_set(&mut self, name: Option<&str>, descriptor_set_layout: &DescriptorSetLayoutHandle, bindings: &[DescriptorSetLayoutBinding]) -> DescriptorSetHandle { + self.pointer.create_descriptor_set(name, descriptor_set_layout, bindings) + } + + fn create_descriptor_set_layout(&mut self, name: Option<&str>, bindings: &[DescriptorSetLayoutBinding]) -> DescriptorSetLayoutHandle { + self.pointer.create_descriptor_set_layout(name, bindings) + } + + fn create_raster_pipeline(&mut self, pipeline_blocks: &[PipelineConfigurationBlocks]) -> PipelineHandle { + self.pointer.create_raster_pipeline(pipeline_blocks) + } + + fn create_compute_pipeline(&mut self, pipeline_layout_handle: &PipelineLayoutHandle, shader_parameter: ShaderParameter) -> PipelineHandle { + self.pointer.create_compute_pipeline(pipeline_layout_handle, shader_parameter) + } + + fn create_ray_tracing_pipeline(&mut self, pipeline_layout_handle: &PipelineLayoutHandle, shaders: &[ShaderParameter]) -> PipelineHandle { + self.pointer.create_ray_tracing_pipeline(pipeline_layout_handle, shaders) + } + + fn create_pipeline_layout(&mut self, descriptor_set_layout_handles: &[DescriptorSetLayoutHandle], push_constant_ranges: &[PushConstantRange]) -> PipelineLayoutHandle { + self.pointer.create_pipeline_layout(descriptor_set_layout_handles, push_constant_ranges) + } + + fn create_sampler(&mut self) -> SamplerHandle { + self.pointer.create_sampler() + } + + fn create_acceleration_structure_instance_buffer(&mut self, name: Option<&str>, max_instance_count: u32) -> BaseBufferHandle { + self.pointer.create_acceleration_structure_instance_buffer(name, max_instance_count) + } + + fn create_bottom_level_acceleration_structure(&mut self, description: &BottomLevelAccelerationStructure,) -> BottomLevelAccelerationStructureHandle { + self.pointer.create_bottom_level_acceleration_structure(description,) + } + + fn create_top_level_acceleration_structure(&mut self, name: Option<&str>,) -> TopLevelAccelerationStructureHandle { + self.pointer.create_top_level_acceleration_structure(name,) + } + + fn write_instance(&mut self, instances_buffer_handle: BaseBufferHandle, transform: [[f32; 4]; 3], custom_index: u16, mask: u8, sbt_record_offset: usize, acceleration_structure: BottomLevelAccelerationStructureHandle) { + self.pointer.write_instance(instances_buffer_handle, transform, custom_index, mask, sbt_record_offset, acceleration_structure) + } + + fn create_synchronizer(&mut self, signaled: bool) -> SynchronizerHandle { + self.pointer.create_synchronizer(signaled) + } + + fn create_image(&mut self, name: Option<&str>, extent: crate::Extent, format: Formats, compression: Option, resource_uses: Uses, device_accesses: DeviceAccesses, use_case: UseCases) -> ImageHandle { + self.pointer.create_image(name, extent, format, compression, resource_uses, device_accesses, use_case) + } +} + +#[cfg(test)] +pub(super) mod tests { + use super::*; + + fn check_triangle(pixels: &[RGBAu8], extent: Extent) { + assert_eq!(pixels.len(), (extent.width * extent.height) as usize); + + let pixel = pixels[0]; // top left + assert_eq!(pixel, RGBAu8 { r: 0, g: 0, b: 0, a: 255 }); + + if extent.width % 2 != 0 { + let pixel = pixels[(extent.width / 2) as usize]; // middle top center + assert_eq!(pixel, RGBAu8 { r: 255, g: 0, b: 0, a: 255 }); + } + + let pixel = pixels[(extent.width - 1) as usize]; // top right + assert_eq!(pixel, RGBAu8 { r: 0, g: 0, b: 0, a: 255 }); + + let pixel = pixels[(extent.width * (extent.height - 1)) as usize]; // bottom left + assert_eq!(pixel, RGBAu8 { r: 0, g: 0, b: 255, a: 255 }); + + let pixel = pixels[(extent.width * extent.height - (extent.width / 2)) as usize]; // middle bottom center + assert!(pixel == RGBAu8 { r: 0, g: 127, b: 127, a: 255 } || pixel == RGBAu8 { r: 0, g: 128, b: 127, a: 255 }); // FIX: workaround for CI, TODO: make near equal function + + let pixel = pixels[(extent.width * extent.height - 1) as usize]; // bottom right + assert_eq!(pixel, RGBAu8 { r: 0, g: 255, b: 0, a: 255 }); + } + + pub(crate) fn render_triangle(renderer: &mut dyn RenderSystem) { + let signal = renderer.create_synchronizer(false); let floats: [f32;21] = [ 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, @@ -595,14 +1080,17 @@ pub(super) mod tests { let pipeline_layout = renderer.create_pipeline_layout(&[], &[]); - let render_target = renderer.create_image(None, extent, Formats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::GpuWrite, UseCases::STATIC); + // 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_image(None, extent, Formats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::STATIC); let attachments = [ AttachmentInformation { image: render_target, layout: Layouts::RenderTarget, format: Formats::RGBAu8, - clear: ClearValue::None, + clear: ClearValue::Color(crate::RGBA { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }), load: false, store: true, } @@ -617,11 +1105,6 @@ pub(super) mod tests { let command_buffer_handle = renderer.create_command_buffer(); - let render_finished_synchronizer = renderer.create_synchronizer(false); - let image_ready = renderer.create_synchronizer(false); - - let image_index = renderer.acquire_swapchain_image(swapchain, image_ready); - renderer.start_frame_capture(); let mut command_buffer_recording = renderer.create_command_buffer_recording(command_buffer_handle, None); @@ -645,22 +1128,33 @@ pub(super) mod tests { command_buffer_recording.end_render_pass(); - command_buffer_recording.copy_to_swapchain(render_target, image_index, swapchain); - - command_buffer_recording.execute(&[image_ready], &[render_finished_synchronizer], render_finished_synchronizer); + let texure_copy_handles = command_buffer_recording.sync_textures(&[render_target]); - renderer.present(image_index, &[swapchain], render_finished_synchronizer); + command_buffer_recording.execute(&[], &[], signal); renderer.end_frame_capture(); - renderer.wait(render_finished_synchronizer); + renderer.wait(signal); // Wait for the render to finish before accessing the image data - // TODO: assert rendering results + assert!(!renderer.has_errors()); - assert!(!renderer.has_errors()) + // Get image data and cast u8 slice to rgbau8 + let pixels = unsafe { std::slice::from_raw_parts(renderer.get_image_data(texure_copy_handles[0]).as_ptr() as *const RGBAu8, (extent.width * extent.height) as usize) }; + + check_triangle(pixels, extent); + + // let mut file = std::fs::File::create("test.png").unwrap(); + + // let mut encoder = png::Encoder::new(&mut file, extent.width, extent.height); + + // encoder.set_color(png::ColorType::Rgba); + // encoder.set_depth(png::BitDepth::Eight); + + // let mut writer = encoder.write_header().unwrap(); + // writer.write_image_data(unsafe { std::slice::from_raw_parts(pixels.as_ptr() as *const u8, pixels.len() * 4) }).unwrap(); } - pub(crate) fn multiframe_present(renderer: &mut dyn RenderSystem) { + pub(crate) fn present(renderer: &mut dyn RenderSystem) { let mut window_system = window_system::WindowSystem::new(); // Use and odd width to make sure there is a middle/center pixel @@ -720,7 +1214,7 @@ pub(super) mod tests { let pipeline_layout = renderer.create_pipeline_layout(&[], &[]); - let render_target = renderer.create_image(None, extent, Formats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::GpuWrite | DeviceAccesses::CpuRead, UseCases::DYNAMIC); + let render_target = renderer.create_image(None, extent, Formats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::GpuWrite, UseCases::STATIC); let attachments = [ AttachmentInformation { @@ -742,13 +1236,138 @@ pub(super) mod tests { let command_buffer_handle = renderer.create_command_buffer(); - let render_finished_synchronizer = renderer.create_synchronizer(true); - let image_ready = renderer.create_synchronizer(true); + let render_finished_synchronizer = renderer.create_synchronizer(false); + let image_ready = renderer.create_synchronizer(false); - for i in 0..2*64 { - renderer.wait(render_finished_synchronizer); + let image_index = renderer.acquire_swapchain_image(swapchain, image_ready); - let image_index = renderer.acquire_swapchain_image(swapchain, image_ready); + renderer.start_frame_capture(); + + let mut command_buffer_recording = renderer.create_command_buffer_recording(command_buffer_handle, None); + + let attachments = [ + AttachmentInformation { + image: render_target, + layout: Layouts::RenderTarget, + format: Formats::RGBAu8, + clear: ClearValue::Color(crate::RGBA { r: 0.0, g: 0.0, b: 0.0, a: 1.0 }), + load: false, + store: true, + } + ]; + + command_buffer_recording.start_render_pass(extent, &attachments); + + command_buffer_recording.bind_raster_pipeline(&pipeline); + + command_buffer_recording.draw_mesh(&mesh); + + command_buffer_recording.end_render_pass(); + + command_buffer_recording.copy_to_swapchain(render_target, image_index, swapchain); + + command_buffer_recording.execute(&[image_ready], &[render_finished_synchronizer], render_finished_synchronizer); + + renderer.present(image_index, &[swapchain], render_finished_synchronizer); + + renderer.end_frame_capture(); + + renderer.wait(render_finished_synchronizer); + + // TODO: assert rendering results + + assert!(!renderer.has_errors()) + } + + pub(crate) fn multiframe_present(renderer: &mut dyn RenderSystem) { + let mut window_system = window_system::WindowSystem::new(); + + // Use and odd width to make sure there is a middle/center pixel + let extent = crate::Extent { width: 1920, height: 1080, depth: 1 }; + + let window_handle = window_system.create_window("Renderer Test", extent, "test"); + + let swapchain = renderer.bind_to_window(&window_system.get_os_handles_2(&window_handle)); + + let floats: [f32;21] = [ + 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, + 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 1.0, + -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0 + ]; + + let vertex_layout = [ + VertexElement{ name: "POSITION".to_string(), format: DataTypes::Float3, binding: 0 }, + VertexElement{ name: "COLOR".to_string(), format: DataTypes::Float4, binding: 0 }, + ]; + + let mesh = unsafe { renderer.add_mesh_from_vertices_and_indices(3, 3, + std::slice::from_raw_parts(floats.as_ptr() as *const u8, (3*4 + 4*4) * 3), + std::slice::from_raw_parts([0u16, 1u16, 2u16].as_ptr() as *const u8, 3 * 2), + &vertex_layout + ) }; + + let vertex_shader_code = " + #version 450 + #pragma shader_stage(vertex) + + layout(location = 0) in vec3 in_position; + layout(location = 1) in vec4 in_color; + + layout(location = 0) out vec4 out_color; + + void main() { + out_color = in_color; + gl_Position = vec4(in_position, 1.0); + } + "; + + let fragment_shader_code = " + #version 450 + #pragma shader_stage(fragment) + + layout(location = 0) in vec4 in_color; + + layout(location = 0) out vec4 out_color; + + void main() { + out_color = in_color; + } + "; + + let vertex_shader = renderer.create_shader(ShaderSourceType::GLSL, ShaderTypes::Vertex, vertex_shader_code.as_bytes()); + let fragment_shader = renderer.create_shader(ShaderSourceType::GLSL, ShaderTypes::Fragment, fragment_shader_code.as_bytes()); + + let pipeline_layout = renderer.create_pipeline_layout(&[], &[]); + + let render_target = renderer.create_image(None, extent, Formats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::GpuWrite | DeviceAccesses::CpuRead, UseCases::DYNAMIC); + + let attachments = [ + AttachmentInformation { + image: render_target, + layout: Layouts::RenderTarget, + format: Formats::RGBAu8, + clear: ClearValue::None, + load: false, + store: true, + } + ]; + + let pipeline = renderer.create_raster_pipeline(&[ + PipelineConfigurationBlocks::Layout { layout: &pipeline_layout }, + PipelineConfigurationBlocks::Shaders { shaders: &[(&vertex_shader, ShaderTypes::Vertex, vec![]), (&fragment_shader, ShaderTypes::Fragment, vec![])], }, + PipelineConfigurationBlocks::VertexInput { vertex_elements: &vertex_layout, }, + PipelineConfigurationBlocks::RenderTargets { targets: &attachments }, + ]); + + let command_buffer_handle = renderer.create_command_buffer(); + + let render_finished_synchronizer = renderer.create_synchronizer(true); + let image_ready = renderer.create_synchronizer(true); + + for i in 0..2*64 { + renderer.wait(render_finished_synchronizer); + + let image_index = renderer.acquire_swapchain_image(swapchain, image_ready); renderer.start_frame_capture(); @@ -1267,583 +1886,237 @@ pub(super) mod tests { renderer.end_frame_capture(); renderer.wait(signal); // Wait for the render to finish before accessing the texture data - - // assert colored triangle was drawn to texture - let _pixels = renderer.get_image_data(texure_copy_handles[0]); - - // TODO: assert rendering results - - assert!(!renderer.has_errors()); - } -} - -/// Enumerates the types of command buffers that can be created. -pub enum CommandBufferType { - /// A command buffer that can perform graphics operations. Draws, blits, presentations, etc. - GRAPHICS, - /// A command buffer that can perform compute operations. Dispatches, etc. - COMPUTE, - /// A command buffer that is optimized for transfer operations. Copies, etc. - TRANSFER -} - -/// Enumerates the types of buffers that can be created. -pub enum BufferType { - /// A buffer that can be used as a vertex buffer. - VERTEX, - /// A buffer that can be used as an index buffer. - INDEX, - /// A buffer that can be used as a uniform buffer. - UNIFORM, - /// A buffer that can be used as a storage buffer. - STORAGE, - /// A buffer that can be used as an indirect buffer. - INDIRECT -} - -/// Enumerates the types of shaders that can be created. -#[derive(Clone, Copy, Serialize, Deserialize, Debug)] -pub enum ShaderTypes { - /// A vertex shader. - Vertex, - /// A fragment shader. - Fragment, - /// A compute shader. - Compute, - Task, - Mesh, - Raygen, - ClosestHit, - AnyHit, - Intersection, - Miss, - Callable, -} - -#[derive(PartialEq, Eq, Clone, Copy)] -/// Enumerates the formats that textures can have. -pub enum Formats { - /// 8 bit unsigned per component normalized RGBA. - RGBAu8, - /// 16 bit unsigned per component normalized RGBA. - RGBAu16, - /// 32 bit unsigned per component normalized RGBA. - RGBAu32, - /// 16 bit float per component RGBA. - RGBAf16, - /// 32 bit float per component RGBA. - RGBAf32, - /// 10 bit unsigned for R, G and 11 bit unsigned for B normalized RGB. - RGBu10u10u11, - /// 8 bit unsigned per component normalized BGRA. - BGRAu8, - /// 32 bit float depth. - Depth32, -U32, -} - -#[derive(Clone, Copy)] -pub enum CompressionSchemes { - BC7, -} - -#[derive(Clone, Copy)] -/// Stores the information of a memory region. -pub struct Memory<'a> { - /// The allocation that the memory region is associated with. - allocation: &'a AllocationHandle, - /// The offset of the memory region. - offset: usize, - /// The size of the memory region. - size: usize, -} - -#[derive(Clone, Copy)] -pub enum ClearValue { - None, - Color(crate::RGBA), - Integer(u32, u32, u32, u32), - Depth(f32), -} - -#[derive(Clone, Copy)] -/// Stores the information of an attachment. -pub struct AttachmentInformation { - /// The image view of the attachment. - pub image: ImageHandle, - /// The format of the attachment. - pub format: Formats, - /// The layout of the attachment. - pub layout: Layouts, - /// The clear color of the attachment. - pub clear: ClearValue, - /// Whether to load the contents of the attchment when starting a render pass. - pub load: bool, - /// Whether to store the contents of the attachment when ending a render pass. - pub store: bool, -} - -#[derive(Clone, Copy)] -/// Stores the information of a image copy. -pub struct ImageCopy { - /// The source image. - pub(super) source: ImageHandle, - pub(super) source_format: Formats, - /// The destination image. - pub(super) destination: ImageHandle, - pub(super) destination_format: Formats, - /// The images extent. - pub(super) extent: crate::Extent, -} - -#[derive(Clone, Copy)] -/// Stores the information of a buffer copy. -pub struct BufferCopy { - /// The source buffer. - pub(super) source: BaseBufferHandle, - /// The destination buffer. - pub(super) destination: BaseBufferHandle, - /// The size of the copy. - pub(super) size: usize, -} - -use serde::{Serialize, Deserialize}; - -bitflags::bitflags! { - #[derive(Clone, Copy, PartialEq, Eq)] - /// Bit flags for the available access policies. - pub struct AccessPolicies : u8 { - /// Will perform read access. - const READ = 0b00000001; - /// Will perform write access. - const WRITE = 0b00000010; - } -} - -#[derive(Clone, Copy)] -pub struct TextureState { - /// The layout of the resource. - pub layout: Layouts, -} - -#[derive(Clone, Copy)] -/// Stores the information of a barrier. -pub enum Barrier { - /// An image barrier. - Image(ImageHandle), - /// A buffer barrier. - Buffer(BaseBufferHandle), - /// A memory barrier. - Memory, -} - -bitflags::bitflags! { - #[derive(Clone, Copy, PartialEq, Eq)] - /// Bit flags for the available pipeline stages. - pub struct Stages : u64 { - /// No stage. - const NONE = 0b0; - /// The vertex stage. - const VERTEX = 0b1; - /// The mesh shader execution stage. - const MESH = 0b10; - /// The fragment stage. - const FRAGMENT = 0b100; - /// The compute stage. - const COMPUTE = 0b1000; - /// The transfer stage. - const TRANSFER = 0b10000; - /// The acceleration structure stage. - const ACCELERATION_STRUCTURE = 0b100000; - /// The presentation stage. - const PRESENTATION = 0b1000000; - /// The host stage. - const HOST = 0b10000000; - /// The shader write stage. - const SHADER_WRITE = 0b1000000000; - /// The indirect commands evaluation stage. - const INDIRECT = 0b10000000000; - } -} - -#[derive(Clone, Copy)] -/// Stores the information of a transition state. -pub struct TransitionState { - /// The stages this transition will either wait or block on. - pub stage: Stages, - /// The type of access that will be done on the resource by the process the operation that requires this transition. - pub access: AccessPolicies, - pub layout: Layouts, -} - -/// Stores the information of a barrier descriptor. -#[derive(Clone, Copy)] -pub struct BarrierDescriptor { - /// The barrier. - pub barrier: Barrier, - /// The state of the resource previous to the barrier. If None, the resource state will be discarded. - pub source: Option, - /// The state of the resource after the barrier. - pub destination: TransitionState, -} - -bitflags::bitflags! { - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - /// Bit flags for the available resource uses. - pub struct Uses : u32 { - /// Resource will be used as a vertex buffer. - const Vertex = 1 << 0; - /// Resource will be used as an index buffer. - const Index = 1 << 1; - /// Resource will be used as a uniform buffer. - const Uniform = 1 << 2; - /// Resource will be used as a storage buffer. - const Storage = 1 << 3; - /// Resource will be used as an indirect buffer. - const Indirect = 1 << 4; - /// Resource will be used as an image. - const Image = 1 << 5; - /// Resource will be used as a render target. - const RenderTarget = 1 << 6; - /// Resource will be used as a depth stencil. - const DepthStencil = 1 << 7; - /// Resource will be used as an acceleration structure. - const AccelerationStructure = 1 << 8; - /// Resource will be used as a transfer source. - const TransferSource = 1 << 9; - /// Resource will be used as a transfer destination. - const TransferDestination = 1 << 10; - } -} - -#[derive(Clone, Copy, PartialEq, Eq)] -/// Enumerates the available layouts. -pub enum Layouts { - /// The layout is undefined. We don't mind what the layout is. - Undefined, - /// The image will be used as render target. - RenderTarget, - /// The resource will be used in a transfer operation. - Transfer, - /// The resource will be used as a presentation source. - Present, - /// The resource will be used as a read only sample source. - Read, - /// The resource will be used as a read/write storage. - General, -} - -#[derive(Clone, Copy)] -/// Enumerates the available descriptor types. -pub enum DescriptorType { - /// A uniform buffer. - UniformBuffer, - /// A storage buffer. - StorageBuffer, - /// An image. - SampledImage, - /// A combined image sampler. - CombinedImageSampler, - /// A storage image. - StorageImage, - /// A sampler. - Sampler, -AccelerationStructure, -} - -/// Stores the information of a descriptor set layout binding. -pub struct DescriptorSetLayoutBinding { - pub name: &'static str, - /// The binding of the descriptor set layout binding. - pub binding: u32, - /// The descriptor type of the descriptor set layout binding. - pub descriptor_type: DescriptorType, - /// The number of descriptors in the descriptor set layout binding. - pub descriptor_count: u32, - /// The stages the descriptor set layout binding will be used in. - pub stages: Stages, - /// The immutable samplers of the descriptor set layout binding. - pub immutable_samplers: Option>, -} - -/// Stores the information of a descriptor. -pub enum DescriptorInfo { - /// A buffer descriptor. - Buffer { - /// The buffer of the descriptor. - buffer: BaseBufferHandle, - /// The offset to start reading from inside the buffer. - offset: usize, - /// How much to read from the buffer after `offset`. - range: usize, - }, - /// An image descriptor. - Image { - /// The image of the descriptor. - image: ImageHandle, - /// The format of the texture. - format: Formats, - /// The layout of the texture. - layout: Layouts, - }, - /// A sampler descriptor. - Sampler { - /// The sampler of the descriptor. - sampler: u32, + + // assert colored triangle was drawn to texture + let _pixels = renderer.get_image_data(texure_copy_handles[0]); + + // TODO: assert rendering results + + assert!(!renderer.has_errors()); } -} -/// Stores the information of a descriptor set write. -pub struct DescriptorWrite { - /// The descriptor set to write to. - pub descriptor_set: DescriptorSetHandle, - /// The binding to write to. - pub binding: u32, - /// The index of the array element to write to in the binding(if the binding is an array). - pub array_element: u32, - /// Information describing the descriptor. - pub descriptor: Descriptor, -} + pub(crate) fn ray_tracing(renderer: &mut dyn RenderSystem) { + //! Tests that the render system can perform rendering with multiple frames in flight. + //! Having multiple frames in flight means allocating and managing multiple resources under a single handle, one for each frame. -/// Describes the details of the memory layout of a particular image. -pub struct ImageSubresourceLayout { - /// The offset inside a memory region where the texture will read it's first texel from. - pub(super) offset: u64, - /// The size of the texture in bytes. - pub(super) size: u64, - /// The row pitch of the texture. - pub(super) row_pitch: u64, - /// The array pitch of the texture. - pub(super) array_pitch: u64, - /// The depth pitch of the texture. - pub(super) depth_pitch: u64, -} + const FRAMES_IN_FLIGHT: usize = 2; -/// Describes the properties of a particular surface. -pub struct SurfaceProperties { - /// The current extent of the surface. - pub(super) extent: crate::Extent, -} + // Use and odd width to make sure there is a middle/center pixel + let _extent = crate::Extent { width: 1920, height: 1080, depth: 1 }; -#[derive(Clone, Copy, PartialEq, Eq)] -/// Enumerates the states of a swapchain's validity for presentation. -pub enum SwapchainStates { - /// The swapchain is valid for presentation. - Ok, - /// The swapchain is suboptimal for presentation. - Suboptimal, - /// The swapchain can't be used for presentation. - Invalid, -} + let positions: [f32; 3 * 3] = [ + 0.0, 1.0, 0.0, + 1.0, -1.0, 0.0, + -1.0, -1.0, 0.0, + ]; -pub struct BufferDescriptor { - pub buffer: BaseBufferHandle, - pub offset: u64, - pub range: u64, - pub slot: u32, -} + let colors: [f32; 4 * 3] = [ + 1.0, 0.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + ]; -pub trait SpecializationMapEntry { - fn get_constant_id(&self) -> u32; - fn get_size(&self) -> usize; - fn get_data(&self) -> &[u8]; - fn get_type(&self) -> String; -} + let vertex_layout = [ + VertexElement{ name: "POSITION".to_string(), format: DataTypes::Float3, binding: 0 }, + VertexElement{ name: "COLOR".to_string(), format: DataTypes::Float4, binding: 0 }, + ]; -pub struct GenericSpecializationMapEntry { - pub r#type: String, - pub constant_id: u32, - pub value: T, -} + let vertex_positions_buffer = renderer.create_buffer(None, positions.len() * 4, Uses::Vertex, DeviceAccesses::CpuWrite | DeviceAccesses::GpuRead, UseCases::STATIC); + let vertex_colors_buffer = renderer.create_buffer(None, positions.len() * 4, Uses::Vertex, DeviceAccesses::CpuWrite | DeviceAccesses::GpuRead, UseCases::STATIC); + let index_buffer = renderer.create_buffer(None, positions.len() * 2, Uses::Index, DeviceAccesses::CpuWrite | DeviceAccesses::GpuRead, UseCases::STATIC); -impl SpecializationMapEntry for GenericSpecializationMapEntry { - fn get_constant_id(&self) -> u32 { - self.constant_id - } + renderer.get_mut_buffer_slice(vertex_positions_buffer).copy_from_slice(unsafe { std::slice::from_raw_parts(positions.as_ptr() as *const u8, positions.len() * 4) }); + renderer.get_mut_buffer_slice(vertex_colors_buffer).copy_from_slice(unsafe { std::slice::from_raw_parts(colors.as_ptr() as *const u8, colors.len() * 4) }); + renderer.get_mut_buffer_slice(index_buffer).copy_from_slice(unsafe { std::slice::from_raw_parts([0u16, 1u16, 2u16].as_ptr() as *const u8, 3 * 2) }); - fn get_type(&self) -> String { - self.r#type.clone() - } + let raygen_shader_code = " +#version 450 +#pragma shader_stage(ray_gen) - fn get_size(&self) -> usize { - std::mem::size_of::() - } +#extension GL_EXT_scalar_block_layout: enable +#extension GL_EXT_buffer_reference: enable +#extension GL_EXT_buffer_reference2: enable +#extension GL_EXT_shader_16bit_storage: require +#extension GL_EXT_ray_tracing: require - fn get_data(&self) -> &[u8] { - unsafe { std::slice::from_raw_parts(&self.value as *const T as *const u8, std::mem::size_of::()) } - } -} +layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS; +layout(binding = 1, set = 0, rgba8) uniform image2D image; -pub type ShaderParameter<'a> = (&'a ShaderHandle, ShaderTypes, Vec>); +layout(location = 0) rayPayloadEXT vec3 hitValue; -pub enum PipelineConfigurationBlocks<'a> { - VertexInput { - vertex_elements: &'a [VertexElement] - }, - InputAssembly { - - }, - RenderTargets { - targets: &'a [AttachmentInformation], - }, - Shaders { - shaders: &'a [(&'a ShaderHandle, ShaderTypes, Vec>)], - }, - Layout { - layout: &'a PipelineLayoutHandle, - } -} +void main() { + const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5); + const vec2 inUV = pixelCenter/vec2(gl_LaunchSizeEXT.xy); + vec2 d = inUV * 2.0 - 1.0; -pub struct PushConstantRange { - pub offset: u32, - pub size: u32, -} + uint rayFlags = gl_RayFlagsOpaqueEXT; + uint cullMask = 0xff; + float tmin = 0.001; + float tmax = 10000.0; -pub enum AccelerationStructureTypes { - TopLevel { - instance_count: u32, - }, - BottomLevel { - vertex_count: u32, - triangle_count: u32, - vertex_position_format: DataTypes, - index_format: DataTypes, - }, -} + traceRayEXT(topLevelAS, rayFlags, cullMask, 0, 0, 0, origin.xyz, tmin, direction.xyz, tmax, 0); -pub struct RenderSystemImplementation { - pointer: Box, + imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 0.0)); } + "; -impl RenderSystemImplementation { - pub fn new(pointer: Box) -> Self { - Self { - pointer: pointer, - } - } -} + let closest_hit_shader_code = " +#version 450 +#pragma shader_stage(closest_hit) -impl orchestrator::Entity for RenderSystemImplementation {} -impl orchestrator::System for RenderSystemImplementation {} +#extension GL_EXT_scalar_block_layout: enable +#extension GL_EXT_buffer_reference: enable +#extension GL_EXT_buffer_reference2: enable +#extension GL_EXT_shader_16bit_storage: require +#extension GL_EXT_ray_tracing: require -impl RenderSystem for RenderSystemImplementation { - fn has_errors(&self) -> bool { - self.pointer.has_errors() - } +layout(location = 0) rayPayloadInEXT vec3 hitValue; +hitAttributeEXT vec2 attribs; - fn add_mesh_from_vertices_and_indices(&mut self, vertex_count: u32, index_count: u32, vertices: &[u8], indices: &[u8], vertex_layout: &[VertexElement]) -> MeshHandle { - self.pointer.add_mesh_from_vertices_and_indices(vertex_count, index_count, vertices, indices, vertex_layout) - } +layout(binding = 3, set = 0) buffer Vertices { vec4 v[]; } vertices; +layout(binding = 4, set = 0) buffer Indices { uint16_t i[]; } indices; - fn create_shader(&mut self, shader_source_type: ShaderSourceType, stage: ShaderTypes, shader: &[u8]) -> ShaderHandle { - self.pointer.create_shader(shader_source_type, stage, shader) - } +void main() { + const vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y); + ivec3 index = ivec3(indices.i[3 * gl_PrimitiveID], indices.i[3 * gl_PrimitiveID + 1], indices.i[3 * gl_PrimitiveID + 2]); +} + "; - fn get_buffer_address(&self, buffer_handle: BaseBufferHandle) -> u64 { - self.pointer.get_buffer_address(buffer_handle) - } + let miss_shader_code = " +#version 450 +#pragma shader_stage(miss) - fn write(&self, descriptor_set_writes: &[DescriptorWrite]) { - self.pointer.write(descriptor_set_writes) - } +#extension GL_EXT_scalar_block_layout: enable +#extension GL_EXT_buffer_reference: enable +#extension GL_EXT_buffer_reference2: enable +#extension GL_EXT_shader_16bit_storage: require +#extension GL_EXT_ray_tracing: require - fn get_buffer_slice(&mut self, buffer_handle: BaseBufferHandle) -> &[u8] { - self.pointer.get_buffer_slice(buffer_handle) - } +layout(location = 0) rayPayloadInEXT vec3 hitValue; - fn get_mut_buffer_slice(&self, buffer_handle: BaseBufferHandle) -> &mut [u8] { - self.pointer.get_mut_buffer_slice(buffer_handle) - } +void main() { + hitValue = vec3(0.0, 0.0, 0.2); +} + "; - fn get_texture_slice_mut(&self, texture_handle: ImageHandle) -> &mut [u8] { - self.pointer.get_texture_slice_mut(texture_handle) - } + let raygen_shader = renderer.create_shader(ShaderSourceType::GLSL, ShaderTypes::Raygen, raygen_shader_code.as_bytes()); + let closest_hit_shader = renderer.create_shader(ShaderSourceType::GLSL, ShaderTypes::ClosestHit, closest_hit_shader_code.as_bytes()); + let miss_shader = renderer.create_shader(ShaderSourceType::GLSL, ShaderTypes::Miss, miss_shader_code.as_bytes()); + + let top_level_acceleration_structure = renderer.create_top_level_acceleration_structure(Some("Top Level")); + let bottom_level_acceleration_structure = renderer.create_bottom_level_acceleration_structure(&BottomLevelAccelerationStructure{ + description: BottomLevelAccelerationStructureDescriptions::Mesh { + vertex_count: 3, + vertex_position_encoding: Encodings::IEEE754, + triangle_count: 1, + index_format: DataTypes::U16, + } + }); - fn get_image_data(&self, texture_copy_handle: TextureCopyHandle) -> &[u8] { - self.pointer.get_image_data(texture_copy_handle) - } + let bindings = [ + DescriptorSetLayoutBinding { + name: "acceleration structure", + descriptor_count: 1, + descriptor_type: DescriptorType::AccelerationStructure, + binding: 0, + stages: Stages::ACCELERATION_STRUCTURE, + immutable_samplers: None, + }, + ]; - fn bind_to_window(&mut self, window_os_handles: &window_system::WindowOsHandles) -> SwapchainHandle { - self.pointer.bind_to_window(window_os_handles) - } + let descriptor_set_layout_handle = renderer.create_descriptor_set_layout(None, &bindings); - fn present(&self, image_index: u32, swapchains: &[SwapchainHandle], synchronizer_handle: SynchronizerHandle) { - self.pointer.present(image_index, swapchains, synchronizer_handle) - } + let descriptor_set = renderer.create_descriptor_set(None, &descriptor_set_layout_handle, &bindings); - fn wait(&self, synchronizer_handle: SynchronizerHandle) { - self.pointer.wait(synchronizer_handle) - } + renderer.write(&[ + DescriptorWrite { descriptor_set: descriptor_set, binding: 0, array_element: 0, descriptor: Descriptor::AccelerationStructure{ handle: top_level_acceleration_structure } }, + ]); - fn start_frame_capture(&self) { - self.pointer.start_frame_capture() - } + // Use and odd width to make sure there is a middle/center pixel + let extent = crate::Extent { width: 1920, height: 1080, depth: 1 }; - fn end_frame_capture(&self) { - self.pointer.end_frame_capture() - } + let render_target = renderer.create_image(None, extent, Formats::RGBAu8, None, Uses::RenderTarget, DeviceAccesses::CpuRead | DeviceAccesses::GpuWrite, UseCases::DYNAMIC); - fn acquire_swapchain_image(&self, swapchain_handle: SwapchainHandle, synchronizer_handle: SynchronizerHandle) -> u32 { - self.pointer.acquire_swapchain_image(swapchain_handle, synchronizer_handle) - } + let pipeline_layout = renderer.create_pipeline_layout(&[descriptor_set_layout_handle], &[]); - fn create_buffer(&mut self, name: Option<&str>, size: usize, uses: Uses, accesses: DeviceAccesses, use_case: UseCases) -> BaseBufferHandle { - self.pointer.create_buffer(name, size, uses, accesses, use_case) - } + let pipeline = renderer.create_ray_tracing_pipeline( + &pipeline_layout, + &[(&raygen_shader, ShaderTypes::Raygen, vec![]), (&closest_hit_shader, ShaderTypes::ClosestHit, vec![]), (&miss_shader, ShaderTypes::Miss, vec![])], + ); - fn create_allocation(&mut self, size: usize, _resource_uses: Uses, resource_device_accesses: DeviceAccesses) -> AllocationHandle { - self.pointer.create_allocation(size, _resource_uses, resource_device_accesses) - } + let command_buffer_handle = renderer.create_command_buffer(); - fn create_command_buffer(&mut self) -> CommandBufferHandle { - self.pointer.create_command_buffer() - } + let render_finished_synchronizer = renderer.create_synchronizer(false); - fn create_command_buffer_recording<'a>(&'a self, command_buffer_handle: CommandBufferHandle, frame: Option) -> Box { - self.pointer.create_command_buffer_recording(command_buffer_handle, frame) - } + let instances_buffer = renderer.create_acceleration_structure_instance_buffer(None, 1); + + renderer.write_instance(instances_buffer, [[1f32, 0f32, 0f32, 0f32], [0f32, 1f32, 0f32, 0f32], [0f32, 0f32, 1f32, 0f32]], 0, 0xFF, 0, bottom_level_acceleration_structure); + + let build_sync = renderer.create_synchronizer(false); + + let scratch_buffer = renderer.create_buffer(None, 1024 * 1024, Uses::AccelerationStructureBuildScratch, DeviceAccesses::GpuWrite, UseCases::DYNAMIC); + + let raygen_sbt_buffer = renderer.create_buffer(None, 64, Uses::ShaderBindingTable, DeviceAccesses::CpuWrite | DeviceAccesses::GpuRead, UseCases::STATIC); + let miss_sbt_buffer = renderer.create_buffer(None, 64, Uses::ShaderBindingTable, DeviceAccesses::CpuWrite | DeviceAccesses::GpuRead, UseCases::STATIC); + let hit_sbt_buffer = renderer.create_buffer(None, 64, Uses::ShaderBindingTable, DeviceAccesses::CpuWrite | DeviceAccesses::GpuRead, UseCases::STATIC); + + for i in 0..FRAMES_IN_FLIGHT * 10 { + { + let mut command_buffer_recording = renderer.create_command_buffer_recording(command_buffer_handle, Some(i as u32)); + + command_buffer_recording.build_bottom_level_acceleration_structures(&[BottomLevelAccelerationStructureBuild { + acceleration_structure: bottom_level_acceleration_structure, + description: BottomLevelAccelerationStructureBuildDescriptions::Mesh { + vertex_buffer: BufferStridedRange { buffer: vertex_positions_buffer, offset: 0, stride: 12, size: 12 * 3 }, + vertex_count: 3, + index_buffer: BufferStridedRange { buffer: index_buffer, offset: 0, stride: 2, size: 2 * 3 }, + vertex_position_encoding: Encodings::IEEE754, + index_format: DataTypes::U16, + triangle_count: 1, + }, + scratch_buffer: BufferDescriptor { buffer: scratch_buffer, offset: 0, range: 1024 * 512, slot: 0 }, + }]); + + command_buffer_recording.build_top_level_acceleration_structure(&TopLevelAccelerationStructureBuild { + acceleration_structure: top_level_acceleration_structure, + description: TopLevelAccelerationStructureBuildDescriptions::Instance { + instances_buffer, + instance_count: 1, + }, + scratch_buffer: BufferDescriptor { buffer: scratch_buffer, offset: 1024 * 512, range: 1024 * 512, slot: 0 }, + }); + + command_buffer_recording.execute(&[], &[build_sync], render_finished_synchronizer); + } - fn create_descriptor_set(&mut self, name: Option<&str>, descriptor_set_layout: &DescriptorSetLayoutHandle, bindings: &[DescriptorSetLayoutBinding]) -> DescriptorSetHandle { - self.pointer.create_descriptor_set(name, descriptor_set_layout, bindings) - } + // renderer.wait(render_finished_synchronizer); - fn create_descriptor_set_layout(&mut self, name: Option<&str>, bindings: &[DescriptorSetLayoutBinding]) -> DescriptorSetLayoutHandle { - self.pointer.create_descriptor_set_layout(name, bindings) - } + renderer.start_frame_capture(); - fn create_raster_pipeline(&mut self, pipeline_blocks: &[PipelineConfigurationBlocks]) -> PipelineHandle { - self.pointer.create_raster_pipeline(pipeline_blocks) - } + let mut command_buffer_recording = renderer.create_command_buffer_recording(command_buffer_handle, Some(i as u32)); - fn create_compute_pipeline(&mut self, pipeline_layout_handle: &PipelineLayoutHandle, shader_parameter: ShaderParameter) -> PipelineHandle { - self.pointer.create_compute_pipeline(pipeline_layout_handle, shader_parameter) - } + command_buffer_recording.bind_ray_tracing_pipeline(&pipeline); - fn create_ray_tracing_pipeline(&mut self, pipeline_layout_handle: &PipelineLayoutHandle, shaders: &[ShaderParameter]) -> PipelineHandle { - self.pointer.create_ray_tracing_pipeline(pipeline_layout_handle, shaders) - } + command_buffer_recording.trace_rays(BindingTables { + raygen: BufferStridedRange { buffer: raygen_sbt_buffer, offset: 0, stride: 64, size: 64 }, + hit: BufferStridedRange { buffer: hit_sbt_buffer, offset: 0, stride: 64, size: 64 }, + miss: BufferStridedRange { buffer: miss_sbt_buffer, offset: 0, stride: 64, size: 64 }, + callable: None, + }, 1920, 1080, 1); - fn create_pipeline_layout(&mut self, descriptor_set_layout_handles: &[DescriptorSetLayoutHandle], push_constant_ranges: &[PushConstantRange]) -> PipelineLayoutHandle { - self.pointer.create_pipeline_layout(descriptor_set_layout_handles, push_constant_ranges) - } + let texure_copy_handles = command_buffer_recording.sync_textures(&[render_target]); - fn create_sampler(&mut self) -> SamplerHandle { - self.pointer.create_sampler() - } + command_buffer_recording.execute(&[build_sync], &[], render_finished_synchronizer); - fn create_acceleration_structure_instance_buffer(&mut self, name: Option<&str>, max_instance_count: u32) -> BaseBufferHandle { - self.pointer.create_acceleration_structure_instance_buffer(name, max_instance_count) - } + renderer.end_frame_capture(); - fn create_acceleration_structure(&mut self, name: Option<&str>, r#type: AccelerationStructureTypes, buffer_descriptor: BufferDescriptor,) -> AccelerationStructureHandle { - self.pointer.create_acceleration_structure(name,r#type,buffer_descriptor) - } + renderer.wait(render_finished_synchronizer); - fn create_synchronizer(&mut self, signaled: bool) -> SynchronizerHandle { - self.pointer.create_synchronizer(signaled) - } + assert!(!renderer.has_errors()); - fn create_image(&mut self, name: Option<&str>, extent: crate::Extent, format: Formats, compression: Option, resource_uses: Uses, device_accesses: DeviceAccesses, use_case: UseCases) -> ImageHandle { - self.pointer.create_image(name, extent, format, compression, resource_uses, device_accesses, use_case) + let pixels = unsafe { std::slice::from_raw_parts(renderer.get_image_data(texure_copy_handles[0]).as_ptr() as *const RGBAu8, (extent.width * extent.height) as usize) }; + + check_triangle(pixels, extent); + } } } \ No newline at end of file diff --git a/src/rendering/vulkan_render_system.rs b/src/rendering/vulkan_render_system.rs index a211f7cd..89c3064d 100644 --- a/src/rendering/vulkan_render_system.rs +++ b/src/rendering/vulkan_render_system.rs @@ -62,6 +62,8 @@ fn uses_to_vk_usage_flags(usage: render_system::Uses) -> vk::BufferUsageFlags { flags |= if usage.contains(render_system::Uses::TransferDestination) { vk::BufferUsageFlags::TRANSFER_DST } else { vk::BufferUsageFlags::empty() }; flags |= if usage.contains(render_system::Uses::AccelerationStructure) { vk::BufferUsageFlags::ACCELERATION_STRUCTURE_STORAGE_KHR } else { vk::BufferUsageFlags::empty() }; flags |= if usage.contains(render_system::Uses::Indirect) { vk::BufferUsageFlags::INDIRECT_BUFFER } else { vk::BufferUsageFlags::empty() }; + flags |= if usage.contains(render_system::Uses::ShaderBindingTable) { vk::BufferUsageFlags::SHADER_BINDING_TABLE_KHR } else { vk::BufferUsageFlags::empty() }; + flags |= if usage.contains(render_system::Uses::AccelerationStructureBuildScratch) { vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR } else { vk::BufferUsageFlags::empty() }; flags } @@ -829,66 +831,120 @@ impl render_system::RenderSystem for VulkanRenderSystem { buffer_handle } - fn create_acceleration_structure(&mut self, name: Option<&str>, r#type: render_system::AccelerationStructureTypes, buffer_descriptor: render_system::BufferDescriptor,) -> render_system::AccelerationStructureHandle { - let (acceleration_structure_type, geometry, instances) = match r#type { - render_system::AccelerationStructureTypes::TopLevel { instance_count } => { - ( - vk::AccelerationStructureTypeKHR::TOP_LEVEL, - vk::AccelerationStructureGeometryKHR::default() - .geometry_type(vk::GeometryTypeKHR::INSTANCES) - .geometry(vk::AccelerationStructureGeometryDataKHR { - instances: vk::AccelerationStructureGeometryInstancesDataKHR::default() - }), - instance_count - ) + fn create_top_level_acceleration_structure(&mut self, name: Option<&str>,) -> render_system::TopLevelAccelerationStructureHandle { + let geometry = vk::AccelerationStructureGeometryKHR::default() + .geometry_type(vk::GeometryTypeKHR::INSTANCES) + .geometry(vk::AccelerationStructureGeometryDataKHR { instances: vk::AccelerationStructureGeometryInstancesDataKHR::default() }); + + let geometries = [geometry]; + + let build_info = vk::AccelerationStructureBuildGeometryInfoKHR::default() + .ty(vk::AccelerationStructureTypeKHR::TOP_LEVEL) + .geometries(&geometries); + + let mut size_info = vk::AccelerationStructureBuildSizesInfoKHR::default(); + + unsafe { + self.acceleration_structure.get_acceleration_structure_build_sizes(vk::AccelerationStructureBuildTypeKHR::DEVICE, &build_info, &[0], &mut size_info); + } + + let acceleration_structure_size = size_info.acceleration_structure_size as usize; + let scratch_size = size_info.build_scratch_size as usize; + + let buffer = self.create_vulkan_buffer(None, acceleration_structure_size, vk::BufferUsageFlags::ACCELERATION_STRUCTURE_STORAGE_KHR | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); + + let create_info = vk::AccelerationStructureCreateInfoKHR::default() + .buffer(buffer.resource) + .size(acceleration_structure_size as u64) + .offset(0) + .ty(vk::AccelerationStructureTypeKHR::TOP_LEVEL); + + let handle = render_system::TopLevelAccelerationStructureHandle(self.acceleration_structures.len() as u64); + + { + let handle = unsafe { + self.acceleration_structure.create_acceleration_structure(&create_info, None).expect("No acceleration structure") + }; + + self.acceleration_structures.push(AccelerationStructure { + acceleration_structure: handle, + buffer: buffer.resource, + scratch_size, + }); + + if let Some(name) = name { + if let Some(debug_utils) = &self.debug_utils { + unsafe { + debug_utils.set_debug_utils_object_name( + self.device.handle(), + &vk::DebugUtilsObjectNameInfoEXT::default() + .object_handle(handle) + .object_name(std::ffi::CString::new(name).unwrap().as_c_str()) + /* .build() */ + ).expect("No debug utils object name"); + } + } } - render_system::AccelerationStructureTypes::BottomLevel { vertex_position_format, triangle_count, vertex_count, index_format } => { - ( - vk::AccelerationStructureTypeKHR::BOTTOM_LEVEL, - vk::AccelerationStructureGeometryKHR::default() + } + + handle + } + + fn create_bottom_level_acceleration_structure(&mut self, description: &render_system::BottomLevelAccelerationStructure,) -> render_system::BottomLevelAccelerationStructureHandle { + let (geometry, primitive_count) = match &description.description { + render_system::BottomLevelAccelerationStructureDescriptions::Mesh { vertex_count, vertex_position_encoding, triangle_count, index_format } => { + (vk::AccelerationStructureGeometryKHR::default() .geometry_type(vk::GeometryTypeKHR::TRIANGLES) .geometry(vk::AccelerationStructureGeometryDataKHR { triangles: vk::AccelerationStructureGeometryTrianglesDataKHR::default() - .vertex_format(match vertex_position_format { - render_system::DataTypes::Float3 => vk::Format::R32G32B32_SFLOAT, + .vertex_format(match vertex_position_encoding { + render_system::Encodings::IEEE754 => vk::Format::R32G32B32_SFLOAT, _ => panic!("Invalid vertex position format"), }) - .vertex_stride(vertex_position_format.size() as u64) - .max_vertex(vertex_count) + .max_vertex(*vertex_count - 1) .index_type(match index_format { - render_system::DataTypes::U8 => vk::IndexType::UINT16, + render_system::DataTypes::U8 => vk::IndexType::UINT8_EXT, render_system::DataTypes::U16 => vk::IndexType::UINT16, render_system::DataTypes::U32 => vk::IndexType::UINT32, _ => panic!("Invalid index format"), }) }), - 0, - ) + *triangle_count) + } + render_system::BottomLevelAccelerationStructureDescriptions::AABB { transform_count } => { + (vk::AccelerationStructureGeometryKHR::default() + .geometry_type(vk::GeometryTypeKHR::AABBS) + .geometry(vk::AccelerationStructureGeometryDataKHR { + aabbs: vk::AccelerationStructureGeometryAabbsDataKHR::default() + }), + *transform_count) } }; let geometries = [geometry]; let build_info = vk::AccelerationStructureBuildGeometryInfoKHR::default() - .ty(acceleration_structure_type) + .ty(vk::AccelerationStructureTypeKHR::BOTTOM_LEVEL) .geometries(&geometries); let mut size_info = vk::AccelerationStructureBuildSizesInfoKHR::default(); unsafe { - self.acceleration_structure.get_acceleration_structure_build_sizes(vk::AccelerationStructureBuildTypeKHR::DEVICE, &build_info, &[instances], &mut size_info); + self.acceleration_structure.get_acceleration_structure_build_sizes(vk::AccelerationStructureBuildTypeKHR::DEVICE, &build_info, &[primitive_count], &mut size_info); } - let acceleration_structure_size = size_info.acceleration_structure_size; - let scratch_size = size_info.build_scratch_size; + let acceleration_structure_size = size_info.acceleration_structure_size as usize; + let scratch_size = size_info.build_scratch_size as usize; + + let buffer_descriptor = self.create_vulkan_buffer(None, acceleration_structure_size, vk::BufferUsageFlags::ACCELERATION_STRUCTURE_STORAGE_KHR | vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS); let create_info = vk::AccelerationStructureCreateInfoKHR::default() - .buffer(self.buffers[buffer_descriptor.buffer.0 as usize].buffer) - .size(acceleration_structure_size) - .offset(buffer_descriptor.offset) - .ty(acceleration_structure_type); + .buffer(buffer_descriptor.resource) + .size(acceleration_structure_size as u64) + .offset(0) + .ty(vk::AccelerationStructureTypeKHR::BOTTOM_LEVEL); - let handle = render_system::AccelerationStructureHandle(self.acceleration_structures.len() as u64); + let handle = render_system::BottomLevelAccelerationStructureHandle(self.acceleration_structures.len() as u64); { let handle = unsafe { @@ -897,26 +953,47 @@ impl render_system::RenderSystem for VulkanRenderSystem { self.acceleration_structures.push(AccelerationStructure { acceleration_structure: handle, + buffer: buffer_descriptor.resource, + scratch_size, }); - if let Some(name) = name { - if let Some(debug_utils) = &self.debug_utils { - unsafe { - debug_utils.set_debug_utils_object_name( - self.device.handle(), - &vk::DebugUtilsObjectNameInfoEXT::default() - .object_handle(handle) - .object_name(std::ffi::CString::new(name).unwrap().as_c_str()) - /* .build() */ - ).expect("No debug utils object name"); - } - } - } + // if let Some(name) = None { + // if let Some(debug_utils) = &self.debug_utils { + // unsafe { + // debug_utils.set_debug_utils_object_name( + // self.device.handle(), + // &vk::DebugUtilsObjectNameInfoEXT::default() + // .object_handle(handle) + // .object_name(std::ffi::CString::new(name).unwrap().as_c_str()) + // /* .build() */ + // ).expect("No debug utils object name"); + // } + // } + // } } handle } + fn write_instance(&mut self, instances_buffer: BaseBufferHandle, transform: [[f32; 4]; 3], custom_index: u16, mask: u8, sbt_record_offset: usize, acceleration_structure: render_system::BottomLevelAccelerationStructureHandle) { + let instance = vk::AccelerationStructureInstanceKHR{ + transform: vk::TransformMatrixKHR { + matrix: [transform[0][0], transform[0][1], transform[0][2], transform[0][3], transform[1][0], transform[1][1], transform[1][2], transform[1][3], transform[2][0], transform[2][1], transform[2][2], transform[2][3]], + }, + instance_custom_index_and_mask: vk::Packed24_8::new(custom_index as u32, mask), + instance_shader_binding_table_record_offset_and_flags: vk::Packed24_8::new(sbt_record_offset as u32, 0), + acceleration_structure_reference: vk::AccelerationStructureReferenceKHR { + device_handle: self.acceleration_structures[acceleration_structure.0 as usize].acceleration_structure.as_raw() as u64, + }, + }; + + let instance_buffer = &mut self.buffers[instances_buffer.0 as usize]; + + let instance_buffer_slice = unsafe { std::slice::from_raw_parts_mut(instance_buffer.pointer as *mut vk::AccelerationStructureInstanceKHR, instance_buffer.size / std::mem::size_of::()) }; + + instance_buffer_slice[0] = instance; + } + fn bind_to_window(&mut self, window_os_handles: &window_system::WindowOsHandles) -> render_system::SwapchainHandle { let surface = self.create_vulkan_surface(window_os_handles); @@ -1051,7 +1128,7 @@ impl render_system::RenderSystem for VulkanRenderSystem { use ash::{vk::{ValidationFeatureEnableEXT, Handle}, Entry}; -use super::render_system::{CommandBufferRecording, Formats, DispatchExtent}; +use super::render_system::{CommandBufferRecording, Formats, DispatchExtent, BaseBufferHandle, RenderSystem}; #[derive(Clone)] pub(crate) struct Swapchain { @@ -2035,6 +2112,11 @@ impl VulkanRenderSystem { } } + fn destroy_vulkan_buffer(&self, buffer: &render_system::BaseBufferHandle) { + let buffer = self.buffers.get(buffer.0 as usize).expect("No buffer with that handle.").buffer.clone(); + unsafe { self.device.destroy_buffer(buffer, None) }; + } + fn create_vulkan_allocation(&self, size: usize,) -> vk::DeviceMemory { let memory_allocate_info = vk::MemoryAllocateInfo::default() .allocation_size(size as u64) @@ -2470,7 +2552,11 @@ impl VulkanCommandBufferRecording<'_> { } } - fn get_acceleration_structure(&self, acceleration_structure_handle: render_system::AccelerationStructureHandle) -> (render_system::AccelerationStructureHandle, &AccelerationStructure) { + fn get_top_level_acceleration_structure(&self, acceleration_structure_handle: render_system::TopLevelAccelerationStructureHandle) -> (render_system::TopLevelAccelerationStructureHandle, &AccelerationStructure) { + (acceleration_structure_handle, &self.render_system.acceleration_structures[acceleration_structure_handle.0 as usize]) + } + + fn get_bottom_level_acceleration_structure(&self, acceleration_structure_handle: render_system::BottomLevelAccelerationStructureHandle) -> (render_system::BottomLevelAccelerationStructureHandle, &AccelerationStructure) { (acceleration_structure_handle, &self.render_system.acceleration_structures[acceleration_structure_handle.0 as usize]) } @@ -2580,21 +2666,82 @@ impl render_system::CommandBufferRecording for VulkanCommandBufferRecording<'_> self.in_render_pass = false; } - fn build_acceleration_structures(&mut self, acceleration_structure_builds: &[render_system::AccelerationStructureBuild]) { - fn visit(this: &mut VulkanCommandBufferRecording, acceleration_structure_builds: &[render_system::AccelerationStructureBuild], mut infos: Vec, mut geometries: Vec>, mut build_range_infos: Vec>) { + fn build_top_level_acceleration_structure(&mut self, acceleration_structure_build: &render_system::TopLevelAccelerationStructureBuild) { + let (_, acceleration_structure) = self.get_top_level_acceleration_structure(acceleration_structure_build.acceleration_structure); + + let (as_geometries, offsets) = match acceleration_structure_build.description { + render_system::TopLevelAccelerationStructureBuildDescriptions::Instance { instances_buffer, instance_count } => { + (vec![ + vk::AccelerationStructureGeometryKHR::default() + .geometry_type(vk::GeometryTypeKHR::INSTANCES) + .geometry(vk::AccelerationStructureGeometryDataKHR{ instances: vk::AccelerationStructureGeometryInstancesDataKHR::default() + .array_of_pointers(false) + .data(vk::DeviceOrHostAddressConstKHR { + device_address: self.render_system.get_buffer_address(instances_buffer), + }) + /* .build() */ + }) + .flags(vk::GeometryFlagsKHR::OPAQUE) + /* .build() */ + ], vec![ + vk::AccelerationStructureBuildRangeInfoKHR::default() + .primitive_count(instance_count) + .primitive_offset(0) + .first_vertex(0) + .transform_offset(0) + /* .build() */ + ]) + } + }; + + let scratch_buffer_address = unsafe { + let (_, buffer) = self.get_buffer(acceleration_structure_build.scratch_buffer.buffer); + self.render_system.device.get_buffer_device_address( + &vk::BufferDeviceAddressInfo::default() + .buffer(buffer.buffer) + /* .build() */ + ) + acceleration_structure_build.scratch_buffer.offset as u64 + }; + + let build_geometry_info = vk::AccelerationStructureBuildGeometryInfoKHR::default() + .flags(vk::BuildAccelerationStructureFlagsKHR::PREFER_FAST_TRACE) + .mode(vk::BuildAccelerationStructureModeKHR::BUILD) + .ty(vk::AccelerationStructureTypeKHR::TOP_LEVEL) + .dst_acceleration_structure(acceleration_structure.acceleration_structure) + .scratch_data(vk::DeviceOrHostAddressKHR { + device_address: scratch_buffer_address, + }) + /* .build() */; + + let infos = vec![build_geometry_info]; + let build_range_infos = vec![offsets]; + let geometries = vec![as_geometries]; + + let command_buffer = self.get_command_buffer(); + + for (info, geos) in infos.iter().zip(geometries) { + info.geometries(&geos); + } + + let build_range_infos = build_range_infos.iter().map(|build_range_info| build_range_info.as_slice()).collect::>(); + + unsafe { + self.render_system.acceleration_structure.cmd_build_acceleration_structures(command_buffer.command_buffer, &infos, &build_range_infos) + } + } + + fn build_bottom_level_acceleration_structures(&mut self, acceleration_structure_builds: &[render_system::BottomLevelAccelerationStructureBuild]) { + fn visit(this: &mut VulkanCommandBufferRecording, acceleration_structure_builds: &[render_system::BottomLevelAccelerationStructureBuild], mut infos: Vec, mut geometries: Vec>, mut build_range_infos: Vec>,) { if let Some(build) = acceleration_structure_builds.first() { - let (acceleration_structure_handle, acceleration_structure) = this.get_acceleration_structure(build.acceleration_structure); + let (_, acceleration_structure) = this.get_bottom_level_acceleration_structure(build.acceleration_structure); - let (as_geometries, offsets) = match build.acceleration_structure_build_type { - render_system::AccelerationStructureBuildAA::AABB { aabb_buffer, transform_buffer, transform_count } => { - (vec![], vec![]) - } - render_system::AccelerationStructureBuildAA::Instance { acceleration_structure, transform_buffer, transform_count } => { + let (as_geometries, offsets) = match &build.description { + render_system::BottomLevelAccelerationStructureBuildDescriptions::AABB { aabb_buffer, transform_buffer, transform_count } => { (vec![], vec![]) } - render_system::AccelerationStructureBuildAA::Mesh { vertex_buffer, index_buffer, transform_buffer, vertex_format, vertex_stride, index_format, index_count, transform_count, vertex_count } => { + render_system::BottomLevelAccelerationStructureBuildDescriptions::Mesh { vertex_buffer, index_buffer, vertex_position_encoding, index_format, triangle_count, vertex_count } => { let vertex_data_address = unsafe { - let (_, buffer) = this.get_buffer(vertex_buffer); + let (_, buffer) = this.get_buffer(vertex_buffer.buffer); this.render_system.device.get_buffer_device_address( &vk::BufferDeviceAddressInfo::default() .buffer(buffer.buffer) @@ -2603,16 +2750,7 @@ impl render_system::CommandBufferRecording for VulkanCommandBufferRecording<'_> }; let index_data_address = unsafe { - let (_, buffer) = this.get_buffer(index_buffer); - this.render_system.device.get_buffer_device_address( - &vk::BufferDeviceAddressInfo::default() - .buffer(buffer.buffer) - /* .build() */ - ) - }; - - let transform_data_address = unsafe { - let (_, buffer) = this.get_buffer(transform_buffer); + let (_, buffer) = this.get_buffer(index_buffer.buffer); this.render_system.device.get_buffer_device_address( &vk::BufferDeviceAddressInfo::default() .buffer(buffer.buffer) @@ -2628,20 +2766,20 @@ impl render_system::CommandBufferRecording for VulkanCommandBufferRecording<'_> device_address: index_data_address, }) .max_vertex(vertex_count - 1) - .transform_data(vk::DeviceOrHostAddressConstKHR { - device_address: transform_data_address, + .vertex_format(match vertex_position_encoding { + render_system::Encodings::IEEE754 => vk::Format::R32G32B32_SFLOAT, + _ => panic!("Invalid vertex position encoding"), }) - .vertex_format(to_format(vertex_format, None)) .index_type(match index_format { render_system::DataTypes::U8 => vk::IndexType::UINT16, render_system::DataTypes::U16 => vk::IndexType::UINT16, render_system::DataTypes::U32 => vk::IndexType::UINT32, _ => panic!("Invalid index format"), }) - .vertex_stride(vertex_stride as u64); + .vertex_stride(vertex_buffer.stride as u64); let build_range_info = vec![vk::AccelerationStructureBuildRangeInfoKHR::default() - .primitive_count(index_count / 3) + .primitive_count(*triangle_count) .primitive_offset(0) .first_vertex(0) .transform_offset(0) @@ -2655,12 +2793,12 @@ impl render_system::CommandBufferRecording for VulkanCommandBufferRecording<'_> }; let scratch_buffer_address = unsafe { - let (_, buffer) = this.get_buffer(build.scratch_buffer); + let (_, buffer) = this.get_buffer(build.scratch_buffer.buffer); this.render_system.device.get_buffer_device_address( &vk::BufferDeviceAddressInfo::default() .buffer(buffer.buffer) /* .build() */ - ) + ) + build.scratch_buffer.offset as u64 }; let build_geometry_info = vk::AccelerationStructureBuildGeometryInfoKHR::default() @@ -2677,7 +2815,7 @@ impl render_system::CommandBufferRecording for VulkanCommandBufferRecording<'_> build_range_infos.push(offsets); geometries.push(as_geometries); - visit(this, &acceleration_structure_builds[1..], infos, geometries, build_range_infos); + visit(this, &acceleration_structure_builds[1..], infos, geometries, build_range_infos,); } else { let command_buffer = this.get_command_buffer(); @@ -2693,7 +2831,7 @@ impl render_system::CommandBufferRecording for VulkanCommandBufferRecording<'_> } } - visit(self, acceleration_structure_builds, Vec::new(), Vec::new(), Vec::new()); + visit(self, acceleration_structure_builds, Vec::new(), Vec::new(), Vec::new(),); } /// Binds a shader to the GPU. @@ -2992,7 +3130,7 @@ impl render_system::CommandBufferRecording for VulkanCommandBufferRecording<'_> let raygen_shader_binding_tables = make_strided_range(binding_tables.raygen); let miss_shader_binding_tables = make_strided_range(binding_tables.miss); let hit_shader_binding_tables = make_strided_range(binding_tables.hit); - let callable_shader_binding_tables = make_strided_range(binding_tables.callable); + let callable_shader_binding_tables = if let Some(binding_table) = binding_tables.callable { make_strided_range(binding_table) } else { vk::StridedDeviceAddressRegionKHR::default() }; unsafe { self.render_system.ray_tracing_pipeline.cmd_trace_rays(command_buffer.command_buffer, &raygen_shader_binding_tables, &miss_shader_binding_tables, &hit_shader_binding_tables, &callable_shader_binding_tables, x, y, z) @@ -3402,6 +3540,8 @@ struct Mesh { struct AccelerationStructure { acceleration_structure: vk::AccelerationStructureKHR, + buffer: vk::Buffer, + scratch_size: usize, } struct Frame { @@ -3458,4 +3598,10 @@ mod tests { let mut vulkan_render_system = VulkanRenderSystem::new(&Settings { validation: true, ray_tracing: false }); render_system::tests::descriptor_sets(&mut vulkan_render_system); } + + #[test] + fn ray_tracing() { + let mut vulkan_render_system = VulkanRenderSystem::new(&Settings { validation: true, ray_tracing: true }); + render_system::tests::ray_tracing(&mut vulkan_render_system); + } } \ 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 02d900a0..274147ca 100644 --- a/src/resource_manager/mesh_resource_handler.rs +++ b/src/resource_manager/mesh_resource_handler.rs @@ -107,12 +107,14 @@ impl ResourceHandler for MeshResourceHandler { let offset = buffer.len(); + let meshlet_stream; + if MESHLETIZE { let meshlets = meshopt::clusterize::build_meshlets(&optimized_indices, vertex_count, 64, 126); let mut index_count: usize = 0; - for meshlet in meshlets { + for meshlet in &meshlets { index_count += meshlet.triangle_count as usize * 3; for i in 0..meshlet.triangle_count as usize { for x in meshlet.indices[i] { @@ -125,6 +127,17 @@ impl ResourceHandler for MeshResourceHandler { assert_eq!(index_count, optimized_indices.len()); index_streams.push(IndexStream{ data_type: IntegralTypes::U8, stream_type: IndexStreamTypes::Meshlets, offset, count: optimized_indices.len() as u32 }); + + let offset = buffer.len(); + + meshlet_stream = Some(MeshletStream{ offset, count: meshlets.len() as u32 }); + + for meshlet in &meshlets { + buffer.push(meshlet.vertex_count as u8); + buffer.push(meshlet.triangle_count as u8); + } + } else { + meshlet_stream = None; } let mesh = Mesh { @@ -133,6 +146,7 @@ impl ResourceHandler for MeshResourceHandler { vertex_components, vertex_count: vertex_count as u32, index_streams, + meshlet_stream, }; let resource_document = GenericResourceSerialization::new(asset_url.to_string(), mesh); @@ -189,6 +203,15 @@ impl ResourceHandler for MeshResourceHandler { file.seek(std::io::SeekFrom::Start(meshlet_indices_streams.offset as u64)).expect("Failed to seek to index buffer"); file.read(&mut buffer.buffer[0..(meshlet_indices_streams.count as usize * meshlet_indices_streams.data_type.size())]).unwrap(); } + "Meshlets" => { + #[cfg(debug_assertions)] + if !mesh.meshlet_stream.is_some() { error!("Requested Meshlets stream but mesh does not have meshlets."); continue; } + + let meshlet_stream = mesh.meshlet_stream.as_ref().unwrap(); + + file.seek(std::io::SeekFrom::Start(meshlet_stream.offset as u64)).expect("Failed to seek to index buffer"); + file.read(&mut buffer.buffer[0..(meshlet_stream.count as usize * 2)]).unwrap(); + } _ => { error!("Unknown buffer tag: {}", buffer.tag); } @@ -249,6 +272,12 @@ pub struct IndexStream { pub data_type: IntegralTypes, } +#[derive(Debug, Serialize, Deserialize)] +pub struct MeshletStream { + pub offset: usize, + pub count: u32, +} + #[derive(Debug, Serialize, Deserialize)] pub struct Mesh { pub compression: CompressionSchemes, @@ -256,6 +285,7 @@ pub struct Mesh { pub vertex_components: Vec, pub vertex_count: u32, pub index_streams: Vec, + pub meshlet_stream: Option, } impl Resource for Mesh { diff --git a/tests/gi.rs b/tests/gi.rs index 7d7debf9..b7cbe187 100644 --- a/tests/gi.rs +++ b/tests/gi.rs @@ -21,7 +21,7 @@ fn gi() { }); let _floor: EntityHandle = orchestrator.spawn(Mesh{ resource_id: "Box", material_id: "white_solid", transform: maths_rs::Mat4f::from_translation(Vec3f::new(0.0, -0.5, 0.0)) * maths_rs::Mat4f::from_scale(Vec3f::new(5.0, 1.0, 2.5)), }); - let _a: EntityHandle = orchestrator.spawn(Mesh{ resource_id: "Suzanne", material_id: "white_solid", transform: maths_rs::Mat4f::from_translation(Vec3f::new(0.0, 0.5, 0.0)) * maths_rs::Mat4f::from_scale(Vec3f::new(0.4, 0.4, 0.4)), }); + let _a: EntityHandle = orchestrator.spawn(Mesh{ resource_id: "Sphere", material_id: "white_solid", transform: maths_rs::Mat4f::from_translation(Vec3f::new(0.0, 0.5, 0.0)) * maths_rs::Mat4f::from_scale(Vec3f::new(0.4, 0.4, 0.4)), }); let _b: EntityHandle = orchestrator.spawn(Mesh{ resource_id: "Box", material_id: "red_solid", transform: maths_rs::Mat4f::from_translation(Vec3f::new(-0.6, 0.17, -0.1)) * maths_rs::Mat4f::from_scale(Vec3f::new(0.34, 0.34, 0.34)), }); let _c: EntityHandle = orchestrator.spawn(Mesh{ resource_id: "Box", material_id: "green_solid", transform: maths_rs::Mat4f::from_translation(Vec3f::new(0.5, 0.13, -0.3)) * maths_rs::Mat4f::from_scale(Vec3f::new(0.26, 0.26, 0.26)), });