capi(d3d12): allow d3d12 to optionally use a resource handle with chain-managed descriptors

This commit is contained in:
chyyran 2024-09-30 02:27:16 -04:00 committed by Ronny Chan
parent 0cb3880d7f
commit 7a13136f9a
6 changed files with 198 additions and 48 deletions

View file

@ -66,9 +66,17 @@ The following changes are applicable if `LIBRA_RUNTIME_D3D11` is defined.
## `LIBRA_RUNTIME_D3D12` changes
The following changes are applicable if `LIBRA_RUNTIME_D3D12` is defined.
* The lifetime of resources will not be extended past the call to the `libra_d3d12_filter_chain_frame` function. In other words, the refcount for any resources passed into the function
will no longer be changed, and it is explicitly the responsibility of the caller to ensure any resources remain alive until the `ID3D12GraphicsCommandList` provided is submitted.
* The fields `format`, `width`, and `height` have been removed from `libra_source_image_d3d12_t`.
* The fields `width` and `height` have been added to `libra_output_image_d3d12_t`.
* You should now pass what was previously `.width` and `.height` of `libra_viewport_t` to these new fields in `libra_output_image_d3d12_t`.
* The `image` parameter of `libra_d3d12_filter_chain_frame` has changed from `libra_source_image_d3d12_t` to `libra_image_d3d12_t`.
* To maintain the previous behaviour, `.image_type` of the `libra_image_d3d12_t` should be set to `IMAGE_TYPE_SOURCE_IMAGE`, and `.handle.source` should be the `libra_source_image_d3d12_t`struct.
* The `out` parameter of `libra_d3d12_filter_chain_frame` has changed from `libra_output_image_d3d11_t` to `libra_image_d3d12_t`.
* To maintain the previous behaviour, `.image_type` of the `libra_image_d3d12_t` should be set to `IMAGE_TYPE_OUTPUT_IMAGE`, and `.handle.output` should be the `libra_output_image_d3d12_t`struct.
* Any `libra_image_d3d12_t` can now optionally pass only the `ID3D12Resource *` for the texture by setting `.image_type` to `IMAGE_TYPE_RESOURCE` and setting `.handle.resource` to the resource pointer.
* If using `IMAGE_TYPE_RESOURCE`, shader resource view and render target view descriptors for the input and output images will be internally allocated by the filter chain. This may result in marginally worse performance.
* In `libra_d3d12_filter_chain_frame`, the position of the `viewport` parameter has moved to after the `out` parameter, and its type has changed from `libra_viewport_t` to `libra_viewport_t *`, which is allowed to be `NULL`.
See [`libra_viewport_t` changes](#libra_viewport_t-changes) for more details.
* The `chain` parameter of `libra_d3d12_filter_chain_get_param` has been made `const`.

View file

@ -51,19 +51,45 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#endif
#if (defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12))
/// The type of image passed to the image.
typedef enum LIBRA_D3D12_IMAGE_TYPE {
#if (defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12))
/// The image handle is a pointer to a `ID3D12Resource`.
LIBRA_D3D12_IMAGE_TYPE_IMAGE_TYPE_RESOURCE = 0,
#endif
#if (defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12))
/// The image handle is a `libra_source_image_d3d12_t`
LIBRA_D3D12_IMAGE_TYPE_IMAGE_TYPE_SOURCE_IMAGE = 1,
#endif
#if (defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12))
/// The image handle is a `libra_output_image_d3d12_t`
LIBRA_D3D12_IMAGE_TYPE_IMAGE_TYPE_OUTPUT_IMAGE = 2,
#endif
} LIBRA_D3D12_IMAGE_TYPE;
#endif
/// Error codes for librashader error types.
enum LIBRA_ERRNO
#ifdef __cplusplus
: int32_t
#endif // __cplusplus
{
/// Error code for an unknown error.
LIBRA_ERRNO_UNKNOWN_ERROR = 0,
/// Error code for an invalid parameter.
LIBRA_ERRNO_INVALID_PARAMETER = 1,
/// Error code for an invalid (non-UTF8) string.
LIBRA_ERRNO_INVALID_STRING = 2,
/// Error code for a preset parser error.
LIBRA_ERRNO_PRESET_ERROR = 3,
/// Error code for a preprocessor error.
LIBRA_ERRNO_PREPROCESS_ERROR = 4,
/// Error code for a shader parameter error.
LIBRA_ERRNO_SHADER_PARAMETER_ERROR = 5,
/// Error code for a reflection error.
LIBRA_ERRNO_REFLECT_ERROR = 6,
/// Error code for a runtime error.
LIBRA_ERRNO_RUNTIME_ERROR = 7,
};
#ifndef __cplusplus
@ -76,23 +102,32 @@ enum LIBRA_PRESET_CTX_ORIENTATION
: uint32_t
#endif // __cplusplus
{
/// Context parameter for vertical orientation.
LIBRA_PRESET_CTX_ORIENTATION_VERTICAL = 0,
/// Context parameter for horizontal orientation.
LIBRA_PRESET_CTX_ORIENTATION_HORIZONTAL,
};
#ifndef __cplusplus
typedef uint32_t LIBRA_PRESET_CTX_ORIENTATION;
#endif // __cplusplus
/// An enum representing graphics runtimes (video drivers) for use in preset contexts.
enum LIBRA_PRESET_CTX_RUNTIME
#ifdef __cplusplus
: uint32_t
#endif // __cplusplus
{
/// No runtime.
LIBRA_PRESET_CTX_RUNTIME_NONE = 0,
/// OpenGL 3.3+
LIBRA_PRESET_CTX_RUNTIME_GL_CORE,
/// Vulkan
LIBRA_PRESET_CTX_RUNTIME_VULKAN,
/// Direct3D 11
LIBRA_PRESET_CTX_RUNTIME_D3D11,
/// Direct3D 12
LIBRA_PRESET_CTX_RUNTIME_D3D12,
/// Metal
LIBRA_PRESET_CTX_RUNTIME_METAL,
};
#ifndef __cplusplus
@ -418,10 +453,10 @@ typedef struct _filter_chain_d3d12 *libra_d3d12_filter_chain_t;
#if (defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12))
/// Direct3D 12 parameters for the source image.
typedef struct libra_source_image_d3d12_t {
/// The resource containing the image.
ID3D12Resource * resource;
/// A CPU descriptor handle to a shader resource view of the image.
D3D12_CPU_DESCRIPTOR_HANDLE descriptor;
/// The resource containing the image.
ID3D12Resource * resource;
} libra_source_image_d3d12_t;
#endif
@ -439,6 +474,31 @@ typedef struct libra_output_image_d3d12_t {
} libra_output_image_d3d12_t;
#endif
#if (defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12))
/// A handle to a Direct3D 12 image.
///
/// This must be either a pointer to a `ID3D12Resource`,
/// or a valid source or output image type.
typedef union libra_image_d3d12_handle_t {
/// A pointer to an `ID3D12Resource`, with descriptors managed by the filter chain.
ID3D12Resource * resource;
/// A source image with externally managed descriptors.
struct libra_source_image_d3d12_t source;
/// An output image with externally managed descriptors.
struct libra_output_image_d3d12_t output;
} libra_image_d3d12_handle_t;
#endif
#if (defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12))
/// Tagged union for a Direct3D 12 image
typedef struct libra_image_d3d12_t {
/// The type of the image.
enum LIBRA_D3D12_IMAGE_TYPE image_type;
/// The handle to the image.
union libra_image_d3d12_handle_t image;
} libra_image_d3d12_t;
#endif
#if (defined(_WIN32) && defined(LIBRA_RUNTIME_D3D12))
/// Options for each Direct3D 12 shader frame.
typedef struct frame_d3d12_opt_t {
@ -469,6 +529,7 @@ typedef struct filter_chain_mtl_opt_t {
#endif
#if (defined(__APPLE__) && defined(LIBRA_RUNTIME_METAL) && defined(__OBJC__))
/// A handle to a Vulkan filter chain.
typedef struct _filter_chain_mtl *libra_mtl_filter_chain_t;
#endif
@ -491,6 +552,7 @@ typedef struct frame_mtl_opt_t {
} frame_mtl_opt_t;
#endif
/// ABI version type alias.
typedef size_t LIBRASHADER_ABI_VERSION;
/// Function pointer definition for libra_abi_version
@ -887,8 +949,8 @@ typedef libra_error_t (*PFN_libra_d3d12_filter_chain_create_deferred)(libra_shad
typedef libra_error_t (*PFN_libra_d3d12_filter_chain_frame)(libra_d3d12_filter_chain_t *chain,
ID3D12GraphicsCommandList * command_list,
size_t frame_count,
struct libra_source_image_d3d12_t image,
struct libra_output_image_d3d12_t out,
struct libra_image_d3d12_t image,
struct libra_image_d3d12_t out,
const struct libra_viewport_t *viewport,
const float *mvp,
const struct frame_d3d12_opt_t *options);
@ -1761,8 +1823,8 @@ libra_error_t libra_d3d12_filter_chain_create_deferred(libra_shader_preset_t *pr
libra_error_t libra_d3d12_filter_chain_frame(libra_d3d12_filter_chain_t *chain,
ID3D12GraphicsCommandList * command_list,
size_t frame_count,
struct libra_source_image_d3d12_t image,
struct libra_output_image_d3d12_t out,
struct libra_image_d3d12_t image,
struct libra_image_d3d12_t out,
const struct libra_viewport_t *viewport,
const float *mvp,
const struct frame_d3d12_opt_t *options);
@ -2025,6 +2087,7 @@ libra_error_t libra_preset_ctx_set_param(libra_preset_ctx_t *context,
/// - GLCore
/// - Direct3D11
/// - Direct3D12
/// - Metal
///
/// This will also set the appropriate video driver extensions.
///

View file

@ -358,7 +358,7 @@ libra_error_t __librashader__noop_d3d12_filter_chain_create_deferred(
libra_error_t __librashader__noop_d3d12_filter_chain_frame(
libra_d3d12_filter_chain_t *chain, ID3D12GraphicsCommandList *command_list,
size_t frame_count, struct libra_source_image_d3d12_t image, struct libra_output_image_d3d12_t out,
size_t frame_count, struct libra_image_d3d12_t image, struct libra_image_d3d12_t out,
const struct libra_viewport_t *viewport, const float *mvp,
const struct frame_d3d12_opt_t *opt) {
return NULL;

View file

@ -8,7 +8,6 @@ use std::ffi::CStr;
use std::mem::{ManuallyDrop, MaybeUninit};
use std::ptr::NonNull;
use std::slice;
use windows::core::Interface;
use windows::Win32::Graphics::Direct3D12::{
ID3D12Device, ID3D12GraphicsCommandList, ID3D12Resource, D3D12_CPU_DESCRIPTOR_HANDLE,
};
@ -20,6 +19,40 @@ use librashader::runtime::d3d12::{
};
use librashader::runtime::{FilterChainParameters, Size, Viewport};
/// Tagged union for a Direct3D 12 image
#[repr(C)]
pub struct libra_image_d3d12_t {
/// The type of the image.
pub image_type: LIBRA_D3D12_IMAGE_TYPE,
/// The handle to the image.
pub handle: libra_image_d3d12_handle_t,
}
/// A handle to a Direct3D 12 image.
///
/// This must be either a pointer to a `ID3D12Resource`,
/// or a valid source or output image type.
#[repr(C)]
pub union libra_image_d3d12_handle_t {
/// A pointer to an `ID3D12Resource`, with descriptors managed by the filter chain.
pub resource: ManuallyDrop<ID3D12Resource>,
/// A source image with externally managed descriptors.
pub source: ManuallyDrop<libra_source_image_d3d12_t>,
/// An output image with externally managed descriptors.
pub output: ManuallyDrop<libra_output_image_d3d12_t>,
}
/// The type of image passed to the image.
#[repr(C)]
pub enum LIBRA_D3D12_IMAGE_TYPE {
/// The image handle is a pointer to a `ID3D12Resource`.
IMAGE_TYPE_RESOURCE = 0,
/// The image handle is a `libra_source_image_d3d12_t`
IMAGE_TYPE_SOURCE_IMAGE = 1,
/// The image handle is a `libra_output_image_d3d12_t`
IMAGE_TYPE_OUTPUT_IMAGE = 2,
}
/// Direct3D 12 parameters for the source image.
#[repr(C)]
pub struct libra_source_image_d3d12_t {
@ -200,18 +233,26 @@ extern_fn! {
/// remain in `D3D12_RESOURCE_STATE_RENDER_TARGET` after all shader passes. The caller must transition
/// the output image to the final resource state.
///
/// The refcount of any COM pointers passed into this frame will not be changed. It is the responsibility
/// of the caller to ensure any resources remain alive until the `ID3D12GraphicsCommandList` provided is
/// submitted.
///
/// ## Parameters
///
/// - `chain` is a handle to the filter chain.
/// - `command_list` is a `ID3D12GraphicsCommandList` to record draw commands to.
/// The provided command list must be open and associated with the `ID3D12Device` this filter chain was created with.
/// - `frame_count` is the number of frames passed to the shader
/// - `image` is a `libra_source_image_d3d12_t`, containing a `ID3D12Resource` pointer and CPU descriptor
/// to an image that will serve as the source image for the frame. The input image must be in the
/// `D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE` resource state or equivalent barrier layout.
/// - `out` is a `libra_output_image_d3d12_t`, containing a CPU descriptor handle, format, and size information
/// for the render target of the frame. The output image must be in
/// - `image` is a `libra_image_d3d12_t` with `image_type` set to `IMAGE_TYPE_SOURCE_IMAGE` or `IMAGE_TYPE_RESOURCE`,
/// with `image_handle` either a `ID3D12Resource*` or an `libra_source_image_d3d12_t` containing a CPU descriptor to a shader resource view,
/// and the image resource the view is of, which will serve as the source image for the frame.
/// The input image resource must be in the `D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE` resource state
/// or equivalent barrier layout. The image resource must have dimension `D3D12_RESOURCE_DIMENSION_TEXTURE2D`.
/// - `out` is a `libra_image_d3d12_t`, with `image_type` set to `IMAGE_TYPE_OUTPUT_IMAGE` or `IMAGE_TYPE_RESOURCE`,
/// with `image_handle` being either a `ID3D12Resource*` or an `libra_output_image_d3d12_t`, containing a CPU descriptor handle,
/// format, and size information for the render target of the frame. The output image must be in
/// `D3D12_RESOURCE_STATE_RENDER_TARGET` resource state or equivalent barrier layout.
/// The image resource must have dimension `D3D12_RESOURCE_DIMENSION_TEXTURE2D`.
///
/// - `viewport` is a pointer to a `libra_viewport_t` that specifies the area onto which scissor and viewport
/// will be applied to the render target. It may be null, in which case a default viewport spanning the
@ -228,18 +269,23 @@ extern_fn! {
/// values for the model view projection matrix.
/// - `opt` may be null, or if it is not null, must be an aligned pointer to a valid `frame_d3d12_opt_t`
/// struct.
/// - `out` must be a descriptor handle to a render target view.
/// - `image.resource` must not be null.
/// - Any resource pointers contained within a `libra_image_d3d12_t` must be non-null.
/// - The `handle` field of any `libra_image_d3d12_t` must be valid for it's `image_type`.
/// - If `image_type` is `IMAGE_TYPE_RESOURCE`, then `handle` must be `ID3D12Resource *`.
/// - If `image_type` is `IMAGE_TYPE_SOURCE`, then `handle` must be `libra_source_image_d3d12_t`.
/// - If `image_type` is `IMAGE_TYPE_OUTPUT`, then `handle` must be `libra_output_image_d3d12_t`.
/// - `command_list` must be a non-null pointer to a `ID3D12GraphicsCommandList` that is open,
/// and must be associated with the `ID3D12Device` this filter chain was created with.
/// - All resource pointers contained within a `libra_image_d3d12_t` must remain valid until the `ID3D12GraphicsCommandList *`
/// provided is submitted after the call to this function.
/// - You must ensure that only one thread has access to `chain` before you call this function. Only one
/// thread at a time may call this function.
nopanic fn libra_d3d12_filter_chain_frame(
chain: *mut libra_d3d12_filter_chain_t,
command_list: ManuallyDrop<ID3D12GraphicsCommandList>,
frame_count: usize,
image: libra_source_image_d3d12_t,
out: libra_output_image_d3d12_t,
image: libra_image_d3d12_t,
out: libra_image_d3d12_t,
viewport: *const libra_viewport_t,
mvp: *const f32,
options: *const MaybeUninit<frame_d3d12_opt_t>
@ -261,10 +307,26 @@ extern_fn! {
let options = options.map(FromUninit::from_uninit);
let output = unsafe {
match out.image_type {
LIBRA_D3D12_IMAGE_TYPE::IMAGE_TYPE_RESOURCE => {
let out = out.handle.resource;
D3D12OutputView::new_from_resource(
out,
chain,
)?
}
LIBRA_D3D12_IMAGE_TYPE::IMAGE_TYPE_OUTPUT_IMAGE => {
let out = out.handle.output;
D3D12OutputView::new_from_raw(
out.descriptor,
Size::new(out.width, out.height),
out.format)
out.format,
)
}
LIBRA_D3D12_IMAGE_TYPE::IMAGE_TYPE_SOURCE_IMAGE => {
return Err(LibrashaderError::InvalidParameter("out"))
}
}
};
let viewport = if viewport.is_null() {
@ -283,10 +345,25 @@ extern_fn! {
}
};
let image = D3D12InputImage::External {
resource: image.resource.to_ref(),
let image = unsafe {
match image.image_type {
LIBRA_D3D12_IMAGE_TYPE::IMAGE_TYPE_RESOURCE => {
let image = image.handle.resource;
D3D12InputImage::Managed(image)
}
LIBRA_D3D12_IMAGE_TYPE::IMAGE_TYPE_SOURCE_IMAGE => {
let image = ManuallyDrop::into_inner(image.handle.source);
D3D12InputImage::External {
resource: image.resource,
descriptor: image.descriptor,
}
}
LIBRA_D3D12_IMAGE_TYPE::IMAGE_TYPE_OUTPUT_IMAGE => {
return Err(LibrashaderError::InvalidParameter("image"))
}
}
};
unsafe {
chain.frame(&command_list, image, &viewport, frame_count, options.as_ref())?;
}

View file

@ -7,9 +7,7 @@ use anyhow::anyhow;
use d3d12_descriptor_heap::{D3D12DescriptorHeap, D3D12DescriptorHeapSlot};
use image::RgbaImage;
use librashader::presets::ShaderPreset;
use librashader::runtime::d3d12::{
D3D12InputImage, D3D12OutputView, FilterChain, FilterChainOptions, FrameOptions,
};
use librashader::runtime::d3d12::{D3D12OutputView, FilterChain, FilterChainOptions, FrameOptions};
use librashader::runtime::Viewport;
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8};
@ -151,14 +149,10 @@ impl RenderTest for Direct3D12 {
current_subframe: options.current_subframe,
});
let image = self.texture.to_ref();
for frame in 0..=frame_count {
filter_chain.frame(
&cmd,
D3D12InputImage::Managed(self.texture.to_ref()),
&viewport,
frame,
options.as_ref(),
)?;
filter_chain.frame(&cmd, image.into(), &viewport, frame, options.as_ref())?;
}
cmd.Close()?;

View file

@ -17,24 +17,26 @@ use windows::Win32::Graphics::Direct3D12::{
};
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT;
/// A **non-owning** reference to a ID3D12Resource.
/// This does not `AddRef` or `Release` the underlying interface.
pub type D3D12ResourceRef<'a> = InterfaceRef<'a, ID3D12Resource>;
/// An image for use as shader resource view.
#[derive(Clone)]
pub enum D3D12InputImage<'a> {
pub enum D3D12InputImage {
/// The filter chain manages the CPU descriptor to the shader resource view.
Managed(InterfaceRef<'a, ID3D12Resource>),
Managed(ManuallyDrop<ID3D12Resource>),
/// The CPU descriptor to the shader resource view is managed externally.
External {
/// The ID3D12Resource that holds the image data.
resource: InterfaceRef<'a, ID3D12Resource>,
resource: ManuallyDrop<ID3D12Resource>,
/// The CPU descriptor to the shader resource view.
descriptor: D3D12_CPU_DESCRIPTOR_HANDLE,
},
}
impl<'a> From<InterfaceRef<'a, ID3D12Resource>> for D3D12InputImage {
fn from(value: InterfaceRef<'a, ID3D12Resource>) -> Self {
Self::Managed(unsafe { std::mem::transmute(value) })
}
}
#[derive(Clone)]
pub(crate) enum InputDescriptor {
Owned(D3D12DescriptorHeapSlot<CpuStagingHeap>),
@ -112,15 +114,21 @@ impl D3D12OutputView {
///
/// SAFETY: the image must be valid until the command list is submitted.
pub unsafe fn new_from_resource(
image: D3D12ResourceRef,
image: ManuallyDrop<ID3D12Resource>,
chain: &mut FilterChainD3D12,
) -> error::Result<D3D12OutputView> {
unsafe { Self::new_from_resource_internal(image, &chain.common.d3d12, &mut chain.rtv_heap) }
unsafe {
Self::new_from_resource_internal(
std::mem::transmute(image),
&chain.common.d3d12,
&mut chain.rtv_heap,
)
}
}
/// Create a new output view from a resource ref
pub(crate) unsafe fn new_from_resource_internal(
image: D3D12ResourceRef,
image: InterfaceRef<ID3D12Resource>,
device: &ID3D12Device,
heap: &mut D3D12DescriptorHeap<RenderTargetHeap>,
) -> error::Result<D3D12OutputView> {
@ -192,8 +200,8 @@ impl InputTexture {
}
// unsafe since the lifetime of the handle has to survive
pub unsafe fn new_from_resource<'a>(
image: InterfaceRef<'a, ID3D12Resource>,
pub unsafe fn new_from_resource(
image: ManuallyDrop<ID3D12Resource>,
filter: FilterMode,
wrap_mode: WrapMode,
device: &ID3D12Device,
@ -225,7 +233,7 @@ impl InputTexture {
}?;
Ok(InputTexture {
resource: unsafe { std::mem::transmute(image) },
resource: image,
descriptor,
size: Size::new(desc.Width as u32, desc.Height),
format: desc.Format,
@ -236,7 +244,7 @@ impl InputTexture {
// unsafe since the lifetime of the handle has to survive
pub unsafe fn new_from_raw(
image: InterfaceRef<ID3D12Resource>,
image: ManuallyDrop<ID3D12Resource>,
descriptor: D3D12_CPU_DESCRIPTOR_HANDLE,
filter: FilterMode,
wrap_mode: WrapMode,