mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-25 21:03:51 +00:00
Fix FSR3 runtime wrapper for local SDK API and real Vulkan resource dispatch
This commit is contained in:
parent
f1099f5940
commit
538a1db866
5 changed files with 486 additions and 90 deletions
|
|
@ -397,6 +397,7 @@ set(WOWEE_SOURCES
|
||||||
|
|
||||||
# Rendering
|
# Rendering
|
||||||
src/rendering/renderer.cpp
|
src/rendering/renderer.cpp
|
||||||
|
src/rendering/amd_fsr3_runtime.cpp
|
||||||
src/rendering/shader.cpp
|
src/rendering/shader.cpp
|
||||||
src/rendering/texture.cpp
|
src/rendering/texture.cpp
|
||||||
src/rendering/mesh.cpp
|
src/rendering/mesh.cpp
|
||||||
|
|
|
||||||
72
include/rendering/amd_fsr3_runtime.hpp
Normal file
72
include/rendering/amd_fsr3_runtime.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
|
namespace wowee::rendering {
|
||||||
|
|
||||||
|
struct AmdFsr3RuntimeInitDesc {
|
||||||
|
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||||
|
VkDevice device = VK_NULL_HANDLE;
|
||||||
|
PFN_vkGetDeviceProcAddr getDeviceProcAddr = nullptr;
|
||||||
|
uint32_t maxRenderWidth = 0;
|
||||||
|
uint32_t maxRenderHeight = 0;
|
||||||
|
uint32_t displayWidth = 0;
|
||||||
|
uint32_t displayHeight = 0;
|
||||||
|
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
bool hdrInput = false;
|
||||||
|
bool depthInverted = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AmdFsr3RuntimeDispatchDesc {
|
||||||
|
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
|
||||||
|
VkImage colorImage = VK_NULL_HANDLE;
|
||||||
|
VkImage depthImage = VK_NULL_HANDLE;
|
||||||
|
VkImage motionVectorImage = VK_NULL_HANDLE;
|
||||||
|
VkImage outputImage = VK_NULL_HANDLE;
|
||||||
|
uint32_t renderWidth = 0;
|
||||||
|
uint32_t renderHeight = 0;
|
||||||
|
uint32_t outputWidth = 0;
|
||||||
|
uint32_t outputHeight = 0;
|
||||||
|
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
VkFormat motionVectorFormat = VK_FORMAT_R16G16_SFLOAT;
|
||||||
|
VkFormat outputFormat = VK_FORMAT_UNDEFINED;
|
||||||
|
float jitterX = 0.0f;
|
||||||
|
float jitterY = 0.0f;
|
||||||
|
float motionScaleX = 1.0f;
|
||||||
|
float motionScaleY = 1.0f;
|
||||||
|
float frameTimeDeltaMs = 16.67f;
|
||||||
|
float cameraNear = 0.1f;
|
||||||
|
float cameraFar = 1000.0f;
|
||||||
|
float cameraFovYRadians = 1.0f;
|
||||||
|
bool reset = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AmdFsr3Runtime {
|
||||||
|
public:
|
||||||
|
AmdFsr3Runtime();
|
||||||
|
~AmdFsr3Runtime();
|
||||||
|
|
||||||
|
bool initialize(const AmdFsr3RuntimeInitDesc& desc);
|
||||||
|
bool dispatchUpscale(const AmdFsr3RuntimeDispatchDesc& desc);
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
bool isReady() const { return ready_; }
|
||||||
|
const std::string& loadedLibraryPath() const { return loadedLibraryPath_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* libHandle_ = nullptr;
|
||||||
|
std::string loadedLibraryPath_;
|
||||||
|
void* scratchBuffer_ = nullptr;
|
||||||
|
size_t scratchBufferSize_ = 0;
|
||||||
|
bool ready_ = false;
|
||||||
|
|
||||||
|
struct RuntimeFns;
|
||||||
|
RuntimeFns* fns_ = nullptr;
|
||||||
|
void* contextStorage_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace wowee::rendering
|
||||||
|
|
||||||
|
|
@ -51,6 +51,7 @@ class WorldMap;
|
||||||
class QuestMarkerRenderer;
|
class QuestMarkerRenderer;
|
||||||
class CharacterPreview;
|
class CharacterPreview;
|
||||||
class Shader;
|
class Shader;
|
||||||
|
class AmdFsr3Runtime;
|
||||||
|
|
||||||
class Renderer {
|
class Renderer {
|
||||||
public:
|
public:
|
||||||
|
|
@ -450,6 +451,7 @@ private:
|
||||||
void* amdScratchBuffer = nullptr;
|
void* amdScratchBuffer = nullptr;
|
||||||
size_t amdScratchBufferSize = 0;
|
size_t amdScratchBufferSize = 0;
|
||||||
#endif
|
#endif
|
||||||
|
std::unique_ptr<AmdFsr3Runtime> amdFsr3Runtime;
|
||||||
|
|
||||||
// Convergent accumulation: jitter for N frames then freeze
|
// Convergent accumulation: jitter for N frames then freeze
|
||||||
int convergenceFrame = 0;
|
int convergenceFrame = 0;
|
||||||
|
|
|
||||||
337
src/rendering/amd_fsr3_runtime.cpp
Normal file
337
src/rendering/amd_fsr3_runtime.cpp
Normal file
|
|
@ -0,0 +1,337 @@
|
||||||
|
#include "rendering/amd_fsr3_runtime.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "core/logger.hpp"
|
||||||
|
|
||||||
|
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
|
#include <FidelityFX/host/ffx_fsr3.h>
|
||||||
|
#include <FidelityFX/host/backends/vk/ffx_vk.h>
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace wowee::rendering {
|
||||||
|
|
||||||
|
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
|
struct AmdFsr3Runtime::RuntimeFns {
|
||||||
|
decltype(&ffxGetScratchMemorySizeVK) getScratchMemorySizeVK = nullptr;
|
||||||
|
decltype(&ffxGetDeviceVK) getDeviceVK = nullptr;
|
||||||
|
decltype(&ffxGetInterfaceVK) getInterfaceVK = nullptr;
|
||||||
|
decltype(&ffxGetCommandListVK) getCommandListVK = nullptr;
|
||||||
|
decltype(&ffxGetResourceVK) getResourceVK = nullptr;
|
||||||
|
decltype(&ffxFsr3ContextCreate) fsr3ContextCreate = nullptr;
|
||||||
|
decltype(&ffxFsr3ContextDispatchUpscale) fsr3ContextDispatchUpscale = nullptr;
|
||||||
|
decltype(&ffxFsr3ContextDestroy) fsr3ContextDestroy = nullptr;
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
struct AmdFsr3Runtime::RuntimeFns {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
AmdFsr3Runtime::AmdFsr3Runtime() = default;
|
||||||
|
|
||||||
|
AmdFsr3Runtime::~AmdFsr3Runtime() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
|
namespace {
|
||||||
|
FfxSurfaceFormat mapVkFormatToFfxSurfaceFormat(VkFormat format, bool isDepth) {
|
||||||
|
if (isDepth) {
|
||||||
|
switch (format) {
|
||||||
|
case VK_FORMAT_D32_SFLOAT:
|
||||||
|
return FFX_SURFACE_FORMAT_R32_FLOAT;
|
||||||
|
case VK_FORMAT_D16_UNORM:
|
||||||
|
return FFX_SURFACE_FORMAT_R16_UNORM;
|
||||||
|
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||||
|
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||||
|
return FFX_SURFACE_FORMAT_R32_FLOAT;
|
||||||
|
default:
|
||||||
|
return FFX_SURFACE_FORMAT_R32_FLOAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (format) {
|
||||||
|
case VK_FORMAT_R16G16B16A16_SFLOAT:
|
||||||
|
return FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT;
|
||||||
|
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||||
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||||
|
return FFX_SURFACE_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||||
|
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||||
|
return FFX_SURFACE_FORMAT_R8G8B8A8_SRGB;
|
||||||
|
case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
|
||||||
|
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
|
||||||
|
return FFX_SURFACE_FORMAT_R10G10B10A2_UNORM;
|
||||||
|
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
|
||||||
|
return FFX_SURFACE_FORMAT_R11G11B10_FLOAT;
|
||||||
|
case VK_FORMAT_R16G16_SFLOAT:
|
||||||
|
return FFX_SURFACE_FORMAT_R16G16_FLOAT;
|
||||||
|
case VK_FORMAT_R16G16_UINT:
|
||||||
|
return FFX_SURFACE_FORMAT_R16G16_UINT;
|
||||||
|
case VK_FORMAT_R16_SFLOAT:
|
||||||
|
return FFX_SURFACE_FORMAT_R16_FLOAT;
|
||||||
|
case VK_FORMAT_R16_UINT:
|
||||||
|
return FFX_SURFACE_FORMAT_R16_UINT;
|
||||||
|
case VK_FORMAT_R16_UNORM:
|
||||||
|
return FFX_SURFACE_FORMAT_R16_UNORM;
|
||||||
|
case VK_FORMAT_R16_SNORM:
|
||||||
|
return FFX_SURFACE_FORMAT_R16_SNORM;
|
||||||
|
case VK_FORMAT_R8_UNORM:
|
||||||
|
return FFX_SURFACE_FORMAT_R8_UNORM;
|
||||||
|
case VK_FORMAT_R8_UINT:
|
||||||
|
return FFX_SURFACE_FORMAT_R8_UINT;
|
||||||
|
case VK_FORMAT_R8G8_UNORM:
|
||||||
|
return FFX_SURFACE_FORMAT_R8G8_UNORM;
|
||||||
|
case VK_FORMAT_R32_SFLOAT:
|
||||||
|
return FFX_SURFACE_FORMAT_R32_FLOAT;
|
||||||
|
case VK_FORMAT_R32_UINT:
|
||||||
|
return FFX_SURFACE_FORMAT_R32_UINT;
|
||||||
|
default:
|
||||||
|
return FFX_SURFACE_FORMAT_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FfxResourceDescription makeResourceDescription(VkFormat format,
|
||||||
|
uint32_t width,
|
||||||
|
uint32_t height,
|
||||||
|
FfxResourceUsage usage,
|
||||||
|
bool isDepth = false) {
|
||||||
|
FfxResourceDescription description{};
|
||||||
|
description.type = FFX_RESOURCE_TYPE_TEXTURE2D;
|
||||||
|
description.format = mapVkFormatToFfxSurfaceFormat(format, isDepth);
|
||||||
|
description.width = width;
|
||||||
|
description.height = height;
|
||||||
|
description.depth = 1;
|
||||||
|
description.mipCount = 1;
|
||||||
|
description.flags = FFX_RESOURCE_FLAGS_NONE;
|
||||||
|
description.usage = usage;
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool AmdFsr3Runtime::initialize(const AmdFsr3RuntimeInitDesc& desc) {
|
||||||
|
shutdown();
|
||||||
|
|
||||||
|
#if !WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
|
(void)desc;
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
if (!desc.physicalDevice || !desc.device || !desc.getDeviceProcAddr ||
|
||||||
|
desc.maxRenderWidth == 0 || desc.maxRenderHeight == 0 ||
|
||||||
|
desc.displayWidth == 0 || desc.displayHeight == 0 ||
|
||||||
|
desc.colorFormat == VK_FORMAT_UNDEFINED) {
|
||||||
|
LOG_WARNING("FSR3 runtime: invalid initialization descriptors.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> candidates;
|
||||||
|
if (const char* envPath = std::getenv("WOWEE_FFX_SDK_RUNTIME_LIB")) {
|
||||||
|
if (*envPath) candidates.emplace_back(envPath);
|
||||||
|
}
|
||||||
|
#if defined(_WIN32)
|
||||||
|
candidates.emplace_back("ffx_fsr3_vk.dll");
|
||||||
|
candidates.emplace_back("ffx_fsr3.dll");
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
candidates.emplace_back("libffx_fsr3_vk.dylib");
|
||||||
|
candidates.emplace_back("libffx_fsr3.dylib");
|
||||||
|
#else
|
||||||
|
candidates.emplace_back("libffx_fsr3_vk.so");
|
||||||
|
candidates.emplace_back("libffx_fsr3.so");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (const std::string& candidate : candidates) {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
HMODULE h = LoadLibraryA(candidate.c_str());
|
||||||
|
if (!h) continue;
|
||||||
|
libHandle_ = reinterpret_cast<void*>(h);
|
||||||
|
#else
|
||||||
|
void* h = dlopen(candidate.c_str(), RTLD_NOW | RTLD_LOCAL);
|
||||||
|
if (!h) continue;
|
||||||
|
libHandle_ = h;
|
||||||
|
#endif
|
||||||
|
loadedLibraryPath_ = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!libHandle_) return false;
|
||||||
|
|
||||||
|
auto resolveSym = [&](const char* name) -> void* {
|
||||||
|
#if defined(_WIN32)
|
||||||
|
return reinterpret_cast<void*>(GetProcAddress(reinterpret_cast<HMODULE>(libHandle_), name));
|
||||||
|
#else
|
||||||
|
return dlsym(libHandle_, name);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
fns_ = new RuntimeFns{};
|
||||||
|
fns_->getScratchMemorySizeVK = reinterpret_cast<decltype(fns_->getScratchMemorySizeVK)>(resolveSym("ffxGetScratchMemorySizeVK"));
|
||||||
|
fns_->getDeviceVK = reinterpret_cast<decltype(fns_->getDeviceVK)>(resolveSym("ffxGetDeviceVK"));
|
||||||
|
fns_->getInterfaceVK = reinterpret_cast<decltype(fns_->getInterfaceVK)>(resolveSym("ffxGetInterfaceVK"));
|
||||||
|
fns_->getCommandListVK = reinterpret_cast<decltype(fns_->getCommandListVK)>(resolveSym("ffxGetCommandListVK"));
|
||||||
|
fns_->getResourceVK = reinterpret_cast<decltype(fns_->getResourceVK)>(resolveSym("ffxGetResourceVK"));
|
||||||
|
fns_->fsr3ContextCreate = reinterpret_cast<decltype(fns_->fsr3ContextCreate)>(resolveSym("ffxFsr3ContextCreate"));
|
||||||
|
fns_->fsr3ContextDispatchUpscale = reinterpret_cast<decltype(fns_->fsr3ContextDispatchUpscale)>(resolveSym("ffxFsr3ContextDispatchUpscale"));
|
||||||
|
fns_->fsr3ContextDestroy = reinterpret_cast<decltype(fns_->fsr3ContextDestroy)>(resolveSym("ffxFsr3ContextDestroy"));
|
||||||
|
|
||||||
|
if (!fns_->getScratchMemorySizeVK || !fns_->getDeviceVK || !fns_->getInterfaceVK ||
|
||||||
|
!fns_->getCommandListVK || !fns_->getResourceVK || !fns_->fsr3ContextCreate || !fns_->fsr3ContextDispatchUpscale ||
|
||||||
|
!fns_->fsr3ContextDestroy) {
|
||||||
|
LOG_WARNING("FSR3 runtime: required symbols not found in ", loadedLibraryPath_);
|
||||||
|
shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
scratchBufferSize_ = fns_->getScratchMemorySizeVK(desc.physicalDevice, FFX_FSR3_CONTEXT_COUNT);
|
||||||
|
if (scratchBufferSize_ == 0) {
|
||||||
|
LOG_WARNING("FSR3 runtime: scratch buffer size query returned 0.");
|
||||||
|
shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
scratchBuffer_ = std::malloc(scratchBufferSize_);
|
||||||
|
if (!scratchBuffer_) {
|
||||||
|
LOG_WARNING("FSR3 runtime: failed to allocate scratch buffer.");
|
||||||
|
shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDeviceContext vkDevCtx{};
|
||||||
|
vkDevCtx.vkDevice = desc.device;
|
||||||
|
vkDevCtx.vkPhysicalDevice = desc.physicalDevice;
|
||||||
|
vkDevCtx.vkDeviceProcAddr = desc.getDeviceProcAddr;
|
||||||
|
|
||||||
|
FfxDevice ffxDevice = fns_->getDeviceVK(&vkDevCtx);
|
||||||
|
FfxInterface backendShared{};
|
||||||
|
FfxErrorCode ifaceErr = fns_->getInterfaceVK(&backendShared, ffxDevice, scratchBuffer_, scratchBufferSize_, FFX_FSR3_CONTEXT_COUNT);
|
||||||
|
if (ifaceErr != FFX_OK) {
|
||||||
|
LOG_WARNING("FSR3 runtime: ffxGetInterfaceVK failed (", static_cast<int>(ifaceErr), ").");
|
||||||
|
shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FfxFsr3ContextDescription ctxDesc{};
|
||||||
|
ctxDesc.flags = FFX_FSR3_ENABLE_AUTO_EXPOSURE |
|
||||||
|
FFX_FSR3_ENABLE_MOTION_VECTORS_JITTER_CANCELLATION |
|
||||||
|
FFX_FSR3_ENABLE_UPSCALING_ONLY;
|
||||||
|
if (desc.hdrInput) ctxDesc.flags |= FFX_FSR3_ENABLE_HIGH_DYNAMIC_RANGE;
|
||||||
|
if (desc.depthInverted) ctxDesc.flags |= FFX_FSR3_ENABLE_DEPTH_INVERTED;
|
||||||
|
ctxDesc.maxRenderSize.width = desc.maxRenderWidth;
|
||||||
|
ctxDesc.maxRenderSize.height = desc.maxRenderHeight;
|
||||||
|
ctxDesc.upscaleOutputSize.width = desc.displayWidth;
|
||||||
|
ctxDesc.upscaleOutputSize.height = desc.displayHeight;
|
||||||
|
ctxDesc.displaySize.width = desc.displayWidth;
|
||||||
|
ctxDesc.displaySize.height = desc.displayHeight;
|
||||||
|
ctxDesc.backendInterfaceSharedResources = backendShared;
|
||||||
|
ctxDesc.backendInterfaceUpscaling = backendShared;
|
||||||
|
ctxDesc.backendInterfaceFrameInterpolation = backendShared;
|
||||||
|
ctxDesc.fpMessage = nullptr;
|
||||||
|
ctxDesc.backBufferFormat = mapVkFormatToFfxSurfaceFormat(desc.colorFormat, false);
|
||||||
|
|
||||||
|
contextStorage_ = std::malloc(sizeof(FfxFsr3Context));
|
||||||
|
if (!contextStorage_) {
|
||||||
|
LOG_WARNING("FSR3 runtime: failed to allocate context storage.");
|
||||||
|
shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::memset(contextStorage_, 0, sizeof(FfxFsr3Context));
|
||||||
|
|
||||||
|
FfxErrorCode createErr = fns_->fsr3ContextCreate(reinterpret_cast<FfxFsr3Context*>(contextStorage_), &ctxDesc);
|
||||||
|
if (createErr != FFX_OK) {
|
||||||
|
LOG_WARNING("FSR3 runtime: ffxFsr3ContextCreate failed (", static_cast<int>(createErr), ").");
|
||||||
|
shutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ready_ = true;
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AmdFsr3Runtime::dispatchUpscale(const AmdFsr3RuntimeDispatchDesc& desc) {
|
||||||
|
#if !WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
|
(void)desc;
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
if (!ready_ || !contextStorage_ || !fns_ || !fns_->fsr3ContextDispatchUpscale) return false;
|
||||||
|
if (!desc.commandBuffer || !desc.colorImage || !desc.depthImage || !desc.motionVectorImage || !desc.outputImage) return false;
|
||||||
|
|
||||||
|
FfxResourceDescription colorDesc = makeResourceDescription(
|
||||||
|
desc.colorFormat, desc.renderWidth, desc.renderHeight, FFX_RESOURCE_USAGE_READ_ONLY);
|
||||||
|
FfxResourceDescription depthDesc = makeResourceDescription(
|
||||||
|
desc.depthFormat, desc.renderWidth, desc.renderHeight, FFX_RESOURCE_USAGE_DEPTHTARGET, true);
|
||||||
|
FfxResourceDescription mvDesc = makeResourceDescription(
|
||||||
|
desc.motionVectorFormat, desc.renderWidth, desc.renderHeight, FFX_RESOURCE_USAGE_READ_ONLY);
|
||||||
|
FfxResourceDescription outDesc = makeResourceDescription(
|
||||||
|
desc.outputFormat, desc.outputWidth, desc.outputHeight, FFX_RESOURCE_USAGE_UAV);
|
||||||
|
|
||||||
|
FfxFsr3DispatchUpscaleDescription dispatch{};
|
||||||
|
dispatch.commandList = fns_->getCommandListVK(desc.commandBuffer);
|
||||||
|
static wchar_t kColorName[] = L"FSR3_Color";
|
||||||
|
static wchar_t kDepthName[] = L"FSR3_Depth";
|
||||||
|
static wchar_t kMotionName[] = L"FSR3_MotionVectors";
|
||||||
|
static wchar_t kOutputName[] = L"FSR3_Output";
|
||||||
|
dispatch.color = fns_->getResourceVK(reinterpret_cast<void*>(desc.colorImage), colorDesc, kColorName, FFX_RESOURCE_STATE_COMPUTE_READ);
|
||||||
|
dispatch.depth = fns_->getResourceVK(reinterpret_cast<void*>(desc.depthImage), depthDesc, kDepthName, FFX_RESOURCE_STATE_COMPUTE_READ);
|
||||||
|
dispatch.motionVectors = fns_->getResourceVK(reinterpret_cast<void*>(desc.motionVectorImage), mvDesc, kMotionName, FFX_RESOURCE_STATE_COMPUTE_READ);
|
||||||
|
dispatch.exposure = FfxResource{};
|
||||||
|
dispatch.reactive = FfxResource{};
|
||||||
|
dispatch.transparencyAndComposition = FfxResource{};
|
||||||
|
dispatch.upscaleOutput = fns_->getResourceVK(reinterpret_cast<void*>(desc.outputImage), outDesc, kOutputName, FFX_RESOURCE_STATE_UNORDERED_ACCESS);
|
||||||
|
dispatch.jitterOffset.x = desc.jitterX;
|
||||||
|
dispatch.jitterOffset.y = desc.jitterY;
|
||||||
|
dispatch.motionVectorScale.x = desc.motionScaleX;
|
||||||
|
dispatch.motionVectorScale.y = desc.motionScaleY;
|
||||||
|
dispatch.renderSize.width = desc.renderWidth;
|
||||||
|
dispatch.renderSize.height = desc.renderHeight;
|
||||||
|
dispatch.enableSharpening = false;
|
||||||
|
dispatch.sharpness = 0.0f;
|
||||||
|
dispatch.frameTimeDelta = std::max(0.001f, desc.frameTimeDeltaMs);
|
||||||
|
dispatch.preExposure = 1.0f;
|
||||||
|
dispatch.reset = desc.reset;
|
||||||
|
dispatch.cameraNear = desc.cameraNear;
|
||||||
|
dispatch.cameraFar = desc.cameraFar;
|
||||||
|
dispatch.cameraFovAngleVertical = desc.cameraFovYRadians;
|
||||||
|
dispatch.viewSpaceToMetersFactor = 1.0f;
|
||||||
|
|
||||||
|
FfxErrorCode err = fns_->fsr3ContextDispatchUpscale(
|
||||||
|
reinterpret_cast<FfxFsr3Context*>(contextStorage_), &dispatch);
|
||||||
|
return err == FFX_OK;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void AmdFsr3Runtime::shutdown() {
|
||||||
|
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
|
if (contextStorage_ && fns_ && fns_->fsr3ContextDestroy) {
|
||||||
|
fns_->fsr3ContextDestroy(reinterpret_cast<FfxFsr3Context*>(contextStorage_));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (contextStorage_) {
|
||||||
|
std::free(contextStorage_);
|
||||||
|
contextStorage_ = nullptr;
|
||||||
|
}
|
||||||
|
if (scratchBuffer_) {
|
||||||
|
std::free(scratchBuffer_);
|
||||||
|
scratchBuffer_ = nullptr;
|
||||||
|
}
|
||||||
|
scratchBufferSize_ = 0;
|
||||||
|
ready_ = false;
|
||||||
|
if (fns_) {
|
||||||
|
delete fns_;
|
||||||
|
fns_ = nullptr;
|
||||||
|
}
|
||||||
|
#if defined(_WIN32)
|
||||||
|
if (libHandle_) FreeLibrary(reinterpret_cast<HMODULE>(libHandle_));
|
||||||
|
#else
|
||||||
|
if (libHandle_) dlclose(libHandle_);
|
||||||
|
#endif
|
||||||
|
libHandle_ = nullptr;
|
||||||
|
loadedLibraryPath_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace wowee::rendering
|
||||||
|
|
@ -57,6 +57,7 @@
|
||||||
#include "rendering/vk_shader.hpp"
|
#include "rendering/vk_shader.hpp"
|
||||||
#include "rendering/vk_pipeline.hpp"
|
#include "rendering/vk_pipeline.hpp"
|
||||||
#include "rendering/vk_utils.hpp"
|
#include "rendering/vk_utils.hpp"
|
||||||
|
#include "rendering/amd_fsr3_runtime.hpp"
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_impl_vulkan.h>
|
#include <imgui_impl_vulkan.h>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
@ -71,13 +72,6 @@
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <future>
|
#include <future>
|
||||||
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
|
||||||
#if defined(_WIN32)
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
namespace rendering {
|
namespace rendering {
|
||||||
|
|
@ -116,63 +110,6 @@ static int envIntOrDefault(const char* key, int defaultValue) {
|
||||||
return static_cast<int>(n);
|
return static_cast<int>(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
|
||||||
struct AmdFsr3RuntimeApi {
|
|
||||||
void* libHandle = nullptr;
|
|
||||||
std::string loadedPath;
|
|
||||||
|
|
||||||
bool load() {
|
|
||||||
if (libHandle) return true;
|
|
||||||
|
|
||||||
std::vector<std::string> candidates;
|
|
||||||
if (const char* envPath = std::getenv("WOWEE_FFX_SDK_RUNTIME_LIB")) {
|
|
||||||
if (*envPath) candidates.emplace_back(envPath);
|
|
||||||
}
|
|
||||||
#if defined(_WIN32)
|
|
||||||
candidates.emplace_back("ffx_fsr3_vk.dll");
|
|
||||||
candidates.emplace_back("ffx_fsr3.dll");
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
candidates.emplace_back("libffx_fsr3_vk.dylib");
|
|
||||||
candidates.emplace_back("libffx_fsr3.dylib");
|
|
||||||
#else
|
|
||||||
candidates.emplace_back("libffx_fsr3_vk.so");
|
|
||||||
candidates.emplace_back("libffx_fsr3.so");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (const std::string& path : candidates) {
|
|
||||||
#if defined(_WIN32)
|
|
||||||
HMODULE h = LoadLibraryA(path.c_str());
|
|
||||||
if (!h) continue;
|
|
||||||
libHandle = reinterpret_cast<void*>(h);
|
|
||||||
#else
|
|
||||||
void* h = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
|
|
||||||
if (!h) continue;
|
|
||||||
libHandle = h;
|
|
||||||
#endif
|
|
||||||
loadedPath = path;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unload() {
|
|
||||||
if (!libHandle) return;
|
|
||||||
#if defined(_WIN32)
|
|
||||||
FreeLibrary(reinterpret_cast<HMODULE>(libHandle));
|
|
||||||
#else
|
|
||||||
dlclose(libHandle);
|
|
||||||
#endif
|
|
||||||
libHandle = nullptr;
|
|
||||||
loadedPath.clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static AmdFsr3RuntimeApi& getAmdFsr3RuntimeApi() {
|
|
||||||
static AmdFsr3RuntimeApi s_runtimeApi;
|
|
||||||
return s_runtimeApi;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static std::vector<std::string> parseEmoteCommands(const std::string& raw) {
|
static std::vector<std::string> parseEmoteCommands(const std::string& raw) {
|
||||||
std::vector<std::string> out;
|
std::vector<std::string> out;
|
||||||
std::string cur;
|
std::string cur;
|
||||||
|
|
@ -1223,8 +1160,15 @@ void Renderer::endFrame() {
|
||||||
if (fsr2_.useAmdBackend) {
|
if (fsr2_.useAmdBackend) {
|
||||||
// Compute passes: motion vectors -> temporal accumulation
|
// Compute passes: motion vectors -> temporal accumulation
|
||||||
dispatchMotionVectors();
|
dispatchMotionVectors();
|
||||||
|
if (fsr2_.amdFsr3FramegenEnabled && fsr2_.amdFsr3FramegenRuntimeReady) {
|
||||||
|
dispatchAmdFsr3Framegen();
|
||||||
|
if (!fsr2_.amdFsr3FramegenRuntimeActive) {
|
||||||
|
dispatchAmdFsr2();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
dispatchAmdFsr2();
|
dispatchAmdFsr2();
|
||||||
dispatchAmdFsr3Framegen();
|
dispatchAmdFsr3Framegen();
|
||||||
|
}
|
||||||
|
|
||||||
// Transition history output: GENERAL -> SHADER_READ_ONLY for sharpen pass
|
// Transition history output: GENERAL -> SHADER_READ_ONLY for sharpen pass
|
||||||
transitionImageLayout(currentCmd, fsr2_.history[fsr2_.currentHistory].image,
|
transitionImageLayout(currentCmd, fsr2_.history[fsr2_.currentHistory].image,
|
||||||
|
|
@ -3908,10 +3852,22 @@ bool Renderer::initFSR2Resources() {
|
||||||
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
if (fsr2_.amdFsr3FramegenEnabled) {
|
if (fsr2_.amdFsr3FramegenEnabled) {
|
||||||
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
||||||
fsr2_.amdFsr3FramegenRuntimeReady = getAmdFsr3RuntimeApi().load();
|
if (!fsr2_.amdFsr3Runtime) fsr2_.amdFsr3Runtime = std::make_unique<AmdFsr3Runtime>();
|
||||||
|
AmdFsr3RuntimeInitDesc fgInit{};
|
||||||
|
fgInit.physicalDevice = vkCtx->getPhysicalDevice();
|
||||||
|
fgInit.device = vkCtx->getDevice();
|
||||||
|
fgInit.getDeviceProcAddr = vkGetDeviceProcAddr;
|
||||||
|
fgInit.maxRenderWidth = fsr2_.internalWidth;
|
||||||
|
fgInit.maxRenderHeight = fsr2_.internalHeight;
|
||||||
|
fgInit.displayWidth = swapExtent.width;
|
||||||
|
fgInit.displayHeight = swapExtent.height;
|
||||||
|
fgInit.colorFormat = vkCtx->getSwapchainFormat();
|
||||||
|
fgInit.hdrInput = false;
|
||||||
|
fgInit.depthInverted = false;
|
||||||
|
fsr2_.amdFsr3FramegenRuntimeReady = fsr2_.amdFsr3Runtime->initialize(fgInit);
|
||||||
if (fsr2_.amdFsr3FramegenRuntimeReady) {
|
if (fsr2_.amdFsr3FramegenRuntimeReady) {
|
||||||
LOG_INFO("FSR3 framegen runtime library loaded from ", getAmdFsr3RuntimeApi().loadedPath,
|
LOG_INFO("FSR3 framegen runtime library loaded from ", fsr2_.amdFsr3Runtime->loadedLibraryPath(),
|
||||||
" (dispatch staged)");
|
" (upscale 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 library was not found. ",
|
||||||
"Set WOWEE_FFX_SDK_RUNTIME_LIB to the SDK runtime binary path.");
|
"Set WOWEE_FFX_SDK_RUNTIME_LIB to the SDK runtime binary path.");
|
||||||
|
|
@ -4218,7 +4174,10 @@ void Renderer::destroyFSR2Resources() {
|
||||||
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
||||||
fsr2_.amdFsr3FramegenRuntimeReady = false;
|
fsr2_.amdFsr3FramegenRuntimeReady = false;
|
||||||
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
getAmdFsr3RuntimeApi().unload();
|
if (fsr2_.amdFsr3Runtime) {
|
||||||
|
fsr2_.amdFsr3Runtime->shutdown();
|
||||||
|
fsr2_.amdFsr3Runtime.reset();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (fsr2_.sharpenPipeline) { vkDestroyPipeline(device, fsr2_.sharpenPipeline, nullptr); fsr2_.sharpenPipeline = VK_NULL_HANDLE; }
|
if (fsr2_.sharpenPipeline) { vkDestroyPipeline(device, fsr2_.sharpenPipeline, nullptr); fsr2_.sharpenPipeline = VK_NULL_HANDLE; }
|
||||||
|
|
@ -4444,20 +4403,47 @@ void Renderer::dispatchAmdFsr3Framegen() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
// Runtime FI/OF dispatch requires linked FidelityFX-SDK implementation binaries.
|
if (!fsr2_.amdFsr3Runtime || !fsr2_.amdFsr3FramegenRuntimeReady) {
|
||||||
// The integration hook is intentionally placed here (right after FSR2 dispatch),
|
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
||||||
// so we can enable real frame generation without refactoring the frame pipeline.
|
return;
|
||||||
if (!fsr2_.amdFsr3FramegenRuntimeReady) {
|
|
||||||
fsr2_.amdFsr3FramegenRuntimeReady = getAmdFsr3RuntimeApi().load();
|
|
||||||
}
|
|
||||||
if (!fsr2_.amdFsr3FramegenRuntimeReady) {
|
|
||||||
static bool warnedMissingRuntime = false;
|
|
||||||
if (!warnedMissingRuntime) {
|
|
||||||
warnedMissingRuntime = true;
|
|
||||||
LOG_WARNING("FSR3 framegen runtime library not found; skipping frame generation dispatch.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AmdFsr3RuntimeDispatchDesc fgDispatch{};
|
||||||
|
fgDispatch.commandBuffer = currentCmd;
|
||||||
|
fgDispatch.colorImage = fsr2_.sceneColor.image;
|
||||||
|
fgDispatch.depthImage = fsr2_.sceneDepth.image;
|
||||||
|
fgDispatch.motionVectorImage = fsr2_.motionVectors.image;
|
||||||
|
fgDispatch.outputImage = fsr2_.history[fsr2_.currentHistory].image;
|
||||||
|
fgDispatch.renderWidth = fsr2_.internalWidth;
|
||||||
|
fgDispatch.renderHeight = fsr2_.internalHeight;
|
||||||
|
fgDispatch.outputWidth = vkCtx->getSwapchainExtent().width;
|
||||||
|
fgDispatch.outputHeight = vkCtx->getSwapchainExtent().height;
|
||||||
|
fgDispatch.colorFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||||
|
fgDispatch.depthFormat = vkCtx->getDepthFormat();
|
||||||
|
fgDispatch.motionVectorFormat = VK_FORMAT_R16G16_SFLOAT;
|
||||||
|
fgDispatch.outputFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
|
||||||
|
glm::vec2 jitterNdc = camera ? camera->getJitter() : glm::vec2(0.0f);
|
||||||
|
fgDispatch.jitterX = jitterNdc.x * 0.5f * static_cast<float>(fsr2_.internalWidth);
|
||||||
|
fgDispatch.jitterY = jitterNdc.y * 0.5f * static_cast<float>(fsr2_.internalHeight);
|
||||||
|
fgDispatch.motionScaleX = static_cast<float>(fsr2_.internalWidth) * fsr2_.motionVecScaleX;
|
||||||
|
fgDispatch.motionScaleY = static_cast<float>(fsr2_.internalHeight) * fsr2_.motionVecScaleY;
|
||||||
|
fgDispatch.frameTimeDeltaMs = glm::max(0.001f, lastDeltaTime_ * 1000.0f);
|
||||||
|
fgDispatch.cameraNear = camera ? camera->getNearPlane() : 0.1f;
|
||||||
|
fgDispatch.cameraFar = camera ? camera->getFarPlane() : 1000.0f;
|
||||||
|
fgDispatch.cameraFovYRadians = camera ? glm::radians(camera->getFovDegrees()) : 1.0f;
|
||||||
|
fgDispatch.reset = fsr2_.needsHistoryReset;
|
||||||
|
|
||||||
|
bool ok = fsr2_.amdFsr3Runtime->dispatchUpscale(fgDispatch);
|
||||||
|
if (!ok) {
|
||||||
|
static bool warnedRuntimeDispatch = false;
|
||||||
|
if (!warnedRuntimeDispatch) {
|
||||||
|
warnedRuntimeDispatch = true;
|
||||||
|
LOG_WARNING("FSR3 runtime dispatch failed; falling back to FSR2 dispatch output.");
|
||||||
}
|
}
|
||||||
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fsr2_.amdFsr3FramegenRuntimeActive = true;
|
||||||
#else
|
#else
|
||||||
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -4543,18 +4529,16 @@ void Renderer::setAmdFsr3FramegenEnabled(bool enabled) {
|
||||||
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
#if WOWEE_HAS_AMD_FSR3_FRAMEGEN
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
||||||
fsr2_.amdFsr3FramegenRuntimeReady = getAmdFsr3RuntimeApi().load();
|
fsr2_.needsRecreate = true;
|
||||||
if (fsr2_.amdFsr3FramegenRuntimeReady) {
|
fsr2_.amdFsr3FramegenRuntimeReady = false;
|
||||||
LOG_INFO("FSR3 framegen runtime library loaded from ", getAmdFsr3RuntimeApi().loadedPath,
|
LOG_INFO("FSR3 framegen requested; runtime will initialize on next FSR2 resource creation.");
|
||||||
" (dispatch staged).");
|
|
||||||
} else {
|
|
||||||
LOG_WARNING("FSR3 framegen enabled, but runtime library not found. ",
|
|
||||||
"Set WOWEE_FFX_SDK_RUNTIME_LIB to the runtime binary path.");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
||||||
fsr2_.amdFsr3FramegenRuntimeReady = false;
|
fsr2_.amdFsr3FramegenRuntimeReady = false;
|
||||||
getAmdFsr3RuntimeApi().unload();
|
if (fsr2_.amdFsr3Runtime) {
|
||||||
|
fsr2_.amdFsr3Runtime->shutdown();
|
||||||
|
fsr2_.amdFsr3Runtime.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
fsr2_.amdFsr3FramegenRuntimeActive = false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue