Implement FSR 2.2 temporal upscaling

Full FSR 2.2 pipeline with depth-based motion vector reprojection,
temporal accumulation with YCoCg neighborhood clamping, and RCAS
contrast-adaptive sharpening.

Architecture (designed for FSR 3.x frame generation readiness):
- Camera: Halton(2,3) sub-pixel jitter with unjittered projection
  stored separately for motion vector computation
- Motion vectors: compute shader reconstructs world position from
  depth + inverse VP, reprojects with previous frame's VP
- Temporal accumulation: compute shader blends 5-10% current frame
  with 90-95% clamped history, adaptive blend for disocclusion
- History: ping-pong R16G16B16A16 buffers at display resolution
- Sharpening: RCAS fragment pass with contrast-adaptive weights

Integration:
- FSR2 replaces both FSR1 and MSAA when enabled
- Scene renders to internal resolution framebuffer (no MSAA)
- Compute passes run between scene and swapchain render passes
- Camera cut detection resets history on teleport
- Quality presets shared with FSR1 (0.50-0.77 scale factors)
- UI: "Upscaling" combo with Off/FSR 1.0/FSR 2.2 options
This commit is contained in:
Kelsi 2026-03-07 23:13:01 -08:00
parent 0ffeabd4ed
commit 52317d1edd
11 changed files with 957 additions and 12 deletions

View file

@ -261,13 +261,15 @@ public:
float getShadowDistance() const { return shadowDistance_; }
void setMsaaSamples(VkSampleCountFlagBits samples);
// FSR 1.0 (FidelityFX Super Resolution) upscaling
// FSR (FidelityFX Super Resolution) upscaling
void setFSREnabled(bool enabled);
bool isFSREnabled() const { return fsr_.enabled; }
void setFSRQuality(float scaleFactor); // 0.50=Perf, 0.59=Balanced, 0.67=Quality, 0.77=UltraQuality
void setFSRSharpness(float sharpness); // 0.0 - 2.0
float getFSRScaleFactor() const { return fsr_.scaleFactor; }
float getFSRSharpness() const { return fsr_.sharpness; }
void setFSR2Enabled(bool enabled);
bool isFSR2Enabled() const { return fsr2_.enabled; }
void setWaterRefractionEnabled(bool enabled);
bool isWaterRefractionEnabled() const;
@ -363,6 +365,65 @@ private:
void destroyFSRResources();
void renderFSRUpscale();
// FSR 2.2 temporal upscaling state
struct FSR2State {
bool enabled = false;
bool needsRecreate = false;
float scaleFactor = 0.77f;
float sharpness = 0.5f;
uint32_t internalWidth = 0;
uint32_t internalHeight = 0;
// Off-screen scene targets (internal resolution, no MSAA — FSR2 replaces AA)
AllocatedImage sceneColor{};
AllocatedImage sceneDepth{};
VkFramebuffer sceneFramebuffer = VK_NULL_HANDLE;
// Samplers
VkSampler linearSampler = VK_NULL_HANDLE; // For color
VkSampler nearestSampler = VK_NULL_HANDLE; // For depth / motion vectors
// Motion vector buffer (internal resolution)
AllocatedImage motionVectors{};
// History buffers (display resolution, ping-pong)
AllocatedImage history[2]{};
uint32_t currentHistory = 0; // Output index (0 or 1)
// Compute pipelines
VkPipeline motionVecPipeline = VK_NULL_HANDLE;
VkPipelineLayout motionVecPipelineLayout = VK_NULL_HANDLE;
VkDescriptorSetLayout motionVecDescSetLayout = VK_NULL_HANDLE;
VkDescriptorPool motionVecDescPool = VK_NULL_HANDLE;
VkDescriptorSet motionVecDescSet = VK_NULL_HANDLE;
VkPipeline accumulatePipeline = VK_NULL_HANDLE;
VkPipelineLayout accumulatePipelineLayout = VK_NULL_HANDLE;
VkDescriptorSetLayout accumulateDescSetLayout = VK_NULL_HANDLE;
VkDescriptorPool accumulateDescPool = VK_NULL_HANDLE;
VkDescriptorSet accumulateDescSets[2] = {}; // Per ping-pong
// RCAS sharpening pass (display resolution)
VkPipeline sharpenPipeline = VK_NULL_HANDLE;
VkPipelineLayout sharpenPipelineLayout = VK_NULL_HANDLE;
VkDescriptorSetLayout sharpenDescSetLayout = VK_NULL_HANDLE;
VkDescriptorPool sharpenDescPool = VK_NULL_HANDLE;
VkDescriptorSet sharpenDescSet = VK_NULL_HANDLE;
// Previous frame state for motion vector reprojection
glm::mat4 prevViewProjection = glm::mat4(1.0f);
glm::vec2 prevJitter = glm::vec2(0.0f);
uint32_t frameIndex = 0;
bool needsHistoryReset = true;
};
FSR2State fsr2_;
bool initFSR2Resources();
void destroyFSR2Resources();
void dispatchMotionVectors();
void dispatchTemporalAccumulate();
void renderFSR2Sharpen();
static float halton(uint32_t index, uint32_t base);
// Footstep event tracking (animation-driven)
uint32_t footstepLastAnimationId = 0;
float footstepLastNormTime = 0.0f;