diff --git a/MIGRATION-ABI2.md b/MIGRATION-ABI2.md index c255977..437abbc 100644 --- a/MIGRATION-ABI2.md +++ b/MIGRATION-ABI2.md @@ -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`. diff --git a/include/librashader.h b/include/librashader.h index 9bfcc5d..6107306 100644 --- a/include/librashader.h +++ b/include/librashader.h @@ -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. /// diff --git a/include/librashader_ld.h b/include/librashader_ld.h index a55bf19..de37c40 100644 --- a/include/librashader_ld.h +++ b/include/librashader_ld.h @@ -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; diff --git a/librashader-capi/src/runtime/d3d12/filter_chain.rs b/librashader-capi/src/runtime/d3d12/filter_chain.rs index 525f929..c5ea0ab 100644 --- a/librashader-capi/src/runtime/d3d12/filter_chain.rs +++ b/librashader-capi/src/runtime/d3d12/filter_chain.rs @@ -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, + /// A source image with externally managed descriptors. + pub source: ManuallyDrop, + /// An output image with externally managed descriptors. + pub output: ManuallyDrop, +} + +/// 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, 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 @@ -261,10 +307,26 @@ extern_fn! { let options = options.map(FromUninit::from_uninit); let output = unsafe { - D3D12OutputView::new_from_raw( - out.descriptor, - Size::new(out.width, out.height), - out.format) + 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, + ) + } + 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(), - descriptor: image.descriptor, + 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())?; } diff --git a/librashader-cli/src/render/d3d12/mod.rs b/librashader-cli/src/render/d3d12/mod.rs index bf6f8c1..04c9b35 100644 --- a/librashader-cli/src/render/d3d12/mod.rs +++ b/librashader-cli/src/render/d3d12/mod.rs @@ -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()?; diff --git a/librashader-runtime-d3d12/src/texture.rs b/librashader-runtime-d3d12/src/texture.rs index c766b1c..748508c 100644 --- a/librashader-runtime-d3d12/src/texture.rs +++ b/librashader-runtime-d3d12/src/texture.rs @@ -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), /// 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, /// The CPU descriptor to the shader resource view. descriptor: D3D12_CPU_DESCRIPTOR_HANDLE, }, } +impl<'a> From> for D3D12InputImage { + fn from(value: InterfaceRef<'a, ID3D12Resource>) -> Self { + Self::Managed(unsafe { std::mem::transmute(value) }) + } +} + #[derive(Clone)] pub(crate) enum InputDescriptor { Owned(D3D12DescriptorHeapSlot), @@ -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, chain: &mut FilterChainD3D12, ) -> error::Result { - 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, device: &ID3D12Device, heap: &mut D3D12DescriptorHeap, ) -> error::Result { @@ -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, 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, + image: ManuallyDrop, descriptor: D3D12_CPU_DESCRIPTOR_HANDLE, filter: FilterMode, wrap_mode: WrapMode,