From 5c726efe2166b2627a0ea6077119e0b2e98080e1 Mon Sep 17 00:00:00 2001 From: chyyran Date: Wed, 28 Aug 2024 01:30:10 -0400 Subject: [PATCH] test(d3d12): Add D3D12 render test --- librashader-test/Cargo.toml | 2 +- .../src/render/d3d12/descriptor_heap.rs | 35 ++ librashader-test/src/render/d3d12/mod.rs | 416 ++++++++++++++++++ librashader-test/src/render/d3d12/util.rs | 213 +++++++++ librashader-test/src/render/mod.rs | 16 +- 5 files changed, 676 insertions(+), 6 deletions(-) create mode 100644 librashader-test/src/render/d3d12/descriptor_heap.rs create mode 100644 librashader-test/src/render/d3d12/mod.rs create mode 100644 librashader-test/src/render/d3d12/util.rs diff --git a/librashader-test/Cargo.toml b/librashader-test/Cargo.toml index 5458afc..870af0d 100644 --- a/librashader-test/Cargo.toml +++ b/librashader-test/Cargo.toml @@ -28,7 +28,7 @@ opengl = ["librashader/runtime-gl", "dep:glow", "dep:glfw"] wgpu = ["librashader/runtime-wgpu", "dep:wgpu", "dep:wgpu-types"] d3d11 = ["librashader/runtime-d3d11", "dep:windows"] -d3d12 = ["librashader/runtime-d3d12", "dep:windows"] +d3d12 = ["librashader/runtime-d3d12", "dep:windows", "dep:d3d12-descriptor-heap"] d3d9 = ["librashader/runtime-d3d9", "dep:windows"] metal = ["librashader/runtime-metal", "dep:objc2", "dep:objc2-metal"] diff --git a/librashader-test/src/render/d3d12/descriptor_heap.rs b/librashader-test/src/render/d3d12/descriptor_heap.rs new file mode 100644 index 0000000..7b6d19a --- /dev/null +++ b/librashader-test/src/render/d3d12/descriptor_heap.rs @@ -0,0 +1,35 @@ +use d3d12_descriptor_heap::D3D12DescriptorHeapType; +use windows::Win32::Graphics::Direct3D12::{ + D3D12_DESCRIPTOR_HEAP_DESC, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, +}; + +#[derive(Clone)] +pub struct CpuStagingHeap; + +#[derive(Clone)] +pub struct RenderTargetHeap; + +impl D3D12DescriptorHeapType for RenderTargetHeap { + // Lut texture heaps are CPU only and get bound to the descriptor heap of the shader. + fn create_desc(size: usize) -> D3D12_DESCRIPTOR_HEAP_DESC { + D3D12_DESCRIPTOR_HEAP_DESC { + Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV, + NumDescriptors: size as u32, + Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE, + NodeMask: 0, + } + } +} + +impl D3D12DescriptorHeapType for CpuStagingHeap { + // Lut texture heaps are CPU only and get bound to the descriptor heap of the shader. + fn create_desc(size: usize) -> D3D12_DESCRIPTOR_HEAP_DESC { + D3D12_DESCRIPTOR_HEAP_DESC { + Type: D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, + NumDescriptors: size as u32, + Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE, + NodeMask: 0, + } + } +} diff --git a/librashader-test/src/render/d3d12/mod.rs b/librashader-test/src/render/d3d12/mod.rs new file mode 100644 index 0000000..a9001f7 --- /dev/null +++ b/librashader-test/src/render/d3d12/mod.rs @@ -0,0 +1,416 @@ +mod descriptor_heap; +mod util; + +use crate::render::d3d12::descriptor_heap::{CpuStagingHeap, RenderTargetHeap}; +use crate::render::RenderTest; +use anyhow::anyhow; +use d3d12_descriptor_heap::{D3D12DescriptorHeap, D3D12DescriptorHeapSlot}; +use image::RgbaImage; +use librashader::runtime::d3d12::{ + D3D12InputImage, D3D12OutputView, FilterChain, FilterChainOptions, +}; +use librashader::runtime::Viewport; +use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8}; +use std::ffi::CStr; +use std::path::Path; +use windows::core::Interface; +use windows::Win32::Foundation::CloseHandle; +use windows::Win32::Graphics::Direct3D::{D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_12_1}; +use windows::Win32::Graphics::Direct3D12::{ + D3D12CreateDevice, ID3D12CommandAllocator, ID3D12CommandQueue, ID3D12Device, ID3D12Fence, + ID3D12GraphicsCommandList, ID3D12InfoQueue1, ID3D12Resource, D3D12_COMMAND_LIST_TYPE_DIRECT, + D3D12_COMMAND_QUEUE_DESC, D3D12_COMMAND_QUEUE_FLAG_NONE, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, + D3D12_FENCE_FLAG_NONE, D3D12_HEAP_FLAG_NONE, D3D12_HEAP_PROPERTIES, D3D12_HEAP_TYPE_CUSTOM, + D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_TYPE_UPLOAD, D3D12_MEMORY_POOL_L0, + D3D12_MEMORY_POOL_UNKNOWN, D3D12_MESSAGE_CALLBACK_FLAG_NONE, D3D12_MESSAGE_CATEGORY, + D3D12_MESSAGE_ID, D3D12_MESSAGE_SEVERITY, D3D12_PLACED_SUBRESOURCE_FOOTPRINT, + D3D12_RENDER_TARGET_VIEW_DESC, D3D12_RENDER_TARGET_VIEW_DESC_0, D3D12_RESOURCE_DESC, + D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RESOURCE_DIMENSION_TEXTURE2D, + D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE, + D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_GENERIC_READ, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RTV_DIMENSION_TEXTURE2D, D3D12_SHADER_RESOURCE_VIEW_DESC, + D3D12_SHADER_RESOURCE_VIEW_DESC_0, D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_SUBRESOURCE_DATA, + D3D12_TEX2D_RTV, D3D12_TEX2D_SRV, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, +}; +use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC}; +use windows::Win32::Graphics::Dxgi::{ + CreateDXGIFactory2, IDXGIAdapter1, IDXGIFactory4, DXGI_ADAPTER_FLAG_NONE, + DXGI_ADAPTER_FLAG_SOFTWARE, DXGI_CREATE_FACTORY_DEBUG, DXGI_CREATE_FACTORY_FLAGS, +}; +use windows::Win32::System::Threading::{CreateEventA, WaitForSingleObject, INFINITE}; + +pub struct Direct3D12 { + device: ID3D12Device, + cpu_heap: D3D12DescriptorHeap, + rtv_heap: D3D12DescriptorHeap, + + texture: D3D12InputImage, + heap_slot: D3D12DescriptorHeapSlot, + command_pool: ID3D12CommandAllocator, + queue: ID3D12CommandQueue, + image: Image, +} + +impl RenderTest for Direct3D12 { + fn new(path: impl AsRef) -> anyhow::Result + where + Self: Sized, + { + Direct3D12::new(path) + } + + fn render(&mut self, path: impl AsRef, frame_count: usize) -> anyhow::Result { + unsafe { + let descriptor = self.rtv_heap.allocate_descriptor()?; + + let cmd: ID3D12GraphicsCommandList = self.device.CreateCommandList( + 0, + D3D12_COMMAND_LIST_TYPE_DIRECT, + &self.command_pool, + None, + )?; + + let fence_event = CreateEventA(None, false, false, None)?; + let fence: ID3D12Fence = self.device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?; + + let mut filter_chain = FilterChain::load_from_path( + path, + &self.device, + Some(&FilterChainOptions { + force_hlsl_pipeline: false, + force_no_mipmaps: false, + disable_cache: false, + }), + )?; + + let mut output_texture = None; + let desc = D3D12_RESOURCE_DESC { + Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D, + Alignment: 0, + Width: self.image.size.width as u64, + Height: self.image.size.height, + DepthOrArraySize: 1, + MipLevels: 1, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Layout: Default::default(), + Flags: D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET, + }; + + // We only need to draw one frame, so don't worry about the performance impact + // of this heap. + self.device.CreateCommittedResource( + &D3D12_HEAP_PROPERTIES { + Type: D3D12_HEAP_TYPE_CUSTOM, + CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE, + MemoryPoolPreference: D3D12_MEMORY_POOL_L0, + CreationNodeMask: 1, + VisibleNodeMask: 1, + }, + D3D12_HEAP_FLAG_NONE, + &desc, + D3D12_RESOURCE_STATE_COMMON | D3D12_RESOURCE_STATE_RENDER_TARGET, + None, + &mut output_texture, + )?; + + let output_texture: ID3D12Resource = + output_texture.ok_or_else(|| anyhow!("Failed to allocate resource"))?; + + self.device + .CreateRenderTargetView(&output_texture, None, *descriptor.as_ref()); + + filter_chain.frame( + &cmd, + self.texture.clone(), + &Viewport::new_render_target_sized_origin( + D3D12OutputView::new_from_raw( + *descriptor.as_ref(), + self.image.size, + DXGI_FORMAT_B8G8R8A8_UNORM, + ), + None, + )?, + frame_count, + None, + )?; + + cmd.Close()?; + self.queue.ExecuteCommandLists(&[Some(cmd.cast()?)]); + self.queue.Signal(&fence, 1)?; + + if fence.GetCompletedValue() < 1 { + fence.SetEventOnCompletion(1, fence_event)?; + WaitForSingleObject(fence_event, INFINITE); + CloseHandle(fence_event)?; + }; + + let mut buffer = vec![0u8; self.image.bytes.len()]; + + output_texture.ReadFromSubresource( + buffer.as_mut_ptr().cast(), + 4 * self.image.size.width, + 0, + 0, + None, + )?; + + BGRA8::convert(&mut buffer); + + let image = RgbaImage::from_raw( + self.image.size.width, + self.image.size.height, + Vec::from(buffer), + ) + .ok_or(anyhow!("Unable to create image from data"))?; + + Ok(image) + } + } +} + +impl Direct3D12 { + pub fn new(image_path: impl AsRef) -> anyhow::Result { + let device = Self::create_device()?; + let mut heap = unsafe { D3D12DescriptorHeap::new(&device, 8)? }; + let mut rtv_heap = unsafe { D3D12DescriptorHeap::new(&device, 16)? }; + + unsafe { + let command_pool: ID3D12CommandAllocator = + device.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT)?; + + let queue: ID3D12CommandQueue = + device.CreateCommandQueue(&D3D12_COMMAND_QUEUE_DESC { + Type: D3D12_COMMAND_LIST_TYPE_DIRECT, + Priority: 0, + Flags: D3D12_COMMAND_QUEUE_FLAG_NONE, + NodeMask: 0, + })?; + let (image, texture, heap_slot) = + Self::load_image(&device, &command_pool, &queue, &mut heap, image_path)?; + + Ok(Self { + device, + cpu_heap: heap, + rtv_heap, + texture, + heap_slot, + command_pool, + image, + queue, + }) + } + } + + fn create_device() -> anyhow::Result { + let dxgi_factory_flags = DXGI_CREATE_FACTORY_DEBUG; + let dxgi_factory: IDXGIFactory4 = unsafe { CreateDXGIFactory2(dxgi_factory_flags) }?; + + let adapter = Self::get_hardware_adapter(&dxgi_factory)?; + + let mut device: Option = None; + unsafe { D3D12CreateDevice(&adapter, D3D_FEATURE_LEVEL_12_1, &mut device) }?; + let device = device.ok_or(anyhow!("Failed to initialize D3D12 device"))?; + + Ok(device) + } + + fn load_image( + device: &ID3D12Device, + command_pool: &ID3D12CommandAllocator, + queue: &ID3D12CommandQueue, + heap: &mut D3D12DescriptorHeap, + path: impl AsRef, + ) -> anyhow::Result<( + Image, + D3D12InputImage, + D3D12DescriptorHeapSlot, + )> { + // 1 time queue infrastructure for lut uploads + let image: Image = Image::load(path, UVDirection::TopLeft)?; + + let desc = D3D12_RESOURCE_DESC { + Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D, + Alignment: 0, + Width: image.size.width as u64, + Height: image.size.height, + DepthOrArraySize: 1, + MipLevels: 1, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Layout: Default::default(), + Flags: Default::default(), + }; + + let descriptor = heap.allocate_descriptor()?; + + // create handles on GPU + let mut resource: Option = None; + unsafe { + let cmd: ID3D12GraphicsCommandList = device.CreateCommandList( + 0, + D3D12_COMMAND_LIST_TYPE_DIRECT, + &command_pool.clone(), + None, + )?; + let fence_event = CreateEventA(None, false, false, None)?; + let fence: ID3D12Fence = device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?; + + device.CreateCommittedResource( + &D3D12_HEAP_PROPERTIES { + Type: D3D12_HEAP_TYPE_DEFAULT, + CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN, + CreationNodeMask: 1, + VisibleNodeMask: 1, + }, + D3D12_HEAP_FLAG_NONE, + &desc, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + None, + &mut resource, + )?; + + let resource = resource.ok_or_else(|| anyhow!("Failed to allocate resource"))?; + let srv_desc = D3D12_SHADER_RESOURCE_VIEW_DESC { + Format: desc.Format, + ViewDimension: D3D12_SRV_DIMENSION_TEXTURE2D, + Shader4ComponentMapping: D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, + Anonymous: D3D12_SHADER_RESOURCE_VIEW_DESC_0 { + Texture2D: D3D12_TEX2D_SRV { + MipLevels: u32::MAX, + ..Default::default() + }, + }, + }; + + device.CreateShaderResourceView(&resource, Some(&srv_desc), *descriptor.as_ref()); + + let mut buffer_desc = D3D12_RESOURCE_DESC { + Dimension: D3D12_RESOURCE_DIMENSION_BUFFER, + ..Default::default() + }; + + let mut layout = D3D12_PLACED_SUBRESOURCE_FOOTPRINT::default(); + let mut total = 0; + // texture upload + device.GetCopyableFootprints( + &desc, + 0, + 1, + 0, + Some(&mut layout), + None, + None, + Some(&mut total), + ); + + buffer_desc.Width = total; + buffer_desc.Height = 1; + buffer_desc.DepthOrArraySize = 1; + buffer_desc.MipLevels = 1; + buffer_desc.SampleDesc.Count = 1; + buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + + let mut upload: Option = None; + + device.CreateCommittedResource( + &D3D12_HEAP_PROPERTIES { + Type: D3D12_HEAP_TYPE_UPLOAD, + CPUPageProperty: D3D12_CPU_PAGE_PROPERTY_UNKNOWN, + MemoryPoolPreference: D3D12_MEMORY_POOL_UNKNOWN, + CreationNodeMask: 1, + VisibleNodeMask: 1, + }, + D3D12_HEAP_FLAG_NONE, + &buffer_desc, + D3D12_RESOURCE_STATE_GENERIC_READ, + None, + &mut upload, + )?; + + let upload = upload.ok_or_else(|| anyhow!("Failed to allocate copy texture"))?; + + let subresource = [D3D12_SUBRESOURCE_DATA { + pData: image.bytes.as_ptr().cast(), + RowPitch: 4 * image.size.width as isize, + SlicePitch: (4 * image.size.width * image.size.height) as isize, + }]; + + util::d3d12_resource_transition( + &cmd, + &resource, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + D3D12_RESOURCE_STATE_COPY_DEST, + ); + + util::d3d12_update_subresources(&cmd, &resource, &upload, 0, 0, 1, &subresource)?; + + util::d3d12_resource_transition( + &cmd, + &resource, + D3D12_RESOURCE_STATE_COPY_DEST, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + ); + + cmd.Close()?; + queue.ExecuteCommandLists(&[Some(cmd.cast()?)]); + queue.Signal(&fence, 1)?; + + if fence.GetCompletedValue() < 1 { + fence.SetEventOnCompletion(1, fence_event)?; + WaitForSingleObject(fence_event, INFINITE); + CloseHandle(fence_event)?; + } + + Ok(( + image, + D3D12InputImage { + resource, + descriptor: descriptor.as_ref().clone(), + }, + descriptor, + )) + } + } + + fn get_hardware_adapter(factory: &IDXGIFactory4) -> windows::core::Result { + for i in 0.. { + let adapter = unsafe { factory.EnumAdapters1(i)? }; + + let mut desc = unsafe { adapter.GetDesc1()? }; + + if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != DXGI_ADAPTER_FLAG_NONE.0 as u32 + { + // Don't select the Basic Render Driver adapter. If you want a + // software adapter, pass in "/warp" on the command line. + continue; + } + + // Check to see whether the adapter supports Direct3D 12, but don't + // create the actual device yet. + if unsafe { + D3D12CreateDevice( + &adapter, + D3D_FEATURE_LEVEL_11_0, + std::ptr::null_mut::>(), + ) + } + .is_ok() + { + return Ok(adapter); + } + } + + // Fallback to warp + unsafe { factory.EnumWarpAdapter() } + } +} diff --git a/librashader-test/src/render/d3d12/util.rs b/librashader-test/src/render/d3d12/util.rs new file mode 100644 index 0000000..1e1dce4 --- /dev/null +++ b/librashader-test/src/render/d3d12/util.rs @@ -0,0 +1,213 @@ +use anyhow::anyhow; +use std::mem::ManuallyDrop; +use windows::Win32::Graphics::Direct3D12::{ + ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_FEATURE_FORMAT_SUPPORT, + D3D12_MEMCPY_DEST, D3D12_PLACED_SUBRESOURCE_FOOTPRINT, D3D12_RESOURCE_BARRIER, + D3D12_RESOURCE_BARRIER_0, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + D3D12_RESOURCE_BARRIER_FLAG_NONE, D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RESOURCE_STATES, D3D12_RESOURCE_TRANSITION_BARRIER, + D3D12_SUBRESOURCE_DATA, D3D12_TEXTURE_COPY_LOCATION, D3D12_TEXTURE_COPY_LOCATION_0, + D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, +}; + +#[inline(always)] +pub fn d3d12_resource_transition( + cmd: &ID3D12GraphicsCommandList, + resource: &ID3D12Resource, + before: D3D12_RESOURCE_STATES, + after: D3D12_RESOURCE_STATES, +) { + d3d12_resource_transition_subresource( + cmd, + resource, + before, + after, + D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + ); +} + +pub fn d3d12_get_resource_transition_subresource( + resource: &ID3D12Resource, + before: D3D12_RESOURCE_STATES, + after: D3D12_RESOURCE_STATES, + subresource: u32, +) -> D3D12_RESOURCE_BARRIER { + D3D12_RESOURCE_BARRIER { + Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE, + Anonymous: D3D12_RESOURCE_BARRIER_0 { + Transition: ManuallyDrop::new(D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: ManuallyDrop::new(Some(resource.clone())), + Subresource: subresource, + StateBefore: before, + StateAfter: after, + }), + }, + } +} + +#[inline(always)] +pub fn d3d12_resource_transition_subresource( + cmd: &ID3D12GraphicsCommandList, + resource: &ID3D12Resource, + before: D3D12_RESOURCE_STATES, + after: D3D12_RESOURCE_STATES, + subresource: u32, +) { + let barrier = [d3d12_get_resource_transition_subresource( + resource, + before, + after, + subresource, + )]; + unsafe { cmd.ResourceBarrier(&barrier) } +} + +pub(crate) fn d3d12_update_subresources( + cmd: &ID3D12GraphicsCommandList, + destination_resource: &ID3D12Resource, + intermediate_resource: &ID3D12Resource, + intermediate_offset: u64, + first_subresouce: u32, + num_subresources: u32, + source: &[D3D12_SUBRESOURCE_DATA], +) -> anyhow::Result { + // let allocation_size = std::mem::size_of::() + // + std::mem::size_of::() + // + std::mem::size_of::() * num_subresources; + + unsafe { + let destination_desc = destination_resource.GetDesc(); + let mut device: Option = None; + destination_resource.GetDevice(&mut device)?; + let device = device.ok_or_else(|| anyhow!("Unable to get device"))?; + + let mut layouts = + vec![D3D12_PLACED_SUBRESOURCE_FOOTPRINT::default(); num_subresources as usize]; + let mut num_rows = vec![0; num_subresources as usize]; + let mut row_sizes_in_bytes = vec![0; num_subresources as usize]; + let mut required_size = 0; + + // texture upload + device.GetCopyableFootprints( + &destination_desc, + first_subresouce, + num_subresources, + intermediate_offset, + Some(layouts.as_mut_ptr()), + Some(num_rows.as_mut_ptr()), + Some(row_sizes_in_bytes.as_mut_ptr()), + Some(&mut required_size), + ); + + update_subresources( + cmd, + destination_resource, + intermediate_resource, + first_subresouce, + num_subresources, + required_size, + &layouts, + &num_rows, + &row_sizes_in_bytes, + source, + ) + } +} + +#[allow(clippy::too_many_arguments)] +fn update_subresources( + cmd: &ID3D12GraphicsCommandList, + destination_resource: &ID3D12Resource, + intermediate_resource: &ID3D12Resource, + first_subresouce: u32, + num_subresources: u32, + required_size: u64, + layouts: &[D3D12_PLACED_SUBRESOURCE_FOOTPRINT], + num_rows: &[u32], + row_sizes_in_bytes: &[u64], + source_data: &[D3D12_SUBRESOURCE_DATA], +) -> anyhow::Result { + // ToDo: implement validation as in the original function + + unsafe { + let mut data = std::ptr::null_mut(); + intermediate_resource.Map(0, None, Some(&mut data))?; + + for i in 0..num_subresources as usize { + let dest_data = D3D12_MEMCPY_DEST { + pData: data.offset(layouts[i].Offset as isize) as *mut std::ffi::c_void, + RowPitch: layouts[i].Footprint.RowPitch as usize, + SlicePitch: ((layouts[i].Footprint.RowPitch) * num_rows[i]) as usize, + }; + + memcpy_subresource( + &dest_data, + &source_data[i], + row_sizes_in_bytes[i], + num_rows[i], + layouts[i].Footprint.Depth, + ); + } + intermediate_resource.Unmap(0, None); + } + + unsafe { + let destination_desc = destination_resource.GetDesc(); + if destination_desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER { + cmd.CopyBufferRegion( + destination_resource, + 0, + intermediate_resource, + layouts[0].Offset, + layouts[0].Footprint.Width as u64, + ); + } else { + for i in 0..num_subresources as usize { + let dest_location = D3D12_TEXTURE_COPY_LOCATION { + pResource: ManuallyDrop::new(Some(destination_resource.clone())), + Type: D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, + Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 { + SubresourceIndex: i as u32 + first_subresouce, + }, + }; + + let source_location = D3D12_TEXTURE_COPY_LOCATION { + pResource: ManuallyDrop::new(Some(intermediate_resource.clone())), + Type: D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT, + Anonymous: D3D12_TEXTURE_COPY_LOCATION_0 { + PlacedFootprint: layouts[i], + }, + }; + + cmd.CopyTextureRegion(&dest_location, 0, 0, 0, &source_location, None); + } + } + Ok(required_size) + } +} + +// this function should not leak to the public API, so +// there is no point in using struct wrappers +unsafe fn memcpy_subresource( + dest: &D3D12_MEMCPY_DEST, + src: &D3D12_SUBRESOURCE_DATA, + row_sizes_in_bytes: u64, + num_rows: u32, + num_slices: u32, +) { + unsafe { + for z in 0..num_slices as usize { + let dest_slice = dest.pData.add(dest.SlicePitch * z); + let src_slice = src.pData.offset(src.SlicePitch * z as isize); + + for y in 0..num_rows as usize { + std::ptr::copy_nonoverlapping( + src_slice.offset(src.RowPitch * y as isize), + dest_slice.add(dest.RowPitch * y), + row_sizes_in_bytes as usize, + ); + } + } + } +} diff --git a/librashader-test/src/render/mod.rs b/librashader-test/src/render/mod.rs index 4e22251..a6fc298 100644 --- a/librashader-test/src/render/mod.rs +++ b/librashader-test/src/render/mod.rs @@ -37,7 +37,7 @@ pub trait RenderTest { /// shader presets, so the actual image that a shader will be applied to /// will often be part of the test harness object. fn render( - &self, + &mut self, path: impl AsRef, frame_count: usize, ) -> anyhow::Result; @@ -51,13 +51,13 @@ mod test { use std::fs::File; const IMAGE_PATH: &str = "../triangle.png"; - const FILTER_PATH: &str = "../test/shaders_slang/crt/crt-geom.slangp"; + const FILTER_PATH: &str = "../test/shaders_slang/crt/crt-royale.slangp"; // const FILTER_PATH: &str = // "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp"; fn do_test() -> anyhow::Result<()> { - let test = T::new(IMAGE_PATH)?; + let mut test = T::new(IMAGE_PATH)?; let image = test.render(FILTER_PATH, 100)?; let out = File::create("out.png")?; @@ -107,9 +107,15 @@ mod test { do_test::() } + #[test] + #[cfg(feature = "d3d12")] + pub fn test_d3d12() -> anyhow::Result<()> { + do_test::() + } + pub fn compare() -> anyhow::Result<()> { - let a = A::new(IMAGE_PATH)?; - let b = B::new(IMAGE_PATH)?; + let mut a = A::new(IMAGE_PATH)?; + let mut b = B::new(IMAGE_PATH)?; let a_image = a.render(FILTER_PATH, 100)?; let b_image = b.render(FILTER_PATH, 100)?;