test(d3d12): Add D3D12 render test
This commit is contained in:
parent
d33c2a84b2
commit
5c726efe21
|
@ -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"]
|
||||
|
|
35
librashader-test/src/render/d3d12/descriptor_heap.rs
Normal file
35
librashader-test/src/render/d3d12/descriptor_heap.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
416
librashader-test/src/render/d3d12/mod.rs
Normal file
416
librashader-test/src/render/d3d12/mod.rs
Normal 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() }
|
||||
}
|
||||
}
|
213
librashader-test/src/render/d3d12/util.rs
Normal file
213
librashader-test/src/render/d3d12/util.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<Path>,
|
||||
frame_count: usize,
|
||||
) -> anyhow::Result<image::RgbaImage>;
|
||||
|
@ -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<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 out = File::create("out.png")?;
|
||||
|
@ -107,9 +107,15 @@ mod test {
|
|||
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<()> {
|
||||
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)?;
|
||||
|
|
Loading…
Reference in a new issue