librashader/librashader-runtime-d3d12/src/util.rs
2023-02-01 02:08:50 -05:00

340 lines
12 KiB
Rust

use std::mem::ManuallyDrop;
use std::u64;
use crate::error;
use windows::core::PCSTR;
use windows::Win32::Graphics::Direct3D::Fxc::{D3DCompile, D3DCOMPILE_DEBUG, D3DCOMPILE_OPTIMIZATION_LEVEL3, D3DCOMPILE_SKIP_OPTIMIZATION};
use windows::Win32::Graphics::Direct3D::ID3DBlob;
use windows::Win32::Graphics::Direct3D12::{ID3D12Device, D3D12_FEATURE_DATA_FORMAT_SUPPORT, D3D12_FEATURE_FORMAT_SUPPORT, ID3D12CommandList, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_RESOURCE_DESC, D3D12_RESOURCE_BARRIER, D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, D3D12_RESOURCE_BARRIER_FLAG_NONE, D3D12_RESOURCE_DESC1, D3D12_RESOURCE_BARRIER_0, D3D12_RESOURCE_TRANSITION_BARRIER, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, D3D12_RESOURCE_STATES, D3D12_PLACED_SUBRESOURCE_FOOTPRINT, D3D12_MEMCPY_DEST, D3D12_SUBRESOURCE_DATA, D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_TEXTURE_COPY_LOCATION, D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX, D3D12_TEXTURE_COPY_LOCATION_0, D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT};
use windows::Win32::Graphics::Dxgi::Common::*;
use crate::error::assume_d3d12_init;
/// wtf retroarch?
const DXGI_FORMAT_EX_A4R4G4B4_UNORM: DXGI_FORMAT = DXGI_FORMAT(1000);
const fn d3d12_format_fallback_list(format: DXGI_FORMAT) -> Option<&'static [DXGI_FORMAT]> {
match format {
DXGI_FORMAT_R32G32B32A32_FLOAT => Some(&[
DXGI_FORMAT_R32G32B32A32_FLOAT,
DXGI_FORMAT_R16G16B16A16_FLOAT,
DXGI_FORMAT_R32G32B32_FLOAT,
DXGI_FORMAT_R11G11B10_FLOAT,
DXGI_FORMAT_UNKNOWN,
]),
DXGI_FORMAT_R16G16B16A16_FLOAT => Some(&[
DXGI_FORMAT_R16G16B16A16_FLOAT,
DXGI_FORMAT_R32G32B32A32_FLOAT,
DXGI_FORMAT_R32G32B32_FLOAT,
DXGI_FORMAT_R11G11B10_FLOAT,
DXGI_FORMAT_UNKNOWN,
]),
DXGI_FORMAT_R8G8B8A8_UNORM => Some(&[
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_B8G8R8X8_UNORM,
DXGI_FORMAT_UNKNOWN,
]),
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB => Some(&[
DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_B8G8R8X8_UNORM,
DXGI_FORMAT_UNKNOWN,
]),
DXGI_FORMAT_B8G8R8A8_UNORM => Some(&[
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN,
]),
DXGI_FORMAT_B8G8R8X8_UNORM => Some(&[
DXGI_FORMAT_B8G8R8X8_UNORM,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN,
]),
DXGI_FORMAT_B5G6R5_UNORM => Some(&[
DXGI_FORMAT_B5G6R5_UNORM,
DXGI_FORMAT_B8G8R8X8_UNORM,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN,
]),
DXGI_FORMAT_EX_A4R4G4B4_UNORM | DXGI_FORMAT_B4G4R4A4_UNORM => Some(&[
DXGI_FORMAT_B4G4R4A4_UNORM,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_UNKNOWN,
]),
DXGI_FORMAT_A8_UNORM => Some(&[
DXGI_FORMAT_A8_UNORM,
DXGI_FORMAT_R8_UNORM,
DXGI_FORMAT_R8G8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_UNKNOWN,
]),
DXGI_FORMAT_R8_UNORM => Some(&[
DXGI_FORMAT_R8_UNORM,
DXGI_FORMAT_A8_UNORM,
DXGI_FORMAT_R8G8_UNORM,
DXGI_FORMAT_R8G8B8A8_UNORM,
DXGI_FORMAT_B8G8R8A8_UNORM,
DXGI_FORMAT_UNKNOWN,
]),
_ => None,
}
}
pub fn d3d12_get_closest_format(
device: &ID3D12Device,
format: DXGI_FORMAT,
format_support: D3D12_FEATURE_DATA_FORMAT_SUPPORT,
) -> DXGI_FORMAT {
let default_list = [format, DXGI_FORMAT_UNKNOWN];
let format_support_list = d3d12_format_fallback_list(format).unwrap_or(&default_list);
for supported in format_support_list {
unsafe {
let mut support = D3D12_FEATURE_DATA_FORMAT_SUPPORT {
Format: format,
..Default::default()
};
if device
.CheckFeatureSupport(
D3D12_FEATURE_FORMAT_SUPPORT,
&mut support as *mut _ as *mut _,
std::mem::size_of::<D3D12_FEATURE_DATA_FORMAT_SUPPORT>() as u32,
)
.is_ok()
&& (support.Support1 & format_support.Support1) == format_support.Support1
&& (support.Support2 & format_support.Support2) == format_support.Support2
{
return *supported;
}
}
}
DXGI_FORMAT_UNKNOWN
}
pub fn d3d_compile_shader(source: &[u8], entry: &[u8], version: &[u8]) -> error::Result<ID3DBlob> {
// todo: compile with dxc
unsafe {
let mut blob = None;
D3DCompile(
source.as_ptr().cast(),
source.len(),
None,
None,
None,
PCSTR(entry.as_ptr()),
PCSTR(version.as_ptr()),
D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,
// if cfg!(feature = "debug-shader") {
// D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION
// } else {
// D3DCOMPILE_OPTIMIZATION_LEVEL3
// },
0,
&mut blob,
None,
)?;
Ok(blob.unwrap())
}
}
#[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);
}
#[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_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: windows::core::ManuallyDrop::new(resource),
Subresource: subresource,
StateBefore: before,
StateAfter: after,
})
},
}];
unsafe {
cmd.ResourceBarrier(&barrier)
}
}
pub 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]
) -> error::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)?;
assume_d3d12_init!(device, "GetDevice");
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
unsafe {
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],
) -> error::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: windows::core::ManuallyDrop::new(destination_resource),
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: windows::core::ManuallyDrop::new(intermediate_resource),
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,
) {
for z in 0..num_slices as usize {
let dest_slice =
dest.pData.offset((dest.SlicePitch * z) as isize);
let src_slice = src.pData.offset((src.SlicePitch * z as isize) as isize);
for y in 0..num_rows as usize {
std::ptr::copy_nonoverlapping(
src_slice.offset((src.RowPitch * y as isize) as isize),
dest_slice.offset((dest.RowPitch * y) as isize),
row_sizes_in_bytes as usize,
);
}
}
}