librashader/librashader-runtime-d3d12/src/mipmap.rs

328 lines
11 KiB
Rust
Raw Normal View History

use crate::descriptor_heap::ResourceWorkHeap;
use crate::resource::{ObtainResourceHandle, ResourceHandleStrategy};
use crate::util::dxc_validate_shader;
2023-02-06 08:17:23 +11:00
use crate::{error, util};
use bytemuck::{Pod, Zeroable};
use d3d12_descriptor_heap::{D3D12DescriptorHeap, D3D12DescriptorHeapSlot};
2023-02-06 08:17:23 +11:00
use librashader_common::Size;
2023-01-28 17:38:55 +11:00
use librashader_runtime::scaling::MipmapSize;
2023-02-06 08:17:23 +11:00
use std::mem::ManuallyDrop;
2024-02-20 04:39:44 +11:00
use windows::Win32::Graphics::Direct3D::Dxc::{
CLSID_DxcLibrary, CLSID_DxcValidator, DxcCreateInstance,
};
2023-02-06 08:17:23 +11:00
use windows::Win32::Graphics::Direct3D12::{
ID3D12DescriptorHeap, ID3D12Device, ID3D12GraphicsCommandList, ID3D12PipelineState,
ID3D12RootSignature, D3D12_COMPUTE_PIPELINE_STATE_DESC,
2023-02-06 08:17:23 +11:00
D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING, D3D12_RESOURCE_BARRIER, D3D12_RESOURCE_BARRIER_0,
D3D12_RESOURCE_BARRIER_TYPE_UAV, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_UAV_BARRIER, D3D12_SHADER_BYTECODE,
D3D12_SHADER_RESOURCE_VIEW_DESC, D3D12_SHADER_RESOURCE_VIEW_DESC_0,
D3D12_SRV_DIMENSION_TEXTURE2D, D3D12_TEX2D_SRV, D3D12_TEX2D_UAV, D3D12_UAV_DIMENSION_TEXTURE2D,
D3D12_UNORDERED_ACCESS_VIEW_DESC, D3D12_UNORDERED_ACCESS_VIEW_DESC_0,
};
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT;
2023-01-26 11:08:25 +11:00
const GENERATE_MIPMAPS_CS: &[u8] = include_bytes!("../shader/mipmap.dxil");
2023-01-27 09:57:54 +11:00
2023-01-26 11:08:25 +11:00
pub struct D3D12MipmapGen {
device: ID3D12Device,
root_signature: ID3D12RootSignature,
pipeline: ID3D12PipelineState,
2023-02-06 15:18:13 +11:00
own_heaps: bool,
2023-01-26 11:08:25 +11:00
}
2023-01-28 17:38:55 +11:00
#[derive(Copy, Clone, Zeroable, Pod)]
#[repr(C)]
struct MipConstants {
inv_out_texel_size: [f32; 2],
src_mip_index: u32,
}
pub struct MipmapGenContext<'a> {
gen: &'a D3D12MipmapGen,
cmd: &'a ID3D12GraphicsCommandList,
heap: &'a mut D3D12DescriptorHeap<ResourceWorkHeap>,
2023-02-06 08:17:23 +11:00
residuals: Vec<D3D12DescriptorHeapSlot<ResourceWorkHeap>>,
residual_barriers: Vec<D3D12_RESOURCE_BARRIER>,
2023-01-28 17:38:55 +11:00
}
2023-02-06 08:17:23 +11:00
impl<'a> MipmapGenContext<'a> {
fn new(
gen: &'a D3D12MipmapGen,
cmd: &'a ID3D12GraphicsCommandList,
heap: &'a mut D3D12DescriptorHeap<ResourceWorkHeap>,
) -> MipmapGenContext<'a> {
2023-01-28 17:38:55 +11:00
Self {
2023-02-06 08:17:23 +11:00
gen,
cmd,
heap,
residuals: Vec::new(),
residual_barriers: Vec::new(),
2023-01-28 17:38:55 +11:00
}
}
/// Generate a set of mipmaps for the resource.
/// This is a "cheap" action and only dispatches a compute shader.
pub fn generate_mipmaps<S: ResourceHandleStrategy<T>, T: ObtainResourceHandle>(
2023-02-06 08:17:23 +11:00
&mut self,
resource: &T,
2023-02-06 08:17:23 +11:00
miplevels: u16,
size: Size<u32>,
format: DXGI_FORMAT,
) -> error::Result<()> {
2023-01-28 17:38:55 +11:00
unsafe {
2023-04-23 15:13:31 +10:00
let (residuals_heap, residual_barriers) = self
2023-02-06 08:17:23 +11:00
.gen
.generate_mipmaps::<S, T>(self.cmd, resource, miplevels, size, format, self.heap)?;
// heap slots always need to be disposed
2023-04-23 15:13:31 +10:00
self.residuals.extend(residuals_heap);
// barriers need to be disposed if the handle strategy is incrementref
S::cleanup_handler(|| self.residual_barriers.extend(residual_barriers));
2023-01-28 17:38:55 +11:00
}
Ok(())
}
2023-04-23 15:13:31 +10:00
fn close(
self,
) -> (
Vec<D3D12DescriptorHeapSlot<ResourceWorkHeap>>,
Vec<D3D12_RESOURCE_BARRIER>,
2023-04-23 15:13:31 +10:00
) {
(self.residuals, self.residual_barriers)
2023-01-28 17:38:55 +11:00
}
}
2023-01-26 11:08:25 +11:00
impl D3D12MipmapGen {
2023-02-06 15:18:13 +11:00
pub fn new(device: &ID3D12Device, own_heaps: bool) -> error::Result<D3D12MipmapGen> {
2023-01-27 18:53:24 +11:00
unsafe {
let library = DxcCreateInstance(&CLSID_DxcLibrary)?;
let validator = DxcCreateInstance(&CLSID_DxcValidator)?;
2024-02-20 04:39:44 +11:00
let blob = dxc_validate_shader(&library, &validator, GENERATE_MIPMAPS_CS)?;
2023-02-06 08:17:23 +11:00
let blob =
std::slice::from_raw_parts(blob.GetBufferPointer().cast(), blob.GetBufferSize());
2023-02-06 11:58:51 +11:00
let root_signature: ID3D12RootSignature = device.CreateRootSignature(0, blob)?;
2023-01-27 18:53:24 +11:00
let desc = D3D12_COMPUTE_PIPELINE_STATE_DESC {
2023-04-23 15:13:31 +10:00
pRootSignature: ManuallyDrop::new(Some(root_signature.clone())),
2023-01-27 18:53:24 +11:00
CS: D3D12_SHADER_BYTECODE {
pShaderBytecode: blob.as_ptr().cast(),
2023-02-06 08:17:23 +11:00
BytecodeLength: blob.len(),
2023-01-27 18:53:24 +11:00
},
NodeMask: 0,
..Default::default()
};
let pipeline = device.CreateComputePipelineState(&desc)?;
2023-04-23 15:13:31 +10:00
drop(ManuallyDrop::into_inner(desc.pRootSignature));
2023-01-27 18:53:24 +11:00
Ok(D3D12MipmapGen {
device: device.clone(),
root_signature,
pipeline,
2023-02-06 15:18:13 +11:00
own_heaps,
2023-01-27 18:53:24 +11:00
})
}
2023-01-28 17:38:55 +11:00
}
2023-01-27 18:53:24 +11:00
2023-02-06 15:18:13 +11:00
/// If own_heap is false, this sets the compute root signature.
/// Otherwise, this does nothing and the root signature is set upon entering a
/// Mipmapping Context
pub fn pin_root_signature(&self, cmd: &ID3D12GraphicsCommandList) {
if !self.own_heaps {
unsafe {
cmd.SetComputeRootSignature(&self.root_signature);
}
}
}
2023-01-28 17:38:55 +11:00
/// Enters a mipmapping compute context.
2023-02-06 15:18:13 +11:00
/// This is a relatively expensive operation if set_heap is true,
2023-01-28 17:38:55 +11:00
/// and should only be done at most a few times per frame.
2023-02-06 15:18:13 +11:00
///
/// If own_heap is false, then you must ensure that the compute root signature
/// is already bound before entering the context.
2023-02-10 13:03:55 +11:00
///
/// The list of returned descriptors must be kept around until the command list has been
/// submitted.
#[must_use]
2023-02-06 10:25:59 +11:00
pub fn mipmapping_context<F, E>(
2023-02-06 08:17:23 +11:00
&self,
cmd: &ID3D12GraphicsCommandList,
work_heap: &mut D3D12DescriptorHeap<ResourceWorkHeap>,
mut f: F,
2023-04-23 15:13:31 +10:00
) -> Result<
(
Vec<D3D12DescriptorHeapSlot<ResourceWorkHeap>>,
Vec<D3D12_RESOURCE_BARRIER>,
2023-04-23 15:13:31 +10:00
),
E,
>
2023-02-06 08:17:23 +11:00
where
2023-02-06 10:25:59 +11:00
F: FnMut(&mut MipmapGenContext) -> Result<(), E>,
2023-01-28 17:38:55 +11:00
{
let heap: ID3D12DescriptorHeap = (&(*work_heap)).into();
unsafe {
cmd.SetPipelineState(&self.pipeline);
2023-02-06 15:18:13 +11:00
if self.own_heaps {
cmd.SetComputeRootSignature(&self.root_signature);
2023-04-23 15:13:31 +10:00
cmd.SetDescriptorHeaps(&[Some(heap)]);
2023-02-06 15:18:13 +11:00
}
2023-01-28 17:38:55 +11:00
}
2023-01-27 18:53:24 +11:00
2023-02-01 09:50:47 +11:00
let mut context = MipmapGenContext::new(self, cmd, work_heap);
2023-02-06 10:25:59 +11:00
f(&mut context)?;
2023-01-28 17:38:55 +11:00
Ok(context.close())
2023-01-26 11:08:25 +11:00
}
2023-01-28 17:38:55 +11:00
/// SAFETY:
/// - handle must be a CPU handle to an SRV
/// - work_heap must have enough descriptors to fit all miplevels.
unsafe fn generate_mipmaps<S: ResourceHandleStrategy<T>, T: ObtainResourceHandle>(
2023-02-06 08:17:23 +11:00
&self,
cmd: &ID3D12GraphicsCommandList,
resource: &T,
2023-02-06 08:17:23 +11:00
miplevels: u16,
size: Size<u32>,
format: DXGI_FORMAT,
work_heap: &mut D3D12DescriptorHeap<ResourceWorkHeap>,
2023-04-23 15:13:31 +10:00
) -> error::Result<(
Vec<D3D12DescriptorHeapSlot<ResourceWorkHeap>>,
Vec<D3D12_RESOURCE_BARRIER>,
2023-04-23 15:13:31 +10:00
)> {
2023-01-28 17:38:55 +11:00
// create views for mipmap generation
let srv = work_heap.allocate_descriptor()?;
unsafe {
2023-01-28 17:38:55 +11:00
let srv_desc = D3D12_SHADER_RESOURCE_VIEW_DESC {
Format: 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: miplevels as u32,
..Default::default()
},
},
};
2023-02-06 08:17:23 +11:00
self.device
.CreateShaderResourceView(resource.handle(), Some(&srv_desc), *srv.as_ref());
2023-01-28 17:38:55 +11:00
}
let mut heap_slots = Vec::with_capacity(miplevels as usize);
heap_slots.push(srv);
for i in 1..miplevels {
let descriptor = work_heap.allocate_descriptor()?;
2023-01-28 17:38:55 +11:00
let desc = D3D12_UNORDERED_ACCESS_VIEW_DESC {
Format: format,
ViewDimension: D3D12_UAV_DIMENSION_TEXTURE2D,
Anonymous: D3D12_UNORDERED_ACCESS_VIEW_DESC_0 {
Texture2D: D3D12_TEX2D_UAV {
MipSlice: i as u32,
..Default::default()
2023-02-06 08:17:23 +11:00
},
2023-01-28 17:38:55 +11:00
},
};
unsafe {
self.device.CreateUnorderedAccessView(
resource.handle(),
None,
Some(&desc),
*descriptor.as_ref(),
);
}
2023-01-28 17:38:55 +11:00
heap_slots.push(descriptor);
}
unsafe {
cmd.SetComputeRootDescriptorTable(0, *heap_slots[0].as_ref());
}
2023-01-28 17:38:55 +11:00
let mut residual_barriers = Vec::new();
2023-01-28 17:38:55 +11:00
for i in 1..miplevels as u32 {
let scaled = size.scale_mipmap(i);
let mipmap_params = MipConstants {
2023-02-06 08:17:23 +11:00
inv_out_texel_size: [1.0 / scaled.width as f32, 1.0 / scaled.height as f32],
2023-01-28 17:38:55 +11:00
src_mip_index: (i - 1),
};
let mipmap_params = bytemuck::bytes_of(&mipmap_params);
2023-02-07 13:56:30 +11:00
let barriers = [
util::d3d12_get_resource_transition_subresource::<S, _>(
2023-02-07 13:56:30 +11:00
resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
i - 1,
),
util::d3d12_get_resource_transition_subresource::<S, _>(
2023-02-07 13:56:30 +11:00
resource,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
i,
),
];
unsafe {
cmd.ResourceBarrier(&barriers);
S::cleanup_handler(|| residual_barriers.extend(barriers));
cmd.SetComputeRootDescriptorTable(1, *heap_slots[i as usize].as_ref());
cmd.SetComputeRoot32BitConstants(
2,
(std::mem::size_of::<MipConstants>() / std::mem::size_of::<u32>()) as u32,
mipmap_params.as_ptr().cast(),
0,
);
cmd.Dispatch(
std::cmp::max(scaled.width.div_ceil(8), 1),
std::cmp::max(scaled.height.div_ceil(8), 1),
1,
);
}
2023-01-28 17:38:55 +11:00
let uav_barrier = ManuallyDrop::new(D3D12_RESOURCE_UAV_BARRIER {
pResource: unsafe { S::obtain(resource) },
2023-01-28 17:38:55 +11:00
});
2023-02-07 13:56:30 +11:00
let barriers = [
D3D12_RESOURCE_BARRIER {
Type: D3D12_RESOURCE_BARRIER_TYPE_UAV,
Anonymous: D3D12_RESOURCE_BARRIER_0 { UAV: uav_barrier },
..Default::default()
},
util::d3d12_get_resource_transition_subresource::<S, _>(
2023-02-07 13:56:30 +11:00
resource,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
i,
),
util::d3d12_get_resource_transition_subresource::<S, _>(
2023-02-07 13:56:30 +11:00
resource,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE,
i - 1,
),
];
unsafe {
cmd.ResourceBarrier(&barriers);
}
2023-04-23 15:13:31 +10:00
S::cleanup_handler(|| residual_barriers.extend(barriers));
2023-01-28 17:38:55 +11:00
}
2023-01-26 11:08:25 +11:00
Ok((heap_slots, residual_barriers))
2023-01-26 11:08:25 +11:00
}
2023-02-06 08:17:23 +11:00
}