diff --git a/features/Frame Generation/SKSE/Plugins/FidelityFX/amd_fidelityfx_dx12.dll b/features/Frame Generation/SKSE/Plugins/FidelityFX/amd_fidelityfx_dx12.dll index 5e381643e6..9bd0fbbf68 100644 Binary files a/features/Frame Generation/SKSE/Plugins/FidelityFX/amd_fidelityfx_dx12.dll and b/features/Frame Generation/SKSE/Plugins/FidelityFX/amd_fidelityfx_dx12.dll differ diff --git a/include/ffx_api.h b/include/ffx_api.h index 4bcefc9247..b24e264736 100644 --- a/include/ffx_api.h +++ b/include/ffx_api.h @@ -100,6 +100,14 @@ struct ffxOverrideVersion uint64_t versionId; ///< Id of version to use. Must be a value returned from a query in ffxQueryDescGetVersions.versionIds array. }; +#define FFX_API_QUERY_DESC_TYPE_GET_PROVIDER_VERSION 6u +struct ffxQueryGetProviderVersion +{ + ffxQueryDescHeader header; + uint64_t versionId; ///< Id of provider being used for queried context. 0 if invalid. + const char* versionName; ///< Version name for display. If nullptr, the query was invalid. +}; + // Memory allocation function. Must return a valid pointer to at least size bytes of memory aligned to hold any type. // May return null to indicate failure. Standard library malloc fulfills this requirement. typedef void* (*ffxAlloc)(void* pUserData, uint64_t size); diff --git a/include/ffx_api_types.h b/include/ffx_api_types.h index 01ab3d08dc..7eee14642e 100644 --- a/include/ffx_api_types.h +++ b/include/ffx_api_types.h @@ -23,6 +23,7 @@ #pragma once #include +#include /// An enumeration of surface formats. Needs to match enum FfxSurfaceFormat enum FfxApiSurfaceFormat @@ -79,7 +80,7 @@ enum FfxApiResourceUsage FFX_API_RESOURCE_USAGE_ARRAYVIEW = (1<<4), ///< Indicates a resource that will generate array views. Works on 2D and cubemap textures FFX_API_RESOURCE_USAGE_STENCILTARGET = (1<<5), ///< Indicates a resource will be used as stencil target. }; -typedef FfxApiResourceUsage FfxApiResorceUsage; // Corrects a typo that shipped with original API + /// An enumeration of resource states. enum FfxApiResourceState diff --git a/include/ffx_framegeneration.h b/include/ffx_framegeneration.h index 3e1de2464e..33617d546d 100644 --- a/include/ffx_framegeneration.h +++ b/include/ffx_framegeneration.h @@ -30,7 +30,6 @@ #if defined(__cplusplus) extern "C" { #endif - enum FfxApiCreateContextFramegenerationFlags { FFX_FRAMEGENERATION_ENABLE_ASYNC_WORKLOAD_SUPPORT = (1<<0), @@ -39,6 +38,7 @@ enum FfxApiCreateContextFramegenerationFlags FFX_FRAMEGENERATION_ENABLE_DEPTH_INVERTED = (1<<3), ///< A bit indicating that the input depth buffer data provided is inverted [1..0]. FFX_FRAMEGENERATION_ENABLE_DEPTH_INFINITE = (1<<4), ///< A bit indicating that the input depth buffer data provided is using an infinite far plane. FFX_FRAMEGENERATION_ENABLE_HIGH_DYNAMIC_RANGE = (1<<5), ///< A bit indicating if the input color data provided to all inputs is using a high-dynamic range. + FFX_FRAMEGENERATION_ENABLE_DEBUG_CHECKING = (1<<6), ///< A bit indicating that the runtime should check some API values and report issues. }; enum FfxApiDispatchFramegenerationFlags @@ -48,7 +48,8 @@ enum FfxApiDispatchFramegenerationFlags FFX_FRAMEGENERATION_FLAG_DRAW_DEBUG_VIEW = (1 << 2), ///< A bit indicating that the generated output resource will contain debug views with relevant information. FFX_FRAMEGENERATION_FLAG_NO_SWAPCHAIN_CONTEXT_NOTIFY = (1 << 3), ///< A bit indicating that the context should only run frame interpolation and not modify the swapchain. FFX_FRAMEGENERATION_FLAG_DRAW_DEBUG_PACING_LINES = (1 << 4), ///< A bit indicating that the debug pacing lines will be drawn to the generated output. - + FFX_FRAMEGENERATION_FLAG_RESERVED_1 = (1 << 5), + FFX_FRAMEGENERATION_FLAG_RESERVED_2 = (1 << 6), }; enum FfxApiUiCompositionFlags @@ -68,7 +69,7 @@ struct ffxCreateContextDescFrameGeneration }; #define FFX_API_CALLBACK_DESC_TYPE_FRAMEGENERATION_PRESENT 0x00020005u -struct ffxCallbackDescFrameGenerationPresent +typedef struct ffxCallbackDescFrameGenerationPresent { ffxDispatchDescHeader header; void* device; ///< The device passed in (from a backend description) during context creation. @@ -78,10 +79,10 @@ struct ffxCallbackDescFrameGenerationPresent struct FfxApiResource outputSwapChainBuffer; ///< Output image that will be presented. bool isGeneratedFrame; ///< true if this frame is generated, false if rendered. uint64_t frameID; ///< Identifier used to select internal resources when async support is enabled. Must increment by exactly one (1) for each frame. Any non-exactly-one difference will reset the frame generation logic. -}; +} ffxCallbackDescFrameGenerationPresent; #define FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION 0x00020003u -struct ffxDispatchDescFrameGeneration +typedef struct ffxDispatchDescFrameGeneration { ffxDispatchDescHeader header; void* commandList; ///< The command list on which to register render commands. @@ -93,7 +94,7 @@ struct ffxDispatchDescFrameGeneration float minMaxLuminance[2]; ///< Min and max luminance values, used when converting HDR colors to linear RGB. struct FfxApiRect2D generationRect; ///< The area of the backbuffer that should be used for generation in case only a part of the screen is used e.g. due to movie bars. uint64_t frameID; ///< Identifier used to select internal resources when async support is enabled. Must increment by exactly one (1) for each frame. Any non-exactly-one difference will reset the frame generation logic. -}; +} ffxDispatchDescFrameGeneration; typedef ffxReturnCode_t(*FfxApiPresentCallbackFunc)(ffxCallbackDescFrameGenerationPresent* params, void* pUserCtx); typedef ffxReturnCode_t(*FfxApiFrameGenerationDispatchFunc)(ffxDispatchDescFrameGeneration* params, void* pUserCtx); @@ -166,12 +167,24 @@ struct ffxConfigureDescFrameGenerationRegisterDistortionFieldResource }; #define FFX_API_CREATE_CONTEXT_DESC_TYPE_FRAMEGENERATION_HUDLESS 0x00020009u +//Pass this optional linked struct at FG context creation to enable app to use different hudlessBackBufferformat (IE.RGBA8_UNORM) from backBufferFormat (IE. BGRA8_UNORM) struct ffxCreateContextDescFrameGenerationHudless { ffxCreateContextDescHeader header; uint32_t hudlessBackBufferFormat; ///< The surface format for the hudless back buffer. One of the values from FfxApiSurfaceFormat. }; +#define FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION_PREPARE_CAMERAINFO 0x0002000au +//Link this struct after ffxDispatchDescFrameGenerationPrepare. This is a required input to FSR3.1.4 and onwards for best quality. +struct ffxDispatchDescFrameGenerationPrepareCameraInfo +{ + ffxConfigureDescHeader header; + float cameraPosition[3]; ///< The camera position in world space + float cameraUp[3]; ///< The camera up normalized vector in world space. + float cameraRight[3]; ///< The camera right normalized vector in world space. + float cameraForward[3]; ///< The camera forward normalized vector in world space. +}; + #if defined(__cplusplus) } // extern "C" #endif diff --git a/include/ffx_framegeneration.hpp b/include/ffx_framegeneration.hpp index af4977bcb2..3cda0c8b94 100644 --- a/include/ffx_framegeneration.hpp +++ b/include/ffx_framegeneration.hpp @@ -35,11 +35,6 @@ struct struct_type : std::integral_constant struct CreateContextDescFrameGeneration : public InitHelper {}; -template<> -struct struct_type : std::integral_constant {}; - -struct CreateContextDescFrameGenerationHudless : public InitHelper {}; - template<> struct struct_type : std::integral_constant {}; @@ -70,4 +65,13 @@ struct struct_type {}; +template<> +struct struct_type : std::integral_constant {}; + +struct CreateContextDescFrameGenerationHudless : public InitHelper {}; + +template<> +struct struct_type : std::integral_constant {}; + +struct DispatchDescFrameGenerationPrepareCameraInfo : public InitHelper {}; } diff --git a/src/FidelityFX.cpp b/src/FidelityFX.cpp index 34c438b8de..3656e8dfdd 100644 --- a/src/FidelityFX.cpp +++ b/src/FidelityFX.cpp @@ -52,6 +52,13 @@ void FidelityFX::SetupFrameGeneration() } } +/** + * @brief Presents the current frame, optionally performing frame generation using FidelityFX. + * + * Configures and dispatches FidelityFX frame generation for the current swap chain frame if enabled. Sets up frame pacing, prepares resources, and issues dispatches for both frame generation parameters and camera information. Increments the internal frame ID after each call. + * + * @param a_useFrameGeneration If true, enables frame generation and dispatches the necessary workloads; otherwise, presents without frame generation. + */ void FidelityFX::Present(bool a_useFrameGeneration) { auto upscaling = globals::upscaling; @@ -149,6 +156,31 @@ void FidelityFX::Present(bool a_useFrameGeneration) if (ffx::Dispatch(frameGenContext, dispatchParameters) != ffx::ReturnCode::Ok) { logger::critical("[FidelityFX] Failed to dispatch frame generation!"); } + + ffx::DispatchDescFrameGenerationPrepareCameraInfo cameraConfig{}; + + auto viewMatrix = globals::upscaling->frameBufferCached.CameraViewInverse.Transpose(); + auto cameraViewToClip = globals::upscaling->frameBufferCached.CameraProjUnjittered.Transpose(); + + cameraConfig.cameraRight[0] = viewMatrix._11; + cameraConfig.cameraRight[1] = viewMatrix._12; + cameraConfig.cameraRight[2] = viewMatrix._13; + + cameraConfig.cameraUp[0] = viewMatrix._21; + cameraConfig.cameraUp[1] = viewMatrix._22; + cameraConfig.cameraUp[2] = viewMatrix._23; + + cameraConfig.cameraForward[0] = viewMatrix._31; + cameraConfig.cameraForward[1] = viewMatrix._32; + cameraConfig.cameraForward[2] = viewMatrix._33; + + cameraConfig.cameraPosition[0] = globals::upscaling->frameBufferCached.CameraPosAdjust.x; + cameraConfig.cameraPosition[1] = globals::upscaling->frameBufferCached.CameraPosAdjust.y; + cameraConfig.cameraPosition[2] = globals::upscaling->frameBufferCached.CameraPosAdjust.z; + + if (ffx::Dispatch(frameGenContext, dispatchParameters, cameraConfig) != ffx::ReturnCode::Ok) { + logger::critical("[FidelityFX] Failed to dispatch frame generation camera info!"); + } } frameID++; diff --git a/src/Hooks.cpp b/src/Hooks.cpp index 20c2c677f1..2cf229eb89 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -196,7 +196,17 @@ namespace LightingExtensions struct IDXGISwapChain_Present { - static HRESULT WINAPI thunk(IDXGISwapChain* This, UINT SyncInterval, UINT Flags) + static HRESULT WINAPI /** + * @brief Presents the swap chain with additional upscaling, overlay, and Reflex marker integration. + * + * Copies frame buffers for upscaling, processes overlays, manages tearing flags, and integrates Streamline Reflex markers for frame timing if enabled. Also collects profiling data and applies frame limiting after presentation. + * + * @param This The swap chain instance to present. + * @param SyncInterval The vertical sync interval. + * @param Flags Presentation flags, possibly modified for tearing support. + * @return HRESULT Result of the present operation. + */ + thunk(IDXGISwapChain* This, UINT SyncInterval, UINT Flags) { auto state = globals::state; auto streamline = globals::streamline; @@ -222,7 +232,21 @@ struct IDXGISwapChain_Present } } - auto retval = func(This, SyncInterval, Flags); + HRESULT retval = S_OK; + + if (globals::streamline->featureReflex) { + sl::FrameToken* frameToken; + globals::streamline->slGetNewFrameToken(frameToken, &globals::state->frameCount); + + globals::streamline->slReflexSetMarker(sl::ReflexMarker::eRenderSubmitEnd, *frameToken); + globals::streamline->slReflexSetMarker(sl::ReflexMarker::ePresentStart, *frameToken); + + retval = func(This, SyncInterval, Flags); + + globals::streamline->slReflexSetMarker(sl::ReflexMarker::ePresentEnd, *frameToken); + } else { + retval = func(This, SyncInterval, Flags); + } TracyD3D11Collect(state->tracyCtx); @@ -281,6 +305,13 @@ HRESULT WINAPI hk_CreateDXGIFactory(REFIID, void** ppFactory) decltype(&D3D11CreateDeviceAndSwapChain) ptrD3D11CreateDeviceAndSwapChain; +/** + * @brief Creates a Direct3D 11 device and swap chain, with support for advanced upscaling and frame generation features. + * + * This function intercepts the standard D3D11 device and swap chain creation process to enable integration with Streamline and FidelityFX technologies, as well as optional D3D12 proxying for frame generation. It adjusts swap chain flags for tearing support, manages feature checks, and conditionally routes device creation through Streamline or FidelityFX proxies based on runtime settings and hardware capabilities. If frame generation is enabled and supported, a D3D12 proxy is used; otherwise, the standard D3D11 creation path is followed. + * + * @return HRESULT indicating the success or failure of device and swap chain creation. + */ HRESULT WINAPI hk_D3D11CreateDeviceAndSwapChain( IDXGIAdapter* pAdapter, D3D_DRIVER_TYPE DriverType, @@ -376,7 +407,7 @@ HRESULT WINAPI hk_D3D11CreateDeviceAndSwapChain( upscaling->d3d12Interop = true; streamline->PostDevice(); - streamline->InstallHooks(*ppImmediateContext); + upscaling->InstallD3DHooks(*ppImmediateContext); IDXGIFactory* factory = nullptr; if (SUCCEEDED((*ppSwapChain)->GetParent(IID_PPV_ARGS(&factory)))) { @@ -455,8 +486,55 @@ HRESULT WINAPI hk_D3D11CreateDeviceAndSwapChain( return ret; } +struct Main_Update_Begin +{ + /** + * @brief Wraps the player character update with Reflex frame timing markers. + * + * If the Reflex feature is enabled, obtains a new frame token and sets markers for input sampling and simulation start before invoking the original update function. + * + * @param a_player Pointer to the player character being updated. + */ + static void thunk(RE::PlayerCharacter* a_player) + { + if (globals::streamline->featureReflex) { + sl::FrameToken* frameToken; + globals::streamline->slGetNewFrameToken(frameToken, &globals::state->frameCount); + globals::streamline->slReflexSetMarker(sl::ReflexMarker::eInputSample, *frameToken); + globals::streamline->slReflexSetMarker(sl::ReflexMarker::eSimulationStart, *frameToken); + } + func(a_player); + } + static inline REL::Relocation func; +}; + +struct Main_Update_Swap +{ + /** + * @brief Wraps a function call with Streamline Reflex simulation and render submit markers. + * + * If the Reflex feature is enabled, obtains a new frame token and sets markers for simulation end and render submit start before invoking the original function. + */ + static void thunk(void* This) + { + if (globals::streamline->featureReflex) { + sl::FrameToken* frameToken; + globals::streamline->slGetNewFrameToken(frameToken, &globals::state->frameCount); + globals::streamline->slReflexSetMarker(sl::ReflexMarker::eSimulationEnd, *frameToken); + globals::streamline->slReflexSetMarker(sl::ReflexMarker::eRenderSubmitStart, *frameToken); + } + func(This); + } + static inline REL::Relocation func; +}; + struct BSShaderRenderTargets_Create { + /** + * @brief Calls the original render target creation function and reinitializes global rendering state. + * + * Invokes the original function, then reinitializes global state and performs necessary setup for rendering targets. + */ static void thunk() { func(); @@ -930,6 +1008,11 @@ namespace Hooks PatchMemory(Address, Data.begin(), Data.size()); } + /** + * @brief Installs hooks, detours, and memory patches for graphics, input, and rendering subsystems. + * + * Sets up function hooks and virtual method overrides for shader management, input polling, rendering pipeline stages, compute shader dispatch, material setup, batch rendering, and window procedure handling. Applies memory patches to adjust render pass cache sizes and offsets. Installs additional update hooks for frame timing and Reflex marker integration when not in VR mode. + */ void Install() { logger::info("Hooking BSInputDeviceManager::PollInputDevices"); @@ -1033,8 +1116,18 @@ namespace Hooks REL::Relocation(renderPassCacheCtor, 0x191 - 2).address(), reinterpret_cast(&passCountSE), 4); } + + if (!REL::Module::IsVR()) { + stl::write_thunk_call(REL::RelocationID(35565, 36564).address() + REL::Relocate(0x53, 0x6E)); + stl::write_thunk_call(REL::RelocationID(35565, 36564).address() + REL::Relocate(0x5D2, 0xA92)); + } } + /** + * @brief Installs Direct3D-related hooks for device and factory creation. + * + * Loads FidelityFX support and patches the import address table (IAT) to redirect D3D11 device and DXGI factory creation functions to custom hook implementations. + */ void InstallD3DHooks() { globals::fidelityFX->LoadFFX(); diff --git a/src/Streamline.cpp b/src/Streamline.cpp index 6d2ba80044..2b3788113b 100644 --- a/src/Streamline.cpp +++ b/src/Streamline.cpp @@ -185,10 +185,15 @@ void Streamline::PostDevice() } } +/** + * @brief Updates and sets camera and frame constants for the current Streamline frame. + * + * Populates and submits camera parameters, projection matrices, motion vector settings, and other per-frame constants to the Streamline SDK for the current frame. Uses cached framebuffer data and global state to ensure correct configuration for upscaling and frame generation features. + */ void Streamline::CheckFrameConstants() { if (frameChecker.IsNewFrame() && globals::streamline->initialized) { - slGetNewFrameToken(frameToken, nullptr); + slGetNewFrameToken(frameToken, &globals::state->frameCount); auto state = globals::state; @@ -204,15 +209,15 @@ void Streamline::CheckFrameConstants() slConstants.cameraNear = *globals::game::cameraNear; slConstants.cameraFar = *globals::game::cameraFar; - auto viewMatrix = frameBufferCached.CameraViewInverse.Transpose(); - auto cameraViewToClip = frameBufferCached.CameraProjUnjittered.Transpose(); + auto viewMatrix = globals::upscaling->frameBufferCached.CameraViewInverse.Transpose(); + auto cameraViewToClip = globals::upscaling->frameBufferCached.CameraProjUnjittered.Transpose(); slConstants.cameraMotionIncluded = sl::Boolean::eTrue; slConstants.cameraPinholeOffset = { 0.f, 0.f }; slConstants.cameraRight = { viewMatrix._11, viewMatrix._12, viewMatrix._13 }; slConstants.cameraUp = { viewMatrix._21, viewMatrix._22, viewMatrix._23 }; slConstants.cameraFwd = { viewMatrix._31, viewMatrix._32, viewMatrix._33 }; - slConstants.cameraPos = *(sl::float3*)&frameBufferCached.CameraPosAdjust; + slConstants.cameraPos = *(sl::float3*)&globals::upscaling->frameBufferCached.CameraPosAdjust; slConstants.cameraViewToClip = *(sl::float4x4*)&cameraViewToClip; slConstants.depthInverted = sl::Boolean::eFalse; @@ -299,6 +304,11 @@ void Streamline::Upscale(Texture2D* a_upscaleTexture, Texture2D* a_alphaMask, sl slEvaluateFeature(sl::kFeatureDLSS, *frameToken, inputs, _countof(inputs), globals::d3d::context); } +/** + * @brief Submits frame resources and markers for DLSS-G frame generation and Reflex latency tracking. + * + * Updates DLSS-G frame generation mode if needed, sets Reflex simulation and render markers, and binds required resources (depth, motion vectors, HUD-less color, UI) for the current frame to the Streamline SDK. No action is taken if Streamline is uninitialized, DLSS-G is unavailable, VR mode is active, or D3D12 interop is not enabled. + */ void Streamline::Present() { if (!initialized || !featureDLSSG || globals::game::isVR || !globals::upscaling->d3d12Interop) @@ -324,14 +334,8 @@ void Streamline::Present() auto state = globals::state; - // Fake NVIDIA Reflex to prevent DLSSG errors - slReflexSetMarker(sl::ReflexMarker::eInputSample, *frameToken); - slReflexSetMarker(sl::ReflexMarker::eSimulationStart, *frameToken); slReflexSetMarker(sl::ReflexMarker::eSimulationEnd, *frameToken); slReflexSetMarker(sl::ReflexMarker::eRenderSubmitStart, *frameToken); - slReflexSetMarker(sl::ReflexMarker::eRenderSubmitEnd, *frameToken); - slReflexSetMarker(sl::ReflexMarker::ePresentStart, *frameToken); - slReflexSetMarker(sl::ReflexMarker::ePresentEnd, *frameToken); sl::Extent fullExtent{ 0, 0, (uint)state->screenSize.x, (uint)state->screenSize.y }; @@ -354,40 +358,15 @@ void Streamline::Present() slSetTag(viewport, inputs, _countof(inputs), globals::d3d::context); } +/** + * @brief Releases DLSS resources and disables DLSS for the current viewport. + * + * Sets the DLSS mode to off and frees all DLSS-related resources associated with the viewport. + */ void Streamline::DestroyDLSSResources() { sl::DLSSOptions dlssOptions{}; dlssOptions.mode = sl::DLSSMode::eOff; slDLSSSetOptions(viewport, dlssOptions); slFreeResources(sl::kFeatureDLSS, viewport); -} - -void Streamline::InstallHooks(ID3D11DeviceContext* a_context) -{ - stl::detour_vfunc<14, ID3D11DeviceContext_Map>(a_context); - stl::detour_vfunc<15, ID3D11DeviceContext_Unmap>(a_context); -} - -HRESULT Streamline::ID3D11DeviceContext_Map::thunk(ID3D11DeviceContext* This, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) -{ - HRESULT hr = func(This, pResource, Subresource, MapType, MapFlags, pMappedResource); - if (hr == S_OK) { - if (*globals::game::perFrame.get() == pResource) - globals::streamline->mappedFrameBuffer = pMappedResource; - } - return hr; -} - -void Streamline::ID3D11DeviceContext_Unmap::thunk(ID3D11DeviceContext* This, ID3D11Resource* pResource, UINT Subresource) -{ - if (*globals::game::perFrame.get() == pResource && globals::streamline->mappedFrameBuffer) - globals::streamline->CacheFramebuffer(); - func(This, pResource, Subresource); -} - -void Streamline::CacheFramebuffer() -{ - auto frameBuffer = (FrameBuffer*)mappedFrameBuffer->pData; - frameBufferCached = *frameBuffer; - mappedFrameBuffer = nullptr; -} +} \ No newline at end of file diff --git a/src/Streamline.h b/src/Streamline.h index b47c45ee68..c19a9e34ea 100644 --- a/src/Streamline.h +++ b/src/Streamline.h @@ -19,6 +19,76 @@ #include #pragma warning(pop) +/** + * @brief Returns the singleton instance of the Streamline class. + * + * Ensures only one instance of Streamline exists throughout the application's lifetime. + * + * @return Pointer to the singleton Streamline instance. + */ +static Streamline* GetSingleton(); + +/** + * @brief Returns a short name identifier for the Streamline class. + * + * @return The string "Streamline". + */ +inline std::string GetShortName(); + +/** + * @brief Loads the NVIDIA Streamline interposer DLL and initializes function pointers. + * + * Dynamically loads the required Streamline SDK library and sets up function pointers for feature management. + */ +void LoadInterposer(); + +/** + * @brief Checks which NVIDIA Streamline features are supported on the given DXGI adapter. + * + * Updates internal flags for DLSS, DLSSG, and Reflex feature support based on the provided adapter. + * + * @param a_adapter Pointer to the DXGI adapter to query for feature support. + */ +void CheckFeatures(IDXGIAdapter* a_adapter); + +/** + * @brief Performs post-device creation setup for Streamline integration. + * + * Should be called after the Direct3D device is created to complete Streamline initialization. + */ +void PostDevice(); + +/** + * @brief Validates or updates frame-related constants for Streamline operations. + * + * Ensures that frame constants required by Streamline features are current and correct. + */ +void CheckFrameConstants(); + +/** + * @brief Performs DLSS upscaling on the provided color and alpha mask textures. + * + * Uses the specified DLSS preset to upscale the input textures via NVIDIA Streamline. + * + * @param a_color Pointer to the color texture to be upscaled. + * @param a_alphaMask Pointer to the alpha mask texture, if used. + * @param a_preset DLSS preset specifying the upscaling quality and performance settings. + */ +void Upscale(Texture2D* a_color, Texture2D* a_alphaMask, sl::DLSSPreset a_preset); + +/** + * @brief Handles presentation logic for Streamline, finalizing or submitting the current frame. + * + * Should be called at the end of the rendering pipeline to complete Streamline processing for the frame. + */ +void Present(); + +/** + * @brief Releases and cleans up resources allocated for DLSS operations. + * + * Frees any memory or handles associated with DLSS to prevent resource leaks. + */ +void DestroyDLSSResources(); class Streamline { public: @@ -93,42 +163,4 @@ class Streamline void Upscale(Texture2D* a_color, Texture2D* a_alphaMask, sl::DLSSPreset a_preset); void Present(); void DestroyDLSSResources(); - - void InstallHooks(ID3D11DeviceContext* a_context); - - struct FrameBuffer - { - Matrix CameraView; - Matrix CameraProj; - Matrix CameraViewProj; - Matrix CameraViewProjUnjittered; - Matrix CameraPreviousViewProjUnjittered; - Matrix CameraProjUnjittered; - Matrix CameraProjUnjitteredInverse; - Matrix CameraViewInverse; - Matrix CameraViewProjInverse; - Matrix CameraProjInverse; - float4 CameraPosAdjust; - float4 CameraPreviousPosAdjust; - float4 FrameParams; - float4 DynamicResolutionParams1; - float4 DynamicResolutionParams2; - }; - - D3D11_MAPPED_SUBRESOURCE* mappedFrameBuffer = nullptr; - FrameBuffer frameBufferCached{}; - - void CacheFramebuffer(); - - struct ID3D11DeviceContext_Map - { - static HRESULT thunk(ID3D11DeviceContext* This, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource); - static inline REL::Relocation func; - }; - - struct ID3D11DeviceContext_Unmap - { - static void thunk(ID3D11DeviceContext* This, ID3D11Resource* pResource, UINT Subresource); - static inline REL::Relocation func; - }; }; diff --git a/src/Upscaling.cpp b/src/Upscaling.cpp index 10c66b95c3..2b85500b22 100644 --- a/src/Upscaling.cpp +++ b/src/Upscaling.cpp @@ -766,6 +766,13 @@ bool Upscaling::IsFrameGenerationActive() const return d3d12Interop && settings.frameGenerationMode; } +/** + * @brief Retrieves the current frame time for frame generation. + * + * Returns the frame time from the D3D12 swap chain if frame generation is active; otherwise, returns 0. + * + * @return float The current frame time in seconds, or 0 if frame generation is inactive. + */ float Upscaling::GetFrameGenerationFrameTime() const { if (!IsFrameGenerationActive()) @@ -778,4 +785,56 @@ float Upscaling::GetFrameGenerationFrameTime() const } return 0.0f; -} \ No newline at end of file +} + +/** + * @brief Installs hooks on the Map and Unmap methods of the provided D3D11 device context. + * + * This enables interception of resource mapping and unmapping operations for frame buffer caching. + */ +void Upscaling::InstallD3DHooks(ID3D11DeviceContext* a_context) +{ + stl::detour_vfunc<14, ID3D11DeviceContext_Map>(a_context); + stl::detour_vfunc<15, ID3D11DeviceContext_Unmap>(a_context); +} + +/** + * @brief Hooks the ID3D11DeviceContext::Map method to track mapping of the per-frame resource. + * + * Calls the original Map function and, if the mapped resource matches the current per-frame buffer, stores the mapped subresource pointer for later use. + * + * @return HRESULT Result of the original Map call. + */ +HRESULT Upscaling::ID3D11DeviceContext_Map::thunk(ID3D11DeviceContext* This, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) +{ + HRESULT hr = func(This, pResource, Subresource, MapType, MapFlags, pMappedResource); + if (hr == S_OK) { + if (*globals::game::perFrame.get() == pResource) + globals::upscaling->mappedFrameBuffer = pMappedResource; + } + return hr; +} + +/** + * @brief Hooked implementation of ID3D11DeviceContext::Unmap that caches the frame buffer if applicable. + * + * If the resource being unmapped matches the current per-frame buffer and a mapped frame buffer is present, caches the frame buffer data before calling the original Unmap function. + */ +void Upscaling::ID3D11DeviceContext_Unmap::thunk(ID3D11DeviceContext* This, ID3D11Resource* pResource, UINT Subresource) +{ + if (*globals::game::perFrame.get() == pResource && globals::upscaling->mappedFrameBuffer) + globals::upscaling->CacheFramebuffer(); + func(This, pResource, Subresource); +} + +/** + * @brief Caches the current frame buffer data and clears the mapped pointer. + * + * Copies the contents of the mapped frame buffer into an internal cache and resets the mapped frame buffer pointer. + */ +void Upscaling::CacheFramebuffer() +{ + auto frameBuffer = (FrameBuffer*)mappedFrameBuffer->pData; + frameBufferCached = *frameBuffer; + mappedFrameBuffer = nullptr; +} diff --git a/src/Upscaling.h b/src/Upscaling.h index c132c9836c..522ea99ac3 100644 --- a/src/Upscaling.h +++ b/src/Upscaling.h @@ -3,6 +3,13 @@ #include "FidelityFX.h" #include "Streamline.h" +/** + * @brief Installs hooks for Direct3D 11 device context operations. + * + * Sets up hooks on the provided ID3D11DeviceContext to intercept and extend D3D11 Map and Unmap operations, enabling custom frame buffer management and resource tracking. + * + * @param a_context Pointer to the Direct3D 11 device context to hook. + */ class Upscaling { public: @@ -176,4 +183,42 @@ class Upscaling logger::info("[Upscaling] Not installing hooks due to Skyrim Upscaler"); } } + + void InstallD3DHooks(ID3D11DeviceContext* a_context); + + struct FrameBuffer + { + Matrix CameraView; + Matrix CameraProj; + Matrix CameraViewProj; + Matrix CameraViewProjUnjittered; + Matrix CameraPreviousViewProjUnjittered; + Matrix CameraProjUnjittered; + Matrix CameraProjUnjitteredInverse; + Matrix CameraViewInverse; + Matrix CameraViewProjInverse; + Matrix CameraProjInverse; + float4 CameraPosAdjust; + float4 CameraPreviousPosAdjust; + float4 FrameParams; + float4 DynamicResolutionParams1; + float4 DynamicResolutionParams2; + }; + + D3D11_MAPPED_SUBRESOURCE* mappedFrameBuffer = nullptr; + FrameBuffer frameBufferCached{}; + + void CacheFramebuffer(); + + struct ID3D11DeviceContext_Map + { + static HRESULT thunk(ID3D11DeviceContext* This, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource); + static inline REL::Relocation func; + }; + + struct ID3D11DeviceContext_Unmap + { + static void thunk(ID3D11DeviceContext* This, ID3D11Resource* pResource, UINT Subresource); + static inline REL::Relocation func; + }; };