diff --git a/.gitattributes b/.gitattributes index 149b5351f2..d6b8922131 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -*.mtl binary -*.obj binary +*.mtl binary -diff +*.obj binary -diff diff --git a/Cargo.lock b/Cargo.lock index 0d1769b4e0..e1181dc18b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4144,6 +4144,7 @@ dependencies = [ "ctor", "env_logger", "futures-lite", + "glam", "heck", "image", "js-sys", diff --git a/api_spec.rs b/api_spec.rs new file mode 100644 index 0000000000..dbf3979795 --- /dev/null +++ b/api_spec.rs @@ -0,0 +1,154 @@ +// Ray tracing api proposal for wgpu (underlining Vulkan, Metal and DX12 implementations) + +// The general design goal is to come up with an simpler Api, which allows for validation. +// Since this validation would have high overhead in some cases, +// I decided to provide more limited functions and unsafe functions for the same action, to evade this tradeoff. + +// Error handling and traits like Debug are omitted. + +// Core structures with no public members +pub struct Blas {} +pub struct Tlas {} +pub struct BlasRequirements {} +pub struct TlasInstances{} + +// Size descriptors used to describe the size requirements of blas geometries. +// Also used internally for strict validation +pub struct BlasTriangleGeometrySizeDescriptor{ + pub vertex_format: wgt::VertexFormat, + pub vertex_count: u32, + pub index_format: Option, + pub index_count: Option, + pub flags: AccelerationStructureGeometryFlags, +} + +pub struct BlasProceduralGeometrySizeDescriptor{ + pub count: u32, + pub flags: AccelerationStructureGeometryFlags, +} + +// Procedural geometry contains AABBs +pub struct BlasProceduralGeometry{ + pub size: BlasTriangleGeometrySize, + pub bounding_box_buffer: Buffer, + pub bounding_box_buffer_offset: wgt::BufferAddress, + pub bounding_box_stride: wgt::BufferAddress, +} + +// Triangle Geometry contains vertices, optionally indices and transforms +pub struct BlasTriangleGeometry{ + pub size: BlasTriangleGeometrySize, + pub vertex_buffer: Buffer + pub first_vertex: u32, + pub vertex_stride: wgt::BufferAddress, + pub index_buffer: Option, + pub index_buffer_offset: Option, + pub transform_buffer: Option, + pub transform_buffer_offset: Option, +} + +// Build flags +pub struct AccelerationStructureFlags{ + // build_speed, small_size, ... +} + +// Geometry flags +pub struct AccelerationStructureGeometryFlags{ + // opaque, no_duplicate_any_hit, ... +} + +// Descriptors used to determine the memory requirements and validation of a acceleration structure +pub enum BlasGeometrySizeDescriptors{ + Triangles{desc: Vec}, + Procedural(desc: Vec) +} + +// With prefer update, we decide if an update is possible, else we rebuild. +// Maybe a force update option could be useful +pub enum UpdateMode{ + Build, + // Update, + PreferUpdate, +} + +// General descriptor for the size requirements, +// since the required size depends on the contents and build flags +pub struct GetBlasRequirementsDescriptor{ + pub flags: AccelerationStructureFlags, +} + +// Creation descriptors, we provide flags, and update_mode. +// We store it in the structure, so we don't need to pass it every build. +pub struct CreateBlasDescriptor<'a>{ + pub flags: AccelerationStructureFlags, + pub update_mode: UpdateMode, +} + +pub struct CreateTlasDescriptor{ + pub max_instances: u32, + pub flags: AccelerationStructureFlags, + pub update_mode: UpdateMode, +} + +// Secure instance entry for tlas +struct TlasInstance{ + transform: [f32; 12], + custom_index: u32, + mask: u8, + shader_binding_table_record_offset: u32, + flags: u8 //bitmap + blas: Blas +} + +impl Device { + // Creates a new bottom level accelerations structures and sets internal states for validation and builds (e.g update mode) + pub fn create_blas(&self, desc: &CreateBlasDescriptor, entries: BlasGeometrySizeDescriptors) -> Blas; + + // Creates a new top level accelerations structures and sets internal states for builds (e.g update mode) + pub fn create_tlas(&self, desc: &CreateTlasDescriptor) -> Tlas; +} + +// Enum for the different types of geometries inside a single blas build +// [Should we used nested iterators with dynamic dispatch instead] +enum BlasGeometries<'a>{ + TriangleGeometries(&'a [BlasTriangleGeometry]) + ProceduralGeometries(&'a [BlasProceduralGeometry]) +} + +impl CommandEncoder { + // Build acceleration structures. + // Elements of blas may be used in a tlas (internal synchronization). + // This function will allocate a single big scratch buffer, that is shared between internal builds. + // If there are to many acceleration structures for a single build (size constraint), + // we will automatically distribute them between multiple internal builds. (reducing the required size of the scratch buffer). + // This version will be implemented in wgpu::util not wgpu-core. + pub fn build_acceleration_structures<'a>(&self, + blas: impl IntoIterator)>, + tlas: impl IntoIterator)>, + ); + + // unsafe version without validation for tlas, directly using an instance buffer. + // u32 for the number of instances to build + pub fn build_acceleration_structures_unsafe_tlas<'a>(&self, + blas: impl IntoIterator)>, + tlas: impl IntoIterator, + ); + + // Creates a new blas and copies (in a compacting way) the contents of the provided blas + // into the new one (compaction flag must be set). + pub fn compact_blas(&self, blas: &Blas) -> Blas; +} + +// Safe Tlas Instance +impl TlasInstances{ + pub fn new(max_instances: u32) -> Self; + + // gets instances to read from + pub fn get(&self) -> &[TlasInstance]; + // gets instances to modify, we keep track of the range to determine what we need to validate and copy + pub fn get_mut_range(&mut self, range: Range) -> &mut [TlasInstance]; + // get the number of instances which will be build + pub fn active(&self) -> u32; + // set the number of instances which will be build + pub fn set_active(&mut self, active: u32); +} \ No newline at end of file diff --git a/examples/src/lib.rs b/examples/src/lib.rs index 1a55631097..d6a18c0f11 100644 --- a/examples/src/lib.rs +++ b/examples/src/lib.rs @@ -13,6 +13,9 @@ pub mod hello_windows; pub mod hello_workgroups; pub mod mipmap; pub mod msaa_line; +pub mod ray_cube_compute; +pub mod ray_cube_fragment; +pub mod ray_scene; pub mod render_to_texture; pub mod repeated_compute; pub mod shadow; diff --git a/examples/src/main.rs b/examples/src/main.rs index 8b149d5a28..d5db62e0cf 100644 --- a/examples/src/main.rs +++ b/examples/src/main.rs @@ -146,6 +146,24 @@ const EXAMPLES: &[ExampleDesc] = &[ webgl: false, // No RODS webgpu: true, }, + ExampleDesc { + name: "ray_cube_compute", + function: wgpu_examples::ray_cube_compute::main, + webgl: false, // No Ray-tracing extensions + webgpu: false, // No Ray-tracing extensions (yet) + }, + ExampleDesc { + name: "ray_cube_fragment", + function: wgpu_examples::ray_cube_fragment::main, + webgl: false, // No Ray-tracing extensions + webgpu: false, // No Ray-tracing extensions (yet) + }, + ExampleDesc { + name: "ray_scene", + function: wgpu_examples::ray_scene::main, + webgl: false, // No Ray-tracing extensions + webgpu: false, // No Ray-tracing extensions (yet) + }, ]; fn get_example_name() -> Option { diff --git a/examples/src/ray_cube_compute/README.md b/examples/src/ray_cube_compute/README.md new file mode 100644 index 0000000000..9110787e38 --- /dev/null +++ b/examples/src/ray_cube_compute/README.md @@ -0,0 +1,14 @@ +# ray-cube + +This example renders a ray traced cube with hardware acceleration. +A separate compute shader is used to perform the ray queries. + +## To Run + +``` +cargo run --example ray-cube-compute +``` + +## Screenshots + +![Cube example](screenshot.png) diff --git a/examples/src/ray_cube_compute/blit.wgsl b/examples/src/ray_cube_compute/blit.wgsl new file mode 100644 index 0000000000..69adbb3ccd --- /dev/null +++ b/examples/src/ray_cube_compute/blit.wgsl @@ -0,0 +1,52 @@ +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) tex_coords: vec2, +}; + +// meant to be called with 3 vertex indices: 0, 1, 2 +// draws one large triangle over the clip space like this: +// (the asterisks represent the clip space bounds) +//-1,1 1,1 +// --------------------------------- +// | * . +// | * . +// | * . +// | * . +// | * . +// | * . +// |*************** +// | . 1,-1 +// | . +// | . +// | . +// | . +// |. +@vertex +fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { + var result: VertexOutput; + let x = i32(vertex_index) / 2; + let y = i32(vertex_index) & 1; + let tc = vec2( + f32(x) * 2.0, + f32(y) * 2.0 + ); + result.position = vec4( + tc.x * 2.0 - 1.0, + 1.0 - tc.y * 2.0, + 0.0, 1.0 + ); + result.tex_coords = tc; + return result; +} + +@group(0) +@binding(0) +var r_color: texture_2d; +@group(0) +@binding(1) +var r_sampler: sampler; + +@fragment +fn fs_main(vertex: VertexOutput) -> @location(0) vec4 { + return textureSample(r_color, r_sampler, vertex.tex_coords); +} diff --git a/examples/src/ray_cube_compute/mod.rs b/examples/src/ray_cube_compute/mod.rs new file mode 100644 index 0000000000..97c68255ae --- /dev/null +++ b/examples/src/ray_cube_compute/mod.rs @@ -0,0 +1,628 @@ +use std::{borrow::Cow, future::Future, iter, mem, pin::Pin, task, time::Instant}; + +use bytemuck::{Pod, Zeroable}; +use glam::{Affine3A, Mat4, Quat, Vec3}; +use wgpu::util::DeviceExt; + +use rt::traits::*; +use wgpu::{ray_tracing as rt, StoreOp}; + +// from cube +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct Vertex { + _pos: [f32; 4], + _tex_coord: [f32; 2], +} + +fn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex { + Vertex { + _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0], + _tex_coord: [tc[0] as f32, tc[1] as f32], + } +} + +fn create_vertices() -> (Vec, Vec) { + let vertex_data = [ + // top (0, 0, 1) + vertex([-1, -1, 1], [0, 0]), + vertex([1, -1, 1], [1, 0]), + vertex([1, 1, 1], [1, 1]), + vertex([-1, 1, 1], [0, 1]), + // bottom (0, 0, -1) + vertex([-1, 1, -1], [1, 0]), + vertex([1, 1, -1], [0, 0]), + vertex([1, -1, -1], [0, 1]), + vertex([-1, -1, -1], [1, 1]), + // right (1, 0, 0) + vertex([1, -1, -1], [0, 0]), + vertex([1, 1, -1], [1, 0]), + vertex([1, 1, 1], [1, 1]), + vertex([1, -1, 1], [0, 1]), + // left (-1, 0, 0) + vertex([-1, -1, 1], [1, 0]), + vertex([-1, 1, 1], [0, 0]), + vertex([-1, 1, -1], [0, 1]), + vertex([-1, -1, -1], [1, 1]), + // front (0, 1, 0) + vertex([1, 1, -1], [1, 0]), + vertex([-1, 1, -1], [0, 0]), + vertex([-1, 1, 1], [0, 1]), + vertex([1, 1, 1], [1, 1]), + // back (0, -1, 0) + vertex([1, -1, 1], [0, 0]), + vertex([-1, -1, 1], [1, 0]), + vertex([-1, -1, -1], [1, 1]), + vertex([1, -1, -1], [0, 1]), + ]; + + let index_data: &[u16] = &[ + 0, 1, 2, 2, 3, 0, // top + 4, 5, 6, 6, 7, 4, // bottom + 8, 9, 10, 10, 11, 8, // right + 12, 13, 14, 14, 15, 12, // left + 16, 17, 18, 18, 19, 16, // front + 20, 21, 22, 22, 23, 20, // back + ]; + + (vertex_data.to_vec(), index_data.to_vec()) +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct Uniforms { + view_inverse: [[f32; 4]; 4], + proj_inverse: [[f32; 4]; 4], +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct AccelerationStructureInstance { + transform: [f32; 12], + custom_index_and_mask: u32, + shader_binding_table_record_offset_and_flags: u32, + acceleration_structure_reference: u64, +} + +impl std::fmt::Debug for AccelerationStructureInstance { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Instance") + .field("transform", &self.transform) + .field("custom_index()", &self.custom_index()) + .field("mask()", &self.mask()) + .field( + "shader_binding_table_record_offset()", + &self.shader_binding_table_record_offset(), + ) + .field("flags()", &self.flags()) + .field( + "acceleration_structure_reference", + &self.acceleration_structure_reference, + ) + .finish() + } +} + +#[allow(dead_code)] +impl AccelerationStructureInstance { + const LOW_24_MASK: u32 = 0x00ff_ffff; + const MAX_U24: u32 = (1u32 << 24u32) - 1u32; + + #[inline] + fn affine_to_rows(mat: &Affine3A) -> [f32; 12] { + let row_0 = mat.matrix3.row(0); + let row_1 = mat.matrix3.row(1); + let row_2 = mat.matrix3.row(2); + let translation = mat.translation; + [ + row_0.x, + row_0.y, + row_0.z, + translation.x, + row_1.x, + row_1.y, + row_1.z, + translation.y, + row_2.x, + row_2.y, + row_2.z, + translation.z, + ] + } + + #[inline] + fn rows_to_affine(rows: &[f32; 12]) -> Affine3A { + Affine3A::from_cols_array(&[ + rows[0], rows[3], rows[6], rows[9], rows[1], rows[4], rows[7], rows[10], rows[2], + rows[5], rows[8], rows[11], + ]) + } + + pub fn transform_as_affine(&self) -> Affine3A { + Self::rows_to_affine(&self.transform) + } + pub fn set_transform(&mut self, transform: &Affine3A) { + self.transform = Self::affine_to_rows(transform); + } + + pub fn custom_index(&self) -> u32 { + self.custom_index_and_mask & Self::LOW_24_MASK + } + + pub fn mask(&self) -> u8 { + (self.custom_index_and_mask >> 24) as u8 + } + + pub fn shader_binding_table_record_offset(&self) -> u32 { + self.shader_binding_table_record_offset_and_flags & Self::LOW_24_MASK + } + + pub fn flags(&self) -> u8 { + (self.shader_binding_table_record_offset_and_flags >> 24) as u8 + } + + pub fn set_custom_index(&mut self, custom_index: u32) { + debug_assert!( + custom_index <= Self::MAX_U24, + "custom_index uses more than 24 bits! {custom_index} > {}", + Self::MAX_U24 + ); + self.custom_index_and_mask = + (custom_index & Self::LOW_24_MASK) | (self.custom_index_and_mask & !Self::LOW_24_MASK) + } + + pub fn set_mask(&mut self, mask: u8) { + self.custom_index_and_mask = + (self.custom_index_and_mask & Self::LOW_24_MASK) | (u32::from(mask) << 24) + } + + pub fn set_shader_binding_table_record_offset( + &mut self, + shader_binding_table_record_offset: u32, + ) { + debug_assert!(shader_binding_table_record_offset <= Self::MAX_U24, "shader_binding_table_record_offset uses more than 24 bits! {shader_binding_table_record_offset} > {}", Self::MAX_U24); + self.shader_binding_table_record_offset_and_flags = (shader_binding_table_record_offset + & Self::LOW_24_MASK) + | (self.shader_binding_table_record_offset_and_flags & !Self::LOW_24_MASK) + } + + pub fn set_flags(&mut self, flags: u8) { + self.shader_binding_table_record_offset_and_flags = + (self.shader_binding_table_record_offset_and_flags & Self::LOW_24_MASK) + | (u32::from(flags) << 24) + } + + pub fn new( + transform: &Affine3A, + custom_index: u32, + mask: u8, + shader_binding_table_record_offset: u32, + flags: u8, + acceleration_structure_reference: u64, + ) -> Self { + debug_assert!( + custom_index <= Self::MAX_U24, + "custom_index uses more than 24 bits! {custom_index} > {}", + Self::MAX_U24 + ); + debug_assert!( + shader_binding_table_record_offset <= Self::MAX_U24, + "shader_binding_table_record_offset uses more than 24 bits! {shader_binding_table_record_offset} > {}", Self::MAX_U24 + ); + AccelerationStructureInstance { + transform: Self::affine_to_rows(transform), + custom_index_and_mask: (custom_index & Self::MAX_U24) | (u32::from(mask) << 24), + shader_binding_table_record_offset_and_flags: (shader_binding_table_record_offset + & Self::MAX_U24) + | (u32::from(flags) << 24), + acceleration_structure_reference, + } + } +} + +/// A wrapper for `pop_error_scope` futures that panics if an error occurs. +/// +/// Given a future `inner` of an `Option` for some error type `E`, +/// wait for the future to be ready, and panic if its value is `Some`. +/// +/// This can be done simpler with `FutureExt`, but we don't want to add +/// a dependency just for this small case. +struct ErrorFuture { + inner: F, +} +impl>> Future for ErrorFuture { + type Output = (); + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<()> { + let inner = unsafe { self.map_unchecked_mut(|me| &mut me.inner) }; + inner.poll(cx).map(|error| { + if let Some(e) = error { + panic!("Rendering {}", e); + } + }) + } +} + +#[allow(dead_code)] +struct Example { + rt_target: wgpu::Texture, + rt_view: wgpu::TextureView, + sampler: wgpu::Sampler, + uniform_buf: wgpu::Buffer, + vertex_buf: wgpu::Buffer, + index_buf: wgpu::Buffer, + blas: rt::Blas, + tlas_package: rt::TlasPackage, + compute_pipeline: wgpu::ComputePipeline, + compute_bind_group: wgpu::BindGroup, + blit_pipeline: wgpu::RenderPipeline, + blit_bind_group: wgpu::BindGroup, + start_inst: Instant, +} + +impl crate::framework::Example for Example { + fn required_features() -> wgpu::Features { + wgpu::Features::TEXTURE_BINDING_ARRAY + | wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY + | wgpu::Features::VERTEX_WRITABLE_STORAGE + | wgpu::Features::RAY_QUERY + | wgpu::Features::RAY_TRACING_ACCELERATION_STRUCTURE + } + + fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { + wgpu::DownlevelCapabilities::default() + } + fn required_limits() -> wgpu::Limits { + wgpu::Limits::default() + } + + fn init( + config: &wgpu::SurfaceConfiguration, + _adapter: &wgpu::Adapter, + device: &wgpu::Device, + queue: &wgpu::Queue, + ) -> Self { + let side_count = 8; + + let rt_target = device.create_texture(&wgpu::TextureDescriptor { + label: Some("rt_target"), + size: wgpu::Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::STORAGE_BINDING, + view_formats: &[wgpu::TextureFormat::Rgba8Unorm], + }); + + let rt_view = rt_target.create_view(&wgpu::TextureViewDescriptor { + label: None, + format: Some(wgpu::TextureFormat::Rgba8Unorm), + dimension: Some(wgpu::TextureViewDimension::D2), + aspect: wgpu::TextureAspect::All, + base_mip_level: 0, + mip_level_count: None, + base_array_layer: 0, + array_layer_count: None, + }); + + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label: Some("rt_sampler"), + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); + + let uniforms = { + let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y); + let proj = Mat4::perspective_rh( + 59.0_f32.to_radians(), + config.width as f32 / config.height as f32, + 0.001, + 1000.0, + ); + + Uniforms { + view_inverse: view.inverse().to_cols_array_2d(), + proj_inverse: proj.inverse().to_cols_array_2d(), + } + }; + + let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Uniform Buffer"), + contents: bytemuck::cast_slice(&[uniforms]), + usage: wgpu::BufferUsages::UNIFORM, + }); + + let (vertex_data, index_data) = create_vertices(); + + let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&vertex_data), + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT, + }); + + let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&index_data), + usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT, + }); + + let blas_geo_size_desc = rt::BlasTriangleGeometrySizeDescriptor { + vertex_format: wgpu::VertexFormat::Float32x4, + vertex_count: vertex_data.len() as u32, + index_format: Some(wgpu::IndexFormat::Uint16), + index_count: Some(index_data.len() as u32), + flags: rt::AccelerationStructureGeometryFlags::OPAQUE, + }; + + let blas = device.create_blas( + &rt::CreateBlasDescriptor { + label: None, + flags: rt::AccelerationStructureFlags::PREFER_FAST_TRACE, + update_mode: rt::AccelerationStructureUpdateMode::Build, + }, + rt::BlasGeometrySizeDescriptors::Triangles { + desc: vec![blas_geo_size_desc.clone()], + }, + ); + + let tlas = device.create_tlas(&rt::CreateTlasDescriptor { + label: None, + flags: rt::AccelerationStructureFlags::PREFER_FAST_TRACE, + update_mode: rt::AccelerationStructureUpdateMode::Build, + max_instances: side_count * side_count, + }); + + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("rt_computer"), + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), + }); + + let blit_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("blit"), + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("blit.wgsl"))), + }); + + let compute_pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("rt"), + layout: None, + module: &shader, + entry_point: "main", + }); + + let compute_bind_group_layout = compute_pipeline.get_bind_group_layout(0); + + let compute_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &compute_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&rt_view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: uniform_buf.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::AccelerationStructure(&tlas), + }, + ], + }); + + let blit_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("blit"), + layout: None, + vertex: wgpu::VertexState { + module: &blit_shader, + entry_point: "vs_main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &blit_shader, + entry_point: "fs_main", + targets: &[Some(config.format.into())], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + let blit_bind_group_layout = blit_pipeline.get_bind_group_layout(0); + + let blit_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &blit_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&rt_view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + ], + }); + + let mut tlas_package = rt::TlasPackage::new(tlas, side_count * side_count); + + let dist = 3.0; + + for x in 0..side_count { + for y in 0..side_count { + *tlas_package + .get_mut_single((x + y * side_count) as usize) + .unwrap() = Some(rt::TlasInstance::new( + &blas, + AccelerationStructureInstance::affine_to_rows( + &Affine3A::from_rotation_translation( + Quat::from_rotation_y(45.9_f32.to_radians()), + Vec3 { + x: x as f32 * dist, + y: y as f32 * dist, + z: -30.0, + }, + ), + ), + 0, + 0xff, + )); + } + } + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.build_acceleration_structures( + iter::once(&rt::BlasBuildEntry { + blas: &blas, + geometry: rt::BlasGeometries::TriangleGeometries(vec![rt::BlasTriangleGeometry { + size: &blas_geo_size_desc, + vertex_buffer: &vertex_buf, + first_vertex: 0, + vertex_stride: mem::size_of::() as u64, + index_buffer: Some(&index_buf), + index_buffer_offset: Some(0), + transform_buffer: None, + transform_buffer_offset: None, + }]), + }), + // iter::empty(), + iter::once(&tlas_package), + ); + + queue.submit(Some(encoder.finish())); + + let start_inst = Instant::now(); + + Example { + rt_target, + rt_view, + sampler, + uniform_buf, + vertex_buf, + index_buf, + blas, + tlas_package, + compute_pipeline, + compute_bind_group, + blit_pipeline, + blit_bind_group, + start_inst, + } + } + + fn update(&mut self, _event: winit::event::WindowEvent) { + //empty + } + + fn resize( + &mut self, + _config: &wgpu::SurfaceConfiguration, + _device: &wgpu::Device, + _queue: &wgpu::Queue, + ) { + } + + fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) { + device.push_error_scope(wgpu::ErrorFilter::Validation); + + let anim_time = self.start_inst.elapsed().as_secs_f64() as f32; + + self.tlas_package + .get_mut_single(0) + .unwrap() + .as_mut() + .unwrap() + .transform = + AccelerationStructureInstance::affine_to_rows(&Affine3A::from_rotation_translation( + Quat::from_euler( + glam::EulerRot::XYZ, + anim_time * 0.342, + anim_time * 0.254, + anim_time * 0.832, + ), + Vec3 { + x: 0.0, + y: 0.0, + z: -6.0, + }, + )); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.build_acceleration_structures(iter::empty(), iter::once(&self.tlas_package)); + + { + let mut cpass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label: None, + timestamp_writes: None, + }); + cpass.set_pipeline(&self.compute_pipeline); + cpass.set_bind_group(0, &self.compute_bind_group, &[]); + cpass.dispatch_workgroups(self.rt_target.width() / 8, self.rt_target.height() / 8, 1); + } + + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + rpass.set_pipeline(&self.blit_pipeline); + rpass.set_bind_group(0, &self.blit_bind_group, &[]); + rpass.draw(0..3, 0..1); + } + + queue.submit(Some(encoder.finish())); + } +} + +pub fn main() { + crate::framework::run::("ray-cube"); +} + +#[cfg(test)] +#[wgpu_test::gpu_test] +static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { + name: "ray_cube_compute", + image_path: "/examples/ray_cube_compute/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters { + required_features: ::required_features(), + required_limits: ::required_limits(), + skips: vec![], + failures: Vec::new(), + required_downlevel_caps: + ::required_downlevel_capabilities(), + }, + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: std::marker::PhantomData::, +}; diff --git a/examples/src/ray_cube_compute/screenshot.png b/examples/src/ray_cube_compute/screenshot.png new file mode 100644 index 0000000000..6b737f1e2a Binary files /dev/null and b/examples/src/ray_cube_compute/screenshot.png differ diff --git a/examples/src/ray_cube_compute/shader.wgsl b/examples/src/ray_cube_compute/shader.wgsl new file mode 100644 index 0000000000..45d6be11cb --- /dev/null +++ b/examples/src/ray_cube_compute/shader.wgsl @@ -0,0 +1,81 @@ +/* +let RAY_FLAG_NONE = 0x00u; +let RAY_FLAG_OPAQUE = 0x01u; +let RAY_FLAG_NO_OPAQUE = 0x02u; +let RAY_FLAG_TERMINATE_ON_FIRST_HIT = 0x04u; +let RAY_FLAG_SKIP_CLOSEST_HIT_SHADER = 0x08u; +let RAY_FLAG_CULL_BACK_FACING = 0x10u; +let RAY_FLAG_CULL_FRONT_FACING = 0x20u; +let RAY_FLAG_CULL_OPAQUE = 0x40u; +let RAY_FLAG_CULL_NO_OPAQUE = 0x80u; +let RAY_FLAG_SKIP_TRIANGLES = 0x100u; +let RAY_FLAG_SKIP_AABBS = 0x200u; + +let RAY_QUERY_INTERSECTION_NONE = 0u; +let RAY_QUERY_INTERSECTION_TRIANGLE = 1u; +let RAY_QUERY_INTERSECTION_GENERATED = 2u; +let RAY_QUERY_INTERSECTION_AABB = 4u; + +struct RayDesc { + flags: u32, + cull_mask: u32, + t_min: f32, + t_max: f32, + origin: vec3, + dir: vec3, +} + +struct RayIntersection { + kind: u32, + t: f32, + instance_custom_index: u32, + instance_id: u32, + sbt_record_offset: u32, + geometry_index: u32, + primitive_index: u32, + barycentrics: vec2, + front_face: bool, + object_to_world: mat4x3, + world_to_object: mat4x3, +} +*/ + +struct Uniforms { + view_inv: mat4x4, + proj_inv: mat4x4, +}; + +@group(0) @binding(0) +var output: texture_storage_2d; + +@group(0) @binding(1) +var uniforms: Uniforms; + +@group(0) @binding(2) +var acc_struct: acceleration_structure; + +@compute @workgroup_size(8, 8) +fn main(@builtin(global_invocation_id) global_id: vec3) { + let target_size = textureDimensions(output); + var color = vec4(vec2(global_id.xy) / vec2(target_size), 0.0, 1.0); + + + let pixel_center = vec2(global_id.xy) + vec2(0.5); + let in_uv = pixel_center/vec2(target_size.xy); + let d = in_uv * 2.0 - 1.0; + + let origin = (uniforms.view_inv * vec4(0.0,0.0,0.0,1.0)).xyz; + let temp = uniforms.proj_inv * vec4(d.x, d.y, 1.0, 1.0); + let direction = (uniforms.view_inv * vec4(normalize(temp.xyz), 0.0)).xyz; + + var rq: ray_query; + rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.1, 200.0, origin, direction)); + rayQueryProceed(&rq); + + let intersection = rayQueryGetCommittedIntersection(&rq); + if (intersection.kind != RAY_QUERY_INTERSECTION_NONE) { + color = vec4(intersection.barycentrics, 1.0 - intersection.barycentrics.x - intersection.barycentrics.y, 1.0); + } + + textureStore(output, global_id.xy, color); +} diff --git a/examples/src/ray_cube_fragment/README.md b/examples/src/ray_cube_fragment/README.md new file mode 100644 index 0000000000..fba22bc6d6 --- /dev/null +++ b/examples/src/ray_cube_fragment/README.md @@ -0,0 +1,13 @@ +# ray-cube + +This example renders a ray traced cube with hardware acceleration. + +## To Run + +``` +cargo run --example ray-cube-fragment +``` + +## Screenshots + +![Cube example](screenshot.png) diff --git a/examples/src/ray_cube_fragment/mod.rs b/examples/src/ray_cube_fragment/mod.rs new file mode 100644 index 0000000000..8aa81a1810 --- /dev/null +++ b/examples/src/ray_cube_fragment/mod.rs @@ -0,0 +1,395 @@ +use std::{borrow::Cow, future::Future, iter, mem, pin::Pin, task, time::Instant}; + +use bytemuck::{Pod, Zeroable}; +use glam::{Mat4, Quat, Vec3}; +use wgpu::util::DeviceExt; + +use rt::traits::*; +use wgpu::ray_tracing as rt; + +// from cube +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct Vertex { + _pos: [f32; 4], + _tex_coord: [f32; 2], +} + +fn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex { + Vertex { + _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0], + _tex_coord: [tc[0] as f32, tc[1] as f32], + } +} + +fn create_vertices() -> (Vec, Vec) { + let vertex_data = [ + // top (0, 0, 1) + vertex([-1, -1, 1], [0, 0]), + vertex([1, -1, 1], [1, 0]), + vertex([1, 1, 1], [1, 1]), + vertex([-1, 1, 1], [0, 1]), + // bottom (0, 0, -1) + vertex([-1, 1, -1], [1, 0]), + vertex([1, 1, -1], [0, 0]), + vertex([1, -1, -1], [0, 1]), + vertex([-1, -1, -1], [1, 1]), + // right (1, 0, 0) + vertex([1, -1, -1], [0, 0]), + vertex([1, 1, -1], [1, 0]), + vertex([1, 1, 1], [1, 1]), + vertex([1, -1, 1], [0, 1]), + // left (-1, 0, 0) + vertex([-1, -1, 1], [1, 0]), + vertex([-1, 1, 1], [0, 0]), + vertex([-1, 1, -1], [0, 1]), + vertex([-1, -1, -1], [1, 1]), + // front (0, 1, 0) + vertex([1, 1, -1], [1, 0]), + vertex([-1, 1, -1], [0, 0]), + vertex([-1, 1, 1], [0, 1]), + vertex([1, 1, 1], [1, 1]), + // back (0, -1, 0) + vertex([1, -1, 1], [0, 0]), + vertex([-1, -1, 1], [1, 0]), + vertex([-1, -1, -1], [1, 1]), + vertex([1, -1, -1], [0, 1]), + ]; + + let index_data: &[u16] = &[ + 0, 1, 2, 2, 3, 0, // top + 4, 5, 6, 6, 7, 4, // bottom + 8, 9, 10, 10, 11, 8, // right + 12, 13, 14, 14, 15, 12, // left + 16, 17, 18, 18, 19, 16, // front + 20, 21, 22, 22, 23, 20, // back + ]; + + (vertex_data.to_vec(), index_data.to_vec()) +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct Uniforms { + view_inverse: [[f32; 4]; 4], + proj_inverse: [[f32; 4]; 4], +} + +/// A wrapper for `pop_error_scope` futures that panics if an error occurs. +/// +/// Given a future `inner` of an `Option` for some error type `E`, +/// wait for the future to be ready, and panic if its value is `Some`. +/// +/// This can be done simpler with `FutureExt`, but we don't want to add +/// a dependency just for this small case. +struct ErrorFuture { + inner: F, +} +impl>> Future for ErrorFuture { + type Output = (); + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<()> { + let inner = unsafe { self.map_unchecked_mut(|me| &mut me.inner) }; + inner.poll(cx).map(|error| { + if let Some(e) = error { + panic!("Rendering {}", e); + } + }) + } +} + +#[allow(dead_code)] +struct Example { + uniforms: Uniforms, + uniform_buf: wgpu::Buffer, + vertex_buf: wgpu::Buffer, + index_buf: wgpu::Buffer, + blas: rt::Blas, + tlas_package: rt::TlasPackage, + pipeline: wgpu::RenderPipeline, + bind_group: wgpu::BindGroup, + start_inst: Instant, +} + +impl crate::framework::Example for Example { + fn required_features() -> wgpu::Features { + wgpu::Features::RAY_QUERY | wgpu::Features::RAY_TRACING_ACCELERATION_STRUCTURE + } + + fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { + wgpu::DownlevelCapabilities::default() + } + fn required_limits() -> wgpu::Limits { + wgpu::Limits::default() + } + + fn init( + config: &wgpu::SurfaceConfiguration, + _adapter: &wgpu::Adapter, + device: &wgpu::Device, + queue: &wgpu::Queue, + ) -> Self { + let side_count = 8; + + let uniforms = { + let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y); + let proj = Mat4::perspective_rh( + 59.0_f32.to_radians(), + config.width as f32 / config.height as f32, + 0.001, + 1000.0, + ); + + Uniforms { + view_inverse: view.inverse().to_cols_array_2d(), + proj_inverse: proj.inverse().to_cols_array_2d(), + } + }; + + let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Uniform Buffer"), + contents: bytemuck::cast_slice(&[uniforms]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let (vertex_data, index_data) = create_vertices(); + + let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&vertex_data), + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT, + }); + + let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&index_data), + usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT, + }); + + let blas_geo_size_desc = rt::BlasTriangleGeometrySizeDescriptor { + vertex_format: wgpu::VertexFormat::Float32x4, + vertex_count: vertex_data.len() as u32, + index_format: Some(wgpu::IndexFormat::Uint16), + index_count: Some(index_data.len() as u32), + flags: rt::AccelerationStructureGeometryFlags::OPAQUE, + }; + + let blas = device.create_blas( + &rt::CreateBlasDescriptor { + label: None, + flags: rt::AccelerationStructureFlags::PREFER_FAST_TRACE, + update_mode: rt::AccelerationStructureUpdateMode::Build, + }, + rt::BlasGeometrySizeDescriptors::Triangles { + desc: vec![blas_geo_size_desc.clone()], + }, + ); + + let tlas = device.create_tlas(&rt::CreateTlasDescriptor { + label: None, + flags: rt::AccelerationStructureFlags::PREFER_FAST_TRACE, + update_mode: rt::AccelerationStructureUpdateMode::Build, + max_instances: side_count * side_count, + }); + + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(config.format.into())], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + let bind_group_layout = pipeline.get_bind_group_layout(0); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: uniform_buf.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::AccelerationStructure(&tlas), + }, + ], + }); + + let tlas_package = rt::TlasPackage::new(tlas, side_count * side_count); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.build_acceleration_structures( + iter::once(&rt::BlasBuildEntry { + blas: &blas, + geometry: rt::BlasGeometries::TriangleGeometries(vec![rt::BlasTriangleGeometry { + size: &blas_geo_size_desc, + vertex_buffer: &vertex_buf, + first_vertex: 0, + vertex_stride: mem::size_of::() as u64, + index_buffer: Some(&index_buf), + index_buffer_offset: Some(0), + transform_buffer: None, + transform_buffer_offset: None, + }]), + }), + // iter::empty(), + iter::once(&tlas_package), + ); + + queue.submit(Some(encoder.finish())); + + let start_inst = Instant::now(); + + Example { + uniforms, + uniform_buf, + vertex_buf, + index_buf, + blas, + tlas_package, + pipeline, + bind_group, + start_inst, + } + } + + fn update(&mut self, _event: winit::event::WindowEvent) {} + + fn resize( + &mut self, + config: &wgpu::SurfaceConfiguration, + _device: &wgpu::Device, + queue: &wgpu::Queue, + ) { + let proj = Mat4::perspective_rh( + 59.0_f32.to_radians(), + config.width as f32 / config.height as f32, + 0.001, + 1000.0, + ); + + self.uniforms.proj_inverse = proj.inverse().to_cols_array_2d(); + + queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(&[self.uniforms])); + } + + fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) { + device.push_error_scope(wgpu::ErrorFilter::Validation); + + // scene update + { + let dist = 12.0; + + let side_count = 8; + + let anim_time = self.start_inst.elapsed().as_secs_f64() as f32; + + for x in 0..side_count { + for y in 0..side_count { + let instance = self + .tlas_package + .get_mut_single((x + y * side_count) as usize) + .unwrap(); + + let x = x as f32 / (side_count - 1) as f32; + let y = y as f32 / (side_count - 1) as f32; + let x = x * 2.0 - 1.0; + let y = y * 2.0 - 1.0; + + let transform = Mat4::from_rotation_translation( + Quat::from_euler( + glam::EulerRot::XYZ, + anim_time * 0.5 * 0.342, + anim_time * 0.5 * 0.254, + anim_time * 0.5 * 0.832, + ), + Vec3 { + x: x * dist, + y: y * dist, + z: -24.0, + }, + ); + let transform = transform.transpose().to_cols_array()[..12] + .try_into() + .unwrap(); + + *instance = Some(rt::TlasInstance::new(&self.blas, transform, 0, 0xff)); + } + } + } + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.build_acceleration_structures(iter::empty(), iter::once(&self.tlas_package)); + + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + rpass.set_pipeline(&self.pipeline); + rpass.set_bind_group(0, &self.bind_group, &[]); + rpass.draw(0..3, 0..1); + } + + queue.submit(Some(encoder.finish())); + } +} + +pub fn main() { + crate::framework::run::("ray-cube"); +} + +#[cfg(test)] +#[wgpu_test::gpu_test] +static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { + name: "ray_cube_fragment", + image_path: "/examples/ray_cube_fragment/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters { + required_features: ::required_features(), + required_limits: ::required_limits(), + skips: vec![], + failures: Vec::new(), + required_downlevel_caps: + ::required_downlevel_capabilities(), + }, + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: std::marker::PhantomData::, +}; diff --git a/examples/src/ray_cube_fragment/screenshot.png b/examples/src/ray_cube_fragment/screenshot.png new file mode 100644 index 0000000000..5e95786759 Binary files /dev/null and b/examples/src/ray_cube_fragment/screenshot.png differ diff --git a/examples/src/ray_cube_fragment/shader.wgsl b/examples/src/ray_cube_fragment/shader.wgsl new file mode 100644 index 0000000000..de331770ae --- /dev/null +++ b/examples/src/ray_cube_fragment/shader.wgsl @@ -0,0 +1,74 @@ +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) tex_coords: vec2, +}; + +// meant to be called with 3 vertex indices: 0, 1, 2 +// draws one large triangle over the clip space like this: +// (the asterisks represent the clip space bounds) +//-1,1 1,1 +// --------------------------------- +// | * . +// | * . +// | * . +// | * . +// | * . +// | * . +// |*************** +// | . 1,-1 +// | . +// | . +// | . +// | . +// |. +@vertex +fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { + var result: VertexOutput; + let x = i32(vertex_index) / 2; + let y = i32(vertex_index) & 1; + let tc = vec2( + f32(x) * 2.0, + f32(y) * 2.0 + ); + result.position = vec4( + tc.x * 2.0 - 1.0, + 1.0 - tc.y * 2.0, + 0.0, 1.0 + ); + result.tex_coords = tc; + return result; +} + +struct Uniforms { + view_inv: mat4x4, + proj_inv: mat4x4, +}; + +@group(0) @binding(0) +var uniforms: Uniforms; + +@group(0) @binding(1) +var acc_struct: acceleration_structure; + +@fragment +fn fs_main(vertex: VertexOutput) -> @location(0) vec4 { + + var color = vec4(vertex.tex_coords, 0.0, 1.0); + + let d = vertex.tex_coords * 2.0 - 1.0; + + let origin = (uniforms.view_inv * vec4(0.0,0.0,0.0,1.0)).xyz; + let temp = uniforms.proj_inv * vec4(d.x, d.y, 1.0, 1.0); + let direction = (uniforms.view_inv * vec4(normalize(temp.xyz), 0.0)).xyz; + + var rq: ray_query; + rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.1, 200.0, origin, direction)); + rayQueryProceed(&rq); + + let intersection = rayQueryGetCommittedIntersection(&rq); + if (intersection.kind != RAY_QUERY_INTERSECTION_NONE) { + color = vec4(intersection.barycentrics, 1.0 - intersection.barycentrics.x - intersection.barycentrics.y, 1.0); + } + + return color; // vec4(vertex.tex_coords, 1.0, 1.0); +} diff --git a/examples/src/ray_scene/cube.mtl b/examples/src/ray_scene/cube.mtl new file mode 100644 index 0000000000..59994638a4 --- /dev/null +++ b/examples/src/ray_scene/cube.mtl @@ -0,0 +1,20 @@ +# Blender MTL File: 'None' +# Material Count: 2 + +newmtl Material +Ns 250.000000 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.009087 0.800000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 + +newmtl None +Ns 500 +Ka 0.8 0.8 0.8 +Kd 0.8 0.8 0.8 +Ks 0.8 0.8 0.8 +d 1 +illum 2 diff --git a/examples/src/ray_scene/cube.obj b/examples/src/ray_scene/cube.obj new file mode 100644 index 0000000000..855a249d94 --- /dev/null +++ b/examples/src/ray_scene/cube.obj @@ -0,0 +1,2587 @@ +# Blender v3.3.1 OBJ File: '' +# www.blender.org +mtllib cube.mtl +o Cube +v 1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 1.000000 +v -1.000000 1.000000 -1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 1.000000 +vt 0.875000 0.500000 +vt 0.625000 0.750000 +vt 0.625000 0.500000 +vt 0.375000 1.000000 +vt 0.375000 0.750000 +vt 0.625000 0.000000 +vt 0.375000 0.250000 +vt 0.375000 0.000000 +vt 0.375000 0.500000 +vt 0.125000 0.750000 +vt 0.125000 0.500000 +vt 0.625000 0.250000 +vt 0.875000 0.750000 +vt 0.625000 1.000000 +vn 0.0000 1.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +usemtl Material +s off +f 5/1/1 3/2/1 1/3/1 +f 3/2/2 8/4/2 4/5/2 +f 7/6/3 6/7/3 8/8/3 +f 2/9/4 8/10/4 6/11/4 +f 1/3/5 4/5/5 2/9/5 +f 5/12/6 2/9/6 6/7/6 +f 5/1/1 7/13/1 3/2/1 +f 3/2/2 7/14/2 8/4/2 +f 7/6/3 5/12/3 6/7/3 +f 2/9/4 4/5/4 8/10/4 +f 1/3/5 3/2/5 4/5/5 +f 5/12/6 1/3/6 2/9/6 +o Suzanne +v 0.437500 2.679682 0.765625 +v -0.437500 2.679682 0.765625 +v 0.500000 2.609370 0.687500 +v -0.500000 2.609370 0.687500 +v 0.546875 2.570307 0.578125 +v -0.546875 2.570307 0.578125 +v 0.351562 2.492182 0.617188 +v -0.351562 2.492182 0.617188 +v 0.351562 2.546870 0.718750 +v -0.351562 2.546870 0.718750 +v 0.351562 2.648432 0.781250 +v -0.351562 2.648432 0.781250 +v 0.273438 2.679682 0.796875 +v -0.273438 2.679682 0.796875 +v 0.203125 2.609370 0.742188 +v -0.203125 2.609370 0.742188 +v 0.156250 2.570307 0.648438 +v -0.156250 2.570307 0.648438 +v 0.078125 2.757807 0.656250 +v -0.078125 2.757807 0.656250 +v 0.140625 2.757807 0.742188 +v -0.140625 2.757807 0.742188 +v 0.242188 2.757807 0.796875 +v -0.242188 2.757807 0.796875 +v 0.273438 2.843745 0.796875 +v -0.273438 2.843745 0.796875 +v 0.203125 2.906245 0.742188 +v -0.203125 2.906245 0.742188 +v 0.156250 2.953120 0.648438 +v -0.156250 2.953120 0.648438 +v 0.351562 3.031245 0.617188 +v -0.351562 3.031245 0.617188 +v 0.351562 2.968745 0.718750 +v -0.351562 2.968745 0.718750 +v 0.351562 2.874995 0.781250 +v -0.351562 2.874995 0.781250 +v 0.437500 2.843745 0.765625 +v -0.437500 2.843745 0.765625 +v 0.500000 2.906245 0.687500 +v -0.500000 2.906245 0.687500 +v 0.546875 2.953120 0.578125 +v -0.546875 2.953120 0.578125 +v 0.625000 2.757807 0.562500 +v -0.625000 2.757807 0.562500 +v 0.562500 2.757807 0.671875 +v -0.562500 2.757807 0.671875 +v 0.468750 2.757807 0.757812 +v -0.468750 2.757807 0.757812 +v 0.476562 2.757807 0.773438 +v -0.476562 2.757807 0.773438 +v 0.445312 2.851557 0.781250 +v -0.445312 2.851557 0.781250 +v 0.351562 2.890620 0.804688 +v -0.351562 2.890620 0.804688 +v 0.265625 2.851557 0.820312 +v -0.265625 2.851557 0.820312 +v 0.226562 2.757807 0.820312 +v -0.226562 2.757807 0.820312 +v 0.265625 2.671870 0.820312 +v -0.265625 2.671870 0.820312 +v 0.351562 2.757807 0.828125 +v -0.351562 2.757807 0.828125 +v 0.351562 2.632807 0.804688 +v -0.351562 2.632807 0.804688 +v 0.445312 2.671870 0.781250 +v -0.445312 2.671870 0.781250 +v 0.000000 2.945307 0.742188 +v 0.000000 2.867182 0.820312 +v 0.000000 1.835932 0.734375 +v 0.000000 2.195307 0.781250 +v 0.000000 2.328120 0.796875 +v 0.000000 1.742182 0.718750 +v 0.000000 2.921870 0.601562 +v 0.000000 3.085932 0.570312 +v 0.000000 3.414057 -0.546875 +v 0.000000 3.078120 -0.851562 +v 0.000000 2.585932 -0.828125 +v 0.000000 2.132807 -0.351562 +v 0.203125 2.328120 0.562500 +v -0.203125 2.328120 0.562500 +v 0.312500 2.078120 0.570312 +v -0.312500 2.078120 0.570312 +v 0.351562 1.820307 0.570312 +v -0.351562 1.820307 0.570312 +v 0.367188 1.624995 0.531250 +v -0.367188 1.624995 0.531250 +v 0.328125 1.570307 0.523438 +v -0.328125 1.570307 0.523438 +v 0.179688 1.546870 0.554688 +v -0.179688 1.546870 0.554688 +v 0.000000 1.531245 0.578125 +v 0.437500 2.374995 0.531250 +v -0.437500 2.374995 0.531250 +v 0.632812 2.476557 0.539062 +v -0.632812 2.476557 0.539062 +v 0.828125 2.664057 0.445312 +v -0.828125 2.664057 0.445312 +v 0.859375 2.945307 0.593750 +v -0.859375 2.945307 0.593750 +v 0.710938 2.999995 0.625000 +v -0.710938 2.999995 0.625000 +v 0.492188 3.117182 0.687500 +v -0.492188 3.117182 0.687500 +v 0.320312 3.273432 0.734375 +v -0.320312 3.273432 0.734375 +v 0.156250 3.234370 0.757812 +v -0.156250 3.234370 0.757812 +v 0.062500 3.007807 0.750000 +v -0.062500 3.007807 0.750000 +v 0.164062 2.929682 0.773438 +v -0.164062 2.929682 0.773438 +v 0.125000 2.820307 0.765625 +v -0.125000 2.820307 0.765625 +v 0.203125 2.609370 0.742188 +v -0.203125 2.609370 0.742188 +v 0.375000 2.531245 0.703125 +v -0.375000 2.531245 0.703125 +v 0.492188 2.578120 0.671875 +v -0.492188 2.578120 0.671875 +v 0.625000 2.703120 0.648438 +v -0.625000 2.703120 0.648438 +v 0.640625 2.812495 0.648438 +v -0.640625 2.812495 0.648438 +v 0.601562 2.890620 0.664062 +v -0.601562 2.890620 0.664062 +v 0.429688 2.953120 0.718750 +v -0.429688 2.953120 0.718750 +v 0.250000 2.984370 0.757812 +v -0.250000 2.984370 0.757812 +v 0.000000 1.749995 0.734375 +v 0.109375 1.796870 0.734375 +v -0.109375 1.796870 0.734375 +v 0.117188 1.679682 0.710938 +v -0.117188 1.679682 0.710938 +v 0.062500 1.632807 0.695312 +v -0.062500 1.632807 0.695312 +v 0.000000 1.624995 0.687500 +v 0.000000 2.320307 0.750000 +v 0.000000 2.374995 0.742188 +v 0.101562 2.367182 0.742188 +v -0.101562 2.367182 0.742188 +v 0.125000 2.289057 0.750000 +v -0.125000 2.289057 0.750000 +v 0.085938 2.226557 0.742188 +v -0.085938 2.226557 0.742188 +v 0.398438 2.468745 0.671875 +v -0.398438 2.468745 0.671875 +v 0.617188 2.570307 0.625000 +v -0.617188 2.570307 0.625000 +v 0.726562 2.718745 0.601562 +v -0.726562 2.718745 0.601562 +v 0.742188 2.890620 0.656250 +v -0.742188 2.890620 0.656250 +v 0.687500 2.929682 0.726562 +v -0.687500 2.929682 0.726562 +v 0.437500 3.062495 0.796875 +v -0.437500 3.062495 0.796875 +v 0.312500 3.156245 0.835938 +v -0.312500 3.156245 0.835938 +v 0.203125 3.132807 0.851562 +v -0.203125 3.132807 0.851562 +v 0.101562 2.945307 0.843750 +v -0.101562 2.945307 0.843750 +v 0.125000 2.414057 0.812500 +v -0.125000 2.414057 0.812500 +v 0.210938 2.070307 0.710938 +v -0.210938 2.070307 0.710938 +v 0.250000 1.812495 0.687500 +v -0.250000 1.812495 0.687500 +v 0.265625 1.695307 0.664062 +v -0.265625 1.695307 0.664062 +v 0.234375 1.601557 0.632812 +v -0.234375 1.601557 0.632812 +v 0.164062 1.585932 0.632812 +v -0.164062 1.585932 0.632812 +v 0.000000 1.570307 0.640625 +v 0.000000 2.562495 0.726562 +v 0.000000 2.726557 0.765625 +v 0.328125 2.992182 0.742188 +v -0.328125 2.992182 0.742188 +v 0.164062 2.656245 0.750000 +v -0.164062 2.656245 0.750000 +v 0.132812 2.726557 0.757812 +v -0.132812 2.726557 0.757812 +v 0.117188 1.828120 0.734375 +v -0.117188 1.828120 0.734375 +v 0.078125 2.070307 0.750000 +v -0.078125 2.070307 0.750000 +v 0.000000 2.070307 0.750000 +v 0.000000 2.187495 0.742188 +v 0.093750 2.242182 0.781250 +v -0.093750 2.242182 0.781250 +v 0.132812 2.289057 0.796875 +v -0.132812 2.289057 0.796875 +v 0.109375 2.382807 0.781250 +v -0.109375 2.382807 0.781250 +v 0.039062 2.390620 0.781250 +v -0.039062 2.390620 0.781250 +v 0.000000 2.312495 0.828125 +v 0.046875 2.367182 0.812500 +v -0.046875 2.367182 0.812500 +v 0.093750 2.359370 0.812500 +v -0.093750 2.359370 0.812500 +v 0.109375 2.289057 0.828125 +v -0.109375 2.289057 0.828125 +v 0.078125 2.265620 0.804688 +v -0.078125 2.265620 0.804688 +v 0.000000 2.226557 0.804688 +v 0.257812 2.203120 0.554688 +v -0.257812 2.203120 0.554688 +v 0.164062 2.273432 0.710938 +v -0.164062 2.273432 0.710938 +v 0.179688 2.203120 0.710938 +v -0.179688 2.203120 0.710938 +v 0.234375 2.265620 0.554688 +v -0.234375 2.265620 0.554688 +v 0.000000 1.640620 0.687500 +v 0.046875 1.648432 0.687500 +v -0.046875 1.648432 0.687500 +v 0.093750 1.695307 0.710938 +v -0.093750 1.695307 0.710938 +v 0.093750 1.773432 0.726562 +v -0.093750 1.773432 0.726562 +v 0.000000 1.734370 0.656250 +v 0.093750 1.765620 0.664062 +v -0.093750 1.765620 0.664062 +v 0.093750 1.703120 0.640625 +v -0.093750 1.703120 0.640625 +v 0.046875 1.664057 0.632812 +v -0.046875 1.664057 0.632812 +v 0.000000 1.656245 0.632812 +v 0.171875 2.734370 0.781250 +v -0.171875 2.734370 0.781250 +v 0.187500 2.671870 0.773438 +v -0.187500 2.671870 0.773438 +v 0.335938 2.945307 0.757812 +v -0.335938 2.945307 0.757812 +v 0.273438 2.937495 0.773438 +v -0.273438 2.937495 0.773438 +v 0.421875 2.914057 0.773438 +v -0.421875 2.914057 0.773438 +v 0.562500 2.867182 0.695312 +v -0.562500 2.867182 0.695312 +v 0.585938 2.804682 0.687500 +v -0.585938 2.804682 0.687500 +v 0.578125 2.710932 0.679688 +v -0.578125 2.710932 0.679688 +v 0.476562 2.617182 0.718750 +v -0.476562 2.617182 0.718750 +v 0.375000 2.578120 0.742188 +v -0.375000 2.578120 0.742188 +v 0.226562 2.624995 0.781250 +v -0.226562 2.624995 0.781250 +v 0.179688 2.812495 0.781250 +v -0.179688 2.812495 0.781250 +v 0.210938 2.890620 0.781250 +v -0.210938 2.890620 0.781250 +v 0.234375 2.874995 0.757812 +v -0.234375 2.874995 0.757812 +v 0.195312 2.812495 0.757812 +v -0.195312 2.812495 0.757812 +v 0.242188 2.640620 0.757812 +v -0.242188 2.640620 0.757812 +v 0.375000 2.601557 0.726562 +v -0.375000 2.601557 0.726562 +v 0.460938 2.632807 0.703125 +v -0.460938 2.632807 0.703125 +v 0.546875 2.726557 0.671875 +v -0.546875 2.726557 0.671875 +v 0.554688 2.796870 0.671875 +v -0.554688 2.796870 0.671875 +v 0.531250 2.851557 0.679688 +v -0.531250 2.851557 0.679688 +v 0.414062 2.906245 0.750000 +v -0.414062 2.906245 0.750000 +v 0.281250 2.914057 0.765625 +v -0.281250 2.914057 0.765625 +v 0.335938 2.921870 0.750000 +v -0.335938 2.921870 0.750000 +v 0.203125 2.687495 0.750000 +v -0.203125 2.687495 0.750000 +v 0.195312 2.742182 0.750000 +v -0.195312 2.742182 0.750000 +v 0.109375 2.976557 0.609375 +v -0.109375 2.976557 0.609375 +v 0.195312 3.179682 0.617188 +v -0.195312 3.179682 0.617188 +v 0.335938 3.203120 0.593750 +v -0.335938 3.203120 0.593750 +v 0.484375 3.070307 0.554688 +v -0.484375 3.070307 0.554688 +v 0.679688 2.968745 0.492188 +v -0.679688 2.968745 0.492188 +v 0.796875 2.921870 0.460938 +v -0.796875 2.921870 0.460938 +v 0.773438 2.679682 0.375000 +v -0.773438 2.679682 0.375000 +v 0.601562 2.515620 0.414062 +v -0.601562 2.515620 0.414062 +v 0.437500 2.421870 0.468750 +v -0.437500 2.421870 0.468750 +v 0.000000 3.414057 0.289062 +v 0.000000 3.499995 -0.078125 +v 0.000000 2.320307 -0.671875 +v 0.000000 2.054682 0.187500 +v 0.000000 1.539057 0.460938 +v 0.000000 1.710932 0.343750 +v 0.000000 1.945307 0.320312 +v 0.000000 2.031245 0.281250 +v 0.851562 2.749995 0.054688 +v -0.851562 2.749995 0.054688 +v 0.859375 2.835932 -0.046875 +v -0.859375 2.835932 -0.046875 +v 0.773438 2.781245 -0.437500 +v -0.773438 2.781245 -0.437500 +v 0.460938 2.953120 -0.703125 +v -0.460938 2.953120 -0.703125 +v 0.734375 2.468745 0.070312 +v -0.734375 2.468745 0.070312 +v 0.593750 2.390620 -0.164062 +v -0.593750 2.390620 -0.164062 +v 0.640625 2.507807 -0.429688 +v -0.640625 2.507807 -0.429688 +v 0.335938 2.570307 -0.664062 +v -0.335938 2.570307 -0.664062 +v 0.234375 2.164057 0.406250 +v -0.234375 2.164057 0.406250 +v 0.179688 2.101557 0.257812 +v -0.179688 2.101557 0.257812 +v 0.289062 1.804682 0.382812 +v -0.289062 1.804682 0.382812 +v 0.250000 2.015620 0.390625 +v -0.250000 2.015620 0.390625 +v 0.328125 1.601557 0.398438 +v -0.328125 1.601557 0.398438 +v 0.140625 1.757807 0.367188 +v -0.140625 1.757807 0.367188 +v 0.125000 1.976557 0.359375 +v -0.125000 1.976557 0.359375 +v 0.164062 1.570307 0.437500 +v -0.164062 1.570307 0.437500 +v 0.218750 2.234370 0.429688 +v -0.218750 2.234370 0.429688 +v 0.210938 2.289057 0.468750 +v -0.210938 2.289057 0.468750 +v 0.203125 2.343745 0.500000 +v -0.203125 2.343745 0.500000 +v 0.210938 2.124995 0.164062 +v -0.210938 2.124995 0.164062 +v 0.296875 2.203120 -0.265625 +v -0.296875 2.203120 -0.265625 +v 0.343750 2.367182 -0.539062 +v -0.343750 2.367182 -0.539062 +v 0.453125 3.382807 -0.382812 +v -0.453125 3.382807 -0.382812 +v 0.453125 3.445307 -0.070312 +v -0.453125 3.445307 -0.070312 +v 0.453125 3.367182 0.234375 +v -0.453125 3.367182 0.234375 +v 0.460938 3.039057 0.429688 +v -0.460938 3.039057 0.429688 +v 0.726562 2.921870 0.335938 +v -0.726562 2.921870 0.335938 +v 0.632812 2.968745 0.281250 +v -0.632812 2.968745 0.281250 +v 0.640625 3.218745 0.054688 +v -0.640625 3.218745 0.054688 +v 0.796875 3.078120 0.125000 +v -0.796875 3.078120 0.125000 +v 0.796875 3.132807 -0.117188 +v -0.796875 3.132807 -0.117188 +v 0.640625 3.265620 -0.195312 +v -0.640625 3.265620 -0.195312 +v 0.640625 3.195307 -0.445312 +v -0.640625 3.195307 -0.445312 +v 0.796875 3.054682 -0.359375 +v -0.796875 3.054682 -0.359375 +v 0.617188 2.843745 -0.585938 +v -0.617188 2.843745 -0.585938 +v 0.484375 2.539057 -0.546875 +v -0.484375 2.539057 -0.546875 +v 0.820312 2.843745 -0.203125 +v -0.820312 2.843745 -0.203125 +v 0.406250 2.343745 0.148438 +v -0.406250 2.343745 0.148438 +v 0.429688 2.320307 -0.210938 +v -0.429688 2.320307 -0.210938 +v 0.890625 2.921870 -0.234375 +v -0.890625 2.921870 -0.234375 +v 0.773438 2.374995 -0.125000 +v -0.773438 2.374995 -0.125000 +v 1.039062 2.414057 -0.328125 +v -1.039062 2.414057 -0.328125 +v 1.281250 2.570307 -0.429688 +v -1.281250 2.570307 -0.429688 +v 1.351562 2.835932 -0.421875 +v -1.351562 2.835932 -0.421875 +v 1.234375 3.023432 -0.421875 +v -1.234375 3.023432 -0.421875 +v 1.023438 2.992182 -0.312500 +v -1.023438 2.992182 -0.312500 +v 1.015625 2.929682 -0.289062 +v -1.015625 2.929682 -0.289062 +v 1.187500 2.953120 -0.390625 +v -1.187500 2.953120 -0.390625 +v 1.265625 2.804682 -0.406250 +v -1.265625 2.804682 -0.406250 +v 1.210938 2.593745 -0.406250 +v -1.210938 2.593745 -0.406250 +v 1.031250 2.476557 -0.304688 +v -1.031250 2.476557 -0.304688 +v 0.828125 2.445307 -0.132812 +v -0.828125 2.445307 -0.132812 +v 0.921875 2.874995 -0.218750 +v -0.921875 2.874995 -0.218750 +v 0.945312 2.820307 -0.289062 +v -0.945312 2.820307 -0.289062 +v 0.882812 2.492182 -0.210938 +v -0.882812 2.492182 -0.210938 +v 1.039062 2.515620 -0.367188 +v -1.039062 2.515620 -0.367188 +v 1.187500 2.609370 -0.445312 +v -1.187500 2.609370 -0.445312 +v 1.234375 2.765620 -0.445312 +v -1.234375 2.765620 -0.445312 +v 1.171875 2.874995 -0.437500 +v -1.171875 2.874995 -0.437500 +v 1.023438 2.859370 -0.359375 +v -1.023438 2.859370 -0.359375 +v 0.843750 2.804682 -0.210938 +v -0.843750 2.804682 -0.210938 +v 0.835938 2.687495 -0.273438 +v -0.835938 2.687495 -0.273438 +v 0.757812 2.609370 -0.273438 +v -0.757812 2.609370 -0.273438 +v 0.820312 2.601557 -0.273438 +v -0.820312 2.601557 -0.273438 +v 0.843750 2.531245 -0.273438 +v -0.843750 2.531245 -0.273438 +v 0.812500 2.499995 -0.273438 +v -0.812500 2.499995 -0.273438 +v 0.726562 2.515620 -0.070312 +v -0.726562 2.515620 -0.070312 +v 0.718750 2.492182 -0.171875 +v -0.718750 2.492182 -0.171875 +v 0.718750 2.554682 -0.187500 +v -0.718750 2.554682 -0.187500 +v 0.796875 2.718745 -0.210938 +v -0.796875 2.718745 -0.210938 +v 0.890625 2.757807 -0.265625 +v -0.890625 2.757807 -0.265625 +v 0.890625 2.749995 -0.320312 +v -0.890625 2.749995 -0.320312 +v 0.812500 2.499995 -0.320312 +v -0.812500 2.499995 -0.320312 +v 0.851562 2.531245 -0.320312 +v -0.851562 2.531245 -0.320312 +v 0.828125 2.593745 -0.320312 +v -0.828125 2.593745 -0.320312 +v 0.765625 2.609370 -0.320312 +v -0.765625 2.609370 -0.320312 +v 0.843750 2.687495 -0.320312 +v -0.843750 2.687495 -0.320312 +v 1.039062 2.843745 -0.414062 +v -1.039062 2.843745 -0.414062 +v 1.187500 2.859370 -0.484375 +v -1.187500 2.859370 -0.484375 +v 1.257812 2.757807 -0.492188 +v -1.257812 2.757807 -0.492188 +v 1.210938 2.601557 -0.484375 +v -1.210938 2.601557 -0.484375 +v 1.046875 2.515620 -0.421875 +v -1.046875 2.515620 -0.421875 +v 0.882812 2.499995 -0.265625 +v -0.882812 2.499995 -0.265625 +v 0.953125 2.804682 -0.343750 +v -0.953125 2.804682 -0.343750 +v 0.890625 2.624995 -0.328125 +v -0.890625 2.624995 -0.328125 +v 0.937500 2.578120 -0.335938 +v -0.937500 2.578120 -0.335938 +v 1.000000 2.640620 -0.367188 +v -1.000000 2.640620 -0.367188 +v 0.960938 2.687495 -0.351562 +v -0.960938 2.687495 -0.351562 +v 1.015625 2.749995 -0.375000 +v -1.015625 2.749995 -0.375000 +v 1.054688 2.703120 -0.382812 +v -1.054688 2.703120 -0.382812 +v 1.109375 2.726557 -0.390625 +v -1.109375 2.726557 -0.390625 +v 1.085938 2.789057 -0.390625 +v -1.085938 2.789057 -0.390625 +v 1.023438 2.953120 -0.484375 +v -1.023438 2.953120 -0.484375 +v 1.250000 2.984370 -0.546875 +v -1.250000 2.984370 -0.546875 +v 1.367188 2.812495 -0.500000 +v -1.367188 2.812495 -0.500000 +v 1.312500 2.570307 -0.531250 +v -1.312500 2.570307 -0.531250 +v 1.039062 2.429682 -0.492188 +v -1.039062 2.429682 -0.492188 +v 0.789062 2.390620 -0.328125 +v -0.789062 2.390620 -0.328125 +v 0.859375 2.898432 -0.382812 +v -0.859375 2.898432 -0.382812 +vt 0.890955 0.590063 +vt 0.860081 0.560115 +vt 0.904571 0.559404 +vt 0.856226 0.850547 +vt 0.888398 0.821999 +vt 0.900640 0.853232 +vt 0.853018 0.521562 +vt 0.920166 0.524546 +vt 0.847458 0.888748 +vt 0.914672 0.888748 +vt 0.798481 0.569535 +vt 0.795104 0.838402 +vt 0.870622 0.589649 +vt 0.828900 0.590771 +vt 0.826436 0.818537 +vt 0.868067 0.821510 +vt 0.854402 0.604754 +vt 0.828171 0.633354 +vt 0.827598 0.775964 +vt 0.852534 0.805700 +vt 0.791018 0.645443 +vt 0.791018 0.762238 +vt 0.855181 0.668527 +vt 0.856142 0.742025 +vt 0.844839 0.707525 +vt 0.854107 0.625459 +vt 0.853157 0.785002 +vt 0.867508 0.642291 +vt 0.900375 0.666964 +vt 0.901223 0.745592 +vt 0.867293 0.768782 +vt 0.842358 0.702491 +vt 0.921180 0.713713 +vt 0.931889 0.636832 +vt 0.918898 0.699697 +vt 0.931368 0.777093 +vt 0.968213 0.770220 +vt 0.905882 0.627902 +vt 0.890474 0.641909 +vt 0.904990 0.784860 +vt 0.906232 0.605742 +vt 0.904357 0.807013 +vt 0.931250 0.820926 +vt 0.933717 0.593037 +vt 0.968392 0.645333 +vt 0.965038 0.841671 +vt 0.968392 0.573812 +vt 0.889591 0.593275 +vt 0.887178 0.818729 +vt 0.900583 0.804677 +vt 0.902359 0.607909 +vt 0.898822 0.786233 +vt 0.899781 0.626257 +vt 0.890219 0.770183 +vt 0.887351 0.775442 +vt 0.887842 0.636527 +vt 0.870376 0.775972 +vt 0.859881 0.623942 +vt 0.870908 0.635245 +vt 0.858859 0.786774 +vt 0.859664 0.608186 +vt 0.857942 0.802505 +vt 0.871664 0.593961 +vt 0.869299 0.817249 +vt 0.879400 0.616512 +vt 0.878029 0.795063 +vt 0.536419 0.062072 +vt 0.518916 0.050294 +vt 0.540260 0.053805 +vt 0.501452 0.062043 +vt 0.518925 0.059681 +vt 0.542788 0.064089 +vt 0.551930 0.058338 +vt 0.495083 0.064047 +vt 0.497626 0.053770 +vt 0.555073 0.061900 +vt 0.482805 0.061829 +vt 0.485955 0.058273 +vt 0.563812 0.076586 +vt 0.546290 0.072669 +vt 0.491565 0.072625 +vt 0.474014 0.076511 +vt 0.583135 0.108495 +vt 0.548333 0.084893 +vt 0.489507 0.084858 +vt 0.454527 0.108481 +vt 0.605512 0.165134 +vt 0.621513 0.227818 +vt 0.553118 0.209599 +vt 0.416514 0.229490 +vt 0.432024 0.165644 +vt 0.485339 0.210053 +vt 0.676379 0.233241 +vt 0.647395 0.200502 +vt 0.360308 0.235899 +vt 0.372747 0.256357 +vt 0.683908 0.279995 +vt 0.664761 0.253225 +vt 0.353696 0.284606 +vt 0.707254 0.310054 +vt 0.715342 0.265392 +vt 0.330721 0.316853 +vt 0.351187 0.317440 +vt 0.697446 0.332673 +vt 0.687515 0.311539 +vt 0.341964 0.339667 +vt 0.362723 0.329722 +vt 0.662817 0.372521 +vt 0.676824 0.323937 +vt 0.379297 0.378686 +vt 0.402772 0.362131 +vt 0.618316 0.375151 +vt 0.639050 0.357330 +vt 0.424583 0.379267 +vt 0.604826 0.397804 +vt 0.626842 0.395792 +vt 0.439252 0.401540 +vt 0.442396 0.381222 +vt 0.553095 0.390512 +vt 0.600808 0.377857 +vt 0.490934 0.391862 +vt 0.482938 0.358497 +vt 0.521923 0.386009 +vt 0.559674 0.357011 +vt 0.521086 0.343868 +vt 0.599845 0.344815 +vt 0.577279 0.340156 +vt 0.441977 0.347815 +vt 0.615546 0.342005 +vt 0.634472 0.332311 +vt 0.425972 0.345582 +vt 0.662406 0.312804 +vt 0.406362 0.336480 +vt 0.668440 0.297958 +vt 0.377061 0.317685 +vt 0.664101 0.277872 +vt 0.370304 0.302644 +vt 0.639236 0.253047 +vt 0.374100 0.281778 +vt 0.613992 0.242662 +vt 0.398938 0.255633 +vt 0.572941 0.258564 +vt 0.424464 0.244473 +vt 0.519760 0.248864 +vt 0.466409 0.259709 +vt 0.558527 0.316594 +vt 0.482619 0.317843 +vt 0.520277 0.294764 +vt 0.556923 0.291214 +vt 0.483433 0.292249 +vt 0.563905 0.272007 +vt 0.475886 0.273078 +vt 0.525483 0.068967 +vt 0.512375 0.068956 +vt 0.531231 0.073829 +vt 0.506626 0.073811 +vt 0.531019 0.087431 +vt 0.555621 0.121749 +vt 0.532669 0.090920 +vt 0.505177 0.090908 +vt 0.482177 0.121781 +vt 0.506827 0.087416 +vt 0.518981 0.151749 +vt 0.532042 0.127713 +vt 0.538112 0.158382 +vt 0.505828 0.127728 +vt 0.518941 0.128358 +vt 0.518925 0.093952 +vt 0.518927 0.085180 +vt 0.548362 0.173560 +vt 0.535214 0.166808 +vt 0.502799 0.166857 +vt 0.489683 0.173693 +vt 0.499851 0.158434 +vt 0.544281 0.193366 +vt 0.537959 0.175966 +vt 0.500100 0.176033 +vt 0.493996 0.193428 +vt 0.528757 0.191785 +vt 0.519841 0.200843 +vt 0.509219 0.191626 +vt 0.500890 0.187571 +vt 0.519132 0.185382 +vt 0.517577 0.190607 +vt 0.518998 0.159028 +vt 0.519016 0.165599 +vt 0.506910 0.171667 +vt 0.528222 0.186316 +vt 0.509787 0.186260 +vt 0.533528 0.184215 +vt 0.537248 0.187577 +vt 0.504547 0.184206 +vt 0.504604 0.176791 +vt 0.531131 0.171631 +vt 0.533449 0.176739 +vt 0.519099 0.179457 +vt 0.561572 0.167779 +vt 0.476363 0.167996 +vt 0.478371 0.149447 +vt 0.559475 0.149319 +vt 0.596138 0.133426 +vt 0.441395 0.133592 +vt 0.601169 0.147885 +vt 0.436337 0.148194 +vt 0.528933 0.084957 +vt 0.508915 0.084945 +vt 0.518925 0.083865 +vt 0.529036 0.075429 +vt 0.508820 0.075415 +vt 0.523751 0.070508 +vt 0.514106 0.070501 +vt 0.518928 0.067899 +vt 0.518929 0.069468 +vt 0.518928 0.074259 +vt 0.516297 0.074966 +vt 0.524236 0.076691 +vt 0.521560 0.074970 +vt 0.513619 0.076684 +vt 0.524601 0.079886 +vt 0.513252 0.079879 +vt 0.518926 0.079331 +vt 0.571787 0.277295 +vt 0.568351 0.292904 +vt 0.468070 0.278617 +vt 0.471978 0.294282 +vt 0.573085 0.311386 +vt 0.467790 0.313081 +vt 0.584855 0.327708 +vt 0.456477 0.329961 +vt 0.458737 0.268049 +vt 0.611720 0.255725 +vt 0.580734 0.266620 +vt 0.427062 0.257728 +vt 0.632494 0.262853 +vt 0.406068 0.265508 +vt 0.653658 0.279971 +vt 0.384904 0.283634 +vt 0.656064 0.297636 +vt 0.383015 0.301864 +vt 0.386858 0.314615 +vt 0.652752 0.310186 +vt 0.411556 0.327673 +vt 0.614408 0.331972 +vt 0.629040 0.323864 +vt 0.426727 0.335361 +vt 0.601033 0.333624 +vt 0.440344 0.336537 +vt 0.601799 0.328453 +vt 0.439372 0.331331 +vt 0.450408 0.323919 +vt 0.613335 0.327083 +vt 0.427623 0.330358 +vt 0.626851 0.320513 +vt 0.413648 0.324175 +vt 0.646248 0.306421 +vt 0.393381 0.310510 +vt 0.649541 0.296225 +vt 0.389662 0.300183 +vt 0.647785 0.283486 +vt 0.391040 0.287071 +vt 0.629829 0.267263 +vt 0.408893 0.269959 +vt 0.612641 0.261560 +vt 0.426254 0.263693 +vt 0.585166 0.270991 +vt 0.454369 0.272583 +vt 0.578124 0.281900 +vt 0.461798 0.283441 +vt 0.579548 0.309340 +vt 0.590644 0.321516 +vt 0.461204 0.311233 +vt 0.577524 0.293776 +vt 0.462754 0.295432 +vt 0.553209 0.433063 +vt 0.523031 0.433628 +vt 0.492809 0.434538 +vt 0.609819 0.431516 +vt 0.435860 0.435740 +vt 0.416915 0.400552 +vt 0.396518 0.425416 +vt 0.648174 0.419316 +vt 0.350292 0.396229 +vt 0.692106 0.388274 +vt 0.312756 0.350588 +vt 0.735879 0.312112 +vt 0.726332 0.341754 +vt 0.301067 0.320593 +vt 0.320452 0.270303 +vt 0.304876 0.261087 +vt 0.698172 0.216906 +vt 0.729900 0.256393 +vt 0.337414 0.219179 +vt 0.663103 0.190671 +vt 0.373474 0.191872 +vt 0.649444 0.022378 +vt 0.621440 0.048089 +vt 0.626908 0.015608 +vt 0.388827 0.021586 +vt 0.416419 0.047631 +vt 0.376796 0.075296 +vt 0.577206 0.032801 +vt 0.567460 0.000144 +vt 0.411318 0.015131 +vt 0.460782 0.032656 +vt 0.547413 0.041724 +vt 0.518922 0.024886 +vt 0.470636 0.000144 +vt 0.490511 0.041669 +vt 0.558059 0.053871 +vt 0.479842 0.053785 +vt 0.576951 0.057998 +vt 0.460920 0.057845 +vt 0.611687 0.078268 +vt 0.425932 0.077985 +vt 0.660451 0.076084 +vt 0.626663 0.111357 +vt 0.410618 0.111244 +vt 0.629482 0.130456 +vt 0.407648 0.130594 +vt 0.413741 0.147158 +vt 0.619303 0.159841 +vt 0.418035 0.160361 +vt 0.389677 0.201890 +vt 0.886245 0.121777 +vt 0.891780 0.036916 +vt 0.945900 0.079569 +vt 0.141314 0.112482 +vt 0.142277 0.021467 +vt 0.183115 0.092127 +vt 0.849114 0.099732 +vt 0.805584 0.010786 +vt 0.232648 0.003484 +vt 0.246353 0.076510 +vt 0.687018 0.077204 +vt 0.672384 0.022201 +vt 0.349875 0.075955 +vt 0.365979 0.020991 +vt 0.760215 0.193244 +vt 0.789046 0.233323 +vt 0.271553 0.193871 +vt 0.241255 0.236977 +vt 0.909112 0.183261 +vt 0.994525 0.167705 +vt 0.107928 0.179083 +vt 0.078961 0.060719 +vt 0.862868 0.338556 +vt 0.962901 0.344752 +vt 0.911671 0.402429 +vt 0.160557 0.356821 +vt 0.043968 0.367038 +vt 0.123776 0.315519 +vt 0.915360 0.259804 +vt 0.999856 0.254640 +vt 0.098965 0.266968 +vt 0.000144 0.259113 +vt 0.011829 0.155367 +vt 0.749542 0.334683 +vt 0.766337 0.300809 +vt 0.789162 0.313727 +vt 0.267408 0.310142 +vt 0.288183 0.346496 +vt 0.242992 0.325552 +vt 0.815314 0.276388 +vt 0.846174 0.293397 +vt 0.213065 0.285164 +vt 0.178537 0.304983 +vt 0.845007 0.256352 +vt 0.873517 0.265922 +vt 0.179662 0.263312 +vt 0.147089 0.274284 +vt 0.859075 0.228168 +vt 0.886999 0.233769 +vt 0.162803 0.231720 +vt 0.131514 0.237587 +vt 0.875030 0.184705 +vt 0.842355 0.195160 +vt 0.145224 0.182749 +vt 0.894128 0.301884 +vt 0.794286 0.364062 +vt 0.770185 0.379538 +vt 0.239776 0.382592 +vt 0.845499 0.449967 +vt 0.106400 0.432652 +vt 0.815858 0.445381 +vt 0.755700 0.418603 +vt 0.287033 0.442912 +vt 0.219260 0.477186 +vt 0.268122 0.398737 +vt 0.185281 0.484099 +vt 0.819845 0.468071 +vt 0.215894 0.503605 +vt 0.809631 0.233887 +vt 0.219168 0.237388 +vt 0.829287 0.219562 +vt 0.199067 0.222464 +vt 0.788458 0.080826 +vt 0.715482 0.139727 +vt 0.319538 0.139409 +vt 0.246666 0.114850 +vt 0.785486 0.152330 +vt 0.245969 0.151002 +vt 0.623495 0.146796 +vt 0.837382 0.156361 +vt 0.196622 0.155241 +vt 0.171653 0.132294 +vt 0.786480 0.117591 +vt 0.858171 0.137775 +vt 0.432388 0.894943 +vt 0.491058 0.881714 +vt 0.506166 0.904851 +vt 0.321637 0.893225 +vt 0.263032 0.878321 +vt 0.315867 0.868209 +vt 0.572792 0.860484 +vt 0.604825 0.879946 +vt 0.181486 0.854693 +vt 0.247207 0.901159 +vt 0.148729 0.873349 +vt 0.619962 0.791615 +vt 0.136063 0.784093 +vt 0.169745 0.787474 +vt 0.586396 0.793977 +vt 0.563786 0.739211 +vt 0.194086 0.733241 +vt 0.208656 0.740879 +vt 0.549027 0.746412 +vt 0.508270 0.697693 +vt 0.250811 0.693249 +vt 0.258399 0.707497 +vt 0.438641 0.680683 +vt 0.434803 0.658882 +vt 0.320962 0.677959 +vt 0.325318 0.656224 +vt 0.500314 0.711729 +vt 0.452955 0.700023 +vt 0.306136 0.696976 +vt 0.505666 0.730944 +vt 0.252524 0.726592 +vt 0.568148 0.787367 +vt 0.188269 0.781375 +vt 0.214575 0.750414 +vt 0.555495 0.826352 +vt 0.199850 0.820889 +vt 0.501231 0.844356 +vt 0.253846 0.840502 +vt 0.457832 0.840040 +vt 0.297562 0.837358 +vt 0.783193 0.187449 +vt 0.246955 0.187075 +vt 0.233625 0.175620 +vt 0.394766 0.686125 +vt 0.391039 0.611891 +vt 0.364838 0.684445 +vt 0.391747 0.862097 +vt 0.438797 0.870229 +vt 0.363377 0.861308 +vt 0.435018 0.718280 +vt 0.323658 0.715731 +vt 0.384658 0.710299 +vt 0.433669 0.729661 +vt 0.374400 0.708969 +vt 0.410995 0.747662 +vt 0.427812 0.742828 +vt 0.324726 0.727177 +vt 0.347028 0.745816 +vt 0.330270 0.740536 +vt 0.384657 0.795423 +vt 0.418086 0.784946 +vt 0.372270 0.794472 +vt 0.431333 0.817535 +vt 0.401605 0.841460 +vt 0.324790 0.815460 +vt 0.338952 0.783073 +vt 0.354026 0.840297 +vt 0.825107 0.209762 +vt 0.199767 0.214827 +vt 0.816266 0.203086 +vt 0.209828 0.206161 +vt 0.226485 0.183086 +vt 0.796021 0.176969 +vt 0.802192 0.184609 +vt 0.448505 0.804621 +vt 0.473386 0.824700 +vt 0.307886 0.802031 +vt 0.282357 0.821525 +vt 0.321237 0.777208 +vt 0.423718 0.754191 +vt 0.435868 0.779569 +vt 0.334089 0.752045 +vt 0.319919 0.747250 +vt 0.437950 0.749777 +vt 0.312907 0.729222 +vt 0.440995 0.724383 +vt 0.445392 0.731997 +vt 0.317510 0.721697 +vt 0.455277 0.713731 +vt 0.303460 0.710657 +vt 0.512485 0.828811 +vt 0.242975 0.824574 +vt 0.550942 0.811814 +vt 0.204839 0.806417 +vt 0.552139 0.787682 +vt 0.204331 0.782156 +vt 0.539407 0.764539 +vt 0.542850 0.755753 +vt 0.217774 0.759319 +vt 0.508439 0.743135 +vt 0.249419 0.738732 +vt 0.454776 0.761665 +vt 0.302729 0.758742 +vt 0.286960 0.745020 +vt 0.470841 0.748408 +vt 0.475403 0.783904 +vt 0.281439 0.780511 +vt 0.268291 0.766661 +vt 0.503673 0.787562 +vt 0.494476 0.802470 +vt 0.252972 0.783410 +vt 0.261790 0.798626 +vt 0.516802 0.807339 +vt 0.239243 0.802891 +vt 0.237920 0.787045 +vt 0.518562 0.791602 +vt 0.484068 0.628776 +vt 0.543385 0.683538 +vt 0.276936 0.625067 +vt 0.216123 0.678120 +vt 0.581052 0.726933 +vt 0.177176 0.720426 +vt 0.616701 0.759965 +vt 0.140379 0.752377 +vt 0.660647 0.741167 +vt 0.707492 0.759884 +vt 0.097038 0.732052 +vt 0.677256 0.670436 +vt 0.745511 0.652100 +vt 0.049526 0.748824 +vt 0.083564 0.662038 +vt 0.671403 0.592656 +vt 0.740843 0.572428 +vt 0.019409 0.639749 +vt 0.092820 0.589862 +vt 0.834705 0.206959 +vt 0.051216 0.522659 +vt 0.033664 0.564403 +vt 0.620420 0.565675 +vt 0.498072 0.552315 +vt 0.145041 0.562595 +vt 0.264218 0.550140 +vt 0.369913 0.610196 +vt 0.464579 0.342230 +vt 0.176788 0.196179 +vt 0.770572 0.444261 +vt 0.271364 0.473316 +vt 0.488870 0.770464 +vt 0.834578 0.206879 +vn 0.9693 -0.0118 0.2456 +vn 0.6076 -0.5104 0.6085 +vn 0.8001 -0.0028 0.5999 +vn -0.6076 -0.5104 0.6085 +vn -0.9693 -0.0118 0.2456 +vn -0.8001 -0.0028 0.5999 +vn 0.6802 -0.5463 0.4888 +vn 0.8682 -0.0048 0.4961 +vn -0.6802 -0.5463 0.4888 +vn -0.8682 -0.0048 0.4961 +vn 0.1193 -0.8712 0.4763 +vn -0.1193 -0.8712 0.4763 +vn 0.7290 -0.6566 0.1934 +vn 0.0995 -0.7515 0.6522 +vn -0.0995 -0.7515 0.6522 +vn -0.7290 -0.6566 0.1934 +vn 0.0314 -0.9670 0.2529 +vn -0.4563 -0.5362 0.7101 +vn 0.4563 -0.5362 0.7101 +vn -0.0314 -0.9670 0.2529 +vn -0.5539 -0.6332 0.5406 +vn 0.5539 -0.6332 0.5406 +vn -0.6899 -0.0041 0.7239 +vn 0.6899 -0.0041 0.7239 +vn 0.8097 -0.0070 0.5868 +vn -0.6506 -0.6883 0.3210 +vn 0.6506 -0.6883 0.3210 +vn -0.9521 -0.0102 0.3057 +vn -0.4560 0.5222 0.7207 +vn 0.4560 0.5222 0.7207 +vn 0.9521 -0.0102 0.3057 +vn -0.8097 -0.0070 0.5868 +vn 0.5306 0.6258 0.5717 +vn 0.1031 0.7402 0.6644 +vn -0.5306 0.6258 0.5717 +vn -0.1031 0.7402 0.6644 +vn -0.1257 0.8416 0.5253 +vn 0.0258 0.9726 0.2312 +vn -0.6644 0.6821 0.3056 +vn -0.0258 0.9726 0.2312 +vn 0.7364 0.6521 0.1803 +vn -0.7364 0.6521 0.1803 +vn -0.6102 0.4956 0.6181 +vn 0.6102 0.4956 0.6181 +vn 0.1257 0.8416 0.5253 +vn -0.6682 0.5371 0.5148 +vn 0.6682 0.5371 0.5148 +vn 0.9645 -0.0127 0.2639 +vn -0.9645 -0.0127 0.2639 +vn -0.7216 0.6556 0.2224 +vn 0.7216 0.6556 0.2224 +vn -0.0432 0.9389 0.3415 +vn 0.0432 0.9389 0.3415 +vn 0.6644 0.6821 0.3056 +vn 0.6237 0.6285 0.4647 +vn -0.6237 0.6285 0.4647 +vn 0.9270 -0.0130 0.3749 +vn -0.6159 -0.6366 0.4641 +vn -0.9270 -0.0130 0.3749 +vn 0.6159 -0.6366 0.4641 +vn 0.0426 -0.9404 0.3375 +vn -0.0426 -0.9404 0.3375 +vn 0.7152 -0.6625 0.2227 +vn -0.7152 -0.6625 0.2227 +vn 0.1836 -0.0053 0.9830 +vn -0.1836 -0.0053 0.9830 +vn 0.1554 -0.7590 0.6323 +vn 0.0000 -0.9677 0.2523 +vn 0.1596 -0.9753 0.1529 +vn -0.1554 -0.7590 0.6323 +vn 0.0000 -0.7753 0.6316 +vn 0.3502 -0.6392 0.6847 +vn 0.5267 -0.8347 0.1611 +vn -0.3502 -0.6392 0.6847 +vn -0.1596 -0.9753 0.1529 +vn 0.9457 -0.2579 0.1977 +vn -0.9457 -0.2579 0.1977 +vn -0.5267 -0.8347 0.1611 +vn 0.9728 0.1003 0.2087 +vn 0.5557 -0.2264 0.8000 +vn -0.5557 -0.2264 0.8000 +vn -0.9728 0.1003 0.2087 +vn 0.9557 0.2492 0.1565 +vn 0.5652 -0.0297 0.8244 +vn -0.5652 -0.0297 0.8244 +vn -0.9557 0.2492 0.1565 +vn 0.8916 -0.3307 0.3095 +vn 0.3842 -0.5671 0.7286 +vn 0.0402 -0.2722 0.9614 +vn -0.3842 -0.5671 0.7286 +vn -0.8916 -0.3307 0.3095 +vn -0.0402 -0.2722 0.9614 +vn 0.5875 -0.7849 0.1970 +vn 0.3489 -0.9371 -0.0082 +vn -0.5875 -0.7849 0.1970 +vn -0.4991 -0.3761 0.7807 +vn 0.5666 -0.3188 0.7598 +vn 0.4991 -0.3761 0.7807 +vn -0.5666 -0.3188 0.7598 +vn 0.8451 0.4434 0.2985 +vn 0.9070 -0.4009 -0.1290 +vn -0.8451 0.4434 0.2985 +vn -0.4607 -0.1448 0.8757 +vn 0.5171 0.8291 0.2125 +vn 0.4607 -0.1448 0.8757 +vn -0.5171 0.8291 0.2125 +vn -0.4801 -0.1833 0.8578 +vn 0.5976 0.7847 0.1646 +vn 0.4801 -0.1833 0.8578 +vn -0.5976 0.7847 0.1646 +vn -0.3085 0.0039 0.9512 +vn 0.2666 0.2166 0.9392 +vn 0.3085 0.0039 0.9512 +vn -0.2666 0.2166 0.9392 +vn -0.6051 0.7680 0.2098 +vn 0.2313 0.9570 0.1751 +vn 0.6051 0.7680 0.2098 +vn 0.1574 0.1660 0.9735 +vn -0.8242 0.5468 0.1473 +vn -0.1574 0.1660 0.9735 +vn 0.8242 0.5468 0.1473 +vn 0.0611 -0.0253 0.9978 +vn 0.0000 0.9636 0.2673 +vn -0.0611 -0.0253 0.9978 +vn 0.0000 -0.0827 0.9966 +vn 0.2582 -0.1265 0.9578 +vn 0.3679 -0.2836 0.8856 +vn -0.2582 -0.1265 0.9578 +vn 0.1490 -0.1542 0.9767 +vn 0.2190 0.0372 0.9750 +vn -0.1490 -0.1542 0.9767 +vn 0.2254 -0.3608 0.9050 +vn -0.2190 0.0372 0.9750 +vn 0.3588 -0.1192 0.9258 +vn -0.2254 -0.3608 0.9050 +vn 0.4602 -0.1651 0.8723 +vn -0.3588 -0.1192 0.9258 +vn 0.4279 -0.3895 0.8156 +vn -0.4602 -0.1651 0.8723 +vn 0.3322 -0.3667 0.8690 +vn -0.4279 -0.3895 0.8156 +vn -0.1522 -0.2549 0.9549 +vn -0.3322 -0.3667 0.8690 +vn -0.0000 0.0643 0.9979 +vn 0.1522 -0.2549 0.9549 +vn 0.0316 -0.1782 0.9835 +vn -0.0316 -0.1782 0.9835 +vn -0.0000 -0.2220 0.9750 +vn -0.2006 -0.1350 0.9703 +vn 0.2006 -0.1350 0.9703 +vn -0.2393 -0.3012 0.9230 +vn 0.2393 -0.3012 0.9230 +vn -0.0589 -0.3784 0.9238 +vn 0.0589 -0.3784 0.9238 +vn 0.1307 -0.3187 0.9388 +vn -0.1307 -0.3187 0.9388 +vn 0.1460 -0.1202 0.9820 +vn 0.5937 0.1082 0.7974 +vn 0.1815 -0.0452 0.9823 +vn -0.1815 -0.0452 0.9823 +vn -0.5937 0.1082 0.7974 +vn -0.1460 -0.1202 0.9820 +vn 0.0000 -0.4760 0.8795 +vn 0.1341 0.0063 0.9909 +vn 0.5003 -0.4293 0.7520 +vn -0.1341 0.0063 0.9909 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -0.0341 0.9994 +vn -0.0000 -0.5870 0.8096 +vn 0.9304 -0.1242 0.3448 +vn 0.5836 -0.6929 0.4235 +vn -0.5836 -0.6929 0.4235 +vn -0.9304 -0.1242 0.3448 +vn -0.5003 -0.4293 0.7520 +vn 0.4931 -0.3412 0.8002 +vn 0.9306 -0.2353 0.2804 +vn -0.9306 -0.2353 0.2804 +vn -0.4931 -0.3412 0.8002 +vn -0.2405 0.9491 0.2036 +vn 0.0000 0.5166 0.8562 +vn 0.2405 0.9491 0.2036 +vn -0.6286 0.7688 0.1177 +vn 0.0000 0.8287 0.5597 +vn 0.0000 0.9515 0.3076 +vn 0.0000 -0.8654 0.5011 +vn 0.0000 -0.4815 0.8764 +vn -0.1833 -0.5864 0.7890 +vn -0.1858 0.5956 0.7815 +vn 0.1858 0.5956 0.7815 +vn 0.3611 0.4713 0.8047 +vn 0.6286 0.7688 0.1177 +vn -0.3611 0.4713 0.8047 +vn -0.4488 -0.3147 0.8364 +vn 0.1833 -0.5864 0.7890 +vn 0.4488 -0.3147 0.8364 +vn 0.0000 0.1578 0.9875 +vn 0.7752 0.0387 0.6306 +vn -0.7752 0.0387 0.6306 +vn -0.6507 0.1488 0.7447 +vn 0.6507 0.1488 0.7447 +vn 0.9278 0.3530 0.1209 +vn -0.9278 0.3530 0.1209 +vn 0.9306 0.3435 0.1263 +vn -0.9306 0.3435 0.1263 +vn -0.1369 -0.5273 0.8386 +vn 0.1369 -0.5273 0.8386 +vn 0.0000 -0.9619 0.2732 +vn -0.6351 0.0428 0.7712 +vn 0.6351 0.0428 0.7712 +vn -0.4141 0.5798 0.7016 +vn 0.4141 0.5798 0.7016 +vn 0.0000 -0.3465 0.9380 +vn 0.0000 0.5588 0.8293 +vn 0.0000 0.5334 0.8459 +vn 0.2959 0.4750 0.8288 +vn -0.6738 0.1155 0.7299 +vn -0.2959 0.4750 0.8288 +vn 0.6738 0.1155 0.7299 +vn -0.5177 -0.7041 0.4860 +vn 0.5177 -0.7041 0.4860 +vn 0.0000 -0.6989 0.7152 +vn -0.0101 -0.0700 0.9975 +vn 0.1581 -0.0843 0.9838 +vn 0.0101 -0.0700 0.9975 +vn -0.1581 -0.0843 0.9838 +vn 0.2934 -0.0602 0.9541 +vn -0.2934 -0.0602 0.9541 +vn 0.1588 -0.1065 0.9816 +vn -0.1588 -0.1065 0.9816 +vn 0.0317 -0.2198 0.9750 +vn 0.1845 -0.1863 0.9650 +vn -0.0317 -0.2198 0.9750 +vn -0.1845 -0.1863 0.9650 +vn 0.2990 -0.0356 0.9536 +vn -0.2990 -0.0356 0.9536 +vn 0.2943 -0.1021 0.9502 +vn -0.2943 -0.1020 0.9502 +vn 0.1776 -0.0608 0.9822 +vn -0.1776 -0.0608 0.9822 +vn -0.2944 0.0046 0.9557 +vn 0.2944 0.0046 0.9557 +vn -0.0887 -0.1272 0.9879 +vn 0.2036 0.1032 0.9736 +vn 0.0887 -0.1272 0.9879 +vn -0.2036 0.1032 0.9736 +vn 0.1435 0.0966 0.9849 +vn -0.1435 0.0966 0.9849 +vn 0.2886 -0.2786 0.9160 +vn -0.2886 -0.2786 0.9160 +vn -0.4508 -0.4658 0.7614 +vn 0.1133 -0.3142 0.9426 +vn -0.1133 -0.3142 0.9426 +vn -0.2741 -0.8556 0.4391 +vn 0.2741 -0.8556 0.4391 +vn -0.1423 -0.5826 0.8002 +vn 0.1423 -0.5826 0.8002 +vn -0.4229 -0.1078 0.8997 +vn 0.4229 -0.1078 0.8997 +vn -0.1921 0.1914 0.9625 +vn 0.1921 0.1914 0.9625 +vn -0.1653 0.6098 0.7751 +vn 0.1653 0.6098 0.7751 +vn 0.1431 0.5587 0.8169 +vn -0.1431 0.5587 0.8169 +vn 0.4323 0.5833 0.6877 +vn -0.4323 0.5833 0.6877 +vn 0.6881 0.2985 0.6614 +vn -0.6881 0.2985 0.6614 +vn 0.7894 -0.2032 0.5793 +vn 0.4508 -0.4658 0.7614 +vn -0.7894 -0.2032 0.5793 +vn 0.8016 0.0110 0.5977 +vn -0.8016 0.0110 0.5977 +vn -0.4603 0.8619 0.2127 +vn 0.0000 0.8592 0.5116 +vn 0.4603 0.8619 0.2127 +vn -0.4792 0.5120 -0.7129 +vn 0.4792 0.5120 -0.7129 +vn -0.2313 0.9570 0.1751 +vn -0.1217 0.6503 -0.7499 +vn 0.1217 0.6503 -0.7499 +vn -0.2275 0.8745 -0.4283 +vn 0.2275 0.8745 -0.4283 +vn -0.3456 0.9125 -0.2192 +vn 0.6957 0.5814 -0.4218 +vn 0.3456 0.9125 -0.2192 +vn -0.6957 0.5814 -0.4218 +vn -0.9070 -0.4009 -0.1290 +vn -0.9302 -0.3062 -0.2024 +vn 0.5444 -0.8372 -0.0533 +vn 0.9302 -0.3062 -0.2024 +vn -0.5444 -0.8372 -0.0533 +vn 0.4720 -0.8637 -0.1768 +vn -0.4720 -0.8637 -0.1768 +vn 0.0000 -0.7711 -0.6367 +vn 0.2771 -0.3147 -0.9078 +vn -0.0000 -0.2133 -0.9770 +vn -0.2771 -0.3147 -0.9078 +vn -0.6894 -0.6687 -0.2786 +vn 0.1514 -0.1510 -0.9769 +vn 0.0000 -0.2974 -0.9548 +vn -0.1514 -0.1510 -0.9769 +vn 0.0675 -0.7832 -0.6181 +vn 0.0000 -0.8818 -0.4716 +vn -0.0675 -0.7832 -0.6181 +vn 0.5551 -0.4762 -0.6820 +vn -0.5551 -0.4762 -0.6820 +vn 0.6204 0.0835 -0.7798 +vn -0.6204 0.0835 -0.7798 +vn 0.7799 -0.0105 -0.6259 +vn -0.7799 -0.0105 -0.6259 +vn 0.6894 -0.6687 -0.2786 +vn 0.8957 0.2578 -0.3624 +vn -0.8957 0.2578 -0.3624 +vn 0.9787 -0.1959 0.0615 +vn -0.9787 -0.1959 0.0615 +vn -0.8872 -0.1577 0.4336 +vn 0.7857 -0.5715 0.2368 +vn -0.7857 -0.5715 0.2368 +vn -0.3489 -0.9371 -0.0082 +vn 0.4455 -0.3584 -0.8204 +vn 0.0000 -0.6913 -0.7226 +vn -0.0000 -0.3049 -0.9524 +vn -0.4455 -0.3584 -0.8204 +vn -0.5223 -0.6536 -0.5477 +vn 0.5223 -0.6536 -0.5477 +vn 0.0000 -0.9417 -0.3365 +vn -0.5071 -0.8376 -0.2033 +vn 0.5727 -0.8197 0.0120 +vn 0.0000 -0.9831 -0.1833 +vn -0.5727 -0.8197 0.0120 +vn 0.7211 -0.6898 0.0651 +vn 0.9850 -0.1605 0.0631 +vn -0.7211 -0.6898 0.0651 +vn -0.9850 -0.1605 0.0631 +vn 0.4730 0.1763 -0.8632 +vn 0.0000 0.3650 -0.9310 +vn -0.4730 0.1763 -0.8632 +vn 0.4442 0.7244 0.5271 +vn 0.0000 0.9997 0.0226 +vn 0.0000 0.8306 0.5568 +vn -0.4442 0.7244 0.5271 +vn -0.4135 0.9096 0.0395 +vn 0.3913 0.8153 -0.4268 +vn 0.0000 0.8343 -0.5514 +vn -0.3913 0.8153 -0.4268 +vn 0.7717 0.6311 0.0785 +vn 0.4444 0.7886 0.4250 +vn -0.7717 0.6311 0.0785 +vn -0.4444 0.7886 0.4250 +vn 0.7418 0.5164 0.4279 +vn 0.6682 0.6719 0.3195 +vn -0.7418 0.5164 0.4279 +vn -0.6682 0.6719 0.3195 +vn 0.8486 0.5288 -0.0140 +vn 0.6784 0.7314 -0.0695 +vn -0.8486 0.5288 -0.0140 +vn -0.6784 0.7314 -0.0695 +vn 0.8722 0.3146 -0.3747 +vn 0.6075 0.5696 -0.5536 +vn -0.8722 0.3146 -0.3747 +vn -0.6075 0.5696 -0.5536 +vn 0.6197 -0.0605 -0.7825 +vn 0.6708 -0.0453 -0.7403 +vn -0.6197 -0.0605 -0.7825 +vn 0.4135 0.9096 0.0395 +vn 0.3406 0.8832 0.3223 +vn -0.3406 0.8832 0.3223 +vn 0.0000 0.5293 0.8485 +vn 0.9983 -0.0283 -0.0502 +vn -0.9983 -0.0283 -0.0502 +vn 0.8403 0.4934 0.2246 +vn -0.8403 0.4934 0.2246 +vn 0.5071 -0.8376 -0.2033 +vn 0.5790 -0.8027 0.1427 +vn -0.5790 -0.8027 0.1427 +vn -0.5633 -0.8173 -0.1213 +vn 0.3123 -0.9500 0.0012 +vn -0.3123 -0.9500 0.0012 +vn 0.8872 -0.1577 0.4336 +vn 0.3255 -0.6029 -0.7284 +vn -0.3255 -0.6029 -0.7284 +vn -0.5292 -0.5051 -0.6817 +vn 0.5633 -0.8173 -0.1213 +vn 0.5292 -0.5051 -0.6817 +vn -0.2793 0.7683 0.5759 +vn 0.5512 -0.0788 0.8307 +vn 0.0188 0.8723 0.4887 +vn 0.2793 0.7683 0.5759 +vn -0.5512 -0.0788 0.8307 +vn -0.4493 -0.0383 0.8926 +vn 0.3215 -0.0923 0.9424 +vn 0.3836 0.8630 0.3288 +vn -0.3215 -0.0923 0.9424 +vn -0.0188 0.8723 0.4887 +vn -0.3836 0.8630 0.3288 +vn 0.7788 0.1678 0.6044 +vn -0.7788 0.1678 0.6044 +vn 0.1545 -0.1239 0.9802 +vn -0.1545 -0.1239 0.9802 +vn 0.6526 -0.4768 0.5888 +vn -0.6526 -0.4768 0.5888 +vn 0.0411 0.3108 0.9496 +vn -0.0411 0.3108 0.9496 +vn 0.5029 -0.7810 0.3703 +vn -0.5029 -0.7810 0.3703 +vn -0.5384 0.2953 0.7893 +vn 0.3300 0.3157 0.8896 +vn 0.0295 -0.6350 0.7719 +vn -0.3300 0.3157 0.8896 +vn -0.0295 -0.6350 0.7719 +vn 0.5384 0.2953 0.7893 +vn 0.1629 0.8581 0.4870 +vn -0.1629 0.8581 0.4870 +vn -0.1868 0.9538 0.2351 +vn 0.1868 0.9538 0.2351 +vn -0.9848 -0.0996 0.1426 +vn 0.9848 -0.0996 0.1426 +vn 0.7622 0.6471 -0.0193 +vn -0.1496 -0.7455 0.6495 +vn 0.1496 -0.7455 0.6495 +vn 0.5605 -0.6609 0.4991 +vn -0.5605 -0.6609 0.4991 +vn 0.6842 -0.5558 0.4722 +vn -0.6842 -0.5558 0.4722 +vn 0.8572 -0.4931 -0.1483 +vn -0.8572 -0.4931 -0.1483 +vn -0.7312 0.1144 0.6725 +vn 0.7312 0.1144 0.6725 +vn 0.4493 -0.0383 0.8926 +vn 0.5998 0.5131 0.6139 +vn -0.5998 0.5131 0.6139 +vn 0.9610 -0.1188 0.2499 +vn 0.8420 -0.1763 0.5098 +vn -0.9610 -0.1188 0.2499 +vn 0.8515 0.0414 0.5228 +vn 0.4814 0.6344 0.6048 +vn -0.8420 -0.1763 0.5098 +vn -0.8515 0.0414 0.5228 +vn -0.4814 0.6344 0.6048 +vn 0.8303 -0.4790 0.2850 +vn 0.6864 -0.6234 0.3746 +vn -0.8303 -0.4790 0.2850 +vn 0.7261 -0.4989 0.4732 +vn 0.7949 -0.2332 0.5601 +vn -0.7261 -0.4989 0.4732 +vn -0.6864 -0.6234 0.3746 +vn -0.7949 -0.2332 0.5601 +vn 0.6593 -0.4685 0.5881 +vn 0.6482 -0.4206 0.6347 +vn -0.6593 -0.4685 0.5881 +vn -0.6482 -0.4206 0.6347 +vn -0.5725 -0.4189 0.7048 +vn 0.7584 0.2665 0.5948 +vn 0.5725 -0.4189 0.7048 +vn -0.7584 0.2665 0.5948 +vn -0.4492 0.3799 0.8086 +vn 0.4492 0.3799 0.8086 +vn -0.2929 0.3709 0.8813 +vn 0.6450 0.3102 0.6984 +vn 0.2929 0.3709 0.8813 +vn -0.6450 0.3102 0.6984 +vn -0.0331 0.9449 0.3256 +vn 0.0331 0.9449 0.3256 +vn 0.4618 -0.3291 0.8237 +vn -0.4618 -0.3291 0.8237 +vn -0.2624 -0.5331 0.8043 +vn 0.2624 -0.5331 0.8043 +vn -0.7529 -0.0338 0.6573 +vn 0.7529 -0.0338 0.6573 +vn -0.5831 0.4999 0.6403 +vn -0.7622 0.6471 -0.0193 +vn 0.5831 0.4999 0.6403 +vn 0.0650 0.7039 0.7074 +vn -0.0650 0.7039 0.7074 +vn 0.1951 0.0390 0.9800 +vn -0.1951 0.0390 0.9800 +vn -0.4085 0.1273 0.9039 +vn 0.4085 0.1273 0.9039 +vn 0.3347 -0.0046 0.9423 +vn -0.3347 -0.0046 0.9423 +vn -0.4448 -0.0937 0.8907 +vn 0.3144 -0.1038 0.9436 +vn 0.3343 0.1068 0.9364 +vn -0.3144 -0.1038 0.9436 +vn -0.3343 0.1068 0.9364 +vn 0.2897 0.3158 0.9035 +vn -0.2897 0.3158 0.9035 +vn -0.3831 -0.0685 0.9211 +vn 0.3831 -0.0685 0.9211 +vn -0.0989 -0.8408 -0.5322 +vn -0.0253 -0.6796 -0.7331 +vn 0.0989 -0.8408 -0.5322 +vn 0.0253 -0.6796 -0.7331 +vn 0.6366 -0.5043 -0.5834 +vn -0.6366 -0.5043 -0.5834 +vn 0.9253 0.0918 -0.3680 +vn -0.9253 0.0918 -0.3680 +vn 0.2870 0.5978 -0.7485 +vn -0.2870 0.5978 -0.7485 +vn -0.4142 0.5509 -0.7245 +vn 0.4142 0.5509 -0.7245 +vn -0.6501 0.5847 -0.4854 +vn 0.6501 0.5847 -0.4854 +vn -0.6708 -0.0453 -0.7403 +vn -0.3679 -0.2836 0.8856 +vn 0.4448 -0.0937 0.8907 +usemtl None +s 1 +f 55/15/7 11/16/8 53/17/9 +f 12/18/10 56/19/11 54/20/12 +f 53/17/9 13/21/13 51/22/14 +f 14/23/15 54/20/12 52/24/16 +f 11/16/8 15/25/17 13/21/13 +f 16/26/18 12/18/10 14/23/15 +f 9/27/19 17/28/20 11/16/8 +f 18/29/21 10/30/22 12/18/10 +f 19/31/23 23/32/24 17/28/20 +f 24/33/25 20/34/26 18/29/21 +f 17/28/20 25/35/27 15/25/17 +f 26/36/28 18/29/21 16/26/18 +f 29/37/29 25/35/27 23/32/24 +f 30/38/30 26/36/28 28/39/31 +f 21/40/32 29/37/29 23/32/24 +f 30/38/30 22/41/33 24/33/25 +f 31/42/34 35/43/35 29/37/29 +f 36/44/36 32/45/37 30/38/30 +f 35/43/35 27/46/38 29/37/29 +f 36/44/36 28/39/31 38/47/39 +f 41/48/40 37/49/41 35/43/35 +f 42/50/42 38/47/39 40/51/43 +f 43/52/44 35/43/35 33/53/45 +f 44/54/46 36/44/36 42/50/42 +f 45/55/47 41/48/40 43/52/44 +f 46/56/48 42/50/42 48/57/49 +f 47/58/50 39/59/51 41/48/40 +f 48/57/49 40/51/43 50/60/52 +f 53/17/9 49/61/53 47/58/50 +f 54/20/12 50/60/52 52/24/16 +f 55/15/7 47/58/50 45/55/47 +f 56/19/11 48/57/49 54/20/12 +f 45/55/47 57/62/54 55/15/7 +f 46/56/48 58/63/55 60/64/56 +f 43/52/44 59/65/57 45/55/47 +f 44/54/46 60/64/56 62/66/58 +f 33/53/45 61/67/59 43/52/44 +f 34/68/60 62/66/58 64/69/61 +f 31/42/34 63/70/62 33/53/45 +f 32/45/37 64/69/61 66/71/63 +f 31/42/34 67/72/64 65/73/65 +f 68/74/66 32/45/37 66/71/63 +f 21/40/32 71/75/67 67/72/64 +f 72/76/68 22/41/33 68/74/66 +f 19/31/23 73/77/69 71/75/67 +f 74/78/70 20/34/26 72/76/68 +f 9/27/19 57/62/54 73/77/69 +f 58/63/55 10/30/22 74/78/70 +f 69/79/71 73/77/69 57/62/54 +f 58/63/55 74/78/70 70/80/72 +f 71/75/67 73/77/69 69/79/71 +f 70/80/72 74/78/70 72/76/68 +f 69/79/71 67/72/64 71/75/67 +f 72/76/68 68/74/66 70/80/72 +f 69/79/71 65/73/65 67/72/64 +f 68/74/66 66/71/63 70/80/72 +f 69/79/71 63/70/62 65/73/65 +f 66/71/63 64/69/61 70/80/72 +f 69/79/71 61/67/59 63/70/62 +f 64/69/61 62/66/58 70/80/72 +f 69/79/71 59/65/57 61/67/59 +f 62/66/58 60/64/56 70/80/72 +f 69/79/71 57/62/54 59/65/57 +f 60/64/56 58/63/55 70/80/72 +f 182/81/73 99/82/74 97/83/75 +f 183/84/76 99/82/74 184/85/77 +f 180/86/78 97/83/75 95/87/79 +f 181/88/80 98/89/81 183/84/76 +f 93/90/82 180/86/78 95/87/79 +f 181/88/80 94/91/83 96/92/84 +f 91/93/85 178/94/86 93/90/82 +f 179/95/87 92/96/88 94/91/83 +f 89/97/89 176/98/90 91/93/85 +f 177/99/91 90/100/92 92/96/88 +f 87/101/93 154/102/94 172/103/95 +f 155/104/96 88/105/97 173/106/98 +f 102/107/99 154/102/94 100/108/100 +f 103/109/101 155/104/96 157/110/102 +f 102/107/99 158/111/103 156/112/104 +f 159/113/105 103/109/101 157/110/102 +f 106/114/106 158/111/103 104/115/107 +f 107/116/108 159/113/105 161/117/109 +f 108/118/110 160/119/111 106/114/106 +f 109/120/112 161/117/109 163/121/113 +f 110/122/114 162/123/115 108/118/110 +f 111/124/116 163/121/113 165/125/117 +f 110/122/114 166/126/118 164/127/119 +f 167/128/120 111/124/116 165/125/117 +f 114/129/121 166/126/118 112/130/122 +f 115/131/123 167/128/120 169/132/124 +f 116/133/125 168/134/126 114/129/121 +f 117/135/127 169/132/124 171/136/128 +f 75/137/129 170/138/130 116/133/125 +f 75/137/129 171/136/128 76/139/131 +f 136/140/132 170/138/130 118/141/133 +f 137/142/134 171/136/128 169/132/124 +f 136/140/132 166/126/118 168/134/126 +f 167/128/120 137/142/134 169/132/124 +f 164/127/119 187/143/135 134/144/136 +f 165/125/117 188/145/137 167/128/120 +f 162/123/115 134/144/136 132/146/138 +f 163/121/113 135/147/139 165/125/117 +f 160/119/111 132/146/138 130/148/140 +f 161/117/109 133/149/141 163/121/113 +f 158/111/103 130/148/140 128/150/142 +f 159/113/105 131/151/143 161/117/109 +f 156/112/104 128/150/142 126/152/144 +f 157/110/102 129/153/145 159/113/105 +f 154/102/94 126/152/144 124/154/146 +f 155/104/96 127/155/147 157/110/102 +f 172/103/95 124/154/146 122/156/148 +f 173/106/98 125/157/149 155/104/96 +f 122/156/148 185/158/150 172/103/95 +f 185/158/150 123/159/151 173/106/98 +f 170/138/130 120/160/152 118/141/133 +f 171/136/128 121/161/153 76/139/131 +f 120/160/152 186/162/154 191/163/155 +f 186/162/154 121/161/153 192/164/156 +f 189/165/157 186/162/154 185/158/150 +f 190/166/158 186/162/154 192/164/156 +f 143/167/159 184/85/77 182/81/73 +f 184/85/77 144/168/160 183/84/76 +f 141/169/161 182/81/73 180/86/78 +f 183/84/76 142/170/162 181/88/80 +f 141/169/161 178/94/86 139/171/163 +f 142/170/162 179/95/87 181/88/80 +f 174/172/164 193/173/165 176/98/90 +f 194/174/166 175/175/167 177/99/91 +f 139/171/163 176/98/90 193/173/165 +f 177/99/91 140/176/168 194/174/166 +f 198/177/169 195/178/170 152/179/171 +f 198/177/169 196/180/172 197/181/173 +f 195/178/170 77/182/174 193/173/165 +f 196/180/172 77/182/174 197/181/173 +f 139/171/163 77/182/174 138/183/175 +f 140/176/168 77/182/174 194/174/166 +f 150/184/176 199/185/177 152/179/171 +f 200/186/178 151/187/179 153/188/180 +f 148/189/181 201/190/182 150/184/176 +f 202/191/183 149/192/184 151/187/179 +f 205/193/185 148/189/181 147/194/186 +f 206/195/187 149/192/184 204/196/188 +f 79/197/189 147/194/186 146/198/190 +f 79/197/189 147/194/186 206/195/187 +f 152/179/171 78/199/191 198/177/169 +f 153/188/180 78/199/191 200/186/178 +f 199/185/177 216/200/192 78/199/191 +f 200/186/178 216/200/192 215/201/193 +f 79/197/189 208/202/194 205/193/185 +f 209/203/195 79/197/189 206/195/187 +f 205/193/185 210/204/196 203/205/197 +f 211/206/198 206/195/187 204/196/188 +f 210/204/196 201/190/182 203/205/197 +f 211/206/198 202/191/183 213/207/199 +f 201/190/182 214/208/200 199/185/177 +f 215/201/193 202/191/183 200/186/178 +f 212/209/201 208/202/194 207/210/202 +f 213/207/199 209/203/195 211/206/198 +f 207/210/202 214/208/200 212/209/201 +f 215/201/193 207/210/202 213/207/199 +f 147/194/186 172/103/95 185/158/150 +f 173/106/98 147/194/186 185/158/150 +f 148/189/181 219/211/203 172/103/95 +f 220/212/204 149/192/184 173/106/98 +f 152/179/171 219/211/203 150/184/176 +f 153/188/180 220/212/204 222/213/205 +f 195/178/170 221/214/206 152/179/171 +f 196/180/172 222/213/205 175/175/167 +f 217/215/207 174/172/164 89/97/89 +f 218/216/208 175/175/167 222/213/205 +f 223/217/209 221/214/206 217/215/207 +f 224/218/210 222/213/205 220/212/204 +f 87/101/93 219/211/203 223/217/209 +f 220/212/204 88/105/97 224/218/210 +f 138/183/175 230/219/211 139/171/163 +f 138/183/175 231/220/212 80/221/213 +f 141/169/161 230/219/211 228/222/214 +f 231/220/212 142/170/162 229/223/215 +f 143/167/159 228/222/214 226/224/216 +f 229/223/215 144/168/160 227/225/217 +f 145/226/218 226/224/216 225/227/219 +f 227/225/217 145/226/218 225/227/219 +f 226/224/216 239/228/220 225/227/219 +f 227/225/217 239/228/220 238/229/221 +f 226/224/216 235/230/222 237/231/223 +f 236/232/224 227/225/217 238/229/221 +f 228/222/214 233/233/225 235/230/222 +f 234/234/226 229/223/215 236/232/224 +f 80/221/213 233/233/225 230/219/211 +f 80/221/213 234/234/226 232/235/227 +f 232/235/227 237/231/223 233/233/225 +f 238/229/221 232/235/227 234/234/226 +f 233/233/225 237/231/223 235/230/222 +f 236/232/224 238/229/221 234/234/226 +f 191/163/155 242/236/228 240/237/229 +f 243/238/230 192/164/156 241/239/231 +f 120/160/152 240/237/229 262/240/232 +f 241/239/231 121/161/153 263/241/233 +f 120/160/152 264/242/234 118/141/133 +f 121/161/153 265/243/235 263/241/233 +f 122/156/148 242/236/228 189/165/157 +f 123/159/151 243/238/230 261/244/236 +f 122/156/148 258/245/237 260/246/238 +f 259/247/239 123/159/151 261/244/236 +f 124/154/146 256/248/240 258/245/237 +f 257/249/241 125/157/149 259/247/239 +f 126/152/144 254/250/242 256/248/240 +f 255/251/243 127/155/147 257/249/241 +f 128/150/142 252/252/244 254/250/242 +f 253/253/245 129/153/145 255/251/243 +f 132/146/138 252/252/244 130/148/140 +f 133/149/141 253/253/245 251/254/246 +f 134/144/136 250/255/247 132/146/138 +f 135/147/139 251/254/246 249/256/248 +f 134/144/136 244/257/249 248/258/250 +f 245/259/251 135/147/139 249/256/248 +f 187/143/135 246/260/252 244/257/249 +f 247/261/253 188/145/137 245/259/251 +f 136/140/132 264/242/234 246/260/252 +f 265/243/235 137/142/134 247/261/253 +f 264/242/234 284/262/254 246/260/252 +f 265/243/235 285/263/255 267/264/256 +f 244/257/249 284/262/254 286/265/257 +f 285/263/255 245/259/251 287/266/258 +f 244/257/249 282/267/259 248/258/250 +f 245/259/251 283/268/260 287/266/258 +f 248/258/250 280/269/261 250/255/247 +f 249/256/248 281/270/262 283/268/260 +f 252/252/244 280/269/261 278/271/263 +f 281/270/262 253/253/245 279/272/264 +f 252/252/244 276/273/265 254/250/242 +f 253/253/245 277/274/266 279/272/264 +f 256/248/240 276/273/265 274/275/267 +f 277/274/266 257/249/241 275/276/268 +f 256/248/240 272/277/269 258/245/237 +f 257/249/241 273/278/270 275/276/268 +f 258/245/237 270/279/271 260/246/238 +f 259/247/239 271/280/272 273/278/270 +f 242/236/228 270/279/271 288/281/273 +f 271/280/272 243/238/230 289/282/274 +f 264/242/234 268/283/275 266/284/276 +f 269/285/277 265/243/235 267/264/256 +f 262/240/232 290/286/278 268/283/275 +f 291/287/279 263/241/233 269/285/277 +f 240/237/229 288/281/273 290/286/278 +f 289/282/274 241/239/231 291/287/279 +f 75/137/129 292/288/280 81/289/281 +f 293/290/282 75/137/129 81/289/281 +f 116/133/125 294/291/283 292/288/280 +f 295/292/284 117/135/127 293/290/282 +f 112/130/122 294/291/283 114/129/121 +f 113/293/285 295/292/284 297/294/286 +f 110/122/114 296/295/287 112/130/122 +f 111/124/116 297/294/286 299/296/288 +f 108/118/110 298/297/289 110/122/114 +f 109/120/112 299/296/288 301/298/290 +f 108/118/110 302/299/291 300/300/292 +f 303/301/293 109/120/112 301/298/290 +f 104/115/107 302/299/291 106/114/106 +f 105/302/294 303/301/293 305/303/295 +f 104/115/107 306/304/296 304/305/297 +f 307/306/298 105/302/294 305/303/295 +f 102/107/99 308/307/299 306/304/296 +f 309/308/300 103/109/101 307/306/298 +f 317/309/301 346/310/302 316/311/303 +f 317/312/301 347/313/304 337/314/305 +f 316/311/303 344/315/306 315/316/307 +f 316/317/303 345/318/308 347/313/304 +f 315/316/307 348/319/309 314/320/310 +f 315/321/307 349/322/311 345/318/308 +f 97/83/75 314/320/310 348/319/309 +f 314/320/310 98/89/81 349/322/311 +f 95/87/79 348/319/309 342/323/312 +f 349/322/311 96/92/84 343/324/313 +f 93/90/82 342/323/312 338/325/314 +f 343/324/313 94/91/83 339/326/315 +f 91/93/85 338/325/314 340/327/316 +f 339/326/315 92/96/88 341/328/317 +f 338/325/314 346/310/302 340/327/316 +f 347/313/304 339/326/315 341/328/317 +f 342/323/312 344/315/306 338/325/314 +f 343/324/313 345/318/308 349/322/311 +f 340/327/316 336/329/318 334/330/319 +f 341/328/317 337/314/305 347/313/304 +f 89/97/89 340/327/316 334/330/319 +f 341/328/317 90/100/92 335/331/320 +f 350/332/321 223/217/209 217/215/207 +f 351/333/322 224/218/210 353/334/323 +f 334/330/319 217/215/207 89/97/89 +f 335/331/320 218/216/208 351/333/322 +f 223/217/209 354/335/324 87/101/93 +f 224/218/210 355/336/325 353/334/323 +f 354/335/324 100/108/100 87/101/93 +f 355/336/325 101/337/326 309/308/300 +f 332/338/327 312/339/328 85/340/329 +f 333/341/330 312/342/328 361/343/331 +f 360/344/332 86/345/333 312/339/328 +f 361/343/331 86/346/333 359/347/334 +f 86/345/333 356/348/335 313/349/336 +f 357/350/337 86/346/333 313/351/336 +f 313/349/336 336/329/318 317/309/301 +f 337/314/305 313/351/336 317/312/301 +f 336/329/318 350/332/321 334/330/319 +f 337/314/305 351/333/322 357/350/337 +f 304/305/297 326/352/338 318/353/339 +f 327/354/340 305/303/295 319/355/341 +f 324/356/342 85/340/329 84/357/343 +f 325/358/344 85/359/329 333/341/330 +f 366/360/345 311/361/346 310/362/347 +f 367/363/348 311/364/346 365/365/349 +f 311/361/346 362/366/350 83/367/351 +f 363/368/352 311/364/346 83/369/351 +f 83/367/351 324/356/342 84/357/343 +f 325/358/344 83/369/351 84/370/343 +f 300/371/292 370/372/353 372/373/354 +f 371/374/355 301/375/290 373/376/356 +f 372/373/354 376/377/357 374/378/358 +f 377/379/359 373/376/356 375/380/360 +f 374/378/358 378/381/361 380/382/362 +f 379/383/363 375/380/360 381/384/364 +f 380/382/362 384/385/365 382/386/366 +f 385/387/367 381/384/364 383/388/368 +f 386/389/369 384/385/365 322/390/370 +f 387/391/371 385/387/367 383/388/368 +f 324/356/342 382/386/366 386/389/369 +f 383/388/368 325/358/344 387/391/371 +f 362/366/350 380/382/362 382/386/366 +f 381/384/364 363/368/352 383/388/368 +f 364/392/372 374/378/358 380/382/362 +f 375/380/360 365/365/349 381/384/364 +f 366/360/345 372/373/354 374/378/358 +f 373/376/356 367/363/348 375/380/360 +f 300/371/292 368/393/373 298/394/289 +f 301/375/290 369/395/374 373/376/356 +f 368/393/373 310/362/347 82/396/375 +f 369/395/374 310/397/347 367/363/348 +f 292/398/280 296/399/287 298/394/289 +f 297/400/286 293/401/282 299/402/288 +f 292/398/280 368/393/373 82/396/375 +f 369/395/374 293/401/282 82/403/375 +f 81/404/281 292/398/280 82/396/375 +f 82/403/375 293/401/282 81/405/281 +f 304/305/297 370/372/353 302/299/291 +f 305/303/295 371/374/355 319/355/341 +f 318/353/339 376/377/357 370/372/353 +f 377/379/359 319/355/341 371/374/355 +f 320/406/376 378/381/361 376/377/357 +f 379/383/363 321/407/377 377/379/359 +f 384/385/365 390/408/378 322/390/370 +f 385/387/367 391/409/379 379/383/363 +f 358/410/380 392/411/381 356/348/335 +f 359/347/334 393/412/382 395/413/383 +f 392/411/381 328/414/384 326/352/338 +f 393/412/382 329/415/385 395/413/383 +f 306/304/296 392/411/381 326/352/338 +f 393/412/382 307/306/298 327/354/340 +f 308/307/299 350/332/321 392/411/381 +f 351/333/322 309/308/300 393/412/382 +f 350/332/321 356/348/335 392/411/381 +f 393/412/382 357/350/337 351/333/322 +f 308/307/299 354/335/324 352/416/386 +f 353/334/323 355/336/325 309/308/300 +f 330/417/387 386/389/369 322/390/370 +f 331/418/388 387/391/371 389/419/389 +f 386/389/369 332/338/327 324/356/342 +f 387/391/371 333/341/330 389/419/389 +f 394/420/390 330/417/387 328/414/384 +f 395/413/383 331/418/388 389/419/389 +f 360/344/332 394/420/390 358/410/380 +f 361/343/331 395/413/383 389/419/389 +f 332/338/327 388/421/391 360/344/332 +f 361/343/331 389/419/389 333/341/330 +f 396/422/392 410/423/393 408/424/394 +f 397/425/395 411/426/396 423/427/397 +f 408/424/394 412/428/398 406/429/399 +f 413/430/400 409/431/401 407/432/402 +f 412/428/398 404/433/403 406/429/399 +f 413/430/400 405/434/404 415/435/405 +f 414/436/406 402/437/407 404/433/403 +f 415/435/405 403/438/408 417/439/409 +f 416/440/410 400/441/411 402/437/407 +f 417/439/409 401/442/412 419/443/413 +f 400/441/411 420/444/414 398/445/415 +f 421/446/416 401/442/412 399/447/417 +f 418/448/418 426/449/419 420/444/414 +f 427/450/420 419/443/413 421/446/416 +f 416/440/410 428/451/421 418/448/418 +f 429/452/422 417/439/409 419/443/413 +f 432/453/423 416/440/410 414/436/406 +f 433/454/424 417/439/409 431/455/425 +f 434/456/426 414/436/406 412/428/398 +f 435/457/427 415/435/405 433/454/424 +f 436/458/428 412/428/398 410/423/393 +f 437/459/429 413/430/400 435/457/427 +f 410/423/393 424/460/430 436/458/428 +f 425/461/431 411/426/396 437/459/429 +f 328/414/384 450/462/432 326/352/338 +f 329/415/385 451/463/433 453/464/434 +f 398/445/415 452/465/435 328/466/384 +f 399/447/417 453/467/434 421/446/416 +f 318/353/339 450/462/432 320/406/376 +f 451/463/433 319/355/341 321/407/377 +f 390/468/378 422/469/436 396/422/392 +f 423/427/397 391/470/379 397/425/395 +f 420/444/414 448/471/437 452/465/435 +f 449/472/438 421/446/416 453/467/434 +f 454/473/439 448/471/437 446/474/440 +f 455/475/441 449/472/438 453/467/434 +f 442/476/442 446/474/440 444/477/443 +f 447/478/444 443/479/445 445/480/446 +f 456/481/447 442/476/442 440/482/448 +f 457/483/449 443/479/445 455/475/441 +f 456/481/447 458/484/450 438/485/451 +f 457/483/449 459/486/452 441/487/453 +f 438/485/451 424/460/430 422/469/436 +f 439/488/454 425/461/431 459/486/452 +f 320/406/376 438/489/451 390/408/378 +f 439/490/454 321/407/377 391/409/379 +f 450/462/432 456/491/447 320/406/376 +f 451/463/433 457/492/449 455/493/441 +f 450/462/432 452/494/435 454/495/439 +f 455/493/441 453/464/434 451/463/433 +f 424/460/430 460/496/455 484/497/456 +f 461/498/457 425/461/431 485/499/458 +f 440/482/448 460/496/455 458/484/450 +f 441/487/453 461/498/457 471/500/459 +f 440/482/448 468/501/460 470/502/461 +f 469/503/462 441/487/453 471/500/459 +f 444/477/443 468/501/460 442/476/442 +f 445/480/446 469/503/462 467/504/463 +f 446/474/440 466/505/464 444/477/443 +f 447/478/444 467/504/463 465/506/465 +f 446/474/440 462/507/466 464/508/467 +f 463/509/468 447/478/444 465/506/465 +f 448/471/437 482/510/469 462/507/466 +f 483/511/470 449/472/438 463/509/468 +f 436/458/428 484/497/456 472/512/471 +f 485/499/458 437/459/429 473/513/472 +f 434/456/426 472/512/471 474/514/473 +f 473/513/472 435/457/427 475/515/474 +f 432/453/423 474/514/473 476/516/475 +f 475/515/474 433/454/424 477/517/476 +f 432/453/423 478/518/477 430/519/478 +f 433/454/424 479/520/479 477/517/476 +f 430/519/478 480/521/480 428/451/421 +f 431/455/425 481/522/481 479/520/479 +f 428/451/421 482/510/469 426/449/419 +f 429/452/422 483/511/470 481/522/481 +f 464/508/467 486/523/482 466/505/464 +f 465/506/465 487/524/483 489/525/484 +f 488/526/485 492/527/486 486/523/482 +f 489/525/484 493/528/487 491/529/488 +f 492/527/486 496/530/489 494/531/490 +f 497/532/491 493/528/487 495/533/492 +f 496/530/489 500/534/493 494/531/490 +f 497/532/491 501/535/494 499/536/495 +f 472/512/471 494/531/490 500/534/493 +f 495/533/492 473/513/472 501/535/494 +f 492/527/486 484/497/456 460/496/455 +f 493/528/487 485/499/458 495/533/492 +f 470/502/461 492/527/486 460/496/455 +f 471/500/459 493/528/487 487/524/483 +f 466/505/464 470/502/461 468/501/460 +f 471/500/459 467/504/463 469/503/462 +f 482/510/469 464/508/467 462/507/466 +f 483/511/470 465/506/465 489/525/484 +f 480/521/480 488/526/485 482/510/469 +f 489/525/484 481/522/481 483/511/470 +f 496/530/489 480/521/480 478/518/477 +f 497/532/491 481/522/481 491/529/488 +f 498/537/496 478/518/477 476/516/475 +f 499/536/495 479/520/479 497/532/491 +f 474/514/473 498/537/496 476/516/475 +f 499/536/495 475/515/474 477/517/476 +f 472/512/471 500/534/493 474/514/473 +f 475/515/474 501/535/494 473/513/472 +f 400/441/411 512/538/497 510/539/498 +f 513/540/499 401/442/412 511/541/500 +f 402/437/407 510/539/498 508/542/501 +f 511/541/500 403/438/408 509/543/502 +f 402/437/407 506/544/503 404/433/403 +f 403/438/408 507/545/504 509/543/502 +f 404/433/403 504/546/505 406/547/399 +f 405/434/404 505/548/506 507/545/504 +f 406/547/399 502/549/507 408/550/394 +f 407/551/402 503/552/508 505/548/506 +f 408/550/394 514/553/509 396/554/392 +f 409/555/401 515/556/510 503/552/508 +f 510/539/498 514/553/509 502/549/507 +f 511/541/500 515/556/510 513/540/499 +f 502/549/507 508/542/501 510/539/498 +f 509/543/502 503/552/508 511/541/500 +f 504/546/505 506/544/503 508/542/501 +f 509/543/502 507/545/504 505/548/506 +f 390/408/378 514/557/509 322/390/370 +f 391/558/379 515/556/510 397/559/395 +f 322/560/370 512/538/497 330/561/387 +f 513/540/499 323/562/511 331/563/388 +f 328/466/384 512/538/497 398/445/415 +f 513/540/499 329/564/385 399/447/417 +f 55/15/7 9/27/19 11/16/8 +f 12/18/10 10/30/22 56/19/11 +f 53/17/9 11/16/8 13/21/13 +f 14/23/15 12/18/10 54/20/12 +f 11/16/8 17/28/20 15/25/17 +f 16/26/18 18/29/21 12/18/10 +f 9/27/19 19/31/23 17/28/20 +f 18/29/21 20/34/26 10/30/22 +f 19/31/23 21/40/32 23/32/24 +f 24/33/25 22/41/33 20/34/26 +f 17/28/20 23/32/24 25/35/27 +f 26/36/28 24/33/25 18/29/21 +f 29/37/29 27/46/38 25/35/27 +f 30/38/30 24/33/25 26/36/28 +f 21/40/32 31/42/34 29/37/29 +f 30/38/30 32/45/37 22/41/33 +f 31/42/34 33/53/45 35/43/35 +f 36/44/36 34/68/60 32/45/37 +f 35/43/35 37/49/41 27/46/38 +f 36/44/36 30/38/30 28/39/31 +f 41/48/40 39/59/51 37/49/41 +f 42/50/42 36/44/36 38/47/39 +f 43/52/44 41/48/40 35/43/35 +f 44/54/46 34/68/60 36/44/36 +f 45/55/47 47/58/50 41/48/40 +f 46/56/48 44/54/46 42/50/42 +f 47/58/50 49/61/53 39/59/51 +f 48/57/49 42/50/42 40/51/43 +f 53/17/9 51/22/14 49/61/53 +f 54/20/12 48/57/49 50/60/52 +f 55/15/7 53/17/9 47/58/50 +f 56/19/11 46/56/48 48/57/49 +f 45/55/47 59/65/57 57/62/54 +f 46/56/48 56/19/11 58/63/55 +f 43/52/44 61/67/59 59/65/57 +f 44/54/46 46/56/48 60/64/56 +f 33/53/45 63/70/62 61/67/59 +f 34/68/60 44/54/46 62/66/58 +f 31/42/34 65/73/65 63/70/62 +f 32/45/37 34/68/60 64/69/61 +f 31/42/34 21/40/32 67/72/64 +f 68/74/66 22/41/33 32/45/37 +f 21/40/32 19/31/23 71/75/67 +f 72/76/68 20/34/26 22/41/33 +f 19/31/23 9/27/19 73/77/69 +f 74/78/70 10/30/22 20/34/26 +f 9/27/19 55/15/7 57/62/54 +f 58/63/55 56/19/11 10/30/22 +f 182/81/73 184/85/77 99/82/74 +f 183/84/76 98/89/81 99/82/74 +f 180/86/78 182/81/73 97/83/75 +f 181/88/80 96/92/84 98/89/81 +f 93/90/82 178/94/86 180/86/78 +f 181/88/80 179/95/87 94/91/83 +f 91/93/85 176/98/90 178/94/86 +f 179/95/87 177/99/91 92/96/88 +f 89/97/89 174/172/164 176/98/90 +f 177/99/91 175/175/167 90/100/92 +f 87/101/93 100/108/100 154/102/94 +f 155/104/96 101/337/326 88/105/97 +f 102/107/99 156/112/104 154/102/94 +f 103/109/101 101/337/326 155/104/96 +f 102/107/99 104/115/107 158/111/103 +f 159/113/105 105/302/294 103/109/101 +f 106/114/106 160/119/111 158/111/103 +f 107/116/108 105/302/294 159/113/105 +f 108/118/110 162/123/115 160/119/111 +f 109/120/112 107/116/108 161/117/109 +f 110/122/114 164/127/119 162/123/115 +f 111/124/116 109/120/112 163/121/113 +f 110/122/114 112/130/122 166/126/118 +f 167/128/120 113/293/285 111/124/116 +f 114/129/121 168/134/126 166/126/118 +f 115/131/123 113/293/285 167/128/120 +f 116/133/125 170/138/130 168/134/126 +f 117/135/127 115/131/123 169/132/124 +f 75/137/129 76/139/131 170/138/130 +f 75/137/129 117/135/127 171/136/128 +f 136/140/132 168/134/126 170/138/130 +f 137/142/134 119/565/512 171/136/128 +f 136/140/132 187/143/135 166/126/118 +f 167/128/120 188/145/137 137/142/134 +f 164/127/119 166/126/118 187/143/135 +f 165/125/117 135/147/139 188/145/137 +f 162/123/115 164/127/119 134/144/136 +f 163/121/113 133/149/141 135/147/139 +f 160/119/111 162/123/115 132/146/138 +f 161/117/109 131/151/143 133/149/141 +f 158/111/103 160/119/111 130/148/140 +f 159/113/105 129/153/145 131/151/143 +f 156/112/104 158/111/103 128/150/142 +f 157/110/102 127/155/147 129/153/145 +f 154/102/94 156/112/104 126/152/144 +f 155/104/96 125/157/149 127/155/147 +f 172/103/95 154/102/94 124/154/146 +f 173/106/98 123/159/151 125/157/149 +f 122/156/148 189/165/157 185/158/150 +f 185/158/150 190/166/158 123/159/151 +f 170/138/130 76/139/131 120/160/152 +f 171/136/128 119/565/512 121/161/153 +f 120/160/152 76/139/131 186/162/154 +f 186/162/154 76/139/131 121/161/153 +f 189/165/157 191/163/155 186/162/154 +f 190/166/158 185/158/150 186/162/154 +f 143/167/159 145/226/218 184/85/77 +f 184/85/77 145/226/218 144/168/160 +f 141/169/161 143/167/159 182/81/73 +f 183/84/76 144/168/160 142/170/162 +f 141/169/161 180/86/78 178/94/86 +f 142/170/162 140/176/168 179/95/87 +f 174/172/164 195/178/170 193/173/165 +f 194/174/166 196/180/172 175/175/167 +f 139/171/163 178/94/86 176/98/90 +f 177/99/91 179/95/87 140/176/168 +f 198/177/169 197/181/173 195/178/170 +f 198/177/169 153/188/180 196/180/172 +f 195/178/170 197/181/173 77/182/174 +f 196/180/172 194/174/166 77/182/174 +f 139/171/163 193/173/165 77/182/174 +f 140/176/168 138/183/175 77/182/174 +f 150/184/176 201/190/182 199/185/177 +f 200/186/178 202/191/183 151/187/179 +f 148/189/181 203/205/197 201/190/182 +f 202/191/183 204/196/188 149/192/184 +f 205/193/185 203/205/197 148/189/181 +f 206/195/187 147/194/186 149/192/184 +f 79/197/189 205/193/185 147/194/186 +f 79/197/189 146/198/190 147/194/186 +f 152/179/171 199/185/177 78/199/191 +f 153/188/180 198/177/169 78/199/191 +f 199/185/177 214/208/200 216/200/192 +f 200/186/178 78/199/191 216/200/192 +f 79/197/189 207/210/202 208/202/194 +f 209/203/195 207/210/202 79/197/189 +f 205/193/185 208/202/194 210/204/196 +f 211/206/198 209/203/195 206/195/187 +f 210/204/196 212/209/201 201/190/182 +f 211/206/198 204/196/188 202/191/183 +f 201/190/182 212/209/201 214/208/200 +f 215/201/193 213/207/199 202/191/183 +f 212/209/201 210/204/196 208/202/194 +f 213/207/199 207/210/202 209/203/195 +f 207/210/202 216/200/192 214/208/200 +f 215/201/193 216/200/192 207/210/202 +f 147/194/186 148/189/181 172/103/95 +f 173/106/98 149/192/184 147/194/186 +f 148/189/181 150/184/176 219/211/203 +f 220/212/204 151/187/179 149/192/184 +f 152/179/171 221/214/206 219/211/203 +f 153/188/180 151/187/179 220/212/204 +f 195/178/170 174/172/164 221/214/206 +f 196/180/172 153/188/180 222/213/205 +f 217/215/207 221/214/206 174/172/164 +f 218/216/208 90/100/92 175/175/167 +f 223/217/209 219/211/203 221/214/206 +f 224/218/210 218/216/208 222/213/205 +f 87/101/93 172/103/95 219/211/203 +f 220/212/204 173/106/98 88/105/97 +f 138/183/175 80/221/213 230/219/211 +f 138/183/175 140/176/168 231/220/212 +f 141/169/161 139/171/163 230/219/211 +f 231/220/212 140/176/168 142/170/162 +f 143/167/159 141/169/161 228/222/214 +f 229/223/215 142/170/162 144/168/160 +f 145/226/218 143/167/159 226/224/216 +f 227/225/217 144/168/160 145/226/218 +f 226/224/216 237/231/223 239/228/220 +f 227/225/217 225/227/219 239/228/220 +f 226/224/216 228/222/214 235/230/222 +f 236/232/224 229/223/215 227/225/217 +f 228/222/214 230/219/211 233/233/225 +f 234/234/226 231/220/212 229/223/215 +f 80/221/213 232/235/227 233/233/225 +f 80/221/213 231/220/212 234/234/226 +f 232/235/227 239/228/220 237/231/223 +f 238/229/221 239/228/220 232/235/227 +f 191/163/155 189/165/157 242/236/228 +f 243/238/230 190/166/158 192/164/156 +f 120/160/152 191/163/155 240/237/229 +f 241/239/231 192/164/156 121/161/153 +f 120/160/152 262/240/232 264/242/234 +f 121/161/153 119/565/512 265/243/235 +f 122/156/148 260/246/238 242/236/228 +f 123/159/151 190/166/158 243/238/230 +f 122/156/148 124/154/146 258/245/237 +f 259/247/239 125/157/149 123/159/151 +f 124/154/146 126/152/144 256/248/240 +f 257/249/241 127/155/147 125/157/149 +f 126/152/144 128/150/142 254/250/242 +f 255/251/243 129/153/145 127/155/147 +f 128/150/142 130/148/140 252/252/244 +f 253/253/245 131/151/143 129/153/145 +f 132/146/138 250/255/247 252/252/244 +f 133/149/141 131/151/143 253/253/245 +f 134/144/136 248/258/250 250/255/247 +f 135/147/139 133/149/141 251/254/246 +f 134/144/136 187/143/135 244/257/249 +f 245/259/251 188/145/137 135/147/139 +f 187/143/135 136/140/132 246/260/252 +f 247/261/253 137/142/134 188/145/137 +f 136/140/132 118/141/133 264/242/234 +f 265/243/235 119/565/512 137/142/134 +f 264/242/234 266/284/276 284/262/254 +f 265/243/235 247/261/253 285/263/255 +f 244/257/249 246/260/252 284/262/254 +f 285/263/255 247/261/253 245/259/251 +f 244/257/249 286/265/257 282/267/259 +f 245/259/251 249/256/248 283/268/260 +f 248/258/250 282/267/259 280/269/261 +f 249/256/248 251/254/246 281/270/262 +f 252/252/244 250/255/247 280/269/261 +f 281/270/262 251/254/246 253/253/245 +f 252/252/244 278/271/263 276/273/265 +f 253/253/245 255/251/243 277/274/266 +f 256/248/240 254/250/242 276/273/265 +f 277/274/266 255/251/243 257/249/241 +f 256/248/240 274/275/267 272/277/269 +f 257/249/241 259/247/239 273/278/270 +f 258/245/237 272/277/269 270/279/271 +f 259/247/239 261/244/236 271/280/272 +f 242/236/228 260/246/238 270/279/271 +f 271/280/272 261/244/236 243/238/230 +f 264/242/234 262/240/232 268/283/275 +f 269/285/277 263/241/233 265/243/235 +f 262/240/232 240/237/229 290/286/278 +f 291/287/279 241/239/231 263/241/233 +f 240/237/229 242/236/228 288/281/273 +f 289/282/274 243/238/230 241/239/231 +f 75/137/129 116/133/125 292/288/280 +f 293/290/282 117/135/127 75/137/129 +f 116/133/125 114/129/121 294/291/283 +f 295/292/284 115/131/123 117/135/127 +f 112/130/122 296/295/287 294/291/283 +f 113/293/285 115/131/123 295/292/284 +f 110/122/114 298/297/289 296/295/287 +f 111/124/116 113/293/285 297/294/286 +f 108/118/110 300/300/292 298/297/289 +f 109/120/112 111/124/116 299/296/288 +f 108/118/110 106/114/106 302/299/291 +f 303/301/293 107/116/108 109/120/112 +f 104/115/107 304/305/297 302/299/291 +f 105/302/294 107/116/108 303/301/293 +f 104/115/107 102/107/99 306/304/296 +f 307/306/298 103/109/101 105/302/294 +f 102/107/99 100/108/100 308/307/299 +f 309/308/300 101/337/326 103/109/101 +f 317/309/301 336/329/318 346/310/302 +f 317/312/301 316/317/303 347/313/304 +f 316/311/303 346/310/302 344/315/306 +f 316/317/303 315/321/307 345/318/308 +f 315/316/307 344/315/306 348/319/309 +f 315/321/307 314/320/310 349/322/311 +f 97/83/75 99/82/74 314/320/310 +f 314/320/310 99/82/74 98/89/81 +f 95/87/79 97/83/75 348/319/309 +f 349/322/311 98/89/81 96/92/84 +f 93/90/82 95/87/79 342/323/312 +f 343/324/313 96/92/84 94/91/83 +f 91/93/85 93/90/82 338/325/314 +f 339/326/315 94/91/83 92/96/88 +f 338/325/314 344/315/306 346/310/302 +f 347/313/304 345/318/308 339/326/315 +f 342/323/312 348/319/309 344/315/306 +f 343/324/313 339/326/315 345/318/308 +f 340/327/316 346/310/302 336/329/318 +f 341/328/317 335/331/320 337/314/305 +f 89/97/89 91/93/85 340/327/316 +f 341/328/317 92/96/88 90/100/92 +f 350/332/321 352/416/386 223/217/209 +f 351/333/322 218/216/208 224/218/210 +f 334/330/319 350/332/321 217/215/207 +f 335/331/320 90/100/92 218/216/208 +f 223/217/209 352/416/386 354/335/324 +f 224/218/210 88/105/97 355/336/325 +f 354/335/324 308/307/299 100/108/100 +f 355/336/325 88/105/97 101/337/326 +f 332/338/327 360/344/332 312/339/328 +f 333/341/330 85/359/329 312/342/328 +f 360/344/332 358/410/380 86/345/333 +f 361/343/331 312/342/328 86/346/333 +f 86/345/333 358/410/380 356/348/335 +f 357/350/337 359/347/334 86/346/333 +f 313/349/336 356/348/335 336/329/318 +f 337/314/305 357/350/337 313/351/336 +f 336/329/318 356/348/335 350/332/321 +f 337/314/305 335/331/320 351/333/322 +f 304/305/297 306/304/296 326/352/338 +f 327/354/340 307/306/298 305/303/295 +f 324/356/342 332/338/327 85/340/329 +f 325/358/344 84/370/343 85/359/329 +f 366/360/345 364/392/372 311/361/346 +f 367/363/348 310/397/347 311/364/346 +f 311/361/346 364/392/372 362/366/350 +f 363/368/352 365/365/349 311/364/346 +f 83/367/351 362/366/350 324/356/342 +f 325/358/344 363/368/352 83/369/351 +f 300/371/292 302/299/291 370/372/353 +f 371/374/355 303/301/293 301/375/290 +f 372/373/354 370/372/353 376/377/357 +f 377/379/359 371/374/355 373/376/356 +f 374/378/358 376/377/357 378/381/361 +f 379/383/363 377/379/359 375/380/360 +f 380/382/362 378/381/361 384/385/365 +f 385/387/367 379/383/363 381/384/364 +f 386/389/369 382/386/366 384/385/365 +f 387/391/371 323/566/511 385/387/367 +f 324/356/342 362/366/350 382/386/366 +f 383/388/368 363/368/352 325/358/344 +f 362/366/350 364/392/372 380/382/362 +f 381/384/364 365/365/349 363/368/352 +f 364/392/372 366/360/345 374/378/358 +f 375/380/360 367/363/348 365/365/349 +f 366/360/345 368/393/373 372/373/354 +f 373/376/356 369/395/374 367/363/348 +f 300/371/292 372/373/354 368/393/373 +f 301/375/290 299/402/288 369/395/374 +f 368/393/373 366/360/345 310/362/347 +f 369/395/374 82/403/375 310/397/347 +f 292/398/280 294/567/283 296/399/287 +f 297/400/286 295/568/284 293/401/282 +f 292/398/280 298/394/289 368/393/373 +f 369/395/374 299/402/288 293/401/282 +f 304/305/297 318/353/339 370/372/353 +f 305/303/295 303/301/293 371/374/355 +f 318/353/339 320/406/376 376/377/357 +f 377/379/359 321/407/377 319/355/341 +f 320/406/376 390/408/378 378/381/361 +f 379/383/363 391/409/379 321/407/377 +f 384/385/365 378/381/361 390/408/378 +f 385/387/367 323/566/511 391/409/379 +f 358/410/380 394/420/390 392/411/381 +f 359/347/334 357/350/337 393/412/382 +f 392/411/381 394/420/390 328/414/384 +f 393/412/382 327/354/340 329/415/385 +f 306/304/296 308/307/299 392/411/381 +f 393/412/382 309/308/300 307/306/298 +f 308/307/299 352/416/386 350/332/321 +f 351/333/322 353/334/323 309/308/300 +f 330/417/387 388/421/391 386/389/369 +f 331/418/388 323/566/511 387/391/371 +f 386/389/369 388/421/391 332/338/327 +f 387/391/371 325/358/344 333/341/330 +f 394/420/390 388/421/391 330/417/387 +f 395/413/383 329/415/385 331/418/388 +f 360/344/332 388/421/391 394/420/390 +f 361/343/331 359/347/334 395/413/383 +f 396/422/392 422/469/436 410/423/393 +f 397/425/395 409/431/401 411/426/396 +f 408/424/394 410/423/393 412/428/398 +f 413/430/400 411/426/396 409/431/401 +f 412/428/398 414/436/406 404/433/403 +f 413/430/400 407/432/402 405/434/404 +f 414/436/406 416/440/410 402/437/407 +f 415/435/405 405/434/404 403/438/408 +f 416/440/410 418/448/418 400/441/411 +f 417/439/409 403/438/408 401/442/412 +f 400/441/411 418/448/418 420/444/414 +f 421/446/416 419/443/413 401/442/412 +f 418/448/418 428/451/421 426/449/419 +f 427/450/420 429/452/422 419/443/413 +f 416/440/410 430/519/478 428/451/421 +f 429/452/422 431/455/425 417/439/409 +f 432/453/423 430/519/478 416/440/410 +f 433/454/424 415/435/405 417/439/409 +f 434/456/426 432/453/423 414/436/406 +f 435/457/427 413/430/400 415/435/405 +f 436/458/428 434/456/426 412/428/398 +f 437/459/429 411/426/396 413/430/400 +f 410/423/393 422/469/436 424/460/430 +f 425/461/431 423/427/397 411/426/396 +f 328/414/384 452/494/435 450/462/432 +f 329/415/385 327/354/340 451/463/433 +f 398/445/415 420/444/414 452/465/435 +f 399/447/417 329/564/385 453/467/434 +f 318/353/339 326/352/338 450/462/432 +f 451/463/433 327/354/340 319/355/341 +f 390/468/378 438/485/451 422/469/436 +f 423/427/397 439/488/454 391/470/379 +f 420/444/414 426/449/419 448/471/437 +f 449/472/438 427/450/420 421/446/416 +f 454/473/439 452/465/435 448/471/437 +f 455/475/441 447/478/444 449/472/438 +f 442/476/442 454/473/439 446/474/440 +f 447/478/444 455/475/441 443/479/445 +f 456/481/447 454/473/439 442/476/442 +f 457/483/449 441/487/453 443/479/445 +f 456/481/447 440/482/448 458/484/450 +f 457/483/449 439/488/454 459/486/452 +f 438/485/451 458/484/450 424/460/430 +f 439/488/454 423/427/397 425/461/431 +f 320/406/376 456/491/447 438/489/451 +f 439/490/454 457/492/449 321/407/377 +f 450/462/432 454/495/439 456/491/447 +f 451/463/433 321/407/377 457/492/449 +f 424/460/430 458/484/450 460/496/455 +f 461/498/457 459/486/452 425/461/431 +f 440/482/448 470/502/461 460/496/455 +f 441/487/453 459/486/452 461/498/457 +f 440/482/448 442/476/442 468/501/460 +f 469/503/462 443/479/445 441/487/453 +f 444/477/443 466/505/464 468/501/460 +f 445/480/446 443/479/445 469/503/462 +f 446/474/440 464/508/467 466/505/464 +f 447/478/444 445/480/446 467/504/463 +f 446/474/440 448/471/437 462/507/466 +f 463/509/468 449/472/438 447/478/444 +f 448/471/437 426/449/419 482/510/469 +f 483/511/470 427/450/420 449/472/438 +f 436/458/428 424/460/430 484/497/456 +f 485/499/458 425/461/431 437/459/429 +f 434/456/426 436/458/428 472/512/471 +f 473/513/472 437/459/429 435/457/427 +f 432/453/423 434/456/426 474/514/473 +f 475/515/474 435/457/427 433/454/424 +f 432/453/423 476/516/475 478/518/477 +f 433/454/424 431/455/425 479/520/479 +f 430/519/478 478/518/477 480/521/480 +f 431/455/425 429/452/422 481/522/481 +f 428/451/421 480/521/480 482/510/469 +f 429/452/422 427/450/420 483/511/470 +f 464/508/467 488/526/485 486/523/482 +f 465/506/465 467/504/463 487/524/483 +f 488/526/485 490/569/513 492/527/486 +f 489/525/484 487/524/483 493/528/487 +f 492/527/486 490/569/513 496/530/489 +f 497/532/491 491/529/488 493/528/487 +f 496/530/489 498/537/496 500/534/493 +f 497/532/491 495/533/492 501/535/494 +f 472/512/471 484/497/456 494/531/490 +f 495/533/492 485/499/458 473/513/472 +f 492/527/486 494/531/490 484/497/456 +f 493/528/487 461/498/457 485/499/458 +f 470/502/461 486/523/482 492/527/486 +f 471/500/459 461/498/457 493/528/487 +f 466/505/464 486/523/482 470/502/461 +f 471/500/459 487/524/483 467/504/463 +f 482/510/469 488/526/485 464/508/467 +f 483/511/470 463/509/468 465/506/465 +f 480/521/480 490/569/513 488/526/485 +f 489/525/484 491/529/488 481/522/481 +f 496/530/489 490/569/513 480/521/480 +f 497/532/491 479/520/479 481/522/481 +f 498/537/496 496/530/489 478/518/477 +f 499/536/495 477/517/476 479/520/479 +f 474/514/473 500/534/493 498/537/496 +f 499/536/495 501/535/494 475/515/474 +f 400/441/411 398/445/415 512/538/497 +f 513/540/499 399/447/417 401/442/412 +f 402/437/407 400/441/411 510/539/498 +f 511/541/500 401/442/412 403/438/408 +f 402/437/407 508/542/501 506/544/503 +f 403/438/408 405/434/404 507/545/504 +f 404/433/403 506/544/503 504/546/505 +f 405/434/404 407/551/402 505/548/506 +f 406/547/399 504/546/505 502/549/507 +f 407/551/402 409/555/401 503/552/508 +f 408/550/394 502/549/507 514/553/509 +f 409/555/401 397/559/395 515/556/510 +f 510/539/498 512/538/497 514/553/509 +f 511/541/500 503/552/508 515/556/510 +f 502/549/507 504/546/505 508/542/501 +f 509/543/502 505/548/506 503/552/508 +f 390/408/378 396/570/392 514/557/509 +f 391/558/379 323/562/511 515/556/510 +f 322/560/370 514/553/509 512/538/497 +f 513/540/499 515/556/510 323/562/511 +f 328/466/384 330/561/387 512/538/497 +f 513/540/499 331/563/388 329/564/385 diff --git a/examples/src/ray_scene/mod.rs b/examples/src/ray_scene/mod.rs new file mode 100644 index 0000000000..db1772820a --- /dev/null +++ b/examples/src/ray_scene/mod.rs @@ -0,0 +1,571 @@ +use std::{borrow::Cow, future::Future, iter, mem, ops::Range, pin::Pin, task, time::Instant}; + +use bytemuck::{Pod, Zeroable}; +use glam::{Mat4, Quat, Vec3}; +use wgpu::util::DeviceExt; + +use rt::traits::*; +use wgpu::ray_tracing as rt; + +// from cube +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)] +struct Vertex { + pos: [f32; 3], + _p0: [u32; 1], + normal: [f32; 3], + _p1: [u32; 1], + uv: [f32; 2], + _p2: [u32; 2], +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct Uniforms { + view_inverse: [[f32; 4]; 4], + proj_inverse: [[f32; 4]; 4], +} + +/// A wrapper for `pop_error_scope` futures that panics if an error occurs. +/// +/// Given a future `inner` of an `Option` for some error type `E`, +/// wait for the future to be ready, and panic if its value is `Some`. +/// +/// This can be done simpler with `FutureExt`, but we don't want to add +/// a dependency just for this small case. +struct ErrorFuture { + inner: F, +} +impl>> Future for ErrorFuture { + type Output = (); + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll<()> { + let inner = unsafe { self.map_unchecked_mut(|me| &mut me.inner) }; + inner.poll(cx).map(|error| { + if let Some(e) = error { + panic!("Rendering {}", e); + } + }) + } +} + +#[derive(Debug, Clone, Default)] +struct RawSceneComponents { + vertices: Vec, + indices: Vec, + geometries: Vec<(Range, Material)>, // index range, material + instances: Vec<(Range, Range)>, // vertex range, geometry range +} + +#[allow(dead_code)] +struct SceneComponents { + vertices: wgpu::Buffer, + indices: wgpu::Buffer, + geometries: wgpu::Buffer, + instances: wgpu::Buffer, + bottom_level_acceleration_structures: Vec, +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct InstanceEntry { + first_vertex: u32, + first_geometry: u32, + last_geometry: u32, + _pad: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable, Default)] +struct GeometryEntry { + first_index: u32, + _p0: [u32; 3], + material: Material, +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable, Default, Debug)] +struct Material { + roughness_exponent: f32, + metalness: f32, + specularity: f32, + _p0: [u32; 1], + albedo: [f32; 3], + _p1: [u32; 1], +} + +fn load_model(scene: &mut RawSceneComponents, path: &str) { + let path = env!("CARGO_MANIFEST_DIR").to_string() + "/src" + path; + println!("{}", path); + let mut object = obj::Obj::load(path).unwrap(); + object.load_mtls().unwrap(); + + let data = object.data; + + let start_vertex_index = scene.vertices.len(); + let start_geometry_index = scene.geometries.len(); + + let mut mapping = std::collections::HashMap::<(usize, usize, usize), usize>::new(); + + let mut next_index = 0; + + for object in data.objects { + for group in object.groups { + let start_index_index = scene.indices.len(); + for poly in group.polys { + for end_index in 2..poly.0.len() { + for &index in &[0, end_index - 1, end_index] { + let obj::IndexTuple(position_id, texture_id, normal_id) = poly.0[index]; + let texture_id = texture_id.expect("uvs required"); + let normal_id = normal_id.expect("normals required"); + + let index = *mapping + .entry((position_id, texture_id, normal_id)) + .or_insert(next_index); + if index == next_index { + next_index += 1; + + scene.vertices.push(Vertex { + pos: data.position[position_id], + uv: data.texture[texture_id], + normal: data.normal[normal_id], + ..Default::default() + }) + } + + scene.indices.push(index as u32); + } + } + } + + let mut material: Material = Default::default(); + + if let Some(obj::ObjMaterial::Mtl(mat)) = group.material { + if let Some(kd) = mat.kd { + material.albedo = kd; + } + if let Some(ns) = mat.ns { + material.roughness_exponent = ns; + } + if let Some(ka) = mat.ka { + material.metalness = ka[0]; + } + if let Some(ks) = mat.ks { + material.specularity = ks[0]; + } + } + + scene + .geometries + .push((start_index_index..scene.indices.len(), material)); + } + } + scene.instances.push(( + start_vertex_index..scene.vertices.len(), + start_geometry_index..scene.geometries.len(), + )); + + // dbg!(scene.vertices.len()); + // dbg!(scene.indices.len()); + // dbg!(&scene.geometries); + // dbg!(&scene.instances); +} + +fn upload_scene_components( + device: &wgpu::Device, + queue: &wgpu::Queue, + scene: &RawSceneComponents, +) -> SceneComponents { + let geometry_buffer_content = scene + .geometries + .iter() + .map(|(index_range, material)| GeometryEntry { + first_index: index_range.start as u32, + material: *material, + ..Default::default() + }) + .collect::>(); + + let instance_buffer_content = scene + .instances + .iter() + .map(|geometry| InstanceEntry { + first_vertex: geometry.0.start as u32, + first_geometry: geometry.1.start as u32, + last_geometry: geometry.1.end as u32, + _pad: 1, + }) + .collect::>(); + + let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertices"), + contents: bytemuck::cast_slice(&scene.vertices), + usage: wgpu::BufferUsages::VERTEX + | wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::BLAS_INPUT, + }); + let indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Indices"), + contents: bytemuck::cast_slice(&scene.indices), + usage: wgpu::BufferUsages::INDEX + | wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::BLAS_INPUT, + }); + let geometries = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Geometries"), + contents: bytemuck::cast_slice(&geometry_buffer_content), + usage: wgpu::BufferUsages::STORAGE, + }); + let instances = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Instances"), + contents: bytemuck::cast_slice(&instance_buffer_content), + usage: wgpu::BufferUsages::STORAGE, + }); + + let (size_descriptors, bottom_level_acceleration_structures): (Vec<_>, Vec<_>) = scene + .instances + .iter() + .map(|(vertex_range, geometry_range)| { + let size_desc: Vec = (*geometry_range) + .clone() + .map(|i| rt::BlasTriangleGeometrySizeDescriptor { + vertex_format: wgpu::VertexFormat::Float32x3, + vertex_count: vertex_range.end as u32 - vertex_range.start as u32, + index_format: Some(wgpu::IndexFormat::Uint32), + index_count: Some( + scene.geometries[i].0.end as u32 - scene.geometries[i].0.start as u32, + ), + flags: rt::AccelerationStructureGeometryFlags::OPAQUE, + }) + .collect(); + + let blas = device.create_blas( + &rt::CreateBlasDescriptor { + label: None, + flags: rt::AccelerationStructureFlags::PREFER_FAST_TRACE, + update_mode: rt::AccelerationStructureUpdateMode::Build, + }, + rt::BlasGeometrySizeDescriptors::Triangles { + desc: size_desc.clone(), + }, + ); + (size_desc, blas) + }) + .unzip(); + + let build_entries: Vec<_> = scene + .instances + .iter() + .zip(size_descriptors.iter()) + .zip(bottom_level_acceleration_structures.iter()) + .map(|(((vertex_range, geometry_range), size_desc), blas)| { + let triangle_geometries: Vec<_> = size_desc + .iter() + .zip(geometry_range.clone()) + .map(|(size, i)| rt::BlasTriangleGeometry { + size, + vertex_buffer: &vertices, + first_vertex: vertex_range.start as u32, + vertex_stride: mem::size_of::() as u64, + index_buffer: Some(&indices), + index_buffer_offset: Some(scene.geometries[i].0.start as u64 * 4), + transform_buffer: None, + transform_buffer_offset: None, + }) + .collect(); + + rt::BlasBuildEntry { + blas, + geometry: rt::BlasGeometries::TriangleGeometries(triangle_geometries), + } + }) + .collect(); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.build_acceleration_structures(build_entries.iter(), iter::empty()); + + queue.submit(Some(encoder.finish())); + + SceneComponents { + vertices, + indices, + geometries, + instances, + bottom_level_acceleration_structures, + } +} + +fn load_scene(device: &wgpu::Device, queue: &wgpu::Queue) -> SceneComponents { + let mut scene = RawSceneComponents::default(); + + load_model(&mut scene, "/skybox/models/teslacyberv3.0.obj"); + load_model(&mut scene, "/ray_scene/cube.obj"); + + upload_scene_components(device, queue, &scene) +} + +#[allow(dead_code)] +struct Example { + uniforms: Uniforms, + uniform_buf: wgpu::Buffer, + tlas_package: rt::TlasPackage, + pipeline: wgpu::RenderPipeline, + bind_group: wgpu::BindGroup, + start_inst: Instant, + scene_components: SceneComponents, +} + +impl crate::framework::Example for Example { + fn required_features() -> wgpu::Features { + wgpu::Features::RAY_QUERY | wgpu::Features::RAY_TRACING_ACCELERATION_STRUCTURE + } + + fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { + wgpu::DownlevelCapabilities::default() + } + fn required_limits() -> wgpu::Limits { + wgpu::Limits::default() + } + + fn init( + config: &wgpu::SurfaceConfiguration, + _adapter: &wgpu::Adapter, + device: &wgpu::Device, + queue: &wgpu::Queue, + ) -> Self { + let side_count = 8; + + let scene_components = load_scene(device, queue); + + let uniforms = { + let view = Mat4::look_at_rh(Vec3::new(0.0, 0.0, 2.5), Vec3::ZERO, Vec3::Y); + let proj = Mat4::perspective_rh( + 59.0_f32.to_radians(), + config.width as f32 / config.height as f32, + 0.001, + 1000.0, + ); + + Uniforms { + view_inverse: view.inverse().to_cols_array_2d(), + proj_inverse: proj.inverse().to_cols_array_2d(), + } + }; + + let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Uniform Buffer"), + contents: bytemuck::cast_slice(&[uniforms]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + + let tlas = device.create_tlas(&rt::CreateTlasDescriptor { + label: None, + flags: rt::AccelerationStructureFlags::PREFER_FAST_TRACE, + update_mode: rt::AccelerationStructureUpdateMode::Build, + max_instances: side_count * side_count, + }); + + let tlas_package = rt::TlasPackage::new(tlas, side_count * side_count); + + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(config.format.into())], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + let bind_group_layout = pipeline.get_bind_group_layout(0); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: uniform_buf.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 5, + resource: tlas_package.as_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: scene_components.vertices.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: scene_components.indices.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: scene_components.geometries.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 4, + resource: scene_components.instances.as_entire_binding(), + }, + ], + }); + + let start_inst = Instant::now(); + + Example { + uniforms, + uniform_buf, + tlas_package, + pipeline, + bind_group, + start_inst, + scene_components, + } + } + + fn update(&mut self, _event: winit::event::WindowEvent) {} + + fn resize( + &mut self, + config: &wgpu::SurfaceConfiguration, + _device: &wgpu::Device, + queue: &wgpu::Queue, + ) { + let proj = Mat4::perspective_rh( + 59.0_f32.to_radians(), + config.width as f32 / config.height as f32, + 0.001, + 1000.0, + ); + + self.uniforms.proj_inverse = proj.inverse().to_cols_array_2d(); + + queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(&[self.uniforms])); + } + + fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) { + device.push_error_scope(wgpu::ErrorFilter::Validation); + + // scene update + { + let dist = 3.5; + + let side_count = 2; + + let anim_time = self.start_inst.elapsed().as_secs_f64() as f32; + + for x in 0..side_count { + for y in 0..side_count { + let instance = self + .tlas_package + .get_mut_single(x + y * side_count) + .unwrap(); + + let blas_index = (x + y) + % self + .scene_components + .bottom_level_acceleration_structures + .len(); + + let x = x as f32 / (side_count - 1) as f32; + let y = y as f32 / (side_count - 1) as f32; + let x = x * 2.0 - 1.0; + let y = y * 2.0 - 1.0; + + let transform = Mat4::from_rotation_translation( + Quat::from_euler( + glam::EulerRot::XYZ, + anim_time * 0.5 * 0.342, + anim_time * 0.5 * 0.254, + anim_time * 0.5 * 0.832, + ), + Vec3 { + x: x * dist, + y: y * dist, + z: -14.0, + }, + ); + let transform = transform.transpose().to_cols_array()[..12] + .try_into() + .unwrap(); + *instance = Some(rt::TlasInstance::new( + &self.scene_components.bottom_level_acceleration_structures[blas_index], + transform, + blas_index as u32, + 0xff, + )); + } + } + } + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.build_acceleration_structures(iter::empty(), iter::once(&self.tlas_package)); + + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::GREEN), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + rpass.set_pipeline(&self.pipeline); + rpass.set_bind_group(0, &self.bind_group, &[]); + rpass.draw(0..3, 0..1); + } + + queue.submit(Some(encoder.finish())); + } +} + +pub fn main() { + crate::framework::run::("ray_scene"); +} + +#[cfg(test)] +#[wgpu_test::gpu_test] +static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { + name: "ray_cube_fragment", + image_path: "/examples/ray_cube_fragment/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::default(), + base_test_parameters: wgpu_test::TestParameters { + required_features: ::required_features(), + required_limits: ::required_limits(), + skips: vec![], + failures: Vec::new(), + required_downlevel_caps: + ::required_downlevel_capabilities(), + }, + comparisons: &[wgpu_test::ComparisonType::Mean(0.02)], + _phantom: std::marker::PhantomData::, +}; diff --git a/examples/src/ray_scene/shader.wgsl b/examples/src/ray_scene/shader.wgsl new file mode 100644 index 0000000000..f3b1a61920 --- /dev/null +++ b/examples/src/ray_scene/shader.wgsl @@ -0,0 +1,168 @@ +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) tex_coords: vec2, +}; + +@vertex +fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { + var result: VertexOutput; + let x = i32(vertex_index) / 2; + let y = i32(vertex_index) & 1; + let tc = vec2( + f32(x) * 2.0, + f32(y) * 2.0 + ); + result.position = vec4( + tc.x * 2.0 - 1.0, + 1.0 - tc.y * 2.0, + 0.0, 1.0 + ); + result.tex_coords = tc; + return result; +} + +/* +let RAY_FLAG_NONE = 0x00u; +let RAY_FLAG_OPAQUE = 0x01u; +let RAY_FLAG_NO_OPAQUE = 0x02u; +let RAY_FLAG_TERMINATE_ON_FIRST_HIT = 0x04u; +let RAY_FLAG_SKIP_CLOSEST_HIT_SHADER = 0x08u; +let RAY_FLAG_CULL_BACK_FACING = 0x10u; +let RAY_FLAG_CULL_FRONT_FACING = 0x20u; +let RAY_FLAG_CULL_OPAQUE = 0x40u; +let RAY_FLAG_CULL_NO_OPAQUE = 0x80u; +let RAY_FLAG_SKIP_TRIANGLES = 0x100u; +let RAY_FLAG_SKIP_AABBS = 0x200u; + +let RAY_QUERY_INTERSECTION_NONE = 0u; +let RAY_QUERY_INTERSECTION_TRIANGLE = 1u; +let RAY_QUERY_INTERSECTION_GENERATED = 2u; +let RAY_QUERY_INTERSECTION_AABB = 4u; + +struct RayDesc { + flags: u32, + cull_mask: u32, + t_min: f32, + t_max: f32, + origin: vec3, + dir: vec3, +} + +struct RayIntersection { + kind: u32, + t: f32, + instance_custom_index: u32, + instance_id: u32, + sbt_record_offset: u32, + geometry_index: u32, + primitive_index: u32, + barycentrics: vec2, + front_face: bool, + object_to_world: mat4x3, + world_to_object: mat4x3, +} +*/ + +struct Uniforms { + view_inv: mat4x4, + proj_inv: mat4x4, +}; + +struct Vertex { + pos: vec3, + normal: vec3, + uv: vec2, +}; + + +struct Instance { + first_vertex: u32, + first_geometry: u32, + last_geometry: u32, + _pad: u32 +}; + +struct Material{ + r: f32, + m: f32, + s: f32, + albedo: vec3 +} + +struct Geometry { + first_index: u32, + material: Material, +}; + + +@group(0) @binding(0) +var uniforms: Uniforms; + +@group(0) @binding(1) +var vertices: array; + +@group(0) @binding(2) +var indices: array; + +@group(0) @binding(3) +var geometries: array; + +@group(0) @binding(4) +var instances: array; + +@group(0) @binding(5) +var acc_struct: acceleration_structure; + +@fragment +fn fs_main(vertex: VertexOutput) -> @location(0) vec4 { + + var color = vec4(vertex.tex_coords, 0.0, 1.0); + + let d = vertex.tex_coords * 2.0 - 1.0; + + let origin = (uniforms.view_inv * vec4(0.0,0.0,0.0,1.0)).xyz; + let temp = uniforms.proj_inv * vec4(d.x, d.y, 1.0, 1.0); + let direction = (uniforms.view_inv * vec4(normalize(temp.xyz), 0.0)).xyz; + + var rq: ray_query; + rayQueryInitialize(&rq, acc_struct, RayDesc(0u, 0xFFu, 0.1, 200.0, origin, direction)); + rayQueryProceed(&rq); + + let intersection = rayQueryGetCommittedIntersection(&rq); + if (intersection.kind != RAY_QUERY_INTERSECTION_NONE) { + let instance = instances[intersection.instance_custom_index]; + let geometry = geometries[intersection.geometry_index + instance.first_geometry]; + + let index_offset = geometry.first_index; + let vertex_offset = instance.first_vertex; + + let first_index_index = intersection.primitive_index * 3u + index_offset; + + let v_0 = vertices[vertex_offset+indices[first_index_index+0u]]; + let v_1 = vertices[vertex_offset+indices[first_index_index+1u]]; + let v_2 = vertices[vertex_offset+indices[first_index_index+2u]]; + + let bary = vec3(1.0 - intersection.barycentrics.x - intersection.barycentrics.y, intersection.barycentrics); + + let pos = v_0.pos * bary.x + v_1.pos * bary.y + v_2.pos * bary.z; + let normal_raw = v_0.normal * bary.x + v_1.normal * bary.y + v_2.normal * bary.z; + let uv = v_0.uv * bary.x + v_1.uv * bary.y + v_2.uv * bary.z; + + let normal = normalize(normal_raw); + + let material = geometry.material; + + color = vec4(material.albedo, 1.0); + + if(intersection.instance_custom_index == 1u){ + color = vec4(normal, 1.0); + } + } + + // let vertex = vertices[0]; + // let index = indices[0]; + // let geometry = geometries[0]; + // let instance = instances[0]; + + return color; // vec4(vertex.tex_coords, 1.0, 1.0); +} diff --git a/player/src/lib.rs b/player/src/lib.rs index eb89e43f73..9913ba6d17 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -123,6 +123,94 @@ impl GlobalPlay for wgc::global::Global { ) .unwrap(); } + trace::Command::BuildAccelerationStructuresUnsafeTlas { blas, tlas } => { + let blas_iter = blas.iter().map(|x| { + let geometries = match &x.geometries { + wgc::ray_tracing::TraceBlasGeometries::TriangleGeometries( + triangle_geometries, + ) => { + let iter = triangle_geometries.iter().map(|tg| { + wgc::ray_tracing::BlasTriangleGeometry { + size: &tg.size, + vertex_buffer: tg.vertex_buffer, + index_buffer: tg.index_buffer, + transform_buffer: tg.transform_buffer, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + transform_buffer_offset: tg.transform_buffer_offset, + } + }); + wgc::ray_tracing::BlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + wgc::ray_tracing::BlasBuildEntry { + blas_id: x.blas_id, + geometries, + } + }); + + if !tlas.is_empty() { + log::error!("a trace of command_encoder_build_acceleration_structures_unsafe_tlas containing a tlas build is not replayable! skipping tlas build"); + } + + self.command_encoder_build_acceleration_structures_unsafe_tlas::( + encoder, + blas_iter, + std::iter::empty(), + ) + .unwrap(); + } + trace::Command::BuildAccelerationStructures { blas, tlas } => { + let blas_iter = blas.iter().map(|x| { + let geometries = match &x.geometries { + wgc::ray_tracing::TraceBlasGeometries::TriangleGeometries( + triangle_geometries, + ) => { + let iter = triangle_geometries.iter().map(|tg| { + wgc::ray_tracing::BlasTriangleGeometry { + size: &tg.size, + vertex_buffer: tg.vertex_buffer, + index_buffer: tg.index_buffer, + transform_buffer: tg.transform_buffer, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + transform_buffer_offset: tg.transform_buffer_offset, + } + }); + wgc::ray_tracing::BlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + wgc::ray_tracing::BlasBuildEntry { + blas_id: x.blas_id, + geometries, + } + }); + + let tlas_iter = tlas.iter().map(|x| { + let instances = x.instances.iter().map(|instance| { + instance + .as_ref() + .map(|instance| wgc::ray_tracing::TlasInstance { + blas_id: instance.blas_id, + transform: &instance.transform, + custom_index: instance.custom_index, + mask: instance.mask, + }) + }); + wgc::ray_tracing::TlasPackage { + tlas_id: x.tlas_id, + instances: Box::new(instances), + lowest_unmodified: x.lowest_unmodified, + } + }); + + self.command_encoder_build_acceleration_structures::( + encoder, blas_iter, tlas_iter, + ) + .unwrap(); + } } } let (cmd_buf, error) = self @@ -370,6 +458,26 @@ impl GlobalPlay for wgc::global::Global { self.queue_submit::(device.transmute(), &[cmdbuf]) .unwrap(); } + Action::CreateBlas { id, desc, sizes } => { + self.device_maintain_ids::(device).unwrap(); + self.device_create_blas::(device, &desc, sizes, Some(id)); + } + Action::FreeBlas(id) => { + self.blas_destroy::(id).unwrap(); + } + Action::DestroyBlas(id) => { + self.blas_drop::(id, true); + } + Action::CreateTlas { id, desc } => { + self.device_maintain_ids::(device).unwrap(); + self.device_create_tlas::(device, &desc, Some(id)); + } + Action::FreeTlas(id) => { + self.tlas_destroy::(id).unwrap(); + } + Action::DestroyTlas(id) => { + self.tlas_drop::(id, true); + } } } } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index c4cbaa9e3e..cc7683f92a 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -39,6 +39,7 @@ serde.workspace = true wgpu-macros.workspace = true wgpu.workspace = true wgt = { workspace = true, features = ["serde"] } +glam.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] env_logger.workspace = true diff --git a/tests/tests/ray_tracing/mesh_gen.rs b/tests/tests/ray_tracing/mesh_gen.rs new file mode 100644 index 0000000000..8bc4b03b7d --- /dev/null +++ b/tests/tests/ray_tracing/mesh_gen.rs @@ -0,0 +1,207 @@ +use bytemuck::{Pod, Zeroable}; +use glam::Affine3A; + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +pub struct Vertex { + _pos: [f32; 4], + _tex_coord: [f32; 2], +} + +fn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex { + Vertex { + _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0], + _tex_coord: [tc[0] as f32, tc[1] as f32], + } +} + +pub fn create_vertices() -> (Vec, Vec) { + let vertex_data = [ + // top (0, 0, 1) + vertex([-1, -1, 1], [0, 0]), + vertex([1, -1, 1], [1, 0]), + vertex([1, 1, 1], [1, 1]), + vertex([-1, 1, 1], [0, 1]), + // bottom (0, 0, -1) + vertex([-1, 1, -1], [1, 0]), + vertex([1, 1, -1], [0, 0]), + vertex([1, -1, -1], [0, 1]), + vertex([-1, -1, -1], [1, 1]), + // right (1, 0, 0) + vertex([1, -1, -1], [0, 0]), + vertex([1, 1, -1], [1, 0]), + vertex([1, 1, 1], [1, 1]), + vertex([1, -1, 1], [0, 1]), + // left (-1, 0, 0) + vertex([-1, -1, 1], [1, 0]), + vertex([-1, 1, 1], [0, 0]), + vertex([-1, 1, -1], [0, 1]), + vertex([-1, -1, -1], [1, 1]), + // front (0, 1, 0) + vertex([1, 1, -1], [1, 0]), + vertex([-1, 1, -1], [0, 0]), + vertex([-1, 1, 1], [0, 1]), + vertex([1, 1, 1], [1, 1]), + // back (0, -1, 0) + vertex([1, -1, 1], [0, 0]), + vertex([-1, -1, 1], [1, 0]), + vertex([-1, -1, -1], [1, 1]), + vertex([1, -1, -1], [0, 1]), + ]; + + let index_data: &[u16] = &[ + 0, 1, 2, 2, 3, 0, // top + 4, 5, 6, 6, 7, 4, // bottom + 8, 9, 10, 10, 11, 8, // right + 12, 13, 14, 14, 15, 12, // left + 16, 17, 18, 18, 19, 16, // front + 20, 21, 22, 22, 23, 20, // back + ]; + + (vertex_data.to_vec(), index_data.to_vec()) +} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +pub struct AccelerationStructureInstance { + transform: [f32; 12], + custom_index_and_mask: u32, + shader_binding_table_record_offset_and_flags: u32, + acceleration_structure_reference: u64, +} + +impl std::fmt::Debug for AccelerationStructureInstance { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Instance") + .field("transform", &self.transform) + .field("custom_index()", &self.custom_index()) + .field("mask()", &self.mask()) + .field( + "shader_binding_table_record_offset()", + &self.shader_binding_table_record_offset(), + ) + .field("flags()", &self.flags()) + .field( + "acceleration_structure_reference", + &self.acceleration_structure_reference, + ) + .finish() + } +} + +#[allow(dead_code)] +impl AccelerationStructureInstance { + const LOW_24_MASK: u32 = 0x00ff_ffff; + const MAX_U24: u32 = (1u32 << 24u32) - 1u32; + + #[inline] + pub fn affine_to_rows(mat: &Affine3A) -> [f32; 12] { + let row_0 = mat.matrix3.row(0); + let row_1 = mat.matrix3.row(1); + let row_2 = mat.matrix3.row(2); + let translation = mat.translation; + [ + row_0.x, + row_0.y, + row_0.z, + translation.x, + row_1.x, + row_1.y, + row_1.z, + translation.y, + row_2.x, + row_2.y, + row_2.z, + translation.z, + ] + } + + #[inline] + fn rows_to_affine(rows: &[f32; 12]) -> Affine3A { + Affine3A::from_cols_array(&[ + rows[0], rows[3], rows[6], rows[9], rows[1], rows[4], rows[7], rows[10], rows[2], + rows[5], rows[8], rows[11], + ]) + } + + pub fn transform_as_affine(&self) -> Affine3A { + Self::rows_to_affine(&self.transform) + } + pub fn set_transform(&mut self, transform: &Affine3A) { + self.transform = Self::affine_to_rows(transform); + } + + pub fn custom_index(&self) -> u32 { + self.custom_index_and_mask & Self::LOW_24_MASK + } + + pub fn mask(&self) -> u8 { + (self.custom_index_and_mask >> 24) as u8 + } + + pub fn shader_binding_table_record_offset(&self) -> u32 { + self.shader_binding_table_record_offset_and_flags & Self::LOW_24_MASK + } + + pub fn flags(&self) -> u8 { + (self.shader_binding_table_record_offset_and_flags >> 24) as u8 + } + + pub fn set_custom_index(&mut self, custom_index: u32) { + debug_assert!( + custom_index <= Self::MAX_U24, + "custom_index uses more than 24 bits! {custom_index} > {}", + Self::MAX_U24 + ); + self.custom_index_and_mask = + (custom_index & Self::LOW_24_MASK) | (self.custom_index_and_mask & !Self::LOW_24_MASK) + } + + pub fn set_mask(&mut self, mask: u8) { + self.custom_index_and_mask = + (self.custom_index_and_mask & Self::LOW_24_MASK) | (u32::from(mask) << 24) + } + + pub fn set_shader_binding_table_record_offset( + &mut self, + shader_binding_table_record_offset: u32, + ) { + debug_assert!(shader_binding_table_record_offset <= Self::MAX_U24, "shader_binding_table_record_offset uses more than 24 bits! {shader_binding_table_record_offset} > {}", Self::MAX_U24); + self.shader_binding_table_record_offset_and_flags = (shader_binding_table_record_offset + & Self::LOW_24_MASK) + | (self.shader_binding_table_record_offset_and_flags & !Self::LOW_24_MASK) + } + + pub fn set_flags(&mut self, flags: u8) { + self.shader_binding_table_record_offset_and_flags = + (self.shader_binding_table_record_offset_and_flags & Self::LOW_24_MASK) + | (u32::from(flags) << 24) + } + + pub fn new( + transform: &Affine3A, + custom_index: u32, + mask: u8, + shader_binding_table_record_offset: u32, + flags: u8, + acceleration_structure_reference: u64, + ) -> Self { + debug_assert!( + custom_index <= Self::MAX_U24, + "custom_index uses more than 24 bits! {custom_index} > {}", + Self::MAX_U24 + ); + debug_assert!( + shader_binding_table_record_offset <= Self::MAX_U24, + "shader_binding_table_record_offset uses more than 24 bits! {shader_binding_table_record_offset} > {}", Self::MAX_U24 + ); + AccelerationStructureInstance { + transform: Self::affine_to_rows(transform), + custom_index_and_mask: (custom_index & Self::MAX_U24) | (u32::from(mask) << 24), + shader_binding_table_record_offset_and_flags: (shader_binding_table_record_offset + & Self::MAX_U24) + | (u32::from(flags) << 24), + acceleration_structure_reference, + } + } +} diff --git a/tests/tests/ray_tracing/mod.rs b/tests/tests/ray_tracing/mod.rs new file mode 100644 index 0000000000..e3440034df --- /dev/null +++ b/tests/tests/ray_tracing/mod.rs @@ -0,0 +1,122 @@ +use std::{iter, mem}; + +use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; + +use rt::traits::*; +use wgpu::ray_tracing as rt; +use wgpu::util::DeviceExt; + +use glam::{Affine3A, Quat, Vec3}; + +use mesh_gen::{AccelerationStructureInstance, Vertex}; + +mod mesh_gen; + +fn required_features() -> wgpu::Features { + wgpu::Features::TEXTURE_BINDING_ARRAY + | wgpu::Features::STORAGE_RESOURCE_BINDING_ARRAY + | wgpu::Features::VERTEX_WRITABLE_STORAGE + | wgpu::Features::RAY_QUERY + | wgpu::Features::RAY_TRACING_ACCELERATION_STRUCTURE +} + +fn execute(ctx: TestingContext) { + let max_instances = 1000; + let device = &ctx.device; + + let (vertex_data, index_data) = mesh_gen::create_vertices(); + + let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&vertex_data), + usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::BLAS_INPUT, + }); + + let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&index_data), + usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::BLAS_INPUT, + }); + + let blas_geo_size_desc = rt::BlasTriangleGeometrySizeDescriptor { + vertex_format: wgpu::VertexFormat::Float32x4, + vertex_count: vertex_data.len() as u32, + index_format: Some(wgpu::IndexFormat::Uint16), + index_count: Some(index_data.len() as u32), + flags: rt::AccelerationStructureGeometryFlags::OPAQUE, + }; + + let blas = device.create_blas( + &rt::CreateBlasDescriptor { + label: None, + flags: rt::AccelerationStructureFlags::PREFER_FAST_TRACE, + update_mode: rt::AccelerationStructureUpdateMode::Build, + }, + rt::BlasGeometrySizeDescriptors::Triangles { + desc: vec![blas_geo_size_desc.clone()], + }, + ); + + let tlas = device.create_tlas(&rt::CreateTlasDescriptor { + label: None, + flags: rt::AccelerationStructureFlags::PREFER_FAST_TRACE, + update_mode: rt::AccelerationStructureUpdateMode::Build, + max_instances, + }); + + let mut tlas_package = rt::TlasPackage::new(tlas, max_instances); + + for i in 0..10000 { + for j in 0..max_instances { + *tlas_package.get_mut_single(0).unwrap() = Some(rt::TlasInstance::new( + &blas, + AccelerationStructureInstance::affine_to_rows( + &Affine3A::from_rotation_translation( + Quat::from_rotation_y(45.9_f32.to_radians()), + Vec3 { + x: j as f32, + y: i as f32, + z: 0.0, + }, + ), + ), + 0, + 0xff, + )); + } + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.build_acceleration_structures( + iter::once(&rt::BlasBuildEntry { + blas: &blas, + geometry: rt::BlasGeometries::TriangleGeometries(vec![rt::BlasTriangleGeometry { + size: &blas_geo_size_desc, + vertex_buffer: &vertex_buf, + first_vertex: 0, + vertex_stride: mem::size_of::() as u64, + index_buffer: Some(&index_buf), + index_buffer_offset: Some(0), + transform_buffer: None, + transform_buffer_offset: None, + }]), + }), + // iter::empty(), + iter::once(&tlas_package), + ); + + ctx.queue.submit(Some(encoder.finish())); + } + + ctx.device.poll(wgpu::Maintain::Wait); +} + +#[gpu_test] +static RAY_TRACING: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .test_features_limits() + .features(required_features()), + ) + .run_sync(execute); diff --git a/tests/tests/root.rs b/tests/tests/root.rs index f886c0f9eb..ec58927d16 100644 --- a/tests/tests/root.rs +++ b/tests/tests/root.rs @@ -27,6 +27,7 @@ mod poll; mod push_constants; mod query_set; mod queue_transfer; +mod ray_tracing; mod resource_descriptor_accessor; mod resource_error; mod scissor_tests; diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index b32250646a..1460d8c478 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -6,7 +6,7 @@ use crate::{ }, error::{ErrorFormatter, PrettyError}, hal_api::HalApi, - id::{BindGroupLayoutId, BufferId, SamplerId, TextureId, TextureViewId}, + id::{BindGroupLayoutId, BufferId, SamplerId, TextureId, TextureViewId, TlasId}, init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction}, resource::{Resource, ResourceInfo, ResourceType}, resource_log, @@ -183,6 +183,8 @@ pub enum CreateBindGroupError { StorageReadNotSupported(wgt::TextureFormat), #[error(transparent)] ResourceUsageConflict(#[from] UsageConflict), + #[error("Tlas {0:?} is invalid or destroyed")] + InvalidTlas(TlasId), } impl PrettyError for CreateBindGroupError { @@ -309,6 +311,7 @@ pub(crate) struct BindingTypeMaxCountValidator { storage_buffers: PerStageBindingTypeCounter, storage_textures: PerStageBindingTypeCounter, uniform_buffers: PerStageBindingTypeCounter, + acceleration_structures: PerStageBindingTypeCounter, } impl BindingTypeMaxCountValidator { @@ -344,7 +347,9 @@ impl BindingTypeMaxCountValidator { wgt::BindingType::StorageTexture { .. } => { self.storage_textures.add(binding.visibility, count); } - wgt::BindingType::AccelerationStructure => todo!(), + wgt::BindingType::AccelerationStructure => { + self.acceleration_structures.add(binding.visibility, count); + } } } @@ -751,6 +756,7 @@ pub enum BindingResource<'a> { SamplerArray(Cow<'a, [SamplerId]>), TextureView(TextureViewId), TextureViewArray(Cow<'a, [TextureViewId]>), + AccelerationStructure(TlasId), } #[derive(Clone, Debug, Error)] diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index b95a90dee8..d175d63036 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -403,6 +403,7 @@ impl Global { let query_set_guard = hub.query_sets.read(); let buffer_guard = hub.buffers.read(); let texture_guard = hub.textures.read(); + let tlas_guard = hub.tlas_s.read(); let mut state = State { binder: Binder::new(), @@ -462,6 +463,8 @@ impl Global { None, None, Some(&*query_set_guard), + None, + Some(&*tlas_guard), ); let discard_hal_labels = self @@ -532,6 +535,20 @@ impl Global { .extend(texture_memory_actions.register_init_action(action)); } + let used_resource = bind_group + .used + .acceleration_structures + .used_resources() + .map(|tlas| { + tracker.tlas_s.add_single(&tlas_guard, tlas.as_info().id()); + crate::ray_tracing::TlasAction { + id: tlas.as_info().id(), + kind: crate::ray_tracing::TlasActionKind::Use, + } + }); + + cmd_buf_data.tlas_actions.extend(used_resource); + let pipeline_layout = state.binder.pipeline_layout.clone(); let entries = state diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index 416c0b057e..283d50858c 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -5,6 +5,7 @@ mod compute; mod draw; mod memory_init; mod query; +mod ray_tracing; mod render; mod transfer; @@ -25,6 +26,7 @@ use crate::id::CommandBufferId; use crate::snatch::SnatchGuard; use crate::init_tracker::BufferInitTrackerAction; +use crate::ray_tracing::{BlasAction, TlasAction}; use crate::resource::{Resource, ResourceInfo, ResourceType}; use crate::track::{Tracker, UsageScope}; use crate::{api_log, global::Global, hal_api::HalApi, id, resource_log, Label}; @@ -106,6 +108,8 @@ pub struct BakedCommands { pub(crate) trackers: Tracker, buffer_memory_init_actions: Vec>, texture_memory_actions: CommandBufferTextureMemoryActions, + blas_actions: Vec, + tlas_actions: Vec, } pub(crate) struct DestroyedBufferError(pub id::BufferId); @@ -118,6 +122,8 @@ pub struct CommandBufferMutable { buffer_memory_init_actions: Vec>, texture_memory_actions: CommandBufferTextureMemoryActions, pub(crate) pending_query_resets: QueryResetMap, + blas_actions: Vec, + tlas_actions: Vec, #[cfg(feature = "trace")] pub(crate) commands: Option>, } @@ -175,6 +181,7 @@ impl CommandBuffer { .unwrap_or(&String::from("")) .as_str(), ), + //Todo come back data: Mutex::new(Some(CommandBufferMutable { encoder: CommandEncoder { raw: encoder, @@ -187,6 +194,8 @@ impl CommandBuffer { buffer_memory_init_actions: Default::default(), texture_memory_actions: Default::default(), pending_query_resets: QueryResetMap::new(), + blas_actions: Default::default(), + tlas_actions: Default::default(), #[cfg(feature = "trace")] commands: if enable_tracing { Some(Vec::new()) @@ -281,6 +290,8 @@ impl CommandBuffer { trackers: data.trackers, buffer_memory_init_actions: data.buffer_memory_init_actions, texture_memory_actions: data.texture_memory_actions, + blas_actions: data.blas_actions, + tlas_actions: data.tlas_actions, } } diff --git a/wgpu-core/src/command/ray_tracing.rs b/wgpu-core/src/command/ray_tracing.rs new file mode 100644 index 0000000000..8324fb99f6 --- /dev/null +++ b/wgpu-core/src/command/ray_tracing.rs @@ -0,0 +1,1638 @@ +use crate::{ + command::CommandBuffer, + device::queue::TempResource, + global::Global, + hal_api::HalApi, + id::CommandEncoderId, + init_tracker::MemoryInitKind, + ray_tracing::{ + tlas_instance_into_bytes, BlasAction, BlasBuildEntry, BlasGeometries, + BuildAccelerationStructureError, TlasAction, TlasBuildEntry, TlasPackage, + ValidateBlasActionsError, ValidateTlasActionsError, + }, + resource::{Blas, Tlas}, + storage::Storage, + FastHashSet, +}; + +use wgt::{math::align_to, BufferUsages}; + +use crate::ray_tracing::BlasTriangleGeometry; +use crate::resource::{Buffer, Resource, ResourceInfo, StagingBuffer}; +use crate::track::PendingTransition; +use hal::{BufferUses, CommandEncoder, Device}; +use parking_lot::{Mutex, RwLockReadGuard}; +use std::ops::Deref; +use std::sync::Arc; +use std::{cmp::max, iter, num::NonZeroU64, ops::Range, ptr}; + +use super::BakedCommands; + +// This should be queried from the device, maybe the the hal api should pre aline it, since I am unsure how else we can idiomatically get this value. +const SCRATCH_BUFFER_ALIGNMENT: u32 = 256; + +impl Global { + pub fn command_encoder_build_acceleration_structures_unsafe_tlas<'a, A: HalApi>( + &self, + command_encoder_id: CommandEncoderId, + blas_iter: impl Iterator>, + tlas_iter: impl Iterator, + ) -> Result<(), BuildAccelerationStructureError> { + profiling::scope!("CommandEncoder::build_acceleration_structures_unsafe_tlas"); + + let hub = A::hub(self); + + let device_guard = hub.devices.write(); + let cmd_buf = CommandBuffer::get_encoder(hub, command_encoder_id)?; + let buffer_guard = hub.buffers.read(); + let blas_guard = hub.blas_s.read(); + let tlas_guard = hub.tlas_s.read(); + + let device = &mut device_guard.get(cmd_buf.device.as_info().id()).unwrap(); + + let build_command_index = NonZeroU64::new( + device + .last_acceleration_structure_build_command_index + .fetch_add(1, std::sync::atomic::Ordering::Relaxed) + + 1, + ) + .unwrap(); + + #[cfg(feature = "trace")] + let trace_blas: Vec = blas_iter + .map(|x| { + let geometries = match x.geometries { + BlasGeometries::TriangleGeometries(triangle_geometries) => { + crate::ray_tracing::TraceBlasGeometries::TriangleGeometries( + triangle_geometries + .map(|tg| crate::ray_tracing::TraceBlasTriangleGeometry { + size: tg.size.clone(), + vertex_buffer: tg.vertex_buffer, + index_buffer: tg.index_buffer, + transform_buffer: tg.transform_buffer, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + transform_buffer_offset: tg.transform_buffer_offset, + }) + .collect(), + ) + } + }; + crate::ray_tracing::TraceBlasBuildEntry { + blas_id: x.blas_id, + geometries, + } + }) + .collect(); + + #[cfg(feature = "trace")] + let trace_tlas: Vec = tlas_iter.collect(); + #[cfg(feature = "trace")] + if let Some(ref mut list) = cmd_buf.data.lock().as_mut().unwrap().commands { + list.push( + crate::device::trace::Command::BuildAccelerationStructuresUnsafeTlas { + blas: trace_blas.clone(), + tlas: trace_tlas.clone(), + }, + ); + if !trace_tlas.is_empty() { + log::warn!("a trace of command_encoder_build_acceleration_structures_unsafe_tlas containing a tlas build is not replayable!"); + } + } + + #[cfg(feature = "trace")] + let blas_iter = trace_blas.iter().map(|x| { + let geometries = match &x.geometries { + crate::ray_tracing::TraceBlasGeometries::TriangleGeometries( + triangle_geometries, + ) => { + let iter = triangle_geometries.iter().map(|tg| BlasTriangleGeometry { + size: &tg.size, + vertex_buffer: tg.vertex_buffer, + index_buffer: tg.index_buffer, + transform_buffer: tg.transform_buffer, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + transform_buffer_offset: tg.transform_buffer_offset, + }); + BlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + BlasBuildEntry { + blas_id: x.blas_id, + geometries, + } + }); + + #[cfg(feature = "trace")] + let tlas_iter = trace_tlas.iter(); + + let mut input_barriers = Vec::>::new(); + let mut buf_storage = Vec::<( + Arc>, + Option>, + Option<(Arc>, Option>)>, + Option<(Arc>, Option>)>, + BlasTriangleGeometry, + Option>>, + )>::new(); + + let mut scratch_buffer_blas_size = 0; + let mut blas_storage = Vec::<(&Blas, hal::AccelerationStructureEntries, u64)>::new(); + let mut cmd_buf_data = cmd_buf.data.lock(); + let cmd_buf_data = cmd_buf_data.as_mut().unwrap(); + for entry in blas_iter { + let blas = cmd_buf_data + .trackers + .blas_s + .add_single(&blas_guard, entry.blas_id) + .ok_or(BuildAccelerationStructureError::InvalidBlas(entry.blas_id))?; + + if blas.raw.is_none() { + return Err(BuildAccelerationStructureError::InvalidBlas(entry.blas_id)); + } + + cmd_buf_data.blas_actions.push(BlasAction { + id: entry.blas_id, + kind: crate::ray_tracing::BlasActionKind::Build(build_command_index), + }); + + match entry.geometries { + BlasGeometries::TriangleGeometries(triangle_geometries) => { + for (i, mesh) in triangle_geometries.enumerate() { + let size_desc = match &blas.sizes { + wgt::BlasGeometrySizeDescriptors::Triangles { desc } => desc, + }; + if i >= size_desc.len() { + return Err( + BuildAccelerationStructureError::IncompatibleBlasBuildSizes( + entry.blas_id, + ), + ); + } + let size_desc = &size_desc[i]; + + if size_desc.flags != mesh.size.flags + || size_desc.vertex_count < mesh.size.vertex_count + || size_desc.vertex_format != mesh.size.vertex_format + || size_desc.index_count.is_none() != mesh.size.index_count.is_none() + || (size_desc.index_count.is_none() + || size_desc.index_count.unwrap() < mesh.size.index_count.unwrap()) + || size_desc.index_format.is_none() != mesh.size.index_format.is_none() + || (size_desc.index_format.is_none() + || size_desc.index_format.unwrap() + != mesh.size.index_format.unwrap()) + { + return Err( + BuildAccelerationStructureError::IncompatibleBlasBuildSizes( + entry.blas_id, + ), + ); + } + + if size_desc.index_count.is_some() && mesh.index_buffer.is_none() { + return Err(BuildAccelerationStructureError::MissingIndexBuffer( + entry.blas_id, + )); + } + let (vertex_buffer, vertex_pending) = cmd_buf_data + .trackers + .buffers + .set_single( + match buffer_guard.get(mesh.vertex_buffer) { + Ok(buffer) => buffer, + Err(_) => { + return Err(BuildAccelerationStructureError::InvalidBuffer( + mesh.vertex_buffer, + )) + } + }, + BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, + ) + .ok_or(BuildAccelerationStructureError::InvalidBuffer( + mesh.vertex_buffer, + ))?; + let index_data = if let Some(index_id) = mesh.index_buffer { + if mesh.index_buffer_offset.is_none() + || mesh.size.index_count.is_none() + || mesh.size.index_count.is_none() + { + return Err( + BuildAccelerationStructureError::MissingAssociatedData( + index_id, + ), + ); + } + let data = cmd_buf_data + .trackers + .buffers + .set_single( + match buffer_guard.get(index_id) { + Ok(buffer) => buffer, + Err(_) => { + return Err( + BuildAccelerationStructureError::InvalidBuffer( + index_id, + ), + ) + } + }, + hal::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, + ) + .ok_or(BuildAccelerationStructureError::InvalidBuffer(index_id))?; + Some(data) + } else { + None + }; + let transform_data = if let Some(transform_id) = mesh.transform_buffer { + if mesh.transform_buffer_offset.is_none() { + return Err( + BuildAccelerationStructureError::MissingAssociatedData( + transform_id, + ), + ); + } + let data = cmd_buf_data + .trackers + .buffers + .set_single( + match buffer_guard.get(transform_id) { + Ok(buffer) => buffer, + Err(_) => { + return Err( + BuildAccelerationStructureError::InvalidBuffer( + transform_id, + ), + ) + } + }, + BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, + ) + .ok_or(BuildAccelerationStructureError::InvalidBuffer( + transform_id, + ))?; + Some(data) + } else { + None + }; + + buf_storage.push(( + vertex_buffer, + vertex_pending, + index_data, + transform_data, + mesh, + None, + )) + } + if let Some(last) = buf_storage.last_mut() { + last.5 = Some(blas.clone()); + } + } + } + } + + let mut triangle_entries = Vec::>::new(); + let snatch_guard = device.snatchable_lock.read(); + for buf in &mut buf_storage { + let mesh = &buf.4; + let vertex_buffer = { + let vertex_buffer = buf.0.as_ref(); + let vertex_raw = vertex_buffer.raw.get(&snatch_guard).ok_or( + BuildAccelerationStructureError::InvalidBuffer(mesh.vertex_buffer), + )?; + if !vertex_buffer.usage.contains(BufferUsages::BLAS_INPUT) { + return Err(BuildAccelerationStructureError::MissingBlasInputUsageFlag( + mesh.vertex_buffer, + )); + } + if let Some(barrier) = buf + .1 + .take() + .map(|pending| pending.into_hal(vertex_buffer, &snatch_guard)) + { + input_barriers.push(barrier); + } + if vertex_buffer.size + < (mesh.size.vertex_count + mesh.first_vertex) as u64 * mesh.vertex_stride + { + return Err(BuildAccelerationStructureError::InsufficientBufferSize( + mesh.vertex_buffer, + vertex_buffer.size, + (mesh.size.vertex_count + mesh.first_vertex) as u64 * mesh.vertex_stride, + )); + } + let vertex_buffer_offset = mesh.first_vertex as u64 * mesh.vertex_stride; + cmd_buf_data.buffer_memory_init_actions.extend( + vertex_buffer.initialization_status.read().create_action( + buffer_guard.get(mesh.vertex_buffer).unwrap(), + vertex_buffer_offset + ..(vertex_buffer_offset + + mesh.size.vertex_count as u64 * mesh.vertex_stride), + MemoryInitKind::NeedsInitializedMemory, + ), + ); + vertex_raw + }; + let index_buffer = if let Some((ref mut index_buffer, ref mut index_pending)) = buf.2 { + let index_id = mesh.index_buffer.as_ref().unwrap(); + let index_raw = index_buffer + .raw + .get(&snatch_guard) + .ok_or(BuildAccelerationStructureError::InvalidBuffer(*index_id))?; + if !index_buffer.usage.contains(BufferUsages::BLAS_INPUT) { + return Err(BuildAccelerationStructureError::MissingBlasInputUsageFlag( + *index_id, + )); + } + if let Some(barrier) = index_pending + .take() + .map(|pending| pending.into_hal(index_buffer, &snatch_guard)) + { + input_barriers.push(barrier); + } + let index_stride = match mesh.size.index_format.unwrap() { + wgt::IndexFormat::Uint16 => 2, + wgt::IndexFormat::Uint32 => 4, + }; + if mesh.index_buffer_offset.unwrap() % index_stride != 0 { + return Err(BuildAccelerationStructureError::UnalignedIndexBufferOffset( + *index_id, + )); + } + let index_buffer_size = mesh.size.index_count.unwrap() as u64 * index_stride; + + if mesh.size.index_count.unwrap() % 3 != 0 { + return Err(BuildAccelerationStructureError::InvalidIndexCount( + *index_id, + mesh.size.index_count.unwrap(), + )); + } + if index_buffer.size + < mesh.size.index_count.unwrap() as u64 * index_stride + + mesh.index_buffer_offset.unwrap() + { + return Err(BuildAccelerationStructureError::InsufficientBufferSize( + *index_id, + index_buffer.size, + mesh.size.index_count.unwrap() as u64 * index_stride + + mesh.index_buffer_offset.unwrap(), + )); + } + + cmd_buf_data.buffer_memory_init_actions.extend( + index_buffer.initialization_status.read().create_action( + match buffer_guard.get(*index_id) { + Ok(buffer) => buffer, + Err(_) => { + return Err(BuildAccelerationStructureError::InvalidBuffer( + *index_id, + )) + } + }, + mesh.index_buffer_offset.unwrap() + ..(mesh.index_buffer_offset.unwrap() + index_buffer_size), + MemoryInitKind::NeedsInitializedMemory, + ), + ); + Some(index_raw) + } else { + None + }; + let transform_buffer = + if let Some((ref mut transform_buffer, ref mut transform_pending)) = buf.3 { + let transform_id = mesh.transform_buffer.as_ref().unwrap(); + if mesh.transform_buffer_offset.is_none() { + return Err(BuildAccelerationStructureError::MissingAssociatedData( + *transform_id, + )); + } + let transform_raw = transform_buffer.raw.get(&snatch_guard).ok_or( + BuildAccelerationStructureError::InvalidBuffer(*transform_id), + )?; + if !transform_buffer.usage.contains(BufferUsages::BLAS_INPUT) { + return Err(BuildAccelerationStructureError::MissingBlasInputUsageFlag( + *transform_id, + )); + } + if let Some(barrier) = transform_pending + .take() + .map(|pending| pending.into_hal(transform_buffer, &snatch_guard)) + { + input_barriers.push(barrier); + } + if mesh.transform_buffer_offset.unwrap() % wgt::TRANSFORM_BUFFER_ALIGNMENT != 0 + { + return Err( + BuildAccelerationStructureError::UnalignedTransformBufferOffset( + *transform_id, + ), + ); + } + if transform_buffer.size < 48 + mesh.transform_buffer_offset.unwrap() { + return Err(BuildAccelerationStructureError::InsufficientBufferSize( + *transform_id, + transform_buffer.size, + 48 + mesh.transform_buffer_offset.unwrap(), + )); + } + cmd_buf_data.buffer_memory_init_actions.extend( + transform_buffer.initialization_status.read().create_action( + buffer_guard.get(*transform_id).unwrap(), + mesh.transform_buffer_offset.unwrap() + ..(mesh.index_buffer_offset.unwrap() + 48), + MemoryInitKind::NeedsInitializedMemory, + ), + ); + Some(transform_raw) + } else { + None + }; + + let triangles = hal::AccelerationStructureTriangles { + vertex_buffer: Some(vertex_buffer), + vertex_format: mesh.size.vertex_format, + first_vertex: mesh.first_vertex, + vertex_count: mesh.size.vertex_count, + vertex_stride: mesh.vertex_stride, + indices: index_buffer.map(|index_buffer| { + hal::AccelerationStructureTriangleIndices:: { + format: mesh.size.index_format.unwrap(), + buffer: Some(index_buffer), + offset: mesh.index_buffer_offset.unwrap() as u32, + count: mesh.size.index_count.unwrap(), + } + }), + transform: transform_buffer.map(|transform_buffer| { + hal::AccelerationStructureTriangleTransform { + buffer: transform_buffer, + offset: mesh.transform_buffer_offset.unwrap() as u32, + } + }), + flags: mesh.size.flags, + }; + triangle_entries.push(triangles); + if let Some(blas) = buf.5.as_ref() { + let scratch_buffer_offset = scratch_buffer_blas_size; + scratch_buffer_blas_size += align_to( + blas.size_info.build_scratch_size as u32, + SCRATCH_BUFFER_ALIGNMENT, + ) as u64; + + blas_storage.push(( + blas, + hal::AccelerationStructureEntries::Triangles(triangle_entries), + scratch_buffer_offset, + )); + triangle_entries = Vec::new(); + } + } + + let mut scratch_buffer_tlas_size = 0; + let mut tlas_storage = Vec::<(&Tlas, hal::AccelerationStructureEntries, u64)>::new(); + let mut tlas_buf_storage = Vec::<( + Arc>, + Option>, + TlasBuildEntry, + )>::new(); + + for entry in tlas_iter { + let data = cmd_buf_data + .trackers + .buffers + .set_single( + match buffer_guard.get(entry.instance_buffer_id) { + Ok(buffer) => buffer, + Err(_) => { + return Err(BuildAccelerationStructureError::InvalidBuffer( + entry.instance_buffer_id, + )); + } + }, + BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, + ) + .ok_or(BuildAccelerationStructureError::InvalidBuffer( + entry.instance_buffer_id, + ))?; + tlas_buf_storage.push((data.0, data.1, entry.clone())); + } + + for tlas_buf in &mut tlas_buf_storage { + let entry = &tlas_buf.2; + let instance_buffer = { + let (instance_buffer, instance_pending) = (&mut tlas_buf.0, &mut tlas_buf.1); + let instance_raw = instance_buffer.raw.get(&snatch_guard).ok_or( + BuildAccelerationStructureError::InvalidBuffer(entry.instance_buffer_id), + )?; + if !instance_buffer.usage.contains(BufferUsages::TLAS_INPUT) { + return Err(BuildAccelerationStructureError::MissingTlasInputUsageFlag( + entry.instance_buffer_id, + )); + } + if let Some(barrier) = instance_pending + .take() + .map(|pending| pending.into_hal(instance_buffer, &snatch_guard)) + { + input_barriers.push(barrier); + } + instance_raw + }; + + let tlas = cmd_buf_data + .trackers + .tlas_s + .add_single(&tlas_guard, entry.tlas_id) + .ok_or(BuildAccelerationStructureError::InvalidTlas(entry.tlas_id))?; + + if tlas.raw.is_none() { + return Err(BuildAccelerationStructureError::InvalidTlas(entry.tlas_id)); + } + + cmd_buf_data.tlas_actions.push(TlasAction { + id: entry.tlas_id, + kind: crate::ray_tracing::TlasActionKind::Build { + build_index: build_command_index, + dependencies: Vec::new(), + }, + }); + + let scratch_buffer_offset = scratch_buffer_tlas_size; + scratch_buffer_tlas_size += align_to( + tlas.size_info.build_scratch_size as u32, + SCRATCH_BUFFER_ALIGNMENT, + ) as u64; + + tlas_storage.push(( + tlas, + hal::AccelerationStructureEntries::Instances(hal::AccelerationStructureInstances { + buffer: Some(instance_buffer), + offset: 0, + count: entry.instance_count, + }), + scratch_buffer_offset, + )); + } + + if max(scratch_buffer_blas_size, scratch_buffer_tlas_size) == 0 { + return Ok(()); + } + + let scratch_buffer = unsafe { + device + .raw() + .create_buffer(&hal::BufferDescriptor { + label: Some("(wgpu) scratch buffer"), + size: max(scratch_buffer_blas_size, scratch_buffer_tlas_size), + usage: hal::BufferUses::ACCELERATION_STRUCTURE_SCRATCH, + memory_flags: hal::MemoryFlags::empty(), + }) + .unwrap() + }; + + let scratch_buffer_barrier = hal::BufferBarrier:: { + buffer: &scratch_buffer, + usage: hal::BufferUses::ACCELERATION_STRUCTURE_SCRATCH + ..hal::BufferUses::ACCELERATION_STRUCTURE_SCRATCH, + }; + + let blas_descriptors = + blas_storage + .iter() + .map(|&(blas, ref entries, ref scratch_buffer_offset)| { + if blas.update_mode == wgt::AccelerationStructureUpdateMode::PreferUpdate { + log::info!("only rebuild implemented") + } + hal::BuildAccelerationStructureDescriptor { + entries, + mode: hal::AccelerationStructureBuildMode::Build, + flags: blas.flags, + source_acceleration_structure: None, + destination_acceleration_structure: blas.raw.as_ref().unwrap(), + scratch_buffer: &scratch_buffer, + scratch_buffer_offset: *scratch_buffer_offset, + } + }); + + let tlas_descriptors = + tlas_storage + .iter() + .map(|&(tlas, ref entries, ref scratch_buffer_offset)| { + if tlas.update_mode == wgt::AccelerationStructureUpdateMode::PreferUpdate { + log::info!("only rebuild implemented") + } + hal::BuildAccelerationStructureDescriptor { + entries, + mode: hal::AccelerationStructureBuildMode::Build, + flags: tlas.flags, + source_acceleration_structure: None, + destination_acceleration_structure: tlas.raw.as_ref().unwrap(), + scratch_buffer: &scratch_buffer, + scratch_buffer_offset: *scratch_buffer_offset, + } + }); + + let blas_present = !blas_storage.is_empty(); + let tlas_present = !tlas_storage.is_empty(); + + let cmd_buf_raw = cmd_buf_data.encoder.open()?; + unsafe { + cmd_buf_raw.transition_buffers(input_barriers.into_iter()); + + if blas_present { + cmd_buf_raw.place_acceleration_structure_barrier( + hal::AccelerationStructureBarrier { + usage: hal::AccelerationStructureUses::BUILD_INPUT + ..hal::AccelerationStructureUses::BUILD_OUTPUT, + }, + ); + + cmd_buf_raw + .build_acceleration_structures(blas_storage.len() as u32, blas_descriptors); + } + + if blas_present && tlas_present { + cmd_buf_raw.transition_buffers(iter::once(scratch_buffer_barrier)); + } + + let mut source_usage = hal::AccelerationStructureUses::empty(); + let mut destination_usage = hal::AccelerationStructureUses::empty(); + if blas_present { + source_usage |= hal::AccelerationStructureUses::BUILD_OUTPUT; + destination_usage |= hal::AccelerationStructureUses::BUILD_INPUT + } + if tlas_present { + source_usage |= hal::AccelerationStructureUses::SHADER_INPUT; + destination_usage |= hal::AccelerationStructureUses::BUILD_OUTPUT; + } + + cmd_buf_raw.place_acceleration_structure_barrier(hal::AccelerationStructureBarrier { + usage: source_usage..destination_usage, + }); + + if tlas_present { + cmd_buf_raw + .build_acceleration_structures(tlas_storage.len() as u32, tlas_descriptors); + + cmd_buf_raw.place_acceleration_structure_barrier( + hal::AccelerationStructureBarrier { + usage: hal::AccelerationStructureUses::BUILD_OUTPUT + ..hal::AccelerationStructureUses::SHADER_INPUT, + }, + ); + } + } + let scratch_mapping = unsafe { + device + .raw() + .map_buffer( + &scratch_buffer, + 0..max(scratch_buffer_blas_size, scratch_buffer_tlas_size), + ) + .map_err(crate::device::DeviceError::from)? + }; + device + .pending_writes + .lock() + .as_mut() + .unwrap() + .temp_resources + .push(TempResource::StagingBuffer(Arc::new(StagingBuffer { + raw: Mutex::new(Some(scratch_buffer)), + device: device.clone(), + size: max(scratch_buffer_blas_size, scratch_buffer_tlas_size), + info: ResourceInfo::new("Raytracing scratch buffer"), + is_coherent: scratch_mapping.is_coherent, + }))); + + Ok(()) + } + + pub fn command_encoder_build_acceleration_structures<'a, A: HalApi>( + &self, + command_encoder_id: CommandEncoderId, + blas_iter: impl Iterator>, + tlas_iter: impl Iterator>, + ) -> Result<(), BuildAccelerationStructureError> { + profiling::scope!("CommandEncoder::build_acceleration_structures"); + + let hub = A::hub(self); + + let cmd_buf = CommandBuffer::get_encoder(hub, command_encoder_id)?; + let buffer_guard = hub.buffers.read(); + let blas_guard = hub.blas_s.read(); + let tlas_guard = hub.tlas_s.read(); + + let device = &cmd_buf.device; + + let build_command_index = NonZeroU64::new( + device + .last_acceleration_structure_build_command_index + .fetch_add(1, std::sync::atomic::Ordering::Relaxed) + + 1, + ) + .unwrap(); + + #[cfg(feature = "trace")] + let trace_blas: Vec = blas_iter + .map(|x| { + let geometries = match x.geometries { + BlasGeometries::TriangleGeometries(triangle_geometries) => { + crate::ray_tracing::TraceBlasGeometries::TriangleGeometries( + triangle_geometries + .map(|tg| crate::ray_tracing::TraceBlasTriangleGeometry { + size: tg.size.clone(), + vertex_buffer: tg.vertex_buffer, + index_buffer: tg.index_buffer, + transform_buffer: tg.transform_buffer, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + transform_buffer_offset: tg.transform_buffer_offset, + }) + .collect(), + ) + } + }; + crate::ray_tracing::TraceBlasBuildEntry { + blas_id: x.blas_id, + geometries, + } + }) + .collect(); + + #[cfg(feature = "trace")] + let trace_tlas: Vec = tlas_iter + .map(|x: TlasPackage| { + let instances = x + .instances + .map(|instance| { + instance.map(|instance| crate::ray_tracing::TraceTlasInstance { + blas_id: instance.blas_id, + transform: *instance.transform, + custom_index: instance.custom_index, + mask: instance.mask, + }) + }) + .collect(); + crate::ray_tracing::TraceTlasPackage { + tlas_id: x.tlas_id, + instances, + lowest_unmodified: x.lowest_unmodified, + } + }) + .collect(); + + #[cfg(feature = "trace")] + if let Some(ref mut list) = cmd_buf.data.lock().as_mut().unwrap().commands { + list.push(crate::device::trace::Command::BuildAccelerationStructures { + blas: trace_blas.clone(), + tlas: trace_tlas.clone(), + }); + } + + #[cfg(feature = "trace")] + let blas_iter = trace_blas.iter().map(|x| { + let geometries = match &x.geometries { + crate::ray_tracing::TraceBlasGeometries::TriangleGeometries( + triangle_geometries, + ) => { + let iter = triangle_geometries.iter().map(|tg| BlasTriangleGeometry { + size: &tg.size, + vertex_buffer: tg.vertex_buffer, + index_buffer: tg.index_buffer, + transform_buffer: tg.transform_buffer, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + transform_buffer_offset: tg.transform_buffer_offset, + }); + BlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + BlasBuildEntry { + blas_id: x.blas_id, + geometries, + } + }); + + #[cfg(feature = "trace")] + let tlas_iter = trace_tlas.iter().map(|x| { + let instances = x.instances.iter().map(|instance| { + instance + .as_ref() + .map(|instance| crate::ray_tracing::TlasInstance { + blas_id: instance.blas_id, + transform: &instance.transform, + custom_index: instance.custom_index, + mask: instance.mask, + }) + }); + TlasPackage { + tlas_id: x.tlas_id, + instances: Box::new(instances), + lowest_unmodified: x.lowest_unmodified, + } + }); + + let mut input_barriers = Vec::>::new(); + let mut buf_storage = Vec::<( + Arc>, + Option>, + Option<(Arc>, Option>)>, + Option<(Arc>, Option>)>, + BlasTriangleGeometry, + Option>>, + )>::new(); + + let mut scratch_buffer_blas_size = 0; + let mut blas_storage = Vec::<(&Blas, hal::AccelerationStructureEntries, u64)>::new(); + let mut cmd_buf_data = cmd_buf.data.lock(); + let cmd_buf_data = cmd_buf_data.as_mut().unwrap(); + for entry in blas_iter { + let blas = cmd_buf_data + .trackers + .blas_s + .add_single(&blas_guard, entry.blas_id) + .ok_or(BuildAccelerationStructureError::InvalidBlas(entry.blas_id))?; + + if blas.raw.is_none() { + return Err(BuildAccelerationStructureError::InvalidBlas(entry.blas_id)); + } + + cmd_buf_data.blas_actions.push(BlasAction { + id: entry.blas_id, + kind: crate::ray_tracing::BlasActionKind::Build(build_command_index), + }); + + match entry.geometries { + BlasGeometries::TriangleGeometries(triangle_geometries) => { + for (i, mesh) in triangle_geometries.enumerate() { + let size_desc = match &blas.sizes { + wgt::BlasGeometrySizeDescriptors::Triangles { desc } => desc, + }; + if i >= size_desc.len() { + return Err( + BuildAccelerationStructureError::IncompatibleBlasBuildSizes( + entry.blas_id, + ), + ); + } + let size_desc = &size_desc[i]; + + if size_desc.flags != mesh.size.flags + || size_desc.vertex_count < mesh.size.vertex_count + || size_desc.vertex_format != mesh.size.vertex_format + || size_desc.index_count.is_none() != mesh.size.index_count.is_none() + || (size_desc.index_count.is_none() + || size_desc.index_count.unwrap() < mesh.size.index_count.unwrap()) + || size_desc.index_format.is_none() != mesh.size.index_format.is_none() + || (size_desc.index_format.is_none() + || size_desc.index_format.unwrap() + != mesh.size.index_format.unwrap()) + { + return Err( + BuildAccelerationStructureError::IncompatibleBlasBuildSizes( + entry.blas_id, + ), + ); + } + + if size_desc.index_count.is_some() && mesh.index_buffer.is_none() { + return Err(BuildAccelerationStructureError::MissingIndexBuffer( + entry.blas_id, + )); + } + let (vertex_buffer, vertex_pending) = cmd_buf_data + .trackers + .buffers + .set_single( + match buffer_guard.get(mesh.vertex_buffer) { + Ok(buffer) => buffer, + Err(_) => { + return Err(BuildAccelerationStructureError::InvalidBuffer( + mesh.vertex_buffer, + )) + } + }, + BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, + ) + .ok_or(BuildAccelerationStructureError::InvalidBuffer( + mesh.vertex_buffer, + ))?; + let index_data = if let Some(index_id) = mesh.index_buffer { + if mesh.index_buffer_offset.is_none() + || mesh.size.index_count.is_none() + || mesh.size.index_count.is_none() + { + return Err( + BuildAccelerationStructureError::MissingAssociatedData( + index_id, + ), + ); + } + let data = cmd_buf_data + .trackers + .buffers + .set_single( + match buffer_guard.get(index_id) { + Ok(buffer) => buffer, + Err(_) => { + return Err( + BuildAccelerationStructureError::InvalidBuffer( + index_id, + ), + ) + } + }, + hal::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, + ) + .ok_or(BuildAccelerationStructureError::InvalidBuffer(index_id))?; + Some(data) + } else { + None + }; + let transform_data = if let Some(transform_id) = mesh.transform_buffer { + if mesh.transform_buffer_offset.is_none() { + return Err( + BuildAccelerationStructureError::MissingAssociatedData( + transform_id, + ), + ); + } + let data = cmd_buf_data + .trackers + .buffers + .set_single( + match buffer_guard.get(transform_id) { + Ok(buffer) => buffer, + Err(_) => { + return Err( + BuildAccelerationStructureError::InvalidBuffer( + transform_id, + ), + ) + } + }, + BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, + ) + .ok_or(BuildAccelerationStructureError::InvalidBuffer( + transform_id, + ))?; + Some(data) + } else { + None + }; + + buf_storage.push(( + vertex_buffer, + vertex_pending, + index_data, + transform_data, + mesh, + None, + )) + } + + if let Some(last) = buf_storage.last_mut() { + last.5 = Some(blas.clone()); + } + } + } + } + + let mut triangle_entries = Vec::>::new(); + let snatch_guard = device.snatchable_lock.read(); + for buf in &mut buf_storage { + let mesh = &buf.4; + let vertex_buffer = { + let vertex_buffer = buf.0.as_ref(); + let vertex_raw = vertex_buffer.raw.get(&snatch_guard).ok_or( + BuildAccelerationStructureError::InvalidBuffer(mesh.vertex_buffer), + )?; + if !vertex_buffer.usage.contains(BufferUsages::BLAS_INPUT) { + return Err(BuildAccelerationStructureError::MissingBlasInputUsageFlag( + mesh.vertex_buffer, + )); + } + if let Some(barrier) = buf + .1 + .take() + .map(|pending| pending.into_hal(vertex_buffer, &snatch_guard)) + { + input_barriers.push(barrier); + } + if vertex_buffer.size + < (mesh.size.vertex_count + mesh.first_vertex) as u64 * mesh.vertex_stride + { + return Err(BuildAccelerationStructureError::InsufficientBufferSize( + mesh.vertex_buffer, + vertex_buffer.size, + (mesh.size.vertex_count + mesh.first_vertex) as u64 * mesh.vertex_stride, + )); + } + let vertex_buffer_offset = mesh.first_vertex as u64 * mesh.vertex_stride; + cmd_buf_data.buffer_memory_init_actions.extend( + vertex_buffer.initialization_status.read().create_action( + buffer_guard.get(mesh.vertex_buffer).unwrap(), + vertex_buffer_offset + ..(vertex_buffer_offset + + mesh.size.vertex_count as u64 * mesh.vertex_stride), + MemoryInitKind::NeedsInitializedMemory, + ), + ); + vertex_raw + }; + let index_buffer = if let Some((ref mut index_buffer, ref mut index_pending)) = buf.2 { + let index_id = mesh.index_buffer.as_ref().unwrap(); + let index_raw = index_buffer + .raw + .get(&snatch_guard) + .ok_or(BuildAccelerationStructureError::InvalidBuffer(*index_id))?; + if !index_buffer.usage.contains(BufferUsages::BLAS_INPUT) { + return Err(BuildAccelerationStructureError::MissingBlasInputUsageFlag( + *index_id, + )); + } + if let Some(barrier) = index_pending + .take() + .map(|pending| pending.into_hal(index_buffer, &snatch_guard)) + { + input_barriers.push(barrier); + } + let index_stride = match mesh.size.index_format.unwrap() { + wgt::IndexFormat::Uint16 => 2, + wgt::IndexFormat::Uint32 => 4, + }; + if mesh.index_buffer_offset.unwrap() % index_stride != 0 { + return Err(BuildAccelerationStructureError::UnalignedIndexBufferOffset( + *index_id, + )); + } + let index_buffer_size = mesh.size.index_count.unwrap() as u64 * index_stride; + + if mesh.size.index_count.unwrap() % 3 != 0 { + return Err(BuildAccelerationStructureError::InvalidIndexCount( + *index_id, + mesh.size.index_count.unwrap(), + )); + } + if index_buffer.size + < mesh.size.index_count.unwrap() as u64 * index_stride + + mesh.index_buffer_offset.unwrap() + { + return Err(BuildAccelerationStructureError::InsufficientBufferSize( + *index_id, + index_buffer.size, + mesh.size.index_count.unwrap() as u64 * index_stride + + mesh.index_buffer_offset.unwrap(), + )); + } + + cmd_buf_data.buffer_memory_init_actions.extend( + index_buffer.initialization_status.read().create_action( + match buffer_guard.get(*index_id) { + Ok(buffer) => buffer, + Err(_) => { + return Err(BuildAccelerationStructureError::InvalidBuffer( + *index_id, + )) + } + }, + mesh.index_buffer_offset.unwrap() + ..(mesh.index_buffer_offset.unwrap() + index_buffer_size), + MemoryInitKind::NeedsInitializedMemory, + ), + ); + Some(index_raw) + } else { + None + }; + let transform_buffer = + if let Some((ref mut transform_buffer, ref mut transform_pending)) = buf.3 { + let transform_id = mesh.transform_buffer.as_ref().unwrap(); + if mesh.transform_buffer_offset.is_none() { + return Err(BuildAccelerationStructureError::MissingAssociatedData( + *transform_id, + )); + } + let transform_raw = transform_buffer.raw.get(&snatch_guard).ok_or( + BuildAccelerationStructureError::InvalidBuffer(*transform_id), + )?; + if !transform_buffer.usage.contains(BufferUsages::BLAS_INPUT) { + return Err(BuildAccelerationStructureError::MissingBlasInputUsageFlag( + *transform_id, + )); + } + if let Some(barrier) = transform_pending + .take() + .map(|pending| pending.into_hal(transform_buffer, &snatch_guard)) + { + input_barriers.push(barrier); + } + if mesh.transform_buffer_offset.unwrap() % wgt::TRANSFORM_BUFFER_ALIGNMENT != 0 + { + return Err( + BuildAccelerationStructureError::UnalignedTransformBufferOffset( + *transform_id, + ), + ); + } + if transform_buffer.size < 48 + mesh.transform_buffer_offset.unwrap() { + return Err(BuildAccelerationStructureError::InsufficientBufferSize( + *transform_id, + transform_buffer.size, + 48 + mesh.transform_buffer_offset.unwrap(), + )); + } + cmd_buf_data.buffer_memory_init_actions.extend( + transform_buffer.initialization_status.read().create_action( + buffer_guard.get(*transform_id).unwrap(), + mesh.transform_buffer_offset.unwrap() + ..(mesh.index_buffer_offset.unwrap() + 48), + MemoryInitKind::NeedsInitializedMemory, + ), + ); + Some(transform_raw) + } else { + None + }; + + let triangles = hal::AccelerationStructureTriangles { + vertex_buffer: Some(vertex_buffer), + vertex_format: mesh.size.vertex_format, + first_vertex: mesh.first_vertex, + vertex_count: mesh.size.vertex_count, + vertex_stride: mesh.vertex_stride, + indices: index_buffer.map(|index_buffer| { + hal::AccelerationStructureTriangleIndices:: { + format: mesh.size.index_format.unwrap(), + buffer: Some(index_buffer), + offset: mesh.index_buffer_offset.unwrap() as u32, + count: mesh.size.index_count.unwrap(), + } + }), + transform: transform_buffer.map(|transform_buffer| { + hal::AccelerationStructureTriangleTransform { + buffer: transform_buffer, + offset: mesh.transform_buffer_offset.unwrap() as u32, + } + }), + flags: mesh.size.flags, + }; + triangle_entries.push(triangles); + if let Some(blas) = buf.5.as_ref() { + let scratch_buffer_offset = scratch_buffer_blas_size; + scratch_buffer_blas_size += align_to( + blas.size_info.build_scratch_size as u32, + SCRATCH_BUFFER_ALIGNMENT, + ) as u64; + + blas_storage.push(( + blas, + hal::AccelerationStructureEntries::Triangles(triangle_entries), + scratch_buffer_offset, + )); + triangle_entries = Vec::new(); + } + } + + let mut tlas_lock_store = Vec::<( + RwLockReadGuard>, + Option, + Arc>, + )>::new(); + + for package in tlas_iter { + let tlas = cmd_buf_data + .trackers + .tlas_s + .add_single(&tlas_guard, package.tlas_id) + .ok_or(BuildAccelerationStructureError::InvalidTlas( + package.tlas_id, + ))?; + tlas_lock_store.push((tlas.instance_buffer.read(), Some(package), tlas.clone())) + } + + let mut scratch_buffer_tlas_size = 0; + let mut tlas_storage = Vec::<( + &Tlas, + hal::AccelerationStructureEntries, + u64, + Range, + )>::new(); + let mut instance_buffer_staging_source = Vec::::new(); + + for entry in &mut tlas_lock_store { + let package = entry.1.take().unwrap(); + let tlas = &entry.2; + if tlas.raw.is_none() { + return Err(BuildAccelerationStructureError::InvalidTlas( + package.tlas_id, + )); + } + + let scratch_buffer_offset = scratch_buffer_tlas_size; + scratch_buffer_tlas_size += align_to( + tlas.size_info.build_scratch_size as u32, + SCRATCH_BUFFER_ALIGNMENT, + ) as u64; + + let first_byte_index = instance_buffer_staging_source.len(); + + let mut dependencies = Vec::new(); + + let mut instance_count = 0; + for instance in package.instances.flatten() { + if instance.custom_index >= (1u32 << 24u32) { + return Err(BuildAccelerationStructureError::TlasInvalidCustomIndex( + package.tlas_id, + )); + } + let blas = cmd_buf_data + .trackers + .blas_s + .add_single(&blas_guard, instance.blas_id) + .ok_or(BuildAccelerationStructureError::InvalidBlasForInstance( + instance.blas_id, + ))?; + + instance_buffer_staging_source + .extend(tlas_instance_into_bytes::(&instance, blas.handle)); + + instance_count += 1; + + dependencies.push(instance.blas_id); + + cmd_buf_data.blas_actions.push(BlasAction { + id: instance.blas_id, + kind: crate::ray_tracing::BlasActionKind::Use, + }); + } + + cmd_buf_data.tlas_actions.push(TlasAction { + id: package.tlas_id, + kind: crate::ray_tracing::TlasActionKind::Build { + build_index: build_command_index, + dependencies, + }, + }); + + if instance_count > tlas.max_instance_count { + return Err(BuildAccelerationStructureError::TlasInstanceCountExceeded( + package.tlas_id, + instance_count, + tlas.max_instance_count, + )); + } + + tlas_storage.push(( + tlas, + hal::AccelerationStructureEntries::Instances(hal::AccelerationStructureInstances { + buffer: Some(entry.0.as_ref().unwrap()), + offset: 0, + count: instance_count, + }), + scratch_buffer_offset, + first_byte_index..instance_buffer_staging_source.len(), + )); + } + + if max(scratch_buffer_blas_size, scratch_buffer_tlas_size) == 0 { + return Ok(()); + } + + let scratch_buffer = unsafe { + device + .raw() + .create_buffer(&hal::BufferDescriptor { + label: Some("(wgpu) scratch buffer"), + size: max(scratch_buffer_blas_size, scratch_buffer_tlas_size), + usage: hal::BufferUses::ACCELERATION_STRUCTURE_SCRATCH | BufferUses::MAP_WRITE, + memory_flags: hal::MemoryFlags::empty(), + }) + .map_err(crate::device::DeviceError::from)? + }; + let staging_buffer = if !instance_buffer_staging_source.is_empty() { + unsafe { + let staging_buffer = device + .raw() + .create_buffer(&hal::BufferDescriptor { + label: Some("(wgpu) instance staging buffer"), + size: instance_buffer_staging_source.len() as u64, + usage: hal::BufferUses::MAP_WRITE | hal::BufferUses::COPY_SRC, + memory_flags: hal::MemoryFlags::empty(), + }) + .map_err(crate::device::DeviceError::from)?; + let mapping = device + .raw() + .map_buffer( + &staging_buffer, + 0..instance_buffer_staging_source.len() as u64, + ) + .map_err(crate::device::DeviceError::from)?; + ptr::copy_nonoverlapping( + instance_buffer_staging_source.as_ptr(), + mapping.ptr.as_ptr(), + instance_buffer_staging_source.len(), + ); + device + .raw() + .unmap_buffer(&staging_buffer) + .map_err(crate::device::DeviceError::from)?; + assert!(mapping.is_coherent); + let buf = StagingBuffer { + raw: Mutex::new(Some(staging_buffer)), + device: device.clone(), + size: instance_buffer_staging_source.len() as u64, + info: ResourceInfo::new("Raytracing staging buffer"), + is_coherent: mapping.is_coherent, + }; + let staging_fid = hub.staging_buffers.request(); + let stage_buf = staging_fid.init(buf); + Some(stage_buf) + } + } else { + None + }; + + let blas_descriptors = + blas_storage + .iter() + .map(|&(blas, ref entries, ref scratch_buffer_offset)| { + if blas.update_mode == wgt::AccelerationStructureUpdateMode::PreferUpdate { + log::info!("only rebuild implemented") + } + hal::BuildAccelerationStructureDescriptor { + entries, + mode: hal::AccelerationStructureBuildMode::Build, + flags: blas.flags, + source_acceleration_structure: None, + destination_acceleration_structure: blas.raw.as_ref().unwrap(), + scratch_buffer: &scratch_buffer, + scratch_buffer_offset: *scratch_buffer_offset, + } + }); + + let tlas_descriptors = tlas_storage.iter().map( + |&(tlas, ref entries, ref scratch_buffer_offset, ref _range)| { + if tlas.update_mode == wgt::AccelerationStructureUpdateMode::PreferUpdate { + log::info!("only rebuild implemented") + } + hal::BuildAccelerationStructureDescriptor { + entries, + mode: hal::AccelerationStructureBuildMode::Build, + flags: tlas.flags, + source_acceleration_structure: None, + destination_acceleration_structure: tlas.raw.as_ref().unwrap(), + scratch_buffer: &scratch_buffer, + scratch_buffer_offset: *scratch_buffer_offset, + } + }, + ); + + let scratch_buffer_barrier = hal::BufferBarrier:: { + buffer: &scratch_buffer, + usage: BufferUses::ACCELERATION_STRUCTURE_SCRATCH + ..BufferUses::ACCELERATION_STRUCTURE_SCRATCH, + }; + + let mut lock_vec = Vec::::Buffer>>>>::new(); + + for tlas in &tlas_storage { + let size = (tlas.3.end - tlas.3.start) as u64; + lock_vec.push(if size == 0 { + None + } else { + Some(tlas.0.instance_buffer.read()) + }) + } + + let instance_buffer_barriers = lock_vec.iter().filter_map(|lock| { + lock.as_ref().map(|lock| hal::BufferBarrier:: { + buffer: lock.as_ref().unwrap(), + usage: BufferUses::COPY_DST..BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT, + }) + }); + + let blas_present = !blas_storage.is_empty(); + let tlas_present = !tlas_storage.is_empty(); + + let cmd_buf_raw = cmd_buf_data.encoder.open()?; + + unsafe { + cmd_buf_raw.transition_buffers(input_barriers.into_iter()); + } + + if blas_present { + unsafe { + cmd_buf_raw.place_acceleration_structure_barrier( + hal::AccelerationStructureBarrier { + usage: hal::AccelerationStructureUses::BUILD_INPUT + ..hal::AccelerationStructureUses::BUILD_OUTPUT, + }, + ); + + cmd_buf_raw + .build_acceleration_structures(blas_storage.len() as u32, blas_descriptors); + } + } + + if blas_present && tlas_present { + unsafe { + cmd_buf_raw.transition_buffers(iter::once(scratch_buffer_barrier)); + } + } + + let mut source_usage = hal::AccelerationStructureUses::empty(); + let mut destination_usage = hal::AccelerationStructureUses::empty(); + if blas_present { + source_usage |= hal::AccelerationStructureUses::BUILD_OUTPUT; + destination_usage |= hal::AccelerationStructureUses::BUILD_INPUT + } + if tlas_present { + source_usage |= hal::AccelerationStructureUses::SHADER_INPUT; + destination_usage |= hal::AccelerationStructureUses::BUILD_OUTPUT; + } + unsafe { + cmd_buf_raw.place_acceleration_structure_barrier(hal::AccelerationStructureBarrier { + usage: source_usage..destination_usage, + }); + } + + if tlas_present { + unsafe { + if let Some(ref staging_buffer) = staging_buffer { + cmd_buf_raw.transition_buffers(iter::once(hal::BufferBarrier:: { + buffer: staging_buffer.raw.lock().as_ref().unwrap(), + usage: hal::BufferUses::MAP_WRITE..hal::BufferUses::COPY_SRC, + })); + } + } + + for &(tlas, ref _entries, ref _scratch_buffer_offset, ref range) in &tlas_storage { + let size = (range.end - range.start) as u64; + if size == 0 { + continue; + } + unsafe { + let temp = hal::BufferCopy { + src_offset: range.start as u64, + dst_offset: 0, + size: NonZeroU64::new(size).unwrap(), + }; + cmd_buf_raw.copy_buffer_to_buffer( + staging_buffer + .as_ref() + .unwrap() + .raw + .lock() + .as_ref() + .unwrap(), + tlas.instance_buffer.read().as_ref().unwrap(), + iter::once(temp), + ); + } + } + + unsafe { + cmd_buf_raw.transition_buffers(instance_buffer_barriers); + + cmd_buf_raw + .build_acceleration_structures(tlas_storage.len() as u32, tlas_descriptors); + + cmd_buf_raw.place_acceleration_structure_barrier( + hal::AccelerationStructureBarrier { + usage: hal::AccelerationStructureUses::BUILD_OUTPUT + ..hal::AccelerationStructureUses::SHADER_INPUT, + }, + ); + } + + if let Some(staging_buffer) = staging_buffer { + device + .pending_writes + .lock() + .as_mut() + .unwrap() + .temp_resources + .push(TempResource::StagingBuffer(staging_buffer)); + } + } + let scratch_mapping = unsafe { + device + .raw() + .map_buffer( + &scratch_buffer, + 0..max(scratch_buffer_blas_size, scratch_buffer_tlas_size), + ) + .map_err(crate::device::DeviceError::from)? + }; + + let buf = StagingBuffer { + raw: Mutex::new(Some(scratch_buffer)), + device: device.clone(), + size: max(scratch_buffer_blas_size, scratch_buffer_tlas_size), + info: ResourceInfo::new("Ratracing scratch buffer"), + is_coherent: scratch_mapping.is_coherent, + }; + let staging_fid = hub.staging_buffers.request(); + let stage_buf = staging_fid.init(buf); + + device + .pending_writes + .lock() + .as_mut() + .unwrap() + .temp_resources + .push(TempResource::StagingBuffer(stage_buf)); + + Ok(()) + } +} + +impl BakedCommands { + // makes sure a blas is build before it is used + pub(crate) fn validate_blas_actions( + &mut self, + blas_guard: &mut Storage>, + ) -> Result<(), ValidateBlasActionsError> { + profiling::scope!("CommandEncoder::[submission]::validate_blas_actions"); + let mut built = FastHashSet::default(); + for action in self.blas_actions.drain(..) { + match action.kind { + crate::ray_tracing::BlasActionKind::Build(id) => { + built.insert(action.id); + let blas = blas_guard + .get(action.id) + .map_err(|_| ValidateBlasActionsError::InvalidBlas(action.id))?; + *blas.built_index.write() = Some(id); + } + crate::ray_tracing::BlasActionKind::Use => { + if !built.contains(&action.id) { + let blas = blas_guard + .get(action.id) + .map_err(|_| ValidateBlasActionsError::InvalidBlas(action.id))?; + if (*blas.built_index.read()).is_none() { + return Err(ValidateBlasActionsError::UsedUnbuilt(action.id)); + } + } + } + } + } + Ok(()) + } + + // makes sure a tlas is build before it is used + pub(crate) fn validate_tlas_actions( + &mut self, + blas_guard: &Storage>, + tlas_guard: &mut Storage>, + ) -> Result<(), ValidateTlasActionsError> { + profiling::scope!("CommandEncoder::[submission]::validate_tlas_actions"); + for action in self.tlas_actions.drain(..) { + match action.kind { + crate::ray_tracing::TlasActionKind::Build { + build_index, + dependencies, + } => { + let tlas = tlas_guard + .get(action.id) + .map_err(|_| ValidateTlasActionsError::InvalidTlas(action.id))?; + + *tlas.built_index.write() = Some(build_index); + *tlas.dependencies.write() = dependencies; + } + crate::ray_tracing::TlasActionKind::Use => { + let tlas = tlas_guard + .get(action.id) + .map_err(|_| ValidateTlasActionsError::InvalidTlas(action.id))?; + + let tlas_build_index = tlas.built_index.read(); + let dependencies = tlas.dependencies.read(); + + if (*tlas_build_index).is_none() { + return Err(ValidateTlasActionsError::UsedUnbuilt(action.id)); + } + for dependency in dependencies.deref() { + let blas = blas_guard.get(*dependency).map_err(|_| { + ValidateTlasActionsError::InvalidBlas(*dependency, action.id) + })?; + let blas_build_index = *blas.built_index.read(); + if blas_build_index.is_none() { + return Err(ValidateTlasActionsError::UsedUnbuilt(action.id)); + } + if blas_build_index.unwrap() > tlas_build_index.unwrap() { + return Err(ValidateTlasActionsError::BlasNewerThenTlas( + *dependency, + action.id, + )); + } + } + } + } + } + Ok(()) + } +} diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 4a9d392a90..a977d9fd34 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -1390,6 +1390,7 @@ impl Global { let buffer_guard = hub.buffers.read(); let texture_guard = hub.textures.read(); let view_guard = hub.texture_views.read(); + let tlas_guard = hub.tlas_s.read(); log::trace!( "Encoding render pass begin in command buffer {:?}", @@ -1425,6 +1426,8 @@ impl Global { Some(&*render_pipeline_guard), Some(&*bundle_guard), Some(&*query_set_guard), + None, + Some(&*tlas_guard), ); let raw = &mut encoder.raw; @@ -1507,6 +1510,21 @@ impl Global { .extend(texture_memory_actions.register_init_action(action)); } + let mapped_used_resources = bind_group + .used + .acceleration_structures + .used_resources() + .map(|blas| { + tracker.tlas_s.add_single(&tlas_guard, blas.as_info().id()); + + crate::ray_tracing::TlasAction { + id: blas.as_info().id(), + kind: crate::ray_tracing::TlasActionKind::Use, + } + }); + + cmd_buf_data.tlas_actions.extend(mapped_used_resources); + let pipeline_layout = state.binder.pipeline_layout.clone(); let entries = state diff --git a/wgpu-core/src/conv.rs b/wgpu-core/src/conv.rs index 0b67ad3cbe..44ed750a43 100644 --- a/wgpu-core/src/conv.rs +++ b/wgpu-core/src/conv.rs @@ -101,6 +101,14 @@ pub fn map_buffer_usage(usage: wgt::BufferUsages) -> hal::BufferUses { hal::BufferUses::QUERY_RESOLVE, usage.contains(wgt::BufferUsages::QUERY_RESOLVE), ); + u.set( + hal::BufferUses::BOTTOM_LEVEL_ACCELERATION_STRUCTURE_INPUT, + usage.contains(wgt::BufferUsages::BLAS_INPUT), + ); + u.set( + hal::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT, + usage.contains(wgt::BufferUsages::TLAS_INPUT), + ); u } diff --git a/wgpu-core/src/device/life.rs b/wgpu-core/src/device/life.rs index 86c5d027c7..f873640afd 100644 --- a/wgpu-core/src/device/life.rs +++ b/wgpu-core/src/device/life.rs @@ -21,6 +21,8 @@ use crate::{ }; use smallvec::SmallVec; +use crate::id::{BlasId, TlasId}; +use crate::resource::{Blas, Tlas}; use parking_lot::Mutex; use std::sync::Arc; use thiserror::Error; @@ -40,6 +42,8 @@ pub(crate) struct ResourceMaps { pub pipeline_layouts: FastHashMap>>, pub render_bundles: FastHashMap>>, pub query_sets: FastHashMap>>, + pub blas_s: FastHashMap>>, + pub tlas_s: FastHashMap>>, pub destroyed_buffers: FastHashMap>>, pub destroyed_textures: FastHashMap>>, } @@ -59,6 +63,8 @@ impl ResourceMaps { pipeline_layouts: FastHashMap::default(), render_bundles: FastHashMap::default(), query_sets: FastHashMap::default(), + blas_s: FastHashMap::default(), + tlas_s: FastHashMap::default(), destroyed_buffers: FastHashMap::default(), destroyed_textures: FastHashMap::default(), } @@ -79,6 +85,8 @@ impl ResourceMaps { render_bundles, query_sets, destroyed_buffers, + tlas_s, + blas_s, destroyed_textures, } = self; buffers.clear(); @@ -93,6 +101,8 @@ impl ResourceMaps { pipeline_layouts.clear(); render_bundles.clear(); query_sets.clear(); + blas_s.clear(); + tlas_s.clear(); destroyed_buffers.clear(); destroyed_textures.clear(); } @@ -111,6 +121,8 @@ impl ResourceMaps { pipeline_layouts, render_bundles, query_sets, + tlas_s, + blas_s, destroyed_buffers, destroyed_textures, } = self; @@ -126,6 +138,8 @@ impl ResourceMaps { pipeline_layouts.extend(other.pipeline_layouts.drain()); render_bundles.extend(other.render_bundles.drain()); query_sets.extend(other.query_sets.drain()); + tlas_s.extend(other.tlas_s.drain()); + blas_s.extend(other.blas_s.drain()); destroyed_buffers.extend(other.destroyed_buffers.drain()); destroyed_textures.extend(other.destroyed_textures.drain()); } @@ -296,6 +310,12 @@ impl LifetimeTracker { .destroyed_textures .insert(destroyed.id, destroyed); } + TempResource::Tlas(raw) => { + last_resources.tlas_s.insert(raw.as_info().id(), raw); + } + TempResource::Blas(raw) => { + last_resources.blas_s.insert(raw.as_info().id(), raw); + } } } @@ -400,6 +420,12 @@ impl LifetimeTracker { TempResource::DestroyedTexture(destroyed) => { resources.destroyed_textures.insert(destroyed.id, destroyed); } + TempResource::Tlas(raw) => { + resources.tlas_s.insert(raw.as_info().id(), raw); + } + TempResource::Blas(raw) => { + resources.blas_s.insert(raw.as_info().id(), raw); + } } } } @@ -513,6 +539,9 @@ impl LifetimeTracker { .samplers .insert(v.as_info().id(), v); } + for v in bind_group.used.acceleration_structures.drain_resources() { + self.suspected_resources.tlas_s.insert(v.as_info().id(), v); + } self.suspected_resources .bind_group_layouts @@ -657,6 +686,30 @@ impl LifetimeTracker { self } + fn triage_suspected_blas(&mut self, trackers: &Mutex>) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = &mut self.suspected_resources.blas_s; + let _ = Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut trackers.blas_s, + |maps| &mut maps.blas_s, + ); + self + } + + fn triage_suspected_tlas(&mut self, trackers: &Mutex>) -> &mut Self { + let mut trackers = trackers.lock(); + let resource_map = &mut self.suspected_resources.tlas_s; + let _ = Self::triage_resources( + resource_map, + self.active.as_mut_slice(), + &mut trackers.tlas_s, + |maps| &mut maps.tlas_s, + ); + self + } + fn triage_suspected_query_sets(&mut self, trackers: &Mutex>) -> &mut Self { let mut trackers = trackers.lock(); let resource_map = &mut self.suspected_resources.query_sets; @@ -725,6 +778,8 @@ impl LifetimeTracker { self.triage_suspected_texture_views(trackers); self.triage_suspected_textures(trackers); self.triage_suspected_buffers(trackers); + self.triage_suspected_tlas(trackers); + self.triage_suspected_blas(trackers); self.triage_suspected_destroyed_buffers(); self.triage_suspected_destroyed_textures(); } diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 7ecda830a3..c9bd0ab63f 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -22,6 +22,7 @@ pub(crate) mod bgl; pub mod global; mod life; pub mod queue; +pub mod ray_tracing; pub mod resource; #[cfg(any(feature = "trace", feature = "replay"))] pub mod trace; diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index a280abd9b3..2481300259 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -25,6 +25,7 @@ use hal::{CommandEncoder as _, Device as _, Queue as _}; use parking_lot::Mutex; use smallvec::SmallVec; +use crate::resource::{Blas, Tlas}; use std::{ iter, mem, ptr, sync::{atomic::Ordering, Arc}, @@ -150,6 +151,8 @@ pub enum TempResource { DestroyedBuffer(Arc>), DestroyedTexture(Arc>), Texture(Arc>), + Tlas(Arc>), + Blas(Arc>), } /// A queue execution for a particular command encoder. @@ -366,6 +369,10 @@ pub enum QueueSubmitError { SurfaceUnconfigured, #[error("GPU got stuck :(")] StuckGpu, + #[error(transparent)] + ValidateBlasActionsError(#[from] crate::ray_tracing::ValidateBlasActionsError), + #[error(transparent)] + ValidateTlasActionsError(#[from] crate::ray_tracing::ValidateTlasActionsError), } //TODO: move out common parts of write_xxx. @@ -1338,7 +1345,28 @@ impl Global { .insert(bundle.as_info().id(), bundle.clone()); } } + for blas in cmd_buf_trackers.blas_s.used_resources() { + blas.info.use_at(submit_index); + if blas.is_unique() { + temp_suspected + .as_mut() + .unwrap() + .blas_s + .insert(blas.as_info().id(), blas.clone()); + } + } + for tlas in cmd_buf_trackers.tlas_s.used_resources() { + tlas.info.use_at(submit_index); + if tlas.is_unique() { + temp_suspected + .as_mut() + .unwrap() + .tlas_s + .insert(tlas.as_info().id(), tlas.clone()); + } + } } + let mut baked = cmdbuf.from_arc_into_baked(); // execute resource transitions unsafe { @@ -1360,6 +1388,11 @@ impl Global { baked .initialize_texture_memory(&mut *trackers, device) .map_err(|err| QueueSubmitError::DestroyedTexture(err.0))?; + let mut blas_guard = hub.blas_s.write(); + baked.validate_blas_actions(&mut blas_guard)?; + let mut tlas_guard = hub.tlas_s.write(); + baked.validate_tlas_actions(&blas_guard, &mut tlas_guard)?; + //Note: stateless trackers are not merged: // device already knows these resources exist. CommandBuffer::insert_barriers_from_tracker( diff --git a/wgpu-core/src/device/ray_tracing.rs b/wgpu-core/src/device/ray_tracing.rs new file mode 100644 index 0000000000..1925b06b64 --- /dev/null +++ b/wgpu-core/src/device/ray_tracing.rs @@ -0,0 +1,384 @@ +#[cfg(feature = "trace")] +use crate::device::trace; +use crate::{ + device::{queue::TempResource, Device, DeviceError}, + global::Global, + hal_api::HalApi, + id::{self, BlasId, TlasId}, + ray_tracing::{get_raw_tlas_instance_size, CreateBlasError, CreateTlasError}, + resource, LabelHelpers, +}; +use parking_lot::{Mutex, RwLock}; +use std::sync::Arc; + +use crate::resource::{ResourceInfo, StagingBuffer}; +use hal::{AccelerationStructureTriangleIndices, Device as _}; + +impl Device { + fn create_blas( + self: &Arc, + self_id: id::DeviceId, + blas_desc: &resource::BlasDescriptor, + sizes: wgt::BlasGeometrySizeDescriptors, + ) -> Result, CreateBlasError> { + debug_assert_eq!(self_id.backend(), A::VARIANT); + + let size_info = match &sizes { + wgt::BlasGeometrySizeDescriptors::Triangles { desc } => { + let mut entries = + Vec::>::with_capacity(desc.len()); + for x in desc { + if x.index_count.is_some() != x.index_format.is_some() { + return Err(CreateBlasError::MissingIndexData); + } + let indices = + x.index_count + .map(|count| AccelerationStructureTriangleIndices:: { + format: x.index_format.unwrap(), + buffer: None, + offset: 0, + count, + }); + entries.push(hal::AccelerationStructureTriangles:: { + vertex_buffer: None, + vertex_format: x.vertex_format, + first_vertex: 0, + vertex_count: x.vertex_count, + vertex_stride: 0, + indices, + transform: None, + flags: x.flags, + }); + } + unsafe { + self.raw().get_acceleration_structure_build_sizes( + &hal::GetAccelerationStructureBuildSizesDescriptor { + entries: &hal::AccelerationStructureEntries::Triangles(entries), + flags: blas_desc.flags, + }, + ) + } + } + }; + + let raw = unsafe { + self.raw() + .create_acceleration_structure(&hal::AccelerationStructureDescriptor { + label: blas_desc.label.borrow_option(), + size: size_info.acceleration_structure_size, + format: hal::AccelerationStructureFormat::BottomLevel, + }) + } + .map_err(DeviceError::from)?; + + let handle = unsafe { self.raw().get_acceleration_structure_device_address(&raw) }; + + Ok(resource::Blas { + raw: Some(raw), + device: self.clone(), + info: ResourceInfo::new( + blas_desc + .label + .to_hal(self.instance_flags) + .unwrap_or(""), + ), + size_info, + sizes, + flags: blas_desc.flags, + update_mode: blas_desc.update_mode, + handle, + built_index: RwLock::new(None), + }) + } + + fn create_tlas( + self: &Arc, + self_id: id::DeviceId, + desc: &resource::TlasDescriptor, + ) -> Result, CreateTlasError> { + debug_assert_eq!(self_id.backend(), A::VARIANT); + + let size_info = unsafe { + self.raw().get_acceleration_structure_build_sizes( + &hal::GetAccelerationStructureBuildSizesDescriptor { + entries: &hal::AccelerationStructureEntries::Instances( + hal::AccelerationStructureInstances { + buffer: None, + offset: 0, + count: desc.max_instances, + }, + ), + flags: desc.flags, + }, + ) + }; + + let raw = unsafe { + self.raw() + .create_acceleration_structure(&hal::AccelerationStructureDescriptor { + label: desc.label.borrow_option(), + size: size_info.acceleration_structure_size, + format: hal::AccelerationStructureFormat::TopLevel, + }) + } + .map_err(DeviceError::from)?; + + let instance_buffer_size = + get_raw_tlas_instance_size::() * std::cmp::max(desc.max_instances, 1) as usize; + let instance_buffer = unsafe { + self.raw().create_buffer(&hal::BufferDescriptor { + label: Some("(wgpu-core) instances_buffer"), + size: instance_buffer_size as u64, + usage: hal::BufferUses::COPY_DST + | hal::BufferUses::TOP_LEVEL_ACCELERATION_STRUCTURE_INPUT, + memory_flags: hal::MemoryFlags::PREFER_COHERENT, + }) + } + .map_err(DeviceError::from)?; + + Ok(resource::Tlas { + raw: Some(raw), + device: self.clone(), + info: ResourceInfo::new( + desc.label + .to_hal(self.instance_flags) + .unwrap_or(""), + ), + size_info, + flags: desc.flags, + update_mode: desc.update_mode, + built_index: RwLock::new(None), + dependencies: RwLock::new(Vec::new()), + instance_buffer: RwLock::new(Some(instance_buffer)), + max_instance_count: desc.max_instances, + }) + } +} + +impl Global { + pub fn device_create_blas( + &self, + device_id: id::DeviceId, + desc: &resource::BlasDescriptor, + sizes: wgt::BlasGeometrySizeDescriptors, + id_in: Option, + ) -> (BlasId, Option, Option) { + profiling::scope!("Device::create_blas"); + + let hub = A::hub(self); + let fid = hub.blas_s.prepare(id_in); + + let device_guard = hub.devices.read(); + let error = loop { + let device = match device_guard.get(device_id) { + Ok(device) => device, + Err(_) => break DeviceError::Invalid.into(), + }; + if !device.is_valid() { + break DeviceError::Lost.into(); + } + + #[cfg(feature = "trace")] + if let Some(trace) = device.trace.lock().as_mut() { + trace.add(trace::Action::CreateBlas { + id: fid.id(), + desc: desc.clone(), + sizes: sizes.clone(), + }); + } + + let blas = match device.create_blas(device_id, desc, sizes) { + Ok(blas) => blas, + Err(e) => break e, + }; + let handle = blas.handle; + + let (id, resource) = fid.assign(blas); + log::info!("Created blas {:?} with {:?}", id, desc); + + device.trackers.lock().blas_s.insert_single(id, resource); + + return (id, Some(handle), None); + }; + + let id = fid.assign_error(desc.label.borrow_or_default()); + (id, None, Some(error)) + } + + pub fn device_create_tlas( + &self, + device_id: id::DeviceId, + desc: &resource::TlasDescriptor, + id_in: Option, + ) -> (TlasId, Option) { + profiling::scope!("Device::create_tlas"); + + let hub = A::hub(self); + let fid = hub.tlas_s.prepare(id_in); + + let device_guard = hub.devices.read(); + let error = loop { + let device = match device_guard.get(device_id) { + Ok(device) => device, + Err(_) => break DeviceError::Invalid.into(), + }; + #[cfg(feature = "trace")] + if let Some(trace) = device.trace.lock().as_mut() { + trace.add(trace::Action::CreateTlas { + id: fid.id(), + desc: desc.clone(), + }); + } + + let tlas = match device.create_tlas(device_id, desc) { + Ok(tlas) => tlas, + Err(e) => break e, + }; + + let id = fid.assign(tlas); + log::info!("Created tlas {:?} with {:?}", id.0, desc); + + device.trackers.lock().tlas_s.insert_single(id.0, id.1); + + return (id.0, None); + }; + + let id = fid.assign_error(desc.label.borrow_or_default()); + (id, Some(error)) + } + + pub fn blas_destroy(&self, blas_id: BlasId) -> Result<(), resource::DestroyError> { + profiling::scope!("Blas::destroy"); + + let hub = A::hub(self); + + let device_guard = hub.devices.write(); + + log::info!("Blas {:?} is destroyed", blas_id); + let blas_guard = hub.blas_s.write(); + let blas = blas_guard + .get(blas_id) + .map_err(|_| resource::DestroyError::Invalid)?; + + let device = device_guard.get(blas.device.info.id()).unwrap(); + + #[cfg(feature = "trace")] + if let Some(trace) = device.trace.lock().as_mut() { + trace.add(trace::Action::FreeBlas(blas_id)); + } + + let temp = TempResource::Blas(blas.clone()); + { + let last_submit_index = blas.info.submission_index(); + drop(blas_guard); + device + .lock_life() + .schedule_resource_destruction(temp, last_submit_index); + } + + Ok(()) + } + + pub fn blas_drop(&self, blas_id: BlasId, wait: bool) { + profiling::scope!("Blas::drop"); + log::debug!("blas {:?} is dropped", blas_id); + + let hub = A::hub(self); + + if let Some(blas) = hub.blas_s.unregister(blas_id) { + let last_submit_index = blas.info.submission_index(); + + blas.device + .lock_life() + .suspected_resources + .blas_s + .insert(blas_id, blas.clone()); + + if wait { + match blas.device.wait_for_submit(last_submit_index) { + Ok(()) => (), + Err(e) => log::error!("Failed to wait for blas {:?}: {:?}", blas_id, e), + } + } + } + } + + pub fn tlas_destroy(&self, tlas_id: TlasId) -> Result<(), resource::DestroyError> { + profiling::scope!("Tlas::destroy"); + + let hub = A::hub(self); + + log::info!("Tlas {:?} is destroyed", tlas_id); + let tlas_guard = hub.tlas_s.write(); + let tlas = tlas_guard + .get(tlas_id) + .map_err(|_| resource::DestroyError::Invalid)?; + + let device = &mut tlas.device.clone(); + + #[cfg(feature = "trace")] + if let Some(trace) = device.trace.lock().as_mut() { + trace.add(trace::Action::FreeTlas(tlas_id)); + } + + let temp = TempResource::Tlas(tlas.clone()); + + let raw_instance_buffer = tlas.instance_buffer.write().take(); + let temp_instance_buffer = match raw_instance_buffer { + None => None, + Some(e) => { + let size = get_raw_tlas_instance_size::() as u64 + * std::cmp::max(tlas.max_instance_count, 1) as u64; + let mapping = unsafe { + device + .raw() + .map_buffer(&e, 0..size) + .map_err(|_| resource::DestroyError::Invalid)? + }; + Some(TempResource::StagingBuffer(Arc::new(StagingBuffer { + raw: Mutex::new(Some(e)), + device: device.clone(), + size, + info: ResourceInfo::new("Raytracing scratch buffer"), + is_coherent: mapping.is_coherent, + }))) + } + }; + { + let last_submit_index = tlas.info.submission_index(); + drop(tlas_guard); + let guard = &mut device.lock_life(); + + guard.schedule_resource_destruction(temp, last_submit_index); + if let Some(temp_instance_buffer) = temp_instance_buffer { + guard.schedule_resource_destruction(temp_instance_buffer, last_submit_index); + } + } + + Ok(()) + } + + pub fn tlas_drop(&self, tlas_id: TlasId, wait: bool) { + profiling::scope!("Tlas::drop"); + log::debug!("tlas {:?} is dropped", tlas_id); + + let hub = A::hub(self); + + if let Some(tlas) = hub.tlas_s.unregister(tlas_id) { + let last_submit_index = tlas.info.submission_index(); + + tlas.device + .lock_life() + .suspected_resources + .tlas_s + .insert(tlas_id, tlas.clone()); + + if wait { + match tlas.device.wait_for_submit(last_submit_index) { + Ok(()) => (), + Err(e) => log::error!("Failed to wait for blas {:?}: {:?}", tlas_id, e), + } + } + } + } +} diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 6100130c43..8a7588333b 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -132,6 +132,7 @@ pub struct Device { pub(crate) instance_flags: wgt::InstanceFlags, pub(crate) pending_writes: Mutex>>, pub(crate) deferred_destroy: Mutex>>, + pub(crate) last_acceleration_structure_build_command_index: AtomicU64, #[cfg(feature = "trace")] pub(crate) trace: Mutex>, } @@ -294,6 +295,7 @@ impl Device { instance_flags, pending_writes: Mutex::new(Some(pending_writes)), deferred_destroy: Mutex::new(Vec::new()), + last_acceleration_structure_build_command_index: AtomicU64::new(0), }) } @@ -545,6 +547,20 @@ impl Device { .insert(resource.as_info().id(), resource.clone()); } } + for resource in trackers.blas_s.used_resources() { + if resource.is_unique() { + temp_suspected + .blas_s + .insert(resource.as_info().id(), resource.clone()); + } + } + for resource in trackers.tlas_s.used_resources() { + if resource.is_unique() { + temp_suspected + .tlas_s + .insert(resource.as_info().id(), resource.clone()); + } + } } self.lock_life().suspected_resources.extend(temp_suspected); } @@ -1499,6 +1515,10 @@ impl Device { .flags .contains(wgt::DownlevelFlags::MULTISAMPLED_SHADING), ); + caps.set( + Caps::RAY_QUERY, + self.features.contains(wgt::Features::RAY_QUERY), + ); caps.set( Caps::DUAL_SOURCE_BLENDING, self.features.contains(wgt::Features::DUAL_SOURCE_BLENDING), @@ -1779,7 +1799,7 @@ impl Device { }, ) } - Bt::AccelerationStructure => todo!(), + Bt::AccelerationStructure => (None, WritableStorage::No), }; // Validate the count parameter @@ -2077,6 +2097,7 @@ impl Device { let buffer_guard = hub.buffers.read(); let texture_view_guard = hub.texture_views.read(); let sampler_guard = hub.samplers.read(); + let tlas_guard = hub.tlas_s.read(); let mut used_buffer_ranges = Vec::new(); let mut used_texture_ranges = Vec::new(); @@ -2084,6 +2105,7 @@ impl Device { let mut hal_buffers = Vec::new(); let mut hal_samplers = Vec::new(); let mut hal_textures = Vec::new(); + let mut hal_tlas_s = Vec::new(); let snatch_guard = self.snatchable_lock.read(); for entry in desc.entries.iter() { let binding = entry.binding; @@ -2258,6 +2280,18 @@ impl Device { (res_index, num_bindings) } + Br::AccelerationStructure(id) => { + let tlas = used + .acceleration_structures + .add_single(&tlas_guard, id) + .ok_or(Error::InvalidTlas(id))?; + + let raw = tlas.raw.as_ref().ok_or(Error::InvalidTlas(id))?; + + let res_index = hal_tlas_s.len(); + hal_tlas_s.push(raw); + (res_index, 1) + } }; hal_entries.push(hal::BindGroupEntry { @@ -2282,7 +2316,7 @@ impl Device { buffers: &hal_buffers, samplers: &hal_samplers, textures: &hal_textures, - acceleration_structures: &[], + acceleration_structures: &hal_tlas_s, }; let raw = unsafe { self.raw diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index 0802b610d8..78d1e6aedc 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -122,6 +122,19 @@ pub enum Action<'a> { size: wgt::Extent3d, }, Submit(crate::SubmissionIndex, Vec), + CreateBlas { + id: id::BlasId, + desc: crate::resource::BlasDescriptor<'a>, + sizes: wgt::BlasGeometrySizeDescriptors, + }, + FreeBlas(id::BlasId), + DestroyBlas(id::BlasId), + CreateTlas { + id: id::TlasId, + desc: crate::resource::TlasDescriptor<'a>, + }, + FreeTlas(id::TlasId), + DestroyTlas(id::TlasId), } #[derive(Debug)] @@ -183,6 +196,14 @@ pub enum Command { timestamp_writes: Option, occlusion_query_set_id: Option, }, + BuildAccelerationStructuresUnsafeTlas { + blas: Vec, + tlas: Vec, + }, + BuildAccelerationStructures { + blas: Vec, + tlas: Vec, + }, } #[cfg(feature = "trace")] diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index 0f4589c8b3..b88b710382 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -112,7 +112,7 @@ use crate::{ instance::{Adapter, Surface}, pipeline::{ComputePipeline, RenderPipeline, ShaderModule}, registry::{Registry, RegistryReport}, - resource::{Buffer, QuerySet, Sampler, StagingBuffer, Texture, TextureView}, + resource::{Blas, Buffer, QuerySet, Sampler, StagingBuffer, Texture, TextureView, Tlas}, storage::{Element, Storage}, }; use std::fmt::Debug; @@ -186,6 +186,8 @@ pub struct Hub { pub textures: Registry>, pub texture_views: Registry>, pub samplers: Registry>, + pub blas_s: Registry>, + pub tlas_s: Registry>, } impl Hub { @@ -208,6 +210,8 @@ impl Hub { textures: Registry::new(A::VARIANT), texture_views: Registry::new(A::VARIANT), samplers: Registry::new(A::VARIANT), + blas_s: Registry::new(A::VARIANT), + tlas_s: Registry::new(A::VARIANT), } } diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index 1dbb491e60..1db97df9bd 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -347,6 +347,8 @@ ids! { pub type RenderBundleEncoderId RenderBundleEncoder; pub type RenderBundleId RenderBundle; pub type QuerySetId QuerySet; + pub type BlasId Blas; + pub type TlasId Tlas; } #[test] diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index 5454f0d682..cf24f589fa 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -65,6 +65,7 @@ pub mod instance; pub mod pipeline; mod pool; pub mod present; +pub mod ray_tracing; pub mod registry; pub mod resource; mod snatch; diff --git a/wgpu-core/src/ray_tracing.rs b/wgpu-core/src/ray_tracing.rs new file mode 100644 index 0000000000..4dba07cb52 --- /dev/null +++ b/wgpu-core/src/ray_tracing.rs @@ -0,0 +1,290 @@ +/// Ray tracing +/// Major missing optimizations (no api surface changes needed): +/// - use custom tracker to track build state +/// - no forced rebuilt (build mode deduction) +/// - lazy instance buffer allocation +/// - maybe share scratch and instance staging buffer allocation +/// - partial instance buffer uploads (api surface already designed with this in mind) +/// - ([non performance] extract function in build (rust function extraction with guards is a pain)) +use std::{num::NonZeroU64, slice}; + +use crate::{ + command::CommandEncoderError, + device::DeviceError, + hal_api::HalApi, + id::{BlasId, BufferId, TlasId}, + resource::CreateBufferError, +}; + +use thiserror::Error; +use wgt::BufferAddress; + +#[derive(Clone, Debug, Error)] +pub enum CreateBlasError { + #[error(transparent)] + Device(#[from] DeviceError), + #[error(transparent)] + CreateBufferError(#[from] CreateBufferError), + #[error( + "Only one of 'index_count' and 'index_format' was provided (either provide both or none)" + )] + MissingIndexData, +} + +#[derive(Clone, Debug, Error)] +pub enum CreateTlasError { + #[error(transparent)] + Device(#[from] DeviceError), + #[error(transparent)] + CreateBufferError(#[from] CreateBufferError), + #[error("Unimplemented Tlas error: this error is not yet implemented")] + Unimplemented, +} + +/// Error encountered while attempting to do a copy on a command encoder. +#[derive(Clone, Debug, Error)] +pub enum BuildAccelerationStructureError { + #[error(transparent)] + Encoder(#[from] CommandEncoderError), + + #[error(transparent)] + Device(#[from] DeviceError), + + #[error("Buffer {0:?} is invalid or destroyed")] + InvalidBuffer(BufferId), + + #[error("Buffer {0:?} is missing `BLAS_INPUT` usage flag")] + MissingBlasInputUsageFlag(BufferId), + + #[error( + "Buffer {0:?} size is insufficient for provided size information (size: {1}, required: {2}" + )] + InsufficientBufferSize(BufferId, u64, u64), + + #[error("Buffer {0:?} associated offset doesn't align with the index type")] + UnalignedIndexBufferOffset(BufferId), + + #[error("Buffer {0:?} associated offset is unaligned")] + UnalignedTransformBufferOffset(BufferId), + + #[error("Buffer {0:?} associated index count not divisible by 3 (count: {1}")] + InvalidIndexCount(BufferId, u32), + + #[error("Buffer {0:?} associated data contains None")] + MissingAssociatedData(BufferId), + + #[error( + "Blas {0:?} build sizes to may be greater than the descriptor at build time specified" + )] + IncompatibleBlasBuildSizes(BlasId), + + #[error("Blas {0:?} build sizes require index buffer but none was provided")] + MissingIndexBuffer(BlasId), + + #[error("Blas {0:?} is invalid or destroyed")] + InvalidBlas(BlasId), + + #[error( + "Tlas {0:?} an associated instances contains an invalid custom index (more than 24bits)" + )] + TlasInvalidCustomIndex(TlasId), + + #[error( + "Tlas {0:?} has {1} active instances but only {2} are allowed as specified by the descriptor at creation" + )] + TlasInstanceCountExceeded(TlasId, u32, u32), + + #[error("Blas {0:?} is invalid or destroyed (for instance)")] + InvalidBlasForInstance(BlasId), + + #[error("Tlas {0:?} is invalid or destroyed")] + InvalidTlas(TlasId), + + #[error("Buffer {0:?} is missing `TLAS_INPUT` usage flag")] + MissingTlasInputUsageFlag(BufferId), +} + +#[derive(Clone, Debug, Error)] +pub enum ValidateBlasActionsError { + #[error("Blas {0:?} is invalid or destroyed")] + InvalidBlas(BlasId), + + #[error("Blas {0:?} is used before it is build")] + UsedUnbuilt(BlasId), +} + +#[derive(Clone, Debug, Error)] +pub enum ValidateTlasActionsError { + #[error("Tlas {0:?} is invalid or destroyed")] + InvalidTlas(TlasId), + + #[error("Tlas {0:?} is used before it is build")] + UsedUnbuilt(TlasId), + + #[error("Blas {0:?} is used before it is build (in Tlas {1:?})")] + UsedUnbuiltBlas(BlasId, TlasId), + + #[error("Blas {0:?} is invalid or destroyed (in Tlas {1:?})")] + InvalidBlas(BlasId, TlasId), + + #[error("Blas {0:?} is newer than the containing Tlas {1:?}")] + BlasNewerThenTlas(BlasId, TlasId), +} + +#[derive(Debug)] +pub struct BlasTriangleGeometry<'a> { + pub size: &'a wgt::BlasTriangleGeometrySizeDescriptor, + pub vertex_buffer: BufferId, + pub index_buffer: Option, + pub transform_buffer: Option, + pub first_vertex: u32, + pub vertex_stride: BufferAddress, + pub index_buffer_offset: Option, + pub transform_buffer_offset: Option, +} + +pub enum BlasGeometries<'a> { + TriangleGeometries(Box> + 'a>), +} + +pub struct BlasBuildEntry<'a> { + pub blas_id: BlasId, + pub geometries: BlasGeometries<'a>, +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TlasBuildEntry { + pub tlas_id: TlasId, + pub instance_buffer_id: BufferId, + pub instance_count: u32, +} + +#[derive(Debug)] +pub struct TlasInstance<'a> { + pub blas_id: BlasId, + pub transform: &'a [f32; 12], + pub custom_index: u32, + pub mask: u8, +} + +pub struct TlasPackage<'a> { + pub tlas_id: TlasId, + pub instances: Box>> + 'a>, + pub lowest_unmodified: u32, +} + +#[derive(Debug, Copy, Clone)] +pub(crate) enum BlasActionKind { + Build(NonZeroU64), + Use, +} + +#[derive(Debug, Clone)] +pub(crate) enum TlasActionKind { + Build { + build_index: NonZeroU64, + dependencies: Vec, + }, + Use, +} + +#[derive(Debug, Clone)] +pub(crate) struct BlasAction { + pub id: BlasId, + pub kind: BlasActionKind, +} + +#[derive(Debug, Clone)] +pub(crate) struct TlasAction { + pub id: TlasId, + pub kind: TlasActionKind, +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TraceBlasTriangleGeometry { + pub size: wgt::BlasTriangleGeometrySizeDescriptor, + pub vertex_buffer: BufferId, + pub index_buffer: Option, + pub transform_buffer: Option, + pub first_vertex: u32, + pub vertex_stride: BufferAddress, + pub index_buffer_offset: Option, + pub transform_buffer_offset: Option, +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum TraceBlasGeometries { + TriangleGeometries(Vec), +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TraceBlasBuildEntry { + pub blas_id: BlasId, + pub geometries: TraceBlasGeometries, +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TraceTlasInstance { + pub blas_id: BlasId, + pub transform: [f32; 12], + pub custom_index: u32, + pub mask: u8, +} + +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct TraceTlasPackage { + pub tlas_id: TlasId, + pub instances: Vec>, + pub lowest_unmodified: u32, +} + +pub(crate) fn get_raw_tlas_instance_size() -> usize { + match A::VARIANT { + wgt::Backend::Empty => 0, + wgt::Backend::Vulkan => 64, + _ => unimplemented!(), + } +} + +#[derive(Clone)] +#[repr(C)] +struct RawTlasInstance { + transform: [f32; 12], + custom_index_and_mask: u32, + shader_binding_table_record_offset_and_flags: u32, + acceleration_structure_reference: u64, +} + +pub(crate) fn tlas_instance_into_bytes( + instance: &TlasInstance, + blas_address: u64, +) -> Vec { + match A::VARIANT { + wgt::Backend::Empty => vec![], + wgt::Backend::Vulkan => { + const MAX_U24: u32 = (1u32 << 24u32) - 1u32; + let temp = RawTlasInstance { + transform: *instance.transform, + custom_index_and_mask: (instance.custom_index & MAX_U24) + | (u32::from(instance.mask) << 24), + shader_binding_table_record_offset_and_flags: 0, + acceleration_structure_reference: blas_address, + }; + let temp: *const _ = &temp; + unsafe { + slice::from_raw_parts::( + temp as *const u8, + std::mem::size_of::(), + ) + .to_vec() + } + } + _ => unimplemented!(), + } +} diff --git a/wgpu-core/src/registry.rs b/wgpu-core/src/registry.rs index f55809770b..735e9ec2e9 100644 --- a/wgpu-core/src/registry.rs +++ b/wgpu-core/src/registry.rs @@ -84,8 +84,9 @@ impl FutureId<'_, T> { /// Registers it with the registry, and fills out the resource info. pub fn assign(self, value: T) -> (Id, Arc) { let mut data = self.data.write(); - data.insert(self.id, self.init(value)); - (self.id, data.get(self.id).unwrap().clone()) + let inited_val = self.init(value); + data.insert(self.id, inited_val.clone()); + (self.id, inited_val) } /// Assign an existing resource to a new ID. diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 5108328f2b..d65588094e 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -36,6 +36,9 @@ use std::{ }, }; +use crate::id::BlasId; +use std::num::NonZeroU64; + /// Information about the wgpu-core resource. /// /// Each type representing a `wgpu-core` resource, like [`Device`], @@ -1504,3 +1507,96 @@ pub enum DestroyError { #[error("Resource is already destroyed")] AlreadyDestroyed, } + +pub type BlasDescriptor<'a> = wgt::CreateBlasDescriptor>; +pub type TlasDescriptor<'a> = wgt::CreateTlasDescriptor>; + +#[derive(Debug)] +pub struct Blas { + pub(crate) raw: Option, + pub(crate) device: Arc>, + pub(crate) info: ResourceInfo>, + pub(crate) size_info: hal::AccelerationStructureBuildSizes, + pub(crate) sizes: wgt::BlasGeometrySizeDescriptors, + pub(crate) flags: wgt::AccelerationStructureFlags, + pub(crate) update_mode: wgt::AccelerationStructureUpdateMode, + pub(crate) built_index: RwLock>, + pub(crate) handle: u64, +} + +impl Drop for Blas { + fn drop(&mut self) { + #[cfg(feature = "trace")] + if let Some(t) = self.device.trace.lock().as_mut() { + t.add(trace::Action::DestroyBlas(self.info.id())); + } + unsafe { + if let Some(structure) = self.raw.take() { + resource_log!("Destroy raw Blas {:?}", self.info.label()); + use hal::Device; + self.device.raw().destroy_acceleration_structure(structure); + } + } + } +} + +impl Resource for Blas { + const TYPE: &'static str = "Blas"; + + type Marker = crate::id::markers::Blas; + + fn as_info(&self) -> &ResourceInfo { + &self.info + } + + fn as_info_mut(&mut self) -> &mut ResourceInfo { + &mut self.info + } +} + +#[derive(Debug)] +pub struct Tlas { + pub(crate) raw: Option, + pub(crate) device: Arc>, + pub(crate) info: ResourceInfo>, + pub(crate) size_info: hal::AccelerationStructureBuildSizes, + pub(crate) max_instance_count: u32, + pub(crate) flags: wgt::AccelerationStructureFlags, + pub(crate) update_mode: wgt::AccelerationStructureUpdateMode, + pub(crate) built_index: RwLock>, + pub(crate) dependencies: RwLock>, + pub(crate) instance_buffer: RwLock>, +} + +impl Drop for Tlas { + fn drop(&mut self) { + #[cfg(feature = "trace")] + if let Some(t) = self.device.trace.lock().as_mut() { + t.add(trace::Action::DestroyTlas(self.info.id())); + } + unsafe { + use hal::Device; + if let Some(structure) = self.raw.take() { + resource_log!("Destroy raw Tlas {:?}", self.info.label()); + self.device.raw().destroy_acceleration_structure(structure); + } + if let Some(buffer) = self.instance_buffer.write().take() { + self.device.raw().destroy_buffer(buffer) + } + } + } +} + +impl Resource for Tlas { + const TYPE: &'static str = "Tlas"; + + type Marker = crate::id::markers::Tlas; + + fn as_info(&self) -> &ResourceInfo { + &self.info + } + + fn as_info_mut(&mut self) -> &mut ResourceInfo { + &mut self.info + } +} diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index a36280d03b..5d26860284 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -320,6 +320,7 @@ pub(crate) struct BindGroupStates { pub textures: TextureBindGroupState, pub views: StatelessBindGroupSate>, pub samplers: StatelessBindGroupSate>, + pub acceleration_structures: StatelessBindGroupSate>, } impl BindGroupStates { @@ -329,6 +330,7 @@ impl BindGroupStates { textures: TextureBindGroupState::new(), views: StatelessBindGroupSate::new(), samplers: StatelessBindGroupSate::new(), + acceleration_structures: StatelessBindGroupSate::new(), } } @@ -341,6 +343,7 @@ impl BindGroupStates { self.textures.optimize(); self.views.optimize(); self.samplers.optimize(); + self.acceleration_structures.optimize(); } } @@ -496,6 +499,8 @@ pub(crate) struct Tracker { pub render_pipelines: StatelessTracker>, pub bundles: StatelessTracker>, pub query_sets: StatelessTracker>, + pub blas_s: StatelessTracker>, + pub tlas_s: StatelessTracker>, } impl Tracker { @@ -510,6 +515,8 @@ impl Tracker { render_pipelines: StatelessTracker::new(), bundles: StatelessTracker::new(), query_sets: StatelessTracker::new(), + blas_s: StatelessTracker::new(), + tlas_s: StatelessTracker::new(), } } @@ -525,6 +532,8 @@ impl Tracker { render_pipelines: Option<&Storage>>, bundles: Option<&Storage>>, query_sets: Option<&Storage>>, + blas_s: Option<&Storage>>, + tlas_s: Option<&Storage>>, ) { if let Some(buffers) = buffers { self.buffers.set_size(buffers.len()); @@ -553,6 +562,12 @@ impl Tracker { if let Some(query_sets) = query_sets { self.query_sets.set_size(query_sets.len()); }; + if let Some(blas_s) = blas_s { + self.blas_s.set_size(blas_s.len()); + }; + if let Some(tlas_s) = tlas_s { + self.tlas_s.set_size(tlas_s.len()); + }; } /// Iterates through all resources in the given bind group and adopts diff --git a/wgpu-core/src/validation.rs b/wgpu-core/src/validation.rs index d2bcff71b0..e7a3368d38 100644 --- a/wgpu-core/src/validation.rs +++ b/wgpu-core/src/validation.rs @@ -21,6 +21,7 @@ enum ResourceType { Sampler { comparison: bool, }, + AccelerationStructure, } #[derive(Debug)] @@ -529,6 +530,7 @@ impl Resource { }); } } + ResourceType::AccelerationStructure => (), }; Ok(()) @@ -606,6 +608,7 @@ impl Resource { }, } } + ResourceType::AccelerationStructure => BindingType::AccelerationStructure, }) } } @@ -913,6 +916,7 @@ impl Interface { size: wgt::BufferSize::new(size as u64).unwrap(), } } + naga::TypeInner::AccelerationStructure => ResourceType::AccelerationStructure, ref other => ResourceType::Buffer { size: wgt::BufferSize::new(other.size(module.to_ctx()) as u64).unwrap(), }, diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index 5fe7c84c8a..257d11c77b 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -1470,6 +1470,9 @@ impl super::Adapter { // But this requires cloning the `spv::Options` struct, which has heap allocations. true, // could check `super::Workarounds::SEPARATE_ENTRY_POINTS` ); + if features.contains(wgt::Features::RAY_QUERY) { + capabilities.push(spv::Capability::RayQueryKHR); + } spv::Options { lang_version: (1, 0), flags, diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index d73d0b70db..1aaa5550f5 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -94,7 +94,7 @@ pub const QUERY_SIZE: u32 = 8; /// Backends supported by wgpu. #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Backend { /// Dummy backend, used for testing. Empty = 0, @@ -1419,7 +1419,7 @@ impl Limits { /// Represents the sets of additional limits on an adapter, /// which take place when running on downlevel backends. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct DownlevelLimits {} #[allow(unknown_lints)] // derivable_impls is nightly only currently @@ -1432,7 +1432,7 @@ impl Default for DownlevelLimits { /// Lists various ways the underlying platform does not conform to the WebGPU standard. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct DownlevelCapabilities { /// Combined boolean flags. pub flags: DownlevelFlags, @@ -1641,7 +1641,7 @@ impl DownlevelFlags { /// Collections of shader features a device supports if they support less than WebGPU normally allows. // TODO: Fill out the differences between shader models more completely #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ShaderModel { /// Extremely limited shaders, including a total instruction limit. Sm2, @@ -1654,7 +1654,7 @@ pub enum ShaderModel { /// Supported physical device types. #[repr(u8)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum DeviceType { /// Other or Unknown. Other, @@ -1672,7 +1672,7 @@ pub enum DeviceType { /// Information about an adapter. #[derive(Clone, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct AdapterInfo { /// Adapter name pub name: String, @@ -2250,7 +2250,7 @@ impl_bitflags!(TextureFormatFeatureFlags); /// /// Features are defined by WebGPU specification unless `Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES` is enabled. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TextureFormatFeatures { /// Valid bits for `TextureDescriptor::Usage` provided for format creation. pub allowed_usages: TextureUsages, @@ -2261,7 +2261,7 @@ pub struct TextureFormatFeatures { /// ASTC block dimensions #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AstcBlock { /// 4x4 block compressed texture. 16 bytes per block (8 bit/px). B4x4, @@ -2296,7 +2296,7 @@ pub enum AstcBlock { /// ASTC RGBA channel #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum AstcChannel { /// 8 bit integer RGBA, [0, 255] converted to/from linear-color float [0, 1] in shader. /// @@ -4999,6 +4999,10 @@ bitflags::bitflags! { const INDIRECT = 1 << 8; /// Allow a buffer to be the destination buffer for a [`CommandEncoder::resolve_query_set`] operation. const QUERY_RESOLVE = 1 << 9; + /// Allows a buffer to be used as input for a bottom level acceleration structure build + const BLAS_INPUT = 1 << 10; + /// Allows a buffer to be used as input for a top level acceleration structure build + const TLAS_INPUT = 1 << 11; } } @@ -5366,7 +5370,7 @@ impl PresentationTimestamp { /// This is not to be used as a generic color type, only for specific wgpu interfaces. #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct Color { /// Red component of the color @@ -5961,7 +5965,7 @@ impl CommandBufferDescriptor { /// https://gpuweb.github.io/gpuweb/#dictdef-gpurenderbundleencoderdescriptor). #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct RenderBundleDepthStencil { /// Format of the attachment. pub format: TextureFormat, @@ -6024,7 +6028,7 @@ impl Default for RenderBundleDescriptor> { /// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagedatalayout). #[repr(C)] #[derive(Clone, Copy, Debug, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ImageDataLayout { /// Offset into the buffer that is the start of the texture. Must be a multiple of texture block size. /// For non-compressed textures, this is 1. @@ -6466,7 +6470,7 @@ pub struct BindGroupLayoutEntry { /// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopybuffer). #[repr(C)] #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ImageCopyBuffer { /// The buffer to be copied to/from. pub buffer: B, @@ -6480,7 +6484,7 @@ pub struct ImageCopyBuffer { /// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexture). #[repr(C)] #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ImageCopyTexture { /// The texture to be copied to/from. pub texture: T, @@ -6612,7 +6616,7 @@ unsafe impl Sync for ExternalImageSource {} /// Corresponds to [HTML Canvas `PredefinedColorSpace`]( /// https://html.spec.whatwg.org/multipage/canvas.html#predefinedcolorspace). #[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] pub enum PredefinedColorSpace { /// sRGB color space @@ -6627,7 +6631,7 @@ pub enum PredefinedColorSpace { /// Corresponds to [WebGPU `GPUImageCopyTextureTagged`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexturetagged). #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ImageCopyTextureTagged { /// The texture to be copied to/from. pub texture: T, @@ -6658,7 +6662,7 @@ impl ImageCopyTextureTagged { /// Subresource range within an image #[repr(C)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct ImageSubresourceRange { /// Aspect of the texture. Color textures must be [`TextureAspect::All`][TAA]. @@ -6759,7 +6763,7 @@ impl ImageSubresourceRange { /// Color variation to use when sampler addressing mode is [`AddressMode::ClampToBorder`] #[repr(C)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum SamplerBorderColor { /// [0, 0, 0, 0] TransparentBlack, @@ -6781,7 +6785,7 @@ pub enum SamplerBorderColor { /// Corresponds to [WebGPU `GPUQuerySetDescriptor`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpuquerysetdescriptor). #[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct QuerySetDescriptor { /// Debug label for the query set. pub label: L, @@ -6808,7 +6812,7 @@ impl QuerySetDescriptor { /// Corresponds to [WebGPU `GPUQueryType`]( /// https://gpuweb.github.io/gpuweb/#enumdef-gpuquerytype). #[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum QueryType { /// Query returns a single 64-bit number, serving as an occlusion boolean. Occlusion, @@ -6954,7 +6958,7 @@ impl DispatchIndirectArgs { /// Describes how shader bound checks should be performed. #[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ShaderBoundChecks { runtime_checks: bool, } @@ -7063,6 +7067,99 @@ impl Default for InstanceDescriptor { } } +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +/// Descriptor for all size definiing attributes of a single triangle geometry inside a bottom level acceleration structure. +pub struct BlasTriangleGeometrySizeDescriptor { + /// Format of a vertex position. + pub vertex_format: VertexFormat, + /// Number of vertices. + pub vertex_count: u32, + /// Format of an index. Only needed if an index buffer is used. + /// If `index_format` is provided `index_count` is required. + pub index_format: Option, + /// Number of indices. Only needed if an index buffer is used. + /// If `index_count` is provided `index_format` is required. + pub index_count: Option, + /// Flags for the geometry. + pub flags: AccelerationStructureGeometryFlags, +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +/// Descriptor for all size definiing attributes of all geometries inside a bottom level acceleration structure. +pub enum BlasGeometrySizeDescriptors { + /// Triangle geometry version. + Triangles { + /// Descriptor for each triangle geometry. + desc: Vec, + }, +} + +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +/// Update mode for acceleration structure builds. +pub enum AccelerationStructureUpdateMode { + /// Always perform a full build. + Build, + /// If possible, perform an incremental update. + /// Not advised for major topology changes. + /// (Useful for e.g. skinning) + PreferUpdate, +} + +#[repr(C)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +/// Descriptor for creating a bottom level acceleration structure. +pub struct CreateBlasDescriptor { + /// Label for the bottom level acceleration structure. + pub label: L, + /// Flags for the bottom level acceleration structure. + pub flags: AccelerationStructureFlags, + /// Update mode for the bottom level acceleration structure. + pub update_mode: AccelerationStructureUpdateMode, +} + +impl CreateBlasDescriptor { + /// Takes a closure and maps the label of the blas descriptor into another. + pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> CreateBlasDescriptor { + CreateBlasDescriptor { + label: fun(&self.label), + flags: self.flags, + update_mode: self.update_mode, + } + } +} + +#[repr(C)] +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +/// Descriptor for creating a top level acceleration structure. +pub struct CreateTlasDescriptor { + /// Label for the top level acceleration structure. + pub label: L, + /// Number of instances that can be stored in the acceleration structure. + pub max_instances: u32, + /// Flags for the bottom level acceleration structure. + pub flags: AccelerationStructureFlags, + /// Update mode for the bottom level acceleration structure. + pub update_mode: AccelerationStructureUpdateMode, +} + +impl CreateTlasDescriptor { + /// Takes a closure and maps the label of the blas descriptor into another. + pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> CreateTlasDescriptor { + CreateTlasDescriptor { + label: fun(&self.label), + flags: self.flags, + update_mode: self.update_mode, + max_instances: self.max_instances, + } + } +} + bitflags::bitflags!( /// Flags for acceleration structures #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -7093,6 +7190,12 @@ bitflags::bitflags!( ); impl_bitflags!(AccelerationStructureGeometryFlags); +/// Alignment requirement for transform buffers used in acceleration structure builds +pub const TRANSFORM_BUFFER_ALIGNMENT: BufferAddress = 16; + +/// Alignment requirement for instance buffers used in acceleration structure builds +pub const INSTANCE_BUFFER_ALIGNMENT: BufferAddress = 16; + pub use send_sync::*; #[doc(hidden)] diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index 572a95b820..16a9a82295 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -1059,6 +1059,11 @@ impl crate::context::Context for ContextWebGpu { type PopErrorScopeFuture = MakeSendFuture Option>; + type BlasData = (); + type BlasId = ObjectId; + type TlasData = (); + type TlasId = ObjectId; + fn init(_instance_desc: wgt::InstanceDescriptor) -> Self { let Some(gpu) = get_browser_gpu_property() else { panic!( @@ -1646,6 +1651,9 @@ impl crate::context::Context for ContextWebGpu { crate::BindingResource::TextureViewArray(..) => { panic!("Web backend does not support BINDING_INDEXING extension") } + crate::BindingResource::AccelerationStructure(_) => { + unimplemented!("Raytracing not implemented for web") + } }; web_sys::GpuBindGroupEntry::new(binding.binding, &mapped_resource) @@ -3444,6 +3452,61 @@ impl crate::context::Context for ContextWebGpu { .collect::(); pass_data.0.execute_bundles(&mapped); } + + fn device_create_blas( + &self, + _device: &Self::DeviceId, + _device_data: &Self::DeviceData, + _desc: &crate::ray_tracing::CreateBlasDescriptor<'_>, + _sizes: wgt::BlasGeometrySizeDescriptors, + ) -> (Self::BlasId, Option, Self::BlasData) { + unimplemented!("Raytracing not implemented for web"); + } + + fn device_create_tlas( + &self, + _device: &Self::DeviceId, + _device_data: &Self::DeviceData, + _desc: &crate::ray_tracing::CreateTlasDescriptor<'_>, + ) -> (Self::TlasId, Self::TlasData) { + unimplemented!("Raytracing not implemented for web"); + } + + fn command_encoder_build_acceleration_structures_unsafe_tlas<'a>( + &'a self, + _encoder: &Self::CommandEncoderId, + _encoder_data: &Self::CommandEncoderData, + _blas: impl Iterator>, + _tlas: impl Iterator>, + ) { + unimplemented!("Raytracing not implemented for web"); + } + + fn command_encoder_build_acceleration_structures<'a>( + &'a self, + _encoder: &Self::CommandEncoderId, + _encoder_data: &Self::CommandEncoderData, + _blas: impl Iterator>, + _tlas: impl Iterator>, + ) { + unimplemented!("Raytracing not implemented for web"); + } + + fn blas_destroy(&self, _blas: &Self::BlasId, _blas_data: &Self::BlasData) { + unimplemented!("Raytracing not implemented for web"); + } + + fn blas_drop(&self, _blas: &Self::BlasId, _blas_data: &Self::BlasData) { + unimplemented!("Raytracing not implemented for web"); + } + + fn tlas_destroy(&self, _tlas: &Self::TlasId, _tlas_data: &Self::TlasData) { + unimplemented!("Raytracing not implemented for web"); + } + + fn tlas_drop(&self, _tlas: &Self::TlasId, _tlas_data: &Self::TlasData) { + unimplemented!("Raytracing not implemented for web"); + } } pub(crate) type SurfaceOutputDetail = (); diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index b43291e797..6de0454df5 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -435,6 +435,16 @@ pub struct CommandEncoder { open: bool, } +#[derive(Debug)] +pub struct Blas { + // error_sink: ErrorSink, +} + +#[derive(Debug)] +pub struct Tlas { + // error_sink: ErrorSink, +} + impl crate::Context for ContextWgpuCore { type AdapterId = wgc::id::AdapterId; type AdapterData = (); @@ -485,6 +495,11 @@ impl crate::Context for ContextWgpuCore { type RequestAdapterFuture = Ready>; + type BlasId = wgc::id::BlasId; + type BlasData = Blas; + type TlasId = wgc::id::TlasId; + type TlasData = Tlas; + #[allow(clippy::type_complexity)] type RequestDeviceFuture = Ready< Result< @@ -1003,6 +1018,9 @@ impl crate::Context for ContextWgpuCore { &remaining_arrayed_texture_views[array.len()..]; bm::BindingResource::TextureViewArray(Owned(views)) } + BindingResource::AccelerationStructure(acceleration_structure) => { + bm::BindingResource::AccelerationStructure(acceleration_structure.id.into()) + } }, }) .collect::>(); @@ -2908,6 +2926,211 @@ impl crate::Context for ContextWgpuCore { ) } } + + fn device_create_blas( + &self, + device: &Self::DeviceId, + device_data: &Self::DeviceData, + desc: &crate::ray_tracing::CreateBlasDescriptor<'_>, + sizes: wgt::BlasGeometrySizeDescriptors, + ) -> (Self::BlasId, Option, Self::BlasData) { + let global = &self.0; + let (id, handle, error) = wgc::gfx_select!(device => global.device_create_blas( + *device, + &desc.map_label(|l| l.map(Borrowed)), + sizes, + None, + )); + if let Some(cause) = error { + self.handle_error( + &device_data.error_sink, + cause, + LABEL, + desc.label, + "Device::create_blas", + ); + } + ( + id, + handle, + Blas { + // error_sink: Arc::clone(&device_data.error_sink), + }, + ) + } + + fn device_create_tlas( + &self, + device: &Self::DeviceId, + device_data: &Self::DeviceData, + desc: &crate::ray_tracing::CreateTlasDescriptor<'_>, + ) -> (Self::TlasId, Self::TlasData) { + let global = &self.0; + let (id, error) = wgc::gfx_select!(device => global.device_create_tlas( + *device, + &desc.map_label(|l| l.map(Borrowed)), + None, + )); + if let Some(cause) = error { + self.handle_error( + &device_data.error_sink, + cause, + LABEL, + desc.label, + "Device::create_blas", + ); + } + ( + id, + Tlas { + // error_sink: Arc::clone(&device_data.error_sink), + }, + ) + } + + fn command_encoder_build_acceleration_structures_unsafe_tlas<'a>( + &'a self, + encoder: &Self::CommandEncoderId, + encoder_data: &Self::CommandEncoderData, + blas: impl Iterator>, + tlas: impl Iterator>, + ) { + let global = &self.0; + + let blas = blas.map(|e: crate::ray_tracing::ContextBlasBuildEntry<'_, Self>| { + let geometries = match e.geometries { + crate::ray_tracing::ContextBlasGeometries::TriangleGeometries( + triangle_geometries, + ) => { + let iter = triangle_geometries.into_iter().map(|tg| { + wgc::ray_tracing::BlasTriangleGeometry { + vertex_buffer: tg.vertex_buffer, + index_buffer: tg.index_buffer, + transform_buffer: tg.transform_buffer, + size: tg.size, + transform_buffer_offset: tg.transform_buffer_offset, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + } + }); + wgc::ray_tracing::BlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + wgc::ray_tracing::BlasBuildEntry { + blas_id: e.blas_id, + geometries, + } + }); + + let tlas = tlas.into_iter().map( + |e: crate::ray_tracing::ContextTlasBuildEntry| { + wgc::ray_tracing::TlasBuildEntry { + tlas_id: e.tlas_id, + instance_buffer_id: e.instance_buffer_id, + instance_count: e.instance_count, + } + }, + ); + + if let Err(cause) = wgc::gfx_select!(encoder => global.command_encoder_build_acceleration_structures_unsafe_tlas( + *encoder, + blas, + tlas + )) { + self.handle_error_nolabel( + &encoder_data.error_sink, + cause, + "CommandEncoder::build_acceleration_structures_unsafe_tlas", + ); + } + } + + fn command_encoder_build_acceleration_structures<'a>( + &'a self, + encoder: &Self::CommandEncoderId, + encoder_data: &Self::CommandEncoderData, + blas: impl Iterator>, + tlas: impl Iterator>, + ) { + let global = &self.0; + + let blas = blas.map(|e: crate::ray_tracing::ContextBlasBuildEntry<'_, Self>| { + let geometries = match e.geometries { + crate::ray_tracing::ContextBlasGeometries::TriangleGeometries( + triangle_geometries, + ) => { + let iter = triangle_geometries.into_iter().map(|tg| { + wgc::ray_tracing::BlasTriangleGeometry { + vertex_buffer: tg.vertex_buffer, + index_buffer: tg.index_buffer, + transform_buffer: tg.transform_buffer, + size: tg.size, + transform_buffer_offset: tg.transform_buffer_offset, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + } + }); + wgc::ray_tracing::BlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + wgc::ray_tracing::BlasBuildEntry { + blas_id: e.blas_id, + geometries, + } + }); + + let tlas = tlas.into_iter().map(|e| { + let instances = e.instances.map( + |instance: Option>| { + instance.map(|instance| wgc::ray_tracing::TlasInstance { + blas_id: instance.blas_id, + transform: instance.transform, + custom_index: instance.custom_index, + mask: instance.mask, + }) + }, + ); + wgc::ray_tracing::TlasPackage { + tlas_id: e.tlas_id, + instances: Box::new(instances), + lowest_unmodified: e.lowest_unmodified, + } + }); + + if let Err(cause) = wgc::gfx_select!(encoder => global.command_encoder_build_acceleration_structures( + *encoder, + blas, + tlas + )) { + self.handle_error_nolabel( + &encoder_data.error_sink, + cause, + "CommandEncoder::build_acceleration_structures_unsafe_tlas", + ); + } + } + + fn blas_destroy(&self, blas: &Self::BlasId, _blas_data: &Self::BlasData) { + let global = &self.0; + let _ = wgc::gfx_select!(blas => global.blas_destroy(*blas)); + } + + fn blas_drop(&self, blas: &Self::BlasId, _blas_data: &Self::BlasData) { + let global = &self.0; + wgc::gfx_select!(blas => global.blas_drop(*blas, false)) + } + + fn tlas_destroy(&self, tlas: &Self::TlasId, _tlas_data: &Self::TlasData) { + let global = &self.0; + let _ = wgc::gfx_select!(tlas => global.tlas_destroy(*tlas)); + } + + fn tlas_drop(&self, tlas: &Self::TlasId, _tlas_data: &Self::TlasData) { + let global = &self.0; + wgc::gfx_select!(tlas => global.tlas_drop(*tlas, false)) + } } impl From for wgc::id::Id diff --git a/wgpu/src/context.rs b/wgpu/src/context.rs index ba1e52ef71..7f0fc4739b 100644 --- a/wgpu/src/context.rs +++ b/wgpu/src/context.rs @@ -74,6 +74,11 @@ pub trait Context: Debug + WasmNotSendSync + Sized { type SurfaceId: ContextId + WasmNotSendSync; type SurfaceData: ContextData; + type BlasId: ContextId + WasmNotSendSync; + type BlasData: ContextData; + type TlasId: ContextId + WasmNotSendSync; + type TlasData: ContextData; + type SurfaceOutputDetail: WasmNotSendSync + 'static; type SubmissionIndex: ContextId + Clone + Copy + WasmNotSendSync; type SubmissionIndexData: ContextData + Copy; @@ -1013,6 +1018,38 @@ pub trait Context: Debug + WasmNotSendSync + Sized { pass_data: &mut Self::RenderPassData, render_bundles: &mut dyn Iterator, ); + + fn device_create_blas( + &self, + device: &Self::DeviceId, + device_data: &Self::DeviceData, + desc: &crate::ray_tracing::CreateBlasDescriptor<'_>, + sizes: wgt::BlasGeometrySizeDescriptors, + ) -> (Self::BlasId, Option, Self::BlasData); + fn device_create_tlas( + &self, + device: &Self::DeviceId, + device_data: &Self::DeviceData, + desc: &crate::ray_tracing::CreateTlasDescriptor<'_>, + ) -> (Self::TlasId, Self::TlasData); + fn command_encoder_build_acceleration_structures_unsafe_tlas<'a>( + &'a self, + encoder: &Self::CommandEncoderId, + encoder_data: &Self::CommandEncoderData, + blas: impl Iterator>, + tlas: impl Iterator>, + ); + fn command_encoder_build_acceleration_structures<'a>( + &'a self, + encoder: &Self::CommandEncoderId, + encoder_data: &Self::CommandEncoderData, + blas: impl Iterator>, + tlas: impl Iterator>, + ); + fn blas_destroy(&self, blas: &Self::BlasId, blas_data: &Self::BlasData); + fn blas_drop(&self, blas: &Self::BlasId, blas_data: &Self::BlasData); + fn tlas_destroy(&self, tlas: &Self::TlasId, tlas_data: &Self::TlasData); + fn tlas_drop(&self, tlas: &Self::TlasId, tlas_data: &Self::TlasData); } /// Object id. @@ -1977,6 +2014,37 @@ pub(crate) trait DynContext: Debug + WasmNotSendSync { pass_data: &mut crate::Data, render_bundles: &mut dyn Iterator, ); + fn device_create_blas( + &self, + device: &ObjectId, + device_data: &crate::Data, + desc: &crate::ray_tracing::CreateBlasDescriptor<'_>, + sizes: wgt::BlasGeometrySizeDescriptors, + ) -> (ObjectId, Option, Box); + fn device_create_tlas( + &self, + device: &ObjectId, + device_data: &crate::Data, + desc: &crate::ray_tracing::CreateTlasDescriptor<'_>, + ) -> (ObjectId, Box); + fn command_encoder_build_acceleration_structures_unsafe_tlas( + &self, + encoder: &ObjectId, + encoder_data: &crate::Data, + blas: &mut dyn Iterator>, + tlas: &mut dyn Iterator, + ); + fn command_encoder_build_acceleration_structures( + &self, + encoder: &ObjectId, + encoder_data: &crate::Data, + blas: &mut dyn Iterator>, + tlas: &mut dyn Iterator>, + ); + fn blas_destroy(&self, blas: &ObjectId, blas_data: &crate::Data); + fn blas_drop(&self, blas: &ObjectId, blas_data: &crate::Data); + fn tlas_destroy(&self, tlas: &ObjectId, tlas_data: &crate::Data); + fn tlas_drop(&self, tlas: &ObjectId, tlas_data: &crate::Data); } // Blanket impl of DynContext for all types which implement Context. @@ -3973,6 +4041,180 @@ where }); Context::render_pass_execute_bundles(self, &mut pass, pass_data, &mut render_bundles) } + + fn device_create_blas( + &self, + device: &ObjectId, + device_data: &crate::Data, + desc: &crate::ray_tracing::CreateBlasDescriptor<'_>, + sizes: wgt::BlasGeometrySizeDescriptors, + ) -> (ObjectId, Option, Box) { + let device = ::from(*device); + let device_data = downcast_ref(device_data); + let (blas, handle, data) = + Context::device_create_blas(self, &device, device_data, desc, sizes); + (blas.into(), handle, Box::new(data) as _) + } + + fn device_create_tlas( + &self, + device: &ObjectId, + device_data: &crate::Data, + desc: &crate::ray_tracing::CreateTlasDescriptor<'_>, + ) -> (ObjectId, Box) { + let device = ::from(*device); + let device_data = downcast_ref(device_data); + let (tlas, data) = Context::device_create_tlas(self, &device, device_data, desc); + (tlas.into(), Box::new(data) as _) + } + + fn command_encoder_build_acceleration_structures_unsafe_tlas( + &self, + encoder: &ObjectId, + encoder_data: &crate::Data, + blas: &mut dyn Iterator>, + tlas: &mut dyn Iterator, + ) { + let encoder = ::from(*encoder); + let encoder_data = downcast_ref(encoder_data); + + let blas = blas.into_iter().map(|e| { + let geometries = match e.geometries { + crate::ray_tracing::DynContextBlasGeometries::TriangleGeometries( + triangle_geometries, + ) => { + let iter = triangle_geometries.into_iter().map(|tg| { + crate::ray_tracing::ContextBlasTriangleGeometry { + vertex_buffer: ::from(tg.vertex_buffer), + index_buffer: tg.index_buffer.map(::from), + transform_buffer: tg.transform_buffer.map(::from), + size: tg.size, + transform_buffer_offset: tg.transform_buffer_offset, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + } + }); + crate::ray_tracing::ContextBlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + crate::ray_tracing::ContextBlasBuildEntry { + blas_id: ::from(e.blas_id), + // blas_data: downcast_ref(e.blas_data), + geometries, + } + }); + + let tlas = tlas + .into_iter() + .map(|e: crate::ray_tracing::DynContextTlasBuildEntry| { + crate::ray_tracing::ContextTlasBuildEntry { + tlas_id: ::from(e.tlas_id), + // tlas_data: downcast_ref(e.tlas_data), + instance_buffer_id: ::from(e.instance_buffer_id), + // instance_buffer_data: downcast_ref(e.instance_buffer_data), + instance_count: e.instance_count, + } + }); + + Context::command_encoder_build_acceleration_structures_unsafe_tlas( + self, + &encoder, + encoder_data, + blas, + tlas, + ) + } + + fn command_encoder_build_acceleration_structures( + &self, + encoder: &ObjectId, + encoder_data: &crate::Data, + blas: &mut dyn Iterator>, + tlas: &mut dyn Iterator>, + ) { + let encoder = ::from(*encoder); + let encoder_data = downcast_ref(encoder_data); + + let blas = blas.into_iter().map(|e| { + let geometries = match e.geometries { + crate::ray_tracing::DynContextBlasGeometries::TriangleGeometries( + triangle_geometries, + ) => { + let iter = triangle_geometries.into_iter().map(|tg| { + crate::ray_tracing::ContextBlasTriangleGeometry { + vertex_buffer: ::from(tg.vertex_buffer), + index_buffer: tg.index_buffer.map(::from), + transform_buffer: tg.transform_buffer.map(::from), + size: tg.size, + transform_buffer_offset: tg.transform_buffer_offset, + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + } + }); + crate::ray_tracing::ContextBlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + crate::ray_tracing::ContextBlasBuildEntry { + blas_id: ::from(e.blas_id), + // blas_data: downcast_ref(e.blas_data), + geometries, + } + }); + + let tlas = tlas + .into_iter() + .map(|e: crate::ray_tracing::DynContextTlasPackage<'_>| { + let instances = e.instances.map( + |instance: Option>| { + instance.map(|instance| crate::ray_tracing::ContextTlasInstance { + blas_id: ::from(instance.blas), + transform: instance.transform, + custom_index: instance.custom_index, + mask: instance.mask, + }) + }, + ); + crate::ray_tracing::ContextTlasPackage { + tlas_id: ::from(e.tlas_id), + instances: Box::new(instances), + lowest_unmodified: e.lowest_unmodified, + } + }); + + Context::command_encoder_build_acceleration_structures( + self, + &encoder, + encoder_data, + blas, + tlas, + ) + } + + fn blas_destroy(&self, blas: &ObjectId, blas_data: &crate::Data) { + let blas = ::from(*blas); + let blas_data = downcast_ref(blas_data); + Context::blas_destroy(self, &blas, blas_data) + } + + fn blas_drop(&self, blas: &ObjectId, blas_data: &crate::Data) { + let blas = ::from(*blas); + let blas_data = downcast_ref(blas_data); + Context::blas_drop(self, &blas, blas_data) + } + + fn tlas_destroy(&self, tlas: &ObjectId, tlas_data: &crate::Data) { + let tlas = ::from(*tlas); + let tlas_data = downcast_ref(tlas_data); + Context::tlas_destroy(self, &tlas, tlas_data) + } + + fn tlas_drop(&self, tlas: &ObjectId, tlas_data: &crate::Data) { + let tlas = ::from(*tlas); + let tlas_data = downcast_ref(tlas_data); + Context::tlas_drop(self, &tlas, tlas_data) + } } pub trait QueueWriteBuffer: WasmNotSendSync + Debug { diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 299b48db3b..15278e3963 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -24,6 +24,20 @@ pub mod util; #[macro_use] mod macros; +/// Module to add ray tracing support to wgpu. +/// It adds support for acceleration structures and ray queries. +/// The features [`Features::RAY_QUERY`] and [`Features::RAY_TRACING_ACCELERATION_STRUCTURE`] where added and are required to use this module. +/// This is an experimental feature and is not available on all backends. +/// It is not part of the WebGPU standard and is only natively supported with the Vulkan backend so far. +/// DirectX 12 and Metal support are planned, the API is subject to change. +/// (It should be somewhat stable, unless adding ray tracing to the WebGPU standard requires deep changes.) +/// +/// Functions to create and build acceleration structures are provided, In traits on the [`Device`] and [`CommandEncoder`]. +/// To use ray queries, add a top level acceleration structure to a bind group, and use the ray query extension in a shader. +/// Naga (our shader compiler) has support for the ray query extension for "wgsl" shaders. +/// For more details see the examples (starting with ray-). +pub mod ray_tracing; + use std::{ any::Any, borrow::Cow, @@ -1030,6 +1044,8 @@ pub enum BindingResource<'a> { /// Corresponds to [`wgt::BindingType::Texture`] and [`wgt::BindingType::StorageTexture`] with /// [`BindGroupLayoutEntry::count`] set to Some. TextureViewArray(&'a [&'a TextureView]), + /// Todo + AccelerationStructure(&'a crate::ray_tracing::Tlas), } #[cfg(send_sync)] static_assertions::assert_impl_all!(BindingResource<'_>: Send, Sync); diff --git a/wgpu/src/ray_tracing.rs b/wgpu/src/ray_tracing.rs new file mode 100644 index 0000000000..9f762a70e7 --- /dev/null +++ b/wgpu/src/ray_tracing.rs @@ -0,0 +1,552 @@ +use std::{fmt::Debug, ops::Range, sync::Arc, thread}; +use wgt::WasmNotSendSync; + +use crate::{ + context::{Context, DynContext, ObjectId}, + BindingResource, Buffer, CommandEncoder, Data, Device, Label, C, +}; + +/// Descriptor for the size defining attributes of a triangle geometry, for a bottom level acceleration structure. +pub type BlasTriangleGeometrySizeDescriptor = wgt::BlasTriangleGeometrySizeDescriptor; +static_assertions::assert_impl_all!(BlasTriangleGeometrySizeDescriptor: Send, Sync); + +/// Descriptor for the size defining attributes, for a bottom level acceleration structure. +pub type BlasGeometrySizeDescriptors = wgt::BlasGeometrySizeDescriptors; +static_assertions::assert_impl_all!(BlasGeometrySizeDescriptors: Send, Sync); + +/// Flags for an acceleration structure. +pub type AccelerationStructureFlags = wgt::AccelerationStructureFlags; +static_assertions::assert_impl_all!(AccelerationStructureFlags: Send, Sync); + +/// Flags for a geometry inside a bottom level acceleration structure. +pub type AccelerationStructureGeometryFlags = wgt::AccelerationStructureGeometryFlags; +static_assertions::assert_impl_all!(AccelerationStructureGeometryFlags: Send, Sync); + +/// Update mode for acceleration structure builds. +pub type AccelerationStructureUpdateMode = wgt::AccelerationStructureUpdateMode; +static_assertions::assert_impl_all!(AccelerationStructureUpdateMode: Send, Sync); + +/// Descriptor to create bottom level acceleration structures. +pub type CreateBlasDescriptor<'a> = wgt::CreateBlasDescriptor>; +static_assertions::assert_impl_all!(CreateBlasDescriptor<'_>: Send, Sync); + +/// Descriptor to create top level acceleration structures. +pub type CreateTlasDescriptor<'a> = wgt::CreateTlasDescriptor>; +static_assertions::assert_impl_all!(CreateTlasDescriptor<'_>: Send, Sync); + +#[derive(Debug)] +/// Definition for a triangle geometry. +/// The size must match the rest of the structures fields, otherwise the build will fail. +/// (e.g. if a index count is present in the size, the index buffer must be present as well.) +pub struct BlasTriangleGeometry<'a> { + /// Sub descriptor for the size defining attributes of a triangle geometry. + pub size: &'a BlasTriangleGeometrySizeDescriptor, + /// Vertex buffer. + pub vertex_buffer: &'a Buffer, + /// Offset into the vertex buffer as a factor of the vertex stride. + pub first_vertex: u32, + /// Vertex stride. + pub vertex_stride: wgt::BufferAddress, + /// Index buffer (optional). + pub index_buffer: Option<&'a Buffer>, + /// Index buffer offset in bytes (optional, required if index buffer is present). + pub index_buffer_offset: Option, + /// Transform buffer containing 3x4 (rows x columns, row mayor) affine transform matrices `[f32; 12]` (optional). + pub transform_buffer: Option<&'a Buffer>, + /// Transform buffer offset in bytes (optional, required if transform buffer is present). + pub transform_buffer_offset: Option, +} +static_assertions::assert_impl_all!(BlasTriangleGeometry<'_>: WasmNotSendSync); + +/// Geometries for a bottom level acceleration structure. +pub enum BlasGeometries<'a> { + /// Triangle geometry variant. + TriangleGeometries(Vec>), +} +static_assertions::assert_impl_all!(BlasGeometries<'_>: WasmNotSendSync); + +/// Entry for a bottom level acceleration structure build. +pub struct BlasBuildEntry<'a> { + /// Reference to the acceleration structure. + pub blas: &'a Blas, + /// Geometries. + pub geometry: BlasGeometries<'a>, +} +static_assertions::assert_impl_all!(BlasBuildEntry<'_>: WasmNotSendSync); + +#[derive(Debug)] +/// Bottom level acceleration structure. +/// Used to represent a collection of geometries for ray tracing inside a top level acceleration structure. +pub struct Blas { + pub(crate) context: Arc, + pub(crate) id: ObjectId, + pub(crate) data: Box, + pub(crate) handle: Option, +} +static_assertions::assert_impl_all!(Blas: WasmNotSendSync); + +impl Blas { + /// Raw handle to the acceleration structure, used inside raw instance buffers. + pub fn handle(&self) -> Option { + self.handle + } + /// Destroy the associated native resources as soon as possible. + pub fn destroy(&self) { + DynContext::blas_destroy(&*self.context, &self.id, self.data.as_ref()); + } +} + +impl Drop for Blas { + fn drop(&mut self) { + if !thread::panicking() { + self.context.blas_drop(&self.id, self.data.as_ref()); + } + } +} + +#[derive(Debug)] +/// Top level acceleration structure. +/// Used to represent a collection of bottom level acceleration structure instances for ray tracing. +pub struct Tlas { + pub(crate) context: Arc, + pub(crate) id: ObjectId, + pub(crate) data: Box, +} +static_assertions::assert_impl_all!(Tlas: WasmNotSendSync); + +impl Tlas { + /// Destroy the associated native resources as soon as possible. + pub fn destroy(&self) { + DynContext::tlas_destroy(&*self.context, &self.id, self.data.as_ref()); + } +} + +impl Drop for Tlas { + fn drop(&mut self) { + if !thread::panicking() { + self.context.tlas_drop(&self.id, self.data.as_ref()); + } + } +} + +/// Entry for a top level acceleration structure build. +/// Used with raw instance buffers for a unvalidated builds. +pub struct TlasBuildEntry<'a> { + /// Reference to the acceleration structure. + pub tlas: &'a Tlas, + /// Reference to the raw instance buffer. + pub instance_buffer: &'a Buffer, + /// Number of instances in the instance buffer. + pub instance_count: u32, +} +static_assertions::assert_impl_all!(TlasBuildEntry<'_>: WasmNotSendSync); + +/// Safe instance for a top level acceleration structure. +#[derive(Debug, Clone)] +pub struct TlasInstance { + pub(crate) blas: ObjectId, + /// Affine transform matrix 3x4 (rows x columns, row mayor order). + pub transform: [f32; 12], + /// Custom index for the instance used inside the shader (max 24 bits). + pub custom_index: u32, + /// Mask for the instance used inside the shader to filter instances. + pub mask: u8, +} + +impl TlasInstance { + /// Construct TlasInstance. + /// - blas: Reference to the bottom level acceleration structure + /// - transform: Transform buffer offset in bytes (optional, required if transform buffer is present) + /// - custom_index: Custom index for the instance used inside the shader (max 24 bits) + /// - mask: Mask for the instance used inside the shader to filter instances + pub fn new(blas: &Blas, transform: [f32; 12], custom_index: u32, mask: u8) -> Self { + Self { + blas: blas.id, + transform, + custom_index, + mask, + } + } + + /// Set the bottom level acceleration structure. + pub fn set_blas(&mut self, blas: &Blas) { + self.blas = blas.id; + } +} + +pub(crate) struct DynContextTlasInstance<'a> { + pub(crate) blas: ObjectId, + pub(crate) transform: &'a [f32; 12], + pub(crate) custom_index: u32, + pub(crate) mask: u8, +} + +/// [Context version] see `TlasInstance`. +#[allow(dead_code)] +pub struct ContextTlasInstance<'a, T: Context> { + pub(crate) blas_id: T::BlasId, + pub(crate) transform: &'a [f32; 12], + pub(crate) custom_index: u32, + pub(crate) mask: u8, +} + +/// The safe version of TlasEntry, containing TlasInstances instead of a raw buffer. +pub struct TlasPackage { + pub(crate) tlas: Tlas, + pub(crate) instances: Vec>, + pub(crate) lowest_unmodified: u32, +} +static_assertions::assert_impl_all!(TlasPackage: WasmNotSendSync); + +impl TlasPackage { + /// Construct TlasPackage consuming the Tlas (prevents modification of the Tlas without using this package). + /// (max_instances needs to fit into tlas) + pub fn new(tlas: Tlas, max_instances: u32) -> Self { + Self::new_with_instances(tlas, vec![None; max_instances as usize]) + } + + /// Construct TlasPackage consuming the Tlas (prevents modification of the Tlas without using this package). + /// This constructor moves the instances into the package (the number of instances needs to fit into tlas). + pub fn new_with_instances(tlas: Tlas, instances: Vec>) -> Self { + Self { + tlas, + lowest_unmodified: instances.len() as u32, + instances, + } + } + + /// Get a reference to all instances. + pub fn get(&self) -> &[Option] { + &self.instances + } + + /// Get a mutable slice to a range of instances. + /// Returns None if the range is out of bounds. + /// All elements from the lowest accessed index up are marked as modified. + /// For better performance it is recommended to reduce access to low elements. + pub fn get_mut_slice(&mut self, range: Range) -> Option<&mut [Option]> { + if range.end > self.instances.len() { + return None; + } + if range.end as u32 > self.lowest_unmodified { + self.lowest_unmodified = range.end as u32; + } + Some(&mut self.instances[range]) + } + + /// Get a single mutable reference to an instance. + /// Returns None if the range is out of bounds. + /// All elements from the lowest accessed index up are marked as modified. + /// For better performance it is recommended to reduce access to low elements. + pub fn get_mut_single(&mut self, index: usize) -> Option<&mut Option> { + if index >= self.instances.len() { + return None; + } + if index as u32 + 1 > self.lowest_unmodified { + self.lowest_unmodified = index as u32 + 1; + } + Some(&mut self.instances[index]) + } + + /// Get the binding resource for the underling acceleration structure, to be used in a + pub fn as_binding(&self) -> BindingResource<'_> { + BindingResource::AccelerationStructure(&self.tlas) + } + + /// Get a reference to the underling top level acceleration structure. + pub fn tlas(&self) -> &Tlas { + &self.tlas + } +} + +pub(crate) struct DynContextBlasTriangleGeometry<'a> { + pub(crate) size: &'a BlasTriangleGeometrySizeDescriptor, + pub(crate) vertex_buffer: ObjectId, + pub(crate) index_buffer: Option, + pub(crate) transform_buffer: Option, + pub(crate) first_vertex: u32, + pub(crate) vertex_stride: wgt::BufferAddress, + pub(crate) index_buffer_offset: Option, + pub(crate) transform_buffer_offset: Option, +} + +pub(crate) enum DynContextBlasGeometries<'a> { + TriangleGeometries(Box> + 'a>), +} + +pub(crate) struct DynContextBlasBuildEntry<'a> { + pub(crate) blas_id: ObjectId, + pub(crate) geometries: DynContextBlasGeometries<'a>, +} + +pub(crate) struct DynContextTlasBuildEntry { + pub(crate) tlas_id: ObjectId, + pub(crate) instance_buffer_id: ObjectId, + pub(crate) instance_count: u32, +} + +pub(crate) struct DynContextTlasPackage<'a> { + pub(crate) tlas_id: ObjectId, + pub(crate) instances: Box>> + 'a>, + pub(crate) lowest_unmodified: u32, +} + +/// [Context version] see `BlasTriangleGeometry`. +#[allow(dead_code)] +pub struct ContextBlasTriangleGeometry<'a, T: Context> { + pub(crate) size: &'a BlasTriangleGeometrySizeDescriptor, + pub(crate) vertex_buffer: T::BufferId, + pub(crate) index_buffer: Option, + pub(crate) transform_buffer: Option, + pub(crate) first_vertex: u32, + pub(crate) vertex_stride: wgt::BufferAddress, + pub(crate) index_buffer_offset: Option, + pub(crate) transform_buffer_offset: Option, +} + +/// [Context version] see `BlasGeometries`. +pub enum ContextBlasGeometries<'a, T: Context> { + /// Triangle geometries. + TriangleGeometries(Box> + 'a>), +} + +/// [Context version] see `BlasBuildEntry`. +#[allow(dead_code)] +pub struct ContextBlasBuildEntry<'a, T: Context> { + pub(crate) blas_id: T::BlasId, + pub(crate) geometries: ContextBlasGeometries<'a, T>, +} + +/// [Context version] see `TlasBuildEntry`. +#[allow(dead_code)] +pub struct ContextTlasBuildEntry { + pub(crate) tlas_id: T::TlasId, + pub(crate) instance_buffer_id: T::BufferId, + pub(crate) instance_count: u32, +} + +/// [Context version] see `TlasPackage`. +#[allow(dead_code)] +pub struct ContextTlasPackage<'a, T: Context> { + pub(crate) tlas_id: T::TlasId, + pub(crate) instances: Box>> + 'a>, + pub(crate) lowest_unmodified: u32, +} + +/// Utility module to add traits for the device and command encoder. +pub mod traits { + pub use super::{CommandEncoderRayTracing as _, DeviceRayTracing as _}; +} + +/// Trait to add ray tracing functions to a [`Device`]. +pub trait DeviceRayTracing { + /// Create a bottom level acceleration structure, used inside a top level acceleration structure for ray tracing. + /// - desc: The descriptor of the acceleration structure. + /// - sizes: Size descriptor limiting what can be built into the acceleration structure. + fn create_blas( + &self, + desc: &CreateBlasDescriptor<'_>, + sizes: BlasGeometrySizeDescriptors, + ) -> Blas; + + /// Create a top level acceleration structure, used for ray tracing. + /// - desc: The descriptor of the acceleration structure. + fn create_tlas(&self, desc: &CreateTlasDescriptor<'_>) -> Tlas; +} + +impl DeviceRayTracing for Device { + fn create_blas( + &self, + desc: &CreateBlasDescriptor<'_>, + sizes: BlasGeometrySizeDescriptors, + ) -> Blas { + let (id, handle, data) = DynContext::device_create_blas( + &*self.context, + &self.id, + self.data.as_ref(), + desc, + sizes, + ); + + Blas { + context: Arc::clone(&self.context), + id, + data, + handle, + } + } + + fn create_tlas(&self, desc: &CreateTlasDescriptor<'_>) -> Tlas { + let (id, data) = + DynContext::device_create_tlas(&*self.context, &self.id, self.data.as_ref(), desc); + + Tlas { + context: Arc::clone(&self.context), + id, + data, + } + } +} + +/// Trait to add ray tracing functions to a [`CommandEncoder`]. +pub trait CommandEncoderRayTracing { + /// Build bottom and top level acceleration structures. + /// - blas: Iterator of bottom level acceleration structure entries to build + /// For each entry, the provided size descriptor must be strictly smaller or equal to the descriptor given at bottom level acceleration structure creation: + /// - Less or equal number of geometries + /// - Same kind of geometry (with index buffer or without) (same vertex/index format) + /// - Same flags + /// - Less or equal number of vertices + /// - Less or equal number of indices (if applicable) + /// - tlas: iterator of top level acceleration structure packages to build + /// + /// A bottom level acceleration structure may be build and used as a reference in a top level acceleration structure in the same invocation of this function. + /// + /// # Bind group usage + /// + /// When a top level acceleration structure is used in a bind group, some validation takes place: + /// - The top level acceleration structure is valid and has been built. + /// - All the bottom level acceleration structures referenced by the top level acceleration structure are valid and have been built prior, + /// or at same time as the containing top level acceleration structure. + fn build_acceleration_structures<'a>( + &mut self, + blas: impl IntoIterator>, + tlas: impl IntoIterator, + ); + + /// Build bottom and top level acceleration structures. + /// See [`CommandEncoderRayTracing::build_acceleration_structures`] for the safe version and more details. + /// + /// # Safety + /// + /// - The contents of the raw instance buffer must be valid for the underling api. + /// - All bottom level acceleration structures, referenced in the raw instance buffer must be valid and built, + /// when the corresponding top level acceleration structure is built. (builds may happen in the same invocation of this function). + /// - At the time when the top level acceleration structure is used in a bind group, all associated bottom level acceleration structures must be valid, + /// and built (no later than the time when the top level acceleration structure was built). + unsafe fn build_acceleration_structures_unsafe_tlas<'a>( + &mut self, + blas: impl IntoIterator>, + tlas: impl IntoIterator>, + ); +} + +impl CommandEncoderRayTracing for CommandEncoder { + fn build_acceleration_structures<'a>( + &mut self, + blas: impl IntoIterator>, + tlas: impl IntoIterator, + ) { + let id = self.id.as_ref().unwrap(); + + let mut blas = blas.into_iter().map(|e: &BlasBuildEntry<'_>| { + let geometries = match &e.geometry { + BlasGeometries::TriangleGeometries(triangle_geometries) => { + let iter = triangle_geometries + .iter() + .map( + |tg: &BlasTriangleGeometry<'_>| DynContextBlasTriangleGeometry { + size: tg.size, + vertex_buffer: tg.vertex_buffer.id, + + index_buffer: tg.index_buffer.map(|index_buffer| index_buffer.id), + + transform_buffer: tg + .transform_buffer + .map(|transform_buffer| transform_buffer.id), + + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + transform_buffer_offset: tg.transform_buffer_offset, + }, + ); + DynContextBlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + DynContextBlasBuildEntry { + blas_id: e.blas.id, + geometries, + } + }); + + let mut tlas = tlas.into_iter().map(|e: &TlasPackage| { + let instances = e.instances.iter().map(|instance: &Option| { + instance.as_ref().map(|instance| DynContextTlasInstance { + blas: instance.blas, + transform: &instance.transform, + custom_index: instance.custom_index, + mask: instance.mask, + }) + }); + DynContextTlasPackage { + tlas_id: e.tlas.id, + instances: Box::new(instances), + lowest_unmodified: e.lowest_unmodified, + } + }); + + DynContext::command_encoder_build_acceleration_structures( + &*self.context, + id, + self.data.as_ref(), + &mut blas, + &mut tlas, + ); + } + + unsafe fn build_acceleration_structures_unsafe_tlas<'a>( + &mut self, + blas: impl IntoIterator>, + tlas: impl IntoIterator>, + ) { + let id = self.id.as_ref().unwrap(); + + let mut blas = blas.into_iter().map(|e: &BlasBuildEntry<'_>| { + let geometries = match &e.geometry { + BlasGeometries::TriangleGeometries(triangle_geometries) => { + let iter = triangle_geometries + .iter() + .map( + |tg: &BlasTriangleGeometry<'_>| DynContextBlasTriangleGeometry { + size: tg.size, + vertex_buffer: tg.vertex_buffer.id, + + index_buffer: tg.index_buffer.map(|index_buffer| index_buffer.id), + + transform_buffer: tg + .transform_buffer + .map(|transform_buffer| transform_buffer.id), + + first_vertex: tg.first_vertex, + vertex_stride: tg.vertex_stride, + index_buffer_offset: tg.index_buffer_offset, + transform_buffer_offset: tg.transform_buffer_offset, + }, + ); + DynContextBlasGeometries::TriangleGeometries(Box::new(iter)) + } + }; + DynContextBlasBuildEntry { + blas_id: e.blas.id, + geometries, + } + }); + + let mut tlas = tlas + .into_iter() + .map(|e: &TlasBuildEntry<'_>| DynContextTlasBuildEntry { + tlas_id: e.tlas.id, + instance_buffer_id: e.instance_buffer.id, + instance_count: e.instance_count, + }); + + DynContext::command_encoder_build_acceleration_structures_unsafe_tlas( + &*self.context, + id, + self.data.as_ref(), + &mut blas, + &mut tlas, + ); + } +}