diff --git a/.gitmodules b/.gitmodules index cd68154717..d4fb422d95 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "extern/Streamline-DX12"] path = extern/Streamline-DX12 url = https://github.com/NVIDIAGameWorks/Streamline.git +[submodule "extern/FidelityFX-SDK"] + path = extern/FidelityFX-SDK + url = https://github.com/MapleHinata/FidelityFX-SDK diff --git a/CMakeLists.txt b/CMakeLists.txt index 407913e691..b2718ae72c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ include(XeSS-SDK) find_path(DETOURS_INCLUDE_DIRS "detours/detours.h") find_library(DETOURS_LIBRARY detours REQUIRED) +include(FidelityFX-SDK) target_compile_definitions( ${PROJECT_NAME} diff --git a/cmake/FidelityFX-SDK.cmake b/cmake/FidelityFX-SDK.cmake new file mode 100644 index 0000000000..1c61ab4c61 --- /dev/null +++ b/cmake/FidelityFX-SDK.cmake @@ -0,0 +1,15 @@ +set(FFX_API_VK OFF) +set(FFX_API_DX12 OFF) +set(FFX_ALL OFF) +set(FFX_FSR3 ON) +set(FFX_FSR ON) +set(FFX_AUTO_COMPILE_SHADERS 1) + +add_subdirectory(${CMAKE_SOURCE_DIR}/extern/FidelityFX-SDK/sdk) + +target_link_libraries( + ${PROJECT_NAME} + PRIVATE + ffx_backend_dx11_x64 + ffx_fsr3_x64 +) diff --git a/extern/FidelityFX-SDK b/extern/FidelityFX-SDK new file mode 160000 index 0000000000..8138c9dc08 --- /dev/null +++ b/extern/FidelityFX-SDK @@ -0,0 +1 @@ +Subproject commit 8138c9dc086154706643a03def91f3d01d391cd0 diff --git a/features/Upscaling/Shaders/Upscaling/EncodeTexturesCS.hlsl b/features/Upscaling/Shaders/Upscaling/EncodeTexturesCS.hlsl index 58c2318c51..b30a551b93 100644 --- a/features/Upscaling/Shaders/Upscaling/EncodeTexturesCS.hlsl +++ b/features/Upscaling/Shaders/Upscaling/EncodeTexturesCS.hlsl @@ -12,11 +12,7 @@ Texture2D MotionVectorMask : register(t2); Texture2D DepthMask : register(t3); RWTexture2D ReactiveMask : register(u0); - -#if defined(DLSS) || defined(FSR) RWTexture2D TransparencyCompositionMask : register(u1); -#endif - RWTexture2D MotionVectorOutput : register(u2); [numthreads(8, 8, 1)] void main(uint3 dispatchID : SV_DispatchThreadID) { @@ -27,14 +23,13 @@ RWTexture2D MotionVectorOutput : register(u2); float2 taaMask = TAAMask[dispatchID.xy]; float transparencyCompositionMask = NormalsWaterMask[dispatchID.xy].z; +#if defined(DLSS) float depth = DepthMask[dispatchID.xy]; -#if defined(DLSS) || defined(XESS) // Find longest motion vector in 5x5 neighborhood float2 motionVector = MotionVectorMask[dispatchID.xy]; float2 longestMotionVector = motionVector; float maxMotionLengthSq = dot(motionVector, motionVector); -#endif [unroll] for (int y = -2; y <= 2; y++) { @@ -50,10 +45,6 @@ RWTexture2D MotionVectorOutput : register(u2); // Take neighbor if it's longer AND closer if (neighborDepth < depth){ - taaMask.x = min(taaMask.x, TAAMask[samplePos].x); - -#if defined(DLSS) || defined(XESS) - float2 neighborMotionVector = MotionVectorMask[samplePos]; // Square motion vector for length @@ -63,21 +54,15 @@ RWTexture2D MotionVectorOutput : register(u2); maxMotionLengthSq = motionLengthSq; longestMotionVector = neighborMotionVector; } -#endif } } } -#if defined(DLSS) || defined(XESS) MotionVectorOutput[dispatchID.xy] = longestMotionVector; #endif -#if defined(DLSS) || defined(FSR) float reactiveMask = taaMask.x * 0.1 + taaMask.y; ReactiveMask[dispatchID.xy] = reactiveMask; + TransparencyCompositionMask[dispatchID.xy] = transparencyCompositionMask; -#else - float reactiveMask = taaMask.x * 0.01 + taaMask.y; - ReactiveMask[dispatchID.xy] = reactiveMask + transparencyCompositionMask * 0.1; -#endif } \ No newline at end of file diff --git a/features/Upscaling/Shaders/Upscaling/FidelityFX/amd_fidelityfx_upscaler_dx12.dll b/features/Upscaling/Shaders/Upscaling/FidelityFX/amd_fidelityfx_upscaler_dx12.dll deleted file mode 100644 index 4bd446fd7b..0000000000 Binary files a/features/Upscaling/Shaders/Upscaling/FidelityFX/amd_fidelityfx_upscaler_dx12.dll and /dev/null differ diff --git a/include/FidelityFX/upscalers/include/ffx_upscale.h b/include/FidelityFX/upscalers/include/ffx_upscale.h deleted file mode 100644 index 2dd434e0cd..0000000000 --- a/include/FidelityFX/upscalers/include/ffx_upscale.h +++ /dev/null @@ -1,218 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (C) 2025 Advanced Micro Devices, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#pragma once -#include "../../api/include/ffx_api.h" -#include "../../api/include/ffx_api_types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -enum FfxApiUpscaleQualityMode -{ - FFX_UPSCALE_QUALITY_MODE_NATIVEAA = 0, ///< Perform upscaling with a per-dimension upscaling ratio of 1.0x. - FFX_UPSCALE_QUALITY_MODE_QUALITY = 1, ///< Perform upscaling with a per-dimension upscaling ratio of 1.5x. - FFX_UPSCALE_QUALITY_MODE_BALANCED = 2, ///< Perform upscaling with a per-dimension upscaling ratio of 1.7x. - FFX_UPSCALE_QUALITY_MODE_PERFORMANCE = 3, ///< Perform upscaling with a per-dimension upscaling ratio of 2.0x. - FFX_UPSCALE_QUALITY_MODE_ULTRA_PERFORMANCE = 4 ///< Perform upscaling with a per-dimension upscaling ratio of 3.0x. -}; - -enum FfxApiCreateContextUpscaleFlags -{ - FFX_UPSCALE_ENABLE_HIGH_DYNAMIC_RANGE = (1<<0), ///< A bit indicating if the input color data provided is using a high-dynamic range. - FFX_UPSCALE_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS = (1<<1), ///< A bit indicating if the motion vectors are rendered at display resolution. - FFX_UPSCALE_ENABLE_MOTION_VECTORS_JITTER_CANCELLATION = (1<<2), ///< A bit indicating that the motion vectors have the jittering pattern applied to them. - FFX_UPSCALE_ENABLE_DEPTH_INVERTED = (1<<3), ///< A bit indicating that the input depth buffer data provided is inverted [1..0]. - FFX_UPSCALE_ENABLE_DEPTH_INFINITE = (1<<4), ///< A bit indicating that the input depth buffer data provided is using an infinite far plane. - FFX_UPSCALE_ENABLE_AUTO_EXPOSURE = (1<<5), ///< A bit indicating if automatic exposure should be applied to input color data. - FFX_UPSCALE_ENABLE_DYNAMIC_RESOLUTION = (1<<6), ///< A bit indicating that the application uses dynamic resolution scaling. - FFX_UPSCALE_ENABLE_DEBUG_CHECKING = (1<<7), ///< A bit indicating that the runtime should check some API values and report issues. - FFX_UPSCALE_ENABLE_NON_LINEAR_COLORSPACE = (1<<8), ///< A bit indicating that the color resource contains perceptual (gamma corrected) colors - FFX_UPSCALE_ENABLE_DEBUG_VISUALIZATION = (1<<9), ///< A bit indicating if debug visualization is allowed. (memory consumption could increase) -}; - -enum FfxApiDispatchFsrUpscaleFlags -{ - FFX_UPSCALE_FLAG_DRAW_DEBUG_VIEW = (1 << 0), ///< A bit indicating that the output resource will contain debug views with relevant information. - FFX_UPSCALE_FLAG_NON_LINEAR_COLOR_SRGB = (1 << 1), ///< A bit indicating that the input color resource contains perceptual sRGB colors - FFX_UPSCALE_FLAG_NON_LINEAR_COLOR_PQ = (1 << 2), ///< A bit indicating that the input color resource contains perceptual PQ colors -}; - -enum FfxApiDispatchUpscaleAutoreactiveFlags -{ - FFX_UPSCALE_AUTOREACTIVEFLAGS_APPLY_TONEMAP = (1<<0), - FFX_UPSCALE_AUTOREACTIVEFLAGS_APPLY_INVERSETONEMAP = (1<<1), - FFX_UPSCALE_AUTOREACTIVEFLAGS_APPLY_THRESHOLD = (1<<2), - FFX_UPSCALE_AUTOREACTIVEFLAGS_USE_COMPONENTS_MAX = (1<<3), -}; - -#define FFX_API_EFFECT_ID_UPSCALE 0x00010000u - -#define FFX_API_CREATE_CONTEXT_DESC_TYPE_UPSCALE 0x00010000u -struct ffxCreateContextDescUpscale -{ - ffxCreateContextDescHeader header; - uint32_t flags; ///< Zero or a combination of values from FfxApiCreateContextUpscaleFlags. - struct FfxApiDimensions2D maxRenderSize; ///< The maximum size that rendering will be performed at. - struct FfxApiDimensions2D maxUpscaleSize; ///< The size of the presentation resolution targeted by the upscaling process. - ffxApiMessage fpMessage; ///< A pointer to a function that can receive messages from the runtime. May be null. -}; - -#define FFX_API_DISPATCH_DESC_TYPE_UPSCALE 0x00010001u -struct ffxDispatchDescUpscale -{ - ffxDispatchDescHeader header; - void* commandList; ///< Command list to record upscaling rendering commands into. - struct FfxApiResource color; ///< Color buffer for the current frame (at render resolution). - struct FfxApiResource depth; ///< 32bit depth values for the current frame (at render resolution). - struct FfxApiResource motionVectors; ///< 2-dimensional motion vectors (at render resolution if FFX_FSR_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS is not set). - struct FfxApiResource exposure; ///< Optional resource containing a 1x1 exposure value. - struct FfxApiResource reactive; ///< Optional resource containing alpha value of reactive objects in the scene. - struct FfxApiResource transparencyAndComposition; ///< Optional resource containing alpha value of special objects in the scene. - struct FfxApiResource output; ///< Output color buffer for the current frame (at presentation resolution). - struct FfxApiFloatCoords2D jitterOffset; ///< The subpixel jitter offset applied to the camera. - struct FfxApiFloatCoords2D motionVectorScale; ///< The scale factor to apply to motion vectors. - struct FfxApiDimensions2D renderSize; ///< The resolution that was used for rendering the input resources. - struct FfxApiDimensions2D upscaleSize; ///< The resolution that the upscaler will upscale to (optional, assumed maxUpscaleSize otherwise). - bool enableSharpening; ///< Enable an additional sharpening pass. - float sharpness; ///< The sharpness value between 0 and 1, where 0 is no additional sharpness and 1 is maximum additional sharpness. - float frameTimeDelta; ///< The time elapsed since the last frame (expressed in milliseconds). - float preExposure; ///< The pre exposure value (must be > 0.0f) - bool reset; ///< A boolean value which when set to true, indicates the camera has moved discontinuously. - float cameraNear; ///< The distance to the near plane of the camera. - float cameraFar; ///< The distance to the far plane of the camera. - float cameraFovAngleVertical; ///< The camera angle field of view in the vertical direction (expressed in radians). - float viewSpaceToMetersFactor; ///< The scale factor to convert view space units to meters - uint32_t flags; ///< Zero or a combination of values from FfxApiDispatchFsrUpscaleFlags. -}; - -#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GETUPSCALERATIOFROMQUALITYMODE 0x00010002u -struct ffxQueryDescUpscaleGetUpscaleRatioFromQualityMode -{ - ffxQueryDescHeader header; - uint32_t qualityMode; ///< The desired quality mode for FSR upscaling. - float* pOutUpscaleRatio; ///< A pointer to a float which will hold the upscaling the per-dimension upscaling ratio. -}; - -#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GETRENDERRESOLUTIONFROMQUALITYMODE 0x00010003u -struct ffxQueryDescUpscaleGetRenderResolutionFromQualityMode -{ - ffxQueryDescHeader header; - uint32_t displayWidth; ///< The target display resolution width. - uint32_t displayHeight; ///< The target display resolution height. - uint32_t qualityMode; ///< The desired quality mode for FSR upscaling. - uint32_t* pOutRenderWidth; ///< A pointer to a uint32_t which will hold the calculated render resolution width. - uint32_t* pOutRenderHeight; ///< A pointer to a uint32_t which will hold the calculated render resolution height. -}; - -#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GETJITTERPHASECOUNT 0x00010004u -struct ffxQueryDescUpscaleGetJitterPhaseCount -{ - ffxQueryDescHeader header; - uint32_t renderWidth; ///< The render resolution width. - uint32_t displayWidth; ///< The output resolution width. - int32_t* pOutPhaseCount; ///< A pointer to a int32_t which will hold the jitter phase count for the scaling factor between renderWidth and displayWidth. -}; - -#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GETJITTEROFFSET 0x00010005u -struct ffxQueryDescUpscaleGetJitterOffset -{ - ffxQueryDescHeader header; - int32_t index; ///< The index within the jitter sequence. - int32_t phaseCount; ///< The length of jitter phase. See ffxQueryDescFsrGetJitterPhaseCount. - float* pOutX; ///< A pointer to a float which will contain the subpixel jitter offset for the x dimension. - float* pOutY; ///< A pointer to a float which will contain the subpixel jitter offset for the y dimension. -}; - -#define FFX_API_DISPATCH_DESC_TYPE_UPSCALE_GENERATEREACTIVEMASK 0x00010006u -struct ffxDispatchDescUpscaleGenerateReactiveMask -{ - ffxDispatchDescHeader header; - void* commandList; ///< The FfxCommandList to record FSRUPSCALE rendering commands into. - struct FfxApiResource colorOpaqueOnly; ///< A FfxApiResource containing the opaque only color buffer for the current frame (at render resolution). - struct FfxApiResource colorPreUpscale; ///< A FfxApiResource containing the opaque+translucent color buffer for the current frame (at render resolution). - struct FfxApiResource outReactive; ///< A FfxApiResource containing the surface to generate the reactive mask into. - struct FfxApiDimensions2D renderSize; ///< The resolution that was used for rendering the input resources. - float scale; ///< A value to scale the output - float cutoffThreshold; ///< A threshold value to generate a binary reactive mask - float binaryValue; ///< A value to set for the binary reactive mask - uint32_t flags; ///< Flags to determine how to generate the reactive mask -}; - -#define FFX_API_CONFIGURE_DESC_TYPE_UPSCALE_KEYVALUE 0x00010007u -struct ffxConfigureDescUpscaleKeyValue -{ - ffxConfigureDescHeader header; - uint64_t key; ///< Configuration key, member of the FfxApiConfigureUpscaleKey enumeration. - uint64_t u64; ///< Integer value or enum value to set. - void* ptr; ///< Pointer to set or pointer to value to set. -}; - -enum FfxApiConfigureUpscaleKey -{ - FFX_API_CONFIGURE_UPSCALE_KEY_FVELOCITYFACTOR = 0, //Override constant buffer fVelocityFactor. The float value is casted from void * ptr. Value of 0.0f can improve temporal stability of bright pixels. Default value is 1.0f. Value is clamped to [0.0f, 1.0f]. - FFX_API_CONFIGURE_UPSCALE_KEY_FREACTIVENESSSCALE = 1, //Override constant buffer fReactivenessScale. The float value is casted from void * ptr. Meant for development purpose to test if writing a larger value to reactive mask, reduces ghosting. Default value is 1.0f. Value is clamped to [0.0f, +infinity]. - FFX_API_CONFIGURE_UPSCALE_KEY_FSHADINGCHANGESCALE = 2, //Override fShadingChangeScale. Increasing this scales fsr3.1 computed shading change value at read to have higher reactiveness. Default value is 1.0f. Value is clamped to [0.0f, +infinity]. - FFX_API_CONFIGURE_UPSCALE_KEY_FACCUMULATIONADDEDPERFRAME = 3, // Override constant buffer fAccumulationAddedPerFrame. Corresponds to amount of accumulation added per frame at pixel coordinate where disocclusion occured or when reactive mask value is > 0.0f. Decreasing this and drawing the ghosting object (IE no mv) to reactive mask with value close to 1.0f can decrease temporal ghosting. Decreasing this value could result in more thin feature pixels flickering. Default value is 0.333. Value is clamped to [0.0f, 1.0f]. - FFX_API_CONFIGURE_UPSCALE_KEY_FMINDISOCCLUSIONACCUMULATION = 4, //Override constant buffer fMinDisocclusionAccumulation. Increasing this value may reduce white pixel temporal flickering around swaying thin objects that are disoccluding one another often. Too high value may increase ghosting. A sufficiently negative value means for pixel coordinate at frame N that is disoccluded, add fAccumulationAddedPerFrame starting at frame N+2. Default value is -0.333. Value is clamped to [-1.0f, 1.0f]. -}; - -#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GPU_MEMORY_USAGE 0x00010008u -struct ffxQueryDescUpscaleGetGPUMemoryUsage -{ - ffxQueryDescHeader header; - struct FfxApiEffectMemoryUsage* gpuMemoryUsageUpscaler; -}; - -#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GPU_MEMORY_USAGE_V2 0x00010009u -struct ffxQueryDescUpscaleGetGPUMemoryUsageV2 -{ - ffxQueryDescHeader header; - void* device; ///< For DX12: pointer to ID3D12Device. For VK, pointer to VkDevice. App needs to fill out before Query() call. - struct FfxApiDimensions2D maxRenderSize; ///< App needs to fill out before Query() call. - struct FfxApiDimensions2D maxUpscaleSize; ///< App needs to fill out before Query() call. - uint32_t flags; ///< Zero or a combination of values from FfxApiCreateContextUpscaleFlags. App needs to fill out before Query() call. - struct FfxApiEffectMemoryUsage* gpuMemoryUsageUpscaler; ///< Output values by Query() call. -}; - -enum FfxApiQueryResourceIdentifiers -{ - FFX_API_QUERY_RESOURCE_INPUT_COLOR = (1<<0), // Color buffer for the current frame (at render resolution). - FFX_API_QUERY_RESOURCE_INPUT_DEPTH = (1<<1), // 32bit depth values for the current frame (at render resolution). - FFX_API_QUERY_RESOURCE_INPUT_MV = (1<<2), // 2-dimensional motion vectors (at render resolution if FFX_FSR_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS is not set). - FFX_API_QUERY_RESOURCE_INPUT_EXPOSURE = (1<<3), // A 1x1 texture containing exposure value or the FFX_UPSCALE_ENABLE_AUTO_EXPOSURE set at context creation. - FFX_API_QUERY_RESOURCE_INPUT_REACTIVEMASK = (1<<4), // - FFX_API_QUERY_RESOURCE_INPUT_TRANSPARENCYCOMPOSITION = (1<<5), // -}; - -#define FFX_API_QUERY_DESC_TYPE_UPSCALE_GET_RESOURCE_REQUIREMENTS 0x0001000au -struct ffxQueryDescUpscaleGetResourceRequirements -{ - ffxQueryDescHeader header; - uint64_t required_resources; // resources 64b bitfield, that given current context state, are required for effect correctness. - uint64_t optional_resources; // resources 64b bitfield, that given current context state, will be consumed if provided, but are optional. -}; - -#ifdef __cplusplus -} -#endif diff --git a/include/FidelityFX/upscalers/include/ffx_upscale.hpp b/include/FidelityFX/upscalers/include/ffx_upscale.hpp deleted file mode 100644 index cb76fa9c4c..0000000000 --- a/include/FidelityFX/upscalers/include/ffx_upscale.hpp +++ /dev/null @@ -1,88 +0,0 @@ -// This file is part of the FidelityFX SDK. -// -// Copyright (C) 2025 Advanced Micro Devices, Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files(the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and /or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions : -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#pragma once - -#include "../../api/include/ffx_api.hpp" -#include "ffx_upscale.h" - -// Helper types for header initialization. Api definition is in .h file. - -namespace ffx -{ - -template<> -struct struct_type : std::integral_constant {}; - -struct CreateContextDescUpscale : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct DispatchDescUpscale : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct QueryDescUpscaleGetUpscaleRatioFromQualityMode : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct QueryDescUpscaleGetRenderResolutionFromQualityMode : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct QueryDescUpscaleGetJitterPhaseCount : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct QueryDescUpscaleGetJitterOffset : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct DispatchDescUpscaleGenerateReactiveMask : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct ConfigureDescUpscaleKeyValue : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct QueryDescUpscaleGetGPUMemoryUsage : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct QueryDescUpscaleGetGPUMemoryUsageV2 : public InitHelper {}; - -template<> -struct struct_type : std::integral_constant {}; - -struct QueryDescUpscaleGetResourceRequirements : public InitHelper {}; - -} diff --git a/src/Features/Upscaling.cpp b/src/Features/Upscaling.cpp index b79619cb82..7e3191d24c 100644 --- a/src/Features/Upscaling.cpp +++ b/src/Features/Upscaling.cpp @@ -6,7 +6,6 @@ #include "Upscaling/DX12SwapChain.h" #include "Upscaling/FidelityFX.h" #include "Upscaling/Streamline.h" -#include "Upscaling/XeSS.h" #include #include #include @@ -15,7 +14,6 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( Upscaling::Settings, upscaleMethod, - upscaleMethodNoDLSS, qualityMode, frameLimitMode, frameGenerationMode, @@ -85,8 +83,6 @@ HRESULT WINAPI hk_D3D11CreateDeviceAndSwapChainUpscaling( const D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_1; - upscaling.CreateSharedD3D12Device(pAdapter); - if (shouldProxy) { logger::info("[Frame Generation] Frame Generation enabled, using D3D12 proxy"); @@ -151,39 +147,25 @@ HRESULT WINAPI hk_D3D11CreateDeviceAndSwapChainUpscaling( void Upscaling::DrawSettings() { - // Display upscaling options in the UI - build labels with version info + // Display upscaling options in the UI std::vector upscaleModes = { "None", "TAA" }; - std::string fsrLabel = "AMD FSR"; - if (!fidelityFX.versionInfo.empty()) { - fsrLabel += " " + fidelityFX.versionInfo; - } - std::array fsrScales{}; + std::string fsrLabel = "AMD FSR 3.1"; upscaleModes.push_back(fsrLabel); - std::string xessLabel = "Intel XeSS"; - if (!xess.versionInfo.empty()) { - xessLabel += " " + xess.versionInfo; - } - std::array xessScales{}; - upscaleModes.push_back(xessLabel); - - std::string dlssLabel = "NVIDIA DLSS 4 Preset K"; - std::array dlssScales{}; + std::string dlssLabel = "NVIDIA DLSS"; upscaleModes.push_back(dlssLabel); // Determine available modes bool featureDLSS = streamline.featureDLSS; + bool featureFSR = true; // FSR is always available uint32_t* currentUpscaleMode = &settings.upscaleMethod; - uint32_t availableModes = 4; - - if (featureDLSS) { - // All modes available including DLSS - } else { - currentUpscaleMode = &settings.upscaleMethodNoDLSS; - availableModes = 3; - } + uint32_t availableModes = 1; // Start with TAA + if (featureFSR) + availableModes = 2; // Add FSR + if (featureDLSS) + availableModes = 3; // Add DLSS if available // Slider for method selection // Clamp the index used to read from the built label vector to avoid OOB if the stored value is stale @@ -196,41 +178,12 @@ void Upscaling::DrawSettings() // Check the current upscale method auto upscaleMethod = GetUpscaleMethod(); - // Prepopulate per-backend local scale arrays only if we already have cached - // preset scales populated by the runtime upscale operation. Until then, - // leave them as unavailable so the UI shows '(unavailable)'. - // Consider the cachedPresetScales valid when the first entry is non-negative - // and the last populated method matches the current method. - if (cachedPresetMethod == upscaleMethod && cachedPresetScales[0] != Upscaling::kScaleUnavailable) { - if (upscaleMethod == UpscaleMethod::kFSR) { - std::copy(cachedPresetScales.begin(), cachedPresetScales.end(), fsrScales.begin()); - } else if (upscaleMethod == UpscaleMethod::kXESS) { - std::copy(cachedPresetScales.begin(), cachedPresetScales.end(), xessScales.begin()); - } else if (upscaleMethod == UpscaleMethod::kDLSS) { - std::copy(cachedPresetScales.begin(), cachedPresetScales.end(), dlssScales.begin()); - } - } - // Display upscaling settings if applicable if (!globals::game::isVR) { if (upscaleMethod != UpscaleMethod::kNONE && upscaleMethod != UpscaleMethod::kTAA) { const char* upscalePresetsDLSS[] = { "Ultra Performance", "Performance", "Balanced", "Quality", "DLAA" }; const char* upscalePresets[] = { "Ultra Performance", "Performance", "Balanced", "Quality", "Native AA" }; - auto formatScalePct = [&](float scale) -> int { - int pct = static_cast(roundf(scale * 100.0f)); - return pct; - }; - - auto makePresetLabel = [&](const char* baseLabel, const std::array& scales) -> std::string { - const int mode = settings.qualityMode; - if (scales[mode] != Upscaling::kScaleUnavailable) { - int pct = formatScalePct(scales[mode]); - return std::format("{} ({}%)", baseLabel, pct); - } - return std::format("{} (unavailable)", baseLabel); - }; - // Compute a safe preset index (4 - qualityMode) clamped to [0,4] to avoid negative/overflow indexing int presetIndex = 0; if (settings.qualityMode <= 4) @@ -240,18 +193,15 @@ void Upscaling::DrawSettings() // Choose preset name set and the corresponding scales once, then show a // single SliderInt to avoid duplicated calls. const char* baseLabel = nullptr; - const std::array* scalesPtr = nullptr; - if (upscaleMethod == UpscaleMethod::kDLSS) { - baseLabel = upscalePresetsDLSS[presetIndex]; - scalesPtr = &dlssScales; - } else { + if (upscaleMethod == UpscaleMethod::kFSR) { baseLabel = upscalePresets[presetIndex]; - scalesPtr = (upscaleMethod == UpscaleMethod::kXESS) ? &xessScales : &fsrScales; + } else if (upscaleMethod == UpscaleMethod::kDLSS) { + baseLabel = upscalePresetsDLSS[presetIndex]; } - if (baseLabel && scalesPtr) { - ImGui::SliderInt("Upscale Preset (Scale %)", (int*)&settings.qualityMode, 0, 4, makePresetLabel(baseLabel, *scalesPtr).c_str()); + if (baseLabel) { + ImGui::SliderInt("Upscale Preset", (int*)&settings.qualityMode, 0, 4, baseLabel); } } } else { @@ -299,8 +249,7 @@ void Upscaling::DrawSettings() ImGui::PopStyleColor(); } - std::string backendLabel = fidelityFX.isFrameGenActive ? "FSR3" : "None"; - std::string enabledLabel = "Enabled (" + backendLabel + ")"; + std::string enabledLabel = "Enabled"; const char* toggleModes[] = { "Disabled", "Enabled" }; const char* toggleModesFG[] = { "Disabled", enabledLabel.c_str() }; @@ -454,39 +403,32 @@ void Upscaling::PostPostLoad() Upscaling::UpscaleMethod Upscaling::GetUpscaleMethod() { - if (streamline.featureDLSS) { - settings.upscaleMethod = std::clamp(settings.upscaleMethod, (uint)UpscaleMethod::kNONE, (uint)UpscaleMethod::kDLSS); - settings.qualityMode = std::clamp(settings.qualityMode, 0u, 4u); - return (UpscaleMethod)settings.upscaleMethod; - } - - settings.upscaleMethodNoDLSS = std::clamp(settings.upscaleMethodNoDLSS, (uint)UpscaleMethod::kNONE, (uint)UpscaleMethod::kXESS); + settings.upscaleMethod = std::clamp(settings.upscaleMethod, (uint)UpscaleMethod::kNONE, (uint)UpscaleMethod::kDLSS); settings.qualityMode = std::clamp(settings.qualityMode, 0u, 4u); - return (UpscaleMethod)settings.upscaleMethodNoDLSS; + return (UpscaleMethod)settings.upscaleMethod; } void Upscaling::CreateUpscalingTextureResources(UpscaleMethod a_upscalemethod) { logger::debug("[Upscaling] Creating texture resources for method {}", (int)a_upscalemethod); - auto renderer = globals::game::renderer; - auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; + if (a_upscalemethod == UpscaleMethod::kDLSS || a_upscalemethod == UpscaleMethod::kFSR) { + auto renderer = globals::game::renderer; + auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; - D3D11_TEXTURE2D_DESC texDesc{}; - D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; - D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + D3D11_TEXTURE2D_DESC texDesc{}; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; - main.texture->GetDesc(&texDesc); - main.SRV->GetDesc(&srvDesc); - main.UAV->GetDesc(&uavDesc); + main.texture->GetDesc(&texDesc); + main.SRV->GetDesc(&srvDesc); + main.UAV->GetDesc(&uavDesc); - texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; - texDesc.Format = DXGI_FORMAT_R8_UNORM; - srvDesc.Format = texDesc.Format; - uavDesc.Format = texDesc.Format; + texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; + texDesc.Format = DXGI_FORMAT_R8_UNORM; + srvDesc.Format = texDesc.Format; + uavDesc.Format = texDesc.Format; - // DLSS uses D3D11 textures (not shared D3D12) - if (a_upscalemethod == UpscaleMethod::kDLSS) { if (!reactiveMaskTexture) { reactiveMaskTexture = new Texture2D(texDesc); reactiveMaskTexture->CreateSRV(srvDesc); @@ -498,18 +440,26 @@ void Upscaling::CreateUpscalingTextureResources(UpscaleMethod a_upscalemethod) transparencyCompositionMaskTexture->CreateSRV(srvDesc); transparencyCompositionMaskTexture->CreateUAV(uavDesc); } + } + // Motion vector copy texture is only needed for DLSS + if (a_upscalemethod == UpscaleMethod::kDLSS) { if (!motionVectorCopyTexture) { + auto renderer = globals::game::renderer; auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; D3D11_TEXTURE2D_DESC motionTexDesc{}; + D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; + D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {}; + motionVector.texture->GetDesc(&motionTexDesc); + motionVector.SRV->GetDesc(&srvDesc); + motionVector.UAV->GetDesc(&uavDesc); - texDesc.Format = motionTexDesc.Format; - srvDesc.Format = texDesc.Format; - uavDesc.Format = texDesc.Format; + srvDesc.Format = motionTexDesc.Format; + uavDesc.Format = motionTexDesc.Format; - motionVectorCopyTexture = new Texture2D(texDesc); + motionVectorCopyTexture = new Texture2D(motionTexDesc); motionVectorCopyTexture->CreateSRV(srvDesc); motionVectorCopyTexture->CreateUAV(uavDesc); } @@ -521,8 +471,8 @@ void Upscaling::DestroyUpscalingTextureResources(UpscaleMethod a_upscalemethod) logger::debug("[Upscaling] Destroying texture resources for method {}", (int)a_upscalemethod); // Clean up D3D11 textures that are no longer needed - // Only destroy DLSS textures when switching away from DLSS - if (a_upscalemethod != UpscaleMethod::kDLSS) { + // Only destroy textures when switching away from methods that use them + if (a_upscalemethod != UpscaleMethod::kDLSS && a_upscalemethod != UpscaleMethod::kFSR) { if (reactiveMaskTexture) { reactiveMaskTexture->srv = nullptr; reactiveMaskTexture->uav = nullptr; @@ -540,7 +490,10 @@ void Upscaling::DestroyUpscalingTextureResources(UpscaleMethod a_upscalemethod) delete transparencyCompositionMaskTexture; transparencyCompositionMaskTexture = nullptr; } + } + // Motion vector copy texture is only needed for DLSS - destroy when switching away from DLSS + if (a_upscalemethod != UpscaleMethod::kDLSS) { if (motionVectorCopyTexture) { motionVectorCopyTexture->srv = nullptr; motionVectorCopyTexture->uav = nullptr; @@ -564,17 +517,6 @@ void Upscaling::CheckResources(UpscaleMethod a_upscalemethod) logger::debug("[Upscaling] Resource change detected - Upscale: {} -> {}, FrameGen: {} -> {}", (int)previousUpscaleMode, (int)a_upscalemethod, previousFrameGenMode, (settings.frameGenerationMode && d3d12SwapChainActive)); - // Synchronise all pending GPU work before destroying contexts - // Otherwise resources will be destroyed whilst in use, causing the device to crash - if (previousUpscaleMode == UpscaleMethod::kFSR || previousUpscaleMode == UpscaleMethod::kXESS) { - UINT64 fenceValue = sharedInteropFenceValue++; - DX::ThrowIfFailed(sharedD3D12CommandQueue->Signal(sharedD3D12Fence.get(), fenceValue)); - if (sharedD3D12Fence->GetCompletedValue() < fenceValue) { - sharedD3D12Fence->SetEventOnCompletion(fenceValue, sharedFenceEvent); - WaitForSingleObject(sharedFenceEvent, INFINITE); - } - } - // Destroy previous upscaling method resources (this will intelligently clean up based on what's still needed) if (upscaleModeChanged) { DestroyUpscalingTextureResources(a_upscalemethod); @@ -583,23 +525,14 @@ void Upscaling::CheckResources(UpscaleMethod a_upscalemethod) streamline.DestroyDLSSResources(); else if (previousUpscaleMode == UpscaleMethod::kFSR) fidelityFX.DestroyFSRResources(); - else if (previousUpscaleMode == UpscaleMethod::kXESS) - xess.DestroyXeSSResources(); - } - // Handle shared resource changes - if (frameGenModeChanged || upscaleModeChanged) { - UpdateSharedResources(); + if (a_upscalemethod == UpscaleMethod::kFSR) + fidelityFX.CreateFSRResources(); } // Create new upscaling method resources if (upscaleModeChanged) { CreateUpscalingTextureResources(a_upscalemethod); - - if (a_upscalemethod == UpscaleMethod::kFSR) - fidelityFX.CreateFSRResources(); - else if (a_upscalemethod == UpscaleMethod::kXESS) - xess.CreateXeSSResources(); } previousUpscaleMode = a_upscalemethod; @@ -619,15 +552,12 @@ ID3D11ComputeShader* Upscaling::GetEncodeTexturesCS() // Add upscale method define switch (upscaleMethod) { - case UpscaleMethod::kFSR: - defines.push_back({ "FSR", "" }); - break; - case UpscaleMethod::kXESS: - defines.push_back({ "XESS", "" }); - break; case UpscaleMethod::kDLSS: defines.push_back({ "DLSS", "" }); break; + case UpscaleMethod::kFSR: + defines.push_back({ "FSR", "" }); + break; default: // No define for NONE or TAA break; @@ -715,70 +645,6 @@ void Upscaling::ConfigureTAA() BSImagespaceShaderISTemporalAA->taaEnabled = upscaleMethod != UpscaleMethod::kNONE; } -void Upscaling::PopulateCachedPresetScales(UpscaleMethod a_method) -{ - try { - // If we already have valid cached scales for this method, no work needed - if (cachedPresetMethod == a_method && cachedPresetScales[0] != Upscaling::kScaleUnavailable) { - return; - } - - // If method changed, mark cache as uninitialized - if (cachedPresetMethod != a_method) { - for (auto& v : cachedPresetScales) v = Upscaling::kScaleUnavailable; - cachedPresetMethod = UpscaleMethod::kNONE; - } - - auto screenSize = globals::state->screenSize; - bool success = false; - - if (a_method == UpscaleMethod::kFSR) { - if (fidelityFX.featureFSR3 || fidelityFX.featureFSR3FG) { - for (uint32_t q = 0; q < cachedPresetScales.size(); ++q) { - float s = fidelityFX.GetInputResolutionScale((uint32_t)screenSize.x, (uint32_t)screenSize.y, q); - // Treat near-1.0 scales as exact 1.0 to avoid showing 99% and to - // ensure IsUpscalingActive() treats them as non-downscaling. - if (s >= 0.99f) - s = 1.0f; - cachedPresetScales[q] = s; - } - success = true; - cachedPresetMethod = UpscaleMethod::kFSR; - } - } else if (a_method == UpscaleMethod::kXESS) { - if (xess.featureXeSS) { - for (uint32_t q = 0; q < cachedPresetScales.size(); ++q) { - float s = xess.GetInputResolutionScale((uint32_t)screenSize.x, (uint32_t)screenSize.y, q); - if (s >= 0.99f) - s = 1.0f; - cachedPresetScales[q] = s; - } - success = true; - cachedPresetMethod = UpscaleMethod::kXESS; - } - } else if (a_method == UpscaleMethod::kDLSS) { - if (streamline.featureDLSS) { - for (uint32_t q = 0; q < cachedPresetScales.size(); ++q) { - float s = streamline.GetInputResolutionScale((uint32_t)screenSize.x, (uint32_t)screenSize.y, q); - if (s >= 0.99f) - s = 1.0f; - cachedPresetScales[q] = s; - } - success = true; - cachedPresetMethod = UpscaleMethod::kDLSS; - } - } - - if (!success) { - for (auto& v : cachedPresetScales) v = Upscaling::kScaleUnavailable; - cachedPresetMethod = UpscaleMethod::kNONE; - } - } catch (...) { - for (auto& v : cachedPresetScales) v = Upscaling::kScaleUnavailable; - cachedPresetMethod = UpscaleMethod::kNONE; - } -} - void Upscaling::ConfigureUpscaling(RE::BSGraphics::State* a_viewport) { auto upscaleMethod = GetUpscaleMethod(); @@ -808,8 +674,6 @@ void Upscaling::ConfigureUpscaling(RE::BSGraphics::State* a_viewport) if (globals::game::isVR) { resolutionScaleBase = 1.0f; - } else if (upscaleMethod == UpscaleMethod::kXESS) { - resolutionScaleBase = xess.GetInputResolutionScale((uint32_t)screenSize.x, (uint32_t)screenSize.y, settings.qualityMode); } else if (upscaleMethod == UpscaleMethod::kDLSS) { resolutionScaleBase = streamline.GetInputResolutionScale((uint32_t)screenSize.x, (uint32_t)screenSize.y, settings.qualityMode); } else if (upscaleMethod == UpscaleMethod::kFSR) { @@ -856,9 +720,6 @@ void Upscaling::ConfigureUpscaling(RE::BSGraphics::State* a_viewport) // Disable dynamic resolution unless the game explictly enables it if (!globals::game::isVR) runtimeData.dynamicResolutionLock = 1; - - // Populate cached scales for the current method - PopulateCachedPresetScales(upscaleMethod); } void Upscaling::SetupResources() @@ -882,12 +743,6 @@ void Upscaling::SetupResources() srvDesc.Format = texDesc.Format; uavDesc.Format = texDesc.Format; - // Initial resource allocation will be handled by CheckResources() during first upscaling call - // This avoids creating unnecessary resources at startup - - // Initialize all shared resources based on current settings - UpdateSharedResources(); - D3D11_DEPTH_STENCIL_DESC depthStencilDesc = {}; depthStencilDesc.DepthEnable = true; // Enable depth testing depthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; // Write to all depth bits @@ -921,21 +776,6 @@ void Upscaling::SetupResources() // Create upscaling data constant buffer for encode textures compute shader upscalingDataCB = new ConstantBuffer(ConstantBufferDesc()); - // Create NIS sharpener texture with swapchain format and UAV access - D3D11_TEXTURE2D_DESC nisTexDesc = texDesc; - nisTexDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - nisTexDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS; - - D3D11_SHADER_RESOURCE_VIEW_DESC nisSrvDesc = srvDesc; - nisSrvDesc.Format = nisTexDesc.Format; - - D3D11_UNORDERED_ACCESS_VIEW_DESC nisUavDesc = uavDesc; - nisUavDesc.Format = nisTexDesc.Format; - - nisSharpenerTexture = new Texture2D(nisTexDesc); - nisSharpenerTexture->CreateSRV(nisSrvDesc); - nisSharpenerTexture->CreateUAV(nisUavDesc); - // Create blend state for depth upscaling D3D11_BLEND_DESC blendDesc = {}; blendDesc.AlphaToCoverageEnable = false; @@ -958,20 +798,12 @@ void Upscaling::SetupResources() rasterizerDesc.AntialiasedLineEnable = false; DX::ThrowIfFailed(globals::d3d::device->CreateRasterizerState(&rasterizerDesc, upscaleRasterizerState.put())); - // Create shared D3D11/D3D12 fences for synchronization - winrt::com_ptr d3d11Device5; - DX::ThrowIfFailed(globals::d3d::device->QueryInterface(IID_PPV_ARGS(d3d11Device5.put()))); - - HANDLE sharedFenceHandle; - DX::ThrowIfFailed(sharedD3D12Device->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(sharedD3D12Fence.put()))); - DX::ThrowIfFailed(sharedD3D12Device->CreateSharedHandle(sharedD3D12Fence.get(), nullptr, GENERIC_ALL, nullptr, &sharedFenceHandle)); - DX::ThrowIfFailed(d3d11Device5->OpenSharedFence(sharedFenceHandle, IID_PPV_ARGS(sharedD3D11Fence.put()))); - CloseHandle(sharedFenceHandle); + CheckResources(GetUpscaleMethod()); - auto upscaleMethod = GetUpscaleMethod(); + if (d3d12SwapChainActive) + dx12SwapChain.CreateSharedResources(); - // Delete or create resources as necessary - CheckResources(upscaleMethod); + copyDepthToSharedBufferPS.attach((ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\Upscaling\\CopyDepthToSharedBufferPS.hlsl", { { "PSHADER", "" } }, "ps_5_0")); } void Upscaling::ClearShaderCache() @@ -985,238 +817,65 @@ void Upscaling::ClearShaderCache() upscaleVS = nullptr; // com_ptr automatically releases } -void Upscaling::CreateSharedD3D12Device(IDXGIAdapter* a_dxgiAdapter) -{ - // Create D3D12 device on same adapter - DX::ThrowIfFailed(D3D12CreateDevice(a_dxgiAdapter, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(sharedD3D12Device.put()))); - - // Create command queue - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; - queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; - queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; - queueDesc.NodeMask = 0; - - DX::ThrowIfFailed(sharedD3D12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(sharedD3D12CommandQueue.put()))); - - // Create command allocator - DX::ThrowIfFailed(sharedD3D12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(sharedD3D12CommandAllocator.put()))); - - // Create command list - DX::ThrowIfFailed(sharedD3D12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, sharedD3D12CommandAllocator.get(), nullptr, IID_PPV_ARGS(sharedD3D12CommandList.put()))); - - // Create fence for synchronization - DX::ThrowIfFailed(sharedD3D12Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(sharedD3D12Fence.put()))); - - sharedFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (sharedFenceEvent == nullptr) { - throw std::runtime_error("Failed to create shared fence event"); - } - - // Close initial command list - sharedD3D12CommandList->Close(); - - logger::info("[Upscaling] Shared D3D12 device and interop resources created successfully"); -} - -void Upscaling::UpdateSharedResources() -{ - logger::debug("[Upscaling] Updating shared D3D12 resources"); - - auto currentMethod = GetUpscaleMethod(); - - // Determine current feature requirements - bool needsUpscalingResources = (currentMethod == UpscaleMethod::kFSR || currentMethod == UpscaleMethod::kXESS); - bool needsFSRSpecific = (currentMethod == UpscaleMethod::kFSR); - bool needsFrameGenResources = (settings.frameGenerationMode && d3d12SwapChainActive); - bool needsSharedBasics = needsUpscalingResources || needsFrameGenResources; - - if (!needsSharedBasics) { - // Clean up all resources when nothing is needed - if (inputColorBufferShared12) { - delete inputColorBufferShared12; - inputColorBufferShared12 = nullptr; - } - if (outputColorBufferShared12) { - delete outputColorBufferShared12; - outputColorBufferShared12 = nullptr; - } - if (reactiveMaskShared12) { - delete reactiveMaskShared12; - reactiveMaskShared12 = nullptr; - } - if (transparencyCompositionMaskShared12) { - delete transparencyCompositionMaskShared12; - transparencyCompositionMaskShared12 = nullptr; - } - if (!d3d12SwapChainActive) { - if (depthBufferShared12) { - delete depthBufferShared12; - depthBufferShared12 = nullptr; - } - if (motionVectorBufferShared12) { - delete motionVectorBufferShared12; - motionVectorBufferShared12 = nullptr; - } - } - copyDepthToSharedBufferPS = nullptr; - return; - } - - // Get required interfaces - winrt::com_ptr d3d11Device5; - DX::ThrowIfFailed(globals::d3d::device->QueryInterface(IID_PPV_ARGS(d3d11Device5.put()))); - - auto renderer = globals::game::renderer; - auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; - - D3D11_TEXTURE2D_DESC texDesc{}; - main.texture->GetDesc(&texDesc); - - // Upscaling-specific resources (FSR/XeSS) - if (needsUpscalingResources) { - if (!inputColorBufferShared12) { - inputColorBufferShared12 = new WrappedResource(texDesc, d3d11Device5.get(), sharedD3D12Device.get()); - } - if (!outputColorBufferShared12) { - outputColorBufferShared12 = new WrappedResource(texDesc, d3d11Device5.get(), sharedD3D12Device.get()); - } - - texDesc.Format = DXGI_FORMAT_R8_UNORM; - if (!reactiveMaskShared12) { - reactiveMaskShared12 = new WrappedResource(texDesc, d3d11Device5.get(), sharedD3D12Device.get()); - } - } else { - // Clean up upscaling-only resources - if (inputColorBufferShared12) { - delete inputColorBufferShared12; - inputColorBufferShared12 = nullptr; - } - if (outputColorBufferShared12) { - delete outputColorBufferShared12; - outputColorBufferShared12 = nullptr; - } - if (reactiveMaskShared12) { - delete reactiveMaskShared12; - reactiveMaskShared12 = nullptr; - } - } - - // FSR-specific resources - if (needsFSRSpecific) { - texDesc.Format = DXGI_FORMAT_R8_UNORM; - if (!transparencyCompositionMaskShared12) { - transparencyCompositionMaskShared12 = new WrappedResource(texDesc, d3d11Device5.get(), sharedD3D12Device.get()); - } - } else { - if (transparencyCompositionMaskShared12) { - delete transparencyCompositionMaskShared12; - transparencyCompositionMaskShared12 = nullptr; - } - } - - // Shared resources (depth/motion - needed by both upscaling and frame generation) - if (needsSharedBasics) { - texDesc.Format = DXGI_FORMAT_R32_FLOAT; - if (!depthBufferShared12) { - depthBufferShared12 = new WrappedResource(texDesc, d3d11Device5.get(), sharedD3D12Device.get()); - } - - auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; - motionVector.texture->GetDesc(&texDesc); - if (!motionVectorBufferShared12) { - motionVectorBufferShared12 = new WrappedResource(texDesc, d3d11Device5.get(), sharedD3D12Device.get()); - } - - if (!copyDepthToSharedBufferPS) { - copyDepthToSharedBufferPS.attach((ID3D11PixelShader*)Util::CompileShader(L"Data\\Shaders\\Upscaling\\CopyDepthToSharedBufferPS.hlsl", { { "PSHADER", "" } }, "ps_5_0")); - } - } else if (!d3d12SwapChainActive) { - if (depthBufferShared12) { - delete depthBufferShared12; - depthBufferShared12 = nullptr; - } - if (motionVectorBufferShared12) { - delete motionVectorBufferShared12; - motionVectorBufferShared12 = nullptr; - } - copyDepthToSharedBufferPS = nullptr; - } - - logger::debug("[Upscaling] Shared resource update complete - Upscaling: {}, FSR: {}, FrameGen: {}", - needsUpscalingResources, needsFSRSpecific, needsFrameGenResources); -} - void Upscaling::CopySharedD3D12Resources() { - // Only copy once per frame for all upscaling systems (XeSS, Frame Generation, etc.) - if (!sharedResourcesFrameChecker.IsNewFrame()) - return; - - auto upscaleMethod = GetUpscaleMethod(); - globals::state->BeginPerfEvent("Copy Shared D3D12 Resources"); auto renderer = globals::game::renderer; auto context = globals::d3d::context; - // Not required by XeSS - if (upscaleMethod == UpscaleMethod::kFSR || (d3d12SwapChainActive && settings.frameGenerationMode && upscaleMethod != UpscaleMethod::kXESS)) { - auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; - context->CopyResource(motionVectorBufferShared12->resource11, motionVector.texture); - } + auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; + context->CopyResource(dx12SwapChain.motionVectorBufferShared12->resource11, motionVector.texture); - if (upscaleMethod == UpscaleMethod::kFSR || upscaleMethod == UpscaleMethod::kXESS || d3d12SwapChainActive && settings.frameGenerationMode) { - auto& depth = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kMAIN]; + auto& depth = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kMAIN]; - { - // Set up viewport for fullscreen rendering - auto screenSize = globals::state->screenSize; - - D3D11_VIEWPORT viewport = {}; - viewport.TopLeftX = 0.0f; - viewport.TopLeftY = 0.0f; - viewport.Width = screenSize.x; - viewport.Height = screenSize.y; - viewport.MinDepth = 0.0f; - viewport.MaxDepth = 1.0f; - context->RSSetViewports(1, &viewport); - - // Set up Input Assembler for fullscreen triangle - context->IASetInputLayout(nullptr); - context->IASetVertexBuffers(0, 0, nullptr, nullptr, nullptr); - context->IASetIndexBuffer(nullptr, DXGI_FORMAT_UNKNOWN, 0); - context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + { + // Set up viewport for fullscreen rendering + auto screenSize = globals::state->screenSize; - // Set up vertex shader - context->VSSetShader(GetUpscaleVS(), nullptr, 0); + D3D11_VIEWPORT viewport = {}; + viewport.TopLeftX = 0.0f; + viewport.TopLeftY = 0.0f; + viewport.Width = screenSize.x; + viewport.Height = screenSize.y; + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + context->RSSetViewports(1, &viewport); - // Set up rasterizer and blend states - context->RSSetState(upscaleRasterizerState.get()); - context->OMSetBlendState(upscaleBlendState.get(), nullptr, 0xffffffff); + // Set up Input Assembler for fullscreen triangle + context->IASetInputLayout(nullptr); + context->IASetVertexBuffers(0, 0, nullptr, nullptr, nullptr); + context->IASetIndexBuffer(nullptr, DXGI_FORMAT_UNKNOWN, 0); + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); - // Set up pixel shader resources - ID3D11ShaderResourceView* views[1] = { depth.depthSRV }; - context->PSSetShaderResources(0, ARRAYSIZE(views), views); + // Set up vertex shader + context->VSSetShader(GetUpscaleVS(), nullptr, 0); - // Set render target view for pixel shader output - ID3D11RenderTargetView* rtvs[1] = { depthBufferShared12->rtv }; - context->OMSetRenderTargets(ARRAYSIZE(rtvs), rtvs, nullptr); + // Set up rasterizer and blend states + context->RSSetState(upscaleRasterizerState.get()); + context->OMSetBlendState(upscaleBlendState.get(), nullptr, 0xffffffff); - context->PSSetShader(copyDepthToSharedBufferPS.get(), nullptr, 0); + // Set up pixel shader resources + ID3D11ShaderResourceView* views[1] = { depth.depthSRV }; + context->PSSetShaderResources(0, ARRAYSIZE(views), views); - context->Draw(3, 0); - } + // Set render target view for pixel shader output + ID3D11RenderTargetView* rtvs[1] = { dx12SwapChain.depthBufferShared12->rtv }; + context->OMSetRenderTargets(ARRAYSIZE(rtvs), rtvs, nullptr); - // Clean up - ID3D11ShaderResourceView* views[1] = { nullptr }; - context->PSSetShaderResources(0, ARRAYSIZE(views), views); + context->PSSetShader(copyDepthToSharedBufferPS.get(), nullptr, 0); - context->OMSetRenderTargets(0, nullptr, nullptr); - context->PSSetShader(nullptr, nullptr, 0); - context->VSSetShader(nullptr, nullptr, 0); + context->Draw(3, 0); } + // Clean up + ID3D11ShaderResourceView* views[1] = { nullptr }; + context->PSSetShaderResources(0, ARRAYSIZE(views), views); + + context->OMSetRenderTargets(0, nullptr, nullptr); + context->PSSetShader(nullptr, nullptr, 0); + context->VSSetShader(nullptr, nullptr, 0); + globals::state->EndPerfEvent(); } @@ -1377,11 +1036,10 @@ float Upscaling::GetFrameGenerationFrameTime() const // Unified interface methods void Upscaling::LoadUpscalingSDKs() { - // Initialize all upscaling SDK components during plugin startup + // Initialize upscaling SDK components during plugin startup // This ensures all SDKs are available before any D3D device creation streamline.LoadInterposer(); - fidelityFX.LoadFFX(); - xess.LoadXeSS(); + fidelityFX.LoadFFX(); // Only for frame generation now } void Upscaling::CheckFrameConstants() @@ -1473,6 +1131,7 @@ void Upscaling::Upscale() context->OMSetRenderTargets(0, nullptr, nullptr); // Unbind all bound render targets auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; + auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; auto dispatchCount = Util::GetScreenDispatchCount(true); @@ -1481,7 +1140,6 @@ void Upscaling::Upscale() auto& temporalAAMask = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kTEMPORAL_AA_MASK]; auto& normals = renderer->GetRuntimeData().renderTargets[globals::deferred->forwardRenderTargets[2]]; - auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; auto& depth = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kMAIN]; { @@ -1497,24 +1155,7 @@ void Upscaling::Upscale() ID3D11ShaderResourceView* views[4] = { temporalAAMask.SRV, normals.SRV, motionVector.SRV, depth.depthSRV }; context->CSSetShaderResources(0, ARRAYSIZE(views), views); - // Use shared D3D12 textures for FSR/XeSS, regular D3D11 textures for DLSS - ID3D11UnorderedAccessView* reactiveMaskUAV = upscaleMethod == UpscaleMethod::kDLSS ? reactiveMaskTexture->uav.get() : reactiveMaskShared12->uav; - - ID3D11UnorderedAccessView* transparencyUAV = nullptr; - if (upscaleMethod == UpscaleMethod::kDLSS) { - transparencyUAV = transparencyCompositionMaskTexture->uav.get(); - } else if (upscaleMethod == UpscaleMethod::kFSR) { - transparencyUAV = transparencyCompositionMaskShared12->uav; - } - - ID3D11UnorderedAccessView* motionVectorUAV = nullptr; - if (upscaleMethod == UpscaleMethod::kDLSS) { - motionVectorUAV = motionVectorCopyTexture->uav.get(); - } else if (upscaleMethod == UpscaleMethod::kXESS) { - motionVectorUAV = motionVectorBufferShared12->uav; - } - - ID3D11UnorderedAccessView* uavs[3] = { reactiveMaskUAV, transparencyUAV, motionVectorUAV }; + ID3D11UnorderedAccessView* uavs[3] = { reactiveMaskTexture->uav.get(), transparencyCompositionMaskTexture->uav.get(), upscaleMethod == UpscaleMethod::kDLSS ? motionVectorCopyTexture->uav.get() : nullptr }; context->CSSetUnorderedAccessViews(0, ARRAYSIZE(uavs), uavs, nullptr); context->CSSetShader(GetEncodeTexturesCS(), nullptr, 0); @@ -1540,69 +1181,14 @@ void Upscaling::Upscale() { state->BeginPerfEvent("Upscaling"); - if (upscaleMethod == UpscaleMethod::kDLSS) - streamline.Upscale(main.texture, reactiveMaskTexture->resource.get(), transparencyCompositionMaskTexture->resource.get(), motionVectorCopyTexture->resource.get(), sl::DLSSPreset::ePresetK); - else { - auto renderSize = Util::ConvertToDynamic(globals::state->screenSize); - - // Copy input color texture to shared D3D12 resource - context->CopyResource(inputColorBufferShared12->resource11, main.texture); - - // Wait for D3D11 to finish - winrt::com_ptr d3d11Context4; - DX::ThrowIfFailed(context->QueryInterface(IID_PPV_ARGS(d3d11Context4.put()))); - DX::ThrowIfFailed(d3d11Context4->Signal(sharedD3D11Fence.get(), sharedInteropFenceValue)); - DX::ThrowIfFailed(sharedD3D12CommandQueue->Wait(sharedD3D12Fence.get(), sharedInteropFenceValue)); - sharedInteropFenceValue++; - - // Reset command allocator and list - DX::ThrowIfFailed(sharedD3D12CommandAllocator->Reset()); - DX::ThrowIfFailed(sharedD3D12CommandList->Reset(sharedD3D12CommandAllocator.get(), nullptr)); - - if (upscaleMethod == UpscaleMethod::kFSR) { - fidelityFX.Upscale( - inputColorBufferShared12->resource.get(), - motionVectorBufferShared12->resource.get(), - depthBufferShared12->resource.get(), - reactiveMaskShared12->resource.get(), - transparencyCompositionMaskShared12->resource.get(), - outputColorBufferShared12->resource.get(), - sharedD3D12CommandList.get(), - (uint32_t)renderSize.x, - (uint32_t)renderSize.y, - jitter); - } else { - xess.Upscale( - inputColorBufferShared12->resource.get(), - motionVectorBufferShared12->resource.get(), - depthBufferShared12->resource.get(), - reactiveMaskShared12->resource.get(), - outputColorBufferShared12->resource.get(), - sharedD3D12CommandList.get(), - (uint32_t)renderSize.x, - (uint32_t)renderSize.y, - jitter); - } - - // Close and execute command list - DX::ThrowIfFailed(sharedD3D12CommandList->Close()); - - ID3D12CommandList* commandLists[] = { sharedD3D12CommandList.get() }; - sharedD3D12CommandQueue->ExecuteCommandLists(1, commandLists); - - // Wait for D3D12 to finish - DX::ThrowIfFailed(sharedD3D12CommandQueue->Signal(sharedD3D12Fence.get(), sharedInteropFenceValue)); - DX::ThrowIfFailed(d3d11Context4->Wait(sharedD3D11Fence.get(), sharedInteropFenceValue)); - sharedInteropFenceValue++; - - // Copy back to main buffer - context->CopyResource(main.texture, outputColorBufferShared12->resource11); + if (upscaleMethod == UpscaleMethod::kDLSS) { + streamline.Upscale(main.texture, reactiveMaskTexture->resource.get(), transparencyCompositionMaskTexture->resource.get(), motionVectorCopyTexture->resource.get()); + } else if (upscaleMethod == UpscaleMethod::kFSR) { + fidelityFX.Upscale(main.texture, reactiveMaskTexture->resource.get(), transparencyCompositionMaskTexture->resource.get(), motionVector.texture); } state->EndPerfEvent(); } - - PopulateCachedPresetScales(upscaleMethod); } void Upscaling::PerformUpscaling() @@ -1748,7 +1334,8 @@ void Upscaling::Main_PostProcessing::thunk(RE::ImageSpaceManager* a1, uint32_t a auto& upscaling = globals::features::upscaling; auto upscaleMethod = upscaling.GetUpscaleMethod(); - upscaling.CopySharedD3D12Resources(); + if (upscaling.d3d12SwapChainActive && upscaling.settings.frameGenerationMode) + upscaling.CopySharedD3D12Resources(); if (upscaleMethod != UpscaleMethod::kNONE && upscaleMethod != UpscaleMethod::kTAA) upscaling.PerformUpscaling(); diff --git a/src/Features/Upscaling.h b/src/Features/Upscaling.h index 6659f18f5f..41538ab833 100644 --- a/src/Features/Upscaling.h +++ b/src/Features/Upscaling.h @@ -1,9 +1,9 @@ #pragma once #include "Feature.h" +#include "Upscaling/DX12SwapChain.h" #include "Upscaling/FidelityFX.h" #include "Upscaling/Streamline.h" -#include "Upscaling/XeSS.h" #include #include #include @@ -43,14 +43,12 @@ struct Upscaling : Feature kNONE, kTAA, kFSR, - kXESS, kDLSS }; struct Settings { - uint upscaleMethod = (uint)UpscaleMethod::kDLSS; - uint upscaleMethodNoDLSS = (uint)UpscaleMethod::kFSR; + uint upscaleMethod = (uint)UpscaleMethod::kTAA; uint qualityMode = 1; // Default to Quality (1=Quality, 2=Balanced, 3=Performance, 4=Ultra Performance, 0=Native AA) uint frameLimitMode = 1; uint frameGenerationMode = 1; @@ -110,7 +108,6 @@ struct Upscaling : Feature void CheckResources(UpscaleMethod a_upscalemethod); void CreateUpscalingTextureResources(UpscaleMethod a_upscalemethod); void DestroyUpscalingTextureResources(UpscaleMethod a_upscalemethod); - void UpdateSharedResources(); winrt::com_ptr encodeTexturesCS[5]; // One for each UpscaleMethod ID3D11ComputeShader* GetEncodeTexturesCS(); @@ -136,39 +133,13 @@ struct Upscaling : Feature Texture2D* reactiveMaskTexture = nullptr; Texture2D* transparencyCompositionMaskTexture = nullptr; Texture2D* motionVectorCopyTexture = nullptr; - Texture2D* nisSharpenerTexture = nullptr; virtual void ClearShaderCache() override; - // Shared D3D12 device and interop resources - winrt::com_ptr sharedD3D12Device; - winrt::com_ptr sharedD3D12CommandQueue; - winrt::com_ptr sharedD3D12CommandAllocator; - winrt::com_ptr sharedD3D12CommandList; - winrt::com_ptr sharedD3D12Fence; - HANDLE sharedFenceEvent = nullptr; - UINT64 sharedFenceValue = 0; - - // D3D11/D3D12 shared fence for interop synchronization - winrt::com_ptr sharedD3D11Fence; - UINT64 sharedInteropFenceValue = 0; - - // Shared D3D12 resources for upscaling systems - WrappedResource* depthBufferShared12 = nullptr; - WrappedResource* motionVectorBufferShared12 = nullptr; - WrappedResource* reactiveMaskShared12 = nullptr; - WrappedResource* transparencyCompositionMaskShared12 = nullptr; - WrappedResource* inputColorBufferShared12 = nullptr; - WrappedResource* outputColorBufferShared12 = nullptr; - - // Frame tracking to ensure shared resources are only copied once per frame - Util::FrameChecker sharedResourcesFrameChecker; - // Static instances instead of singletons static inline Streamline streamline; - static inline XeSS xess; - static inline FidelityFX fidelityFX; - static inline class DX12SwapChain dx12SwapChain; + static inline FidelityFX fidelityFX; // Only for frame generation + static inline DX12SwapChain dx12SwapChain; winrt::com_ptr copyDepthToSharedBufferPS; @@ -178,7 +149,6 @@ struct Upscaling : Feature float dynamicResolutionWidthRatio = 1.0f; float dynamicResolutionHeightRatio = 1.0f; - void CreateSharedD3D12Device(IDXGIAdapter* a_dxgiAdapter); void CopySharedD3D12Resources(); void PostDisplay(); void PerformUpscaling(); @@ -215,17 +185,6 @@ struct Upscaling : Feature IDXGISwapChain* GetProxySwapChain(); private: - // Sentinel value used to mark unavailable preset scales. - static constexpr float kScaleUnavailable = 0.0f; - - // Cached preset scales for UI labels. Uninitialized entries are marked - // with kScaleUnavailable. - std::array cachedPresetScales{}; - UpscaleMethod cachedPresetMethod = UpscaleMethod::kNONE; - // Populate the cachedPresetScales for the specified method. Encapsulates - // vendor SDK calls and sentinel handling. - void PopulateCachedPresetScales(UpscaleMethod a_method); - struct Main_UpdateJitter { static void thunk(RE::BSGraphics::State* a_state); diff --git a/src/Features/Upscaling/DX12SwapChain.cpp b/src/Features/Upscaling/DX12SwapChain.cpp index 0d232531ab..a2d005809f 100644 --- a/src/Features/Upscaling/DX12SwapChain.cpp +++ b/src/Features/Upscaling/DX12SwapChain.cpp @@ -7,24 +7,28 @@ #include "FidelityFX.h" #include "Streamline.h" -void DX12SwapChain::InitializeD3D12Resources() +void DX12SwapChain::CreateD3D12Device(IDXGIAdapter* a_adapter) { - auto& upscaling = globals::features::upscaling; + DX::ThrowIfFailed(D3D12CreateDevice(a_adapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&d3d12Device))); + + D3D12_COMMAND_QUEUE_DESC queueDesc = {}; + queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL; + queueDesc.NodeMask = 0; + + DX::ThrowIfFailed(d3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue))); - // Create frame-specific command allocators and lists using shared device for (int i = 0; i < 2; i++) { - DX::ThrowIfFailed(upscaling.sharedD3D12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocators[i]))); - DX::ThrowIfFailed(upscaling.sharedD3D12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocators[i].get(), nullptr, IID_PPV_ARGS(&commandLists[i]))); + DX::ThrowIfFailed(d3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocators[i]))); + DX::ThrowIfFailed(d3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocators[i].get(), nullptr, IID_PPV_ARGS(&commandLists[i]))); commandLists[i]->Close(); } } void DX12SwapChain::CreateSwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC a_swapChainDesc) { - auto& upscaling = globals::features::upscaling; - - // Initialize D3D12 resources first - InitializeD3D12Resources(); + CreateD3D12Device(adapter); IDXGIFactory4* dxgiFactory; DX::ThrowIfFailed(adapter->GetParent(IID_PPV_ARGS(&dxgiFactory))); @@ -44,7 +48,7 @@ void DX12SwapChain::CreateSwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC ffxSwapChainDesc.desc = &swapChainDesc; ffxSwapChainDesc.dxgiFactory = dxgiFactory; ffxSwapChainDesc.fullscreenDesc = nullptr; - ffxSwapChainDesc.gameQueue = upscaling.sharedD3D12CommandQueue.get(); + ffxSwapChainDesc.gameQueue = commandQueue.get(); ffxSwapChainDesc.hwnd = a_swapChainDesc.OutputWindow; ffxSwapChainDesc.swapchain = &swapChain; @@ -64,11 +68,9 @@ void DX12SwapChain::CreateSwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC void DX12SwapChain::CreateInterop() { - auto& upscaling = globals::features::upscaling; - HANDLE sharedFenceHandle; - DX::ThrowIfFailed(upscaling.sharedD3D12Device->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d12Fence))); - DX::ThrowIfFailed(upscaling.sharedD3D12Device->CreateSharedHandle(d3d12Fence.get(), nullptr, GENERIC_ALL, nullptr, &sharedFenceHandle)); + DX::ThrowIfFailed(d3d12Device->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d12Fence))); + DX::ThrowIfFailed(d3d12Device->CreateSharedHandle(d3d12Fence.get(), nullptr, GENERIC_ALL, nullptr, &sharedFenceHandle)); DX::ThrowIfFailed(d3d11Device->OpenSharedFence(sharedFenceHandle, IID_PPV_ARGS(&d3d11Fence))); CloseHandle(sharedFenceHandle); @@ -84,10 +86,10 @@ void DX12SwapChain::CreateInterop() texDesc11.SampleDesc.Quality = 0; texDesc11.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; - swapChainBufferWrapped = new WrappedResource(texDesc11, d3d11Device.get(), upscaling.sharedD3D12Device.get()); + swapChainBufferWrapped = new WrappedResource(texDesc11, d3d11Device.get(), d3d12Device.get()); texDesc11.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - uiBufferWrapped = new WrappedResource(texDesc11, d3d11Device.get(), upscaling.sharedD3D12Device.get()); + uiBufferWrapped = new WrappedResource(texDesc11, d3d11Device.get(), d3d12Device.get()); } DXGISwapChainProxy* DX12SwapChain::GetSwapChainProxy() @@ -117,7 +119,7 @@ HRESULT DX12SwapChain::Present(UINT SyncInterval, UINT Flags) // Wait for D3D11 to finish DX::ThrowIfFailed(d3d11Context->Signal(d3d11Fence.get(), fenceValue)); - DX::ThrowIfFailed(upscaling.sharedD3D12CommandQueue->Wait(d3d12Fence.get(), fenceValue)); + DX::ThrowIfFailed(commandQueue->Wait(d3d12Fence.get(), fenceValue)); fenceValue++; // New frame, reset @@ -150,13 +152,13 @@ HRESULT DX12SwapChain::Present(UINT SyncInterval, UINT Flags) DX::ThrowIfFailed(commandLists[frameIndex]->Close()); ID3D12CommandList* commandListsToExecute[] = { commandLists[frameIndex].get() }; - upscaling.sharedD3D12CommandQueue->ExecuteCommandLists(1, commandListsToExecute); + commandQueue->ExecuteCommandLists(1, commandListsToExecute); // Present the frame DX::ThrowIfFailed(swapChain->Present(SyncInterval, Flags)); // Wait for D3D12 to finish - DX::ThrowIfFailed(upscaling.sharedD3D12CommandQueue->Signal(d3d12Fence.get(), fenceValue)); + DX::ThrowIfFailed(commandQueue->Signal(d3d12Fence.get(), fenceValue)); DX::ThrowIfFailed(d3d11Context->Wait(d3d11Fence.get(), fenceValue)); fenceValue++; @@ -213,24 +215,19 @@ float DX12SwapChain::GetFrameTime() const WrappedResource::WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc, ID3D11Device5* a_d3d11Device, ID3D12Device* a_d3d12Device) { - D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | D3D12_RESOURCE_FLAG_ALLOW_SIMULTANEOUS_ACCESS; - if (a_texDesc.BindFlags & D3D11_BIND_DEPTH_STENCIL) - flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - if (a_texDesc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) - flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - if (!(a_texDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE)) - flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; - if (!(a_texDesc.BindFlags & D3D11_BIND_RENDER_TARGET)) - flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; - D3D12_RESOURCE_DESC desc12{ D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, a_texDesc.Width, a_texDesc.Height, (UINT16)a_texDesc.ArraySize, (UINT16)a_texDesc.MipLevels, a_texDesc.Format, { a_texDesc.SampleDesc.Count, a_texDesc.SampleDesc.Quality }, D3D12_TEXTURE_LAYOUT_UNKNOWN, flags }; - D3D12_HEAP_PROPERTIES heapProp = { D3D12_HEAP_TYPE_DEFAULT, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 1, 1 }; - - DX::ThrowIfFailed(a_d3d12Device->CreateCommittedResource(&heapProp, D3D12_HEAP_FLAG_SHARED, &desc12, D3D12_RESOURCE_STATE_COMMON, nullptr, IID_PPV_ARGS(&resource))); + // Create D3D11 shared texture directly instead of wrapping D3D12 resource + a_texDesc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED | D3D11_RESOURCE_MISC_SHARED_NTHANDLE; + DX::ThrowIfFailed(a_d3d11Device->CreateTexture2D(&a_texDesc, nullptr, &resource11)); + // Get shared handle from D3D11 texture to enable D3D12 access + winrt::com_ptr dxgiResource; + DX::ThrowIfFailed(resource11->QueryInterface(IID_PPV_ARGS(dxgiResource.put()))); HANDLE sharedHandle = nullptr; - DX::ThrowIfFailed(a_d3d12Device->CreateSharedHandle(resource.get(), nullptr, GENERIC_ALL, nullptr, &sharedHandle)); + DX::ThrowIfFailed(dxgiResource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, &sharedHandle)); - DX::ThrowIfFailed(a_d3d11Device->OpenSharedResource1(sharedHandle, IID_PPV_ARGS(&resource11))); + // Open the shared D3D11 texture as D3D12 resource + DX::ThrowIfFailed(a_d3d12Device->OpenSharedHandle(sharedHandle, IID_PPV_ARGS(resource.put()))); + CloseHandle(sharedHandle); if (a_texDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE) { D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; @@ -400,4 +397,21 @@ void DX12SwapChain::SetUIBuffer() data.RTV = uiBufferWrapped->rtv; d3d11Context->OMSetRenderTargets(1, &data.RTV, nullptr); } +} + +void DX12SwapChain::CreateSharedResources() +{ + auto renderer = globals::game::renderer; + + // Create depth buffer + auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; + D3D11_TEXTURE2D_DESC texDesc{}; + main.texture->GetDesc(&texDesc); + texDesc.Format = DXGI_FORMAT_R32_FLOAT; + depthBufferShared12 = new WrappedResource(texDesc, d3d11Device.get(), d3d12Device.get()); + + // Create motion vector buffer + auto& motionVector = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMOTION_VECTOR]; + motionVector.texture->GetDesc(&texDesc); + motionVectorBufferShared12 = new WrappedResource(texDesc, d3d11Device.get(), d3d12Device.get()); } \ No newline at end of file diff --git a/src/Features/Upscaling/DX12SwapChain.h b/src/Features/Upscaling/DX12SwapChain.h index 087179b4b8..19d51e276a 100644 --- a/src/Features/Upscaling/DX12SwapChain.h +++ b/src/Features/Upscaling/DX12SwapChain.h @@ -61,7 +61,8 @@ struct DXGISwapChainProxy : IDXGISwapChain class DX12SwapChain { public: - // D3D12 resources for swap chain (uses shared device from Upscaling) + winrt::com_ptr d3d12Device; + winrt::com_ptr commandQueue; winrt::com_ptr commandAllocators[2]; winrt::com_ptr commandLists[2]; @@ -72,6 +73,10 @@ class DX12SwapChain WrappedResource* swapChainBufferWrapped; WrappedResource* uiBufferWrapped; + // D3D12 interop resources for frame generation + WrappedResource* depthBufferShared12 = nullptr; + WrappedResource* motionVectorBufferShared12 = nullptr; + winrt::com_ptr d3d11Device; winrt::com_ptr d3d11Context; @@ -92,7 +97,7 @@ class DX12SwapChain // Returns the current frame time (in seconds) for accurate FPS calculation when frame generation is active float GetFrameTime() const; - void InitializeD3D12Resources(); + void CreateD3D12Device(IDXGIAdapter* a_adapter); void CreateSwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC swapChainDesc); void CreateInterop(); @@ -107,4 +112,7 @@ class DX12SwapChain HANDLE GetFrameLatencyWaitableObject(); void SetUIBuffer(); + + // D3D12 interop resource management + void CreateSharedResources(); }; diff --git a/src/Features/Upscaling/FidelityFX.cpp b/src/Features/Upscaling/FidelityFX.cpp index bcf6517149..d7f57c1f82 100644 --- a/src/Features/Upscaling/FidelityFX.cpp +++ b/src/Features/Upscaling/FidelityFX.cpp @@ -13,14 +13,9 @@ std::vector> FidelityFX::dllVersions = {}; void FidelityFX::LoadFFX() { - // Load upscaler and frame generation DLLs and their function pointers - std::wstring upscalerDllName = L"amd_fidelityfx_upscaler_dx12.dll"; + // Load uframe generation DLL and its function pointers std::wstring framegenDllName = L"amd_fidelityfx_framegeneration_dx12.dll"; - - std::wstring upscalerPath = std::wstring(FidelityFX::PluginDir) + L"\\" + upscalerDllName; std::wstring framegenPath = std::wstring(FidelityFX::PluginDir) + L"\\" + framegenDllName; - - featureFSR3 = LoadLibrary(upscalerPath.c_str()); featureFSR3FG = LoadLibrary(framegenPath.c_str()); // Load loader DLL from plugin directory @@ -38,12 +33,6 @@ void FidelityFX::LoadFFX() ffxLoadFunctions(&ffxModule, module); - if (featureFSR3) { - logger::info("[FidelityFX] Upscaler DLL found and available"); - } else { - logger::warn("[FidelityFX] Upscaler DLL not found - FSR3 upscaling disabled"); - } - if (featureFSR3FG) { logger::info("[FidelityFX] Frame generation DLL found and available"); } else { @@ -58,16 +47,15 @@ void FidelityFX::LoadFFX() void FidelityFX::SetupFrameGeneration() { auto& swapChain = globals::features::upscaling.dx12SwapChain; - auto& upscaling = globals::features::upscaling; ffx::CreateContextDescFrameGeneration createFg{}; createFg.displaySize = { swapChain.swapChainDesc.Width, swapChain.swapChainDesc.Height }; createFg.maxRenderSize = createFg.displaySize; - createFg.flags = 0; + createFg.flags = FFX_FRAMEGENERATION_ENABLE_ASYNC_WORKLOAD_SUPPORT; createFg.backBufferFormat = ffxApiGetSurfaceFormatDX12(swapChain.swapChainDesc.Format); ffx::CreateBackendDX12Desc backendDesc{}; - backendDesc.device = upscaling.sharedD3D12Device.get(); + backendDesc.device = swapChain.d3d12Device.get(); if (ffx::CreateContext(frameGenContext, nullptr, createFg, backendDesc) != ffx::ReturnCode::Ok) logger::critical("[FidelityFX] Failed to create frame generation context!"); @@ -112,8 +100,8 @@ void FidelityFX::Present(bool a_useFrameGeneration) configParameters.frameID = frameID; configParameters.swapChain = swapChain.swapChain; configParameters.onlyPresentGenerated = false; - configParameters.allowAsyncWorkloads = false; configParameters.flags = 0; + configParameters.allowAsyncWorkloads = true; auto state = globals::state; @@ -137,14 +125,9 @@ void FidelityFX::Present(bool a_useFrameGeneration) } if (a_useFrameGeneration) { - auto commandList = swapChain.commandLists[swapChain.frameIndex].get(); - - auto depth = upscaling.depthBufferShared12->resource.get(); - auto motionVectors = upscaling.motionVectorBufferShared12->resource.get(); - ffx::DispatchDescFrameGenerationPrepare dispatchParameters{}; - dispatchParameters.commandList = commandList; + dispatchParameters.commandList = swapChain.commandLists[swapChain.frameIndex].get(); dispatchParameters.motionVectorScale.x = renderSize.x; dispatchParameters.motionVectorScale.y = renderSize.y; @@ -164,8 +147,8 @@ void FidelityFX::Present(bool a_useFrameGeneration) dispatchParameters.frameID = frameID; - dispatchParameters.depth = ffxApiGetResourceDX12(depth); - dispatchParameters.motionVectors = ffxApiGetResourceDX12(motionVectors); + dispatchParameters.depth = ffxApiGetResourceDX12(swapChain.depthBufferShared12->resource.get()); + dispatchParameters.motionVectors = ffxApiGetResourceDX12(swapChain.motionVectorBufferShared12->resource.get()); ffx::DispatchDescFrameGenerationPrepareCameraInfo cameraConfig{}; @@ -202,152 +185,133 @@ void FidelityFX::Present(bool a_useFrameGeneration) void FidelityFX::CreateFSRResources() { auto state = globals::state; - auto& upscaling = globals::features::upscaling; - ffx::CreateContextDescUpscale createUpscaling; - createUpscaling.maxRenderSize.width = (uint)state->screenSize.x; - createUpscaling.maxRenderSize.height = (uint)state->screenSize.y; - createUpscaling.maxUpscaleSize.width = (uint)state->screenSize.x; - createUpscaling.maxUpscaleSize.height = (uint)state->screenSize.y; - createUpscaling.flags = FFX_UPSCALE_ENABLE_NON_LINEAR_COLORSPACE | FFX_UPSCALE_ENABLE_AUTO_EXPOSURE; - - createUpscaling.fpMessage = [](uint32_t type, const wchar_t* wideMessage) { - auto message = stl::utf16_to_utf8(wideMessage); - if (message.has_value()) { - if (type == FFX_API_MESSAGE_TYPE_ERROR) - logger::error("[FidelityFX] {}", message.value()); - else - logger::warn("[FidelityFX] {}", message.value()); - } - }; + // Prevent multiple allocations + if (fsrScratchBuffer) { + logger::warn("[FidelityFX] FSR resources already created, skipping allocation"); + return; + } - ffx::CreateBackendDX12Desc backendDesc{}; - backendDesc.device = upscaling.sharedD3D12Device.get(); + auto fsrDevice = ffxGetDeviceDX11(globals::d3d::device); - if (ffx::CreateContext(upscalingContext, nullptr, createUpscaling, backendDesc) != ffx::ReturnCode::Ok) - logger::critical("[FidelityFX] Failed to create FSR3 API context"); + size_t scratchBufferSize = ffxGetScratchMemorySizeDX11(FFX_FSR3UPSCALER_CONTEXT_COUNT); + fsrScratchBuffer = calloc(scratchBufferSize, 1); + if (!fsrScratchBuffer) { + logger::critical("[FidelityFX] Failed to allocate FSR3 scratch buffer memory!"); + return; + } + memset(fsrScratchBuffer, 0, scratchBufferSize); + + FfxInterface fsrInterface; + if (ffxGetInterfaceDX11(&fsrInterface, fsrDevice, fsrScratchBuffer, scratchBufferSize, FFX_FSR3UPSCALER_CONTEXT_COUNT) != FFX_OK) { + logger::critical("[FidelityFX] Failed to initialize FSR3 backend interface!"); + free(fsrScratchBuffer); + fsrScratchBuffer = nullptr; + return; + } - // Query version information after context creation - QueryVersion(); + FfxFsr3ContextDescription contextDescription; + contextDescription.maxRenderSize.width = (uint)state->screenSize.x; + contextDescription.maxRenderSize.height = (uint)state->screenSize.y; + contextDescription.maxUpscaleSize.width = (uint)state->screenSize.x; + contextDescription.maxUpscaleSize.height = (uint)state->screenSize.y; + contextDescription.displaySize.width = (uint)state->screenSize.x; + contextDescription.displaySize.height = (uint)state->screenSize.y; + contextDescription.flags = FFX_FSR3_ENABLE_UPSCALING_ONLY | FFX_FSR3_ENABLE_AUTO_EXPOSURE | FFX_FSR3_ENABLE_HIGH_DYNAMIC_RANGE; + contextDescription.backendInterfaceUpscaling = fsrInterface; + + if (ffxFsr3ContextCreate(&fsrContext, &contextDescription) != FFX_OK) { + logger::critical("[FidelityFX] Failed to initialize FSR3 context!"); + free(fsrScratchBuffer); + fsrScratchBuffer = nullptr; + return; + } } void FidelityFX::DestroyFSRResources() { - if (ffx::DestroyContext(upscalingContext) != ffx::ReturnCode::Ok) - logger::critical("[FidelityFX] Failed to destroy FSR3 API context"); - upscalingContext = {}; + if (ffxFsr3ContextDestroy(&fsrContext) != FFX_OK) + logger::critical("[FidelityFX] Failed to destroy FSR3 context!"); + + // Free the scratch buffer to prevent memory leak + if (fsrScratchBuffer) { + free(fsrScratchBuffer); + fsrScratchBuffer = nullptr; + } } float FidelityFX::GetInputResolutionScale([[maybe_unused]] uint32_t outputWidth, [[maybe_unused]] uint32_t outputHeight, uint32_t qualityMode) { - float upscaleRatio = 1.0f; - - ffx::QueryDescUpscaleGetUpscaleRatioFromQualityMode query{}; - query.qualityMode = qualityMode; - query.pOutUpscaleRatio = &upscaleRatio; + return 1.0f / ffxFsr3GetUpscaleRatioFromQualityMode((FfxFsr3QualityMode)qualityMode); +} - if (ffx::Query(upscalingContext, query) != ffx::ReturnCode::Ok) { - logger::critical("[FidelityFX] Failed to query upscale ratio for quality preset {}", qualityMode); - return 1.0f; +FfxResource ffxGetResource(ID3D11Resource* dx11Resource, + [[maybe_unused]] wchar_t const* ffxResName, + FfxResourceStates state = FFX_RESOURCE_STATE_PIXEL_COMPUTE_READ) +{ + FfxResource resource = {}; + resource.resource = reinterpret_cast(const_cast(dx11Resource)); + resource.state = state; + resource.description = GetFfxResourceDescriptionDX11(dx11Resource); + +#ifdef _DEBUG + if (ffxResName) { + wcscpy_s(resource.name, ffxResName); } +#endif - // Convert upscale ratio to resolution scale (input resolution / output resolution) - float resolutionScale = 1.0f / upscaleRatio; - - return resolutionScale; + return resource; } -void FidelityFX::Upscale( - ID3D12Resource* a_inputColorTexture, - ID3D12Resource* a_motionVectorTexture, - ID3D12Resource* a_depthTexture, - ID3D12Resource* a_reactiveMask, - ID3D12Resource* a_transparencyCompositionMask, - ID3D12Resource* a_outputTexture, - ID3D12GraphicsCommandList* a_commandList, - uint32_t a_renderWidth, - uint32_t a_renderHeight, - float2 a_jitter) +void FidelityFX::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors) { + auto renderer = globals::game::renderer; + auto context = globals::d3d::context; auto state = globals::state; + auto& depthTexture = renderer->GetDepthStencilData().depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kMAIN]; - ffx::DispatchDescUpscale dispatchUpscale{}; + auto screenSize = state->screenSize; + auto renderSize = Util::ConvertToDynamic(screenSize); - dispatchUpscale.commandList = a_commandList; - dispatchUpscale.color = ffxApiGetResourceDX12(a_inputColorTexture, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.depth = ffxApiGetResourceDX12(a_depthTexture, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.motionVectors = ffxApiGetResourceDX12(a_motionVectorTexture, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.output = ffxApiGetResourceDX12(a_outputTexture, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS); - dispatchUpscale.exposure = ffxApiGetResourceDX12(nullptr, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.reactive = ffxApiGetResourceDX12(a_reactiveMask, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.transparencyAndComposition = ffxApiGetResourceDX12(a_transparencyCompositionMask, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); + { + FfxFsr3DispatchUpscaleDescription dispatchParameters{}; - dispatchUpscale.jitterOffset.x = -a_jitter.x; - dispatchUpscale.jitterOffset.y = -a_jitter.y; - dispatchUpscale.motionVectorScale.x = (globals::game::isVR ? 0.5f : 1.0f) * (float)a_renderWidth; - dispatchUpscale.motionVectorScale.y = (float)a_renderHeight; - dispatchUpscale.reset = false; - dispatchUpscale.enableSharpening = true; - dispatchUpscale.sharpness = 0.5f; + dispatchParameters.commandList = ffxGetCommandListDX11(context); + dispatchParameters.color = ffxGetResource(a_upscalingTexture, L"FSR3_Input_OutputColor"); + dispatchParameters.depth = ffxGetResource(depthTexture.texture, L"FSR3_InputDepth"); + dispatchParameters.motionVectors = ffxGetResource(a_motionVectors, L"FSR3_InputMotionVectors"); + dispatchParameters.exposure = ffxGetResource(nullptr, L"FSR3_InputExposure"); + dispatchParameters.upscaleOutput = dispatchParameters.color; + dispatchParameters.reactive = ffxGetResource(a_reactiveMask, L"FSR3_InputReactiveMap"); + dispatchParameters.transparencyAndComposition = ffxGetResource(a_transparencyCompositionMask, L"FSR3_TransparencyAndCompositionMap"); - dispatchUpscale.frameTimeDelta = static_cast(RE::GetSecondsSinceLastFrame() * 1000.f); + dispatchParameters.motionVectorScale.x = globals::game::isVR ? renderSize.x * 0.5f : renderSize.x; + dispatchParameters.motionVectorScale.y = renderSize.y; + dispatchParameters.renderSize.width = (uint)renderSize.x; + dispatchParameters.renderSize.height = (uint)renderSize.y; - dispatchUpscale.preExposure = 1.0f; - dispatchUpscale.renderSize.width = a_renderWidth; - dispatchUpscale.renderSize.height = a_renderHeight; - dispatchUpscale.upscaleSize.width = (uint32_t)state->screenSize.x; - dispatchUpscale.upscaleSize.height = (uint32_t)state->screenSize.y; + auto& upscaling = globals::features::upscaling; + auto jitter = upscaling.jitter; - dispatchUpscale.cameraFovAngleVertical = Util::GetVerticalFOVRad(); + dispatchParameters.jitterOffset.x = -jitter.x; + dispatchParameters.jitterOffset.y = -jitter.y; - dispatchUpscale.cameraFar = *globals::game::cameraFar; - dispatchUpscale.cameraNear = *globals::game::cameraNear; + dispatchParameters.frameTimeDelta = *globals::game::deltaTime * 1000.f; - dispatchUpscale.viewSpaceToMetersFactor = 0.01428222656f; + dispatchParameters.cameraFar = *globals::game::cameraFar; + dispatchParameters.cameraNear = *globals::game::cameraNear; - dispatchUpscale.flags = FFX_UPSCALE_FLAG_NON_LINEAR_COLOR_SRGB; + dispatchParameters.enableSharpening = true; + dispatchParameters.sharpness = 0.5f; - if (ffx::Dispatch(upscalingContext, dispatchUpscale) != ffx::ReturnCode::Ok) - logger::critical("[FidelityFX] Failed to upscale"); -} + dispatchParameters.cameraFovAngleVertical = Util::GetVerticalFOVRad(); + dispatchParameters.viewSpaceToMetersFactor = 0.01428222656f; + dispatchParameters.reset = false; + dispatchParameters.preExposure = 1.0f; -void FidelityFX::QueryVersion() -{ - auto& upscaling = globals::features::upscaling; + dispatchParameters.flags = 0; - // Clear existing version info - versionInfo.clear(); - - // Query upscaler versions if available - if (featureFSR3) { - ffxQueryDescGetVersions upscalerQuery{}; - upscalerQuery.header.type = FFX_API_QUERY_DESC_TYPE_GET_VERSIONS; - upscalerQuery.header.pNext = nullptr; - - ffx::CreateContextDescUpscale dummyUpscaler{}; - upscalerQuery.createDescType = dummyUpscaler.header.type; - upscalerQuery.device = upscaling.sharedD3D12Device.get(); - - uint64_t upscalerCount = 0; - upscalerQuery.outputCount = &upscalerCount; - upscalerQuery.versionIds = nullptr; - upscalerQuery.versionNames = nullptr; - - if (ffxModule.Query(nullptr, &upscalerQuery.header) == (ffxReturnCode_t)ffx::ReturnCode::Ok && upscalerCount > 0) { - // Allocate arrays for version info - std::vector upscalerIds(upscalerCount); - std::vector upscalerNames(upscalerCount); - - upscalerQuery.versionIds = upscalerIds.data(); - upscalerQuery.versionNames = upscalerNames.data(); - - // Second query to get actual data - if (ffxModule.Query(nullptr, &upscalerQuery.header) == (ffxReturnCode_t)ffx::ReturnCode::Ok) { - if (upscalerCount > 0 && upscalerNames[0]) { - versionInfo = upscalerNames[0]; - logger::info("[FidelityFX] Upscaler version: {}", versionInfo); - } - } - } + if (ffxFsr3ContextDispatchUpscale(&fsrContext, &dispatchParameters) != FFX_OK) + logger::critical("[FidelityFX] Failed to dispatch upscaling!"); } } \ No newline at end of file diff --git a/src/Features/Upscaling/FidelityFX.h b/src/Features/Upscaling/FidelityFX.h index 6b5c0aa6f7..f888b2db94 100644 --- a/src/Features/Upscaling/FidelityFX.h +++ b/src/Features/Upscaling/FidelityFX.h @@ -3,13 +3,16 @@ #include #include +#include +#include +#include + #include #include #include #include #include -#include #include "../../Buffer.h" #include "../../State.h" @@ -23,10 +26,9 @@ class FidelityFX ffx::Context swapChainContext{}; ffx::Context frameGenContext; - ffx::Context upscalingContext; + FfxFsr3Context fsrContext; bool featureFSR3FG = false; - bool featureFSR3 = false; // Track if FidelityFX is currently being used for frame generation bool isFrameGenActive = false; @@ -34,25 +36,19 @@ class FidelityFX // Cached DLL version info for FidelityFX plugin directory static std::vector> dllVersions; - std::string versionInfo; - void LoadFFX(); - void QueryVersion(); void SetupFrameGeneration(); void Present(bool a_useFrameGeneration); void CreateFSRResources(); + void DestroyFSRResources(); - float GetInputResolutionScale(uint32_t outputWidth, uint32_t outputHeight, uint32_t qualityPreset); - void Upscale( - ID3D12Resource* a_inputColorTexture, - ID3D12Resource* a_motionVectorTexture, - ID3D12Resource* a_depthTexture, - ID3D12Resource* a_reactiveMask, - ID3D12Resource* a_transparencyCompositionMask, - ID3D12Resource* a_outputTexture, - ID3D12GraphicsCommandList* a_commandList, - uint32_t a_renderWidth, - uint32_t a_renderHeight, - float2 a_jitter); + + float GetInputResolutionScale(uint32_t outputWidth, uint32_t outputHeight, uint32_t qualityMode); + + void Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors); + +private: + // FSR scratch buffer - needs to be freed in DestroyFSRResources + void* fsrScratchBuffer = nullptr; }; diff --git a/src/Features/Upscaling/Streamline.cpp b/src/Features/Upscaling/Streamline.cpp index c65f6859b2..76baccaa91 100644 --- a/src/Features/Upscaling/Streamline.cpp +++ b/src/Features/Upscaling/Streamline.cpp @@ -251,7 +251,7 @@ void Streamline::CheckFrameConstants() } } -void Streamline::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors, sl::DLSSPreset a_preset) +void Streamline::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors) { CheckFrameConstants(); @@ -290,11 +290,11 @@ void Streamline::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_r dlssOptions.preExposure = 1.0f; dlssOptions.sharpness = 0.0f; - dlssOptions.dlaaPreset = a_preset; - dlssOptions.qualityPreset = a_preset; - dlssOptions.balancedPreset = a_preset; - dlssOptions.performancePreset = a_preset; - dlssOptions.ultraPerformancePreset = a_preset; + dlssOptions.dlaaPreset = sl::DLSSPreset::ePresetK; + dlssOptions.qualityPreset = dlssOptions.dlaaPreset; + dlssOptions.balancedPreset = dlssOptions.dlaaPreset; + dlssOptions.performancePreset = dlssOptions.dlaaPreset; + dlssOptions.ultraPerformancePreset = dlssOptions.dlaaPreset; if (SL_FAILED(result, slDLSSSetOptions(viewport, dlssOptions))) { logger::critical("[Streamline] Could not enable DLSS"); diff --git a/src/Features/Upscaling/Streamline.h b/src/Features/Upscaling/Streamline.h index b40803e64e..08c51fdda9 100644 --- a/src/Features/Upscaling/Streamline.h +++ b/src/Features/Upscaling/Streamline.h @@ -74,7 +74,7 @@ class Streamline void CheckFrameConstants(); - void Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors, sl::DLSSPreset a_preset); + void Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors); float GetInputResolutionScale(uint32_t outputWidth, uint32_t outputHeight, uint32_t qualityPreset); diff --git a/src/Features/Upscaling/XeSS.cpp b/src/Features/Upscaling/XeSS.cpp deleted file mode 100644 index 10b1feda7c..0000000000 --- a/src/Features/Upscaling/XeSS.cpp +++ /dev/null @@ -1,212 +0,0 @@ -#include "XeSS.h" -#include "../../Utils/FileSystem.h" - -#include "../../State.h" -#include "../Upscaling.h" - -#include -#include -#include - -// Define the static member -std::vector> XeSS::dllVersions = {}; - -void XeSS::LoadXeSS() -{ - std::wstring dllPath = std::wstring(XeSS::PluginDir) + L"\\libxess.dll"; - module = LoadLibrary(dllPath.c_str()); - - // Cache all DLL versions in the XeSS directory - std::filesystem::path pluginDir = std::filesystem::path(XeSS::PluginDir); - XeSS::dllVersions = Util::EnumerateDllVersions(pluginDir); - - if (module) { - xessGetVersion = (xessGetVersionPtr)GetProcAddress(module, "xessGetVersion"); - xessGetIntelXeFXVersion = (xessGetIntelXeFXVersionPtr)GetProcAddress(module, "xessGetIntelXeFXVersion"); - xessD3D12CreateContext = (xessD3D12CreateContextPtr)GetProcAddress(module, "xessD3D12CreateContext"); - xessD3D12Init = (xessD3D12InitPtr)GetProcAddress(module, "xessD3D12Init"); - xessD3D12Execute = (xessD3D12ExecutePtr)GetProcAddress(module, "xessD3D12Execute"); - xessDestroyContext = (xessDestroyContextPtr)GetProcAddress(module, "xessDestroyContext"); - xessSetJitterScale = (xessSetJitterScalePtr)GetProcAddress(module, "xessSetJitterScale"); - xessSetVelocityScale = (xessSetVelocityScalePtr)GetProcAddress(module, "xessSetVelocityScale"); - xessGetInputResolution = (xessGetInputResolutionPtr)GetProcAddress(module, "xessGetInputResolution"); - featureXeSS = true; - logger::info("[XeSS] Successfully loaded XeSS SDK"); - } else { - featureXeSS = false; - logger::error("[XeSS] Failed to load libxess.dll"); - } -} - -void XeSS::QueryVersion() -{ - // Clear existing version info - versionInfo.clear(); - - xess_version_t version; - xess_version_t versionXeFX; - - if (xessGetVersion(&version) == XESS_RESULT_SUCCESS && xessGetIntelXeFXVersion(xessContext, &versionXeFX) == XESS_RESULT_SUCCESS) { - bool xeFX = versionXeFX.major != 0 && versionXeFX.minor != 0 && versionXeFX.patch != 0; - versionInfo = std::format("{}.{}.{} {}", version.major, version.minor, version.patch, xeFX ? "XMX" : "DP4a"); - logger::info("[XeSS] Upscaler version: {}", versionInfo); - } -} - -void XeSS::CreateXeSSResources() -{ - auto& upscaling = globals::features::upscaling; - if (!featureXeSS || !upscaling.sharedD3D12Device) { - logger::error("[XeSS] XeSS not available or shared D3D12 device not available, cannot create resources"); - return; - } - - auto state = globals::state; - - xess_result_t createResult = xessD3D12CreateContext(upscaling.sharedD3D12Device.get(), &xessContext); - if (createResult != XESS_RESULT_SUCCESS) { - logger::critical("[XeSS] Failed to create XeSS context, error: {} ({})", magic_enum::enum_name(createResult), (int)createResult); - return; - } - - xess_d3d12_init_params_t initParams{}; - initParams.outputResolution.x = (uint32_t)state->screenSize.x; - initParams.outputResolution.y = (uint32_t)state->screenSize.y; - initParams.qualitySetting = XESS_QUALITY_SETTING_AA; - initParams.initFlags = XESS_INIT_FLAG_ENABLE_AUTOEXPOSURE | XESS_INIT_FLAG_USE_NDC_VELOCITY | XESS_INIT_FLAG_RESPONSIVE_PIXEL_MASK; - - initParams.creationNodeMask = 1; - initParams.visibleNodeMask = 1; - initParams.pTempBufferHeap = nullptr; - initParams.bufferHeapOffset = 0; - initParams.pTempTextureHeap = nullptr; - initParams.textureHeapOffset = 0; - initParams.pPipelineLibrary = nullptr; - - xess_result_t initResult = xessD3D12Init(xessContext, &initParams); - if (initResult != XESS_RESULT_SUCCESS) { - logger::critical("[XeSS] Failed to initialize XeSS context, error: {} ({})", magic_enum::enum_name(initResult), (int)initResult); - return; - } - - QueryVersion(); -} - -void XeSS::DestroyXeSSResources() -{ - if (xessContext && xessDestroyContext) { - xess_result_t result = xessDestroyContext(xessContext); - if (result != XESS_RESULT_SUCCESS) { - logger::error("[XeSS] Failed to destroy XeSS context, error: {} ({})", magic_enum::enum_name(result), (int)result); - } - xessContext = nullptr; - } -} - -float XeSS::GetInputResolutionScale(uint32_t outputWidth, uint32_t outputHeight, uint32_t qualityMode) -{ - // Check if XeSS context is valid - if (!xessContext) { - logger::error("[XeSS] GetInputResolutionScale called with null context"); - return 1.0f; - } - - // Check if function pointer is valid - if (!xessGetInputResolution) { - logger::error("[XeSS] GetInputResolutionScale called with null function pointer"); - return 1.0f; - } - - // Validate input parameters - if (outputWidth == 0 || outputHeight == 0) { - logger::error("[XeSS] GetInputResolutionScale called with invalid resolution: {}x{}", outputWidth, outputHeight); - return 1.0f; - } - - xess_quality_settings_t xessQuality; - switch (qualityMode) { - case 1: - xessQuality = XESS_QUALITY_SETTING_QUALITY; - break; - case 2: - xessQuality = XESS_QUALITY_SETTING_BALANCED; - break; - case 3: - xessQuality = XESS_QUALITY_SETTING_PERFORMANCE; - break; - case 4: - xessQuality = XESS_QUALITY_SETTING_ULTRA_PERFORMANCE; - break; - default: - xessQuality = XESS_QUALITY_SETTING_AA; - break; - } - - xess_2d_t outputResolution = { outputWidth, outputHeight }; - xess_2d_t inputResolution = { 0, 0 }; - - xess_result_t result = xessGetInputResolution(xessContext, &outputResolution, xessQuality, &inputResolution); - if (result != XESS_RESULT_SUCCESS) { - logger::critical("[XeSS] Failed to get input resolution, error: {} ({})", magic_enum::enum_name(result), (int)result); - return 1.0f; - } - - // Calculate scale as ratio of input to output resolution - float scaleX = (float)inputResolution.x / (float)outputResolution.x; - float scaleY = (float)inputResolution.y / (float)outputResolution.y; - - // Use the average scale (both should be the same for uniform scaling) - return (scaleX + scaleY) * 0.5f; -} - -void XeSS::Upscale( - ID3D12Resource* a_inputColorTexture, - ID3D12Resource* a_motionVectorTexture, - ID3D12Resource* a_depthTexture, - ID3D12Resource* a_reactiveMask, - ID3D12Resource* a_outputTexture, - ID3D12GraphicsCommandList* a_commandList, - uint32_t a_renderWidth, - uint32_t a_renderHeight, - float2 a_jitter) -{ - // Set velocity and jitter scales - xess_result_t velocityResult = xessSetVelocityScale(xessContext, globals::game::isVR ? 1.0f : 2.0f, -2.0f); - if (velocityResult != XESS_RESULT_SUCCESS) { - logger::warn("[XeSS] Failed to set velocity scale, error: {} ({})", magic_enum::enum_name(velocityResult), (int)velocityResult); - } - - xess_result_t jitterResult = xessSetJitterScale(xessContext, 1.0f, 1.0f); - if (jitterResult != XESS_RESULT_SUCCESS) { - logger::warn("[XeSS] Failed to set jitter scale, error: {} ({})", magic_enum::enum_name(jitterResult), (int)jitterResult); - } - - // XeSS execution parameters - xess_d3d12_execute_params_t execParams{}; - execParams.pColorTexture = a_inputColorTexture; - execParams.pVelocityTexture = a_motionVectorTexture; - execParams.pDepthTexture = a_depthTexture; - execParams.pExposureScaleTexture = nullptr; - execParams.pResponsivePixelMaskTexture = a_reactiveMask; - execParams.pOutputTexture = a_outputTexture; - execParams.jitterOffsetX = -a_jitter.x; - execParams.jitterOffsetY = -a_jitter.y; - execParams.exposureScale = 1.0f; - execParams.resetHistory = 0; - execParams.inputWidth = a_renderWidth; - execParams.inputHeight = a_renderHeight; - execParams.inputColorBase = { 0, 0 }; - execParams.inputMotionVectorBase = { 0, 0 }; - execParams.inputDepthBase = { 0, 0 }; - execParams.inputResponsiveMaskBase = { 0, 0 }; - execParams.reserved0 = { 0, 0 }; - execParams.outputColorBase = { 0, 0 }; - execParams.pDescriptorHeap = nullptr; - execParams.descriptorHeapOffset = 0; - - xess_result_t result = xessD3D12Execute(xessContext, a_commandList, &execParams); - if (result != XESS_RESULT_SUCCESS) { - logger::error("[XeSS] Failed to execute XeSS upscaling, error: {} ({})", magic_enum::enum_name(result), (int)result); - return; - } -} \ No newline at end of file diff --git a/src/Features/Upscaling/XeSS.h b/src/Features/Upscaling/XeSS.h deleted file mode 100644 index 6beb0f0790..0000000000 --- a/src/Features/Upscaling/XeSS.h +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include "../../Buffer.h" -#include "../../State.h" -#include "DX12SwapChain.h" -#include -#include -#include - -// Include XeSS headers -#include -#include - -// XeSS function pointers - matching exact signatures from xess.h and xess_d3d12.h -typedef xess_result_t (*xessGetVersionPtr)(xess_version_t* pVersion); -typedef xess_result_t (*xessGetIntelXeFXVersionPtr)(xess_context_handle_t hContext, xess_version_t* pVersion); -typedef xess_result_t (*xessD3D12CreateContextPtr)(ID3D12Device* pDevice, xess_context_handle_t* phContext); -typedef xess_result_t (*xessD3D12InitPtr)(xess_context_handle_t hContext, const xess_d3d12_init_params_t* pInitParams); -typedef xess_result_t (*xessD3D12ExecutePtr)(xess_context_handle_t hContext, ID3D12GraphicsCommandList* pCommandList, const xess_d3d12_execute_params_t* pExecParams); -typedef xess_result_t (*xessDestroyContextPtr)(xess_context_handle_t hContext); -typedef xess_result_t (*xessSetJitterScalePtr)(xess_context_handle_t hContext, float x, float y); -typedef xess_result_t (*xessSetVelocityScalePtr)(xess_context_handle_t hContext, float x, float y); -typedef xess_result_t (*xessGetInputResolutionPtr)(xess_context_handle_t hContext, const xess_2d_t* pOutputResolution, xess_quality_settings_t qualitySettings, xess_2d_t* pInputResolution); - -class XeSS -{ -public: - static constexpr const wchar_t* PluginDir = L"Data\\Shaders\\Upscaling\\XeSS"; - - XeSS() = default; - - HMODULE module = nullptr; - - // XeSS function pointers - xessGetVersionPtr xessGetVersion = nullptr; - xessGetIntelXeFXVersionPtr xessGetIntelXeFXVersion = nullptr; - xessD3D12CreateContextPtr xessD3D12CreateContext = nullptr; - xessD3D12InitPtr xessD3D12Init = nullptr; - xessD3D12ExecutePtr xessD3D12Execute = nullptr; - xessDestroyContextPtr xessDestroyContext = nullptr; - xessSetJitterScalePtr xessSetJitterScale = nullptr; - xessSetVelocityScalePtr xessSetVelocityScale = nullptr; - xessGetInputResolutionPtr xessGetInputResolution = nullptr; - - xess_context_handle_t xessContext = nullptr; - - bool featureXeSS = false; // whether enabled - - // Cached DLL version info for XeSS plugin directory - static std::vector> dllVersions; - - std::string versionInfo; - - void LoadXeSS(); - void QueryVersion(); - void CreateXeSSResources(); - void DestroyXeSSResources(); - float GetInputResolutionScale(uint32_t outputWidth, uint32_t outputHeight, uint32_t qualityPreset); - void Upscale( - ID3D12Resource* a_inputColorTexture, - ID3D12Resource* a_motionVectorTexture, - ID3D12Resource* a_depthTexture, - ID3D12Resource* a_reactiveMask, - ID3D12Resource* a_outputTexture, - ID3D12GraphicsCommandList* a_commandList, - uint32_t a_renderWidth, - uint32_t a_renderHeight, - float2 a_jitter); -}; \ No newline at end of file diff --git a/src/Hooks.cpp b/src/Hooks.cpp index fdcb084a6c..d29a3f04ad 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -343,6 +343,18 @@ struct ID3D11Device_CreatePixelShader static inline REL::Relocation func; }; +struct ID3D11Device_CreateSamplerState +{ + static HRESULT STDMETHODCALLTYPE thunk(ID3D11Device* This, D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState) + { + // Limit Anisotropy to 8x for performance + D3D11_SAMPLER_DESC descCopy = *pSamplerDesc; // make a copy, pSamplerDesc is supposed to be immutable + descCopy.MaxAnisotropy = std::min(descCopy.MaxAnisotropy, 8u); + return func(This, &descCopy, ppSamplerState); + } + static inline REL::Relocation func; +}; + struct BSShaderRenderTargets_Create { /** @@ -436,6 +448,8 @@ namespace Hooks stl::detour_vfunc<15, ID3D11Device_CreatePixelShader>(globals::d3d::device); } + stl::detour_vfunc<23, ID3D11Device_CreateSamplerState>(globals::d3d::device); + globals::InstallD3DHooks(globals::d3d::context); globals::menu->Init(); diff --git a/src/State.cpp b/src/State.cpp index b5c5bfd612..4772bc14cd 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -763,9 +763,12 @@ void State::UpdateSharedData(bool a_inWorld, bool a_prepass) auto& upscaling = globals::features::upscaling; if (upscaling.loaded) { - if (temporal && (a_inWorld || a_prepass) && upscaling.GetUpscaleMethod() != Upscaling::UpscaleMethod::kTAA) { + auto upscaleMethod = upscaling.GetUpscaleMethod(); + if (temporal && (a_inWorld || a_prepass) && upscaleMethod != Upscaling::UpscaleMethod::kTAA) { auto renderSize = Util::ConvertToDynamic(screenSize); - data.MipBias = std::log2f(renderSize.x / screenSize.x) - 1.0f; + data.MipBias = std::log2f(renderSize.x / screenSize.x); + if (upscaleMethod == Upscaling::UpscaleMethod::kDLSS) + data.MipBias -= 1.0f; } else { data.MipBias = 0; }