test(d3d12): Add D3D12 render test

This commit is contained in:
chyyran 2024-08-28 01:30:10 -04:00 committed by Ronny Chan
parent d33c2a84b2
commit 5c726efe21
5 changed files with 676 additions and 6 deletions

View file

@ -28,7 +28,7 @@ opengl = ["librashader/runtime-gl", "dep:glow", "dep:glfw"]
wgpu = ["librashader/runtime-wgpu", "dep:wgpu", "dep:wgpu-types"] wgpu = ["librashader/runtime-wgpu", "dep:wgpu", "dep:wgpu-types"]
d3d11 = ["librashader/runtime-d3d11", "dep:windows"] 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"] d3d9 = ["librashader/runtime-d3d9", "dep:windows"]
metal = ["librashader/runtime-metal", "dep:objc2", "dep:objc2-metal"] metal = ["librashader/runtime-metal", "dep:objc2", "dep:objc2-metal"]

View file

@ -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,
}
}
}

View file

@ -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<CpuStagingHeap>,
rtv_heap: D3D12DescriptorHeap<RenderTargetHeap>,
texture: D3D12InputImage,
heap_slot: D3D12DescriptorHeapSlot<CpuStagingHeap>,
command_pool: ID3D12CommandAllocator,
queue: ID3D12CommandQueue,
image: Image<BGRA8>,
}
impl RenderTest for Direct3D12 {
fn new(path: impl AsRef<Path>) -> anyhow::Result<Self>
where
Self: Sized,
{
Direct3D12::new(path)
}
fn render(&mut self, path: impl AsRef<Path>, frame_count: usize) -> anyhow::Result<RgbaImage> {
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<Path>) -> anyhow::Result<Self> {
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<ID3D12Device> {
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<ID3D12Device> = 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<CpuStagingHeap>,
path: impl AsRef<Path>,
) -> anyhow::Result<(
Image<BGRA8>,
D3D12InputImage,
D3D12DescriptorHeapSlot<CpuStagingHeap>,
)> {
// 1 time queue infrastructure for lut uploads
let image: Image<BGRA8> = 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<ID3D12Resource> = 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<ID3D12Resource> = 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<IDXGIAdapter1> {
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::<Option<ID3D12Device>>(),
)
}
.is_ok()
{
return Ok(adapter);
}
}
// Fallback to warp
unsafe { factory.EnumWarpAdapter() }
}
}

View file

@ -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<u64> {
// let allocation_size = std::mem::size_of::<D3D12_PLACED_SUBRESOURCE_FOOTPRINT>()
// + std::mem::size_of::<u32>()
// + std::mem::size_of::<u64>() * num_subresources;
unsafe {
let destination_desc = destination_resource.GetDesc();
let mut device: Option<ID3D12Device> = 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<u64> {
// 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,
);
}
}
}
}

View file

@ -37,7 +37,7 @@ pub trait RenderTest {
/// shader presets, so the actual image that a shader will be applied to /// shader presets, so the actual image that a shader will be applied to
/// will often be part of the test harness object. /// will often be part of the test harness object.
fn render( fn render(
&self, &mut self,
path: impl AsRef<Path>, path: impl AsRef<Path>,
frame_count: usize, frame_count: usize,
) -> anyhow::Result<image::RgbaImage>; ) -> anyhow::Result<image::RgbaImage>;
@ -51,13 +51,13 @@ mod test {
use std::fs::File; use std::fs::File;
const IMAGE_PATH: &str = "../triangle.png"; 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 = // const FILTER_PATH: &str =
// "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp"; // "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp";
fn do_test<T: RenderTest>() -> anyhow::Result<()> { fn do_test<T: RenderTest>() -> anyhow::Result<()> {
let test = T::new(IMAGE_PATH)?; let mut test = T::new(IMAGE_PATH)?;
let image = test.render(FILTER_PATH, 100)?; let image = test.render(FILTER_PATH, 100)?;
let out = File::create("out.png")?; let out = File::create("out.png")?;
@ -107,9 +107,15 @@ mod test {
do_test::<crate::render::d3d9::Direct3D9>() do_test::<crate::render::d3d9::Direct3D9>()
} }
#[test]
#[cfg(feature = "d3d12")]
pub fn test_d3d12() -> anyhow::Result<()> {
do_test::<crate::render::d3d12::Direct3D12>()
}
pub fn compare<A: RenderTest, B: RenderTest>() -> anyhow::Result<()> { pub fn compare<A: RenderTest, B: RenderTest>() -> anyhow::Result<()> {
let a = A::new(IMAGE_PATH)?; let mut a = A::new(IMAGE_PATH)?;
let b = B::new(IMAGE_PATH)?; let mut b = B::new(IMAGE_PATH)?;
let a_image = a.render(FILTER_PATH, 100)?; let a_image = a.render(FILTER_PATH, 100)?;
let b_image = b.render(FILTER_PATH, 100)?; let b_image = b.render(FILTER_PATH, 100)?;