use alloc::vec::Vec; use core::{mem, ops::Range}; use windows::Win32::{ Foundation, Graphics::{Direct3D12, Dxgi}, }; use windows_core::Interface; use super::conv; use crate::{ auxil::{ self, dxgi::{name::ObjectExt, result::HResult as _}, }, dx12::borrow_interface_temporarily, AccelerationStructureEntries, CommandEncoder as _, }; fn make_box(origin: &wgt::Origin3d, size: &crate::CopyExtent) -> Direct3D12::D3D12_BOX { Direct3D12::D3D12_BOX { left: origin.x, top: origin.y, right: origin.x + size.width, bottom: origin.y + size.height, front: origin.z, back: origin.z + size.depth, } } impl crate::BufferTextureCopy { fn to_subresource_footprint( &self, format: wgt::TextureFormat, ) -> Direct3D12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT { let (block_width, _) = format.block_dimensions(); Direct3D12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT { Offset: self.buffer_layout.offset, Footprint: Direct3D12::D3D12_SUBRESOURCE_FOOTPRINT { Format: auxil::dxgi::conv::map_texture_format_for_copy( format, self.texture_base.aspect, ) .unwrap(), Width: self.size.width, Height: self.size.height, Depth: self.size.depth, RowPitch: { let actual = self.buffer_layout.bytes_per_row.unwrap_or_else(|| { // this may happen for single-line updates let block_size = format .block_copy_size(Some(self.texture_base.aspect.map())) .unwrap(); (self.size.width / block_width) * block_size }); wgt::math::align_to(actual, Direct3D12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT) }, }, } } } impl super::Temp { fn prepare_marker(&mut self, marker: &str) -> (&[u16], u32) { self.marker.clear(); self.marker.extend(marker.encode_utf16()); self.marker.push(0); (&self.marker, self.marker.len() as u32 * 2) } } impl Drop for super::CommandEncoder { fn drop(&mut self) { use crate::CommandEncoder; unsafe { self.discard_encoding() } let mut rtv_pool = self.rtv_pool.lock(); for handle in self.temp_rtv_handles.drain(..) { rtv_pool.free_handle(handle); } drop(rtv_pool); self.counters.command_encoders.sub(1); } } impl super::CommandEncoder { unsafe fn begin_pass(&mut self, kind: super::PassKind, label: crate::Label) { let list = self.list.as_ref().unwrap(); self.pass.kind = kind; if let Some(label) = label { let (wide_label, size) = self.temp.prepare_marker(label); unsafe { list.BeginEvent(0, Some(wide_label.as_ptr().cast()), size) }; self.pass.has_label = true; } self.pass.dirty_root_elements = 0; self.pass.dirty_vertex_buffers = 0; unsafe { list.SetDescriptorHeaps(&[ Some(self.shared.heap_views.raw.clone()), Some(self.shared.sampler_heap.heap().clone()), ]) }; } unsafe fn end_pass(&mut self) { let list = self.list.as_ref().unwrap(); unsafe { list.SetDescriptorHeaps(&[]) }; if self.pass.has_label { unsafe { list.EndEvent() }; } self.pass.clear(); } unsafe fn prepare_vertex_buffers(&mut self) { while self.pass.dirty_vertex_buffers != 0 { let list = self.list.as_ref().unwrap(); let index = self.pass.dirty_vertex_buffers.trailing_zeros(); self.pass.dirty_vertex_buffers ^= 1 << index; unsafe { list.IASetVertexBuffers( index, Some(&self.pass.vertex_buffers[index as usize..][..1]), ); } } } unsafe fn prepare_draw(&mut self, first_vertex: i32, first_instance: u32) { unsafe { self.prepare_vertex_buffers(); } if let Some(root_index) = self .pass .layout .special_constants .as_ref() .map(|sc| sc.root_index) { let needs_update = match self.pass.root_elements[root_index as usize] { super::RootElement::SpecialConstantBuffer { first_vertex: other_vertex, first_instance: other_instance, other: _, } => first_vertex != other_vertex || first_instance != other_instance, _ => true, }; if needs_update { self.pass.dirty_root_elements |= 1 << root_index; self.pass.root_elements[root_index as usize] = super::RootElement::SpecialConstantBuffer { first_vertex, first_instance, other: 0, }; } } self.update_root_elements(); } fn prepare_dispatch(&mut self, count: [u32; 3]) { if let Some(root_index) = self .pass .layout .special_constants .as_ref() .map(|sc| sc.root_index) { let needs_update = match self.pass.root_elements[root_index as usize] { super::RootElement::SpecialConstantBuffer { first_vertex, first_instance, other, } => [first_vertex as u32, first_instance, other] != count, _ => true, }; if needs_update { self.pass.dirty_root_elements |= 1 << root_index; self.pass.root_elements[root_index as usize] = super::RootElement::SpecialConstantBuffer { first_vertex: count[0] as i32, first_instance: count[1], other: count[2], }; } } self.update_root_elements(); } // Note: we have to call this lazily before draw calls. Otherwise, D3D complains // about the root parameters being incompatible with root signature. fn update_root_elements(&mut self) { use super::PassKind as Pk; while self.pass.dirty_root_elements != 0 { let list = self.list.as_ref().unwrap(); let index = self.pass.dirty_root_elements.trailing_zeros(); self.pass.dirty_root_elements ^= 1 << index; match self.pass.root_elements[index as usize] { super::RootElement::Empty => log::error!("Root index {index} is not bound"), super::RootElement::Constant => { let info = self.pass.layout.root_constant_info.as_ref().unwrap(); for offset in info.range.clone() { let val = self.pass.constant_data[offset as usize]; match self.pass.kind { Pk::Render => unsafe { list.SetGraphicsRoot32BitConstant(index, val, offset) }, Pk::Compute => unsafe { list.SetComputeRoot32BitConstant(index, val, offset) }, Pk::Transfer => (), } } } super::RootElement::SpecialConstantBuffer { first_vertex, first_instance, other, } => match self.pass.kind { Pk::Render => { unsafe { list.SetGraphicsRoot32BitConstant(index, first_vertex as u32, 0) }; unsafe { list.SetGraphicsRoot32BitConstant(index, first_instance, 1) }; } Pk::Compute => { unsafe { list.SetComputeRoot32BitConstant(index, first_vertex as u32, 0) }; unsafe { list.SetComputeRoot32BitConstant(index, first_instance, 1) }; unsafe { list.SetComputeRoot32BitConstant(index, other, 2) }; } Pk::Transfer => (), }, super::RootElement::Table(descriptor) => match self.pass.kind { Pk::Render => unsafe { list.SetGraphicsRootDescriptorTable(index, descriptor) }, Pk::Compute => unsafe { list.SetComputeRootDescriptorTable(index, descriptor) }, Pk::Transfer => (), }, super::RootElement::DynamicUniformBuffer { address } => { let address = address.ptr; match self.pass.kind { Pk::Render => unsafe { list.SetGraphicsRootConstantBufferView(index, address) }, Pk::Compute => unsafe { list.SetComputeRootConstantBufferView(index, address) }, Pk::Transfer => (), } } super::RootElement::DynamicOffsetsBuffer { start, end } => { let values = &self.pass.dynamic_storage_buffer_offsets[start..end]; for (offset, &value) in values.iter().enumerate() { match self.pass.kind { Pk::Render => unsafe { list.SetGraphicsRoot32BitConstant(index, value, offset as u32) }, Pk::Compute => unsafe { list.SetComputeRoot32BitConstant(index, value, offset as u32) }, Pk::Transfer => (), } } } super::RootElement::SamplerHeap => match self.pass.kind { Pk::Render => unsafe { list.SetGraphicsRootDescriptorTable( index, self.shared.sampler_heap.gpu_descriptor_table(), ) }, Pk::Compute => unsafe { list.SetComputeRootDescriptorTable( index, self.shared.sampler_heap.gpu_descriptor_table(), ) }, Pk::Transfer => (), }, } } } fn reset_signature(&mut self, layout: &super::PipelineLayoutShared) { if let Some(root_index) = layout.special_constants.as_ref().map(|sc| sc.root_index) { self.pass.root_elements[root_index as usize] = super::RootElement::SpecialConstantBuffer { first_vertex: 0, first_instance: 0, other: 0, }; } if let Some(root_index) = layout.sampler_heap_root_index { self.pass.root_elements[root_index as usize] = super::RootElement::SamplerHeap; } self.pass.layout = layout.clone(); self.pass.dirty_root_elements = (1 << layout.total_root_elements) - 1; } fn write_pass_end_timestamp_if_requested(&mut self) { if let Some((query_set_raw, index)) = self.end_of_pass_timer_query.take() { use crate::CommandEncoder as _; unsafe { self.write_timestamp( &crate::dx12::QuerySet { raw: query_set_raw, raw_ty: Direct3D12::D3D12_QUERY_TYPE_TIMESTAMP, }, index, ); } } } unsafe fn buf_tex_intermediate( &mut self, region: crate::BufferTextureCopy, tex_fmt: wgt::TextureFormat, copy_op: impl FnOnce(&mut Self, &super::Buffer, wgt::BufferSize, crate::BufferTextureCopy) -> T, ) -> (T, super::Buffer) { let size = { let copy_info = region.buffer_layout.get_buffer_texture_copy_info( tex_fmt, region.texture_base.aspect.map(), ®ion.size.into(), ); copy_info.unwrap().bytes_in_copy }; let size = wgt::BufferSize::new(size).unwrap(); let buffer = { let (resource, allocation) = super::suballocation::DeviceAllocationContext::from(&*self) .create_buffer(&crate::BufferDescriptor { label: None, size: size.get(), usage: wgt::BufferUses::COPY_SRC | wgt::BufferUses::COPY_DST, memory_flags: crate::MemoryFlags::empty(), }) .expect(concat!( "internal error: ", "failed to allocate intermediate buffer ", "for offset alignment" )); super::Buffer { resource, size: size.get(), allocation, } }; let mut region = region; region.buffer_layout.offset = 0; unsafe { self.transition_buffers( [crate::BufferBarrier { buffer: &buffer, usage: crate::StateTransition { from: wgt::BufferUses::empty(), to: wgt::BufferUses::COPY_DST, }, }] .into_iter(), ) }; let t = copy_op(self, &buffer, size, region); unsafe { self.transition_buffers( [crate::BufferBarrier { buffer: &buffer, usage: crate::StateTransition { from: wgt::BufferUses::COPY_DST, to: wgt::BufferUses::COPY_SRC, }, }] .into_iter(), ) }; (t, buffer) } } impl crate::CommandEncoder for super::CommandEncoder { type A = super::Api; unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> { let list = loop { if let Some(list) = self.free_lists.pop() { // TODO: Is an error expected here and should we print it? let reset_result = unsafe { list.Reset(&self.allocator, None) }; if reset_result.is_ok() { break Some(list); } } else { break None; } }; let list = if let Some(list) = list { list } else { unsafe { self.device.CreateCommandList( 0, Direct3D12::D3D12_COMMAND_LIST_TYPE_DIRECT, &self.allocator, None, ) } .into_device_result("Create command list")? }; if let Some(label) = label { list.set_name(label)?; } self.list = Some(list); self.temp.clear(); self.pass.clear(); Ok(()) } unsafe fn discard_encoding(&mut self) { if let Some(list) = self.list.take() { if unsafe { list.Close() }.is_ok() { self.free_lists.push(list); } } } unsafe fn end_encoding(&mut self) -> Result { let raw = self.list.take().unwrap(); unsafe { raw.Close() }.into_device_result("GraphicsCommandList::close")?; Ok(super::CommandBuffer { raw }) } unsafe fn reset_all>(&mut self, command_buffers: I) { self.intermediate_copy_bufs.clear(); for cmd_buf in command_buffers { self.free_lists.push(cmd_buf.raw); } if let Err(e) = unsafe { self.allocator.Reset() } { log::error!("ID3D12CommandAllocator::Reset() failed with {e}"); } } unsafe fn transition_buffers<'a, T>(&mut self, barriers: T) where T: Iterator>, { self.temp.barriers.clear(); for barrier in barriers { let s0 = conv::map_buffer_usage_to_state(barrier.usage.from); let s1 = conv::map_buffer_usage_to_state(barrier.usage.to); if s0 != s1 { let raw = Direct3D12::D3D12_RESOURCE_BARRIER { Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE, Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 { Transition: mem::ManuallyDrop::new( Direct3D12::D3D12_RESOURCE_TRANSITION_BARRIER { pResource: unsafe { borrow_interface_temporarily(&barrier.buffer.resource) }, Subresource: Direct3D12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, StateBefore: s0, StateAfter: s1, }, ), }, }; self.temp.barriers.push(raw); } else if barrier.usage.from == wgt::BufferUses::STORAGE_READ_WRITE || barrier.usage.from == wgt::BufferUses::ACCELERATION_STRUCTURE_QUERY { let raw = Direct3D12::D3D12_RESOURCE_BARRIER { Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_UAV, Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE, Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 { UAV: mem::ManuallyDrop::new(Direct3D12::D3D12_RESOURCE_UAV_BARRIER { pResource: unsafe { borrow_interface_temporarily(&barrier.buffer.resource) }, }), }, }; self.temp.barriers.push(raw); } } if !self.temp.barriers.is_empty() { unsafe { self.list .as_ref() .unwrap() .ResourceBarrier(&self.temp.barriers) }; } } unsafe fn transition_textures<'a, T>(&mut self, barriers: T) where T: Iterator>, { self.temp.barriers.clear(); for barrier in barriers { let s0 = conv::map_texture_usage_to_state(barrier.usage.from); let s1 = conv::map_texture_usage_to_state(barrier.usage.to); if s0 != s1 { let mut raw = Direct3D12::D3D12_RESOURCE_BARRIER { Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE, Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 { Transition: mem::ManuallyDrop::new( Direct3D12::D3D12_RESOURCE_TRANSITION_BARRIER { pResource: unsafe { borrow_interface_temporarily(&barrier.texture.resource) }, Subresource: Direct3D12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, StateBefore: s0, StateAfter: s1, }, ), }, }; let tex_mip_level_count = barrier.texture.mip_level_count; let tex_array_layer_count = barrier.texture.array_layer_count(); if barrier.range.is_full_resource( barrier.texture.format, tex_mip_level_count, tex_array_layer_count, ) { // Only one barrier if it affects the whole image. self.temp.barriers.push(raw); } else { // Selected texture aspect is relevant if the texture format has both depth _and_ stencil aspects. let planes = if barrier.texture.format.is_combined_depth_stencil_format() { match barrier.range.aspect { wgt::TextureAspect::All => 0..2, wgt::TextureAspect::DepthOnly => 0..1, wgt::TextureAspect::StencilOnly => 1..2, _ => unreachable!(), } } else if let Some(planes) = barrier.texture.format.planes() { match barrier.range.aspect { wgt::TextureAspect::All => 0..planes, wgt::TextureAspect::Plane0 => 0..1, wgt::TextureAspect::Plane1 => 1..2, wgt::TextureAspect::Plane2 => 2..3, _ => unreachable!(), } } else { match barrier.texture.format { wgt::TextureFormat::Stencil8 => 1..2, wgt::TextureFormat::Depth24Plus => 0..2, // TODO: investigate why tests fail if we set this to 0..1 _ => 0..1, } }; for mip_level in barrier.range.mip_range(tex_mip_level_count) { for array_layer in barrier.range.layer_range(tex_array_layer_count) { for plane in planes.clone() { unsafe { &mut *raw.Anonymous.Transition }.Subresource = barrier .texture .calc_subresource(mip_level, array_layer, plane); self.temp.barriers.push(raw.clone()); } } } } } else if barrier.usage.from == wgt::TextureUses::STORAGE_READ_WRITE { let raw = Direct3D12::D3D12_RESOURCE_BARRIER { Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_UAV, Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE, Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 { UAV: mem::ManuallyDrop::new(Direct3D12::D3D12_RESOURCE_UAV_BARRIER { pResource: unsafe { borrow_interface_temporarily(&barrier.texture.resource) }, }), }, }; self.temp.barriers.push(raw); } } if !self.temp.barriers.is_empty() { unsafe { self.list .as_ref() .unwrap() .ResourceBarrier(&self.temp.barriers) }; } } unsafe fn clear_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange) { let list = self.list.as_ref().unwrap(); let mut offset = range.start; while offset < range.end { let size = super::ZERO_BUFFER_SIZE.min(range.end - offset); unsafe { list.CopyBufferRegion(&buffer.resource, offset, &self.shared.zero_buffer, 0, size) }; offset += size; } } unsafe fn copy_buffer_to_buffer( &mut self, src: &super::Buffer, dst: &super::Buffer, regions: T, ) where T: Iterator, { let list = self.list.as_ref().unwrap(); for r in regions { unsafe { list.CopyBufferRegion( &dst.resource, r.dst_offset, &src.resource, r.src_offset, r.size.get(), ) }; } } unsafe fn copy_texture_to_texture( &mut self, src: &super::Texture, _src_usage: wgt::TextureUses, dst: &super::Texture, regions: T, ) where T: Iterator, { let list = self.list.as_ref().unwrap(); for r in regions { let src_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION { pResource: unsafe { borrow_interface_temporarily(&src.resource) }, Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 { SubresourceIndex: src.calc_subresource_for_copy(&r.src_base), }, }; let dst_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION { pResource: unsafe { borrow_interface_temporarily(&dst.resource) }, Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 { SubresourceIndex: dst.calc_subresource_for_copy(&r.dst_base), }, }; let src_box = make_box(&r.src_base.origin, &r.size); unsafe { list.CopyTextureRegion( &dst_location, r.dst_base.origin.x, r.dst_base.origin.y, r.dst_base.origin.z, &src_location, Some(&src_box), ) }; } } unsafe fn copy_buffer_to_texture( &mut self, src: &super::Buffer, dst: &super::Texture, regions: T, ) where T: Iterator, { let offset_alignment = self.shared.private_caps.texture_data_placement_alignment(); for naive_copy_region in regions { let is_offset_aligned = naive_copy_region.buffer_layout.offset % offset_alignment == 0; let (final_copy_region, src) = if is_offset_aligned { (naive_copy_region, src) } else { let (intermediate_to_dst_region, intermediate_buf) = unsafe { let src_offset = naive_copy_region.buffer_layout.offset; self.buf_tex_intermediate( naive_copy_region, dst.format, |this, buf, size, intermediate_to_dst_region| { let layout = crate::BufferCopy { src_offset, dst_offset: 0, size, }; this.copy_buffer_to_buffer(src, buf, [layout].into_iter()); intermediate_to_dst_region }, ) }; self.intermediate_copy_bufs.push(intermediate_buf); let intermediate_buf = self.intermediate_copy_bufs.last().unwrap(); (intermediate_to_dst_region, intermediate_buf) }; let list = self.list.as_ref().unwrap(); let src_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION { pResource: unsafe { borrow_interface_temporarily(&src.resource) }, Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 { PlacedFootprint: final_copy_region.to_subresource_footprint(dst.format), }, }; let dst_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION { pResource: unsafe { borrow_interface_temporarily(&dst.resource) }, Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 { SubresourceIndex: dst .calc_subresource_for_copy(&final_copy_region.texture_base), }, }; let src_box = make_box(&wgt::Origin3d::ZERO, &final_copy_region.size); unsafe { list.CopyTextureRegion( &dst_location, final_copy_region.texture_base.origin.x, final_copy_region.texture_base.origin.y, final_copy_region.texture_base.origin.z, &src_location, Some(&src_box), ) }; } } unsafe fn copy_texture_to_buffer( &mut self, src: &super::Texture, _src_usage: wgt::TextureUses, dst: &super::Buffer, regions: T, ) where T: Iterator, { let copy_aligned = |this: &mut Self, src: &super::Texture, dst: &super::Buffer, r: crate::BufferTextureCopy| { let list = this.list.as_ref().unwrap(); let src_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION { pResource: unsafe { borrow_interface_temporarily(&src.resource) }, Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 { SubresourceIndex: src.calc_subresource_for_copy(&r.texture_base), }, }; let dst_location = Direct3D12::D3D12_TEXTURE_COPY_LOCATION { pResource: unsafe { borrow_interface_temporarily(&dst.resource) }, Type: Direct3D12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, Anonymous: Direct3D12::D3D12_TEXTURE_COPY_LOCATION_0 { PlacedFootprint: r.to_subresource_footprint(src.format), }, }; let src_box = make_box(&r.texture_base.origin, &r.size); unsafe { list.CopyTextureRegion(&dst_location, 0, 0, 0, &src_location, Some(&src_box)) }; }; let offset_alignment = self.shared.private_caps.texture_data_placement_alignment(); for r in regions { let is_offset_aligned = r.buffer_layout.offset % offset_alignment == 0; if is_offset_aligned { copy_aligned(self, src, dst, r) } else { let orig_offset = r.buffer_layout.offset; let (intermediate_to_dst_region, src) = unsafe { self.buf_tex_intermediate( r, src.format, |this, buf, size, intermediate_region| { copy_aligned(this, src, buf, intermediate_region); crate::BufferCopy { src_offset: 0, dst_offset: orig_offset, size, } }, ) }; unsafe { self.copy_buffer_to_buffer(&src, dst, [intermediate_to_dst_region].into_iter()); } self.intermediate_copy_bufs.push(src); }; } } unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) { unsafe { self.list .as_ref() .unwrap() .BeginQuery(&set.raw, set.raw_ty, index) }; } unsafe fn end_query(&mut self, set: &super::QuerySet, index: u32) { unsafe { self.list .as_ref() .unwrap() .EndQuery(&set.raw, set.raw_ty, index) }; } unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) { unsafe { self.list.as_ref().unwrap().EndQuery( &set.raw, Direct3D12::D3D12_QUERY_TYPE_TIMESTAMP, index, ) }; } unsafe fn read_acceleration_structure_compact_size( &mut self, acceleration_structure: &super::AccelerationStructure, buf: &super::Buffer, ) { let list = self .list .as_ref() .unwrap() .cast::() .unwrap(); unsafe { list.EmitRaytracingAccelerationStructurePostbuildInfo( &Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_DESC { DestBuffer: buf.resource.GetGPUVirtualAddress(), InfoType: Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_POSTBUILD_INFO_COMPACTED_SIZE, }, &[ acceleration_structure.resource.GetGPUVirtualAddress() ], ) } } unsafe fn reset_queries(&mut self, _set: &super::QuerySet, _range: Range) { // nothing to do here } unsafe fn copy_query_results( &mut self, set: &super::QuerySet, range: Range, buffer: &super::Buffer, offset: wgt::BufferAddress, _stride: wgt::BufferSize, ) { unsafe { self.list.as_ref().unwrap().ResolveQueryData( &set.raw, set.raw_ty, range.start, range.end - range.start, &buffer.resource, offset, ) }; } // render unsafe fn begin_render_pass( &mut self, desc: &crate::RenderPassDescriptor, ) -> Result<(), crate::DeviceError> { unsafe { self.begin_pass(super::PassKind::Render, desc.label) }; // Start timestamp if any (before all other commands but after debug marker) if let Some(timestamp_writes) = desc.timestamp_writes.as_ref() { if let Some(index) = timestamp_writes.beginning_of_pass_write_index { unsafe { self.write_timestamp(timestamp_writes.query_set, index); } } self.end_of_pass_timer_query = timestamp_writes .end_of_pass_write_index .map(|index| (timestamp_writes.query_set.raw.clone(), index)); } let mut color_views = [Direct3D12::D3D12_CPU_DESCRIPTOR_HANDLE { ptr: 0 }; crate::MAX_COLOR_ATTACHMENTS]; let mut rtv_pool = self.rtv_pool.lock(); for (rtv, cat) in color_views.iter_mut().zip(desc.color_attachments.iter()) { if let Some(cat) = cat.as_ref() { if cat.target.view.dimension == wgt::TextureViewDimension::D3 { let desc = Direct3D12::D3D12_RENDER_TARGET_VIEW_DESC { Format: cat.target.view.raw_format, ViewDimension: Direct3D12::D3D12_RTV_DIMENSION_TEXTURE3D, Anonymous: Direct3D12::D3D12_RENDER_TARGET_VIEW_DESC_0 { Texture3D: Direct3D12::D3D12_TEX3D_RTV { MipSlice: cat.target.view.mip_slice, FirstWSlice: cat.depth_slice.unwrap(), WSize: 1, }, }, }; let handle = rtv_pool.alloc_handle()?; unsafe { self.device.CreateRenderTargetView( &cat.target.view.texture, Some(&desc), handle.raw, ) }; *rtv = handle.raw; self.temp_rtv_handles.push(handle); } else { *rtv = cat.target.view.handle_rtv.unwrap().raw; } } else { *rtv = self.null_rtv_handle.raw; } } drop(rtv_pool); let ds_view = desc.depth_stencil_attachment.as_ref().map(|ds| { if ds.target.usage == wgt::TextureUses::DEPTH_STENCIL_WRITE { ds.target.view.handle_dsv_rw.as_ref().unwrap().raw } else { ds.target.view.handle_dsv_ro.as_ref().unwrap().raw } }); let list = self.list.as_ref().unwrap(); #[allow(trivial_casts)] // No other clean way to write the coercion inside .map() below? unsafe { list.OMSetRenderTargets( desc.color_attachments.len() as u32, Some(color_views.as_ptr()), false, ds_view.as_ref().map(core::ptr::from_ref), ) }; self.pass.resolves.clear(); for (rtv, cat) in color_views.iter().zip(desc.color_attachments.iter()) { if let Some(cat) = cat.as_ref() { if !cat.ops.contains(crate::AttachmentOps::LOAD) { let value = [ cat.clear_value.r as f32, cat.clear_value.g as f32, cat.clear_value.b as f32, cat.clear_value.a as f32, ]; unsafe { list.ClearRenderTargetView(*rtv, &value, None) }; } if let Some(ref target) = cat.resolve_target { self.pass.resolves.push(super::PassResolve { src: ( cat.target.view.texture.clone(), cat.target.view.subresource_index, ), dst: (target.view.texture.clone(), target.view.subresource_index), format: target.view.raw_format, }); } } } if let Some(ref ds) = desc.depth_stencil_attachment { let mut flags = Direct3D12::D3D12_CLEAR_FLAGS::default(); let aspects = ds.target.view.aspects; if !ds.depth_ops.contains(crate::AttachmentOps::LOAD) && aspects.contains(crate::FormatAspects::DEPTH) { flags |= Direct3D12::D3D12_CLEAR_FLAG_DEPTH; } if !ds.stencil_ops.contains(crate::AttachmentOps::LOAD) && aspects.contains(crate::FormatAspects::STENCIL) { flags |= Direct3D12::D3D12_CLEAR_FLAG_STENCIL; } if let Some(ds_view) = ds_view { if flags != Direct3D12::D3D12_CLEAR_FLAGS::default() { unsafe { // list.ClearDepthStencilView( // ds_view, // flags, // ds.clear_value.0, // ds.clear_value.1 as u8, // None, // ) // TODO: Replace with the above in the next breaking windows-rs release, // when https://github.com/microsoft/win32metadata/pull/1971 is in. (Interface::vtable(list).ClearDepthStencilView)( Interface::as_raw(list), ds_view, flags, ds.clear_value.0, ds.clear_value.1 as u8, 0, core::ptr::null(), ) } } } } let raw_vp = Direct3D12::D3D12_VIEWPORT { TopLeftX: 0.0, TopLeftY: 0.0, Width: desc.extent.width as f32, Height: desc.extent.height as f32, MinDepth: 0.0, MaxDepth: 1.0, }; let raw_rect = Foundation::RECT { left: 0, top: 0, right: desc.extent.width as i32, bottom: desc.extent.height as i32, }; unsafe { list.RSSetViewports(core::slice::from_ref(&raw_vp)) }; unsafe { list.RSSetScissorRects(core::slice::from_ref(&raw_rect)) }; Ok(()) } unsafe fn end_render_pass(&mut self) { if !self.pass.resolves.is_empty() { let list = self.list.as_ref().unwrap(); self.temp.barriers.clear(); // All the targets are expected to be in `COLOR_TARGET` state, // but D3D12 has special source/destination states for the resolves. for resolve in self.pass.resolves.iter() { let barrier = Direct3D12::D3D12_RESOURCE_BARRIER { Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE, Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 { // Note: this assumes `D3D12_RESOURCE_STATE_RENDER_TARGET`. // If it's not the case, we can include the `TextureUses` in `PassResolve`. Transition: mem::ManuallyDrop::new( Direct3D12::D3D12_RESOURCE_TRANSITION_BARRIER { pResource: unsafe { borrow_interface_temporarily(&resolve.src.0) }, Subresource: resolve.src.1, StateBefore: Direct3D12::D3D12_RESOURCE_STATE_RENDER_TARGET, StateAfter: Direct3D12::D3D12_RESOURCE_STATE_RESOLVE_SOURCE, }, ), }, }; self.temp.barriers.push(barrier); let barrier = Direct3D12::D3D12_RESOURCE_BARRIER { Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE, Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 { // Note: this assumes `D3D12_RESOURCE_STATE_RENDER_TARGET`. // If it's not the case, we can include the `TextureUses` in `PassResolve`. Transition: mem::ManuallyDrop::new( Direct3D12::D3D12_RESOURCE_TRANSITION_BARRIER { pResource: unsafe { borrow_interface_temporarily(&resolve.dst.0) }, Subresource: resolve.dst.1, StateBefore: Direct3D12::D3D12_RESOURCE_STATE_RENDER_TARGET, StateAfter: Direct3D12::D3D12_RESOURCE_STATE_RESOLVE_DEST, }, ), }, }; self.temp.barriers.push(barrier); } if !self.temp.barriers.is_empty() { profiling::scope!("ID3D12GraphicsCommandList::ResourceBarrier"); unsafe { list.ResourceBarrier(&self.temp.barriers) }; } for resolve in self.pass.resolves.iter() { profiling::scope!("ID3D12GraphicsCommandList::ResolveSubresource"); unsafe { list.ResolveSubresource( &resolve.dst.0, resolve.dst.1, &resolve.src.0, resolve.src.1, resolve.format, ) }; } // Flip all the barriers to reverse, back into `COLOR_TARGET`. for barrier in self.temp.barriers.iter_mut() { let transition = unsafe { &mut *barrier.Anonymous.Transition }; mem::swap(&mut transition.StateBefore, &mut transition.StateAfter); } if !self.temp.barriers.is_empty() { profiling::scope!("ID3D12GraphicsCommandList::ResourceBarrier"); unsafe { list.ResourceBarrier(&self.temp.barriers) }; } } self.write_pass_end_timestamp_if_requested(); unsafe { self.end_pass() }; } unsafe fn set_bind_group( &mut self, layout: &super::PipelineLayout, index: u32, group: &super::BindGroup, dynamic_offsets: &[wgt::DynamicOffset], ) { let info = &layout.bind_group_infos[index as usize]; let mut root_index = info.base_root_index as usize; // Bind CBV/SRC/UAV descriptor tables if info.tables.contains(super::TableTypes::SRV_CBV_UAV) { self.pass.root_elements[root_index] = super::RootElement::Table(group.handle_views.unwrap().gpu); root_index += 1; } let mut offsets_index = 0; if let Some(dynamic_storage_buffer_offsets) = info.dynamic_storage_buffer_offsets.as_ref() { let root_index = dynamic_storage_buffer_offsets.root_index; let range = &dynamic_storage_buffer_offsets.range; if range.end > self.pass.dynamic_storage_buffer_offsets.len() { self.pass .dynamic_storage_buffer_offsets .resize(range.end, 0); } offsets_index += range.start; self.pass.root_elements[root_index as usize] = super::RootElement::DynamicOffsetsBuffer { start: range.start, end: range.end, }; if self.pass.layout.signature == layout.shared.signature { self.pass.dirty_root_elements |= 1 << root_index; } else { // D3D12 requires full reset on signature change // but we don't reset it here since it will be reset below }; } // Bind root descriptors for dynamic uniform buffers // or set root constants for offsets of dynamic storage buffers for (&dynamic_buffer, &offset) in group.dynamic_buffers.iter().zip(dynamic_offsets) { match dynamic_buffer { super::DynamicBuffer::Uniform(gpu_base) => { self.pass.root_elements[root_index] = super::RootElement::DynamicUniformBuffer { address: Direct3D12::D3D12_GPU_DESCRIPTOR_HANDLE { ptr: gpu_base.ptr + offset as u64, }, }; root_index += 1; } super::DynamicBuffer::Storage => { self.pass.dynamic_storage_buffer_offsets[offsets_index] = offset; offsets_index += 1; } } } if self.pass.layout.signature == layout.shared.signature { self.pass.dirty_root_elements |= (1 << root_index) - (1 << info.base_root_index); } else { // D3D12 requires full reset on signature change self.reset_signature(&layout.shared); }; } unsafe fn set_push_constants( &mut self, layout: &super::PipelineLayout, _stages: wgt::ShaderStages, offset_bytes: u32, data: &[u32], ) { let offset_words = offset_bytes as usize / 4; let info = layout.shared.root_constant_info.as_ref().unwrap(); self.pass.root_elements[info.root_index as usize] = super::RootElement::Constant; self.pass.constant_data[offset_words..(offset_words + data.len())].copy_from_slice(data); if self.pass.layout.signature == layout.shared.signature { self.pass.dirty_root_elements |= 1 << info.root_index; } else { // D3D12 requires full reset on signature change self.reset_signature(&layout.shared); }; } unsafe fn insert_debug_marker(&mut self, label: &str) { let (wide_label, size) = self.temp.prepare_marker(label); unsafe { self.list .as_ref() .unwrap() .SetMarker(0, Some(wide_label.as_ptr().cast()), size) }; } unsafe fn begin_debug_marker(&mut self, group_label: &str) { let (wide_label, size) = self.temp.prepare_marker(group_label); unsafe { self.list .as_ref() .unwrap() .BeginEvent(0, Some(wide_label.as_ptr().cast()), size) }; } unsafe fn end_debug_marker(&mut self) { unsafe { self.list.as_ref().unwrap().EndEvent() } } unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) { let list = self.list.clone().unwrap(); if self.pass.layout.signature != pipeline.layout.signature { // D3D12 requires full reset on signature change unsafe { list.SetGraphicsRootSignature(pipeline.layout.signature.as_ref()) }; self.reset_signature(&pipeline.layout); }; unsafe { list.SetPipelineState(&pipeline.raw) }; unsafe { list.IASetPrimitiveTopology(pipeline.topology) }; for (index, (vb, &stride)) in self .pass .vertex_buffers .iter_mut() .zip(pipeline.vertex_strides.iter()) .enumerate() { if let Some(stride) = stride { if vb.StrideInBytes != stride { vb.StrideInBytes = stride; self.pass.dirty_vertex_buffers |= 1 << index; } } } } unsafe fn set_index_buffer<'a>( &mut self, binding: crate::BufferBinding<'a, super::Buffer>, format: wgt::IndexFormat, ) { let ibv = Direct3D12::D3D12_INDEX_BUFFER_VIEW { BufferLocation: binding.resolve_address(), SizeInBytes: binding.resolve_size().try_into().unwrap(), Format: auxil::dxgi::conv::map_index_format(format), }; unsafe { self.list.as_ref().unwrap().IASetIndexBuffer(Some(&ibv)) } } unsafe fn set_vertex_buffer<'a>( &mut self, index: u32, binding: crate::BufferBinding<'a, super::Buffer>, ) { let vb = &mut self.pass.vertex_buffers[index as usize]; vb.BufferLocation = binding.resolve_address(); vb.SizeInBytes = binding.resolve_size().try_into().unwrap(); self.pass.dirty_vertex_buffers |= 1 << index; } unsafe fn set_viewport(&mut self, rect: &crate::Rect, depth_range: Range) { let raw_vp = Direct3D12::D3D12_VIEWPORT { TopLeftX: rect.x, TopLeftY: rect.y, Width: rect.w, Height: rect.h, MinDepth: depth_range.start, MaxDepth: depth_range.end, }; unsafe { self.list .as_ref() .unwrap() .RSSetViewports(core::slice::from_ref(&raw_vp)) } } unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect) { let raw_rect = Foundation::RECT { left: rect.x as i32, top: rect.y as i32, right: (rect.x + rect.w) as i32, bottom: (rect.y + rect.h) as i32, }; unsafe { self.list .as_ref() .unwrap() .RSSetScissorRects(core::slice::from_ref(&raw_rect)) } } unsafe fn set_stencil_reference(&mut self, value: u32) { unsafe { self.list.as_ref().unwrap().OMSetStencilRef(value) } } unsafe fn set_blend_constants(&mut self, color: &[f32; 4]) { unsafe { self.list.as_ref().unwrap().OMSetBlendFactor(Some(color)) } } unsafe fn draw( &mut self, first_vertex: u32, vertex_count: u32, first_instance: u32, instance_count: u32, ) { unsafe { self.prepare_draw(first_vertex as i32, first_instance) }; unsafe { self.list.as_ref().unwrap().DrawInstanced( vertex_count, instance_count, first_vertex, first_instance, ) } } unsafe fn draw_indexed( &mut self, first_index: u32, index_count: u32, base_vertex: i32, first_instance: u32, instance_count: u32, ) { unsafe { self.prepare_draw(base_vertex, first_instance) }; unsafe { self.list.as_ref().unwrap().DrawIndexedInstanced( index_count, instance_count, first_index, base_vertex, first_instance, ) } } unsafe fn draw_mesh_tasks( &mut self, group_count_x: u32, group_count_y: u32, group_count_z: u32, ) { self.prepare_dispatch([group_count_x, group_count_y, group_count_z]); let cmd_list6: Direct3D12::ID3D12GraphicsCommandList6 = self.list.as_ref().unwrap().cast().unwrap(); unsafe { cmd_list6.DispatchMesh(group_count_x, group_count_y, group_count_z); } } unsafe fn draw_indirect( &mut self, buffer: &super::Buffer, offset: wgt::BufferAddress, draw_count: u32, ) { if self .pass .layout .special_constants .as_ref() .and_then(|sc| sc.indirect_cmd_signatures.as_ref()) .is_some() { unsafe { self.prepare_vertex_buffers() }; self.update_root_elements(); } else { unsafe { self.prepare_draw(0, 0) }; } let cmd_signature = &self .pass .layout .special_constants .as_ref() .and_then(|sc| sc.indirect_cmd_signatures.as_ref()) .unwrap_or_else(|| &self.shared.cmd_signatures) .draw; unsafe { self.list.as_ref().unwrap().ExecuteIndirect( cmd_signature, draw_count, &buffer.resource, offset, None, 0, ) } } unsafe fn draw_indexed_indirect( &mut self, buffer: &super::Buffer, offset: wgt::BufferAddress, draw_count: u32, ) { if self .pass .layout .special_constants .as_ref() .and_then(|sc| sc.indirect_cmd_signatures.as_ref()) .is_some() { unsafe { self.prepare_vertex_buffers() }; self.update_root_elements(); } else { unsafe { self.prepare_draw(0, 0) }; } let cmd_signature = &self .pass .layout .special_constants .as_ref() .and_then(|sc| sc.indirect_cmd_signatures.as_ref()) .unwrap_or_else(|| &self.shared.cmd_signatures) .draw_indexed; unsafe { self.list.as_ref().unwrap().ExecuteIndirect( cmd_signature, draw_count, &buffer.resource, offset, None, 0, ) } } unsafe fn draw_mesh_tasks_indirect( &mut self, buffer: &::Buffer, offset: wgt::BufferAddress, draw_count: u32, ) { if self .pass .layout .special_constants .as_ref() .and_then(|sc| sc.indirect_cmd_signatures.as_ref()) .is_some() { self.update_root_elements(); } else { self.prepare_dispatch([0; 3]); } let cmd_list6: Direct3D12::ID3D12GraphicsCommandList6 = self.list.as_ref().unwrap().cast().unwrap(); let Some(cmd_signature) = &self .pass .layout .special_constants .as_ref() .and_then(|sc| sc.indirect_cmd_signatures.as_ref()) .unwrap_or_else(|| &self.shared.cmd_signatures) .draw_mesh else { panic!("Feature `MESH_SHADING` not enabled"); }; unsafe { cmd_list6.ExecuteIndirect(cmd_signature, draw_count, &buffer.resource, offset, None, 0); } } unsafe fn draw_indirect_count( &mut self, buffer: &super::Buffer, offset: wgt::BufferAddress, count_buffer: &super::Buffer, count_offset: wgt::BufferAddress, max_count: u32, ) { unsafe { self.prepare_draw(0, 0) }; unsafe { self.list.as_ref().unwrap().ExecuteIndirect( &self.shared.cmd_signatures.draw, max_count, &buffer.resource, offset, &count_buffer.resource, count_offset, ) } } unsafe fn draw_indexed_indirect_count( &mut self, buffer: &super::Buffer, offset: wgt::BufferAddress, count_buffer: &super::Buffer, count_offset: wgt::BufferAddress, max_count: u32, ) { unsafe { self.prepare_draw(0, 0) }; unsafe { self.list.as_ref().unwrap().ExecuteIndirect( &self.shared.cmd_signatures.draw_indexed, max_count, &buffer.resource, offset, &count_buffer.resource, count_offset, ) } } unsafe fn draw_mesh_tasks_indirect_count( &mut self, buffer: &::Buffer, offset: wgt::BufferAddress, count_buffer: &::Buffer, count_offset: wgt::BufferAddress, max_count: u32, ) { self.prepare_dispatch([0; 3]); let cmd_list6: Direct3D12::ID3D12GraphicsCommandList6 = self.list.as_ref().unwrap().cast().unwrap(); let Some(ref command_signature) = self.shared.cmd_signatures.draw_mesh else { panic!("Feature `MESH_SHADING` not enabled"); }; unsafe { cmd_list6.ExecuteIndirect( command_signature, max_count, &buffer.resource, offset, &count_buffer.resource, count_offset, ); } } // compute unsafe fn begin_compute_pass<'a>( &mut self, desc: &crate::ComputePassDescriptor<'a, super::QuerySet>, ) { unsafe { self.begin_pass(super::PassKind::Compute, desc.label) }; if let Some(timestamp_writes) = desc.timestamp_writes.as_ref() { if let Some(index) = timestamp_writes.beginning_of_pass_write_index { unsafe { self.write_timestamp(timestamp_writes.query_set, index); } } self.end_of_pass_timer_query = timestamp_writes .end_of_pass_write_index .map(|index| (timestamp_writes.query_set.raw.clone(), index)); } } unsafe fn end_compute_pass(&mut self) { self.write_pass_end_timestamp_if_requested(); unsafe { self.end_pass() }; } unsafe fn set_compute_pipeline(&mut self, pipeline: &super::ComputePipeline) { let list = self.list.clone().unwrap(); if self.pass.layout.signature != pipeline.layout.signature { // D3D12 requires full reset on signature change unsafe { list.SetComputeRootSignature(pipeline.layout.signature.as_ref()) }; self.reset_signature(&pipeline.layout); }; unsafe { list.SetPipelineState(&pipeline.raw) } } unsafe fn dispatch(&mut self, count @ [x, y, z]: [u32; 3]) { self.prepare_dispatch(count); unsafe { self.list.as_ref().unwrap().Dispatch(x, y, z) } } unsafe fn dispatch_indirect(&mut self, buffer: &super::Buffer, offset: wgt::BufferAddress) { if self .pass .layout .special_constants .as_ref() .and_then(|sc| sc.indirect_cmd_signatures.as_ref()) .is_some() { self.update_root_elements(); } else { self.prepare_dispatch([0; 3]); } let cmd_signature = &self .pass .layout .special_constants .as_ref() .and_then(|sc| sc.indirect_cmd_signatures.as_ref()) .unwrap_or_else(|| &self.shared.cmd_signatures) .dispatch; unsafe { self.list.as_ref().unwrap().ExecuteIndirect( cmd_signature, 1, &buffer.resource, offset, None, 0, ) } } unsafe fn build_acceleration_structures<'a, T>( &mut self, _descriptor_count: u32, descriptors: T, ) where super::Api: 'a, T: IntoIterator< Item = crate::BuildAccelerationStructureDescriptor< 'a, super::Buffer, super::AccelerationStructure, >, >, { // Implement using `BuildRaytracingAccelerationStructure`: // https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#buildraytracingaccelerationstructure let list = self .list .as_ref() .unwrap() .cast::() .unwrap(); for descriptor in descriptors { // TODO: This is the same as getting build sizes apart from requiring buffers, should this be de-duped? let mut geometry_desc; let ty; let inputs0; let num_desc; match descriptor.entries { AccelerationStructureEntries::Instances(instances) => { let desc_address = unsafe { instances .buffer .expect("needs buffer to build") .resource .GetGPUVirtualAddress() } + instances.offset as u64; ty = Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL; inputs0 = Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS_0 { InstanceDescs: desc_address, }; num_desc = instances.count; } AccelerationStructureEntries::Triangles(triangles) => { geometry_desc = Vec::with_capacity(triangles.len()); for triangle in triangles { let transform_address = triangle.transform.as_ref().map_or(0, |transform| unsafe { transform.buffer.resource.GetGPUVirtualAddress() + transform.offset as u64 }); let index_format = triangle .indices .as_ref() .map_or(Dxgi::Common::DXGI_FORMAT_UNKNOWN, |indices| { auxil::dxgi::conv::map_index_format(indices.format) }); let vertex_format = auxil::dxgi::conv::map_vertex_format(triangle.vertex_format); let index_count = triangle.indices.as_ref().map_or(0, |indices| indices.count); let index_address = triangle.indices.as_ref().map_or(0, |indices| unsafe { indices .buffer .expect("needs buffer to build") .resource .GetGPUVirtualAddress() + indices.offset as u64 }); let vertex_address = unsafe { triangle .vertex_buffer .expect("needs buffer to build") .resource .GetGPUVirtualAddress() + (triangle.first_vertex as u64 * triangle.vertex_stride) }; let triangle_desc = Direct3D12::D3D12_RAYTRACING_GEOMETRY_TRIANGLES_DESC { Transform3x4: transform_address, IndexFormat: index_format, VertexFormat: vertex_format, IndexCount: index_count, VertexCount: triangle.vertex_count, IndexBuffer: index_address, VertexBuffer: Direct3D12::D3D12_GPU_VIRTUAL_ADDRESS_AND_STRIDE { StartAddress: vertex_address, StrideInBytes: triangle.vertex_stride, }, }; geometry_desc.push(Direct3D12::D3D12_RAYTRACING_GEOMETRY_DESC { Type: Direct3D12::D3D12_RAYTRACING_GEOMETRY_TYPE_TRIANGLES, Flags: conv::map_acceleration_structure_geometry_flags(triangle.flags), Anonymous: Direct3D12::D3D12_RAYTRACING_GEOMETRY_DESC_0 { Triangles: triangle_desc, }, }) } ty = Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; inputs0 = Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS_0 { pGeometryDescs: geometry_desc.as_ptr(), }; num_desc = geometry_desc.len() as u32; } AccelerationStructureEntries::AABBs(aabbs) => { geometry_desc = Vec::with_capacity(aabbs.len()); for aabb in aabbs { let aabb_address = unsafe { aabb.buffer .expect("needs buffer to build") .resource .GetGPUVirtualAddress() + (aabb.offset as u64 * aabb.stride) }; let aabb_desc = Direct3D12::D3D12_RAYTRACING_GEOMETRY_AABBS_DESC { AABBCount: aabb.count as u64, AABBs: Direct3D12::D3D12_GPU_VIRTUAL_ADDRESS_AND_STRIDE { StartAddress: aabb_address, StrideInBytes: aabb.stride, }, }; geometry_desc.push(Direct3D12::D3D12_RAYTRACING_GEOMETRY_DESC { Type: Direct3D12::D3D12_RAYTRACING_GEOMETRY_TYPE_PROCEDURAL_PRIMITIVE_AABBS, Flags: conv::map_acceleration_structure_geometry_flags(aabb.flags), Anonymous: Direct3D12::D3D12_RAYTRACING_GEOMETRY_DESC_0 { AABBs: aabb_desc, }, }) } ty = Direct3D12::D3D12_RAYTRACING_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL; inputs0 = Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS_0 { pGeometryDescs: geometry_desc.as_ptr(), }; num_desc = geometry_desc.len() as u32; } }; let acceleration_structure_inputs = Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_INPUTS { Type: ty, Flags: conv::map_acceleration_structure_build_flags( descriptor.flags, Some(descriptor.mode), ), NumDescs: num_desc, DescsLayout: Direct3D12::D3D12_ELEMENTS_LAYOUT_ARRAY, Anonymous: inputs0, }; let dst_acceleration_structure_address = unsafe { descriptor .destination_acceleration_structure .resource .GetGPUVirtualAddress() }; let src_acceleration_structure_address = descriptor .source_acceleration_structure .as_ref() .map_or(0, |source| unsafe { source.resource.GetGPUVirtualAddress() }); let scratch_address = unsafe { descriptor.scratch_buffer.resource.GetGPUVirtualAddress() + descriptor.scratch_buffer_offset }; let desc = Direct3D12::D3D12_BUILD_RAYTRACING_ACCELERATION_STRUCTURE_DESC { DestAccelerationStructureData: dst_acceleration_structure_address, Inputs: acceleration_structure_inputs, SourceAccelerationStructureData: src_acceleration_structure_address, ScratchAccelerationStructureData: scratch_address, }; unsafe { list.BuildRaytracingAccelerationStructure(&desc, None) }; } } unsafe fn place_acceleration_structure_barrier( &mut self, _barriers: crate::AccelerationStructureBarrier, ) { // TODO: This is not very optimal, we should be using [enhanced barriers](https://microsoft.github.io/DirectX-Specs/d3d/D3D12EnhancedBarriers.html) if possible let list = self .list .as_ref() .unwrap() .cast::() .unwrap(); unsafe { list.ResourceBarrier(&[Direct3D12::D3D12_RESOURCE_BARRIER { Type: Direct3D12::D3D12_RESOURCE_BARRIER_TYPE_UAV, Flags: Direct3D12::D3D12_RESOURCE_BARRIER_FLAG_NONE, Anonymous: Direct3D12::D3D12_RESOURCE_BARRIER_0 { UAV: mem::ManuallyDrop::new(Direct3D12::D3D12_RESOURCE_UAV_BARRIER { pResource: Default::default(), }), }, }]) } } unsafe fn copy_acceleration_structure_to_acceleration_structure( &mut self, src: &super::AccelerationStructure, dst: &super::AccelerationStructure, copy: wgt::AccelerationStructureCopy, ) { let list = self .list .as_ref() .unwrap() .cast::() .unwrap(); unsafe { list.CopyRaytracingAccelerationStructure( dst.resource.GetGPUVirtualAddress(), src.resource.GetGPUVirtualAddress(), conv::map_acceleration_structure_copy_mode(copy), ) } } }