From 112beaa59299c29a78b2463a5bc492479bf7094b Mon Sep 17 00:00:00 2001 From: Nico de Poel Date: Tue, 11 Mar 2025 21:17:43 +0100 Subject: [PATCH] Implemented FSR2 for Vulkan, along with some initial bits for DX11 --- FSR2UnityPlugin.cpp | 166 ++++++++++++++++++++++++++++++++++++++++---- FSR2UnityTypes.h | 26 ++++--- 2 files changed, 171 insertions(+), 21 deletions(-) diff --git a/FSR2UnityPlugin.cpp b/FSR2UnityPlugin.cpp index d6eee69..4749d18 100644 --- a/FSR2UnityPlugin.cpp +++ b/FSR2UnityPlugin.cpp @@ -28,7 +28,9 @@ static const int32_t BaseEventId = 0; static IUnityInterfaces* s_UnityInterfaces = nullptr; static IUnityLog* s_Log = nullptr; static IUnityGraphics* s_Graphics = nullptr; +static IUnityGraphicsD3D11* s_GraphicsD3D11 = nullptr; static IUnityGraphicsD3D12v7* s_GraphicsD3D12 = nullptr; +static IUnityGraphicsVulkanV2* s_GraphicsVulkan = nullptr; static UnityGfxRenderer s_RendererType = kUnityGfxRendererNull; static FfxDevice s_Device = nullptr; @@ -80,6 +82,16 @@ static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType ev s_RendererType = s_Graphics->GetRenderer(); switch (s_RendererType) { + case kUnityGfxRendererD3D11: + { + s_GraphicsD3D11 = s_UnityInterfaces->Get(); + if (s_GraphicsD3D11 == nullptr) + { + UNITY_LOG_ERROR(s_Log, "Could not obtain D3D11 Graphics interface!"); + return; + } + break; + } case kUnityGfxRendererD3D12: { s_GraphicsD3D12 = s_UnityInterfaces->Get(); @@ -90,13 +102,25 @@ static void UNITY_INTERFACE_API OnGraphicsDeviceEvent(UnityGfxDeviceEventType ev } 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_GraphicsD3D11 = nullptr; s_RendererType = kUnityGfxRendererNull; break; } @@ -139,9 +163,28 @@ static void FreeFeatureSlot(uint32_t featureSlot) memset(&s_Features[featureSlot], 0, sizeof(FSR2Feature)); } +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) + if (s_GraphicsD3D11 != nullptr) + { + //ID3D11Device* device = s_GraphicsD3D11->GetDevice(); + //if (device == nullptr) + // return false; + + //s_Device = ffxGetDeviceDX11(device); + + //size_t scratchBufferSize = ffxFsr2GetScratchMemorySizeDX11(); + //void* scratchBuffer = malloc(scratchBufferSize); + //ffxFsr2GetInterfaceDX11(&s_Fsr2Interface, device, scratchBuffer, scratchBufferSize); + //return true; + } + else if (s_GraphicsD3D12 != nullptr) { ID3D12Device* device = s_GraphicsD3D12->GetDevice(); if (device == nullptr) @@ -154,6 +197,19 @@ extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_InitApi() ffxFsr2GetInterfaceDX12(&s_Fsr2Interface, device, scratchBuffer, scratchBufferSize); return true; } + else if (s_GraphicsVulkan != nullptr) + { + UnityVulkanInstance instance = s_GraphicsVulkan->Instance(); + if (instance.device == nullptr) + return false; + + s_Device = ffxGetDeviceVK(instance.device); + + size_t scratchBufferSize = ffxFsr2GetScratchMemorySizeVK(instance.physicalDevice); + void* scratchBuffer = malloc(scratchBufferSize); + ffxFsr2GetInterfaceVK(&s_Fsr2Interface, scratchBuffer, scratchBufferSize, instance.physicalDevice, &GetVulkanDeviceProcAddr); + return true; + } return false; } @@ -232,6 +288,37 @@ extern "C" int32_t UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API AMDUP_GetBaseEvent return BaseEventId; } +static FfxResource GetVulkanTextureResource(UnityVulkanInstance& instance, FSR2Feature& feature, FSR2TextureDesc& texture, FfxResourceStates state = FFX_RESOURCE_STATE_COMPUTE_READ) +{ + VkImageView imageView = nullptr; + + if (texture.image != 0) + { + VkImageViewCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + createInfo.image = (VkImage)texture.image; + createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + createInfo.format = (VkFormat)texture.format; + createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + createInfo.subresourceRange.levelCount = 1; + createInfo.subresourceRange.layerCount = 1; + + vkCreateImageView(instance.device, &createInfo, nullptr, &imageView); + } + + texture.view = (intptr_t)imageView; + return ffxGetTextureResourceVK(&feature.upscalingContext, (VkImage)texture.image, imageView, texture.width, texture.height, (VkFormat)texture.format, nullptr, state); +} + +static void DestroyVulkanImageView(UnityVulkanInstance& instance, FSR2TextureDesc& texture) +{ + if (texture.view == 0) + return; + + vkDestroyImageView(instance.device, (VkImageView)texture.view, nullptr); + texture.view = 0; +} + // Plugin function to handle a specific rendering event static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data) { @@ -271,13 +358,28 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data) s_GraphicsD3D12->CommandRecordingState(&state); dispatchDescription.commandList = ffxGetCommandListDX12(state.commandList); - dispatchDescription.color = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.colorInput); - dispatchDescription.depth = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.depth); - dispatchDescription.motionVectors = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.motionVectors); - dispatchDescription.exposure = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.exposureTexture); - dispatchDescription.reactive = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.reactiveMask); - dispatchDescription.transparencyAndComposition = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.transparencyMask); - dispatchDescription.output = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.colorOutput, nullptr, FFX_RESOURCE_STATE_UNORDERED_ACCESS); + dispatchDescription.color = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.colorInput.image); + dispatchDescription.depth = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.depth.image); + dispatchDescription.motionVectors = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.motionVectors.image); + dispatchDescription.exposure = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.exposureTexture.image); + dispatchDescription.reactive = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.reactiveMask.image); + dispatchDescription.transparencyAndComposition = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.transparencyMask.image); + dispatchDescription.output = ffxGetResourceDX12(&feature.upscalingContext, (ID3D12Resource*)feature.textureTable.colorOutput.image, nullptr, FFX_RESOURCE_STATE_UNORDERED_ACCESS); + } + else if (s_GraphicsVulkan != nullptr) + { + UnityVulkanRecordingState state; + s_GraphicsVulkan->CommandRecordingState(&state, kUnityVulkanGraphicsQueueAccess_DontCare); + dispatchDescription.commandList = ffxGetCommandListVK(state.commandBuffer); + + UnityVulkanInstance instance = s_GraphicsVulkan->Instance(); + dispatchDescription.color = GetVulkanTextureResource(instance, feature, feature.textureTable.colorInput); + dispatchDescription.depth = GetVulkanTextureResource(instance, feature, feature.textureTable.depth); + dispatchDescription.motionVectors = GetVulkanTextureResource(instance, feature, feature.textureTable.motionVectors); + dispatchDescription.exposure = GetVulkanTextureResource(instance, feature, feature.textureTable.exposureTexture); + dispatchDescription.reactive = GetVulkanTextureResource(instance, feature, feature.textureTable.reactiveMask); + dispatchDescription.transparencyAndComposition = GetVulkanTextureResource(instance, feature, feature.textureTable.transparencyMask); + dispatchDescription.output = GetVulkanTextureResource(instance, feature, feature.textureTable.colorOutput, FFX_RESOURCE_STATE_UNORDERED_ACCESS); } dispatchDescription.jitterOffset.x = params->jitterOffsetX; @@ -310,6 +412,23 @@ static void UNITY_INTERFACE_API OnRenderEventAndData(int eventID, void* data) case BaseEventId + FSR2PluginEvent::ePostExecute: { auto* params = (FSR2CommandExecutionData*)data; + if (params->featureSlot < 0 || params->featureSlot >= s_Features.size()) + return; + + auto& feature = s_Features[params->featureSlot]; + + if (s_GraphicsVulkan != nullptr) + { + UnityVulkanInstance instance = s_GraphicsVulkan->Instance(); + DestroyVulkanImageView(instance, feature.textureTable.colorInput); + DestroyVulkanImageView(instance, feature.textureTable.depth); + DestroyVulkanImageView(instance, feature.textureTable.motionVectors); + DestroyVulkanImageView(instance, feature.textureTable.exposureTexture); + DestroyVulkanImageView(instance, feature.textureTable.reactiveMask); + DestroyVulkanImageView(instance, feature.textureTable.transparencyMask); + DestroyVulkanImageView(instance, feature.textureTable.colorOutput); + } + break; } case BaseEventId + FSR2PluginEvent::eInit: @@ -361,15 +480,36 @@ static void UNITY_INTERFACE_API OnSetTextureEvent(int eventID, void* data) } case kUnityRenderingExtEventUpdateTextureEndV2: { - intptr_t texturePtr = 0; + // 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 + FSR2TextureDesc* textureDesc = ((FSR2TextureDesc*)&feature.textureTable) + textureSlot; + if (s_GraphicsD3D12 != nullptr) { - texturePtr = (intptr_t)s_GraphicsD3D12->TextureFromNativeTexture((UnityTextureID)params->textureID); + textureDesc->image = (intptr_t)s_GraphicsD3D12->TextureFromNativeTexture((UnityTextureID)params->textureID); + } + else if (s_GraphicsVulkan != nullptr) + { + VkAccessFlags accessFlags = (textureSlot == FSR2Textures::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->view = 0; + textureDesc->width = params->width; + textureDesc->height = params->height; + textureDesc->format = (uint32_t)image.format; + } + else + { + textureDesc->image = 0; + textureDesc->view = 0; + } } - // 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) = texturePtr; break; } } diff --git a/FSR2UnityTypes.h b/FSR2UnityTypes.h index 4d2336b..7b219ac 100644 --- a/FSR2UnityTypes.h +++ b/FSR2UnityTypes.h @@ -59,14 +59,24 @@ struct FSR2CommandExecutionData uint32_t featureSlot; }; +struct FSR2TextureDesc +{ + intptr_t image; + intptr_t view; + + uint32_t width; + uint32_t height; + uint32_t format; +}; + struct FSR2TextureTable { - intptr_t colorInput; - intptr_t colorOutput; - intptr_t depth; - intptr_t motionVectors; - intptr_t transparencyMask; - intptr_t exposureTexture; - intptr_t reactiveMask; - intptr_t biasColorMask; + FSR2TextureDesc colorInput; + FSR2TextureDesc colorOutput; + FSR2TextureDesc depth; + FSR2TextureDesc motionVectors; + FSR2TextureDesc transparencyMask; + FSR2TextureDesc exposureTexture; + FSR2TextureDesc reactiveMask; + FSR2TextureDesc biasColorMask; };