dx12: use persistently bound buffers

This commit is contained in:
chyyran 2023-02-05 22:24:58 -05:00
parent f077f86bad
commit fd48d88fdd
9 changed files with 134 additions and 106 deletions

View file

@ -1,19 +1,17 @@
use crate::error; use crate::error;
use crate::error::assume_d3d12_init; use crate::error::assume_d3d12_init;
use std::ops::Range; use std::ffi::c_void;
use std::mem::ManuallyDrop;
use std::ops::{Deref, DerefMut, Range};
use std::ptr::NonNull;
use windows::Win32::Graphics::Direct3D12::{ use windows::Win32::Graphics::Direct3D12::{
ID3D12Device, ID3D12Resource, D3D12_CONSTANT_BUFFER_VIEW_DESC, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
D3D12_HEAP_FLAG_NONE, D3D12_HEAP_PROPERTIES, D3D12_HEAP_TYPE_UPLOAD, D3D12_MEMORY_POOL_UNKNOWN, D3D12_HEAP_FLAG_NONE, D3D12_HEAP_PROPERTIES, D3D12_HEAP_TYPE_UPLOAD, D3D12_MEMORY_POOL_UNKNOWN,
D3D12_RANGE, D3D12_RESOURCE_DESC, D3D12_RESOURCE_DIMENSION_BUFFER, D3D12_RANGE, D3D12_RESOURCE_DESC, D3D12_RESOURCE_DIMENSION_BUFFER,
D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_TEXTURE_LAYOUT_ROW_MAJOR, D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
}; };
use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC; use windows::Win32::Graphics::Dxgi::Common::DXGI_SAMPLE_DESC;
pub struct D3D12ConstantBuffer {
pub buffer: D3D12Buffer,
pub desc: D3D12_CONSTANT_BUFFER_VIEW_DESC,
}
pub struct D3D12Buffer { pub struct D3D12Buffer {
handle: ID3D12Resource, handle: ID3D12Resource,
size: usize, size: usize,
@ -100,15 +98,56 @@ impl D3D12Buffer {
} }
} }
impl D3D12ConstantBuffer { /// SAFETY: Creating the pointer should be safe in multithreaded contexts.
pub fn new(buffer: D3D12Buffer) -> D3D12ConstantBuffer { ///
unsafe { /// Mutation is guarded by DerefMut<Target=[u8]>
let desc = D3D12_CONSTANT_BUFFER_VIEW_DESC { unsafe impl Send for RawD3D12Buffer {}
BufferLocation: buffer.handle.GetGPUVirtualAddress(), pub struct RawD3D12Buffer {
SizeInBytes: buffer.size as u32, buffer: ManuallyDrop<D3D12Buffer>,
}; ptr: NonNull<c_void>,
}
D3D12ConstantBuffer { buffer, desc } impl RawD3D12Buffer {
pub fn new(buffer: D3D12Buffer) -> error::Result<Self> {
let buffer = ManuallyDrop::new(buffer);
let range = D3D12_RANGE { Begin: 0, End: 0 };
let mut ptr = std::ptr::null_mut();
unsafe {
buffer.handle.Map(0, Some(&range), Some(&mut ptr))?;
}
// panic-safety: If Map returns Ok then ptr is not null
assert!(!ptr.is_null());
let ptr = unsafe { NonNull::new_unchecked(ptr) };
Ok(RawD3D12Buffer { buffer, ptr })
}
pub fn bind_cbv(&self, index: u32, cmd: &ID3D12GraphicsCommandList) {
unsafe { cmd.SetGraphicsRootConstantBufferView(index, self.buffer.gpu_address()) }
}
}
impl Drop for RawD3D12Buffer {
fn drop(&mut self) {
unsafe {
self.buffer.handle.Unmap(0, None);
ManuallyDrop::drop(&mut self.buffer);
} }
} }
} }
impl Deref for RawD3D12Buffer {
type Target = [u8];
fn deref(&self) -> &Self::Target {
unsafe { std::slice::from_raw_parts(self.ptr.as_ptr().cast(), self.buffer.size) }
}
}
impl DerefMut for RawD3D12Buffer {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr().cast(), self.buffer.size) }
}
}

View file

@ -1,10 +1,10 @@
use crate::error; use crate::error;
use bitvec::bitvec;
use bitvec::boxed::BitBox;
use std::cell::RefCell; use std::cell::RefCell;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
use bitvec::{bits, bitvec};
use bitvec::boxed::BitBox;
use crate::error::FilterChainError; use crate::error::FilterChainError;
use windows::Win32::Graphics::Direct3D12::{ use windows::Win32::Graphics::Direct3D12::{
@ -193,7 +193,7 @@ impl<T> D3D12DescriptorHeap<T> {
handle_size: device.GetDescriptorHandleIncrementSize(desc.Type) as usize, handle_size: device.GetDescriptorHandleIncrementSize(desc.Type) as usize,
start: 0, start: 0,
num_descriptors: desc.NumDescriptors as usize, num_descriptors: desc.NumDescriptors as usize,
map: bitvec![0; desc.NumDescriptors as usize].into_boxed_bitslice() map: bitvec![0; desc.NumDescriptors as usize].into_boxed_bitslice(),
})), })),
PhantomData::default(), PhantomData::default(),
)) ))
@ -253,7 +253,7 @@ impl<T> D3D12DescriptorHeap<T> {
handle_size: inner.handle_size, handle_size: inner.handle_size,
start: 0, start: 0,
num_descriptors: size, num_descriptors: size,
map: bitvec![0; size].into_boxed_bitslice() map: bitvec![0; size].into_boxed_bitslice(),
}); });
start += size; start += size;

View file

@ -1,4 +1,3 @@
use std::error::Error;
use thiserror::Error; use thiserror::Error;
/// Cumulative error type for Direct3D12 filter chains. /// Cumulative error type for Direct3D12 filter chains.

View file

@ -1,4 +1,4 @@
use crate::buffer::{D3D12Buffer, D3D12ConstantBuffer}; use crate::buffer::{D3D12Buffer, RawD3D12Buffer};
use crate::descriptor_heap::{ use crate::descriptor_heap::{
CpuStagingHeap, D3D12DescriptorHeap, RenderTargetHeap, ResourceWorkHeap, CpuStagingHeap, D3D12DescriptorHeap, RenderTargetHeap, ResourceWorkHeap,
}; };
@ -391,8 +391,8 @@ impl FilterChainD3D12 {
let Ok(graphics_pipeline) = let Ok(graphics_pipeline) =
D3D12GraphicsPipeline::new_from_dxil( D3D12GraphicsPipeline::new_from_dxil(
device, device,
&library, library,
&validator, validator,
&dxil, &dxil,
root_signature, root_signature,
render_format, render_format,
@ -401,8 +401,8 @@ impl FilterChainD3D12 {
} else { } else {
let graphics_pipeline = D3D12GraphicsPipeline::new_from_hlsl( let graphics_pipeline = D3D12GraphicsPipeline::new_from_hlsl(
device, device,
&library, library,
&compiler, compiler,
&hlsl, &hlsl,
root_signature, root_signature,
render_format, render_format,
@ -410,37 +410,26 @@ impl FilterChainD3D12 {
(hlsl_reflection, graphics_pipeline) (hlsl_reflection, graphics_pipeline)
}; };
let uniform_storage = UniformStorage::new( // minimum size here has to be 1 byte.
reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize), let ubo_size = reflection.ubo.as_ref().map_or(1, |ubo| ubo.size as usize);
reflection let push_size = reflection
.push_constant .push_constant
.as_ref() .as_ref()
.map_or(0, |push| push.size as usize), .map_or(1, |push| push.size as usize);
let uniform_storage = UniformStorage::new_with_storage(
RawD3D12Buffer::new(D3D12Buffer::new(device, ubo_size)?)?,
RawD3D12Buffer::new(D3D12Buffer::new(device, push_size)?)?
); );
let ubo_cbuffer = if let Some(ubo) = &reflection.ubo && ubo.size != 0 {
let buffer = D3D12ConstantBuffer::new(D3D12Buffer::new(device, ubo.size as usize)?);
Some(buffer)
} else {
None
};
let push_cbuffer = if let Some(push) = &reflection.push_constant && push.size != 0 {
let buffer = D3D12ConstantBuffer::new(D3D12Buffer::new(device, push.size as usize)?);
Some(buffer)
} else {
None
};
let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset()); let uniform_bindings = reflection.meta.create_binding_map(|param| param.offset());
Ok((reflection, Ok((reflection,
uniform_bindings, uniform_bindings,
uniform_storage, uniform_storage,
push_cbuffer,
ubo_cbuffer,
graphics_pipeline, graphics_pipeline,
config.clone(), config,
source)) source))
}).collect(); }).collect();
@ -456,16 +445,7 @@ impl FilterChainD3D12 {
.map( .map(
|( |(
( (
( (reflection, uniform_bindings, uniform_storage, pipeline, config, source),
reflection,
uniform_bindings,
uniform_storage,
push_cbuffer,
ubo_cbuffer,
pipeline,
config,
source,
),
mut texture_heap, mut texture_heap,
), ),
mut sampler_heap, mut sampler_heap,
@ -476,8 +456,6 @@ impl FilterChainD3D12 {
reflection, reflection,
uniform_bindings, uniform_bindings,
uniform_storage, uniform_storage,
push_cbuffer,
ubo_cbuffer,
pipeline, pipeline,
config, config,
texture_heap, texture_heap,

View file

@ -1,4 +1,4 @@
use crate::buffer::D3D12ConstantBuffer; use crate::buffer::RawD3D12Buffer;
use crate::descriptor_heap::{D3D12DescriptorHeapSlot, ResourceWorkHeap, SamplerWorkHeap}; use crate::descriptor_heap::{D3D12DescriptorHeapSlot, ResourceWorkHeap, SamplerWorkHeap};
use crate::error; use crate::error;
use crate::filter_chain::FilterCommon; use crate::filter_chain::FilterCommon;
@ -13,7 +13,7 @@ use librashader_reflect::reflect::semantics::{MemberOffset, TextureBinding, Unif
use librashader_reflect::reflect::ShaderReflection; use librashader_reflect::reflect::ShaderReflection;
use librashader_runtime::binding::{BindSemantics, TextureInput}; use librashader_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::quad::QuadType; use librashader_runtime::quad::QuadType;
use librashader_runtime::uniforms::{UniformStorage, UniformStorageAccess}; use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::ops::Deref; use std::ops::Deref;
use windows::core::Interface; use windows::core::Interface;
@ -30,9 +30,8 @@ pub(crate) struct FilterPass {
pub(crate) reflection: ShaderReflection, pub(crate) reflection: ShaderReflection,
pub(crate) config: ShaderPassConfig, pub(crate) config: ShaderPassConfig,
pub(crate) uniform_bindings: FxHashMap<UniformBinding, MemberOffset>, pub(crate) uniform_bindings: FxHashMap<UniformBinding, MemberOffset>,
pub uniform_storage: UniformStorage, pub uniform_storage:
pub(crate) push_cbuffer: Option<D3D12ConstantBuffer>, UniformStorage<NoUniformBinder, Option<()>, RawD3D12Buffer, RawD3D12Buffer>,
pub(crate) ubo_cbuffer: Option<D3D12ConstantBuffer>,
pub(crate) texture_heap: [D3D12DescriptorHeapSlot<ResourceWorkHeap>; 16], pub(crate) texture_heap: [D3D12DescriptorHeapSlot<ResourceWorkHeap>; 16],
pub(crate) sampler_heap: [D3D12DescriptorHeapSlot<SamplerWorkHeap>; 16], pub(crate) sampler_heap: [D3D12DescriptorHeapSlot<SamplerWorkHeap>; 16],
pub source: ShaderSource, pub source: ShaderSource,
@ -44,7 +43,7 @@ impl TextureInput for InputTexture {
} }
} }
impl BindSemantics for FilterPass { impl BindSemantics<NoUniformBinder, Option<()>, RawD3D12Buffer, RawD3D12Buffer> for FilterPass {
type InputTexture = InputTexture; type InputTexture = InputTexture;
type SamplerSet = SamplerSet; type SamplerSet = SamplerSet;
type DescriptorSet<'a> = ( type DescriptorSet<'a> = (
@ -160,33 +159,16 @@ impl FilterPass {
source, source,
); );
// todo: write directly to persistently bound cbuffer.
if let Some(ubo) = &self.reflection.ubo if let Some(ubo) = &self.reflection.ubo
&& let Some(cbuffer) = &mut self.ubo_cbuffer
&& ubo.size != 0 && ubo.size != 0
{ {
{ self.uniform_storage.inner_ubo().bind_cbv(2, cmd);
let guard = cbuffer.buffer.map(None)?;
guard.slice.copy_from_slice(self.uniform_storage.ubo_slice());
}
unsafe {
cmd.SetGraphicsRootConstantBufferView(2, cbuffer.desc.BufferLocation)
}
} }
if let Some(push) = &self.reflection.push_constant if let Some(push) = &self.reflection.push_constant
&& let Some(cbuffer) = &mut self.push_cbuffer
&& push.size != 0 && push.size != 0
{ {
{ self.uniform_storage.inner_push().bind_cbv(3, cmd);
let guard = cbuffer.buffer.map(None)?;
guard.slice.copy_from_slice(self.uniform_storage.push_slice());
}
unsafe {
cmd.SetGraphicsRootConstantBufferView(3, cbuffer.desc.BufferLocation)
}
} }
unsafe { unsafe {

View file

@ -7,10 +7,12 @@ use std::u64;
use widestring::u16cstr; use widestring::u16cstr;
use windows::core::{Interface, PCSTR, PCWSTR}; use windows::core::{Interface, PCSTR, PCWSTR};
use windows::Win32::Graphics::Direct3D::Dxc::{ use windows::Win32::Graphics::Direct3D::Dxc::{
DxcValidatorFlags_InPlaceEdit, IDxcBlob, IDxcBlobUtf8, IDxcCompiler, IDxcUtils, IDxcValidator, DxcValidatorFlags_InPlaceEdit, IDxcBlob, IDxcCompiler, IDxcUtils, IDxcValidator, DXC_CP,
DXC_CP, DXC_CP_UTF8, DXC_CP_UTF8,
};
use windows::Win32::Graphics::Direct3D::Fxc::{
D3DCompile, D3DCOMPILE_DEBUG, D3DCOMPILE_OPTIMIZATION_LEVEL3, D3DCOMPILE_SKIP_OPTIMIZATION,
}; };
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::Direct3D::ID3DBlob;
use windows::Win32::Graphics::Direct3D12::{ use windows::Win32::Graphics::Direct3D12::{
ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_FEATURE_DATA_FORMAT_SUPPORT, ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_FEATURE_DATA_FORMAT_SUPPORT,

View file

@ -318,7 +318,7 @@ impl FilterChainVulkan {
let spirv_words = reflect.compile(None)?; let spirv_words = reflect.compile(None)?;
let ubo_size = reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize); let ubo_size = reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize);
let uniform_storage = UniformStorage::new_with_storage( let uniform_storage = UniformStorage::new_with_ubo_storage(
RawVulkanBuffer::new( RawVulkanBuffer::new(
&vulkan.device, &vulkan.device,
&vulkan.memory_properties, &vulkan.memory_properties,

View file

@ -50,10 +50,11 @@ where
} }
/// Trait that abstracts binding of semantics to shader uniforms. /// Trait that abstracts binding of semantics to shader uniforms.
pub trait BindSemantics<H = NoUniformBinder, C = Option<()>, S = Box<[u8]>> pub trait BindSemantics<H = NoUniformBinder, C = Option<()>, U = Box<[u8]>, P = Box<[u8]>>
where where
C: Copy, C: Copy,
S: Deref<Target = [u8]> + DerefMut, U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
H: BindUniform<C, f32>, H: BindUniform<C, f32>,
H: BindUniform<C, u32>, H: BindUniform<C, u32>,
H: BindUniform<C, i32>, H: BindUniform<C, i32>,
@ -89,7 +90,7 @@ where
fn bind_semantics<'a>( fn bind_semantics<'a>(
device: &Self::DeviceContext, device: &Self::DeviceContext,
sampler_set: &Self::SamplerSet, sampler_set: &Self::SamplerSet,
uniform_storage: &mut UniformStorage<H, C, S>, uniform_storage: &mut UniformStorage<H, C, U, P>,
descriptor_set: &mut Self::DescriptorSet<'a>, descriptor_set: &mut Self::DescriptorSet<'a>,
mvp: &[f32; 16], mvp: &[f32; 16],
frame_count: u32, frame_count: u32,

View file

@ -37,9 +37,10 @@ pub trait UniformStorageAccess {
fn push_slice(&self) -> &[u8]; fn push_slice(&self) -> &[u8];
} }
impl<T, H, S> UniformStorageAccess for UniformStorage<T, H, S> impl<T, H, U, P> UniformStorageAccess for UniformStorage<T, H, U, P>
where where
S: Deref<Target = [u8]> + DerefMut, U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
{ {
fn ubo_pointer(&self) -> *const u8 { fn ubo_pointer(&self) -> *const u8 {
self.ubo.as_ptr() self.ubo.as_ptr()
@ -68,25 +69,32 @@ impl<T> BindUniform<Option<()>, T> for NoUniformBinder {
} }
/// A helper to bind uniform variables to UBO or Push Constant Buffers. /// A helper to bind uniform variables to UBO or Push Constant Buffers.
pub struct UniformStorage<H = NoUniformBinder, C = Option<()>, S = Box<[u8]>> pub struct UniformStorage<H = NoUniformBinder, C = Option<()>, U = Box<[u8]>, P = Box<[u8]>>
where where
S: Deref<Target = [u8]> + DerefMut, U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
{ {
ubo: S, ubo: U,
push: Box<[u8]>, push: P,
_h: PhantomData<H>, _h: PhantomData<H>,
_c: PhantomData<C>, _c: PhantomData<C>,
} }
impl<H, C, S> UniformStorage<H, C, S> impl<H, C, U, P> UniformStorage<H, C, U, P>
where where
S: Deref<Target = [u8]> + DerefMut, U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
{ {
/// Access the backing storage for the UBO. /// Access the backing storage for the UBO.
pub fn inner_ubo(&self) -> &S { pub fn inner_ubo(&self) -> &U {
&self.ubo &self.ubo
} }
/// Access the backing storage for the Push storage.
pub fn inner_push(&self) -> &P {
&self.push
}
pub(crate) fn buffer(&mut self, ty: UniformMemberBlock) -> &mut [u8] { pub(crate) fn buffer(&mut self, ty: UniformMemberBlock) -> &mut [u8] {
match ty { match ty {
UniformMemberBlock::Ubo => self.ubo.deref_mut(), UniformMemberBlock::Ubo => self.ubo.deref_mut(),
@ -95,10 +103,11 @@ where
} }
} }
impl<H, C, S> UniformStorage<H, C, S> impl<H, C, U, P> UniformStorage<H, C, U, P>
where where
C: Copy, C: Copy,
S: Deref<Target = [u8]> + DerefMut, U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
{ {
#[inline(always)] #[inline(always)]
fn write_scalar_inner<T: UniformScalar>(buffer: &mut [u8], value: T) { fn write_scalar_inner<T: UniformScalar>(buffer: &mut [u8], value: T) {
@ -125,7 +134,23 @@ where
} }
/// Create a new `UniformStorage` with the given backing storage /// Create a new `UniformStorage` with the given backing storage
pub fn new_with_storage(storage: S, push_size: usize) -> UniformStorage<H, C, S> { pub fn new_with_storage(ubo: U, push: P) -> UniformStorage<H, C, U, P> {
UniformStorage {
ubo,
push,
_h: Default::default(),
_c: Default::default(),
}
}
}
impl<H, C, U> UniformStorage<H, C, U, Box<[u8]>>
where
C: Copy,
U: Deref<Target = [u8]> + DerefMut,
{
/// Create a new `UniformStorage` with the given backing storage
pub fn new_with_ubo_storage(storage: U, push_size: usize) -> UniformStorage<H, C, U, Box<[u8]>> {
UniformStorage { UniformStorage {
ubo: storage, ubo: storage,
push: vec![0u8; push_size].into_boxed_slice(), push: vec![0u8; push_size].into_boxed_slice(),
@ -135,7 +160,7 @@ where
} }
} }
impl<H, C> UniformStorage<H, C, Box<[u8]>> { impl<H, C> UniformStorage<H, C, Box<[u8]>, Box<[u8]>> {
/// Create a new `UniformStorage` with the given size for UBO and Push Constant Buffer sizes. /// Create a new `UniformStorage` with the given size for UBO and Push Constant Buffer sizes.
pub fn new(ubo_size: usize, push_size: usize) -> UniformStorage<H, C, Box<[u8]>> { pub fn new(ubo_size: usize, push_size: usize) -> UniformStorage<H, C, Box<[u8]>> {
UniformStorage { UniformStorage {
@ -147,10 +172,11 @@ impl<H, C> UniformStorage<H, C, Box<[u8]>> {
} }
} }
impl<H, C, S> UniformStorage<H, C, S> impl<H, C, U, P> UniformStorage<H, C, U, P>
where where
C: Copy, C: Copy,
S: Deref<Target = [u8]> + DerefMut, U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
H: for<'a> BindUniform<C, &'a [f32; 4]>, H: for<'a> BindUniform<C, &'a [f32; 4]>,
{ {
#[inline(always)] #[inline(always)]
@ -178,10 +204,11 @@ where
} }
} }
impl<H, C, S> UniformStorage<H, C, S> impl<H, C, U, P> UniformStorage<H, C, U, P>
where where
C: Copy, C: Copy,
S: Deref<Target = [u8]> + DerefMut, U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
H: for<'a> BindUniform<C, &'a [f32; 16]>, H: for<'a> BindUniform<C, &'a [f32; 16]>,
{ {
#[inline(always)] #[inline(always)]