Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
8 changes: 8 additions & 0 deletions include/ffx_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion include/ffx_api_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#pragma once

#include <stdint.h>
#include <stdbool.h>

/// An enumeration of surface formats. Needs to match enum FfxSurfaceFormat
enum FfxApiSurfaceFormat
Expand Down Expand Up @@ -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
Expand Down
25 changes: 19 additions & 6 deletions include/ffx_framegeneration.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#if defined(__cplusplus)
extern "C" {
#endif

enum FfxApiCreateContextFramegenerationFlags
{
FFX_FRAMEGENERATION_ENABLE_ASYNC_WORKLOAD_SUPPORT = (1<<0),
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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);
Expand Down Expand Up @@ -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.
};
Comment on lines +177 to +186

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the header type usage.

The struct uses ffxConfigureDescHeader while being a dispatch descriptor type. This might be intentional for chaining, but please verify this is the correct header type.


🏁 Script executed:

#!/bin/bash
# Description: Check if other dispatch descriptor types use ffxConfigureDescHeader for chaining

# Search for other dispatch descriptor types and their header usage
ast-grep --pattern 'struct $STRUCT {
  $HEADER header;
  $$$
}' | grep -B1 -A1 "FFX_API_DISPATCH_DESC_TYPE"

Length of output: 107


I’ll check all dispatch descriptor definitions and inspect their header fields for consistency:


🏁 Script executed:

#!/bin/bash
# List all dispatch descriptor macros and the next line (struct name)
rg "#define FFX_API_DISPATCH_DESC_TYPE" -n -A1

echo "----"

# Show each dispatch descriptor struct definition and its first few lines to see which header type is used
rg "struct ffxDispatchDesc" -n include -A5

Length of output: 3375


Use ffxDispatchDescHeader for dispatch descriptors

The camera-info dispatch struct is the only FFX_API_DISPATCH_DESC_TYPE_* that uses ffxConfigureDescHeader—all other dispatch descriptors use ffxDispatchDescHeader. To keep the chaining consistent, update the header type:

• File: include/ffx_framegeneration.h
Lines: ~181
Change:

-    ffxConfigureDescHeader header;
+    ffxDispatchDescHeader  header;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#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.
};
#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
{
ffxDispatchDescHeader 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.
};
🤖 Prompt for AI Agents
In include/ffx_framegeneration.h around lines 177 to 186, the struct
ffxDispatchDescFrameGenerationPrepareCameraInfo currently uses
ffxConfigureDescHeader as its header type, but all other dispatch descriptor
structs use ffxDispatchDescHeader for chaining consistency. Update the header
type in this struct from ffxConfigureDescHeader to ffxDispatchDescHeader to
align with the other dispatch descriptors.


#if defined(__cplusplus)
} // extern "C"
#endif
14 changes: 9 additions & 5 deletions include/ffx_framegeneration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ struct struct_type<ffxCreateContextDescFrameGeneration> : std::integral_constant

struct CreateContextDescFrameGeneration : public InitHelper<ffxCreateContextDescFrameGeneration> {};

template<>
struct struct_type<ffxCreateContextDescFrameGenerationHudless> : std::integral_constant<uint64_t, FFX_API_CREATE_CONTEXT_DESC_TYPE_FRAMEGENERATION_HUDLESS> {};

struct CreateContextDescFrameGenerationHudless : public InitHelper<ffxCreateContextDescFrameGenerationHudless> {};

template<>
struct struct_type<ffxConfigureDescFrameGeneration> : std::integral_constant<uint64_t, FFX_API_CONFIGURE_DESC_TYPE_FRAMEGENERATION> {};

Expand Down Expand Up @@ -70,4 +65,13 @@ struct struct_type<ffxConfigureDescFrameGenerationRegisterDistortionFieldResourc

struct ConfigureDescFrameGenerationRegisterDistortionFieldResource : public InitHelper<ffxConfigureDescFrameGenerationRegisterDistortionFieldResource> {};

template<>
struct struct_type<ffxCreateContextDescFrameGenerationHudless> : std::integral_constant<uint64_t, FFX_API_CREATE_CONTEXT_DESC_TYPE_FRAMEGENERATION_HUDLESS> {};

struct CreateContextDescFrameGenerationHudless : public InitHelper<ffxCreateContextDescFrameGenerationHudless> {};

template<>
struct struct_type<ffxDispatchDescFrameGenerationPrepareCameraInfo> : std::integral_constant<uint64_t, FFX_API_DISPATCH_DESC_TYPE_FRAMEGENERATION_PREPARE_CAMERAINFO> {};

struct DispatchDescFrameGenerationPrepareCameraInfo : public InitHelper<ffxDispatchDescFrameGenerationPrepareCameraInfo> {};
}
32 changes: 32 additions & 0 deletions src/FidelityFX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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++;
Expand Down
99 changes: 96 additions & 3 deletions src/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

TracyD3D11Collect(state->tracyCtx);

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)))) {
Expand Down Expand Up @@ -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<decltype(thunk)> 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<decltype(thunk)> 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();
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -1033,8 +1116,18 @@ namespace Hooks
REL::Relocation<std::uintptr_t>(renderPassCacheCtor, 0x191 - 2).address(),
reinterpret_cast<const uint8_t*>(&passCountSE), 4);
}

if (!REL::Module::IsVR()) {
stl::write_thunk_call<Main_Update_Begin>(REL::RelocationID(35565, 36564).address() + REL::Relocate(0x53, 0x6E));
stl::write_thunk_call<Main_Update_Swap>(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();
Expand Down
Loading