From b132ce5a2a7ec76c64e463f580fa267d727b2acf Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 11 Mar 2025 23:05:39 +0100 Subject: [PATCH] Added Vulkan implementation based on the upgradable FFX API. Works, but FFX doesn't like it when I link to both DX12 and Vulkan libraries, always picking the first over the other. Means Vulkan will break if DX12 is the first library in the linker list, and vice versa. --- FSR3UnityPlugin.cpp | 173 +++++++++++++++++++++++++++++++++++--------- FSR3UnityTypes.h | 25 +++++-- 2 files changed, 155 insertions(+), 43 deletions(-) diff --git a/FSR3UnityPlugin.cpp b/FSR3UnityPlugin.cpp index 5c10dfd..1a71137 100644 --- a/FSR3UnityPlugin.cpp +++ b/FSR3UnityPlugin.cpp @@ -26,6 +26,7 @@ static IUnityInterfaces* s_UnityInterfaces = nullptr; static IUnityLog* s_Log = nullptr; static IUnityGraphics* s_Graphics = nullptr; static IUnityGraphicsD3D12v7* s_GraphicsD3D12 = nullptr; +static IUnityGraphicsVulkan* s_GraphicsVulkan = nullptr; static UnityGfxRenderer s_RendererType = kUnityGfxRendererNull; static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType eventType); @@ -47,7 +48,8 @@ static std::vector s_Features; static std::queue s_FeatureSlots; static std::mutex s_FeatureMutex; -static ffx::CreateBackendDX12Desc s_BackendDesc{}; +static ffx::CreateBackendDX12Desc s_DX12BackendDesc{}; +static ffx::CreateBackendVKDesc s_VulkanBackendDesc{}; // Unity plugin load event extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API UnityPluginLoad(IUnityInterfaces* unityInterfaces) @@ -76,20 +78,35 @@ static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType ev case kUnityGfxDeviceEventInitialize: { s_RendererType = s_Graphics->GetRenderer(); - if (s_RendererType != kUnityGfxRendererD3D12) - return; - - s_GraphicsD3D12 = s_UnityInterfaces->Get(); - if (s_GraphicsD3D12 == nullptr) + switch (s_RendererType) { - UNITY_LOG_ERROR(s_Log, "Could not obtain D3D12 Graphics interface!"); - return; + case kUnityGfxRendererD3D12: + { + s_GraphicsD3D12 = s_UnityInterfaces->Get(); + if (s_GraphicsD3D12 == nullptr) + { + UNITY_LOG_ERROR(s_Log, "Could not obtain D3D12 Graphics interface!"); + return; + } + break; + } + case kUnityGfxRendererVulkan: + { + s_GraphicsVulkan = s_UnityInterfaces->Get(); + if (s_GraphicsVulkan == nullptr) + { + UNITY_LOG_ERROR(s_Log, "Could not obtain Vulkan Graphics interface!"); + return; + } + break; + } } break; } case kUnityGfxDeviceEventShutdown: { + s_GraphicsVulkan = nullptr; s_GraphicsD3D12 = nullptr; s_RendererType = kUnityGfxRendererNull; break; @@ -133,17 +150,36 @@ static void FreeFeatureSlot(uint32_t featureSlot) memset(&s_Features[featureSlot], 0, sizeof(FSR3Feature)); } +static PFN_vkVoidFunction GetVulkanDeviceProcAddr(VkDevice device, const char* pName) +{ + UnityVulkanInstance instance = s_GraphicsVulkan->Instance(); + return instance.getInstanceProcAddr(instance.instance, pName); +} + extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_InitApi() { - if (s_GraphicsD3D12 == nullptr) - return false; + if (s_GraphicsD3D12 != nullptr) + { + ID3D12Device* device = s_GraphicsD3D12->GetDevice(); + if (device == nullptr) + return false; - ID3D12Device* device = s_GraphicsD3D12->GetDevice(); - if (device == nullptr) - return false; + s_DX12BackendDesc.device = device; + return true; + } + else if (s_GraphicsVulkan != nullptr) + { + UnityVulkanInstance instance = s_GraphicsVulkan->Instance(); + if (instance.device == nullptr) + return false; + + s_VulkanBackendDesc.vkDevice = instance.device; + s_VulkanBackendDesc.vkPhysicalDevice = instance.physicalDevice; + s_VulkanBackendDesc.vkDeviceProcAddr = &GetVulkanDeviceProcAddr; + return true; + } - s_BackendDesc.device = device; - return true; + return false; } extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_ShutdownApi() @@ -158,7 +194,11 @@ extern "C" void UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_ShutdownApi() } } - s_BackendDesc.device = nullptr; + s_VulkanBackendDesc.vkDevice = nullptr; + s_VulkanBackendDesc.vkPhysicalDevice = nullptr; + s_VulkanBackendDesc.vkDeviceProcAddr = nullptr; + + s_DX12BackendDesc.device = nullptr; } extern "C" uint32_t UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_GetDeviceVersion() @@ -214,12 +254,28 @@ extern "C" int32_t UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_GetBaseEvent return BaseEventId; } +static FfxApiResource GetVulkanTextureResource(UnityVulkanInstance& instance, FSR3TextureDesc& texture, FfxApiResourceState state = FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ) +{ + FfxApiResourceDescription desc{}; + + if (texture.image != 0) + { + desc.type = FFX_API_RESOURCE_TYPE_TEXTURE2D; + desc.format = ffxApiGetSurfaceFormatVK((VkFormat)texture.format); + desc.width = texture.width; + desc.height = texture.height; + desc.depth = 1; + desc.mipCount = 1; + desc.flags = FFX_API_RESOURCE_FLAGS_NONE; + desc.usage = state == FFX_API_RESOURCE_STATE_UNORDERED_ACCESS ? FFX_API_RESOURCE_USAGE_UAV : FFX_API_RESOURCE_USAGE_READ_ONLY; + } + + return ffxApiGetResourceVK((void*)texture.image, desc, state); +} + // Plugin function to handle a specific rendering event static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data) { - if (s_GraphicsD3D12 == nullptr || s_BackendDesc.device == nullptr) - return; - // User rendering code switch (eventID) { @@ -245,19 +301,37 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data) auto& feature = s_Features[params->featureSlot]; - UnityGraphicsD3D12RecordingState state; - s_GraphicsD3D12->CommandRecordingState(&state); - ffx::DispatchDescUpscale dispatchUpscale{}; - dispatchUpscale.commandList = state.commandList; - dispatchUpscale.color = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.colorInput, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.depth = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.depth, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.motionVectors = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.motionVectors, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.exposure = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.exposureTexture, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.reactive = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.reactiveMask, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.transparencyAndComposition = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.transparencyMask, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); - dispatchUpscale.output = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.colorOutput, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS); + if (s_GraphicsD3D12 != nullptr) + { + UnityGraphicsD3D12RecordingState state; + s_GraphicsD3D12->CommandRecordingState(&state); + dispatchUpscale.commandList = state.commandList; + + dispatchUpscale.color = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.colorInput.image, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); + dispatchUpscale.depth = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.depth.image, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); + dispatchUpscale.motionVectors = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.motionVectors.image, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); + dispatchUpscale.exposure = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.exposureTexture.image, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); + dispatchUpscale.reactive = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.reactiveMask.image, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); + dispatchUpscale.transparencyAndComposition = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.transparencyMask.image, FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ); + dispatchUpscale.output = ffxApiGetResourceDX12((ID3D12Resource*)feature.textureTable.colorOutput.image, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS); + } + else if (s_GraphicsVulkan != nullptr) + { + UnityVulkanRecordingState state; + s_GraphicsVulkan->CommandRecordingState(&state, kUnityVulkanGraphicsQueueAccess_DontCare); + dispatchUpscale.commandList = state.commandBuffer; + + UnityVulkanInstance instance = s_GraphicsVulkan->Instance(); + dispatchUpscale.color = GetVulkanTextureResource(instance, feature.textureTable.colorInput); + dispatchUpscale.depth = GetVulkanTextureResource(instance, feature.textureTable.depth); + dispatchUpscale.motionVectors = GetVulkanTextureResource(instance, feature.textureTable.motionVectors); + dispatchUpscale.exposure = GetVulkanTextureResource(instance, feature.textureTable.exposureTexture); + dispatchUpscale.reactive = GetVulkanTextureResource(instance, feature.textureTable.reactiveMask); + dispatchUpscale.transparencyAndComposition = GetVulkanTextureResource(instance, feature.textureTable.transparencyMask); + dispatchUpscale.output = GetVulkanTextureResource(instance, feature.textureTable.colorOutput, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS); + } dispatchUpscale.jitterOffset.x = params->jitterOffsetX; dispatchUpscale.jitterOffset.y = params->jitterOffsetY; @@ -309,7 +383,14 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data) createUpscaling.maxRenderSize = { params->maxRenderSizeWidth, params->maxRenderSizeHeight }; createUpscaling.flags = params->flags; - ffx::CreateContext(feature.upscalingContext, nullptr, createUpscaling, s_BackendDesc); + if (s_GraphicsD3D12 != nullptr) + { + ffx::CreateContext(feature.upscalingContext, nullptr, createUpscaling, s_DX12BackendDesc); + } + else if (s_GraphicsVulkan != nullptr) + { + ffx::CreateContext(feature.upscalingContext, nullptr, createUpscaling, s_VulkanBackendDesc); + } break; } } @@ -317,9 +398,6 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data) static void UNITY_INTERFACE_API OnSetTextureEvent(int eventID, void* data) { - if (s_GraphicsD3D12 == nullptr) - return; - auto* params = (UnityRenderingExtTextureUpdateParamsV2*)data; // userData = (featureId & (int) ushort.MaxValue) << 16 | (textureSlot & (int) short.MaxValue) << 1 | (clearTextureTable ? 1 : 0); @@ -347,7 +425,32 @@ static void UNITY_INTERFACE_API OnSetTextureEvent(int eventID, void* data) { // We organized the texture table struct to be ordered the same as the texture slot enum // This way we can use the texture slot value simply as a pointer offset into the texture table - *(((intptr_t*)&feature.textureTable) + textureSlot) = (intptr_t)s_GraphicsD3D12->TextureFromNativeTexture((UnityTextureID)params->textureID); + FSR3TextureDesc* textureDesc = ((FSR3TextureDesc*)&feature.textureTable) + textureSlot; + + if (s_GraphicsD3D12 != nullptr) + { + textureDesc->image = (intptr_t)s_GraphicsD3D12->TextureFromNativeTexture((UnityTextureID)params->textureID); + } + else if (s_GraphicsVulkan != nullptr) + { + VkAccessFlags accessFlags = (textureSlot == FSR3Textures::tColorOutput) ? VK_ACCESS_SHADER_WRITE_BIT : VK_ACCESS_SHADER_READ_BIT; + + UnityVulkanImage image; + if (s_GraphicsVulkan->AccessTextureByID((UnityTextureID)params->textureID, UnityVulkanWholeImage, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, accessFlags, + kUnityVulkanResourceAccess_PipelineBarrier, &image)) + { + textureDesc->image = (intptr_t)image.image; + textureDesc->width = params->width; + textureDesc->height = params->height; + textureDesc->format = (uint32_t)image.format; + } + else + { + textureDesc->image = 0; + } + } + break; } } diff --git a/FSR3UnityTypes.h b/FSR3UnityTypes.h index 6c62e12..b72993b 100644 --- a/FSR3UnityTypes.h +++ b/FSR3UnityTypes.h @@ -59,16 +59,25 @@ struct FSR3CommandExecutionData uint32_t featureSlot; }; +struct FSR3TextureDesc +{ + intptr_t image; + + uint32_t width; + uint32_t height; + uint32_t format; +}; + struct FSR3TextureTable { - intptr_t colorInput; - intptr_t colorOutput; - intptr_t depth; - intptr_t motionVectors; - intptr_t transparencyMask; - intptr_t exposureTexture; - intptr_t reactiveMask; - intptr_t biasColorMask; + FSR3TextureDesc colorInput; + FSR3TextureDesc colorOutput; + FSR3TextureDesc depth; + FSR3TextureDesc motionVectors; + FSR3TextureDesc transparencyMask; + FSR3TextureDesc exposureTexture; + FSR3TextureDesc reactiveMask; + FSR3TextureDesc biasColorMask; uint32_t featureSlot; };