From 61cb2df40038dd21a3f048b44278d00f6da2bc6e Mon Sep 17 00:00:00 2001 From: Kelsi Date: Mon, 9 Mar 2026 01:31:01 -0700 Subject: [PATCH] Add ABI v2 external-handle plumbing for FSR3 bridge dispatch --- README.md | 1 + docs/AMD_FSR2_INTEGRATION.md | 1 + include/rendering/amd_fsr3_runtime.hpp | 8 +++ include/rendering/amd_fsr3_wrapper_abi.h | 20 +++++- src/rendering/amd_fsr3_runtime.cpp | 16 +++++ src/rendering/amd_fsr3_wrapper_impl.cpp | 25 +++++++ src/rendering/renderer.cpp | 88 ++++++++++++++++++++++++ 7 files changed, 158 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6ffc53e1..7d920d2c 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ make -j$(nproc) - Set to `0` to skip adapter/device preflight. - Bridge preflight also checks Vulkan Win32 interop funcs/extensions before enabling DX12 path. - Path B wrapper libraries must export the clean wrapper ABI (`include/rendering/amd_fsr3_wrapper_abi.h`): + - ABI version is currently `2` (dispatch includes external-memory/semaphore handle fields for bridge interop plumbing). - `wowee_fsr3_wrapper_get_abi_version` - `wowee_fsr3_wrapper_initialize` - `wowee_fsr3_wrapper_dispatch_upscale` diff --git a/docs/AMD_FSR2_INTEGRATION.md b/docs/AMD_FSR2_INTEGRATION.md index f192c45d..a2e81ca6 100644 --- a/docs/AMD_FSR2_INTEGRATION.md +++ b/docs/AMD_FSR2_INTEGRATION.md @@ -67,6 +67,7 @@ Runtime note: - required device extensions: `VK_KHR_external_memory`, `VK_KHR_external_memory_win32`, `VK_KHR_external_semaphore`, `VK_KHR_external_semaphore_win32` - Path B wrapper ABI contract is declared in: - `include/rendering/amd_fsr3_wrapper_abi.h` +- Current wrapper ABI version: `2` (adds external memory/semaphore handle fields in dispatch payload for bridge interop). - Required wrapper exports: - `wowee_fsr3_wrapper_get_abi_version` - `wowee_fsr3_wrapper_initialize` diff --git a/include/rendering/amd_fsr3_runtime.hpp b/include/rendering/amd_fsr3_runtime.hpp index e4e8bc5c..b2b1b678 100644 --- a/include/rendering/amd_fsr3_runtime.hpp +++ b/include/rendering/amd_fsr3_runtime.hpp @@ -44,6 +44,14 @@ struct AmdFsr3RuntimeDispatchDesc { float cameraFar = 1000.0f; float cameraFovYRadians = 1.0f; bool reset = false; + uint32_t externalFlags = 0; + uint64_t colorMemoryHandle = 0; + uint64_t depthMemoryHandle = 0; + uint64_t motionVectorMemoryHandle = 0; + uint64_t outputMemoryHandle = 0; + uint64_t frameGenOutputMemoryHandle = 0; + uint64_t acquireSemaphoreHandle = 0; + uint64_t releaseSemaphoreHandle = 0; }; class AmdFsr3Runtime { diff --git a/include/rendering/amd_fsr3_wrapper_abi.h b/include/rendering/amd_fsr3_wrapper_abi.h index 6968b46a..2eb0366c 100644 --- a/include/rendering/amd_fsr3_wrapper_abi.h +++ b/include/rendering/amd_fsr3_wrapper_abi.h @@ -7,7 +7,7 @@ extern "C" { #endif -#define WOWEE_FSR3_WRAPPER_ABI_VERSION 1u +#define WOWEE_FSR3_WRAPPER_ABI_VERSION 2u typedef void* WoweeFsr3WrapperContext; @@ -50,6 +50,14 @@ typedef struct WoweeFsr3WrapperDispatchDesc { float cameraFar; float cameraFovYRadians; uint32_t reset; + uint32_t externalFlags; + uint64_t colorMemoryHandle; + uint64_t depthMemoryHandle; + uint64_t motionVectorMemoryHandle; + uint64_t outputMemoryHandle; + uint64_t frameGenOutputMemoryHandle; + uint64_t acquireSemaphoreHandle; + uint64_t releaseSemaphoreHandle; } WoweeFsr3WrapperDispatchDesc; enum { @@ -58,6 +66,16 @@ enum { WOWEE_FSR3_WRAPPER_ENABLE_FRAME_GENERATION = 1u << 2 }; +enum { + WOWEE_FSR3_WRAPPER_EXTERNAL_COLOR_MEMORY = 1u << 0, + WOWEE_FSR3_WRAPPER_EXTERNAL_DEPTH_MEMORY = 1u << 1, + WOWEE_FSR3_WRAPPER_EXTERNAL_MOTION_MEMORY = 1u << 2, + WOWEE_FSR3_WRAPPER_EXTERNAL_OUTPUT_MEMORY = 1u << 3, + WOWEE_FSR3_WRAPPER_EXTERNAL_FRAMEGEN_OUTPUT_MEMORY = 1u << 4, + WOWEE_FSR3_WRAPPER_EXTERNAL_ACQUIRE_SEMAPHORE = 1u << 5, + WOWEE_FSR3_WRAPPER_EXTERNAL_RELEASE_SEMAPHORE = 1u << 6 +}; + uint32_t wowee_fsr3_wrapper_get_abi_version(void); const char* wowee_fsr3_wrapper_get_name(void); int32_t wowee_fsr3_wrapper_initialize(const WoweeFsr3WrapperInitDesc* initDesc, diff --git a/src/rendering/amd_fsr3_runtime.cpp b/src/rendering/amd_fsr3_runtime.cpp index dbcf04ad..42b93e1c 100644 --- a/src/rendering/amd_fsr3_runtime.cpp +++ b/src/rendering/amd_fsr3_runtime.cpp @@ -462,6 +462,14 @@ bool AmdFsr3Runtime::dispatchUpscale(const AmdFsr3RuntimeDispatchDesc& desc) { wrapperDesc.cameraFar = desc.cameraFar; wrapperDesc.cameraFovYRadians = desc.cameraFovYRadians; wrapperDesc.reset = desc.reset ? 1u : 0u; + wrapperDesc.externalFlags = desc.externalFlags; + wrapperDesc.colorMemoryHandle = desc.colorMemoryHandle; + wrapperDesc.depthMemoryHandle = desc.depthMemoryHandle; + wrapperDesc.motionVectorMemoryHandle = desc.motionVectorMemoryHandle; + wrapperDesc.outputMemoryHandle = desc.outputMemoryHandle; + wrapperDesc.frameGenOutputMemoryHandle = desc.frameGenOutputMemoryHandle; + wrapperDesc.acquireSemaphoreHandle = desc.acquireSemaphoreHandle; + wrapperDesc.releaseSemaphoreHandle = desc.releaseSemaphoreHandle; const bool ok = fns_->wrapperDispatchUpscale(static_cast(wrapperContext_), &wrapperDesc) == 0; if (!ok) { lastError_ = "wrapper upscale dispatch failed"; @@ -570,6 +578,14 @@ bool AmdFsr3Runtime::dispatchFrameGeneration(const AmdFsr3RuntimeDispatchDesc& d wrapperDesc.cameraFar = desc.cameraFar; wrapperDesc.cameraFovYRadians = desc.cameraFovYRadians; wrapperDesc.reset = desc.reset ? 1u : 0u; + wrapperDesc.externalFlags = desc.externalFlags; + wrapperDesc.colorMemoryHandle = desc.colorMemoryHandle; + wrapperDesc.depthMemoryHandle = desc.depthMemoryHandle; + wrapperDesc.motionVectorMemoryHandle = desc.motionVectorMemoryHandle; + wrapperDesc.outputMemoryHandle = desc.outputMemoryHandle; + wrapperDesc.frameGenOutputMemoryHandle = desc.frameGenOutputMemoryHandle; + wrapperDesc.acquireSemaphoreHandle = desc.acquireSemaphoreHandle; + wrapperDesc.releaseSemaphoreHandle = desc.releaseSemaphoreHandle; const bool ok = fns_->wrapperDispatchFramegen(static_cast(wrapperContext_), &wrapperDesc) == 0; if (!ok) { lastError_ = "wrapper frame generation dispatch failed"; diff --git a/src/rendering/amd_fsr3_wrapper_impl.cpp b/src/rendering/amd_fsr3_wrapper_impl.cpp index 85ca9a65..2cb82911 100644 --- a/src/rendering/amd_fsr3_wrapper_impl.cpp +++ b/src/rendering/amd_fsr3_wrapper_impl.cpp @@ -657,6 +657,20 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_dispatch_upscale(WoweeFsr3W if (!dispatchDesc->commandBuffer || !dispatchDesc->colorImage || !dispatchDesc->depthImage || !dispatchDesc->motionVectorImage || !dispatchDesc->outputImage) return -1; WrapperContext* ctx = reinterpret_cast(context); +#if defined(_WIN32) + if (ctx->backend == WrapperBackend::Dx12Bridge) { + const uint32_t requiredMask = + WOWEE_FSR3_WRAPPER_EXTERNAL_COLOR_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_DEPTH_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_MOTION_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_OUTPUT_MEMORY; + if ((dispatchDesc->externalFlags & requiredMask) != requiredMask || + dispatchDesc->colorMemoryHandle == 0 || dispatchDesc->depthMemoryHandle == 0 || + dispatchDesc->motionVectorMemoryHandle == 0 || dispatchDesc->outputMemoryHandle == 0) { + return -1; + } + } +#endif FfxResourceDescription colorDesc = makeResourceDescription( dispatchDesc->colorFormat, dispatchDesc->renderWidth, dispatchDesc->renderHeight, FFX_RESOURCE_USAGE_READ_ONLY); @@ -712,6 +726,17 @@ WOWEE_FSR3_WRAPPER_EXPORT int32_t wowee_fsr3_wrapper_dispatch_framegen(WoweeFsr3 if (!dispatchDesc->commandBuffer || !dispatchDesc->outputImage || !dispatchDesc->frameGenOutputImage) return -1; WrapperContext* ctx = reinterpret_cast(context); if (!ctx->frameGenerationReady || !ctx->fns.fsr3DispatchFrameGeneration) return -1; +#if defined(_WIN32) + if (ctx->backend == WrapperBackend::Dx12Bridge) { + const uint32_t requiredMask = + WOWEE_FSR3_WRAPPER_EXTERNAL_OUTPUT_MEMORY | + WOWEE_FSR3_WRAPPER_EXTERNAL_FRAMEGEN_OUTPUT_MEMORY; + if ((dispatchDesc->externalFlags & requiredMask) != requiredMask || + dispatchDesc->outputMemoryHandle == 0 || dispatchDesc->frameGenOutputMemoryHandle == 0) { + return -1; + } + } +#endif FfxResourceDescription presentDesc = makeResourceDescription( dispatchDesc->outputFormat, dispatchDesc->outputWidth, dispatchDesc->outputHeight, FFX_RESOURCE_USAGE_READ_ONLY); diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 598d067b..c1ff9bfc 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -72,6 +72,9 @@ #include #include #include +#if defined(_WIN32) +#include +#endif namespace wowee { namespace rendering { @@ -110,6 +113,29 @@ static int envIntOrDefault(const char* key, int defaultValue) { return static_cast(n); } +#if defined(_WIN32) +static uint64_t exportImageMemoryHandleWin32(VkDevice device, PFN_vkGetDeviceProcAddr getDeviceProcAddr, + VmaAllocator allocator, const AllocatedImage& image) { + if (!device || !getDeviceProcAddr || !allocator || !image.allocation) return 0; + auto getMemHandle = reinterpret_cast( + getDeviceProcAddr(device, "vkGetMemoryWin32HandleKHR")); + if (!getMemHandle) return 0; + + VmaAllocationInfo allocInfo{}; + vmaGetAllocationInfo(allocator, image.allocation, &allocInfo); + if (allocInfo.deviceMemory == VK_NULL_HANDLE) return 0; + + VkMemoryGetWin32HandleInfoKHR handleInfo{}; + handleInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR; + handleInfo.memory = allocInfo.deviceMemory; + handleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT; + + HANDLE outHandle = nullptr; + if (getMemHandle(device, &handleInfo, &outHandle) != VK_SUCCESS || !outHandle) return 0; + return reinterpret_cast(outHandle); +} +#endif + static std::vector parseEmoteCommands(const std::string& raw) { std::vector out; std::string cur; @@ -4488,6 +4514,53 @@ void Renderer::dispatchAmdFsr3Framegen() { fgDispatch.cameraFovYRadians = camera ? glm::radians(camera->getFovDegrees()) : 1.0f; fgDispatch.reset = fsr2_.needsHistoryReset; +#if defined(_WIN32) + std::vector exportedHandles; + auto trackHandle = [&](uint64_t h) { + if (!h) return; + HANDLE raw = reinterpret_cast(h); + exportedHandles.push_back(raw); + }; + auto cleanupExportedHandles = [&]() { + for (HANDLE h : exportedHandles) { + if (h) CloseHandle(h); + } + exportedHandles.clear(); + }; + + fgDispatch.externalFlags = 0; + fgDispatch.colorMemoryHandle = exportImageMemoryHandleWin32( + device, vkGetDeviceProcAddr, alloc, fsr2_.sceneColor); + if (fgDispatch.colorMemoryHandle) { + fgDispatch.externalFlags |= WOWEE_FSR3_WRAPPER_EXTERNAL_COLOR_MEMORY; + trackHandle(fgDispatch.colorMemoryHandle); + } + fgDispatch.depthMemoryHandle = exportImageMemoryHandleWin32( + device, vkGetDeviceProcAddr, alloc, fsr2_.sceneDepth); + if (fgDispatch.depthMemoryHandle) { + fgDispatch.externalFlags |= WOWEE_FSR3_WRAPPER_EXTERNAL_DEPTH_MEMORY; + trackHandle(fgDispatch.depthMemoryHandle); + } + fgDispatch.motionVectorMemoryHandle = exportImageMemoryHandleWin32( + device, vkGetDeviceProcAddr, alloc, fsr2_.motionVectors); + if (fgDispatch.motionVectorMemoryHandle) { + fgDispatch.externalFlags |= WOWEE_FSR3_WRAPPER_EXTERNAL_MOTION_MEMORY; + trackHandle(fgDispatch.motionVectorMemoryHandle); + } + fgDispatch.outputMemoryHandle = exportImageMemoryHandleWin32( + device, vkGetDeviceProcAddr, alloc, fsr2_.history[fsr2_.currentHistory]); + if (fgDispatch.outputMemoryHandle) { + fgDispatch.externalFlags |= WOWEE_FSR3_WRAPPER_EXTERNAL_OUTPUT_MEMORY; + trackHandle(fgDispatch.outputMemoryHandle); + } + fgDispatch.frameGenOutputMemoryHandle = exportImageMemoryHandleWin32( + device, vkGetDeviceProcAddr, alloc, fsr2_.framegenOutput); + if (fgDispatch.frameGenOutputMemoryHandle) { + fgDispatch.externalFlags |= WOWEE_FSR3_WRAPPER_EXTERNAL_FRAMEGEN_OUTPUT_MEMORY; + trackHandle(fgDispatch.frameGenOutputMemoryHandle); + } +#endif + if (!fsr2_.amdFsr3Runtime->dispatchUpscale(fgDispatch)) { static bool warnedRuntimeDispatch = false; if (!warnedRuntimeDispatch) { @@ -4497,6 +4570,9 @@ void Renderer::dispatchAmdFsr3Framegen() { fsr2_.amdFsr3RuntimeLastError = fsr2_.amdFsr3Runtime->lastError(); fsr2_.amdFsr3FallbackCount++; fsr2_.amdFsr3FramegenRuntimeActive = false; +#if defined(_WIN32) + cleanupExportedHandles(); +#endif return; } fsr2_.amdFsr3RuntimeLastError.clear(); @@ -4504,10 +4580,16 @@ void Renderer::dispatchAmdFsr3Framegen() { if (!fsr2_.amdFsr3FramegenEnabled) { fsr2_.amdFsr3FramegenRuntimeActive = false; +#if defined(_WIN32) + cleanupExportedHandles(); +#endif return; } if (!fsr2_.amdFsr3Runtime->isFrameGenerationReady()) { fsr2_.amdFsr3FramegenRuntimeActive = false; +#if defined(_WIN32) + cleanupExportedHandles(); +#endif return; } if (!fsr2_.amdFsr3Runtime->dispatchFrameGeneration(fgDispatch)) { @@ -4519,12 +4601,18 @@ void Renderer::dispatchAmdFsr3Framegen() { fsr2_.amdFsr3RuntimeLastError = fsr2_.amdFsr3Runtime->lastError(); fsr2_.amdFsr3FallbackCount++; fsr2_.amdFsr3FramegenRuntimeActive = false; +#if defined(_WIN32) + cleanupExportedHandles(); +#endif return; } fsr2_.amdFsr3RuntimeLastError.clear(); fsr2_.amdFsr3FramegenDispatchCount++; fsr2_.framegenOutputValid = true; fsr2_.amdFsr3FramegenRuntimeActive = true; +#if defined(_WIN32) + cleanupExportedHandles(); +#endif #else fsr2_.amdFsr3FramegenRuntimeActive = false; #endif