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::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::{
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_RANGE, D3D12_RESOURCE_DESC, D3D12_RESOURCE_DIMENSION_BUFFER,
D3D12_RESOURCE_STATE_GENERIC_READ, D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
};
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 {
handle: ID3D12Resource,
size: usize,
@ -100,15 +98,56 @@ impl D3D12Buffer {
}
}
impl D3D12ConstantBuffer {
pub fn new(buffer: D3D12Buffer) -> D3D12ConstantBuffer {
unsafe {
let desc = D3D12_CONSTANT_BUFFER_VIEW_DESC {
BufferLocation: buffer.handle.GetGPUVirtualAddress(),
SizeInBytes: buffer.size as u32,
};
/// SAFETY: Creating the pointer should be safe in multithreaded contexts.
///
/// Mutation is guarded by DerefMut<Target=[u8]>
unsafe impl Send for RawD3D12Buffer {}
pub struct RawD3D12Buffer {
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 bitvec::bitvec;
use bitvec::boxed::BitBox;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::ops::Deref;
use std::rc::Rc;
use bitvec::{bits, bitvec};
use bitvec::boxed::BitBox;
use crate::error::FilterChainError;
use windows::Win32::Graphics::Direct3D12::{
@ -193,7 +193,7 @@ impl<T> D3D12DescriptorHeap<T> {
handle_size: device.GetDescriptorHandleIncrementSize(desc.Type) as usize,
start: 0,
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(),
))
@ -253,7 +253,7 @@ impl<T> D3D12DescriptorHeap<T> {
handle_size: inner.handle_size,
start: 0,
num_descriptors: size,
map: bitvec![0; size].into_boxed_bitslice()
map: bitvec![0; size].into_boxed_bitslice(),
});
start += size;

View file

@ -1,4 +1,3 @@
use std::error::Error;
use thiserror::Error;
/// 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::{
CpuStagingHeap, D3D12DescriptorHeap, RenderTargetHeap, ResourceWorkHeap,
};
@ -391,8 +391,8 @@ impl FilterChainD3D12 {
let Ok(graphics_pipeline) =
D3D12GraphicsPipeline::new_from_dxil(
device,
&library,
&validator,
library,
validator,
&dxil,
root_signature,
render_format,
@ -401,8 +401,8 @@ impl FilterChainD3D12 {
} else {
let graphics_pipeline = D3D12GraphicsPipeline::new_from_hlsl(
device,
&library,
&compiler,
library,
compiler,
&hlsl,
root_signature,
render_format,
@ -410,37 +410,26 @@ impl FilterChainD3D12 {
(hlsl_reflection, graphics_pipeline)
};
let uniform_storage = UniformStorage::new(
reflection.ubo.as_ref().map_or(0, |ubo| ubo.size as usize),
reflection
// minimum size here has to be 1 byte.
let ubo_size = reflection.ubo.as_ref().map_or(1, |ubo| ubo.size as usize);
let push_size = reflection
.push_constant
.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());
Ok((reflection,
uniform_bindings,
uniform_storage,
push_cbuffer,
ubo_cbuffer,
graphics_pipeline,
config.clone(),
config,
source))
}).collect();
@ -456,16 +445,7 @@ impl FilterChainD3D12 {
.map(
|(
(
(
reflection,
uniform_bindings,
uniform_storage,
push_cbuffer,
ubo_cbuffer,
pipeline,
config,
source,
),
(reflection, uniform_bindings, uniform_storage, pipeline, config, source),
mut texture_heap,
),
mut sampler_heap,
@ -476,8 +456,6 @@ impl FilterChainD3D12 {
reflection,
uniform_bindings,
uniform_storage,
push_cbuffer,
ubo_cbuffer,
pipeline,
config,
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::error;
use crate::filter_chain::FilterCommon;
@ -13,7 +13,7 @@ use librashader_reflect::reflect::semantics::{MemberOffset, TextureBinding, Unif
use librashader_reflect::reflect::ShaderReflection;
use librashader_runtime::binding::{BindSemantics, TextureInput};
use librashader_runtime::quad::QuadType;
use librashader_runtime::uniforms::{UniformStorage, UniformStorageAccess};
use librashader_runtime::uniforms::{NoUniformBinder, UniformStorage};
use rustc_hash::FxHashMap;
use std::ops::Deref;
use windows::core::Interface;
@ -30,9 +30,8 @@ pub(crate) struct FilterPass {
pub(crate) reflection: ShaderReflection,
pub(crate) config: ShaderPassConfig,
pub(crate) uniform_bindings: FxHashMap<UniformBinding, MemberOffset>,
pub uniform_storage: UniformStorage,
pub(crate) push_cbuffer: Option<D3D12ConstantBuffer>,
pub(crate) ubo_cbuffer: Option<D3D12ConstantBuffer>,
pub uniform_storage:
UniformStorage<NoUniformBinder, Option<()>, RawD3D12Buffer, RawD3D12Buffer>,
pub(crate) texture_heap: [D3D12DescriptorHeapSlot<ResourceWorkHeap>; 16],
pub(crate) sampler_heap: [D3D12DescriptorHeapSlot<SamplerWorkHeap>; 16],
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 SamplerSet = SamplerSet;
type DescriptorSet<'a> = (
@ -160,33 +159,16 @@ impl FilterPass {
source,
);
// todo: write directly to persistently bound cbuffer.
if let Some(ubo) = &self.reflection.ubo
&& let Some(cbuffer) = &mut self.ubo_cbuffer
&& ubo.size != 0
{
{
let guard = cbuffer.buffer.map(None)?;
guard.slice.copy_from_slice(self.uniform_storage.ubo_slice());
}
unsafe {
cmd.SetGraphicsRootConstantBufferView(2, cbuffer.desc.BufferLocation)
}
self.uniform_storage.inner_ubo().bind_cbv(2, cmd);
}
if let Some(push) = &self.reflection.push_constant
&& let Some(cbuffer) = &mut self.push_cbuffer
&& push.size != 0
{
{
let guard = cbuffer.buffer.map(None)?;
guard.slice.copy_from_slice(self.uniform_storage.push_slice());
}
unsafe {
cmd.SetGraphicsRootConstantBufferView(3, cbuffer.desc.BufferLocation)
}
self.uniform_storage.inner_push().bind_cbv(3, cmd);
}
unsafe {

View file

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

View file

@ -318,7 +318,7 @@ impl FilterChainVulkan {
let spirv_words = reflect.compile(None)?;
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(
&vulkan.device,
&vulkan.memory_properties,

View file

@ -50,10 +50,11 @@ where
}
/// 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
C: Copy,
S: Deref<Target = [u8]> + DerefMut,
U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
H: BindUniform<C, f32>,
H: BindUniform<C, u32>,
H: BindUniform<C, i32>,
@ -89,7 +90,7 @@ where
fn bind_semantics<'a>(
device: &Self::DeviceContext,
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>,
mvp: &[f32; 16],
frame_count: u32,

View file

@ -37,9 +37,10 @@ pub trait UniformStorageAccess {
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
S: Deref<Target = [u8]> + DerefMut,
U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
{
fn ubo_pointer(&self) -> *const u8 {
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.
pub struct UniformStorage<H = NoUniformBinder, C = Option<()>, S = Box<[u8]>>
pub struct UniformStorage<H = NoUniformBinder, C = Option<()>, U = Box<[u8]>, P = Box<[u8]>>
where
S: Deref<Target = [u8]> + DerefMut,
U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
{
ubo: S,
push: Box<[u8]>,
ubo: U,
push: P,
_h: PhantomData<H>,
_c: PhantomData<C>,
}
impl<H, C, S> UniformStorage<H, C, S>
impl<H, C, U, P> UniformStorage<H, C, U, P>
where
S: Deref<Target = [u8]> + DerefMut,
U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
{
/// Access the backing storage for the UBO.
pub fn inner_ubo(&self) -> &S {
pub fn inner_ubo(&self) -> &U {
&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] {
match ty {
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
C: Copy,
S: Deref<Target = [u8]> + DerefMut,
U: Deref<Target = [u8]> + DerefMut,
P: Deref<Target = [u8]> + DerefMut,
{
#[inline(always)]
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
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 {
ubo: storage,
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.
pub fn new(ubo_size: usize, push_size: usize) -> UniformStorage<H, C, Box<[u8]>> {
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
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]>,
{
#[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
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]>,
{
#[inline(always)]