Bridge FSR3 Vulkan framegen dispatch and route sharpen to interpolated output

This commit is contained in:
Kelsi 2026-03-08 23:20:50 -07:00
parent 538a1db866
commit aa43aa6fc8
4 changed files with 154 additions and 22 deletions

View file

@ -17,6 +17,7 @@ struct AmdFsr3RuntimeInitDesc {
VkFormat colorFormat = VK_FORMAT_UNDEFINED; VkFormat colorFormat = VK_FORMAT_UNDEFINED;
bool hdrInput = false; bool hdrInput = false;
bool depthInverted = false; bool depthInverted = false;
bool enableFrameGeneration = false;
}; };
struct AmdFsr3RuntimeDispatchDesc { struct AmdFsr3RuntimeDispatchDesc {
@ -25,6 +26,7 @@ struct AmdFsr3RuntimeDispatchDesc {
VkImage depthImage = VK_NULL_HANDLE; VkImage depthImage = VK_NULL_HANDLE;
VkImage motionVectorImage = VK_NULL_HANDLE; VkImage motionVectorImage = VK_NULL_HANDLE;
VkImage outputImage = VK_NULL_HANDLE; VkImage outputImage = VK_NULL_HANDLE;
VkImage frameGenOutputImage = VK_NULL_HANDLE;
uint32_t renderWidth = 0; uint32_t renderWidth = 0;
uint32_t renderHeight = 0; uint32_t renderHeight = 0;
uint32_t outputWidth = 0; uint32_t outputWidth = 0;
@ -51,9 +53,11 @@ public:
bool initialize(const AmdFsr3RuntimeInitDesc& desc); bool initialize(const AmdFsr3RuntimeInitDesc& desc);
bool dispatchUpscale(const AmdFsr3RuntimeDispatchDesc& desc); bool dispatchUpscale(const AmdFsr3RuntimeDispatchDesc& desc);
bool dispatchFrameGeneration(const AmdFsr3RuntimeDispatchDesc& desc);
void shutdown(); void shutdown();
bool isReady() const { return ready_; } bool isReady() const { return ready_; }
bool isFrameGenerationReady() const { return frameGenerationReady_; }
const std::string& loadedLibraryPath() const { return loadedLibraryPath_; } const std::string& loadedLibraryPath() const { return loadedLibraryPath_; }
private: private:
@ -62,6 +66,7 @@ private:
void* scratchBuffer_ = nullptr; void* scratchBuffer_ = nullptr;
size_t scratchBufferSize_ = 0; size_t scratchBufferSize_ = 0;
bool ready_ = false; bool ready_ = false;
bool frameGenerationReady_ = false;
struct RuntimeFns; struct RuntimeFns;
RuntimeFns* fns_ = nullptr; RuntimeFns* fns_ = nullptr;
@ -69,4 +74,3 @@ private:
}; };
} // namespace wowee::rendering } // namespace wowee::rendering

View file

@ -411,6 +411,8 @@ private:
// History buffers (display resolution, ping-pong) // History buffers (display resolution, ping-pong)
AllocatedImage history[2]{}; AllocatedImage history[2]{};
AllocatedImage framegenOutput{};
bool framegenOutputValid = false;
uint32_t currentHistory = 0; // Output index (0 or 1) uint32_t currentHistory = 0; // Output index (0 or 1)
// Compute pipelines // Compute pipelines

View file

@ -28,6 +28,8 @@ struct AmdFsr3Runtime::RuntimeFns {
decltype(&ffxGetResourceVK) getResourceVK = nullptr; decltype(&ffxGetResourceVK) getResourceVK = nullptr;
decltype(&ffxFsr3ContextCreate) fsr3ContextCreate = nullptr; decltype(&ffxFsr3ContextCreate) fsr3ContextCreate = nullptr;
decltype(&ffxFsr3ContextDispatchUpscale) fsr3ContextDispatchUpscale = nullptr; decltype(&ffxFsr3ContextDispatchUpscale) fsr3ContextDispatchUpscale = nullptr;
decltype(&ffxFsr3ConfigureFrameGeneration) fsr3ConfigureFrameGeneration = nullptr;
decltype(&ffxFsr3DispatchFrameGeneration) fsr3DispatchFrameGeneration = nullptr;
decltype(&ffxFsr3ContextDestroy) fsr3ContextDestroy = nullptr; decltype(&ffxFsr3ContextDestroy) fsr3ContextDestroy = nullptr;
}; };
#else #else
@ -42,6 +44,10 @@ AmdFsr3Runtime::~AmdFsr3Runtime() {
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN #if WOWEE_HAS_AMD_FSR3_FRAMEGEN
namespace { namespace {
FfxErrorCode vkSwapchainConfigureNoop(const FfxFrameGenerationConfig*) {
return FFX_OK;
}
FfxSurfaceFormat mapVkFormatToFfxSurfaceFormat(VkFormat format, bool isDepth) { FfxSurfaceFormat mapVkFormatToFfxSurfaceFormat(VkFormat format, bool isDepth) {
if (isDepth) { if (isDepth) {
switch (format) { switch (format) {
@ -178,6 +184,8 @@ bool AmdFsr3Runtime::initialize(const AmdFsr3RuntimeInitDesc& desc) {
fns_->getResourceVK = reinterpret_cast<decltype(fns_->getResourceVK)>(resolveSym("ffxGetResourceVK")); fns_->getResourceVK = reinterpret_cast<decltype(fns_->getResourceVK)>(resolveSym("ffxGetResourceVK"));
fns_->fsr3ContextCreate = reinterpret_cast<decltype(fns_->fsr3ContextCreate)>(resolveSym("ffxFsr3ContextCreate")); fns_->fsr3ContextCreate = reinterpret_cast<decltype(fns_->fsr3ContextCreate)>(resolveSym("ffxFsr3ContextCreate"));
fns_->fsr3ContextDispatchUpscale = reinterpret_cast<decltype(fns_->fsr3ContextDispatchUpscale)>(resolveSym("ffxFsr3ContextDispatchUpscale")); fns_->fsr3ContextDispatchUpscale = reinterpret_cast<decltype(fns_->fsr3ContextDispatchUpscale)>(resolveSym("ffxFsr3ContextDispatchUpscale"));
fns_->fsr3ConfigureFrameGeneration = reinterpret_cast<decltype(fns_->fsr3ConfigureFrameGeneration)>(resolveSym("ffxFsr3ConfigureFrameGeneration"));
fns_->fsr3DispatchFrameGeneration = reinterpret_cast<decltype(fns_->fsr3DispatchFrameGeneration)>(resolveSym("ffxFsr3DispatchFrameGeneration"));
fns_->fsr3ContextDestroy = reinterpret_cast<decltype(fns_->fsr3ContextDestroy)>(resolveSym("ffxFsr3ContextDestroy")); fns_->fsr3ContextDestroy = reinterpret_cast<decltype(fns_->fsr3ContextDestroy)>(resolveSym("ffxFsr3ContextDestroy"));
if (!fns_->getScratchMemorySizeVK || !fns_->getDeviceVK || !fns_->getInterfaceVK || if (!fns_->getScratchMemorySizeVK || !fns_->getDeviceVK || !fns_->getInterfaceVK ||
@ -217,8 +225,10 @@ bool AmdFsr3Runtime::initialize(const AmdFsr3RuntimeInitDesc& desc) {
FfxFsr3ContextDescription ctxDesc{}; FfxFsr3ContextDescription ctxDesc{};
ctxDesc.flags = FFX_FSR3_ENABLE_AUTO_EXPOSURE | ctxDesc.flags = FFX_FSR3_ENABLE_AUTO_EXPOSURE |
FFX_FSR3_ENABLE_MOTION_VECTORS_JITTER_CANCELLATION | FFX_FSR3_ENABLE_MOTION_VECTORS_JITTER_CANCELLATION;
FFX_FSR3_ENABLE_UPSCALING_ONLY; if (!desc.enableFrameGeneration) {
ctxDesc.flags |= FFX_FSR3_ENABLE_UPSCALING_ONLY;
}
if (desc.hdrInput) ctxDesc.flags |= FFX_FSR3_ENABLE_HIGH_DYNAMIC_RANGE; if (desc.hdrInput) ctxDesc.flags |= FFX_FSR3_ENABLE_HIGH_DYNAMIC_RANGE;
if (desc.depthInverted) ctxDesc.flags |= FFX_FSR3_ENABLE_DEPTH_INVERTED; if (desc.depthInverted) ctxDesc.flags |= FFX_FSR3_ENABLE_DEPTH_INVERTED;
ctxDesc.maxRenderSize.width = desc.maxRenderWidth; ctxDesc.maxRenderSize.width = desc.maxRenderWidth;
@ -227,6 +237,9 @@ bool AmdFsr3Runtime::initialize(const AmdFsr3RuntimeInitDesc& desc) {
ctxDesc.upscaleOutputSize.height = desc.displayHeight; ctxDesc.upscaleOutputSize.height = desc.displayHeight;
ctxDesc.displaySize.width = desc.displayWidth; ctxDesc.displaySize.width = desc.displayWidth;
ctxDesc.displaySize.height = desc.displayHeight; ctxDesc.displaySize.height = desc.displayHeight;
if (!backendShared.fpSwapChainConfigureFrameGeneration) {
backendShared.fpSwapChainConfigureFrameGeneration = vkSwapchainConfigureNoop;
}
ctxDesc.backendInterfaceSharedResources = backendShared; ctxDesc.backendInterfaceSharedResources = backendShared;
ctxDesc.backendInterfaceUpscaling = backendShared; ctxDesc.backendInterfaceUpscaling = backendShared;
ctxDesc.backendInterfaceFrameInterpolation = backendShared; ctxDesc.backendInterfaceFrameInterpolation = backendShared;
@ -248,6 +261,27 @@ bool AmdFsr3Runtime::initialize(const AmdFsr3RuntimeInitDesc& desc) {
return false; return false;
} }
if (desc.enableFrameGeneration) {
if (!fns_->fsr3ConfigureFrameGeneration || !fns_->fsr3DispatchFrameGeneration) {
LOG_WARNING("FSR3 runtime: frame generation symbols unavailable in ", loadedLibraryPath_);
shutdown();
return false;
}
FfxFrameGenerationConfig fgCfg{};
fgCfg.frameGenerationEnabled = true;
fgCfg.allowAsyncWorkloads = false;
fgCfg.flags = 0;
fgCfg.onlyPresentInterpolated = false;
FfxErrorCode cfgErr = fns_->fsr3ConfigureFrameGeneration(
reinterpret_cast<FfxFsr3Context*>(contextStorage_), &fgCfg);
if (cfgErr != FFX_OK) {
LOG_WARNING("FSR3 runtime: ffxFsr3ConfigureFrameGeneration failed (", static_cast<int>(cfgErr), ").");
shutdown();
return false;
}
frameGenerationReady_ = true;
}
ready_ = true; ready_ = true;
return true; return true;
#endif #endif
@ -305,6 +339,39 @@ bool AmdFsr3Runtime::dispatchUpscale(const AmdFsr3RuntimeDispatchDesc& desc) {
#endif #endif
} }
bool AmdFsr3Runtime::dispatchFrameGeneration(const AmdFsr3RuntimeDispatchDesc& desc) {
#if !WOWEE_HAS_AMD_FSR3_FRAMEGEN
(void)desc;
return false;
#else
if (!ready_ || !frameGenerationReady_ || !contextStorage_ || !fns_ || !fns_->fsr3DispatchFrameGeneration) return false;
if (!desc.commandBuffer || !desc.outputImage || !desc.frameGenOutputImage ||
desc.outputWidth == 0 || desc.outputHeight == 0 || desc.outputFormat == VK_FORMAT_UNDEFINED) return false;
FfxResourceDescription presentDesc = makeResourceDescription(
desc.outputFormat, desc.outputWidth, desc.outputHeight, FFX_RESOURCE_USAGE_READ_ONLY);
FfxResourceDescription fgOutDesc = makeResourceDescription(
desc.outputFormat, desc.outputWidth, desc.outputHeight, FFX_RESOURCE_USAGE_UAV);
static wchar_t kPresentName[] = L"FSR3_PresentColor";
static wchar_t kInterpolatedName[] = L"FSR3_InterpolatedOutput";
FfxFrameGenerationDispatchDescription fgDispatch{};
fgDispatch.commandList = fns_->getCommandListVK(desc.commandBuffer);
fgDispatch.presentColor = fns_->getResourceVK(
reinterpret_cast<void*>(desc.outputImage), presentDesc, kPresentName, FFX_RESOURCE_STATE_COMPUTE_READ);
fgDispatch.outputs[0] = fns_->getResourceVK(
reinterpret_cast<void*>(desc.frameGenOutputImage), fgOutDesc, kInterpolatedName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
fgDispatch.numInterpolatedFrames = 1;
fgDispatch.reset = desc.reset;
fgDispatch.backBufferTransferFunction = FFX_BACKBUFFER_TRANSFER_FUNCTION_SRGB;
fgDispatch.minMaxLuminance[0] = 0.0f;
fgDispatch.minMaxLuminance[1] = 1.0f;
FfxErrorCode err = fns_->fsr3DispatchFrameGeneration(&fgDispatch);
return err == FFX_OK;
#endif
}
void AmdFsr3Runtime::shutdown() { void AmdFsr3Runtime::shutdown() {
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN #if WOWEE_HAS_AMD_FSR3_FRAMEGEN
if (contextStorage_ && fns_ && fns_->fsr3ContextDestroy) { if (contextStorage_ && fns_ && fns_->fsr3ContextDestroy) {
@ -321,6 +388,7 @@ void AmdFsr3Runtime::shutdown() {
} }
scratchBufferSize_ = 0; scratchBufferSize_ = 0;
ready_ = false; ready_ = false;
frameGenerationReady_ = false;
if (fns_) { if (fns_) {
delete fns_; delete fns_;
fns_ = nullptr; fns_ = nullptr;

View file

@ -1167,14 +1167,21 @@ void Renderer::endFrame() {
} }
} else { } else {
dispatchAmdFsr2(); dispatchAmdFsr2();
dispatchAmdFsr3Framegen();
} }
// Transition history output: GENERAL -> SHADER_READ_ONLY for sharpen pass // Transition post-FSR input for sharpen pass.
transitionImageLayout(currentCmd, fsr2_.history[fsr2_.currentHistory].image, if (fsr2_.amdFsr3FramegenRuntimeActive && fsr2_.framegenOutput.image) {
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, transitionImageLayout(currentCmd, fsr2_.framegenOutput.image,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
fsr2_.framegenOutputValid = true;
} else {
transitionImageLayout(currentCmd, fsr2_.history[fsr2_.currentHistory].image,
VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
} else { } else {
transitionImageLayout(currentCmd, fsr2_.sceneColor.image, transitionImageLayout(currentCmd, fsr2_.sceneColor.image,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
@ -3751,6 +3758,7 @@ bool Renderer::initFSR2Resources() {
fsr2_.useAmdBackend = false; fsr2_.useAmdBackend = false;
fsr2_.amdFsr3FramegenRuntimeActive = false; fsr2_.amdFsr3FramegenRuntimeActive = false;
fsr2_.amdFsr3FramegenRuntimeReady = false; fsr2_.amdFsr3FramegenRuntimeReady = false;
fsr2_.framegenOutputValid = false;
#if WOWEE_HAS_AMD_FSR2 #if WOWEE_HAS_AMD_FSR2
LOG_INFO("FSR2: AMD FidelityFX SDK detected at build time."); LOG_INFO("FSR2: AMD FidelityFX SDK detected at build time.");
#else #else
@ -3782,6 +3790,9 @@ bool Renderer::initFSR2Resources() {
VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT); VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
if (!fsr2_.history[i].image) { LOG_ERROR("FSR2: failed to create history buffer ", i); destroyFSR2Resources(); return false; } if (!fsr2_.history[i].image) { LOG_ERROR("FSR2: failed to create history buffer ", i); destroyFSR2Resources(); return false; }
} }
fsr2_.framegenOutput = createImage(device, alloc, swapExtent.width, swapExtent.height,
VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT);
if (!fsr2_.framegenOutput.image) { LOG_ERROR("FSR2: failed to create framegen output"); destroyFSR2Resources(); return false; }
// Scene framebuffer (non-MSAA: [color, depth]) // Scene framebuffer (non-MSAA: [color, depth])
// Must use the same render pass as the swapchain — which must be non-MSAA when FSR2 is active // Must use the same render pass as the swapchain — which must be non-MSAA when FSR2 is active
@ -3861,15 +3872,16 @@ bool Renderer::initFSR2Resources() {
fgInit.maxRenderHeight = fsr2_.internalHeight; fgInit.maxRenderHeight = fsr2_.internalHeight;
fgInit.displayWidth = swapExtent.width; fgInit.displayWidth = swapExtent.width;
fgInit.displayHeight = swapExtent.height; fgInit.displayHeight = swapExtent.height;
fgInit.colorFormat = vkCtx->getSwapchainFormat(); fgInit.colorFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
fgInit.hdrInput = false; fgInit.hdrInput = false;
fgInit.depthInverted = false; fgInit.depthInverted = false;
fgInit.enableFrameGeneration = true;
fsr2_.amdFsr3FramegenRuntimeReady = fsr2_.amdFsr3Runtime->initialize(fgInit); fsr2_.amdFsr3FramegenRuntimeReady = fsr2_.amdFsr3Runtime->initialize(fgInit);
if (fsr2_.amdFsr3FramegenRuntimeReady) { if (fsr2_.amdFsr3FramegenRuntimeReady) {
LOG_INFO("FSR3 framegen runtime library loaded from ", fsr2_.amdFsr3Runtime->loadedLibraryPath(), LOG_INFO("FSR3 framegen runtime library loaded from ", fsr2_.amdFsr3Runtime->loadedLibraryPath(),
" (upscale dispatch enabled)"); " (upscale+framegen dispatch enabled)");
} else { } else {
LOG_WARNING("FSR3 framegen toggle is enabled, but runtime library was not found. ", LOG_WARNING("FSR3 framegen toggle is enabled, but runtime initialization failed. ",
"Set WOWEE_FFX_SDK_RUNTIME_LIB to the SDK runtime binary path."); "Set WOWEE_FFX_SDK_RUNTIME_LIB to the SDK runtime binary path.");
} }
} }
@ -4173,6 +4185,7 @@ void Renderer::destroyFSR2Resources() {
#endif #endif
fsr2_.amdFsr3FramegenRuntimeActive = false; fsr2_.amdFsr3FramegenRuntimeActive = false;
fsr2_.amdFsr3FramegenRuntimeReady = false; fsr2_.amdFsr3FramegenRuntimeReady = false;
fsr2_.framegenOutputValid = false;
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN #if WOWEE_HAS_AMD_FSR3_FRAMEGEN
if (fsr2_.amdFsr3Runtime) { if (fsr2_.amdFsr3Runtime) {
fsr2_.amdFsr3Runtime->shutdown(); fsr2_.amdFsr3Runtime->shutdown();
@ -4201,6 +4214,7 @@ void Renderer::destroyFSR2Resources() {
destroyImage(device, alloc, fsr2_.motionVectors); destroyImage(device, alloc, fsr2_.motionVectors);
for (int i = 0; i < 2; i++) destroyImage(device, alloc, fsr2_.history[i]); for (int i = 0; i < 2; i++) destroyImage(device, alloc, fsr2_.history[i]);
destroyImage(device, alloc, fsr2_.framegenOutput);
destroyImage(device, alloc, fsr2_.sceneDepth); destroyImage(device, alloc, fsr2_.sceneDepth);
destroyImage(device, alloc, fsr2_.sceneColor); destroyImage(device, alloc, fsr2_.sceneColor);
@ -4398,15 +4412,36 @@ void Renderer::dispatchAmdFsr2() {
} }
void Renderer::dispatchAmdFsr3Framegen() { void Renderer::dispatchAmdFsr3Framegen() {
if (!fsr2_.amdFsr3FramegenEnabled) {
fsr2_.amdFsr3FramegenRuntimeActive = false;
return;
}
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN #if WOWEE_HAS_AMD_FSR3_FRAMEGEN
if (!fsr2_.amdFsr3Runtime || !fsr2_.amdFsr3FramegenRuntimeReady) { if (!fsr2_.amdFsr3Runtime || !fsr2_.amdFsr3FramegenRuntimeReady) {
fsr2_.amdFsr3FramegenRuntimeActive = false; fsr2_.amdFsr3FramegenRuntimeActive = false;
return; return;
} }
uint32_t outputIdx = fsr2_.currentHistory;
transitionImageLayout(currentCmd, fsr2_.sceneColor.image,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
transitionImageLayout(currentCmd, fsr2_.motionVectors.image,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
transitionImageLayout(currentCmd, fsr2_.sceneDepth.image,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
transitionImageLayout(currentCmd, fsr2_.history[outputIdx].image,
fsr2_.needsHistoryReset ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
if (fsr2_.amdFsr3FramegenEnabled && fsr2_.framegenOutput.image) {
transitionImageLayout(currentCmd, fsr2_.framegenOutput.image,
fsr2_.framegenOutputValid ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_GENERAL,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
}
AmdFsr3RuntimeDispatchDesc fgDispatch{}; AmdFsr3RuntimeDispatchDesc fgDispatch{};
fgDispatch.commandBuffer = currentCmd; fgDispatch.commandBuffer = currentCmd;
@ -4422,6 +4457,7 @@ void Renderer::dispatchAmdFsr3Framegen() {
fgDispatch.depthFormat = vkCtx->getDepthFormat(); fgDispatch.depthFormat = vkCtx->getDepthFormat();
fgDispatch.motionVectorFormat = VK_FORMAT_R16G16_SFLOAT; fgDispatch.motionVectorFormat = VK_FORMAT_R16G16_SFLOAT;
fgDispatch.outputFormat = VK_FORMAT_R16G16B16A16_SFLOAT; fgDispatch.outputFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
fgDispatch.frameGenOutputImage = fsr2_.framegenOutput.image;
glm::vec2 jitterNdc = camera ? camera->getJitter() : glm::vec2(0.0f); glm::vec2 jitterNdc = camera ? camera->getJitter() : glm::vec2(0.0f);
fgDispatch.jitterX = jitterNdc.x * 0.5f * static_cast<float>(fsr2_.internalWidth); fgDispatch.jitterX = jitterNdc.x * 0.5f * static_cast<float>(fsr2_.internalWidth);
fgDispatch.jitterY = jitterNdc.y * 0.5f * static_cast<float>(fsr2_.internalHeight); fgDispatch.jitterY = jitterNdc.y * 0.5f * static_cast<float>(fsr2_.internalHeight);
@ -4433,16 +4469,34 @@ void Renderer::dispatchAmdFsr3Framegen() {
fgDispatch.cameraFovYRadians = camera ? glm::radians(camera->getFovDegrees()) : 1.0f; fgDispatch.cameraFovYRadians = camera ? glm::radians(camera->getFovDegrees()) : 1.0f;
fgDispatch.reset = fsr2_.needsHistoryReset; fgDispatch.reset = fsr2_.needsHistoryReset;
bool ok = fsr2_.amdFsr3Runtime->dispatchUpscale(fgDispatch); if (!fsr2_.amdFsr3Runtime->dispatchUpscale(fgDispatch)) {
if (!ok) {
static bool warnedRuntimeDispatch = false; static bool warnedRuntimeDispatch = false;
if (!warnedRuntimeDispatch) { if (!warnedRuntimeDispatch) {
warnedRuntimeDispatch = true; warnedRuntimeDispatch = true;
LOG_WARNING("FSR3 runtime dispatch failed; falling back to FSR2 dispatch output."); LOG_WARNING("FSR3 runtime upscale dispatch failed; falling back to FSR2 dispatch output.");
} }
fsr2_.amdFsr3FramegenRuntimeActive = false; fsr2_.amdFsr3FramegenRuntimeActive = false;
return; return;
} }
if (!fsr2_.amdFsr3FramegenEnabled) {
fsr2_.amdFsr3FramegenRuntimeActive = false;
return;
}
if (!fsr2_.amdFsr3Runtime->isFrameGenerationReady()) {
fsr2_.amdFsr3FramegenRuntimeActive = false;
return;
}
if (!fsr2_.amdFsr3Runtime->dispatchFrameGeneration(fgDispatch)) {
static bool warnedFgDispatch = false;
if (!warnedFgDispatch) {
warnedFgDispatch = true;
LOG_WARNING("FSR3 runtime frame generation dispatch failed; using upscaled output only.");
}
fsr2_.amdFsr3FramegenRuntimeActive = false;
return;
}
fsr2_.framegenOutputValid = true;
fsr2_.amdFsr3FramegenRuntimeActive = true; fsr2_.amdFsr3FramegenRuntimeActive = true;
#else #else
fsr2_.amdFsr3FramegenRuntimeActive = false; fsr2_.amdFsr3FramegenRuntimeActive = false;
@ -4462,9 +4516,13 @@ void Renderer::renderFSR2Sharpen() {
// Update sharpen descriptor to point at current history output // Update sharpen descriptor to point at current history output
VkDescriptorImageInfo imgInfo{}; VkDescriptorImageInfo imgInfo{};
imgInfo.sampler = fsr2_.linearSampler; imgInfo.sampler = fsr2_.linearSampler;
imgInfo.imageView = fsr2_.useAmdBackend if (fsr2_.useAmdBackend) {
? fsr2_.history[outputIdx].imageView imgInfo.imageView = (fsr2_.amdFsr3FramegenRuntimeActive && fsr2_.framegenOutput.imageView)
: fsr2_.sceneColor.imageView; ? fsr2_.framegenOutput.imageView
: fsr2_.history[outputIdx].imageView;
} else {
imgInfo.imageView = fsr2_.sceneColor.imageView;
}
imgInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; imgInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}; VkWriteDescriptorSet write{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};