// Copyright © 2019 piet-gpu developers. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use crate::dx12::error::{self, error_if_failed_else_unit, explain_error, Error}; use crate::MapMode; use smallvec::SmallVec; use std::convert::{TryFrom, TryInto}; use std::sync::atomic::{AtomicPtr, Ordering}; use std::{ffi, mem, ptr}; use winapi::shared::{dxgi, dxgi1_2, dxgi1_3, dxgi1_4, dxgiformat, dxgitype, minwindef, windef}; use winapi::um::d3dcommon::ID3DBlob; use winapi::um::{ d3d12, d3d12sdklayers, d3dcommon, d3dcompiler, dxgidebug, handleapi, synchapi, winnt, }; use winapi::Interface; use wio::com::ComPtr; // everything is ripped from d3d12-rs, but wio::com::ComPtr, and winapi are used more directly #[derive(Clone)] pub struct Heap(pub ComPtr); pub struct Resource { // Note: the use of AtomicPtr is to support explicit destruction, // similar to Vulkan. ptr: AtomicPtr, } #[derive(Clone)] pub struct Adapter1(pub ComPtr); #[derive(Clone)] pub struct Factory2(pub ComPtr); #[derive(Clone)] pub struct Factory4(pub ComPtr); #[derive(Clone)] pub struct SwapChain3(pub ComPtr); #[derive(Clone)] pub struct Device(pub ComPtr); #[derive(Clone)] pub struct CommandQueue(pub ComPtr); #[derive(Clone)] pub struct CommandAllocator(pub ComPtr); pub type CpuDescriptor = d3d12::D3D12_CPU_DESCRIPTOR_HANDLE; pub type GpuDescriptor = d3d12::D3D12_GPU_DESCRIPTOR_HANDLE; #[derive(Clone)] pub struct DescriptorHeap(ComPtr); #[derive(Clone)] pub struct RootSignature(pub ComPtr); #[derive(Clone)] pub struct CommandSignature(pub ComPtr); #[derive(Clone)] pub struct GraphicsCommandList(pub ComPtr); pub struct Event(pub winnt::HANDLE); #[derive(Clone)] pub struct Fence(pub ComPtr); #[derive(Clone)] pub struct PipelineState(pub ComPtr); #[derive(Clone)] pub struct CachedPSO(d3d12::D3D12_CACHED_PIPELINE_STATE); #[derive(Clone)] pub struct Blob(pub ComPtr); #[derive(Clone)] pub struct ShaderByteCode { pub bytecode: d3d12::D3D12_SHADER_BYTECODE, } #[derive(Clone)] pub struct QueryHeap(pub ComPtr); impl Resource { pub unsafe fn new(ptr: *mut d3d12::ID3D12Resource) -> Resource { Resource { ptr: AtomicPtr::new(ptr), } } pub fn get(&self) -> *const d3d12::ID3D12Resource { self.get_mut() } pub fn get_mut(&self) -> *mut d3d12::ID3D12Resource { self.ptr.load(Ordering::Relaxed) } // Safety: call only single-threaded. pub unsafe fn destroy(&self) { (*self.get()).Release(); self.ptr.store(ptr::null_mut(), Ordering::Relaxed); } pub unsafe fn map_buffer( &self, offset: u64, size: u64, mode: MapMode, ) -> Result<*mut u8, Error> { let mut mapped_memory: *mut u8 = ptr::null_mut(); let (begin, end) = match mode { MapMode::Read => (offset as usize, (offset + size) as usize), MapMode::Write => (0, 0), }; let range = d3d12::D3D12_RANGE { Begin: begin, End: end, }; explain_error( (*self.get()).Map(0, &range, &mut mapped_memory as *mut _ as *mut _), "could not map GPU mem to CPU mem", )?; Ok(mapped_memory.add(offset as usize)) } pub unsafe fn unmap_buffer(&self, offset: u64, size: u64, mode: MapMode) -> Result<(), Error> { let (begin, end) = match mode { MapMode::Read => (0, 0), MapMode::Write => (offset as usize, (offset + size) as usize), }; let range = d3d12::D3D12_RANGE { Begin: begin, End: end, }; (*self.get()).Unmap(0, &range); Ok(()) } } impl Drop for Resource { fn drop(&mut self) { unsafe { let ptr = self.get(); if !ptr.is_null() { (*ptr).Release(); } } } } impl Clone for Resource { fn clone(&self) -> Self { unsafe { let ptr = self.get_mut(); (*ptr).AddRef(); Resource { ptr: AtomicPtr::new(ptr), } } } } impl Factory4 { pub unsafe fn create(flags: minwindef::UINT) -> Result { let mut factory = ptr::null_mut(); explain_error( dxgi1_3::CreateDXGIFactory2( flags, &dxgi1_4::IDXGIFactory4::uuidof(), &mut factory as *mut _ as *mut _, ), "error creating DXGI factory", )?; Ok(Factory4(ComPtr::from_raw(factory))) } pub unsafe fn enumerate_adapters(&self, id: u32) -> Result { let mut adapter = ptr::null_mut(); error_if_failed_else_unit(self.0.EnumAdapters1(id, &mut adapter))?; let mut desc = mem::zeroed(); (*adapter).GetDesc(&mut desc); //println!("desc: {:?}", desc.Description); Ok(Adapter1(ComPtr::from_raw(adapter))) } pub unsafe fn create_swapchain_for_hwnd( &self, command_queue: &CommandQueue, hwnd: windef::HWND, desc: dxgi1_2::DXGI_SWAP_CHAIN_DESC1, ) -> Result { let mut swap_chain = ptr::null_mut(); explain_error( self.0.CreateSwapChainForHwnd( command_queue.0.as_raw() as *mut _, hwnd, &desc, ptr::null(), ptr::null_mut(), &mut swap_chain as *mut _ as *mut _, ), "could not creation swapchain for hwnd", )?; Ok(SwapChain3(ComPtr::from_raw(swap_chain))) } } impl CommandQueue { pub unsafe fn signal(&self, fence: &Fence, value: u64) -> Result<(), Error> { explain_error( self.0.Signal(fence.0.as_raw(), value), "error setting signal", ) } pub unsafe fn execute_command_lists(&self, command_lists: &[*mut d3d12::ID3D12CommandList]) { let num_command_lists = command_lists.len().try_into().unwrap(); self.0 .ExecuteCommandLists(num_command_lists, command_lists.as_ptr()); } pub unsafe fn get_timestamp_frequency(&self) -> Result { let mut result: u64 = 0; explain_error( self.0.GetTimestampFrequency(&mut result), "could not get timestamp frequency", )?; Ok(result) } } impl SwapChain3 { pub unsafe fn get_buffer(&self, id: u32) -> Resource { let mut resource = ptr::null_mut(); error::error_if_failed_else_unit(self.0.GetBuffer( id, &d3d12::ID3D12Resource::uuidof(), &mut resource as *mut _ as *mut _, )) .expect("SwapChain3 could not get buffer"); Resource::new(resource) } pub unsafe fn get_current_back_buffer_index(&self) -> u32 { self.0.GetCurrentBackBufferIndex() } pub unsafe fn present(&self, interval: u32, flags: u32) -> Result<(), Error> { error::error_if_failed_else_unit(self.0.Present1( interval, flags, &dxgi1_2::DXGI_PRESENT_PARAMETERS { ..mem::zeroed() } as *const _, )) } } impl Blob { #[allow(unused)] pub unsafe fn print_to_console(blob: &Blob) { println!("==SHADER COMPILE MESSAGES=="); let message = { let pointer = blob.0.GetBufferPointer(); let size = blob.0.GetBufferSize(); let slice = std::slice::from_raw_parts(pointer as *const u8, size as usize); String::from_utf8_lossy(slice).into_owned() }; println!("{}", message); println!("==========================="); } } impl Device { pub unsafe fn create_device(factory4: &Factory4) -> Result { let mut id = 0; loop { // This always returns DXGI_ERROR_NOT_FOUND if no suitable adapter is found. // Might be slightly more useful to retain the error from the attempt to create. let adapter = factory4.enumerate_adapters(id)?; if let Ok(device) = Self::create_using_adapter(&adapter, d3dcommon::D3D_FEATURE_LEVEL_12_0) { return Ok(device); } id += 1; } } pub unsafe fn create_using_adapter( adapter: &Adapter1, feature_level: d3dcommon::D3D_FEATURE_LEVEL, ) -> Result { let mut device = ptr::null_mut(); error_if_failed_else_unit(d3d12::D3D12CreateDevice( adapter.0.as_raw() as *mut _, feature_level, &d3d12::ID3D12Device::uuidof(), &mut device as *mut _ as *mut _, ))?; Ok(Device(ComPtr::from_raw(device))) } pub unsafe fn create_command_allocator( &self, list_type: d3d12::D3D12_COMMAND_LIST_TYPE, ) -> Result { let mut allocator = ptr::null_mut(); explain_error( self.0.CreateCommandAllocator( list_type, &d3d12::ID3D12CommandAllocator::uuidof(), &mut allocator as *mut _ as *mut _, ), "device could not create command allocator", )?; Ok(CommandAllocator(ComPtr::from_raw(allocator))) } pub unsafe fn create_command_queue( &self, list_type: d3d12::D3D12_COMMAND_LIST_TYPE, priority: minwindef::INT, flags: d3d12::D3D12_COMMAND_QUEUE_FLAGS, node_mask: minwindef::UINT, ) -> Result { let desc = d3d12::D3D12_COMMAND_QUEUE_DESC { Type: list_type, Priority: priority, Flags: flags, NodeMask: node_mask, }; let mut cmd_q = ptr::null_mut(); explain_error( self.0.CreateCommandQueue( &desc, &d3d12::ID3D12CommandQueue::uuidof(), &mut cmd_q as *mut _ as *mut _, ), "device could not create command queue", )?; Ok(CommandQueue(ComPtr::from_raw(cmd_q))) } pub unsafe fn create_descriptor_heap( &self, heap_description: &d3d12::D3D12_DESCRIPTOR_HEAP_DESC, ) -> Result { let mut heap = ptr::null_mut(); explain_error( self.0.CreateDescriptorHeap( heap_description, &d3d12::ID3D12DescriptorHeap::uuidof(), &mut heap as *mut _ as *mut _, ), "device could not create descriptor heap", )?; Ok(DescriptorHeap(ComPtr::from_raw(heap))) } pub unsafe fn get_descriptor_increment_size( &self, heap_type: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE, ) -> u32 { self.0.GetDescriptorHandleIncrementSize(heap_type) } pub unsafe fn copy_descriptors( &self, dst_starts: &[d3d12::D3D12_CPU_DESCRIPTOR_HANDLE], dst_sizes: &[u32], src_starts: &[d3d12::D3D12_CPU_DESCRIPTOR_HANDLE], src_sizes: &[u32], descriptor_heap_type: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE, ) { debug_assert_eq!(dst_starts.len(), dst_sizes.len()); debug_assert_eq!(src_starts.len(), src_sizes.len()); debug_assert_eq!( src_sizes.iter().copied().sum::(), dst_sizes.iter().copied().sum() ); self.0.CopyDescriptors( dst_starts.len().try_into().unwrap(), dst_starts.as_ptr(), dst_sizes.as_ptr(), src_starts.len().try_into().unwrap(), src_starts.as_ptr(), src_sizes.as_ptr(), descriptor_heap_type, ); } pub unsafe fn copy_one_descriptor( &self, dst: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, src: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, descriptor_heap_type: d3d12::D3D12_DESCRIPTOR_HEAP_TYPE, ) { self.0 .CopyDescriptorsSimple(1, dst, src, descriptor_heap_type); } pub unsafe fn create_compute_pipeline_state( &self, compute_pipeline_desc: &d3d12::D3D12_COMPUTE_PIPELINE_STATE_DESC, ) -> Result { let mut pipeline_state = ptr::null_mut(); explain_error( self.0.CreateComputePipelineState( compute_pipeline_desc as *const _, &d3d12::ID3D12PipelineState::uuidof(), &mut pipeline_state as *mut _ as *mut _, ), "device could not create compute pipeline state", )?; Ok(PipelineState(ComPtr::from_raw(pipeline_state))) } pub unsafe fn create_root_signature( &self, node_mask: minwindef::UINT, blob: Blob, ) -> Result { let mut signature = ptr::null_mut(); explain_error( self.0.CreateRootSignature( node_mask, blob.0.GetBufferPointer(), blob.0.GetBufferSize(), &d3d12::ID3D12RootSignature::uuidof(), &mut signature as *mut _ as *mut _, ), "device could not create root signature", )?; Ok(RootSignature(ComPtr::from_raw(signature))) } pub unsafe fn create_graphics_command_list( &self, list_type: d3d12::D3D12_COMMAND_LIST_TYPE, allocator: &CommandAllocator, initial_ps: Option<&PipelineState>, node_mask: minwindef::UINT, ) -> Result { let mut command_list = ptr::null_mut(); let p_initial_state = initial_ps.map(|p| p.0.as_raw()).unwrap_or(ptr::null_mut()); explain_error( self.0.CreateCommandList( node_mask, list_type, allocator.0.as_raw(), p_initial_state, &d3d12::ID3D12GraphicsCommandList::uuidof(), &mut command_list as *mut _ as *mut _, ), "device could not create graphics command list", )?; Ok(GraphicsCommandList(ComPtr::from_raw(command_list))) } pub unsafe fn create_byte_addressed_buffer_unordered_access_view( &self, resource: &Resource, descriptor: CpuDescriptor, first_element: u64, num_elements: u32, ) { // shouldn't flags be dxgiformat::DXGI_FORMAT_R32_TYPELESS? let mut uav_desc = d3d12::D3D12_UNORDERED_ACCESS_VIEW_DESC { Format: dxgiformat::DXGI_FORMAT_R32_TYPELESS, ViewDimension: d3d12::D3D12_UAV_DIMENSION_BUFFER, ..mem::zeroed() }; *uav_desc.u.Buffer_mut() = d3d12::D3D12_BUFFER_UAV { FirstElement: first_element, NumElements: num_elements, // shouldn't StructureByteStride be 0? StructureByteStride: 0, CounterOffsetInBytes: 0, // shouldn't flags be d3d12::D3D12_BUFFER_UAV_FLAG_RAW? Flags: d3d12::D3D12_BUFFER_UAV_FLAG_RAW, }; self.0 .CreateUnorderedAccessView(resource.get_mut(), ptr::null_mut(), &uav_desc, descriptor) } pub unsafe fn create_unordered_access_view( &self, resource: &Resource, descriptor: CpuDescriptor, ) { self.0.CreateUnorderedAccessView( resource.get_mut(), ptr::null_mut(), ptr::null(), descriptor, ) } pub unsafe fn create_fence(&self, initial: u64) -> Result { let mut fence = ptr::null_mut(); explain_error( self.0.CreateFence( initial, d3d12::D3D12_FENCE_FLAG_NONE, &d3d12::ID3D12Fence::uuidof(), &mut fence as *mut _ as *mut _, ), "device could not create fence", )?; Ok(Fence(ComPtr::from_raw(fence))) } pub unsafe fn create_committed_resource( &self, heap_properties: &d3d12::D3D12_HEAP_PROPERTIES, flags: d3d12::D3D12_HEAP_FLAGS, resource_description: &d3d12::D3D12_RESOURCE_DESC, initial_resource_state: d3d12::D3D12_RESOURCE_STATES, optimized_clear_value: *const d3d12::D3D12_CLEAR_VALUE, ) -> Result { let mut resource = ptr::null_mut(); explain_error( self.0.CreateCommittedResource( heap_properties as *const _, flags, resource_description as *const _, initial_resource_state, optimized_clear_value, &d3d12::ID3D12Resource::uuidof(), &mut resource as *mut _ as *mut _, ), "device could not create committed resource", )?; Ok(Resource::new(resource)) } pub unsafe fn create_query_heap( &self, heap_type: d3d12::D3D12_QUERY_HEAP_TYPE, num_expected_queries: u32, ) -> Result { let query_heap_desc = d3d12::D3D12_QUERY_HEAP_DESC { Type: heap_type, Count: num_expected_queries, NodeMask: 0, }; let mut query_heap = ptr::null_mut(); explain_error( self.0.CreateQueryHeap( &query_heap_desc as *const _, &d3d12::ID3D12QueryHeap::uuidof(), &mut query_heap as *mut _ as *mut _, ), "could not create query heap", )?; Ok(QueryHeap(ComPtr::from_raw(query_heap))) } pub unsafe fn create_buffer( &self, buffer_size_in_bytes: u64, heap_type: d3d12::D3D12_HEAP_TYPE, cpu_page: d3d12::D3D12_CPU_PAGE_PROPERTY, memory_pool_preference: d3d12::D3D12_MEMORY_POOL, init_resource_state: d3d12::D3D12_RESOURCE_STATES, resource_flags: d3d12::D3D12_RESOURCE_FLAGS, ) -> Result { let heap_properties = d3d12::D3D12_HEAP_PROPERTIES { Type: heap_type, CPUPageProperty: cpu_page, MemoryPoolPreference: memory_pool_preference, //we don't care about multi-adapter operation, so these next two will be zero CreationNodeMask: 0, VisibleNodeMask: 0, }; let resource_description = d3d12::D3D12_RESOURCE_DESC { Dimension: d3d12::D3D12_RESOURCE_DIMENSION_BUFFER, Width: buffer_size_in_bytes, Height: 1, DepthOrArraySize: 1, MipLevels: 1, SampleDesc: dxgitype::DXGI_SAMPLE_DESC { Count: 1, Quality: 0, }, Layout: d3d12::D3D12_TEXTURE_LAYOUT_ROW_MAJOR, Flags: resource_flags, ..mem::zeroed() }; let buffer = self.create_committed_resource( &heap_properties, d3d12::D3D12_HEAP_FLAG_NONE, &resource_description, init_resource_state, ptr::null(), )?; Ok(buffer) } pub unsafe fn create_texture2d_buffer( &self, width: u64, height: u32, format: dxgiformat::DXGI_FORMAT, allow_unordered_access: bool, ) -> Result { // Images are always created device-local. let heap_properties = d3d12::D3D12_HEAP_PROPERTIES { Type: d3d12::D3D12_HEAP_TYPE_DEFAULT, CPUPageProperty: d3d12::D3D12_CPU_PAGE_PROPERTY_UNKNOWN, MemoryPoolPreference: d3d12::D3D12_MEMORY_POOL_UNKNOWN, //we don't care about multi-adapter operation, so these next two will be zero CreationNodeMask: 0, VisibleNodeMask: 0, }; let (flags, initial_resource_state) = { if allow_unordered_access { ( d3d12::D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS, d3d12::D3D12_RESOURCE_STATE_UNORDERED_ACCESS, ) } else { ( d3d12::D3D12_RESOURCE_FLAG_NONE, d3d12::D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, ) } }; let resource_description = d3d12::D3D12_RESOURCE_DESC { Dimension: d3d12::D3D12_RESOURCE_DIMENSION_TEXTURE2D, Width: width, Height: height, DepthOrArraySize: 1, MipLevels: 1, SampleDesc: dxgitype::DXGI_SAMPLE_DESC { Count: 1, Quality: 0, }, Layout: d3d12::D3D12_TEXTURE_LAYOUT_UNKNOWN, Flags: flags, Format: format, ..mem::zeroed() }; let buffer = self.create_committed_resource( &heap_properties, //TODO: is this heap flag ok? d3d12::D3D12_HEAP_FLAG_NONE, &resource_description, initial_resource_state, ptr::null(), )?; Ok(buffer) } pub unsafe fn get_features_architecture( &self, ) -> Result { let mut features_architecture = mem::zeroed(); explain_error( self.0.CheckFeatureSupport( d3d12::D3D12_FEATURE_ARCHITECTURE, &mut features_architecture as *mut _ as *mut _, mem::size_of::() as u32, ), "error querying feature architecture", )?; Ok(features_architecture) } } impl DescriptorHeap { pub unsafe fn get_cpu_descriptor_handle_for_heap_start(&self) -> CpuDescriptor { self.0.GetCPUDescriptorHandleForHeapStart() } pub unsafe fn get_gpu_descriptor_handle_for_heap_start(&self) -> GpuDescriptor { self.0.GetGPUDescriptorHandleForHeapStart() } } impl RootSignature { pub unsafe fn serialize_description( desc: &d3d12::D3D12_ROOT_SIGNATURE_DESC, version: d3d12::D3D_ROOT_SIGNATURE_VERSION, ) -> Result { let mut blob = ptr::null_mut(); let mut error_blob_ptr = ptr::null_mut(); let hresult = d3d12::D3D12SerializeRootSignature(desc, version, &mut blob, &mut error_blob_ptr); #[cfg(debug_assertions)] { let error_blob = if error_blob_ptr.is_null() { None } else { Some(Blob(ComPtr::from_raw(error_blob_ptr))) }; if let Some(error_blob) = &error_blob { Blob::print_to_console(error_blob); } } explain_error(hresult, "could not serialize root signature description")?; Ok(Blob(ComPtr::from_raw(blob))) } } impl ShaderByteCode { // `blob` may not be null. // TODO: this is not super elegant, maybe want to move the get // operations closer to where they're used. #[allow(unused)] pub unsafe fn from_blob(blob: Blob) -> ShaderByteCode { ShaderByteCode { bytecode: d3d12::D3D12_SHADER_BYTECODE { BytecodeLength: blob.0.GetBufferSize(), pShaderBytecode: blob.0.GetBufferPointer(), }, } } /// Compile a shader from raw HLSL. /// /// * `target`: example format: `ps_5_1`. #[allow(unused)] pub unsafe fn compile( source: &str, target: &str, entry: &str, flags: minwindef::DWORD, ) -> Result { let mut shader_blob_ptr: *mut ID3DBlob = ptr::null_mut(); //TODO: use error blob properly let mut error_blob_ptr: *mut ID3DBlob = ptr::null_mut(); let target = ffi::CString::new(target) .expect("could not convert target format string into ffi::CString"); let entry = ffi::CString::new(entry) .expect("could not convert entry name String into ffi::CString"); let hresult = d3dcompiler::D3DCompile( source.as_ptr() as *const _, source.len(), ptr::null(), ptr::null(), d3dcompiler::D3D_COMPILE_STANDARD_FILE_INCLUDE, entry.as_ptr(), target.as_ptr(), flags, 0, &mut shader_blob_ptr, &mut error_blob_ptr, ); let error_blob = if error_blob_ptr.is_null() { None } else { Some(Blob(ComPtr::from_raw(error_blob_ptr))) }; #[cfg(debug_assertions)] { if let Some(error_blob) = &error_blob { Blob::print_to_console(error_blob); } } // TODO: we can put the shader compilation error into the returned error. explain_error(hresult, "shader compilation failed")?; Ok(Blob(ComPtr::from_raw(shader_blob_ptr))) } /// Create bytecode from a slice. /// /// # Safety /// /// This call elides the lifetime from the slice. The caller is responsible /// for making sure the reference remains valid for the lifetime of this /// object. #[allow(unused)] pub unsafe fn from_slice(bytecode: &[u8]) -> ShaderByteCode { ShaderByteCode { bytecode: d3d12::D3D12_SHADER_BYTECODE { BytecodeLength: bytecode.len(), pShaderBytecode: bytecode.as_ptr() as *const _, }, } } } impl Fence { pub unsafe fn set_event_on_completion(&self, event: &Event, value: u64) -> Result<(), Error> { explain_error( self.0.SetEventOnCompletion(value, event.0), "error setting event completion", ) } pub unsafe fn get_value(&self) -> u64 { self.0.GetCompletedValue() } } impl Event { pub unsafe fn create(manual_reset: bool, initial_state: bool) -> Result { let handle = synchapi::CreateEventA( ptr::null_mut(), manual_reset as _, initial_state as _, ptr::null(), ); if handle.is_null() { // TODO: should probably call GetLastError here Err(Error::Hresult(-1)) } else { Ok(Event(handle)) } } /// Wait for the event, or a timeout. /// /// If the timeout is `winapi::um::winbase::INFINITE`, it will wait until the /// event is signaled. /// /// The return value is defined here: /// https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject pub unsafe fn wait(&self, timeout_ms: u32) -> u32 { synchapi::WaitForSingleObject(self.0, timeout_ms) } } impl Drop for Event { fn drop(&mut self) { unsafe { handleapi::CloseHandle(self.0); } } } impl CommandAllocator { pub unsafe fn reset(&self) -> Result<(), Error> { error::error_if_failed_else_unit(self.0.Reset()) } } impl GraphicsCommandList { pub unsafe fn as_raw_command_list(&self) -> *mut d3d12::ID3D12CommandList { self.0.as_raw() as *mut d3d12::ID3D12CommandList } pub unsafe fn close(&self) -> Result<(), Error> { explain_error(self.0.Close(), "error closing command list") } pub unsafe fn reset( &self, allocator: &CommandAllocator, initial_pso: Option<&PipelineState>, ) -> Result<(), Error> { let p_initial_state = initial_pso.map(|p| p.0.as_raw()).unwrap_or(ptr::null_mut()); error::error_if_failed_else_unit(self.0.Reset(allocator.0.as_raw(), p_initial_state)) } pub unsafe fn set_compute_pipeline_root_signature(&self, signature: &RootSignature) { self.0.SetComputeRootSignature(signature.0.as_raw()); } pub unsafe fn resource_barrier(&self, resource_barriers: &[d3d12::D3D12_RESOURCE_BARRIER]) { self.0.ResourceBarrier( resource_barriers .len() .try_into() .expect("Waaaaaay too many barriers"), resource_barriers.as_ptr(), ); } pub unsafe fn dispatch(&self, count_x: u32, count_y: u32, count_z: u32) { self.0.Dispatch(count_x, count_y, count_z); } pub unsafe fn set_pipeline_state(&self, pipeline_state: &PipelineState) { self.0.SetPipelineState(pipeline_state.0.as_raw()); } pub unsafe fn set_compute_root_descriptor_table( &self, root_parameter_index: u32, base_descriptor: d3d12::D3D12_GPU_DESCRIPTOR_HANDLE, ) { self.0 .SetComputeRootDescriptorTable(root_parameter_index, base_descriptor); } pub unsafe fn set_descriptor_heaps(&self, descriptor_heaps: &[&DescriptorHeap]) { let mut descriptor_heap_pointers: SmallVec<[_; 4]> = descriptor_heaps.iter().map(|dh| dh.0.as_raw()).collect(); self.0.SetDescriptorHeaps( u32::try_from(descriptor_heap_pointers.len()) .expect("could not safely convert descriptor_heap_pointers.len() into u32"), descriptor_heap_pointers.as_mut_ptr(), ); } pub unsafe fn end_timing_query(&self, query_heap: &QueryHeap, index: u32) { self.0.EndQuery( query_heap.0.as_raw(), d3d12::D3D12_QUERY_TYPE_TIMESTAMP, index, ); } pub unsafe fn resolve_timing_query_data( &self, query_heap: &QueryHeap, start_index: u32, num_queries: u32, destination_buffer: &Resource, aligned_destination_buffer_offset: u64, ) { self.0.ResolveQueryData( query_heap.0.as_raw() as *mut _, d3d12::D3D12_QUERY_TYPE_TIMESTAMP, start_index, num_queries, destination_buffer.get_mut(), aligned_destination_buffer_offset, ); } pub unsafe fn clear_uav( &self, gpu_handle: d3d12::D3D12_GPU_DESCRIPTOR_HANDLE, cpu_handle: d3d12::D3D12_CPU_DESCRIPTOR_HANDLE, resource: &Resource, value: u32, size: Option, ) { // In testing, only the first value seems to be used, but just in case... let values = [value, value, value, value]; let mut rect = d3d12::D3D12_RECT { left: 0, right: 0, top: 0, bottom: 1, }; let (num_rects, p_rects) = if let Some(size) = size { rect.right = (size / 4).try_into().unwrap(); (1, &rect as *const _) } else { (0, std::ptr::null()) }; self.0.ClearUnorderedAccessViewUint( gpu_handle, cpu_handle, resource.get_mut(), &values, num_rects, p_rects, ); } /// Copy an entire resource (buffer or image) pub unsafe fn copy_resource(&self, src: &Resource, dst: &Resource) { self.0.CopyResource(dst.get_mut(), src.get_mut()); } pub unsafe fn copy_buffer( &self, dst_buf: &Resource, dst_offset: u64, src_buf: &Resource, src_offset: u64, size: u64, ) { self.0.CopyBufferRegion( dst_buf.get_mut(), dst_offset, src_buf.get_mut(), src_offset, size, ); } pub unsafe fn copy_buffer_to_texture( &self, buffer: &Resource, texture: &Resource, width: u32, height: u32, ) { let mut src = d3d12::D3D12_TEXTURE_COPY_LOCATION { pResource: buffer.get_mut(), Type: d3d12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, ..mem::zeroed() }; let row_pitch = width * 4; assert!( row_pitch % d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT == 0, "TODO: handle unaligned row pitch" ); let footprint = d3d12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT { Offset: 0, Footprint: d3d12::D3D12_SUBRESOURCE_FOOTPRINT { Format: dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM, Width: width, Height: height, Depth: 1, RowPitch: row_pitch, }, }; *src.u.PlacedFootprint_mut() = footprint; let mut dst = d3d12::D3D12_TEXTURE_COPY_LOCATION { pResource: texture.get_mut(), Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, ..mem::zeroed() }; *dst.u.SubresourceIndex_mut() = 0; self.0.CopyTextureRegion(&dst, 0, 0, 0, &src, ptr::null()); } pub unsafe fn copy_texture_to_buffer( &self, texture: &Resource, buffer: &Resource, width: u32, height: u32, ) { let mut src = d3d12::D3D12_TEXTURE_COPY_LOCATION { pResource: texture.get_mut(), Type: d3d12::D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, ..mem::zeroed() }; *src.u.SubresourceIndex_mut() = 0; let mut dst = d3d12::D3D12_TEXTURE_COPY_LOCATION { pResource: buffer.get_mut(), Type: d3d12::D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, ..mem::zeroed() }; let row_pitch = width * 4; assert!( row_pitch % d3d12::D3D12_TEXTURE_DATA_PITCH_ALIGNMENT == 0, "TODO: handle unaligned row pitch" ); let footprint = d3d12::D3D12_PLACED_SUBRESOURCE_FOOTPRINT { Offset: 0, Footprint: d3d12::D3D12_SUBRESOURCE_FOOTPRINT { Format: dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM, Width: width, Height: height, Depth: 1, RowPitch: row_pitch, }, }; *dst.u.PlacedFootprint_mut() = footprint; self.0.CopyTextureRegion(&dst, 0, 0, 0, &src, ptr::null()); } } pub unsafe fn create_uav_resource_barrier( resource: *mut d3d12::ID3D12Resource, ) -> d3d12::D3D12_RESOURCE_BARRIER { let uav = d3d12::D3D12_RESOURCE_UAV_BARRIER { pResource: resource, }; let mut resource_barrier: d3d12::D3D12_RESOURCE_BARRIER = mem::zeroed(); resource_barrier.Type = d3d12::D3D12_RESOURCE_BARRIER_TYPE_UAV; resource_barrier.Flags = d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE; *resource_barrier.u.UAV_mut() = uav; resource_barrier } pub unsafe fn create_transition_resource_barrier( resource: *mut d3d12::ID3D12Resource, state_before: d3d12::D3D12_RESOURCE_STATES, state_after: d3d12::D3D12_RESOURCE_STATES, ) -> d3d12::D3D12_RESOURCE_BARRIER { let transition = d3d12::D3D12_RESOURCE_TRANSITION_BARRIER { pResource: resource, Subresource: d3d12::D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, StateBefore: state_before, StateAfter: state_after, }; let mut resource_barrier: d3d12::D3D12_RESOURCE_BARRIER = mem::zeroed(); resource_barrier.Type = d3d12::D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; resource_barrier.Flags = d3d12::D3D12_RESOURCE_BARRIER_FLAG_NONE; *resource_barrier.u.Transition_mut() = transition; resource_barrier } #[allow(unused)] pub unsafe fn enable_debug_layer() -> Result<(), Error> { let mut debug_controller: *mut d3d12sdklayers::ID3D12Debug1 = ptr::null_mut(); explain_error( d3d12::D3D12GetDebugInterface( &d3d12sdklayers::ID3D12Debug1::uuidof(), &mut debug_controller as *mut _ as *mut _, ), "could not create debug controller", )?; let debug_controller = ComPtr::from_raw(debug_controller); debug_controller.EnableDebugLayer(); let mut queue = ptr::null_mut(); let hr = dxgi1_3::DXGIGetDebugInterface1( 0, &dxgidebug::IDXGIInfoQueue::uuidof(), &mut queue as *mut _ as *mut _, ); explain_error(hr, "failed to enable debug layer")?; debug_controller.SetEnableGPUBasedValidation(minwindef::TRUE); Ok(()) }