From 65bacb2f4a100d7046937d68579bc42d0b942d56 Mon Sep 17 00:00:00 2001 From: MikhailGorobets Date: Thu, 12 Mar 2026 11:20:55 +0600 Subject: [PATCH 1/3] Implement SuperResolution API and factory infrastructure --- CMakeLists.txt | 14 +- Graphics/CMakeLists.txt | 5 +- Graphics/SuperResolution/CMakeLists.txt | 13 +- .../include/SuperResolutionBase.hpp | 91 +++++++++ .../include/SuperResolutionInternal.hpp | 47 +++++ .../interface/SuperResolution.h | 29 ++- .../interface/SuperResolutionFactory.h | 22 ++- .../interface/SuperResolutionFactoryLoader.h | 34 ++-- .../SuperResolution/src/SuperResolution.def | 2 +- .../src/SuperResolutionFactory.cpp | 176 ++++++++++++------ Tests/DiligentCoreAPITest/CMakeLists.txt | 9 + .../src/c_interface/SuperResolution_C_Test.c | 85 +++++++++ .../SuperResolutionFactoryH_test.c | 7 +- .../SuperResolution/SuperResolutionH_test.c | 1 + 14 files changed, 425 insertions(+), 110 deletions(-) create mode 100644 Graphics/SuperResolution/include/SuperResolutionBase.hpp create mode 100644 Graphics/SuperResolution/include/SuperResolutionInternal.hpp create mode 100644 Tests/DiligentCoreAPITest/src/c_interface/SuperResolution_C_Test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a06ee52baa..980206ca2e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ set(VULKAN_SUPPORTED FALSE CACHE INTERNAL "Vulkan is not supported") set(METAL_SUPPORTED FALSE CACHE INTERNAL "Metal is not supported") set(WEBGPU_SUPPORTED FALSE CACHE INTERNAL "WebGPU is not supported") set(ARCHIVER_SUPPORTED FALSE CACHE INTERNAL "Archiver is not supported") +set(SUPER_RESOLUTION_SUPPORTED FALSE CACHE INTERNAL "Super resolution is not supported") set(DILIGENT_CORE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "DiligentCore module source directory") @@ -180,10 +181,11 @@ if(MINGW) endif() if(PLATFORM_WIN32) - set(GL_SUPPORTED TRUE CACHE INTERNAL "OpenGL is supported on Win32 platform") - set(VULKAN_SUPPORTED TRUE CACHE INTERNAL "Vulkan is supported on Win32 platform") - set(WEBGPU_SUPPORTED TRUE CACHE INTERNAL "WebGPU is supported on Win32 platform") - set(ARCHIVER_SUPPORTED TRUE CACHE INTERNAL "Archiver is supported on Win32 platform") + set(GL_SUPPORTED TRUE CACHE INTERNAL "OpenGL is supported on Win32 platform") + set(VULKAN_SUPPORTED TRUE CACHE INTERNAL "Vulkan is supported on Win32 platform") + set(WEBGPU_SUPPORTED TRUE CACHE INTERNAL "WebGPU is supported on Win32 platform") + set(ARCHIVER_SUPPORTED TRUE CACHE INTERNAL "Archiver is supported on Win32 platform") + set(SUPER_RESOLUTION_SUPPORTED TRUE CACHE INTERNAL "Super resolution is supported on Win32 platform") target_compile_definitions(Diligent-PublicBuildSettings INTERFACE PLATFORM_WIN32=1) elseif(PLATFORM_UNIVERSAL_WINDOWS) set(ARCHIVER_SUPPORTED TRUE CACHE INTERNAL "Archiver is supported on Universal Windows platform") @@ -303,6 +305,7 @@ else() option(DILIGENT_NO_WEBGPU "Disable WebGPU backend" ON) endif() option(DILIGENT_NO_ARCHIVER "Do not build archiver" OFF) +option(DILIGENT_NO_SUPER_RESOLUTION "Do not build super resolution" OFF) option(DILIGENT_EMSCRIPTEN_STRIP_DEBUG_INFO "Strip debug information from WebAsm binaries" OFF) @@ -329,6 +332,9 @@ endif() if(${DILIGENT_NO_ARCHIVER}) set(ARCHIVER_SUPPORTED FALSE CACHE INTERNAL "Archiver is forcibly disabled") endif() +if(${DILIGENT_NO_SUPER_RESOLUTION}) + set(SUPER_RESOLUTION_SUPPORTED FALSE CACHE INTERNAL "Super resolution is forcibly disabled") +endif() if(NOT (${D3D11_SUPPORTED} OR ${D3D12_SUPPORTED} OR ${GL_SUPPORTED} OR ${GLES_SUPPORTED} OR ${VULKAN_SUPPORTED} OR ${METAL_SUPPORTED} OR ${WEBGPU_SUPPORTED})) message(FATAL_ERROR "No rendering backends are select to build") diff --git a/Graphics/CMakeLists.txt b/Graphics/CMakeLists.txt index 3cebd8ad10..49e32b169d 100644 --- a/Graphics/CMakeLists.txt +++ b/Graphics/CMakeLists.txt @@ -56,4 +56,7 @@ if(ARCHIVER_SUPPORTED) endif() add_subdirectory(GraphicsTools) -add_subdirectory(SuperResolution) + +if(SUPER_RESOLUTION_SUPPORTED) + add_subdirectory(SuperResolution) +endif() diff --git a/Graphics/SuperResolution/CMakeLists.txt b/Graphics/SuperResolution/CMakeLists.txt index 6bae288a20..8ad4e3314a 100644 --- a/Graphics/SuperResolution/CMakeLists.txt +++ b/Graphics/SuperResolution/CMakeLists.txt @@ -1,10 +1,12 @@ -cmake_minimum_required (VERSION 3.10) +cmake_minimum_required (VERSION 3.11) include(../../BuildTools/CMake/BuildUtils.cmake) project(Diligent-SuperResolution CXX) set(INCLUDE + include/SuperResolutionBase.hpp + include/SuperResolutionInternal.hpp ) set(INTERFACE @@ -29,6 +31,7 @@ set(DLL_SOURCE add_library(Diligent-SuperResolutionInterface INTERFACE) target_link_libraries (Diligent-SuperResolutionInterface INTERFACE Diligent-GraphicsEngineInterface) target_include_directories(Diligent-SuperResolutionInterface INTERFACE interface) +target_compile_definitions(Diligent-SuperResolutionInterface INTERFACE SUPER_RESOLUTION_SUPPORTED=1) add_library(Diligent-SuperResolution-static STATIC ${SOURCE} ${INTERFACE} ${INCLUDE} @@ -45,17 +48,21 @@ endif() target_include_directories(Diligent-SuperResolution-static PRIVATE include + ../GraphicsEngine/include + ../GraphicsEngineD3DBase/include + ../GraphicsEngineNextGenBase/include ) target_compile_definitions(Diligent-SuperResolution-shared PUBLIC DILIGENT_SUPER_RESOLUTION_SHARED=1) - target_link_libraries(Diligent-SuperResolution-static PUBLIC Diligent-SuperResolutionInterface PRIVATE Diligent-BuildSettings Diligent-Common + Diligent-GraphicsAccessories + Diligent-ShaderTools ) if(D3D12_SUPPORTED) @@ -86,7 +93,7 @@ endif() if (MINGW_BUILD) # Restrict export to GetSuperResolutionFactory file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/export.map - "{ global: *GetSuperResolutionFactory*; local: *; };" + "{ global: *CreateSuperResolutionFactory*; local: *; };" ) target_link_options(Diligent-SuperResolution-shared PRIVATE LINKER:--version-script=export.map) endif() diff --git a/Graphics/SuperResolution/include/SuperResolutionBase.hpp b/Graphics/SuperResolution/include/SuperResolutionBase.hpp new file mode 100644 index 0000000000..af29b6f52d --- /dev/null +++ b/Graphics/SuperResolution/include/SuperResolutionBase.hpp @@ -0,0 +1,91 @@ +/* + * Copyright 2026 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "ObjectBase.hpp" +#include "SuperResolution.h" + +#include +#include + +namespace Diligent +{ + +class SuperResolutionBase : public ObjectBase +{ +public: + using TBase = ObjectBase; + + SuperResolutionBase(IReferenceCounters* pRefCounters, + const SuperResolutionDesc& Desc) : + TBase{pRefCounters}, + m_Desc{Desc} + { + if (Desc.Name != nullptr) + { + m_Name = Desc.Name; + m_Desc.Name = m_Name.c_str(); + } + } + + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_SuperResolution, TBase) + + virtual const SuperResolutionDesc& DILIGENT_CALL_TYPE GetDesc() const override final + { + return m_Desc; + } + + virtual void DILIGENT_CALL_TYPE GetJitterOffset(Uint32 Index, float* pJitterX, float* pJitterY) const override final + { + DEV_CHECK_ERR(pJitterX != nullptr && pJitterY != nullptr, "pJitterX and pJitterY must not be null"); + + if (!m_JitterPattern.empty()) + { + const Uint32 WrappedIndex = Index % static_cast(m_JitterPattern.size()); + *pJitterX = m_JitterPattern[WrappedIndex].X; + *pJitterY = m_JitterPattern[WrappedIndex].Y; + } + else + { + *pJitterX = 0.0f; + *pJitterY = 0.0f; + } + } + +protected: + struct JitterOffset + { + float X = 0.0f; + float Y = 0.0f; + }; + + SuperResolutionDesc m_Desc; + std::string m_Name; + std::vector m_JitterPattern; +}; + +} // namespace Diligent diff --git a/Graphics/SuperResolution/include/SuperResolutionInternal.hpp b/Graphics/SuperResolution/include/SuperResolutionInternal.hpp new file mode 100644 index 0000000000..8ce66908fa --- /dev/null +++ b/Graphics/SuperResolution/include/SuperResolutionInternal.hpp @@ -0,0 +1,47 @@ +/* + * Copyright 2026 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "SuperResolutionFactory.h" + +#include +#include + +namespace Diligent +{ + +enum SUPER_RESOLUTION_BACKEND : Uint8 +{ + SUPER_RESOLUTION_BACKEND_D3D12_DSR, + SUPER_RESOLUTION_BACKEND_METAL_FX, + SUPER_RESOLUTION_BACKEND_SOFTWARE, + SUPER_RESOLUTION_BACKEND_COUNT +}; + +using SuperResolutionVariants = std::array, SUPER_RESOLUTION_BACKEND_COUNT>; + +} // namespace Diligent diff --git a/Graphics/SuperResolution/interface/SuperResolution.h b/Graphics/SuperResolution/interface/SuperResolution.h index 9cee8356bd..460229f4d8 100644 --- a/Graphics/SuperResolution/interface/SuperResolution.h +++ b/Graphics/SuperResolution/interface/SuperResolution.h @@ -29,7 +29,7 @@ /// \file /// Defines Diligent::ISuperResolution interface and related data structures -#include "../../GraphicsEngine/interface/DeviceObject.h" +#include "../../../Primitives/interface/Object.h" #include "../../GraphicsEngine/interface/GraphicsTypes.h" #include "../../GraphicsEngine/interface/TextureView.h" #include "../../GraphicsEngine/interface/DeviceContext.h" @@ -62,7 +62,10 @@ DEFINE_FLAG_ENUM_OPERATORS(SUPER_RESOLUTION_FLAGS) /// This structure describes the super resolution upscaler object and is part of the creation /// parameters given to ISuperResolutionFactory::CreateSuperResolution(). -struct SuperResolutionDesc DILIGENT_DERIVE(DeviceObjectAttribs) +struct SuperResolutionDesc +{ + /// Object name. + const Char* Name DEFAULT_INITIALIZER(nullptr); /// Unique identifier of the super resolution variant to create. /// @@ -107,13 +110,6 @@ struct SuperResolutionDesc DILIGENT_DERIVE(DeviceObjectAttribs) /// Optional. Used for temporal upscaling to guide the denoiser for areas with inaccurate motion information (e.g., alpha-blended objects). TEXTURE_FORMAT ReactiveMaskFormat DEFAULT_INITIALIZER(TEX_FORMAT_UNKNOWN); - /// Ignore history mask texture format. - /// - /// Optional. Used for temporal upscaling to indicate regions where temporal history - /// should be completely discarded (binary mask: 0 = use history, 1 = ignore history). - /// Unlike the reactive mask which provides proportional control, this is a binary decision. - TEXTURE_FORMAT IgnoreHistoryMaskFormat DEFAULT_INITIALIZER(TEX_FORMAT_UNKNOWN); - /// Exposure scale texture format. /// /// Optional. When auto-exposure is disabled, specifies the format of the 1x1 exposure @@ -178,7 +174,7 @@ struct ExecuteSuperResolutionAttribs /// where temporal history should be completely discarded. /// Unlike the reactive mask which provides proportional control, /// this is a binary decision (discard or keep). - /// Only used when SuperResolutionDesc::IgnoreHistoryMaskFormat != TEX_FORMAT_UNKNOWN. + /// Format must be TEX_FORMAT_R8_UINT. ITextureView* pIgnoreHistoryMaskTextureSRV DEFAULT_INITIALIZER(nullptr); /// Jitter offset X applied to the projection matrix (in pixels). @@ -263,8 +259,8 @@ typedef struct ExecuteSuperResolutionAttribs ExecuteSuperResolutionAttribs; #define DILIGENT_INTERFACE_NAME ISuperResolution #include "../../../Primitives/interface/DefineInterfaceHelperMacros.h" -#define ISuperResolutionInclusiveMethods \ - IDeviceObjectInclusiveMethods; \ +#define ISuperResolutionInclusiveMethods \ + IObjectInclusiveMethods; \ ISuperResolutionMethods SuperResolution /// Super resolution upscaler interface. @@ -272,12 +268,10 @@ typedef struct ExecuteSuperResolutionAttribs ExecuteSuperResolutionAttribs; /// The super resolution object encapsulates a hardware-accelerated or software-based super resolution /// effect (e.g., MetalFX on Metal, DirectSR on D3D12). /// It is created via ISuperResolutionFactory::CreateSuperResolution(). -DILIGENT_BEGIN_INTERFACE(ISuperResolution, IDeviceObject) +DILIGENT_BEGIN_INTERFACE(ISuperResolution, IObject) { -#if DILIGENT_CPP_INTERFACE /// Returns the super resolution description used to create the object. - virtual const SuperResolutionDesc& METHOD(GetDesc)() const override = 0; -#endif + VIRTUAL const SuperResolutionDesc REF METHOD(GetDesc)(THIS) CONST PURE; /// Returns the optimal jitter offset for the given frame index. @@ -315,8 +309,7 @@ DILIGENT_END_INTERFACE #if DILIGENT_C_INTERFACE // clang-format off -# define ISuperResolution_GetDesc(This) (const struct SuperResolutionDesc*)IDeviceObject_GetDesc(This) - +# define ISuperResolution_GetDesc(This) CALL_IFACE_METHOD(SuperResolution, GetDesc, This) # define ISuperResolution_GetJitterOffset(This, ...) CALL_IFACE_METHOD(SuperResolution, GetJitterOffset, This, __VA_ARGS__) # define ISuperResolution_Execute(This, ...) CALL_IFACE_METHOD(SuperResolution, Execute, This, __VA_ARGS__) diff --git a/Graphics/SuperResolution/interface/SuperResolutionFactory.h b/Graphics/SuperResolution/interface/SuperResolutionFactory.h index 4f3d1e56cc..a79369a7dc 100644 --- a/Graphics/SuperResolution/interface/SuperResolutionFactory.h +++ b/Graphics/SuperResolution/interface/SuperResolutionFactory.h @@ -233,11 +233,14 @@ typedef struct SuperResolutionSourceSettingsAttribs SuperResolutionSourceSetting // clang-format off /// SuperResolution factory interface +/// +/// The factory is created per render device using CreateSuperResolutionFactory(). +/// It enumerates available super resolution backends, queries optimal settings, +/// and creates upscaler instances for the device it was created with. DILIGENT_BEGIN_INTERFACE(ISuperResolutionFactory, IObject) { - /// Enumerates the supported super resolution variants for the given render device. + /// Enumerates the supported super resolution variants. - /// \param [in] pDevice - Render device to query the supported super resolution variants for. /// \param [in, out] NumVariants - Number of super resolution variants. If `Variants` is null, this /// parameter is used to return the number of supported variants. /// If `Variants` is not null, this parameter should contain the maximum number @@ -246,14 +249,12 @@ DILIGENT_BEGIN_INTERFACE(ISuperResolutionFactory, IObject) /// \param [out] Variants - Array to receive the supported super resolution variants. /// Each variant is described by SuperResolutionInfo structure. VIRTUAL void METHOD(EnumerateVariants)(THIS_ - IRenderDevice* pDevice, Uint32 REF NumVariants, SuperResolutionInfo* Variants) PURE; /// Returns the optimal source (input) settings for super resolution upscaling. - /// \param [in] pDevice - Render device to query the optimal source settings for. /// \param [in] Attribs - Attributes, see Diligent::SuperResolutionSourceSettingsAttribs for details. /// \param [out] Settings - On success, receives the optimal source settings, /// see Diligent::SuperResolutionSourceSettings for details. @@ -262,14 +263,12 @@ DILIGENT_BEGIN_INTERFACE(ISuperResolutionFactory, IObject) /// Use this method to determine the optimal render resolution before creating /// the upscaler object. VIRTUAL void METHOD(GetSourceSettings)(THIS_ - IRenderDevice* pDevice, const SuperResolutionSourceSettingsAttribs REF Attribs, SuperResolutionSourceSettings REF Settings) CONST PURE; - /// Creates a new upscaler object. + /// Creates a new upscaler object. - /// \param [in] pDevice - Render device to create the upscaler for. /// \param [in] Desc - Super resolution upscaler description, see Diligent::SuperResolutionDesc for details. /// \param [out] ppUpscaler - Address of the memory location where a pointer to the /// super resolution upscaler interface will be written. @@ -279,7 +278,6 @@ DILIGENT_BEGIN_INTERFACE(ISuperResolutionFactory, IObject) /// \remarks On backends that don't support hardware upscaling, the method will /// return nullptr. VIRTUAL void METHOD(CreateSuperResolution)(THIS_ - IRenderDevice* pDevice, const SuperResolutionDesc REF Desc, ISuperResolution** ppUpscaler) PURE; @@ -324,4 +322,12 @@ DILIGENT_END_INTERFACE #endif +/// Creates a super resolution factory for the specified render device. + +/// \param [in] pDevice - Render device to create the factory for. +/// \param [out] ppFactory - Address of the memory location where a pointer to the +/// super resolution factory interface will be written. +void DILIGENT_GLOBAL_FUNCTION(CreateSuperResolutionFactory)(IRenderDevice* pDevice, + ISuperResolutionFactory** ppFactory); + DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/SuperResolution/interface/SuperResolutionFactoryLoader.h b/Graphics/SuperResolution/interface/SuperResolutionFactoryLoader.h index 2da0dff36f..8c98f2c5ff 100644 --- a/Graphics/SuperResolution/interface/SuperResolutionFactoryLoader.h +++ b/Graphics/SuperResolution/interface/SuperResolutionFactoryLoader.h @@ -44,41 +44,45 @@ DILIGENT_BEGIN_NAMESPACE(Diligent) -typedef struct ISuperResolutionFactory* (*GetSuperResolutionFactoryType)(); +typedef void (*CreateSuperResolutionFactoryType)(IRenderDevice* pDevice, ISuperResolutionFactory** ppFactory); #if DILIGENT_SUPER_RESOLUTION_EXPLICIT_LOAD -inline GetSuperResolutionFactoryType DILIGENT_GLOBAL_FUNCTION(LoadSuperResolutionFactory)() +inline CreateSuperResolutionFactoryType DILIGENT_GLOBAL_FUNCTION(LoadSuperResolutionFactory)() { - static GetSuperResolutionFactoryType GetFactoryFunc = NULL; - if (GetFactoryFunc == NULL) + static CreateSuperResolutionFactoryType CreateFactoryFunc = NULL; + if (CreateFactoryFunc == NULL) { - GetFactoryFunc = (GetSuperResolutionFactoryType)LoadEngineDll("SuperResolution", "GetSuperResolutionFactory"); + CreateFactoryFunc = (CreateSuperResolutionFactoryType)LoadEngineDll("SuperResolution", "CreateSuperResolutionFactory"); } - return GetFactoryFunc; + return CreateFactoryFunc; } #else API_QUALIFIER -struct ISuperResolutionFactory* DILIGENT_GLOBAL_FUNCTION(GetSuperResolutionFactory)(); +void DILIGENT_GLOBAL_FUNCTION(CreateSuperResolutionFactory)(IRenderDevice* pDevice, + ISuperResolutionFactory** ppFactory); #endif -/// Loads the SuperResolution implementation DLL if necessary and returns the SuperResolution factory. -inline struct ISuperResolutionFactory* DILIGENT_GLOBAL_FUNCTION(LoadAndGetSuperResolutionFactory)() +/// Loads the SuperResolution implementation DLL if necessary and creates a SuperResolution factory +/// for the specified render device. +inline void DILIGENT_GLOBAL_FUNCTION(LoadAndCreateSuperResolutionFactory)(IRenderDevice* pDevice, + ISuperResolutionFactory** ppFactory) { - GetSuperResolutionFactoryType GetFactoryFunc = NULL; + CreateSuperResolutionFactoryType CreateFactoryFunc = NULL; #if DILIGENT_SUPER_RESOLUTION_EXPLICIT_LOAD - GetFactoryFunc = DILIGENT_GLOBAL_FUNCTION(LoadSuperResolutionFactory)(); - if (GetFactoryFunc == NULL) + CreateFactoryFunc = DILIGENT_GLOBAL_FUNCTION(LoadSuperResolutionFactory)(); + if (CreateFactoryFunc == NULL) { - return NULL; + *ppFactory = NULL; + return; } #else - GetFactoryFunc = DILIGENT_GLOBAL_FUNCTION(GetSuperResolutionFactory); + CreateFactoryFunc = DILIGENT_GLOBAL_FUNCTION(CreateSuperResolutionFactory); #endif - return GetFactoryFunc(); + CreateFactoryFunc(pDevice, ppFactory); } DILIGENT_END_NAMESPACE // namespace Diligent diff --git a/Graphics/SuperResolution/src/SuperResolution.def b/Graphics/SuperResolution/src/SuperResolution.def index 8395d1fb20..ffa1fdd26d 100644 --- a/Graphics/SuperResolution/src/SuperResolution.def +++ b/Graphics/SuperResolution/src/SuperResolution.def @@ -1,2 +1,2 @@ EXPORTS - GetSuperResolutionFactory=Diligent_GetSuperResolutionFactory \ No newline at end of file + CreateSuperResolutionFactory=Diligent_CreateSuperResolutionFactory diff --git a/Graphics/SuperResolution/src/SuperResolutionFactory.cpp b/Graphics/SuperResolution/src/SuperResolutionFactory.cpp index fa235cf722..cab5c2f3c2 100644 --- a/Graphics/SuperResolution/src/SuperResolutionFactory.cpp +++ b/Graphics/SuperResolution/src/SuperResolutionFactory.cpp @@ -26,9 +26,12 @@ #include "SuperResolutionFactory.h" #include "SuperResolutionFactoryLoader.h" -#include "DummyReferenceCounters.hpp" +#include "ObjectBase.hpp" +#include "RefCntAutoPtr.hpp" #include "EngineMemory.h" #include "PlatformDebug.hpp" +#include "DebugUtilities.hpp" +#include "SuperResolutionInternal.hpp" namespace Diligent { @@ -36,87 +39,134 @@ namespace Diligent namespace { -class SuperResolutionFactoryImpl final : public ISuperResolutionFactory +class SuperResolutionFactoryImpl final : public ObjectBase { public: - static SuperResolutionFactoryImpl* GetInstance() - { - static SuperResolutionFactoryImpl TheFactory; - return &TheFactory; - } + using TBase = ObjectBase; - SuperResolutionFactoryImpl() : - m_RefCounters{*this} - {} + SuperResolutionFactoryImpl(IReferenceCounters* pRefCounters, IRenderDevice* pDevice); - virtual void DILIGENT_CALL_TYPE QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) override final; + IMPLEMENT_QUERY_INTERFACE_IN_PLACE(IID_SuperResolutionFactory, TBase) - virtual ReferenceCounterValueType DILIGENT_CALL_TYPE AddRef() override final - { - return m_RefCounters.AddStrongRef(); - } + virtual void DILIGENT_CALL_TYPE EnumerateVariants(Uint32& NumVariants, SuperResolutionInfo* Variants) override final; - virtual ReferenceCounterValueType DILIGENT_CALL_TYPE Release() override final - { - return m_RefCounters.ReleaseStrongRef(); - } + virtual void DILIGENT_CALL_TYPE GetSourceSettings(const SuperResolutionSourceSettingsAttribs& Attribs, SuperResolutionSourceSettings& Settings) const override final; - virtual IReferenceCounters* DILIGENT_CALL_TYPE GetReferenceCounters() const override final - { - return const_cast(static_cast(&m_RefCounters)); - } - - virtual void DILIGENT_CALL_TYPE EnumerateVariants(IRenderDevice* pDevice, Uint32& NumVariants, SuperResolutionInfo* Variants) override final; - - virtual void DILIGENT_CALL_TYPE GetSourceSettings(IRenderDevice* pDevice, - const SuperResolutionSourceSettingsAttribs& Attribs, - SuperResolutionSourceSettings& Settings) const override final; + virtual void DILIGENT_CALL_TYPE CreateSuperResolution(const SuperResolutionDesc& Desc, ISuperResolution** ppUpscaler) override final; - virtual void DILIGENT_CALL_TYPE CreateSuperResolution(IRenderDevice* pDevice, - const SuperResolutionDesc& Desc, - ISuperResolution** ppUpscaler) override final; - - virtual void DILIGENT_CALL_TYPE - SetMessageCallback(DebugMessageCallbackType MessageCallback) const override final; + virtual void DILIGENT_CALL_TYPE SetMessageCallback(DebugMessageCallbackType MessageCallback) const override final; virtual void DILIGENT_CALL_TYPE SetBreakOnError(bool BreakOnError) const override final; virtual void DILIGENT_CALL_TYPE SetMemoryAllocator(IMemoryAllocator* pAllocator) const override final; private: - DummyReferenceCounters m_RefCounters; + void PopulateVariants(); + +private: + SUPER_RESOLUTION_BACKEND FindVariant(const INTERFACE_ID& VariantId) const; + + RefCntAutoPtr m_pDevice; + SuperResolutionVariants m_Variants{}; }; -void SuperResolutionFactoryImpl::QueryInterface(const INTERFACE_ID& IID, IObject** ppInterface) +SuperResolutionFactoryImpl::SuperResolutionFactoryImpl(IReferenceCounters* pRefCounters, IRenderDevice* pDevice) : + TBase{pRefCounters}, + m_pDevice{pDevice} { - if (ppInterface == nullptr) - return; + PopulateVariants(); +} - *ppInterface = nullptr; - if (IID == IID_Unknown || IID == IID_SuperResolutionFactory) +void SuperResolutionFactoryImpl::PopulateVariants() +{ +} + +SUPER_RESOLUTION_BACKEND SuperResolutionFactoryImpl::FindVariant(const INTERFACE_ID& VariantId) const +{ + for (Uint32 BackendIdx = 0; BackendIdx < SUPER_RESOLUTION_BACKEND_COUNT; ++BackendIdx) { - *ppInterface = this; - (*ppInterface)->AddRef(); + for (const auto& Info : m_Variants[BackendIdx]) + { + if (Info.VariantId == VariantId) + return static_cast(BackendIdx); + } } + return SUPER_RESOLUTION_BACKEND_COUNT; } -void SuperResolutionFactoryImpl::EnumerateVariants(IRenderDevice* pDevice, Uint32& NumVariants, SuperResolutionInfo* Variants) +void SuperResolutionFactoryImpl::EnumerateVariants(Uint32& NumVariants, SuperResolutionInfo* Variants) { - NumVariants = 0; + Uint32 Count = 0; + for (Uint32 BackendIdx = 0; BackendIdx < SUPER_RESOLUTION_BACKEND_COUNT; ++BackendIdx) + Count += static_cast(m_Variants[BackendIdx].size()); + + if (Variants == nullptr) + { + NumVariants = Count; + return; + } + + const Uint32 MaxVariants = NumVariants; + NumVariants = 0; + for (Uint32 BackendIdx = 0; BackendIdx < SUPER_RESOLUTION_BACKEND_COUNT; ++BackendIdx) + { + for (const auto& Info : m_Variants[BackendIdx]) + { + if (NumVariants >= MaxVariants) + break; + Variants[NumVariants++] = Info; + } + } } -void SuperResolutionFactoryImpl::GetSourceSettings(IRenderDevice* pDevice, - const SuperResolutionSourceSettingsAttribs& Attribs, - SuperResolutionSourceSettings& Settings) const +void SuperResolutionFactoryImpl::GetSourceSettings(const SuperResolutionSourceSettingsAttribs& Attribs, SuperResolutionSourceSettings& Settings) const { Settings = {}; + + const auto Backend = FindVariant(Attribs.VariantId); + if (Backend == SUPER_RESOLUTION_BACKEND_COUNT) + { + LOG_WARNING_MESSAGE("Super resolution variant not found for the specified VariantId"); + return; + } + + switch (Backend) + { + default: + LOG_WARNING_MESSAGE("Unknown super resolution backend"); + break; + } } -void SuperResolutionFactoryImpl::CreateSuperResolution(IRenderDevice* pDevice, - const SuperResolutionDesc& Desc, - ISuperResolution** ppUpscaler) +void SuperResolutionFactoryImpl::CreateSuperResolution(const SuperResolutionDesc& Desc, ISuperResolution** ppUpscaler) { + DEV_CHECK_ERR(ppUpscaler != nullptr, "ppUpscaler must not be null"); + if (ppUpscaler == nullptr) + return; + + *ppUpscaler = nullptr; + + const auto Backend = FindVariant(Desc.VariantId); + if (Backend == SUPER_RESOLUTION_BACKEND_COUNT) + { + LOG_ERROR_MESSAGE("Super resolution variant not found for the specified VariantId. Call EnumerateVariants() to get valid variant IDs."); + return; + } + + try + { + switch (Backend) + { + default: + LOG_ERROR_MESSAGE("Unknown super resolution backend"); + break; + } + } + catch (...) + { + LOG_ERROR("Failed to create super resolution upscaler '", (Desc.Name ? Desc.Name : ""), "'"); + } } void SuperResolutionFactoryImpl::SetMessageCallback(DebugMessageCallbackType MessageCallback) const @@ -137,10 +187,23 @@ void SuperResolutionFactoryImpl::SetMemoryAllocator(IMemoryAllocator* pAllocator } // namespace -API_QUALIFIER -ISuperResolutionFactory* GetSuperResolutionFactory() +API_QUALIFIER void CreateSuperResolutionFactory(IRenderDevice* pDevice, ISuperResolutionFactory** ppFactory) { - return SuperResolutionFactoryImpl::GetInstance(); + DEV_CHECK_ERR(ppFactory != nullptr, "ppFactory must not be null"); + if (ppFactory == nullptr) + return; + + *ppFactory = nullptr; + + try + { + auto* pFactory = NEW_RC_OBJ(GetRawAllocator(), "SuperResolutionFactoryImpl instance", SuperResolutionFactoryImpl)(pDevice); + pFactory->QueryInterface(IID_SuperResolutionFactory, reinterpret_cast(ppFactory)); + } + catch (...) + { + LOG_ERROR("Failed to create super resolution factory"); + } } } // namespace Diligent @@ -148,8 +211,9 @@ ISuperResolutionFactory* GetSuperResolutionFactory() extern "C" { API_QUALIFIER - Diligent::ISuperResolutionFactory* Diligent_GetSuperResolutionFactory() + void Diligent_CreateSuperResolutionFactory(Diligent::IRenderDevice* pDevice, + Diligent::ISuperResolutionFactory** ppFactory) { - return Diligent::GetSuperResolutionFactory(); + Diligent::CreateSuperResolutionFactory(pDevice, ppFactory); } } diff --git a/Tests/DiligentCoreAPITest/CMakeLists.txt b/Tests/DiligentCoreAPITest/CMakeLists.txt index ef5c7fe764..89b20f6dda 100644 --- a/Tests/DiligentCoreAPITest/CMakeLists.txt +++ b/Tests/DiligentCoreAPITest/CMakeLists.txt @@ -35,6 +35,11 @@ if(NOT ARCHIVER_SUPPORTED) list(REMOVE_ITEM SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/RenderStateCacheTest.cpp) endif() +if(NOT SUPER_RESOLUTION_SUPPORTED) + list(REMOVE_ITEM SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/SuperResolutionTest.cpp) + list(REMOVE_ITEM SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/src/c_interface/SuperResolution_C_Test.c) +endif() + if(D3D11_SUPPORTED) file(GLOB D3D11_SOURCE LIST_DIRECTORIES false src/D3D11/*) file(GLOB D3D11_INCLUDE LIST_DIRECTORIES false include/D3D11/*) @@ -114,6 +119,10 @@ PRIVATE Diligent-ShaderTools ) +if(SUPER_RESOLUTION_SUPPORTED) + target_link_libraries(DiligentCoreAPITest PRIVATE Diligent-SuperResolution-static) +endif() + if(TARGET Diligent-HLSL2GLSLConverterLib) target_link_libraries(DiligentCoreAPITest PRIVATE Diligent-HLSL2GLSLConverterLib) endif() diff --git a/Tests/DiligentCoreAPITest/src/c_interface/SuperResolution_C_Test.c b/Tests/DiligentCoreAPITest/src/c_interface/SuperResolution_C_Test.c new file mode 100644 index 0000000000..b7a5c40fd0 --- /dev/null +++ b/Tests/DiligentCoreAPITest/src/c_interface/SuperResolution_C_Test.c @@ -0,0 +1,85 @@ +/* + * Copyright 2026 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "SuperResolution.h" +#include "SuperResolutionFactory.h" + +int TestObjectCInterface(struct IObject* pObject); + +int TestSuperResolutionCInterface(struct ISuperResolution* pUpscaler) +{ + IObject* pUnknown = NULL; + ReferenceCounterValueType RefCnt1 = 0, RefCnt2 = 0; + + const SuperResolutionDesc* pUpscalerDesc = NULL; + float JitterX = 0.0f; + float JitterY = 0.0f; + + int num_errors = + TestObjectCInterface((struct IObject*)pUpscaler); + + IObject_QueryInterface(pUpscaler, &IID_Unknown, &pUnknown); + if (pUnknown != NULL) + IObject_Release(pUnknown); + else + ++num_errors; + + RefCnt1 = IObject_AddRef(pUpscaler); + if (RefCnt1 <= 1) + ++num_errors; + RefCnt2 = IObject_Release(pUpscaler); + if (RefCnt2 <= 0) + ++num_errors; + if (RefCnt2 != RefCnt1 - 1) + ++num_errors; + + pUpscalerDesc = ISuperResolution_GetDesc(pUpscaler); + if (pUpscalerDesc == NULL) + ++num_errors; + if (pUpscalerDesc->Name == NULL) + ++num_errors; + if (pUpscalerDesc->InputWidth == 0) + ++num_errors; + if (pUpscalerDesc->InputHeight == 0) + ++num_errors; + + ISuperResolution_GetJitterOffset(pUpscaler, 0, &JitterX, &JitterY); + (void)JitterX; + (void)JitterY; + + return num_errors; +} + +int TestSuperResolutionFactoryCInterface(struct ISuperResolutionFactory* pFactory) +{ + int num_errors = 0; + Uint32 NumVariants = 0; + + ISuperResolutionFactory_EnumerateVariants(pFactory, &NumVariants, NULL); + (void)NumVariants; + + return num_errors; +} diff --git a/Tests/IncludeTest/SuperResolution/SuperResolutionFactoryH_test.c b/Tests/IncludeTest/SuperResolution/SuperResolutionFactoryH_test.c index cbb14786a0..27b6a6a7d2 100644 --- a/Tests/IncludeTest/SuperResolution/SuperResolutionFactoryH_test.c +++ b/Tests/IncludeTest/SuperResolution/SuperResolutionFactoryH_test.c @@ -28,9 +28,8 @@ void TestSuperResolutionFactory_CInterface(ISuperResolutionFactory* pSuperResolutionFactory) { - IRenderDevice* pDevice = NULL; - ISuperResolutionFactory_EnumerateVariants(pSuperResolutionFactory, pDevice, (Uint32*)NULL, (SuperResolutionInfo*)NULL); - ISuperResolutionFactory_GetSourceSettings(pSuperResolutionFactory, pDevice, (const SuperResolutionSourceSettingsAttribs*)NULL, (SuperResolutionSourceSettings*)NULL); - ISuperResolutionFactory_CreateSuperResolution(pSuperResolutionFactory, pDevice, (const SuperResolutionDesc*)NULL, (ISuperResolution**)NULL); + ISuperResolutionFactory_EnumerateVariants(pSuperResolutionFactory, (Uint32*)NULL, (SuperResolutionInfo*)NULL); + ISuperResolutionFactory_GetSourceSettings(pSuperResolutionFactory, (const SuperResolutionSourceSettingsAttribs*)NULL, (SuperResolutionSourceSettings*)NULL); + ISuperResolutionFactory_CreateSuperResolution(pSuperResolutionFactory, (const SuperResolutionDesc*)NULL, (ISuperResolution**)NULL); ISuperResolutionFactory_SetMessageCallback(pSuperResolutionFactory, (DebugMessageCallbackType)NULL); } diff --git a/Tests/IncludeTest/SuperResolution/SuperResolutionH_test.c b/Tests/IncludeTest/SuperResolution/SuperResolutionH_test.c index 963b72cf0a..60abe3091a 100644 --- a/Tests/IncludeTest/SuperResolution/SuperResolutionH_test.c +++ b/Tests/IncludeTest/SuperResolution/SuperResolutionH_test.c @@ -28,6 +28,7 @@ void TestSuperResolution_CInterface(ISuperResolution* pUpscaler) { + ISuperResolution_GetDesc(pUpscaler); ISuperResolution_GetJitterOffset(pUpscaler, 0, (float*)NULL, (float*)NULL); ISuperResolution_Execute(pUpscaler, (const ExecuteSuperResolutionAttribs*)NULL); } From eedebec97a9bc27a425d341bfb4d0e04cb8f7532 Mon Sep 17 00:00:00 2001 From: MikhailGorobets Date: Thu, 12 Mar 2026 11:21:24 +0600 Subject: [PATCH 2/3] Add DirectSR (D3D12) super resolution implementation --- .github/workflows/msvc_analysis.yml | 2 +- Graphics/SuperResolution/CMakeLists.txt | 15 +- .../include/SuperResolution_D3D12.hpp | 51 ++ .../src/SuperResolutionFactory.cpp | 27 ++ .../src/SuperResolution_D3D12.cpp | 446 +++++++++++++++++- Tests/DiligentCoreAPITest/CMakeLists.txt | 4 + .../src/SuperResolutionTest.cpp | 423 +++++++++++++++++ 7 files changed, 965 insertions(+), 3 deletions(-) create mode 100644 Graphics/SuperResolution/include/SuperResolution_D3D12.hpp create mode 100644 Tests/DiligentCoreAPITest/src/SuperResolutionTest.cpp diff --git a/.github/workflows/msvc_analysis.yml b/.github/workflows/msvc_analysis.yml index 1ed71e73a6..936226d88a 100644 --- a/.github/workflows/msvc_analysis.yml +++ b/.github/workflows/msvc_analysis.yml @@ -60,7 +60,7 @@ jobs: # Ruleset file that will determine what checks will be run ruleset: NativeRecommendedRules.ruleset # Paths to ignore analysis of CMake targets and includes - ignoredPaths: '${{ github.workspace }}/ThirdParty' + ignoredPaths: '${{ github.workspace }}/ThirdParty;${{ env.DILIGENT_BUILD_DIR }}/_deps' # Upload SARIF file to GitHub Code Scanning Alerts - name: Upload SARIF to GitHub diff --git a/Graphics/SuperResolution/CMakeLists.txt b/Graphics/SuperResolution/CMakeLists.txt index 8ad4e3314a..e817e953b8 100644 --- a/Graphics/SuperResolution/CMakeLists.txt +++ b/Graphics/SuperResolution/CMakeLists.txt @@ -4,6 +4,18 @@ include(../../BuildTools/CMake/BuildUtils.cmake) project(Diligent-SuperResolution CXX) +if(D3D12_SUPPORTED) + # Fetch DirectSR headers + FetchContent_DeclareShallowGit(DirectSR-Headers + GIT_REPOSITORY https://github.com/MikhailGorobets/DirectSR-Headers.git + GIT_TAG dev + ) + FetchContent_MakeAvailable(DirectSR-Headers) + if(TARGET DirectSR-AgilitySDK) + set_target_properties(DirectSR-AgilitySDK PROPERTIES FOLDER DiligentCore/ThirdParty) + endif() +endif() + set(INCLUDE include/SuperResolutionBase.hpp include/SuperResolutionInternal.hpp @@ -20,6 +32,7 @@ set(SOURCE ) if(D3D12_SUPPORTED) + list(APPEND INCLUDE include/SuperResolution_D3D12.hpp) list(APPEND SOURCE src/SuperResolution_D3D12.cpp) endif() @@ -66,7 +79,7 @@ PRIVATE ) if(D3D12_SUPPORTED) - target_link_libraries(Diligent-SuperResolution-static PRIVATE Diligent-GraphicsEngineD3D12-static) + target_link_libraries(Diligent-SuperResolution-static PRIVATE Diligent-GraphicsEngineD3D12-static DirectSR-Headers) target_include_directories(Diligent-SuperResolution-static PRIVATE ../GraphicsEngineD3D12/include) endif() diff --git a/Graphics/SuperResolution/include/SuperResolution_D3D12.hpp b/Graphics/SuperResolution/include/SuperResolution_D3D12.hpp new file mode 100644 index 0000000000..1e04e51f72 --- /dev/null +++ b/Graphics/SuperResolution/include/SuperResolution_D3D12.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 2026 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "SuperResolutionInternal.hpp" + +#include "../../GraphicsEngineD3D12/include/pch.h" +#include + +namespace Diligent +{ + +CComPtr CreateDSRDeviceD3D12(IRenderDevice* pDevice); + +void EnumerateVariantsD3D12(IDSRDevice* pDSRDevice, + std::vector& Variants); + +void GetSourceSettingsD3D12(IDSRDevice* pDSRDevice, + const SuperResolutionSourceSettingsAttribs& Attribs, + SuperResolutionSourceSettings& Settings); + +void CreateSuperResolutionD3D12(IRenderDevice* pDevice, + IDSRDevice* pDSRDevice, + const SuperResolutionDesc& Desc, + ISuperResolution** ppUpscaler); + +} // namespace Diligent diff --git a/Graphics/SuperResolution/src/SuperResolutionFactory.cpp b/Graphics/SuperResolution/src/SuperResolutionFactory.cpp index cab5c2f3c2..c181b30887 100644 --- a/Graphics/SuperResolution/src/SuperResolutionFactory.cpp +++ b/Graphics/SuperResolution/src/SuperResolutionFactory.cpp @@ -33,6 +33,10 @@ #include "DebugUtilities.hpp" #include "SuperResolutionInternal.hpp" +#if D3D12_SUPPORTED +# include "SuperResolution_D3D12.hpp" +#endif + namespace Diligent { @@ -68,6 +72,10 @@ class SuperResolutionFactoryImpl final : public ObjectBase m_pDevice; SuperResolutionVariants m_Variants{}; + +#if D3D12_SUPPORTED + CComPtr m_pDSRDevice; +#endif }; @@ -75,11 +83,20 @@ SuperResolutionFactoryImpl::SuperResolutionFactoryImpl(IReferenceCounters* pRefC TBase{pRefCounters}, m_pDevice{pDevice} { +#if D3D12_SUPPORTED + if (m_pDevice != nullptr && m_pDevice->GetDeviceInfo().Type == RENDER_DEVICE_TYPE_D3D12) + m_pDSRDevice = CreateDSRDeviceD3D12(m_pDevice); +#endif + PopulateVariants(); } void SuperResolutionFactoryImpl::PopulateVariants() { +#if D3D12_SUPPORTED + if (m_pDSRDevice) + EnumerateVariantsD3D12(m_pDSRDevice, m_Variants[SUPER_RESOLUTION_BACKEND_D3D12_DSR]); +#endif } SUPER_RESOLUTION_BACKEND SuperResolutionFactoryImpl::FindVariant(const INTERFACE_ID& VariantId) const @@ -133,6 +150,11 @@ void SuperResolutionFactoryImpl::GetSourceSettings(const SuperResolutionSourceSe switch (Backend) { +#if D3D12_SUPPORTED + case SUPER_RESOLUTION_BACKEND_D3D12_DSR: + GetSourceSettingsD3D12(m_pDSRDevice, Attribs, Settings); + break; +#endif default: LOG_WARNING_MESSAGE("Unknown super resolution backend"); break; @@ -158,6 +180,11 @@ void SuperResolutionFactoryImpl::CreateSuperResolution(const SuperResolutionDesc { switch (Backend) { +#if D3D12_SUPPORTED + case SUPER_RESOLUTION_BACKEND_D3D12_DSR: + CreateSuperResolutionD3D12(m_pDevice, m_pDSRDevice, Desc, ppUpscaler); + break; +#endif default: LOG_ERROR_MESSAGE("Unknown super resolution backend"); break; diff --git a/Graphics/SuperResolution/src/SuperResolution_D3D12.cpp b/Graphics/SuperResolution/src/SuperResolution_D3D12.cpp index 92fcc323a6..039a02faf8 100644 --- a/Graphics/SuperResolution/src/SuperResolution_D3D12.cpp +++ b/Graphics/SuperResolution/src/SuperResolution_D3D12.cpp @@ -24,9 +24,453 @@ * of the possibility of such damages. */ -#include "SuperResolution.h" +#include "SuperResolutionBase.hpp" +#include "SuperResolution_D3D12.hpp" + +#include "RenderDeviceD3D12Impl.hpp" +#include "DeviceContextD3D12Impl.hpp" +#include "DXGITypeConversions.hpp" namespace Diligent { +CComPtr CreateDSRDeviceD3D12(IRenderDevice* pDevice) +{ + HMODULE hD3D12 = GetModuleHandleA("d3d12.dll"); + if (!hD3D12) + { + LOG_WARNING_MESSAGE("d3d12.dll is not loaded. DirectSR features will be disabled."); + return {}; + } + + using D3D12GetInterfaceProcType = HRESULT(WINAPI*)(REFCLSID, REFIID, void**); + auto pfnD3D12GetInterface = reinterpret_cast(GetProcAddress(hD3D12, "D3D12GetInterface")); + if (!pfnD3D12GetInterface) + { + LOG_WARNING_MESSAGE("D3D12GetInterface is not available. DirectSR features will be disabled."); + return {}; + } + + CComPtr pDSRFactory; + if (HRESULT hr = pfnD3D12GetInterface(CLSID_D3D12DSRDeviceFactory, IID_PPV_ARGS(&pDSRFactory)); FAILED(hr)) + { + LOG_WARNING_MESSAGE("Failed to create DirectSR device factory. HRESULT: ", hr); + return {}; + } + + ID3D12Device* pd3d12Device = ClassPtrCast(pDevice)->GetD3D12Device(); + + CComPtr pDSRDevice; + if (HRESULT hr = pDSRFactory->CreateDSRDevice(pd3d12Device, 0, IID_PPV_ARGS(&pDSRDevice)); FAILED(hr)) + { + LOG_WARNING_MESSAGE("Failed to create DirectSR device. HRESULT: ", hr); + return {}; + } + + LOG_INFO_MESSAGE("DirectSR device initialized successfully. ", pDSRDevice->GetNumSuperResVariants(), " upscaler variant(s) found."); + return pDSRDevice; +} + +namespace +{ + +static DSR_SUPERRES_CREATE_ENGINE_FLAGS SuperResolutionFlagsToDSRFlags(SUPER_RESOLUTION_FLAGS Flags) +{ + DSR_SUPERRES_CREATE_ENGINE_FLAGS DSRFlags = DSR_SUPERRES_CREATE_ENGINE_FLAG_NONE; + + if (Flags & SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE) + DSRFlags |= DSR_SUPERRES_CREATE_ENGINE_FLAG_AUTO_EXPOSURE; + if (Flags & SUPER_RESOLUTION_FLAG_ENABLE_SHARPENING) + DSRFlags |= DSR_SUPERRES_CREATE_ENGINE_FLAG_ENABLE_SHARPENING; + + return DSRFlags; +} + +class SuperResolutionD3D12 final : public SuperResolutionBase +{ +public: + SuperResolutionD3D12(IReferenceCounters* pRefCounters, + RenderDeviceD3D12Impl* pDevice, + const SuperResolutionDesc& Desc, + IDSRDevice* pDSRDevice); + + ~SuperResolutionD3D12(); + + virtual void DILIGENT_CALL_TYPE Execute(const ExecuteSuperResolutionAttribs& Attribs) override final; + +private: + RefCntAutoPtr m_pDevice; + CComPtr m_pDSREngine; + std::vector> m_DSRUpscalers; +}; + +SuperResolutionD3D12::SuperResolutionD3D12(IReferenceCounters* pRefCounters, + RenderDeviceD3D12Impl* pDevice, + const SuperResolutionDesc& Desc, + IDSRDevice* pDSRDevice) : + SuperResolutionBase{pRefCounters, Desc}, + m_pDevice{pDevice}, + m_DSRUpscalers(pDevice->GetCommandQueueCount()) +{ + + DEV_CHECK_ERR(Desc.OutputWidth > 0 && Desc.OutputHeight > 0, "Output resolution must be greater than zero"); + DEV_CHECK_ERR(Desc.OutputFormat != TEX_FORMAT_UNKNOWN, "OutputFormat must not be TEX_FORMAT_UNKNOWN"); + DEV_CHECK_ERR(Desc.ColorFormat != TEX_FORMAT_UNKNOWN, "ColorFormat must not be TEX_FORMAT_UNKNOWN"); + DEV_CHECK_ERR(Desc.InputWidth > 0 && Desc.InputHeight > 0, "InputWidth and InputHeight must be greater than zero. "); + DEV_CHECK_ERR(Desc.InputWidth <= Desc.OutputWidth && Desc.InputHeight <= Desc.OutputHeight, "Input resolution must not exceed output resolution"); + DEV_CHECK_ERR(Desc.DepthFormat != TEX_FORMAT_UNKNOWN, "DepthFormat must not be TEX_FORMAT_UNKNOWN. DirectSR upscalers are always temporal and require a depth buffer."); + DEV_CHECK_ERR(Desc.MotionFormat != TEX_FORMAT_UNKNOWN, "MotionFormat must not be TEX_FORMAT_UNKNOWN. DirectSR upscalers are always temporal and require motion vectors."); + DEV_CHECK_ERR(Desc.MotionFormat == TEX_FORMAT_RG16_FLOAT, "MotionFormat must be TEX_FORMAT_RG16_FLOAT. Got: ", GetTextureFormatAttribs(Desc.MotionFormat).Name); + DEV_CHECK_ERR((Desc.Flags & SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE) != 0 || Desc.ExposureFormat != TEX_FORMAT_UNKNOWN, + "ExposureFormat must not be TEX_FORMAT_UNKNOWN when SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE is not set. " + "Either enable auto-exposure or specify a valid ExposureFormat (e.g. TEX_FORMAT_R32_FLOAT)."); + + DEV_CHECK_ERR(pDSRDevice != nullptr, "DirectSR device is not available"); + + DSR_SUPERRES_CREATE_ENGINE_PARAMETERS CreateInfo = {}; + CreateInfo.VariantId = reinterpret_cast(Desc.VariantId); + CreateInfo.TargetFormat = TexFormatToDXGI_Format(Desc.OutputFormat); + CreateInfo.SourceColorFormat = TexFormatToDXGI_Format(Desc.ColorFormat); + CreateInfo.SourceDepthFormat = TexFormatToDXGI_Format(Desc.DepthFormat); + CreateInfo.ExposureScaleFormat = TexFormatToDXGI_Format(Desc.ExposureFormat); + CreateInfo.Flags = SuperResolutionFlagsToDSRFlags(Desc.Flags); + CreateInfo.MaxSourceSize = {Desc.InputWidth, Desc.InputHeight}; + CreateInfo.TargetSize = {Desc.OutputWidth, Desc.OutputHeight}; + + if (HRESULT hr = pDSRDevice->CreateSuperResEngine(&CreateInfo, IID_PPV_ARGS(&m_pDSREngine)); FAILED(hr)) + LOG_ERROR_AND_THROW("Failed to create DirectSR super resolution engine. HRESULT: ", hr); + + // Cache the optimal jitter pattern + { + DSR_SIZE SourceSize = {Desc.InputWidth, Desc.InputHeight}; + DSR_SIZE TargetSize = {Desc.OutputWidth, Desc.OutputHeight}; + Uint32 PatternSize = 0; + + if (HRESULT hr = m_pDSREngine->GetOptimalJitterPattern(SourceSize, TargetSize, &PatternSize, nullptr); SUCCEEDED(hr) && PatternSize > 0) + { + std::vector DSRPattern(PatternSize); + if (hr = m_pDSREngine->GetOptimalJitterPattern(SourceSize, TargetSize, &PatternSize, DSRPattern.data()); SUCCEEDED(hr)) + { + m_JitterPattern.resize(PatternSize); + for (Uint32 i = 0; i < PatternSize; ++i) + { + m_JitterPattern[i].X = DSRPattern[i].X; + m_JitterPattern[i].Y = DSRPattern[i].Y; + } + } + } + else + { + LOG_WARNING_MESSAGE("Failed to get optimal jitter pattern from DirectSR engine. HRESULT: ", hr); + } + } +} + +SuperResolutionD3D12::~SuperResolutionD3D12() = default; + +void DILIGENT_CALL_TYPE SuperResolutionD3D12::Execute(const ExecuteSuperResolutionAttribs& Attribs) +{ + DEV_CHECK_ERR(Attribs.pContext != nullptr, "Device context must not be null"); + DEV_CHECK_ERR(Attribs.pColorTextureSRV != nullptr, "Color texture SRV must not be null"); + DEV_CHECK_ERR(Attribs.pOutputTextureView != nullptr, "Output texture view must not be null"); + DEV_CHECK_ERR(Attribs.pDepthTextureSRV != nullptr, "Depth texture SRV must not be null"); + DEV_CHECK_ERR(Attribs.pMotionVectorsSRV != nullptr, "Motion vectors SRV must not be null. DirectSR upscalers are always temporal"); + DEV_CHECK_ERR((m_Desc.Flags & SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE) != 0 || Attribs.pExposureTextureSRV != nullptr, + "Exposure texture SRV must not be null when SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE is not set"); + DEV_CHECK_ERR(Attribs.CameraNear > 0, "CameraNear must be greater than zero for temporal upscaling"); + DEV_CHECK_ERR(Attribs.CameraFar > 0, "CameraFar must be greater than zero for temporal upscaling."); + DEV_CHECK_ERR(Attribs.CameraFovAngleVert > 0, "CameraFovAngleVert must be greater than zero for temporal upscaling."); + DEV_CHECK_ERR(Attribs.TimeDeltaInSeconds >= 0, "TimeDeltaInSeconds must be non-negative."); + + auto* pCtx = ClassPtrCast(Attribs.pContext); + + const SoftwareQueueIndex QueueId = pCtx->GetCommandQueueId(); + VERIFY_EXPR(static_cast(QueueId) < m_DSRUpscalers.size()); + + // Lazily create an upscaler for this queue on first use. + auto& pDSRUpscaler = m_DSRUpscalers[static_cast(QueueId)]; + if (!pDSRUpscaler) + { + m_pDevice->LockCmdQueueAndRun(QueueId, [&](ICommandQueueD3D12* pCmdQueue) { + if (HRESULT hr = m_pDSREngine->CreateUpscaler(pCmdQueue->GetD3D12CommandQueue(), IID_PPV_ARGS(&pDSRUpscaler)); FAILED(hr)) + LOG_ERROR_AND_THROW("Failed to create DirectSR upscaler for queue ", static_cast(QueueId), ". HRESULT: ", hr); + }); + } + + // Validate color texture + { + const auto& TexDesc = Attribs.pColorTextureSRV->GetTexture()->GetDesc(); + const auto& ViewDesc = Attribs.pColorTextureSRV->GetDesc(); + DEV_CHECK_ERR(ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE, + "Color texture view '", TexDesc.Name, "' must be TEXTURE_VIEW_SHADER_RESOURCE"); + DEV_CHECK_ERR(TexDesc.Width >= m_Desc.InputWidth && TexDesc.Height >= m_Desc.InputHeight, + "Color texture '", TexDesc.Name, "' dimensions (", TexDesc.Width, "x", TexDesc.Height, + ") must be at least the upscaler input resolution (", m_Desc.InputWidth, "x", m_Desc.InputHeight, ")"); + DEV_CHECK_ERR(ViewDesc.Format == m_Desc.ColorFormat, + "Color texture view '", TexDesc.Name, "' format (", GetTextureFormatAttribs(ViewDesc.Format).Name, + ") does not match the expected ColorFormat (", GetTextureFormatAttribs(m_Desc.ColorFormat).Name, ")"); + } + + // Validate output texture + { + const auto& TexDesc = Attribs.pOutputTextureView->GetTexture()->GetDesc(); + const auto& ViewDesc = Attribs.pOutputTextureView->GetDesc(); + DEV_CHECK_ERR(ViewDesc.ViewType == TEXTURE_VIEW_UNORDERED_ACCESS, + "Output texture view '", TexDesc.Name, "' must be TEXTURE_VIEW_UNORDERED_ACCESS"); + DEV_CHECK_ERR(TexDesc.Width == m_Desc.OutputWidth && TexDesc.Height == m_Desc.OutputHeight, + "Output texture '", TexDesc.Name, "' dimensions (", TexDesc.Width, "x", TexDesc.Height, + ") must match the upscaler output resolution (", m_Desc.OutputWidth, "x", m_Desc.OutputHeight, ")"); + DEV_CHECK_ERR(ViewDesc.Format == m_Desc.OutputFormat, + "Output texture view '", TexDesc.Name, "' format (", GetTextureFormatAttribs(ViewDesc.Format).Name, + ") does not match the expected OutputFormat (", GetTextureFormatAttribs(m_Desc.OutputFormat).Name, ")"); + } + + // Validate depth texture + if (Attribs.pDepthTextureSRV != nullptr) + { + const auto& TexDesc = Attribs.pDepthTextureSRV->GetTexture()->GetDesc(); + const auto& ViewDesc = Attribs.pDepthTextureSRV->GetDesc(); + DEV_CHECK_ERR(ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE, + "Depth texture view '", TexDesc.Name, "' must be TEXTURE_VIEW_SHADER_RESOURCE"); + DEV_CHECK_ERR(TexDesc.Width >= m_Desc.InputWidth && TexDesc.Height >= m_Desc.InputHeight, + "Depth texture '", TexDesc.Name, "' dimensions (", TexDesc.Width, "x", TexDesc.Height, + ") must be at least the upscaler input resolution (", m_Desc.InputWidth, "x", m_Desc.InputHeight, ")"); + DEV_CHECK_ERR(ViewDesc.Format == m_Desc.DepthFormat, + "Depth texture view '", TexDesc.Name, "' format (", GetTextureFormatAttribs(ViewDesc.Format).Name, + ") does not match the expected DepthFormat (", GetTextureFormatAttribs(m_Desc.DepthFormat).Name, ")"); + } + + // Validate motion vectors texture + if (Attribs.pMotionVectorsSRV != nullptr) + { + const auto& TexDesc = Attribs.pMotionVectorsSRV->GetTexture()->GetDesc(); + const auto& ViewDesc = Attribs.pMotionVectorsSRV->GetDesc(); + DEV_CHECK_ERR(ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE, + "Motion vectors view '", TexDesc.Name, "' must be TEXTURE_VIEW_SHADER_RESOURCE"); + DEV_CHECK_ERR(TexDesc.Width >= m_Desc.InputWidth && TexDesc.Height >= m_Desc.InputHeight, + "Motion vectors texture '", TexDesc.Name, "' dimensions (", TexDesc.Width, "x", TexDesc.Height, + ") must be at least the upscaler input resolution (", m_Desc.InputWidth, "x", m_Desc.InputHeight, ")"); + DEV_CHECK_ERR(ViewDesc.Format == TEX_FORMAT_RG16_FLOAT, + "Motion vectors view '", TexDesc.Name, "' format (", GetTextureFormatAttribs(ViewDesc.Format).Name, + ") must be TEX_FORMAT_RG16_FLOAT"); + } + + // Validate exposure texture + if (Attribs.pExposureTextureSRV != nullptr) + { + const auto& TexDesc = Attribs.pExposureTextureSRV->GetTexture()->GetDesc(); + const auto& ViewDesc = Attribs.pExposureTextureSRV->GetDesc(); + DEV_CHECK_ERR(ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE, + "Exposure texture view '", TexDesc.Name, "' must be TEXTURE_VIEW_SHADER_RESOURCE"); + DEV_CHECK_ERR(TexDesc.Width == 1 && TexDesc.Height == 1, + "Exposure texture '", TexDesc.Name, "' dimensions (", TexDesc.Width, "x", TexDesc.Height, + ") must be 1x1"); + DEV_CHECK_ERR(ViewDesc.Format == m_Desc.ExposureFormat, + "Exposure texture view '", TexDesc.Name, "' format (", GetTextureFormatAttribs(ViewDesc.Format).Name, + ") does not match the expected ExposureFormat (", GetTextureFormatAttribs(m_Desc.ExposureFormat).Name, ")"); + } + + // Validate reactive mask texture + if (Attribs.pReactiveMaskTextureSRV != nullptr) + { + const auto& TexDesc = Attribs.pReactiveMaskTextureSRV->GetTexture()->GetDesc(); + const auto& ViewDesc = Attribs.pReactiveMaskTextureSRV->GetDesc(); + DEV_CHECK_ERR(ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE, + "Reactive mask view '", TexDesc.Name, "' must be TEXTURE_VIEW_SHADER_RESOURCE"); + DEV_CHECK_ERR(m_Desc.ReactiveMaskFormat != TEX_FORMAT_UNKNOWN, + "Reactive mask texture '", TexDesc.Name, "' provided but ReactiveMaskFormat was not set in SuperResolutionDesc"); + DEV_CHECK_ERR(TexDesc.Width >= m_Desc.InputWidth && TexDesc.Height >= m_Desc.InputHeight, + "Reactive mask texture '", TexDesc.Name, "' dimensions (", TexDesc.Width, "x", TexDesc.Height, + ") must be at least the upscaler input resolution (", m_Desc.InputWidth, "x", m_Desc.InputHeight, ")"); + DEV_CHECK_ERR(ViewDesc.Format == TEX_FORMAT_R8_UNORM, + "Reactive mask view '", TexDesc.Name, "' format (", GetTextureFormatAttribs(ViewDesc.Format).Name, + ") must be TEX_FORMAT_R8_UNORM"); + } + + // Validate ignore history mask texture + if (Attribs.pIgnoreHistoryMaskTextureSRV != nullptr) + { + const auto& TexDesc = Attribs.pIgnoreHistoryMaskTextureSRV->GetTexture()->GetDesc(); + const auto& ViewDesc = Attribs.pIgnoreHistoryMaskTextureSRV->GetDesc(); + DEV_CHECK_ERR(ViewDesc.ViewType == TEXTURE_VIEW_SHADER_RESOURCE, + "Ignore history mask view '", TexDesc.Name, "' must be TEXTURE_VIEW_SHADER_RESOURCE"); + DEV_CHECK_ERR(TexDesc.Width >= m_Desc.InputWidth && TexDesc.Height >= m_Desc.InputHeight, + "Ignore history mask texture '", TexDesc.Name, "' dimensions (", TexDesc.Width, "x", TexDesc.Height, + ") must be at least the upscaler input resolution (", m_Desc.InputWidth, "x", m_Desc.InputHeight, ")"); + DEV_CHECK_ERR(ViewDesc.Format == TEX_FORMAT_R8_UINT, + "Ignore history mask view '", TexDesc.Name, "' format (", GetTextureFormatAttribs(ViewDesc.Format).Name, + ") must be TEX_FORMAT_R8_UINT"); + } + + auto GetD3D12Resource = [](ITextureView* pView) -> ID3D12Resource* { + if (pView != nullptr) + { + auto* pTexD3D12 = ClassPtrCast(pView->GetTexture()); + return pTexD3D12->GetD3D12Resource(); + } + return nullptr; + }; + + DSR_SUPERRES_UPSCALER_EXECUTE_PARAMETERS ExecuteParams = {}; + + ExecuteParams.pTargetTexture = GetD3D12Resource(Attribs.pOutputTextureView); + ExecuteParams.TargetRegion = {0, 0, static_cast(m_Desc.OutputWidth), static_cast(m_Desc.OutputHeight)}; + ExecuteParams.pSourceColorTexture = GetD3D12Resource(Attribs.pColorTextureSRV); + ExecuteParams.SourceColorRegion = {0, 0, static_cast(m_Desc.InputWidth), static_cast(m_Desc.InputHeight)}; + ExecuteParams.pSourceDepthTexture = GetD3D12Resource(Attribs.pDepthTextureSRV); + ExecuteParams.SourceDepthRegion = {0, 0, static_cast(m_Desc.InputWidth), static_cast(m_Desc.InputHeight)}; + ExecuteParams.pMotionVectorsTexture = GetD3D12Resource(Attribs.pMotionVectorsSRV); + ExecuteParams.MotionVectorsRegion = {0, 0, static_cast(m_Desc.InputWidth), static_cast(m_Desc.InputHeight)}; + ExecuteParams.MotionVectorScale = {Attribs.MotionVectorScaleX, Attribs.MotionVectorScaleY}; + ExecuteParams.CameraJitter = {Attribs.JitterX, Attribs.JitterY}; + ExecuteParams.ExposureScale = Attribs.ExposureScale; + ExecuteParams.PreExposure = Attribs.PreExposure; + ExecuteParams.Sharpness = Attribs.Sharpness; + ExecuteParams.CameraNear = Attribs.CameraNear; + ExecuteParams.CameraFar = Attribs.CameraFar; + ExecuteParams.CameraFovAngleVert = Attribs.CameraFovAngleVert; + ExecuteParams.pExposureScaleTexture = GetD3D12Resource(Attribs.pExposureTextureSRV); + ExecuteParams.pIgnoreHistoryMaskTexture = GetD3D12Resource(Attribs.pIgnoreHistoryMaskTextureSRV); + ExecuteParams.IgnoreHistoryMaskRegion = {0, 0, static_cast(m_Desc.InputWidth), static_cast(m_Desc.InputHeight)}; + ExecuteParams.pReactiveMaskTexture = GetD3D12Resource(Attribs.pReactiveMaskTextureSRV); + ExecuteParams.ReactiveMaskRegion = {0, 0, static_cast(m_Desc.InputWidth), static_cast(m_Desc.InputHeight)}; + + DSR_SUPERRES_UPSCALER_EXECUTE_FLAGS Flags = DSR_SUPERRES_UPSCALER_EXECUTE_FLAG_NONE; + if (Attribs.ResetHistory) + Flags |= DSR_SUPERRES_UPSCALER_EXECUTE_FLAG_RESET_HISTORY; + + // Transition all textures to the states expected by DirectSR and flush the context. + // DirectSR submits its own command list(s) to the command queue, so all rendering work must be submitted before DirectSR reads the inputs. + // Input textures must be in D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, output must be in D3D12_RESOURCE_STATE_UNORDERED_ACCESS. + DeviceContextD3D12Impl* pDeviceCtx = ClassPtrCast(Attribs.pContext); + pDeviceCtx->TransitionTextureState(Attribs.pColorTextureSRV->GetTexture(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + pDeviceCtx->TransitionTextureState(Attribs.pDepthTextureSRV->GetTexture(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + pDeviceCtx->TransitionTextureState(Attribs.pMotionVectorsSRV->GetTexture(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + pDeviceCtx->TransitionTextureState(Attribs.pOutputTextureView->GetTexture(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + if (Attribs.pExposureTextureSRV) + pDeviceCtx->TransitionTextureState(Attribs.pExposureTextureSRV->GetTexture(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + if (Attribs.pReactiveMaskTextureSRV) + pDeviceCtx->TransitionTextureState(Attribs.pReactiveMaskTextureSRV->GetTexture(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + if (Attribs.pIgnoreHistoryMaskTextureSRV) + pDeviceCtx->TransitionTextureState(Attribs.pIgnoreHistoryMaskTextureSRV->GetTexture(), D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + pDeviceCtx->Flush(); + + if (HRESULT hr = pDSRUpscaler->Execute(&ExecuteParams, Attribs.TimeDeltaInSeconds, Flags); FAILED(hr)) + LOG_ERROR_MESSAGE("DirectSR Execute failed. HRESULT: ", hr); + + pDeviceCtx->TransitionTextureState(Attribs.pOutputTextureView->GetTexture(), D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE); +} + +} // anonymous namespace + +void EnumerateVariantsD3D12(IDSRDevice* pDSRDevice, std::vector& Variants) +{ + DEV_CHECK_ERR(pDSRDevice != nullptr, "DirectSR device must not be null"); + + static_assert(sizeof(SuperResolutionInfo::VariantId) == sizeof(DSR_SUPERRES_VARIANT_DESC::VariantId), "GUID/INTERFACE_ID size mismatch"); + + const Uint32 DSRNumVariants = pDSRDevice->GetNumSuperResVariants(); + for (Uint32 Idx = 0; Idx < DSRNumVariants; ++Idx) + { + DSR_SUPERRES_VARIANT_DESC VariantDesc = {}; + if (FAILED(pDSRDevice->GetSuperResVariantDesc(Idx, &VariantDesc))) + continue; + + SuperResolutionInfo Info{}; + Info.Type = SUPER_RESOLUTION_TYPE_TEMPORAL; + + Info.TemporalCapFlags = SUPER_RESOLUTION_TEMPORAL_CAP_FLAG_NATIVE; + if (VariantDesc.Flags & DSR_SUPERRES_VARIANT_FLAG_SUPPORTS_EXPOSURE_SCALE_TEXTURE) + Info.TemporalCapFlags |= SUPER_RESOLUTION_TEMPORAL_CAP_FLAG_EXPOSURE_SCALE_TEXTURE; + if (VariantDesc.Flags & DSR_SUPERRES_VARIANT_FLAG_SUPPORTS_IGNORE_HISTORY_MASK) + Info.TemporalCapFlags |= SUPER_RESOLUTION_TEMPORAL_CAP_FLAG_IGNORE_HISTORY_MASK; + if (VariantDesc.Flags & DSR_SUPERRES_VARIANT_FLAG_SUPPORTS_REACTIVE_MASK) + Info.TemporalCapFlags |= SUPER_RESOLUTION_TEMPORAL_CAP_FLAG_REACTIVE_MASK; + if (VariantDesc.Flags & DSR_SUPERRES_VARIANT_FLAG_SUPPORTS_SHARPNESS) + Info.TemporalCapFlags |= SUPER_RESOLUTION_TEMPORAL_CAP_FLAG_SHARPNESS; + + strncpy_s(Info.Name, sizeof(Info.Name), VariantDesc.VariantName, _TRUNCATE); + memcpy(&Info.VariantId, &VariantDesc.VariantId, sizeof(Info.VariantId)); + + Variants.push_back(Info); + } +} + +void GetSourceSettingsD3D12(IDSRDevice* pDSRDevice, + const SuperResolutionSourceSettingsAttribs& Attribs, + SuperResolutionSourceSettings& Settings) +{ + Settings = {}; + + DEV_CHECK_ERR(pDSRDevice != nullptr, "DirectSR device must not be null"); + DEV_CHECK_ERR(Attribs.OutputWidth > 0 && Attribs.OutputHeight > 0, "Output resolution must be greater than zero"); + DEV_CHECK_ERR(Attribs.OptimizationType < SUPER_RESOLUTION_OPTIMIZATION_TYPE_COUNT, "Invalid optimization type"); + + DSR_OPTIMIZATION_TYPE DSROptType = DSR_OPTIMIZATION_TYPE_BALANCED; + switch (Attribs.OptimizationType) + { + // clang-format off + case SUPER_RESOLUTION_OPTIMIZATION_TYPE_MAX_QUALITY: DSROptType = DSR_OPTIMIZATION_TYPE_MAX_QUALITY; break; + case SUPER_RESOLUTION_OPTIMIZATION_TYPE_HIGH_QUALITY: DSROptType = DSR_OPTIMIZATION_TYPE_HIGH_QUALITY; break; + case SUPER_RESOLUTION_OPTIMIZATION_TYPE_BALANCED: DSROptType = DSR_OPTIMIZATION_TYPE_BALANCED; break; + case SUPER_RESOLUTION_OPTIMIZATION_TYPE_HIGH_PERFORMANCE: DSROptType = DSR_OPTIMIZATION_TYPE_HIGH_PERFORMANCE; break; + case SUPER_RESOLUTION_OPTIMIZATION_TYPE_MAX_PERFORMANCE: DSROptType = DSR_OPTIMIZATION_TYPE_MAX_PERFORMANCE; break; + default: break; + // clang-format on + } + + const Uint32 NumVariants = pDSRDevice->GetNumSuperResVariants(); + Uint32 VariantIndex = UINT32_MAX; + for (Uint32 Idx = 0; Idx < NumVariants; ++Idx) + { + DSR_SUPERRES_VARIANT_DESC VariantDesc = {}; + if (SUCCEEDED(pDSRDevice->GetSuperResVariantDesc(Idx, &VariantDesc))) + { + if (memcmp(&VariantDesc.VariantId, &Attribs.VariantId, sizeof(GUID)) == 0) + { + VariantIndex = Idx; + break; + } + } + } + + if (VariantIndex == UINT32_MAX) + { + LOG_WARNING_MESSAGE("DirectSR variant not found for the specified VariantId"); + return; + } + + DSR_SIZE TargetSize = {Attribs.OutputWidth, Attribs.OutputHeight}; + + DSR_SUPERRES_CREATE_ENGINE_FLAGS DSRCreateFlags = DSR_SUPERRES_CREATE_ENGINE_FLAG_NONE; + if (Attribs.Flags & SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE) + DSRCreateFlags |= DSR_SUPERRES_CREATE_ENGINE_FLAG_AUTO_EXPOSURE; + if (Attribs.Flags & SUPER_RESOLUTION_FLAG_ENABLE_SHARPENING) + DSRCreateFlags |= DSR_SUPERRES_CREATE_ENGINE_FLAG_ENABLE_SHARPENING; + + DSR_SUPERRES_SOURCE_SETTINGS SourceSettings = {}; + if (HRESULT hr = pDSRDevice->QuerySuperResSourceSettings(VariantIndex, TargetSize, TexFormatToDXGI_Format(Attribs.OutputFormat), DSROptType, DSRCreateFlags, &SourceSettings); SUCCEEDED(hr)) + { + Settings.OptimalInputWidth = SourceSettings.OptimalSize.Width; + Settings.OptimalInputHeight = SourceSettings.OptimalSize.Height; + } + else + { + LOG_WARNING_MESSAGE("DirectSR QuerySuperResSourceSettings failed. HRESULT: ", hr); + } +} + +void CreateSuperResolutionD3D12(IRenderDevice* pDevice, + IDSRDevice* pDSRDevice, + const SuperResolutionDesc& Desc, + ISuperResolution** ppUpscaler) +{ + DEV_CHECK_ERR(pDSRDevice != nullptr, "DirectSR device must not be null"); + DEV_CHECK_ERR(pDevice != nullptr, "Render device must not be null"); + + auto* pDeviceD3D12 = ClassPtrCast(pDevice); + auto* pUpscaler = NEW_RC_OBJ(GetRawAllocator(), "SuperResolutionD3D12 instance", SuperResolutionD3D12)(pDeviceD3D12, Desc, pDSRDevice); + pUpscaler->QueryInterface(IID_SuperResolution, reinterpret_cast(ppUpscaler)); +} + } // namespace Diligent diff --git a/Tests/DiligentCoreAPITest/CMakeLists.txt b/Tests/DiligentCoreAPITest/CMakeLists.txt index 89b20f6dda..09d19e1fb8 100644 --- a/Tests/DiligentCoreAPITest/CMakeLists.txt +++ b/Tests/DiligentCoreAPITest/CMakeLists.txt @@ -121,6 +121,10 @@ PRIVATE if(SUPER_RESOLUTION_SUPPORTED) target_link_libraries(DiligentCoreAPITest PRIVATE Diligent-SuperResolution-static) + if(D3D12_SUPPORTED AND TARGET DirectSR-AgilitySDK) + target_link_libraries(DiligentCoreAPITest PRIVATE DirectSR-AgilitySDK) + copy_directsr_dlls(DiligentCoreAPITest) + endif() endif() if(TARGET Diligent-HLSL2GLSLConverterLib) diff --git a/Tests/DiligentCoreAPITest/src/SuperResolutionTest.cpp b/Tests/DiligentCoreAPITest/src/SuperResolutionTest.cpp new file mode 100644 index 0000000000..cc0593858e --- /dev/null +++ b/Tests/DiligentCoreAPITest/src/SuperResolutionTest.cpp @@ -0,0 +1,423 @@ +/* + * Copyright 2026 Diligent Graphics LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "GPUTestingEnvironment.hpp" +#include "GraphicsAccessories.hpp" +#include "SuperResolutionFactory.h" +#include "SuperResolutionFactoryLoader.h" + +#include "gtest/gtest.h" + +using namespace Diligent; +using namespace Diligent::Testing; + +extern "C" +{ + int TestSuperResolutionCInterface(void* pUpscaler); + int TestSuperResolutionFactoryCInterface(void* pFactory); +} + +namespace +{ + +static ISuperResolutionFactory* GetFactory() +{ + auto* pDevice = GPUTestingEnvironment::GetInstance()->GetDevice(); + static RefCntAutoPtr pFactory; + if (!pFactory) + LoadAndCreateSuperResolutionFactory(pDevice, &pFactory); + return pFactory; +} + +static const SuperResolutionInfo* FindVariantByType(const SuperResolutionInfo* pVariants, Uint32 NumVariants, SUPER_RESOLUTION_TYPE Type) +{ + for (Uint32 VariantIdx = 0; VariantIdx < NumVariants; ++VariantIdx) + { + if (pVariants[VariantIdx].Type == Type) + return &pVariants[VariantIdx]; + } + return nullptr; +} + +TEST(SuperResolutionTest, EnumerateVariants) +{ + auto* pFactory = GetFactory(); + ASSERT_NE(pFactory, nullptr); + + Uint32 NumVariants = 0; + pFactory->EnumerateVariants(NumVariants, nullptr); + if (NumVariants == 0) + { + GTEST_SKIP() << "No super resolution variants available on this device"; + } + + std::vector Variants(NumVariants); + pFactory->EnumerateVariants(NumVariants, Variants.data()); + + for (Uint32 VariantIdx = 0; VariantIdx < NumVariants; ++VariantIdx) + { + EXPECT_NE(Variants[VariantIdx].Name[0], '\0') << "Variant " << VariantIdx << " has empty name"; + EXPECT_NE(Variants[VariantIdx].VariantId, IID_Unknown) << "Variant " << VariantIdx << " has unknown UID"; + } +} + +TEST(SuperResolutionTest, QuerySourceSettings) +{ + auto* pFactory = GetFactory(); + ASSERT_NE(pFactory, nullptr); + + Uint32 NumVariants = 0; + pFactory->EnumerateVariants(NumVariants, nullptr); + if (NumVariants == 0) + { + GTEST_SKIP() << "No super resolution variants available on this device"; + } + + std::vector Variants(NumVariants); + pFactory->EnumerateVariants(NumVariants, Variants.data()); + + for (Uint32 VariantIdx = 0; VariantIdx < NumVariants; ++VariantIdx) + { + SuperResolutionSourceSettingsAttribs Attribs; + Attribs.VariantId = Variants[VariantIdx].VariantId; + Attribs.OutputWidth = 1920; + Attribs.OutputHeight = 1080; + Attribs.OptimizationType = SUPER_RESOLUTION_OPTIMIZATION_TYPE_BALANCED; + Attribs.OutputFormat = TEX_FORMAT_RGBA16_FLOAT; + Attribs.Flags = SUPER_RESOLUTION_FLAG_NONE; + + SuperResolutionSourceSettings Settings; + pFactory->GetSourceSettings(Attribs, Settings); + + EXPECT_GT(Settings.OptimalInputWidth, 0u) << "Variant " << Variants[VariantIdx].Name; + EXPECT_GT(Settings.OptimalInputHeight, 0u) << "Variant " << Variants[VariantIdx].Name; + EXPECT_LE(Settings.OptimalInputWidth, 1920u) << "Variant " << Variants[VariantIdx].Name; + EXPECT_LE(Settings.OptimalInputHeight, 1080u) << "Variant " << Variants[VariantIdx].Name; + } + + // Test all optimization types produce monotonically decreasing input resolution + // (enum is ordered from MAX_QUALITY=0 to MAX_PERFORMANCE) + { + const auto& Variant = Variants[0]; + + Uint32 PrevWidth = 0; + for (Uint8 OptimizationType = SUPER_RESOLUTION_OPTIMIZATION_TYPE_MAX_QUALITY; OptimizationType < SUPER_RESOLUTION_OPTIMIZATION_TYPE_MAX_PERFORMANCE; ++OptimizationType) + { + SuperResolutionSourceSettingsAttribs Attribs; + Attribs.VariantId = Variant.VariantId; + Attribs.OutputWidth = 1920; + Attribs.OutputHeight = 1080; + Attribs.OptimizationType = static_cast(OptimizationType); + Attribs.OutputFormat = TEX_FORMAT_RGBA16_FLOAT; + Attribs.Flags = SUPER_RESOLUTION_FLAG_NONE; + + SuperResolutionSourceSettings Settings; + pFactory->GetSourceSettings(Attribs, Settings); + + // First iteration: just record. Subsequent: input should decrease or stay same. + if (PrevWidth > 0) + EXPECT_LE(Settings.OptimalInputWidth, PrevWidth) << "OptimizationType " << OptimizationType; + PrevWidth = Settings.OptimalInputWidth; + } + } +} + +TEST(SuperResolutionTest, CreateTemporalUpscaler) +{ + auto* pFactory = GetFactory(); + ASSERT_NE(pFactory, nullptr); + + Uint32 NumVariants = 0; + pFactory->EnumerateVariants(NumVariants, nullptr); + if (NumVariants == 0) + { + GTEST_SKIP() << "No super resolution variants available on this device"; + } + + std::vector Variants(NumVariants); + pFactory->EnumerateVariants(NumVariants, Variants.data()); + + const auto* pTemporalInfo = FindVariantByType(Variants.data(), NumVariants, SUPER_RESOLUTION_TYPE_TEMPORAL); + if (pTemporalInfo == nullptr) + { + GTEST_SKIP() << "Temporal super resolution is not supported by this device"; + } + + GPUTestingEnvironment::ScopedReset EnvironmentAutoReset; + + // Query optimal input resolution + SuperResolutionSourceSettingsAttribs QueryAttribs; + QueryAttribs.VariantId = pTemporalInfo->VariantId; + QueryAttribs.OutputWidth = 1920; + QueryAttribs.OutputHeight = 1080; + QueryAttribs.OptimizationType = SUPER_RESOLUTION_OPTIMIZATION_TYPE_BALANCED; + QueryAttribs.OutputFormat = TEX_FORMAT_RGBA16_FLOAT; + QueryAttribs.Flags = SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE; + + SuperResolutionSourceSettings SourceSettings; + pFactory->GetSourceSettings(QueryAttribs, SourceSettings); + ASSERT_GT(SourceSettings.OptimalInputWidth, 0u); + ASSERT_GT(SourceSettings.OptimalInputHeight, 0u); + + SuperResolutionDesc Desc; + Desc.Name = "Test Temporal Upscaler"; + Desc.VariantId = pTemporalInfo->VariantId; + Desc.OutputWidth = QueryAttribs.OutputWidth; + Desc.OutputHeight = QueryAttribs.OutputHeight; + Desc.OutputFormat = QueryAttribs.OutputFormat; + Desc.InputWidth = SourceSettings.OptimalInputWidth; + Desc.InputHeight = SourceSettings.OptimalInputHeight; + Desc.ColorFormat = TEX_FORMAT_RGBA16_FLOAT; + Desc.DepthFormat = TEX_FORMAT_R32_FLOAT; + Desc.MotionFormat = TEX_FORMAT_RG16_FLOAT; + Desc.Flags = QueryAttribs.Flags; + + RefCntAutoPtr pUpscaler; + pFactory->CreateSuperResolution(Desc, &pUpscaler); + ASSERT_NE(pUpscaler, nullptr) << "Failed to create temporal super resolution upscaler"; + + const auto& RetDesc = pUpscaler->GetDesc(); + EXPECT_EQ(RetDesc.VariantId, pTemporalInfo->VariantId); + EXPECT_EQ(RetDesc.OutputWidth, 1920u); + EXPECT_EQ(RetDesc.OutputHeight, 1080u); + EXPECT_EQ(RetDesc.InputWidth, SourceSettings.OptimalInputWidth); + EXPECT_EQ(RetDesc.InputHeight, SourceSettings.OptimalInputHeight); + + // Temporal upscaler should return non-trivial jitter pattern (Halton sequence) + float JitterX = 0.0f, JitterY = 0.0f; + pUpscaler->GetJitterOffset(0, &JitterX, &JitterY); + EXPECT_TRUE(JitterX != 0.0f || JitterY != 0.0f); + + // Verify a few frames produce different jitter values + float PrevJitterX = JitterX, PrevJitterY = JitterY; + pUpscaler->GetJitterOffset(1, &JitterX, &JitterY); + EXPECT_TRUE(JitterX != PrevJitterX || JitterY != PrevJitterY); +} + +TEST(SuperResolutionTest, ExecuteTemporalUpscaler) +{ + auto* pEnv = GPUTestingEnvironment::GetInstance(); + auto* pDevice = pEnv->GetDevice(); + auto* pFactory = GetFactory(); + ASSERT_NE(pFactory, nullptr); + + Uint32 NumVariants = 0; + pFactory->EnumerateVariants(NumVariants, nullptr); + if (NumVariants == 0) + { + GTEST_SKIP() << "No super resolution variants available on this device"; + } + + std::vector Variants(NumVariants); + pFactory->EnumerateVariants(NumVariants, Variants.data()); + + const auto* pTemporalInfo = FindVariantByType(Variants.data(), NumVariants, SUPER_RESOLUTION_TYPE_TEMPORAL); + if (pTemporalInfo == nullptr) + { + GTEST_SKIP() << "Temporal super resolution is not supported by this device"; + } + + GPUTestingEnvironment::ScopedReset EnvironmentAutoReset; + + + // Query optimal input resolution + SuperResolutionSourceSettingsAttribs QueryAttribs{}; + QueryAttribs.VariantId = pTemporalInfo->VariantId; + QueryAttribs.OutputWidth = 1920; + QueryAttribs.OutputHeight = 1080; + QueryAttribs.OptimizationType = SUPER_RESOLUTION_OPTIMIZATION_TYPE_BALANCED; + QueryAttribs.OutputFormat = TEX_FORMAT_RGBA16_FLOAT; + QueryAttribs.Flags = SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE; + + SuperResolutionSourceSettings SourceSettings{}; + pFactory->GetSourceSettings(QueryAttribs, SourceSettings); + ASSERT_GT(SourceSettings.OptimalInputWidth, 0u); + ASSERT_GT(SourceSettings.OptimalInputHeight, 0u); + + // Create upscaler + SuperResolutionDesc UpscalerDesc{}; + UpscalerDesc.Name = "Test Temporal Execute Upscaler"; + UpscalerDesc.VariantId = pTemporalInfo->VariantId; + UpscalerDesc.OutputWidth = QueryAttribs.OutputWidth; + UpscalerDesc.OutputHeight = QueryAttribs.OutputHeight; + UpscalerDesc.OutputFormat = QueryAttribs.OutputFormat; + UpscalerDesc.InputWidth = SourceSettings.OptimalInputWidth; + UpscalerDesc.InputHeight = SourceSettings.OptimalInputHeight; + UpscalerDesc.ColorFormat = TEX_FORMAT_RGBA16_FLOAT; + UpscalerDesc.DepthFormat = TEX_FORMAT_R32_FLOAT; + UpscalerDesc.MotionFormat = TEX_FORMAT_RG16_FLOAT; + UpscalerDesc.Flags = QueryAttribs.Flags; + + RefCntAutoPtr pUpscaler; + pFactory->CreateSuperResolution(UpscalerDesc, &pUpscaler); + ASSERT_NE(pUpscaler, nullptr); + + + // Create input color texture + TextureDesc ColorTexDesc; + ColorTexDesc.Name = "SR Color Input"; + ColorTexDesc.Type = RESOURCE_DIM_TEX_2D; + ColorTexDesc.Width = SourceSettings.OptimalInputWidth; + ColorTexDesc.Height = SourceSettings.OptimalInputHeight; + ColorTexDesc.Format = TEX_FORMAT_RGBA16_FLOAT; + ColorTexDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_RENDER_TARGET; + ColorTexDesc.Usage = USAGE_DEFAULT; + + RefCntAutoPtr pColorTex; + pDevice->CreateTexture(ColorTexDesc, nullptr, &pColorTex); + ASSERT_NE(pColorTex, nullptr); + + // Create depth texture + TextureDesc DepthTexDesc; + DepthTexDesc.Name = "SR Depth Input"; + DepthTexDesc.Type = RESOURCE_DIM_TEX_2D; + DepthTexDesc.Width = SourceSettings.OptimalInputWidth; + DepthTexDesc.Height = SourceSettings.OptimalInputHeight; + DepthTexDesc.Format = TEX_FORMAT_D32_FLOAT; + DepthTexDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_DEPTH_STENCIL; + DepthTexDesc.Usage = USAGE_DEFAULT; + + RefCntAutoPtr pDepthTex; + pDevice->CreateTexture(DepthTexDesc, nullptr, &pDepthTex); + ASSERT_NE(pDepthTex, nullptr); + + // Create motion vectors texture + TextureDesc MotionTexDesc; + MotionTexDesc.Name = "SR Motion Vectors"; + MotionTexDesc.Type = RESOURCE_DIM_TEX_2D; + MotionTexDesc.Width = SourceSettings.OptimalInputWidth; + MotionTexDesc.Height = SourceSettings.OptimalInputHeight; + MotionTexDesc.Format = TEX_FORMAT_RG16_FLOAT; + MotionTexDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_RENDER_TARGET; + MotionTexDesc.Usage = USAGE_DEFAULT; + + RefCntAutoPtr pMotionTex; + pDevice->CreateTexture(MotionTexDesc, nullptr, &pMotionTex); + ASSERT_NE(pMotionTex, nullptr); + + // Create output texture + TextureDesc OutputTexDesc{}; + OutputTexDesc.Name = "SR Output"; + OutputTexDesc.Type = RESOURCE_DIM_TEX_2D; + OutputTexDesc.Width = QueryAttribs.OutputWidth; + OutputTexDesc.Height = QueryAttribs.OutputHeight; + OutputTexDesc.Format = TEX_FORMAT_RGBA16_FLOAT; + OutputTexDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_UNORDERED_ACCESS; + OutputTexDesc.Usage = USAGE_DEFAULT; + + RefCntAutoPtr pOutputTex; + pDevice->CreateTexture(OutputTexDesc, nullptr, &pOutputTex); + ASSERT_NE(pOutputTex, nullptr); + + // Execute temporal upscaling with reset + auto* pContext = pEnv->GetDeviceContext(); + + ExecuteSuperResolutionAttribs Attribs; + Attribs.pContext = pContext; + Attribs.pColorTextureSRV = pColorTex->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); + Attribs.pDepthTextureSRV = pDepthTex->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); + Attribs.pMotionVectorsSRV = pMotionTex->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); + Attribs.pOutputTextureView = pOutputTex->GetDefaultView(TEXTURE_VIEW_UNORDERED_ACCESS); + Attribs.JitterX = 0.0f; + Attribs.JitterY = 0.0f; + Attribs.MotionVectorScaleX = 1.0f; + Attribs.MotionVectorScaleY = 1.0f; + Attribs.ExposureScale = 1.0f; + Attribs.Sharpness = 0.5f; + Attribs.CameraNear = 0.1f; + Attribs.CameraFar = 1000.0f; + Attribs.CameraFovAngleVert = 1.0472f; // ~60 degrees + + Attribs.TimeDeltaInSeconds = 0.016f; + Attribs.ResetHistory = true; + pUpscaler->Execute(Attribs); + + // Execute a second frame without reset + Attribs.JitterX = -0.25f; + Attribs.JitterY = 0.25f; + Attribs.ResetHistory = False; + pUpscaler->Execute(Attribs); + + pContext->Flush(); + pContext->WaitForIdle(); +} + +TEST(SuperResolution_CInterface, Factory) +{ + auto* pFactory = GetFactory(); + ASSERT_NE(pFactory, nullptr); + EXPECT_EQ(TestSuperResolutionFactoryCInterface(pFactory), 0); +} + +TEST(SuperResolution_CInterface, SuperResolution) +{ + auto* pFactory = GetFactory(); + ASSERT_NE(pFactory, nullptr); + + Uint32 NumVariants = 0; + pFactory->EnumerateVariants(NumVariants, nullptr); + if (NumVariants == 0) + { + GTEST_SKIP() << "No super resolution variants available on this device"; + } + + std::vector Variants(NumVariants); + pFactory->EnumerateVariants(NumVariants, Variants.data()); + + GPUTestingEnvironment::ScopedReset EnvironmentAutoReset; + + SuperResolutionSourceSettingsAttribs QueryAttribs; + QueryAttribs.VariantId = Variants[0].VariantId; + QueryAttribs.OutputWidth = 1920; + QueryAttribs.OutputHeight = 1080; + QueryAttribs.OptimizationType = SUPER_RESOLUTION_OPTIMIZATION_TYPE_BALANCED; + QueryAttribs.OutputFormat = TEX_FORMAT_RGBA16_FLOAT; + QueryAttribs.Flags = SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE; + + SuperResolutionSourceSettings SourceSettings; + pFactory->GetSourceSettings(QueryAttribs, SourceSettings); + + SuperResolutionDesc Desc; + Desc.Name = "C Interface Test Upscaler"; + Desc.VariantId = Variants[0].VariantId; + Desc.OutputWidth = QueryAttribs.OutputWidth; + Desc.OutputHeight = QueryAttribs.OutputHeight; + Desc.OutputFormat = QueryAttribs.OutputFormat; + Desc.InputWidth = SourceSettings.OptimalInputWidth; + Desc.InputHeight = SourceSettings.OptimalInputHeight; + Desc.ColorFormat = TEX_FORMAT_RGBA16_FLOAT; + Desc.DepthFormat = TEX_FORMAT_R32_FLOAT; + Desc.MotionFormat = TEX_FORMAT_RG16_FLOAT; + Desc.Flags = SUPER_RESOLUTION_FLAG_AUTO_EXPOSURE; + + RefCntAutoPtr pUpscaler; + pFactory->CreateSuperResolution(Desc, &pUpscaler); + ASSERT_NE(pUpscaler, nullptr); + + EXPECT_EQ(TestSuperResolutionCInterface(pUpscaler), 0); +} + +} // namespace From 205d2042110411d0ba786dd99d717f36d6a105fe Mon Sep 17 00:00:00 2001 From: MikhailGorobets Date: Fri, 13 Mar 2026 14:34:00 +0600 Subject: [PATCH 3/3] Add MetalFX (Metal) super resolution implementation --- CMakeLists.txt | 10 ++++++---- Graphics/SuperResolution/CMakeLists.txt | 6 ++++++ .../src/SuperResolutionFactory.cpp | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 980206ca2e..39ab3cc9bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,12 +201,14 @@ elseif(PLATFORM_LINUX) set(ARCHIVER_SUPPORTED TRUE CACHE INTERNAL "Archiver is supported on Linux platform") target_compile_definitions(Diligent-PublicBuildSettings INTERFACE PLATFORM_LINUX=1) elseif(PLATFORM_MACOS) - set(GL_SUPPORTED TRUE CACHE INTERNAL "OpenGL is supported on MacOS platform") - set(VULKAN_SUPPORTED TRUE CACHE INTERNAL "Vulkan is enabled through MoltenVK on MacOS platform") - set(ARCHIVER_SUPPORTED TRUE CACHE INTERNAL "Archiver is supported on MacOS platform") + set(GL_SUPPORTED TRUE CACHE INTERNAL "OpenGL is supported on MacOS platform") + set(VULKAN_SUPPORTED TRUE CACHE INTERNAL "Vulkan is enabled through MoltenVK on MacOS platform") + set(ARCHIVER_SUPPORTED TRUE CACHE INTERNAL "Archiver is supported on MacOS platform") + set(SUPER_RESOLUTION_SUPPORTED TRUE CACHE INTERNAL "Super resolution is supported on MacOS platform") target_compile_definitions(Diligent-PublicBuildSettings INTERFACE PLATFORM_MACOS=1 PLATFORM_APPLE=1) elseif(PLATFORM_IOS) - set(GLES_SUPPORTED TRUE CACHE INTERNAL "OpenGLES is supported on iOS platform") + set(GLES_SUPPORTED TRUE CACHE INTERNAL "OpenGLES is supported on iOS platform") + set(SUPER_RESOLUTION_SUPPORTED TRUE CACHE INTERNAL "Super resolution is supported on iOS platform") target_compile_definitions(Diligent-PublicBuildSettings INTERFACE PLATFORM_IOS=1 PLATFORM_APPLE=1) elseif(PLATFORM_TVOS) target_compile_definitions(Diligent-PublicBuildSettings INTERFACE PLATFORM_TVOS=1 PLATFORM_APPLE=1) diff --git a/Graphics/SuperResolution/CMakeLists.txt b/Graphics/SuperResolution/CMakeLists.txt index e817e953b8..c1ef4161c4 100644 --- a/Graphics/SuperResolution/CMakeLists.txt +++ b/Graphics/SuperResolution/CMakeLists.txt @@ -83,6 +83,12 @@ if(D3D12_SUPPORTED) target_include_directories(Diligent-SuperResolution-static PRIVATE ../GraphicsEngineD3D12/include) endif() +if(METAL_SUPPORTED) + target_include_directories(Diligent-SuperResolution-static PRIVATE + ${CMAKE_SOURCE_DIR}/DiligentCorePro/Graphics/SuperResolution/include + ) +endif() + target_link_libraries(Diligent-SuperResolution-shared PUBLIC Diligent-SuperResolutionInterface diff --git a/Graphics/SuperResolution/src/SuperResolutionFactory.cpp b/Graphics/SuperResolution/src/SuperResolutionFactory.cpp index c181b30887..e7781e7146 100644 --- a/Graphics/SuperResolution/src/SuperResolutionFactory.cpp +++ b/Graphics/SuperResolution/src/SuperResolutionFactory.cpp @@ -37,6 +37,10 @@ # include "SuperResolution_D3D12.hpp" #endif +#if METAL_SUPPORTED +# include "SuperResolution_Metal.hpp" +#endif + namespace Diligent { @@ -97,6 +101,11 @@ void SuperResolutionFactoryImpl::PopulateVariants() if (m_pDSRDevice) EnumerateVariantsD3D12(m_pDSRDevice, m_Variants[SUPER_RESOLUTION_BACKEND_D3D12_DSR]); #endif + +#if METAL_SUPPORTED + if (m_pDevice != nullptr && m_pDevice->GetDeviceInfo().Type == RENDER_DEVICE_TYPE_METAL) + EnumerateVariantsMetal(m_pDevice, m_Variants[SUPER_RESOLUTION_BACKEND_METAL_FX]); +#endif } SUPER_RESOLUTION_BACKEND SuperResolutionFactoryImpl::FindVariant(const INTERFACE_ID& VariantId) const @@ -154,6 +163,11 @@ void SuperResolutionFactoryImpl::GetSourceSettings(const SuperResolutionSourceSe case SUPER_RESOLUTION_BACKEND_D3D12_DSR: GetSourceSettingsD3D12(m_pDSRDevice, Attribs, Settings); break; +#endif +#if METAL_SUPPORTED + case SUPER_RESOLUTION_BACKEND_METAL_FX: + GetSourceSettingsMetal(Attribs, Settings); + break; #endif default: LOG_WARNING_MESSAGE("Unknown super resolution backend"); @@ -184,6 +198,11 @@ void SuperResolutionFactoryImpl::CreateSuperResolution(const SuperResolutionDesc case SUPER_RESOLUTION_BACKEND_D3D12_DSR: CreateSuperResolutionD3D12(m_pDevice, m_pDSRDevice, Desc, ppUpscaler); break; +#endif +#if METAL_SUPPORTED + case SUPER_RESOLUTION_BACKEND_METAL_FX: + CreateSuperResolutionMetal(m_pDevice, Desc, ppUpscaler); + break; #endif default: LOG_ERROR_MESSAGE("Unknown super resolution backend");