1918 lines
86 KiB
C++
1918 lines
86 KiB
C++
// This file is part of the FidelityFX SDK.
|
|
//
|
|
// Copyright (c) 2022 Advanced Micro Devices, Inc. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
#include "../ffx_fsr2.h"
|
|
#include "ffx_fsr2_vk.h"
|
|
#include "shaders/ffx_fsr2_shaders_vk.h" // include all the precompiled VK shaders for the FSR2 passes
|
|
#include "../ffx_fsr2_private.h"
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <codecvt>
|
|
#include <locale>
|
|
|
|
// prototypes for functions in the interface
|
|
FfxErrorCode GetDeviceCapabilitiesVK(FfxFsr2Interface* backendInterface, FfxDeviceCapabilities* deviceCapabilities, FfxDevice device);
|
|
FfxErrorCode CreateBackendContextVK(FfxFsr2Interface* backendInterface, FfxDevice device);
|
|
FfxErrorCode DestroyBackendContextVK(FfxFsr2Interface* backendInterface);
|
|
FfxErrorCode CreateResourceVK(FfxFsr2Interface* backendInterface, const FfxCreateResourceDescription* desc, FfxResourceInternal* outResource);
|
|
FfxErrorCode RegisterResourceVK(FfxFsr2Interface* backendInterface, const FfxResource* inResource, FfxResourceInternal* outResourceInternal);
|
|
FfxErrorCode UnregisterResourcesVK(FfxFsr2Interface* backendInterface);
|
|
FfxResourceDescription GetResourceDescriptorVK(FfxFsr2Interface* backendInterface, FfxResourceInternal resource);
|
|
FfxErrorCode DestroyResourceVK(FfxFsr2Interface* backendInterface, FfxResourceInternal resource);
|
|
FfxErrorCode CreatePipelineVK(FfxFsr2Interface* backendInterface, FfxFsr2Pass passId, const FfxPipelineDescription* desc, FfxPipelineState* outPass);
|
|
FfxErrorCode DestroyPipelineVK(FfxFsr2Interface* backendInterface, FfxPipelineState* pipeline);
|
|
FfxErrorCode ScheduleGpuJobVK(FfxFsr2Interface* backendInterface, const FfxGpuJobDescription* job);
|
|
FfxErrorCode ExecuteGpuJobsVK(FfxFsr2Interface* backendInterface, FfxCommandList commandList);
|
|
|
|
#define FSR2_MAX_QUEUED_FRAMES ( 4)
|
|
#define FSR2_MAX_RESOURCE_COUNT (64)
|
|
#define FSR2_MAX_STAGING_RESOURCE_COUNT ( 8)
|
|
#define FSR2_MAX_BARRIERS (16)
|
|
#define FSR2_MAX_GPU_JOBS (32)
|
|
#define FSR2_MAX_IMAGE_COPY_MIPS (32)
|
|
#define FSR2_MAX_SAMPLERS ( 2)
|
|
#define FSR2_MAX_UNIFORM_BUFFERS ( 4)
|
|
#define FSR2_MAX_IMAGE_VIEWS (32)
|
|
#define FSR2_MAX_BUFFERED_DESCRIPTORS (FFX_FSR2_PASS_COUNT * FSR2_MAX_QUEUED_FRAMES)
|
|
#define FSR2_UBO_RING_BUFFER_SIZE (FSR2_MAX_BUFFERED_DESCRIPTORS * FSR2_MAX_UNIFORM_BUFFERS)
|
|
#define FSR2_UBO_MEMORY_BLOCK_SIZE (FSR2_UBO_RING_BUFFER_SIZE * 256)
|
|
|
|
typedef struct BackendContext_VK {
|
|
|
|
// store for resources and resourceViews
|
|
typedef struct Resource
|
|
{
|
|
#ifdef _DEBUG
|
|
char resourceName[64] = {};
|
|
#endif
|
|
VkImage imageResource;
|
|
VkImageAspectFlags aspectFlags;
|
|
VkBuffer bufferResource;
|
|
VkDeviceMemory deviceMemory;
|
|
VkMemoryPropertyFlags memoryProperties;
|
|
FfxResourceDescription resourceDescription;
|
|
FfxResourceStates state;
|
|
VkImageView allMipsImageView;
|
|
VkImageView singleMipImageViews[FSR2_MAX_IMAGE_VIEWS];
|
|
bool undefined;
|
|
} Resource;
|
|
|
|
typedef struct UniformBuffer
|
|
{
|
|
VkBuffer bufferResource;
|
|
uint8_t* pData;
|
|
} UniformBuffer;
|
|
|
|
typedef struct PipelineLayout
|
|
{
|
|
VkDescriptorSetLayout descriptorSetLayout;
|
|
VkDescriptorSet descriptorSets[FSR2_MAX_QUEUED_FRAMES];
|
|
uint32_t descriptorSetIndex;
|
|
VkPipelineLayout pipelineLayout;
|
|
} PipelineLayout;
|
|
|
|
typedef struct VKFunctionTable
|
|
{
|
|
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr = 0;
|
|
PFN_vkSetDebugUtilsObjectNameEXT vkSetDebugUtilsObjectNameEXT = 0;
|
|
PFN_vkCreateDescriptorPool vkCreateDescriptorPool = 0;
|
|
PFN_vkCreateSampler vkCreateSampler = 0;
|
|
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout = 0;
|
|
PFN_vkCreateBuffer vkCreateBuffer = 0;
|
|
PFN_vkCreateImage vkCreateImage = 0;
|
|
PFN_vkCreateImageView vkCreateImageView = 0;
|
|
PFN_vkCreateShaderModule vkCreateShaderModule = 0;
|
|
PFN_vkCreatePipelineLayout vkCreatePipelineLayout = 0;
|
|
PFN_vkCreateComputePipelines vkCreateComputePipelines = 0;
|
|
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout = 0;
|
|
PFN_vkDestroyPipeline vkDestroyPipeline = 0;
|
|
PFN_vkDestroyImage vkDestroyImage = 0;
|
|
PFN_vkDestroyImageView vkDestroyImageView = 0;
|
|
PFN_vkDestroyBuffer vkDestroyBuffer = 0;
|
|
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout = 0;
|
|
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool = 0;
|
|
PFN_vkDestroySampler vkDestroySampler = 0;
|
|
PFN_vkDestroyShaderModule vkDestroyShaderModule = 0;
|
|
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements = 0;
|
|
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements = 0;
|
|
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets = 0;
|
|
PFN_vkAllocateMemory vkAllocateMemory = 0;
|
|
PFN_vkFreeMemory vkFreeMemory = 0;
|
|
PFN_vkMapMemory vkMapMemory = 0;
|
|
PFN_vkUnmapMemory vkUnmapMemory = 0;
|
|
PFN_vkBindBufferMemory vkBindBufferMemory = 0;
|
|
PFN_vkBindImageMemory vkBindImageMemory = 0;
|
|
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets = 0;
|
|
PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges = 0;
|
|
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier = 0;
|
|
PFN_vkCmdBindPipeline vkCmdBindPipeline = 0;
|
|
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets = 0;
|
|
PFN_vkCmdDispatch vkCmdDispatch = 0;
|
|
PFN_vkCmdCopyBuffer vkCmdCopyBuffer = 0;
|
|
PFN_vkCmdCopyImage vkCmdCopyImage = 0;
|
|
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage = 0;
|
|
PFN_vkCmdClearColorImage vkCmdClearColorImage = 0;
|
|
} VkFunctionTable;
|
|
|
|
VkPhysicalDevice physicalDevice = nullptr;
|
|
VkDevice device = nullptr;
|
|
VkFunctionTable vkFunctionTable = {};
|
|
|
|
uint32_t gpuJobCount = 0;
|
|
FfxGpuJobDescription gpuJobs[FSR2_MAX_GPU_JOBS] = {};
|
|
|
|
uint32_t nextStaticResource = 0;
|
|
uint32_t nextDynamicResource = 0;
|
|
uint32_t stagingResourceCount = 0;
|
|
Resource resources[FSR2_MAX_RESOURCE_COUNT] = {};
|
|
FfxResourceInternal stagingResources[FSR2_MAX_STAGING_RESOURCE_COUNT] = {};
|
|
|
|
VkDescriptorPool descPool = nullptr;
|
|
VkDescriptorSetLayout samplerDescriptorSetLayout = nullptr;
|
|
VkDescriptorSet samplerDescriptorSet = nullptr;
|
|
uint32_t allocatedPipelineLayoutCount = 0;
|
|
PipelineLayout pipelineLayouts[FFX_FSR2_PASS_COUNT] = {};
|
|
VkSampler pointSampler = nullptr;
|
|
VkSampler linearSampler = nullptr;
|
|
|
|
VkDeviceMemory uboMemory = nullptr;
|
|
VkMemoryPropertyFlags uboMemoryProperties = 0;
|
|
UniformBuffer uboRingBuffer[FSR2_UBO_RING_BUFFER_SIZE] = {};
|
|
uint32_t uboRingBufferIndex = 0;
|
|
|
|
VkImageMemoryBarrier imageMemoryBarriers[FSR2_MAX_BARRIERS] = {};
|
|
VkBufferMemoryBarrier bufferMemoryBarriers[FSR2_MAX_BARRIERS] = {};
|
|
uint32_t scheduledImageBarrierCount = 0;
|
|
uint32_t scheduledBufferBarrierCount = 0;
|
|
VkPipelineStageFlags srcStageMask = 0;
|
|
VkPipelineStageFlags dstStageMask = 0;
|
|
|
|
uint32_t numDeviceExtensions = 0;
|
|
VkExtensionProperties* extensionProperties = nullptr;
|
|
|
|
} BackendContext_VK;
|
|
|
|
FFX_API size_t ffxFsr2GetScratchMemorySizeVK(VkPhysicalDevice physicalDevice)
|
|
{
|
|
uint32_t numExtensions = 0;
|
|
|
|
if (physicalDevice)
|
|
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &numExtensions, nullptr);
|
|
|
|
return FFX_ALIGN_UP(sizeof(BackendContext_VK) + sizeof(VkExtensionProperties) * numExtensions, sizeof(uint64_t));
|
|
}
|
|
|
|
FfxErrorCode ffxFsr2GetInterfaceVK(
|
|
FfxFsr2Interface* outInterface,
|
|
void* scratchBuffer,
|
|
size_t scratchBufferSize,
|
|
VkPhysicalDevice physicalDevice,
|
|
PFN_vkGetDeviceProcAddr getDeviceProcAddr)
|
|
{
|
|
FFX_RETURN_ON_ERROR(
|
|
outInterface,
|
|
FFX_ERROR_INVALID_POINTER);
|
|
FFX_RETURN_ON_ERROR(
|
|
scratchBuffer,
|
|
FFX_ERROR_INVALID_POINTER);
|
|
FFX_RETURN_ON_ERROR(
|
|
scratchBufferSize >= ffxFsr2GetScratchMemorySizeVK(physicalDevice),
|
|
FFX_ERROR_INSUFFICIENT_MEMORY);
|
|
|
|
outInterface->fpGetDeviceCapabilities = GetDeviceCapabilitiesVK;
|
|
outInterface->fpCreateBackendContext = CreateBackendContextVK;
|
|
outInterface->fpDestroyBackendContext = DestroyBackendContextVK;
|
|
outInterface->fpCreateResource = CreateResourceVK;
|
|
outInterface->fpRegisterResource = RegisterResourceVK;
|
|
outInterface->fpUnregisterResources = UnregisterResourcesVK;
|
|
outInterface->fpGetResourceDescription = GetResourceDescriptorVK;
|
|
outInterface->fpDestroyResource = DestroyResourceVK;
|
|
outInterface->fpCreatePipeline = CreatePipelineVK;
|
|
outInterface->fpDestroyPipeline = DestroyPipelineVK;
|
|
outInterface->fpScheduleGpuJob = ScheduleGpuJobVK;
|
|
outInterface->fpExecuteGpuJobs = ExecuteGpuJobsVK;
|
|
outInterface->scratchBuffer = scratchBuffer;
|
|
outInterface->scratchBufferSize = scratchBufferSize;
|
|
|
|
BackendContext_VK* context = (BackendContext_VK*)scratchBuffer;
|
|
|
|
context->physicalDevice = physicalDevice;
|
|
context->vkFunctionTable.vkGetDeviceProcAddr = getDeviceProcAddr;
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
void loadVKFunctions(BackendContext_VK* backendContext, PFN_vkGetDeviceProcAddr getDeviceProcAddr)
|
|
{
|
|
FFX_ASSERT(NULL != backendContext);
|
|
|
|
backendContext->vkFunctionTable.vkSetDebugUtilsObjectNameEXT = (PFN_vkSetDebugUtilsObjectNameEXT)getDeviceProcAddr(backendContext->device, "vkSetDebugUtilsObjectNameEXT");
|
|
backendContext->vkFunctionTable.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)getDeviceProcAddr(backendContext->device, "vkFlushMappedMemoryRanges");
|
|
backendContext->vkFunctionTable.vkCreateDescriptorPool = (PFN_vkCreateDescriptorPool)getDeviceProcAddr(backendContext->device, "vkCreateDescriptorPool");
|
|
backendContext->vkFunctionTable.vkCreateSampler = (PFN_vkCreateSampler)getDeviceProcAddr(backendContext->device, "vkCreateSampler");
|
|
backendContext->vkFunctionTable.vkCreateDescriptorSetLayout = (PFN_vkCreateDescriptorSetLayout)getDeviceProcAddr(backendContext->device, "vkCreateDescriptorSetLayout");
|
|
backendContext->vkFunctionTable.vkCreateBuffer = (PFN_vkCreateBuffer)getDeviceProcAddr(backendContext->device, "vkCreateBuffer");
|
|
backendContext->vkFunctionTable.vkCreateImage = (PFN_vkCreateImage)getDeviceProcAddr(backendContext->device, "vkCreateImage");
|
|
backendContext->vkFunctionTable.vkCreateImageView = (PFN_vkCreateImageView)getDeviceProcAddr(backendContext->device, "vkCreateImageView");
|
|
backendContext->vkFunctionTable.vkCreateShaderModule = (PFN_vkCreateShaderModule)getDeviceProcAddr(backendContext->device, "vkCreateShaderModule");
|
|
backendContext->vkFunctionTable.vkCreatePipelineLayout = (PFN_vkCreatePipelineLayout)getDeviceProcAddr(backendContext->device, "vkCreatePipelineLayout");
|
|
backendContext->vkFunctionTable.vkCreateComputePipelines = (PFN_vkCreateComputePipelines)getDeviceProcAddr(backendContext->device, "vkCreateComputePipelines");
|
|
backendContext->vkFunctionTable.vkDestroyPipelineLayout = (PFN_vkDestroyPipelineLayout)getDeviceProcAddr(backendContext->device, "vkDestroyPipelineLayout");
|
|
backendContext->vkFunctionTable.vkDestroyPipeline = (PFN_vkDestroyPipeline)getDeviceProcAddr(backendContext->device, "vkDestroyPipeline");
|
|
backendContext->vkFunctionTable.vkDestroyImage = (PFN_vkDestroyImage)getDeviceProcAddr(backendContext->device, "vkDestroyImage");
|
|
backendContext->vkFunctionTable.vkDestroyImageView = (PFN_vkDestroyImageView)getDeviceProcAddr(backendContext->device, "vkDestroyImageView");
|
|
backendContext->vkFunctionTable.vkDestroyBuffer = (PFN_vkDestroyBuffer)getDeviceProcAddr(backendContext->device, "vkDestroyBuffer");
|
|
backendContext->vkFunctionTable.vkDestroyDescriptorSetLayout = (PFN_vkDestroyDescriptorSetLayout)getDeviceProcAddr(backendContext->device, "vkDestroyDescriptorSetLayout");
|
|
backendContext->vkFunctionTable.vkDestroyDescriptorPool = (PFN_vkDestroyDescriptorPool)getDeviceProcAddr(backendContext->device, "vkDestroyDescriptorPool");
|
|
backendContext->vkFunctionTable.vkDestroySampler = (PFN_vkDestroySampler)getDeviceProcAddr(backendContext->device, "vkDestroySampler");
|
|
backendContext->vkFunctionTable.vkDestroyShaderModule = (PFN_vkDestroyShaderModule)getDeviceProcAddr(backendContext->device, "vkDestroyShaderModule");
|
|
backendContext->vkFunctionTable.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)getDeviceProcAddr(backendContext->device, "vkGetBufferMemoryRequirements");
|
|
backendContext->vkFunctionTable.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)getDeviceProcAddr(backendContext->device, "vkGetImageMemoryRequirements");
|
|
backendContext->vkFunctionTable.vkAllocateDescriptorSets = (PFN_vkAllocateDescriptorSets)getDeviceProcAddr(backendContext->device, "vkAllocateDescriptorSets");
|
|
backendContext->vkFunctionTable.vkAllocateMemory = (PFN_vkAllocateMemory)getDeviceProcAddr(backendContext->device, "vkAllocateMemory");
|
|
backendContext->vkFunctionTable.vkFreeMemory = (PFN_vkFreeMemory)getDeviceProcAddr(backendContext->device, "vkFreeMemory");
|
|
backendContext->vkFunctionTable.vkMapMemory = (PFN_vkMapMemory)getDeviceProcAddr(backendContext->device, "vkMapMemory");
|
|
backendContext->vkFunctionTable.vkUnmapMemory = (PFN_vkUnmapMemory)getDeviceProcAddr(backendContext->device, "vkUnmapMemory");
|
|
backendContext->vkFunctionTable.vkBindBufferMemory = (PFN_vkBindBufferMemory)getDeviceProcAddr(backendContext->device, "vkBindBufferMemory");
|
|
backendContext->vkFunctionTable.vkBindImageMemory = (PFN_vkBindImageMemory)getDeviceProcAddr(backendContext->device, "vkBindImageMemory");
|
|
backendContext->vkFunctionTable.vkUpdateDescriptorSets = (PFN_vkUpdateDescriptorSets)getDeviceProcAddr(backendContext->device, "vkUpdateDescriptorSets");
|
|
backendContext->vkFunctionTable.vkCmdPipelineBarrier = (PFN_vkCmdPipelineBarrier)getDeviceProcAddr(backendContext->device, "vkCmdPipelineBarrier");
|
|
backendContext->vkFunctionTable.vkCmdBindPipeline = (PFN_vkCmdBindPipeline)getDeviceProcAddr(backendContext->device, "vkCmdBindPipeline");
|
|
backendContext->vkFunctionTable.vkCmdBindDescriptorSets = (PFN_vkCmdBindDescriptorSets)getDeviceProcAddr(backendContext->device, "vkCmdBindDescriptorSets");
|
|
backendContext->vkFunctionTable.vkCmdDispatch = (PFN_vkCmdDispatch)getDeviceProcAddr(backendContext->device, "vkCmdDispatch");
|
|
backendContext->vkFunctionTable.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)getDeviceProcAddr(backendContext->device, "vkCmdCopyBuffer");
|
|
backendContext->vkFunctionTable.vkCmdCopyImage = (PFN_vkCmdCopyImage)getDeviceProcAddr(backendContext->device, "vkCmdCopyImage");
|
|
backendContext->vkFunctionTable.vkCmdCopyBufferToImage = (PFN_vkCmdCopyBufferToImage)getDeviceProcAddr(backendContext->device, "vkCmdCopyBufferToImage");
|
|
backendContext->vkFunctionTable.vkCmdClearColorImage = (PFN_vkCmdClearColorImage)getDeviceProcAddr(backendContext->device, "vkCmdClearColorImage");
|
|
}
|
|
|
|
void setVKObjectName(BackendContext_VK::VKFunctionTable& vkFunctionTable, VkDevice device, VkObjectType objectType, uint64_t object, char* name)
|
|
{
|
|
VkDebugUtilsObjectNameInfoEXT s{ VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, nullptr, objectType, object, name };
|
|
|
|
if (vkFunctionTable.vkSetDebugUtilsObjectNameEXT)
|
|
vkFunctionTable.vkSetDebugUtilsObjectNameEXT(device, &s);
|
|
}
|
|
|
|
VkFormat getVKFormatFromSurfaceFormat(FfxSurfaceFormat fmt)
|
|
{
|
|
switch (fmt) {
|
|
|
|
case(FFX_SURFACE_FORMAT_R32G32B32A32_TYPELESS):
|
|
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
case(FFX_SURFACE_FORMAT_R32G32B32A32_FLOAT):
|
|
return VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
case(FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT):
|
|
return VK_FORMAT_R16G16B16A16_SFLOAT;
|
|
case(FFX_SURFACE_FORMAT_R32G32_FLOAT):
|
|
return VK_FORMAT_R32G32_SFLOAT;
|
|
case(FFX_SURFACE_FORMAT_R32_UINT):
|
|
return VK_FORMAT_R32_UINT;
|
|
case(FFX_SURFACE_FORMAT_R8G8B8A8_TYPELESS):
|
|
return VK_FORMAT_R8G8B8A8_UNORM;
|
|
case(FFX_SURFACE_FORMAT_R8G8B8A8_UNORM):
|
|
return VK_FORMAT_R8G8B8A8_UNORM;
|
|
case(FFX_SURFACE_FORMAT_R11G11B10_FLOAT):
|
|
return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
|
|
case(FFX_SURFACE_FORMAT_R16G16_FLOAT):
|
|
return VK_FORMAT_R16G16_SFLOAT;
|
|
case(FFX_SURFACE_FORMAT_R16G16_UINT):
|
|
return VK_FORMAT_R16G16_UINT;
|
|
case(FFX_SURFACE_FORMAT_R16_FLOAT):
|
|
return VK_FORMAT_R16_SFLOAT;
|
|
case(FFX_SURFACE_FORMAT_R16_UINT):
|
|
return VK_FORMAT_R16_UINT;
|
|
case(FFX_SURFACE_FORMAT_R16_UNORM):
|
|
return VK_FORMAT_R16_UNORM;
|
|
case(FFX_SURFACE_FORMAT_R16_SNORM):
|
|
return VK_FORMAT_R16_SNORM;
|
|
case(FFX_SURFACE_FORMAT_R8_UNORM):
|
|
return VK_FORMAT_R8_UNORM;
|
|
case(FFX_SURFACE_FORMAT_R8G8_UNORM):
|
|
return VK_FORMAT_R8G8_UNORM;
|
|
case(FFX_SURFACE_FORMAT_R32_FLOAT):
|
|
return VK_FORMAT_R32_SFLOAT;
|
|
default:
|
|
return VK_FORMAT_UNDEFINED;
|
|
}
|
|
}
|
|
|
|
VkImageUsageFlags getVKImageUsageFlagsFromResourceUsage(FfxResourceUsage flags)
|
|
{
|
|
VkImageUsageFlags ret = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
if (flags & FFX_RESOURCE_USAGE_RENDERTARGET) ret |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
if (flags & FFX_RESOURCE_USAGE_UAV) ret |= (VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
|
|
return ret;
|
|
}
|
|
|
|
VkBufferUsageFlags getVKBufferUsageFlagsFromResourceUsage(FfxResourceUsage flags)
|
|
{
|
|
if (flags & FFX_RESOURCE_USAGE_UAV)
|
|
return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
else
|
|
return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
}
|
|
|
|
VkImageType getVKImageTypeFromResourceType(FfxResourceType type)
|
|
{
|
|
switch (type) {
|
|
case(FFX_RESOURCE_TYPE_TEXTURE1D):
|
|
return VK_IMAGE_TYPE_1D;
|
|
case(FFX_RESOURCE_TYPE_TEXTURE2D):
|
|
return VK_IMAGE_TYPE_2D;
|
|
case(FFX_RESOURCE_TYPE_TEXTURE3D):
|
|
return VK_IMAGE_TYPE_3D;
|
|
default:
|
|
return VK_IMAGE_TYPE_MAX_ENUM;
|
|
}
|
|
}
|
|
|
|
VkImageLayout getVKImageLayoutFromResourceState(FfxResourceStates state)
|
|
{
|
|
switch (state) {
|
|
|
|
case(FFX_RESOURCE_STATE_GENERIC_READ):
|
|
return VK_IMAGE_LAYOUT_GENERAL;
|
|
case(FFX_RESOURCE_STATE_UNORDERED_ACCESS):
|
|
return VK_IMAGE_LAYOUT_GENERAL;
|
|
case(FFX_RESOURCE_STATE_COMPUTE_READ):
|
|
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
case FFX_RESOURCE_STATE_COPY_SRC:
|
|
return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
case FFX_RESOURCE_STATE_COPY_DEST:
|
|
return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
default:
|
|
return VK_IMAGE_LAYOUT_GENERAL;
|
|
}
|
|
}
|
|
|
|
VkPipelineStageFlags getVKPipelineStageFlagsFromResourceState(FfxResourceStates state)
|
|
{
|
|
switch (state) {
|
|
|
|
case(FFX_RESOURCE_STATE_GENERIC_READ):
|
|
case(FFX_RESOURCE_STATE_UNORDERED_ACCESS):
|
|
case(FFX_RESOURCE_STATE_COMPUTE_READ):
|
|
return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
case FFX_RESOURCE_STATE_COPY_SRC:
|
|
case FFX_RESOURCE_STATE_COPY_DEST:
|
|
return VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
default:
|
|
return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
|
|
}
|
|
}
|
|
|
|
VkAccessFlags getVKAccessFlagsFromResourceState(FfxResourceStates state)
|
|
{
|
|
switch (state) {
|
|
|
|
case(FFX_RESOURCE_STATE_GENERIC_READ):
|
|
return VK_ACCESS_SHADER_READ_BIT;
|
|
case(FFX_RESOURCE_STATE_UNORDERED_ACCESS):
|
|
return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
|
case(FFX_RESOURCE_STATE_COMPUTE_READ):
|
|
return VK_ACCESS_SHADER_READ_BIT;
|
|
case FFX_RESOURCE_STATE_COPY_SRC:
|
|
return VK_ACCESS_TRANSFER_READ_BIT;
|
|
case FFX_RESOURCE_STATE_COPY_DEST:
|
|
return VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
default:
|
|
return VK_ACCESS_SHADER_READ_BIT;
|
|
}
|
|
}
|
|
|
|
FfxSurfaceFormat ffxGetSurfaceFormatVK(VkFormat fmt)
|
|
{
|
|
switch (fmt) {
|
|
|
|
case(VK_FORMAT_R32G32B32A32_SFLOAT):
|
|
return FFX_SURFACE_FORMAT_R32G32B32A32_FLOAT;
|
|
case(VK_FORMAT_R16G16B16A16_SFLOAT):
|
|
return FFX_SURFACE_FORMAT_R16G16B16A16_FLOAT;
|
|
case(VK_FORMAT_R32G32_SFLOAT):
|
|
return FFX_SURFACE_FORMAT_R32G32_FLOAT;
|
|
case(VK_FORMAT_R32_UINT):
|
|
return FFX_SURFACE_FORMAT_R32_UINT;
|
|
case(VK_FORMAT_R8G8B8A8_UNORM):
|
|
return FFX_SURFACE_FORMAT_R8G8B8A8_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_R32_SFLOAT):
|
|
return FFX_SURFACE_FORMAT_R32_FLOAT;
|
|
default:
|
|
return FFX_SURFACE_FORMAT_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
uint32_t findMemoryTypeIndex(VkPhysicalDevice physicalDevice, VkMemoryRequirements memRequirements, VkMemoryPropertyFlags requestedProperties, VkMemoryPropertyFlags& outProperties)
|
|
{
|
|
FFX_ASSERT(NULL != physicalDevice);
|
|
|
|
VkPhysicalDeviceMemoryProperties memProperties;
|
|
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
|
|
|
|
uint32_t bestCandidate = UINT32_MAX;
|
|
|
|
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
|
|
if ((memRequirements.memoryTypeBits & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & requestedProperties)) {
|
|
|
|
// if just device-local memory is requested, make sure this is the invisible heap to prevent over-subscribing the local heap
|
|
if (requestedProperties == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT && (memProperties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))
|
|
continue;
|
|
|
|
bestCandidate = i;
|
|
outProperties = memProperties.memoryTypes[i].propertyFlags;
|
|
|
|
// if host-visible memory is requested, check for host coherency as well and if available, return immediately
|
|
if ((requestedProperties & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && (memProperties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
|
|
return bestCandidate;
|
|
}
|
|
}
|
|
|
|
return bestCandidate;
|
|
}
|
|
|
|
VkDescriptorBufferInfo accquireDynamicUBO(BackendContext_VK* backendContext, uint32_t size, void* pData)
|
|
{
|
|
// the ubo ring buffer is pre-populated with VkBuffer objects of 256-bytes to prevent creating buffers at runtime
|
|
FFX_ASSERT(size <= 256);
|
|
|
|
BackendContext_VK::UniformBuffer& ubo = backendContext->uboRingBuffer[backendContext->uboRingBufferIndex];
|
|
|
|
VkDescriptorBufferInfo bufferInfo = {};
|
|
|
|
bufferInfo.buffer = ubo.bufferResource;
|
|
bufferInfo.offset = 0;
|
|
bufferInfo.range = size;
|
|
|
|
if (pData)
|
|
{
|
|
memcpy(ubo.pData, pData, size);
|
|
|
|
// flush mapped range if memory type is not coherant
|
|
if ((backendContext->uboMemoryProperties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
|
|
{
|
|
VkMappedMemoryRange memoryRange;
|
|
memset(&memoryRange, 0, sizeof(memoryRange));
|
|
|
|
memoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
|
memoryRange.memory = backendContext->uboMemory;
|
|
memoryRange.offset = 256 * backendContext->uboRingBufferIndex;
|
|
memoryRange.size = size;
|
|
|
|
backendContext->vkFunctionTable.vkFlushMappedMemoryRanges(backendContext->device, 1, &memoryRange);
|
|
}
|
|
}
|
|
|
|
backendContext->uboRingBufferIndex++;
|
|
|
|
if (backendContext->uboRingBufferIndex >= FSR2_UBO_RING_BUFFER_SIZE)
|
|
backendContext->uboRingBufferIndex = 0;
|
|
|
|
return bufferInfo;
|
|
}
|
|
|
|
// Create a FfxFsr2Device from a VkDevice
|
|
FfxDevice ffxGetDeviceVK(VkDevice vkDevice)
|
|
{
|
|
FFX_ASSERT(NULL != vkDevice);
|
|
return reinterpret_cast<FfxDevice>(vkDevice);
|
|
}
|
|
|
|
FfxCommandList ffxGetCommandListVK(VkCommandBuffer cmdBuf)
|
|
{
|
|
FFX_ASSERT(NULL != cmdBuf);
|
|
return reinterpret_cast<FfxCommandList>(cmdBuf);
|
|
}
|
|
|
|
FfxResource ffxGetTextureResourceVK(FfxFsr2Context* context, VkImage imgVk, VkImageView imageView, uint32_t width, uint32_t height, VkFormat imgFormat, wchar_t* name, FfxResourceStates state)
|
|
{
|
|
FfxResource resource = {};
|
|
resource.resource = reinterpret_cast<void*>(imgVk);
|
|
resource.state = state;
|
|
resource.descriptorData = reinterpret_cast<uint64_t>(imageView);
|
|
resource.description.flags = FFX_RESOURCE_FLAGS_NONE;
|
|
resource.description.type = FFX_RESOURCE_TYPE_TEXTURE2D;
|
|
resource.description.width = width;
|
|
resource.description.height = height;
|
|
resource.description.depth = 1;
|
|
resource.description.mipCount = 1;
|
|
resource.description.format = ffxGetSurfaceFormatVK(imgFormat);
|
|
|
|
switch (imgFormat)
|
|
{
|
|
case VK_FORMAT_D16_UNORM:
|
|
case VK_FORMAT_D32_SFLOAT:
|
|
case VK_FORMAT_D16_UNORM_S8_UINT:
|
|
case VK_FORMAT_D24_UNORM_S8_UINT:
|
|
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
|
{
|
|
resource.isDepth = true;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
resource.isDepth = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
if (name) {
|
|
wcscpy(resource.name, name);
|
|
}
|
|
#endif
|
|
|
|
return resource;
|
|
}
|
|
|
|
FfxResource ffxGetBufferResourceVK(FfxFsr2Context* context, VkBuffer bufVk, uint32_t size, wchar_t* name, FfxResourceStates state)
|
|
{
|
|
FfxResource resource = {};
|
|
resource.resource = reinterpret_cast<void*>(bufVk);
|
|
resource.state = state;
|
|
resource.descriptorData = 0;
|
|
resource.description.flags = FFX_RESOURCE_FLAGS_NONE;
|
|
resource.description.type = FFX_RESOURCE_TYPE_BUFFER;
|
|
resource.description.width = size;
|
|
resource.description.height = 1;
|
|
resource.description.depth = 1;
|
|
resource.description.mipCount = 1;
|
|
resource.description.format = FFX_SURFACE_FORMAT_UNKNOWN;
|
|
resource.isDepth = false;
|
|
|
|
#ifdef _DEBUG
|
|
if (name) {
|
|
wcscpy(resource.name, name);
|
|
}
|
|
#endif
|
|
|
|
return resource;
|
|
}
|
|
|
|
VkImage ffxGetVkImage(FfxFsr2Context* context, uint32_t resId)
|
|
{
|
|
FFX_ASSERT(NULL != context);
|
|
|
|
FfxFsr2Context_Private* contextPrivate = (FfxFsr2Context_Private*)(context);
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)(contextPrivate->contextDescription.callbacks.scratchBuffer);
|
|
|
|
int32_t internalIndex = contextPrivate->uavResources[resId].internalIndex;
|
|
|
|
return (internalIndex == -1) ? nullptr : backendContext->resources[internalIndex].imageResource;
|
|
}
|
|
|
|
VkImageView ffxGetVkImageView(FfxFsr2Context* context, uint32_t resId)
|
|
{
|
|
FFX_ASSERT(NULL != context);
|
|
|
|
FfxFsr2Context_Private* contextPrivate = (FfxFsr2Context_Private*)(context);
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)(contextPrivate->contextDescription.callbacks.scratchBuffer);
|
|
BackendContext_VK::Resource& internalRes = backendContext->resources[contextPrivate->uavResources[resId].internalIndex];
|
|
|
|
return internalRes.allMipsImageView;
|
|
}
|
|
|
|
VkImageLayout ffxGetVkImageLayout(FfxFsr2Context* context, uint32_t resId)
|
|
{
|
|
FfxFsr2Context_Private* contextPrivate = (FfxFsr2Context_Private*)(context);
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)(contextPrivate->contextDescription.callbacks.scratchBuffer);
|
|
BackendContext_VK::Resource& internalRes = backendContext->resources[contextPrivate->uavResources[resId].internalIndex];
|
|
|
|
return getVKImageLayoutFromResourceState(internalRes.state);
|
|
}
|
|
|
|
FfxErrorCode RegisterResourceVK(
|
|
FfxFsr2Interface* backendInterface,
|
|
const FfxResource* inFfxResource,
|
|
FfxResourceInternal* outFfxResourceInternal
|
|
)
|
|
{
|
|
FFX_ASSERT(NULL != backendInterface);
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)(backendInterface->scratchBuffer);
|
|
|
|
if (inFfxResource->resource == nullptr) {
|
|
|
|
outFfxResourceInternal->internalIndex = FFX_FSR2_RESOURCE_IDENTIFIER_NULL;
|
|
return FFX_OK;
|
|
}
|
|
|
|
FFX_ASSERT(backendContext->nextDynamicResource > backendContext->nextStaticResource);
|
|
outFfxResourceInternal->internalIndex = backendContext->nextDynamicResource--;
|
|
|
|
BackendContext_VK::Resource* backendResource = &backendContext->resources[outFfxResourceInternal->internalIndex];
|
|
|
|
backendResource->resourceDescription = inFfxResource->description;
|
|
backendResource->state = inFfxResource->state;
|
|
backendResource->undefined = false;
|
|
|
|
#ifdef _DEBUG
|
|
size_t retval = 0;
|
|
wcstombs_s(&retval, backendResource->resourceName, sizeof(backendResource->resourceName), inFfxResource->name, sizeof(backendResource->resourceName));
|
|
if (retval >= 64) backendResource->resourceName[63] = '\0';
|
|
#endif
|
|
|
|
if (inFfxResource->description.type == FFX_RESOURCE_TYPE_BUFFER)
|
|
{
|
|
VkBuffer buffer = reinterpret_cast<VkBuffer>(inFfxResource->resource);
|
|
|
|
backendResource->bufferResource = buffer;
|
|
}
|
|
else
|
|
{
|
|
VkImage image = reinterpret_cast<VkImage>(inFfxResource->resource);
|
|
VkImageView imageView = reinterpret_cast<VkImageView>(inFfxResource->descriptorData);
|
|
|
|
backendResource->imageResource = image;
|
|
|
|
if (image) {
|
|
|
|
if (imageView) {
|
|
|
|
if (inFfxResource->isDepth)
|
|
backendResource->aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
|
|
else
|
|
backendResource->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
backendResource->allMipsImageView = imageView;
|
|
backendResource->singleMipImageViews[0] = imageView;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
// dispose dynamic resources: This should be called at the end of the frame
|
|
FfxErrorCode UnregisterResourcesVK(FfxFsr2Interface* backendInterface)
|
|
{
|
|
FFX_ASSERT(NULL != backendInterface);
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)(backendInterface->scratchBuffer);
|
|
|
|
backendContext->nextDynamicResource = FSR2_MAX_RESOURCE_COUNT - 1;
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
FfxErrorCode GetDeviceCapabilitiesVK(FfxFsr2Interface* backendInterface, FfxDeviceCapabilities* deviceCapabilities, FfxDevice device)
|
|
{
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
|
|
// no shader model in vulkan so assume the minimum
|
|
deviceCapabilities->minimumSupportedShaderModel = FFX_SHADER_MODEL_5_1;
|
|
deviceCapabilities->waveLaneCountMin = 32;
|
|
deviceCapabilities->waveLaneCountMax = 32;
|
|
deviceCapabilities->fp16Supported = false;
|
|
deviceCapabilities->raytracingSupported = false;
|
|
|
|
BackendContext_VK* context = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
|
|
// check if extensions are enabled
|
|
|
|
for (uint32_t i = 0; i < backendContext->numDeviceExtensions; i++)
|
|
{
|
|
if (strcmp(backendContext->extensionProperties[i].extensionName, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME) == 0)
|
|
{
|
|
// check if we the max subgroup size allows us to use wave64
|
|
VkPhysicalDeviceSubgroupSizeControlProperties subgroupSizeControlProperties = {};
|
|
subgroupSizeControlProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES;
|
|
|
|
VkPhysicalDeviceProperties2 deviceProperties2 = {};
|
|
deviceProperties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
|
|
deviceProperties2.pNext = &subgroupSizeControlProperties;
|
|
vkGetPhysicalDeviceProperties2(context->physicalDevice, &deviceProperties2);
|
|
|
|
deviceCapabilities->waveLaneCountMin = subgroupSizeControlProperties.minSubgroupSize;
|
|
deviceCapabilities->waveLaneCountMax = subgroupSizeControlProperties.maxSubgroupSize;
|
|
}
|
|
if (strcmp(backendContext->extensionProperties[i].extensionName, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME) == 0)
|
|
{
|
|
// check for fp16 support
|
|
VkPhysicalDeviceShaderFloat16Int8Features shaderFloat18Int8Features = {};
|
|
shaderFloat18Int8Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES;
|
|
|
|
VkPhysicalDeviceFeatures2 physicalDeviceFeatures2 = {};
|
|
physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
|
physicalDeviceFeatures2.pNext = &shaderFloat18Int8Features;
|
|
|
|
vkGetPhysicalDeviceFeatures2(context->physicalDevice, &physicalDeviceFeatures2);
|
|
|
|
deviceCapabilities->fp16Supported = (bool)shaderFloat18Int8Features.shaderFloat16;
|
|
}
|
|
if (strcmp(backendContext->extensionProperties[i].extensionName, VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME) == 0)
|
|
{
|
|
// check for ray tracing support
|
|
VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures = {};
|
|
accelerationStructureFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR;
|
|
|
|
VkPhysicalDeviceFeatures2 physicalDeviceFeatures2 = {};
|
|
physicalDeviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
|
physicalDeviceFeatures2.pNext = &accelerationStructureFeatures;
|
|
|
|
vkGetPhysicalDeviceFeatures2(context->physicalDevice, &physicalDeviceFeatures2);
|
|
|
|
deviceCapabilities->raytracingSupported = (bool)accelerationStructureFeatures.accelerationStructure;
|
|
}
|
|
}
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
FfxErrorCode CreateBackendContextVK(FfxFsr2Interface* backendInterface, FfxDevice device)
|
|
{
|
|
FFX_ASSERT(NULL != backendInterface);
|
|
|
|
VkDevice vkDevice = reinterpret_cast<VkDevice>(device);
|
|
|
|
// set up some internal resources we need (space for resource views and constant buffers)
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
backendContext->extensionProperties = (VkExtensionProperties*)(backendContext + 1);
|
|
|
|
// make sure the extra parameters were already passed in
|
|
FFX_ASSERT(backendContext->physicalDevice != NULL);
|
|
|
|
// if vkGetDeviceProcAddr is NULL, use the one from the vulkan header
|
|
if (backendContext->vkFunctionTable.vkGetDeviceProcAddr == NULL)
|
|
backendContext->vkFunctionTable.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
|
|
|
|
if (vkDevice != NULL) {
|
|
backendContext->device = vkDevice;
|
|
}
|
|
|
|
backendContext->nextStaticResource = 0;
|
|
backendContext->nextDynamicResource = FSR2_MAX_RESOURCE_COUNT - 1;
|
|
|
|
// load vulkan functions
|
|
loadVKFunctions(backendContext, backendContext->vkFunctionTable.vkGetDeviceProcAddr);
|
|
|
|
// enumerate all the device extensions
|
|
backendContext->numDeviceExtensions = 0;
|
|
vkEnumerateDeviceExtensionProperties(backendContext->physicalDevice, nullptr, &backendContext->numDeviceExtensions, nullptr);
|
|
vkEnumerateDeviceExtensionProperties(backendContext->physicalDevice, nullptr, &backendContext->numDeviceExtensions, backendContext->extensionProperties);
|
|
|
|
// create descriptor pool
|
|
VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = {};
|
|
|
|
VkDescriptorPoolSize poolSizes[] = {
|
|
{ VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, FSR2_MAX_IMAGE_VIEWS * FSR2_MAX_BUFFERED_DESCRIPTORS },
|
|
{ VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, FSR2_MAX_IMAGE_VIEWS * FSR2_MAX_BUFFERED_DESCRIPTORS },
|
|
{ VK_DESCRIPTOR_TYPE_SAMPLER, FSR2_MAX_SAMPLERS * FSR2_MAX_BUFFERED_DESCRIPTORS },
|
|
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, FSR2_MAX_UNIFORM_BUFFERS * FSR2_MAX_BUFFERED_DESCRIPTORS },
|
|
};
|
|
|
|
descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
descriptorPoolCreateInfo.maxSets = (FSR2_MAX_BUFFERED_DESCRIPTORS * FSR2_MAX_QUEUED_FRAMES);
|
|
descriptorPoolCreateInfo.poolSizeCount = 4;
|
|
descriptorPoolCreateInfo.pPoolSizes = poolSizes;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateDescriptorPool(backendContext->device, &descriptorPoolCreateInfo, nullptr, &backendContext->descPool) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
VkSamplerCreateInfo samplerCreateInfo = {};
|
|
|
|
samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
|
samplerCreateInfo.magFilter = VK_FILTER_NEAREST;
|
|
samplerCreateInfo.minFilter = VK_FILTER_NEAREST;
|
|
samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
|
samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
|
samplerCreateInfo.minLod = -1000;
|
|
samplerCreateInfo.maxLod = 1000;
|
|
samplerCreateInfo.maxAnisotropy = 1.0f;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateSampler(backendContext->device, &samplerCreateInfo, nullptr, &backendContext->pointSampler) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
samplerCreateInfo.magFilter = VK_FILTER_LINEAR;
|
|
samplerCreateInfo.minFilter = VK_FILTER_LINEAR;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateSampler(backendContext->device, &samplerCreateInfo, nullptr, &backendContext->linearSampler) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
{
|
|
VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {};
|
|
|
|
VkDescriptorSetLayoutBinding bindings[] = {
|
|
{ 0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT, &backendContext->pointSampler },
|
|
{ 1, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT, &backendContext->linearSampler },
|
|
};
|
|
|
|
descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
descriptorSetLayoutCreateInfo.bindingCount = 2;
|
|
descriptorSetLayoutCreateInfo.pBindings = bindings;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateDescriptorSetLayout(backendContext->device, &descriptorSetLayoutCreateInfo, NULL, &backendContext->samplerDescriptorSetLayout) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
}
|
|
|
|
{
|
|
VkDescriptorSetAllocateInfo allocateInfo = {};
|
|
|
|
allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
allocateInfo.descriptorPool = backendContext->descPool;
|
|
allocateInfo.descriptorSetCount = 1;
|
|
allocateInfo.pSetLayouts = &backendContext->samplerDescriptorSetLayout;
|
|
|
|
backendContext->vkFunctionTable.vkAllocateDescriptorSets(backendContext->device, &allocateInfo, &backendContext->samplerDescriptorSet);
|
|
}
|
|
|
|
// allocate ring buffer of uniform buffers
|
|
{
|
|
for (uint32_t i = 0; i < FSR2_UBO_RING_BUFFER_SIZE; i++)
|
|
{
|
|
BackendContext_VK::UniformBuffer& ubo = backendContext->uboRingBuffer[i];
|
|
|
|
VkBufferCreateInfo bufferInfo = {};
|
|
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.size = 256;
|
|
bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateBuffer(backendContext->device, &bufferInfo, NULL, &ubo.bufferResource) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
}
|
|
|
|
// allocate memory block for all uniform buffers
|
|
VkMemoryRequirements memRequirements = {};
|
|
backendContext->vkFunctionTable.vkGetBufferMemoryRequirements(backendContext->device, backendContext->uboRingBuffer[0].bufferResource, &memRequirements);
|
|
|
|
VkMemoryPropertyFlags requiredMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
|
|
VkMemoryAllocateInfo allocInfo{};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
allocInfo.allocationSize = FSR2_UBO_MEMORY_BLOCK_SIZE;
|
|
allocInfo.memoryTypeIndex = findMemoryTypeIndex(backendContext->physicalDevice, memRequirements, requiredMemoryProperties, backendContext->uboMemoryProperties);
|
|
|
|
if (allocInfo.memoryTypeIndex == UINT32_MAX) {
|
|
requiredMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
|
allocInfo.memoryTypeIndex = findMemoryTypeIndex(backendContext->physicalDevice, memRequirements, requiredMemoryProperties, backendContext->uboMemoryProperties);
|
|
|
|
if (allocInfo.memoryTypeIndex == UINT32_MAX) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
}
|
|
|
|
VkResult result = backendContext->vkFunctionTable.vkAllocateMemory(backendContext->device, &allocInfo, nullptr, &backendContext->uboMemory);
|
|
|
|
if (result != VK_SUCCESS) {
|
|
switch (result) {
|
|
case(VK_ERROR_OUT_OF_HOST_MEMORY):
|
|
case(VK_ERROR_OUT_OF_DEVICE_MEMORY):
|
|
return FFX_ERROR_OUT_OF_MEMORY;
|
|
default:
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
}
|
|
|
|
// map the memory block
|
|
uint8_t* pData = nullptr;
|
|
|
|
if (backendContext->vkFunctionTable.vkMapMemory(backendContext->device, backendContext->uboMemory, 0, FSR2_UBO_MEMORY_BLOCK_SIZE, 0, reinterpret_cast<void**>(&pData)) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
// bind each 256-byte block to the ubos
|
|
for (uint32_t i = 0; i < FSR2_UBO_RING_BUFFER_SIZE; i++)
|
|
{
|
|
BackendContext_VK::UniformBuffer& ubo = backendContext->uboRingBuffer[i];
|
|
|
|
// get the buffer memory requirements for each buffer object to silence validation errors
|
|
VkMemoryRequirements memRequirements = {};
|
|
backendContext->vkFunctionTable.vkGetBufferMemoryRequirements(backendContext->device, ubo.bufferResource, &memRequirements);
|
|
|
|
ubo.pData = pData + 256 * i;
|
|
|
|
if (backendContext->vkFunctionTable.vkBindBufferMemory(backendContext->device, ubo.bufferResource, backendContext->uboMemory, 256 * i) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
backendContext->gpuJobCount = 0;
|
|
backendContext->scheduledImageBarrierCount = 0;
|
|
backendContext->scheduledBufferBarrierCount = 0;
|
|
backendContext->stagingResourceCount = 0;
|
|
backendContext->allocatedPipelineLayoutCount = 0;
|
|
backendContext->srcStageMask = 0;
|
|
backendContext->dstStageMask = 0;
|
|
backendContext->uboRingBufferIndex = 0;
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
FfxErrorCode DestroyBackendContextVK(FfxFsr2Interface* backendInterface)
|
|
{
|
|
FFX_ASSERT(NULL != backendInterface);
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
|
|
for (uint32_t i = 0; i < backendContext->stagingResourceCount; i++)
|
|
DestroyResourceVK(backendInterface, backendContext->stagingResources[i]);
|
|
|
|
for (uint32_t i = 0; i < FSR2_UBO_RING_BUFFER_SIZE; i++)
|
|
{
|
|
BackendContext_VK::UniformBuffer& ubo = backendContext->uboRingBuffer[i];
|
|
|
|
backendContext->vkFunctionTable.vkDestroyBuffer(backendContext->device, ubo.bufferResource, nullptr);
|
|
|
|
ubo.bufferResource = nullptr;
|
|
ubo.pData = nullptr;
|
|
}
|
|
|
|
backendContext->vkFunctionTable.vkUnmapMemory(backendContext->device, backendContext->uboMemory);
|
|
backendContext->vkFunctionTable.vkFreeMemory(backendContext->device, backendContext->uboMemory, nullptr);
|
|
backendContext->uboMemory = nullptr;
|
|
|
|
backendContext->vkFunctionTable.vkDestroyDescriptorPool(backendContext->device, backendContext->descPool, nullptr);
|
|
backendContext->descPool = nullptr;
|
|
|
|
backendContext->vkFunctionTable.vkDestroyDescriptorSetLayout(backendContext->device, backendContext->samplerDescriptorSetLayout, nullptr);
|
|
backendContext->samplerDescriptorSet = nullptr;
|
|
backendContext->samplerDescriptorSetLayout = nullptr;
|
|
|
|
backendContext->vkFunctionTable.vkDestroySampler(backendContext->device, backendContext->pointSampler, nullptr);
|
|
backendContext->vkFunctionTable.vkDestroySampler(backendContext->device, backendContext->linearSampler, nullptr);
|
|
backendContext->pointSampler = nullptr;
|
|
backendContext->linearSampler = nullptr;
|
|
|
|
if (backendContext->device != nullptr) {
|
|
|
|
backendContext->device = nullptr;
|
|
}
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
// create a internal resource that will stay alive until effect gets shut down
|
|
FfxErrorCode CreateResourceVK(
|
|
FfxFsr2Interface* backendInterface,
|
|
const FfxCreateResourceDescription* createResourceDescription,
|
|
FfxResourceInternal* outResource)
|
|
{
|
|
FFX_ASSERT(NULL != backendInterface);
|
|
FFX_ASSERT(NULL != createResourceDescription);
|
|
FFX_ASSERT(NULL != outResource);
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
VkDevice vkDevice = reinterpret_cast<VkDevice>(backendContext->device);
|
|
|
|
FFX_ASSERT(backendContext->nextStaticResource + 1 < backendContext->nextDynamicResource);
|
|
outResource->internalIndex = backendContext->nextStaticResource++;
|
|
BackendContext_VK::Resource* res = &backendContext->resources[outResource->internalIndex];
|
|
res->resourceDescription = createResourceDescription->resourceDescription;
|
|
res->resourceDescription.mipCount = createResourceDescription->resourceDescription.mipCount;
|
|
res->undefined = true; // A flag to make sure the first barrier for this image resource always uses an src layout of undefined
|
|
|
|
if (res->resourceDescription.mipCount == 0)
|
|
res->resourceDescription.mipCount = (uint32_t)(1 + floor(log2(FFX_MAXIMUM(FFX_MAXIMUM(createResourceDescription->resourceDescription.width, createResourceDescription->resourceDescription.height), createResourceDescription->resourceDescription.depth))));
|
|
#ifdef _DEBUG
|
|
size_t retval = 0;
|
|
wcstombs_s(&retval, res->resourceName, sizeof(res->resourceName), createResourceDescription->name, sizeof(res->resourceName));
|
|
if (retval >= 64) res->resourceName[63] = '\0';
|
|
#endif
|
|
VkMemoryRequirements memRequirements = {};
|
|
|
|
switch (createResourceDescription->resourceDescription.type)
|
|
{
|
|
case FFX_RESOURCE_TYPE_BUFFER:
|
|
{
|
|
VkBufferCreateInfo bufferInfo = {};
|
|
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bufferInfo.size = createResourceDescription->resourceDescription.width;
|
|
bufferInfo.usage = getVKBufferUsageFlagsFromResourceUsage(createResourceDescription->usage);
|
|
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
if (createResourceDescription->initData)
|
|
bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateBuffer(backendContext->device, &bufferInfo, NULL, &res->bufferResource) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
setVKObjectName(backendContext->vkFunctionTable, backendContext->device, VK_OBJECT_TYPE_BUFFER, (uint64_t)res->bufferResource, res->resourceName);
|
|
#endif
|
|
|
|
backendContext->vkFunctionTable.vkGetBufferMemoryRequirements(backendContext->device, res->bufferResource, &memRequirements);
|
|
break;
|
|
}
|
|
case FFX_RESOURCE_TYPE_TEXTURE1D:
|
|
case FFX_RESOURCE_TYPE_TEXTURE2D:
|
|
case FFX_RESOURCE_TYPE_TEXTURE3D:
|
|
{
|
|
VkImageCreateInfo imageInfo = {};
|
|
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
imageInfo.imageType = getVKImageTypeFromResourceType(createResourceDescription->resourceDescription.type);
|
|
imageInfo.extent.width = createResourceDescription->resourceDescription.width;
|
|
imageInfo.extent.height = createResourceDescription->resourceDescription.type == FFX_RESOURCE_TYPE_TEXTURE1D ? 1 : createResourceDescription->resourceDescription.height;
|
|
imageInfo.extent.depth = createResourceDescription->resourceDescription.type == FFX_RESOURCE_TYPE_TEXTURE3D ? createResourceDescription->resourceDescription.depth : 1;
|
|
imageInfo.mipLevels = res->resourceDescription.mipCount;
|
|
imageInfo.arrayLayers = 1;
|
|
imageInfo.format = getVKFormatFromSurfaceFormat(createResourceDescription->resourceDescription.format);
|
|
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
imageInfo.usage = getVKImageUsageFlagsFromResourceUsage(createResourceDescription->usage);
|
|
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateImage(backendContext->device, &imageInfo, nullptr, &res->imageResource) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
res->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
#ifdef _DEBUG
|
|
setVKObjectName(backendContext->vkFunctionTable, backendContext->device, VK_OBJECT_TYPE_IMAGE, (uint64_t)res->imageResource, res->resourceName);
|
|
#endif
|
|
|
|
backendContext->vkFunctionTable.vkGetImageMemoryRequirements(backendContext->device, res->imageResource, &memRequirements);
|
|
break;
|
|
}
|
|
default:;
|
|
}
|
|
|
|
VkMemoryPropertyFlags requiredMemoryProperties;
|
|
|
|
if (createResourceDescription->heapType == FFX_HEAP_TYPE_UPLOAD)
|
|
requiredMemoryProperties = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
|
|
else
|
|
requiredMemoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
|
|
VkMemoryAllocateInfo allocInfo{};
|
|
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
allocInfo.allocationSize = memRequirements.size;
|
|
allocInfo.memoryTypeIndex = findMemoryTypeIndex(backendContext->physicalDevice, memRequirements, requiredMemoryProperties, res->memoryProperties);
|
|
|
|
if (allocInfo.memoryTypeIndex == UINT32_MAX) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
VkResult result = backendContext->vkFunctionTable.vkAllocateMemory(backendContext->device, &allocInfo, nullptr, &res->deviceMemory);
|
|
|
|
if (result != VK_SUCCESS) {
|
|
switch (result) {
|
|
case(VK_ERROR_OUT_OF_HOST_MEMORY):
|
|
case(VK_ERROR_OUT_OF_DEVICE_MEMORY):
|
|
return FFX_ERROR_OUT_OF_MEMORY;
|
|
default:
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
}
|
|
|
|
switch (createResourceDescription->resourceDescription.type)
|
|
{
|
|
case FFX_RESOURCE_TYPE_BUFFER:
|
|
{
|
|
if (backendContext->vkFunctionTable.vkBindBufferMemory(backendContext->device, res->bufferResource, res->deviceMemory, 0) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
break;
|
|
}
|
|
case FFX_RESOURCE_TYPE_TEXTURE1D:
|
|
case FFX_RESOURCE_TYPE_TEXTURE2D:
|
|
case FFX_RESOURCE_TYPE_TEXTURE3D:
|
|
{
|
|
if (backendContext->vkFunctionTable.vkBindImageMemory(backendContext->device, res->imageResource, res->deviceMemory, 0) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
VkImageViewCreateInfo imageViewCreateInfo = {};
|
|
imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
imageViewCreateInfo.image = res->imageResource;
|
|
imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
imageViewCreateInfo.format = getVKFormatFromSurfaceFormat(createResourceDescription->resourceDescription.format);
|
|
imageViewCreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
imageViewCreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
imageViewCreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
imageViewCreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
|
imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
|
|
imageViewCreateInfo.subresourceRange.levelCount = res->resourceDescription.mipCount;
|
|
imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
|
|
imageViewCreateInfo.subresourceRange.layerCount = 1;
|
|
|
|
// create an image view containing all mip levels for use as an srv
|
|
if (backendContext->vkFunctionTable.vkCreateImageView(backendContext->device, &imageViewCreateInfo, NULL, &res->allMipsImageView) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
#ifdef _DEBUG
|
|
setVKObjectName(backendContext->vkFunctionTable, backendContext->device, VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)res->allMipsImageView, res->resourceName);
|
|
#endif
|
|
// create image views of individual mip levels for use as a uav
|
|
for (uint32_t mip = 0; mip < res->resourceDescription.mipCount; ++mip)
|
|
{
|
|
imageViewCreateInfo.subresourceRange.levelCount = 1;
|
|
imageViewCreateInfo.subresourceRange.baseMipLevel = mip;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateImageView(backendContext->device, &imageViewCreateInfo, NULL, &res->singleMipImageViews[mip]) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
#ifdef _DEBUG
|
|
setVKObjectName(backendContext->vkFunctionTable, backendContext->device, VK_OBJECT_TYPE_IMAGE_VIEW, (uint64_t)res->singleMipImageViews[mip], res->resourceName);
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
default:;
|
|
}
|
|
|
|
if (createResourceDescription->initData)
|
|
{
|
|
// only allow copies directy into mapped memory for buffer resources since all texture resources are in optimal tiling
|
|
if (createResourceDescription->heapType == FFX_HEAP_TYPE_UPLOAD && createResourceDescription->resourceDescription.type == FFX_RESOURCE_TYPE_BUFFER)
|
|
{
|
|
void* data = NULL;
|
|
|
|
if (backendContext->vkFunctionTable.vkMapMemory(backendContext->device, res->deviceMemory, 0, createResourceDescription->initDataSize, 0, &data) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
memcpy(data, createResourceDescription->initData, createResourceDescription->initDataSize);
|
|
|
|
// flush mapped range if memory type is not coherant
|
|
if ((res->memoryProperties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
|
|
{
|
|
VkMappedMemoryRange memoryRange = {};
|
|
memoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
|
memoryRange.memory = res->deviceMemory;
|
|
memoryRange.size = createResourceDescription->initDataSize;
|
|
|
|
backendContext->vkFunctionTable.vkFlushMappedMemoryRanges(backendContext->device, 1, &memoryRange);
|
|
}
|
|
|
|
backendContext->vkFunctionTable.vkUnmapMemory(backendContext->device, res->deviceMemory);
|
|
}
|
|
else
|
|
{
|
|
FfxResourceInternal copySrc;
|
|
FfxCreateResourceDescription uploadDesc = { *createResourceDescription };
|
|
uploadDesc.heapType = FFX_HEAP_TYPE_UPLOAD;
|
|
uploadDesc.resourceDescription.type = FFX_RESOURCE_TYPE_BUFFER;
|
|
uploadDesc.resourceDescription.width = createResourceDescription->initDataSize;
|
|
uploadDesc.usage = FFX_RESOURCE_USAGE_READ_ONLY;
|
|
uploadDesc.initalState = FFX_RESOURCE_STATE_GENERIC_READ;
|
|
uploadDesc.initData = createResourceDescription->initData;
|
|
uploadDesc.initDataSize = createResourceDescription->initDataSize;
|
|
|
|
backendInterface->fpCreateResource(backendInterface, &uploadDesc, ©Src);
|
|
|
|
// setup the upload job
|
|
FfxGpuJobDescription copyJob =
|
|
{
|
|
FFX_GPU_JOB_COPY
|
|
};
|
|
copyJob.copyJobDescriptor.src = copySrc;
|
|
copyJob.copyJobDescriptor.dst = *outResource;
|
|
|
|
backendInterface->fpScheduleGpuJob(backendInterface, ©Job);
|
|
|
|
// add to the list of staging resources to delete later
|
|
uint32_t stagingResIdx = backendContext->stagingResourceCount++;
|
|
|
|
FFX_ASSERT(backendContext->stagingResourceCount < FSR2_MAX_STAGING_RESOURCE_COUNT);
|
|
|
|
backendContext->stagingResources[stagingResIdx] = copySrc;
|
|
}
|
|
}
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
FfxResourceDescription GetResourceDescriptorVK(FfxFsr2Interface* backendInterface, FfxResourceInternal resource)
|
|
{
|
|
FFX_ASSERT(NULL != backendInterface);
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
|
|
if (resource.internalIndex != -1)
|
|
{
|
|
FfxResourceDescription desc = backendContext->resources[resource.internalIndex].resourceDescription;
|
|
return desc;
|
|
}
|
|
else
|
|
{
|
|
FfxResourceDescription desc = {};
|
|
return desc;
|
|
}
|
|
}
|
|
|
|
FfxErrorCode CreatePipelineVK(FfxFsr2Interface* backendInterface, FfxFsr2Pass pass, const FfxPipelineDescription* pipelineDescription, FfxPipelineState* outPipeline)
|
|
{
|
|
FFX_ASSERT(NULL != backendInterface);
|
|
FFX_ASSERT(NULL != pipelineDescription);
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
|
|
// query device capabilities
|
|
FfxDeviceCapabilities deviceCapabilities;
|
|
|
|
GetDeviceCapabilitiesVK(backendInterface, &deviceCapabilities, ffxGetDeviceVK(backendContext->device));
|
|
|
|
// check if we can force wave64
|
|
bool canForceWave64 = false;
|
|
bool useLut = false;
|
|
|
|
if (deviceCapabilities.waveLaneCountMin == 32 && deviceCapabilities.waveLaneCountMax == 64) {
|
|
|
|
useLut = true;
|
|
canForceWave64 = true;
|
|
}
|
|
|
|
// check if we have 16bit floating point.
|
|
bool supportedFP16 = deviceCapabilities.fp16Supported;
|
|
|
|
if (pass == FFX_FSR2_PASS_ACCUMULATE || pass == FFX_FSR2_PASS_ACCUMULATE_SHARPEN)
|
|
{
|
|
VkPhysicalDeviceProperties physicalDeviceProperties = {};
|
|
vkGetPhysicalDeviceProperties(backendContext->physicalDevice, &physicalDeviceProperties);
|
|
|
|
// Workaround: Disable FP16 path for the accumulate pass on NVIDIA due to reduced occupancy and high VRAM throughput.
|
|
if (physicalDeviceProperties.vendorID == 0x10DE)
|
|
supportedFP16 = false;
|
|
}
|
|
|
|
// work out what permutation to load.
|
|
uint32_t flags = 0;
|
|
flags |= (pipelineDescription->contextFlags & FFX_FSR2_ENABLE_HIGH_DYNAMIC_RANGE) ? FSR2_SHADER_PERMUTATION_HDR_COLOR_INPUT : 0;
|
|
flags |= (pipelineDescription->contextFlags & FFX_FSR2_ENABLE_DISPLAY_RESOLUTION_MOTION_VECTORS) ? 0 : FSR2_SHADER_PERMUTATION_LOW_RES_MOTION_VECTORS;
|
|
flags |= (pipelineDescription->contextFlags & FFX_FSR2_ENABLE_MOTION_VECTORS_JITTER_CANCELLATION) ? FSR2_SHADER_PERMUTATION_JITTER_MOTION_VECTORS : 0;
|
|
flags |= (pipelineDescription->contextFlags & FFX_FSR2_ENABLE_DEPTH_INVERTED) ? FSR2_SHADER_PERMUTATION_DEPTH_INVERTED : 0;
|
|
flags |= (pass == FFX_FSR2_PASS_ACCUMULATE_SHARPEN) ? FSR2_SHADER_PERMUTATION_ENABLE_SHARPENING : 0;
|
|
flags |= (useLut) ? FSR2_SHADER_PERMUTATION_REPROJECT_USE_LANCZOS_TYPE : 0;
|
|
flags |= (canForceWave64) ? FSR2_SHADER_PERMUTATION_FORCE_WAVE64 : 0;
|
|
flags |= (supportedFP16 && (pass != FFX_FSR2_PASS_RCAS)) ? FSR2_SHADER_PERMUTATION_ALLOW_FP16 : 0;
|
|
|
|
const Fsr2ShaderBlobVK shaderBlob = fsr2GetPermutationBlobByIndex(pass, flags);
|
|
FFX_ASSERT(shaderBlob.data && shaderBlob.size);
|
|
|
|
// populate the pass.
|
|
outPipeline->srvCount = shaderBlob.sampledImageCount;
|
|
outPipeline->uavCount = shaderBlob.storageImageCount;
|
|
outPipeline->constCount = shaderBlob.uniformBufferCount;
|
|
|
|
FFX_ASSERT(shaderBlob.storageImageCount < FFX_MAX_NUM_UAVS);
|
|
FFX_ASSERT(shaderBlob.sampledImageCount < FFX_MAX_NUM_SRVS);
|
|
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
|
|
|
for (uint32_t srvIndex = 0; srvIndex < outPipeline->srvCount; ++srvIndex)
|
|
{
|
|
outPipeline->srvResourceBindings[srvIndex].slotIndex = shaderBlob.boundSampledImageBindings[srvIndex];
|
|
wcscpy(outPipeline->srvResourceBindings[srvIndex].name, converter.from_bytes(shaderBlob.boundSampledImageNames[srvIndex]).c_str());
|
|
}
|
|
for (uint32_t uavIndex = 0; uavIndex < outPipeline->uavCount; ++uavIndex)
|
|
{
|
|
outPipeline->uavResourceBindings[uavIndex].slotIndex = shaderBlob.boundStorageImageBindings[uavIndex];
|
|
wcscpy(outPipeline->uavResourceBindings[uavIndex].name, converter.from_bytes(shaderBlob.boundStorageImageNames[uavIndex]).c_str());
|
|
}
|
|
for (uint32_t cbIndex = 0; cbIndex < outPipeline->constCount; ++cbIndex)
|
|
{
|
|
outPipeline->cbResourceBindings[cbIndex].slotIndex = shaderBlob.boundUniformBufferBindings[cbIndex];
|
|
wcscpy(outPipeline->cbResourceBindings[cbIndex].name, converter.from_bytes(shaderBlob.boundUniformBufferNames[cbIndex]).c_str());
|
|
}
|
|
|
|
// create descriptor set layout
|
|
FFX_ASSERT(backendContext->allocatedPipelineLayoutCount < FFX_FSR2_PASS_COUNT);
|
|
BackendContext_VK::PipelineLayout& pipelineLayout = backendContext->pipelineLayouts[backendContext->allocatedPipelineLayoutCount++];
|
|
VkDescriptorSetLayoutBinding bindings[32];
|
|
uint32_t bindingIndex = 0;
|
|
|
|
for (uint32_t srvIndex = 0; srvIndex < outPipeline->srvCount; ++srvIndex)
|
|
{
|
|
VkDescriptorSetLayoutBinding& binding = bindings[bindingIndex++];
|
|
binding.binding = outPipeline->srvResourceBindings[srvIndex].slotIndex;
|
|
binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
|
binding.descriptorCount = 1;
|
|
binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
binding.pImmutableSamplers = nullptr;
|
|
}
|
|
|
|
for (uint32_t uavIndex = 0; uavIndex < outPipeline->uavCount; ++uavIndex)
|
|
{
|
|
VkDescriptorSetLayoutBinding& binding = bindings[bindingIndex++];
|
|
binding.binding = outPipeline->uavResourceBindings[uavIndex].slotIndex;
|
|
binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
binding.descriptorCount = 1;
|
|
binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
binding.pImmutableSamplers = nullptr;
|
|
}
|
|
|
|
for (uint32_t cbIndex = 0; cbIndex < outPipeline->constCount; ++cbIndex)
|
|
{
|
|
VkDescriptorSetLayoutBinding& binding = bindings[bindingIndex++];
|
|
binding.binding = outPipeline->cbResourceBindings[cbIndex].slotIndex;
|
|
binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
binding.descriptorCount = 1;
|
|
binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
binding.pImmutableSamplers = nullptr;
|
|
}
|
|
|
|
VkDescriptorSetLayoutCreateInfo dsLayoutCreateInfo = {};
|
|
dsLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
|
dsLayoutCreateInfo.bindingCount = bindingIndex;
|
|
dsLayoutCreateInfo.pBindings = bindings;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateDescriptorSetLayout(backendContext->device, &dsLayoutCreateInfo, nullptr, &pipelineLayout.descriptorSetLayout) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
// allocate descriptor sets
|
|
pipelineLayout.descriptorSetIndex = 0;
|
|
|
|
for (uint32_t i = 0; i < FSR2_MAX_QUEUED_FRAMES; i++)
|
|
{
|
|
VkDescriptorSetAllocateInfo allocateInfo = {};
|
|
allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
allocateInfo.descriptorPool = backendContext->descPool;
|
|
allocateInfo.descriptorSetCount = 1;
|
|
allocateInfo.pSetLayouts = &pipelineLayout.descriptorSetLayout;
|
|
|
|
backendContext->vkFunctionTable.vkAllocateDescriptorSets(backendContext->device, &allocateInfo, &pipelineLayout.descriptorSets[i]);
|
|
}
|
|
|
|
// create pipeline layout
|
|
VkDescriptorSetLayout dsLayouts[] = { backendContext->samplerDescriptorSetLayout, pipelineLayout.descriptorSetLayout };
|
|
|
|
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
|
|
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
|
pipelineLayoutCreateInfo.setLayoutCount = 2;
|
|
pipelineLayoutCreateInfo.pSetLayouts = dsLayouts;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreatePipelineLayout(backendContext->device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout.pipelineLayout) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
// create the shader module
|
|
VkShaderModuleCreateInfo shaderModuleCreateInfo = {};
|
|
shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
shaderModuleCreateInfo.pCode = (uint32_t*)shaderBlob.data;
|
|
shaderModuleCreateInfo.codeSize = shaderBlob.size;
|
|
|
|
VkShaderModule shaderModule = nullptr;
|
|
|
|
if (backendContext->vkFunctionTable.vkCreateShaderModule(backendContext->device, &shaderModuleCreateInfo, nullptr, &shaderModule) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
// fill out shader stage create info
|
|
VkPipelineShaderStageCreateInfo shaderStageCreateInfo = {};
|
|
shaderStageCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shaderStageCreateInfo.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
shaderStageCreateInfo.pName = "main";
|
|
shaderStageCreateInfo.module = shaderModule;
|
|
|
|
// set wave64 if possible
|
|
VkPipelineShaderStageRequiredSubgroupSizeCreateInfo subgroupSizeCreateInfo = {};
|
|
|
|
if (canForceWave64) {
|
|
|
|
subgroupSizeCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO;
|
|
subgroupSizeCreateInfo.requiredSubgroupSize = 64;
|
|
|
|
shaderStageCreateInfo.pNext = &subgroupSizeCreateInfo;
|
|
}
|
|
|
|
// create the compute pipeline
|
|
VkComputePipelineCreateInfo pipelineCreateInfo = {};
|
|
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
pipelineCreateInfo.stage = shaderStageCreateInfo;
|
|
pipelineCreateInfo.layout = pipelineLayout.pipelineLayout;
|
|
|
|
VkPipeline computePipeline = nullptr;
|
|
if (backendContext->vkFunctionTable.vkCreateComputePipelines(backendContext->device, nullptr, 1, &pipelineCreateInfo, nullptr, &computePipeline) != VK_SUCCESS) {
|
|
return FFX_ERROR_BACKEND_API_ERROR;
|
|
}
|
|
|
|
backendContext->vkFunctionTable.vkDestroyShaderModule(backendContext->device, shaderModule, nullptr);
|
|
|
|
outPipeline->pipeline = reinterpret_cast<FfxPipeline>(computePipeline);
|
|
outPipeline->rootSignature = reinterpret_cast<FfxRootSignature>(&pipelineLayout);
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
FfxErrorCode ScheduleGpuJobVK(FfxFsr2Interface* backendInterface, const FfxGpuJobDescription* job)
|
|
{
|
|
FFX_ASSERT(NULL != backendInterface);
|
|
FFX_ASSERT(NULL != job);
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
|
|
FFX_ASSERT(backendContext->gpuJobCount < FSR2_MAX_GPU_JOBS);
|
|
|
|
backendContext->gpuJobs[backendContext->gpuJobCount] = *job;
|
|
|
|
if (job->jobType == FFX_GPU_JOB_COMPUTE) {
|
|
|
|
// needs to copy SRVs and UAVs in case they are on the stack only
|
|
FfxComputeJobDescription* computeJob = &backendContext->gpuJobs[backendContext->gpuJobCount].computeJobDescriptor;
|
|
const uint32_t numConstBuffers = job->computeJobDescriptor.pipeline.constCount;
|
|
for (uint32_t currentRootConstantIndex = 0; currentRootConstantIndex < numConstBuffers; ++currentRootConstantIndex)
|
|
{
|
|
computeJob->cbs[currentRootConstantIndex].uint32Size = job->computeJobDescriptor.cbs[currentRootConstantIndex].uint32Size;
|
|
memcpy(computeJob->cbs[currentRootConstantIndex].data, job->computeJobDescriptor.cbs[currentRootConstantIndex].data, computeJob->cbs[currentRootConstantIndex].uint32Size * sizeof(uint32_t));
|
|
}
|
|
}
|
|
|
|
backendContext->gpuJobCount++;
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
void addBarrier(BackendContext_VK* backendContext, FfxResourceInternal* resource, FfxResourceStates newState)
|
|
{
|
|
FFX_ASSERT(NULL != backendContext);
|
|
FFX_ASSERT(NULL != resource);
|
|
|
|
BackendContext_VK::Resource& ffxResource = backendContext->resources[resource->internalIndex];
|
|
|
|
if (ffxResource.resourceDescription.type == FFX_RESOURCE_TYPE_BUFFER)
|
|
{
|
|
VkBuffer vkResource = ffxResource.bufferResource;
|
|
VkBufferMemoryBarrier* barrier = &backendContext->bufferMemoryBarriers[backendContext->scheduledBufferBarrierCount];
|
|
|
|
FfxResourceStates& curState = backendContext->resources[resource->internalIndex].state;
|
|
|
|
barrier->sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
|
barrier->pNext = nullptr;
|
|
barrier->srcAccessMask = getVKAccessFlagsFromResourceState(curState);
|
|
barrier->dstAccessMask = getVKAccessFlagsFromResourceState(newState);
|
|
barrier->srcQueueFamilyIndex = 0;
|
|
barrier->dstQueueFamilyIndex = 0;
|
|
barrier->buffer = vkResource;
|
|
barrier->offset = 0;
|
|
barrier->size = VK_WHOLE_SIZE;
|
|
|
|
backendContext->srcStageMask |= getVKPipelineStageFlagsFromResourceState(curState);
|
|
backendContext->dstStageMask |= getVKPipelineStageFlagsFromResourceState(newState);
|
|
|
|
curState = newState;
|
|
|
|
++backendContext->scheduledBufferBarrierCount;
|
|
}
|
|
else
|
|
{
|
|
VkImage vkResource = ffxResource.imageResource;
|
|
VkImageMemoryBarrier* barrier = &backendContext->imageMemoryBarriers[backendContext->scheduledImageBarrierCount];
|
|
|
|
FfxResourceStates& curState = backendContext->resources[resource->internalIndex].state;
|
|
|
|
VkImageSubresourceRange range;
|
|
range.aspectMask = backendContext->resources[resource->internalIndex].aspectFlags;
|
|
range.baseMipLevel = 0;
|
|
range.levelCount = backendContext->resources[resource->internalIndex].resourceDescription.mipCount;
|
|
range.baseArrayLayer = 0;
|
|
range.layerCount = 1;
|
|
|
|
barrier->sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier->pNext = nullptr;
|
|
barrier->srcAccessMask = getVKAccessFlagsFromResourceState(curState);
|
|
barrier->dstAccessMask = getVKAccessFlagsFromResourceState(newState);
|
|
barrier->oldLayout = ffxResource.undefined ? VK_IMAGE_LAYOUT_UNDEFINED : getVKImageLayoutFromResourceState(curState);
|
|
barrier->newLayout = getVKImageLayoutFromResourceState(newState);
|
|
barrier->srcQueueFamilyIndex = 0;
|
|
barrier->dstQueueFamilyIndex = 0;
|
|
barrier->image = vkResource;
|
|
barrier->subresourceRange = range;
|
|
|
|
backendContext->srcStageMask |= getVKPipelineStageFlagsFromResourceState(curState);
|
|
backendContext->dstStageMask |= getVKPipelineStageFlagsFromResourceState(newState);
|
|
|
|
curState = newState;
|
|
|
|
++backendContext->scheduledImageBarrierCount;
|
|
}
|
|
|
|
if (ffxResource.undefined)
|
|
ffxResource.undefined = false;
|
|
}
|
|
|
|
void flushBarriers(BackendContext_VK* backendContext, VkCommandBuffer vkCommandBuffer)
|
|
{
|
|
FFX_ASSERT(NULL != backendContext);
|
|
FFX_ASSERT(NULL != vkCommandBuffer);
|
|
|
|
if (backendContext->scheduledImageBarrierCount > 0 || backendContext->scheduledBufferBarrierCount > 0)
|
|
{
|
|
backendContext->vkFunctionTable.vkCmdPipelineBarrier(vkCommandBuffer, backendContext->srcStageMask, backendContext->dstStageMask, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, backendContext->scheduledBufferBarrierCount, backendContext->bufferMemoryBarriers, backendContext->scheduledImageBarrierCount, backendContext->imageMemoryBarriers);
|
|
backendContext->scheduledImageBarrierCount = 0;
|
|
backendContext->scheduledBufferBarrierCount = 0;
|
|
backendContext->srcStageMask = 0;
|
|
backendContext->dstStageMask = 0;
|
|
}
|
|
}
|
|
|
|
static FfxErrorCode executeGpuJobCompute(BackendContext_VK* backendContext, FfxGpuJobDescription* job, VkCommandBuffer vkCommandBuffer)
|
|
{
|
|
uint32_t imageInfoIndex = 0;
|
|
uint32_t bufferInfoIndex = 0;
|
|
uint32_t descriptorWriteIndex = 0;
|
|
VkDescriptorImageInfo imageInfos[FSR2_MAX_IMAGE_VIEWS];
|
|
VkDescriptorBufferInfo bufferInfos[FSR2_MAX_UNIFORM_BUFFERS];
|
|
VkWriteDescriptorSet writeDatas[FSR2_MAX_IMAGE_VIEWS + FSR2_MAX_UNIFORM_BUFFERS];
|
|
|
|
BackendContext_VK::PipelineLayout* pipelineLayout = reinterpret_cast<BackendContext_VK::PipelineLayout*>(job->computeJobDescriptor.pipeline.rootSignature);
|
|
|
|
// bind uavs
|
|
for (uint32_t uav = 0; uav < job->computeJobDescriptor.pipeline.uavCount; ++uav)
|
|
{
|
|
addBarrier(backendContext, &job->computeJobDescriptor.uavs[uav], FFX_RESOURCE_STATE_UNORDERED_ACCESS);
|
|
|
|
BackendContext_VK::Resource ffxResource = backendContext->resources[job->computeJobDescriptor.uavs[uav].internalIndex];
|
|
|
|
writeDatas[descriptorWriteIndex] = {};
|
|
writeDatas[descriptorWriteIndex].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
writeDatas[descriptorWriteIndex].dstSet = pipelineLayout->descriptorSets[pipelineLayout->descriptorSetIndex];
|
|
writeDatas[descriptorWriteIndex].descriptorCount = 1;
|
|
writeDatas[descriptorWriteIndex].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
|
writeDatas[descriptorWriteIndex].pImageInfo = &imageInfos[imageInfoIndex];
|
|
writeDatas[descriptorWriteIndex].dstBinding = job->computeJobDescriptor.pipeline.uavResourceBindings[uav].slotIndex;
|
|
writeDatas[descriptorWriteIndex].dstArrayElement = 0;
|
|
|
|
imageInfos[imageInfoIndex] = {};
|
|
imageInfos[imageInfoIndex].imageLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
imageInfos[imageInfoIndex].imageView = ffxResource.singleMipImageViews[job->computeJobDescriptor.uavMip[uav]];
|
|
|
|
imageInfoIndex++;
|
|
descriptorWriteIndex++;
|
|
}
|
|
|
|
// bind srvs
|
|
for (uint32_t srv = 0; srv < job->computeJobDescriptor.pipeline.srvCount; ++srv)
|
|
{
|
|
addBarrier(backendContext, &job->computeJobDescriptor.srvs[srv], FFX_RESOURCE_STATE_COMPUTE_READ);
|
|
|
|
BackendContext_VK::Resource ffxResource = backendContext->resources[job->computeJobDescriptor.srvs[srv].internalIndex];
|
|
|
|
writeDatas[descriptorWriteIndex] = {};
|
|
writeDatas[descriptorWriteIndex].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
writeDatas[descriptorWriteIndex].dstSet = pipelineLayout->descriptorSets[pipelineLayout->descriptorSetIndex];
|
|
writeDatas[descriptorWriteIndex].descriptorCount = 1;
|
|
writeDatas[descriptorWriteIndex].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
|
writeDatas[descriptorWriteIndex].pImageInfo = &imageInfos[imageInfoIndex];
|
|
writeDatas[descriptorWriteIndex].dstBinding = job->computeJobDescriptor.pipeline.srvResourceBindings[srv].slotIndex;
|
|
writeDatas[descriptorWriteIndex].dstArrayElement = 0;
|
|
|
|
imageInfos[imageInfoIndex] = {};
|
|
imageInfos[imageInfoIndex].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
imageInfos[imageInfoIndex].imageView = ffxResource.allMipsImageView;
|
|
|
|
imageInfoIndex++;
|
|
descriptorWriteIndex++;
|
|
}
|
|
|
|
// update ubos
|
|
for (uint32_t i = 0; i < job->computeJobDescriptor.pipeline.constCount; ++i)
|
|
{
|
|
writeDatas[descriptorWriteIndex] = {};
|
|
writeDatas[descriptorWriteIndex].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
writeDatas[descriptorWriteIndex].dstSet = pipelineLayout->descriptorSets[pipelineLayout->descriptorSetIndex];
|
|
writeDatas[descriptorWriteIndex].descriptorCount = 1;
|
|
writeDatas[descriptorWriteIndex].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
writeDatas[descriptorWriteIndex].pBufferInfo = &bufferInfos[bufferInfoIndex];
|
|
writeDatas[descriptorWriteIndex].dstBinding = job->computeJobDescriptor.pipeline.cbResourceBindings[i].slotIndex;
|
|
writeDatas[descriptorWriteIndex].dstArrayElement = 0;
|
|
|
|
bufferInfos[bufferInfoIndex] = accquireDynamicUBO(backendContext, job->computeJobDescriptor.cbs[i].uint32Size * sizeof(uint32_t), job->computeJobDescriptor.cbs[i].data);
|
|
|
|
bufferInfoIndex++;
|
|
descriptorWriteIndex++;
|
|
}
|
|
|
|
// insert all the barriers
|
|
flushBarriers(backendContext, vkCommandBuffer);
|
|
|
|
// update all uavs and srvs
|
|
backendContext->vkFunctionTable.vkUpdateDescriptorSets(backendContext->device, descriptorWriteIndex, writeDatas, 0, nullptr);
|
|
|
|
// bind pipeline
|
|
backendContext->vkFunctionTable.vkCmdBindPipeline(vkCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, reinterpret_cast<VkPipeline>(job->computeJobDescriptor.pipeline.pipeline));
|
|
|
|
// bind descriptor sets
|
|
VkDescriptorSet sets[] = {
|
|
backendContext->samplerDescriptorSet,
|
|
pipelineLayout->descriptorSets[pipelineLayout->descriptorSetIndex],
|
|
};
|
|
|
|
backendContext->vkFunctionTable.vkCmdBindDescriptorSets(vkCommandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout->pipelineLayout, 0, 2, sets, 0, nullptr);
|
|
|
|
// dispatch
|
|
backendContext->vkFunctionTable.vkCmdDispatch(vkCommandBuffer, job->computeJobDescriptor.dimensions[0], job->computeJobDescriptor.dimensions[1], job->computeJobDescriptor.dimensions[2]);
|
|
|
|
// move to another descriptor set for the next compute render job so that we don't overwrite descriptors in-use
|
|
pipelineLayout->descriptorSetIndex++;
|
|
|
|
if (pipelineLayout->descriptorSetIndex >= FSR2_MAX_QUEUED_FRAMES)
|
|
pipelineLayout->descriptorSetIndex = 0;
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
static FfxErrorCode executeGpuJobCopy(BackendContext_VK* backendContext, FfxGpuJobDescription* job, VkCommandBuffer vkCommandBuffer)
|
|
{
|
|
BackendContext_VK::Resource ffxResourceSrc = backendContext->resources[job->copyJobDescriptor.src.internalIndex];
|
|
BackendContext_VK::Resource ffxResourceDst = backendContext->resources[job->copyJobDescriptor.dst.internalIndex];
|
|
|
|
addBarrier(backendContext, &job->copyJobDescriptor.src, FFX_RESOURCE_STATE_COPY_SRC);
|
|
addBarrier(backendContext, &job->copyJobDescriptor.dst, FFX_RESOURCE_STATE_COPY_DEST);
|
|
flushBarriers(backendContext, vkCommandBuffer);
|
|
|
|
if (ffxResourceSrc.resourceDescription.type == FFX_RESOURCE_TYPE_BUFFER && ffxResourceDst.resourceDescription.type == FFX_RESOURCE_TYPE_BUFFER)
|
|
{
|
|
VkBuffer vkResourceSrc = ffxResourceSrc.bufferResource;
|
|
VkBuffer vkResourceDst = ffxResourceDst.bufferResource;
|
|
|
|
VkBufferCopy bufferCopy = {};
|
|
|
|
bufferCopy.dstOffset = 0;
|
|
bufferCopy.srcOffset = 0;
|
|
bufferCopy.size = ffxResourceSrc.resourceDescription.width;
|
|
|
|
backendContext->vkFunctionTable.vkCmdCopyBuffer(vkCommandBuffer, vkResourceSrc, vkResourceDst, 1, &bufferCopy);
|
|
}
|
|
else if (ffxResourceSrc.resourceDescription.type == FFX_RESOURCE_TYPE_BUFFER && ffxResourceDst.resourceDescription.type != FFX_RESOURCE_TYPE_BUFFER)
|
|
{
|
|
VkBuffer vkResourceSrc = ffxResourceSrc.bufferResource;
|
|
VkImage vkResourceDst = ffxResourceDst.imageResource;
|
|
|
|
VkImageSubresourceLayers subresourceLayers = {};
|
|
|
|
subresourceLayers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
subresourceLayers.baseArrayLayer = 0;
|
|
subresourceLayers.layerCount = 1;
|
|
subresourceLayers.mipLevel = 0;
|
|
|
|
VkOffset3D offset = {};
|
|
|
|
offset.x = 0;
|
|
offset.y = 0;
|
|
offset.z = 0;
|
|
|
|
VkExtent3D extent = {};
|
|
|
|
extent.width = ffxResourceDst.resourceDescription.width;
|
|
extent.height = ffxResourceDst.resourceDescription.height;
|
|
extent.depth = ffxResourceDst.resourceDescription.depth;
|
|
|
|
VkBufferImageCopy bufferImageCopy = {};
|
|
|
|
bufferImageCopy.bufferOffset = 0;
|
|
bufferImageCopy.bufferRowLength = 0;
|
|
bufferImageCopy.bufferImageHeight = 0;
|
|
bufferImageCopy.imageSubresource = subresourceLayers;
|
|
bufferImageCopy.imageOffset = offset;
|
|
bufferImageCopy.imageExtent = extent;
|
|
|
|
backendContext->vkFunctionTable.vkCmdCopyBufferToImage(vkCommandBuffer, vkResourceSrc, vkResourceDst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferImageCopy);
|
|
}
|
|
else
|
|
{
|
|
VkImageCopy imageCopies[FSR2_MAX_IMAGE_COPY_MIPS];
|
|
VkImage vkResourceSrc = ffxResourceSrc.imageResource;
|
|
VkImage vkResourceDst = ffxResourceDst.imageResource;
|
|
|
|
for (uint32_t mip = 0; mip < ffxResourceSrc.resourceDescription.mipCount; mip++)
|
|
{
|
|
VkImageSubresourceLayers subresourceLayers = {};
|
|
|
|
subresourceLayers.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
subresourceLayers.baseArrayLayer = 0;
|
|
subresourceLayers.layerCount = 1;
|
|
subresourceLayers.mipLevel = mip;
|
|
|
|
VkOffset3D offset = {};
|
|
|
|
offset.x = 0;
|
|
offset.y = 0;
|
|
offset.z = 0;
|
|
|
|
VkExtent3D extent = {};
|
|
|
|
extent.width = ffxResourceSrc.resourceDescription.width / (mip + 1);
|
|
extent.height = ffxResourceSrc.resourceDescription.height / (mip + 1);
|
|
extent.depth = ffxResourceSrc.resourceDescription.depth / (mip + 1);
|
|
|
|
VkImageCopy& copyRegion = imageCopies[mip];
|
|
|
|
copyRegion.srcSubresource = subresourceLayers;
|
|
copyRegion.srcOffset = offset;
|
|
copyRegion.dstSubresource = subresourceLayers;
|
|
copyRegion.dstOffset = offset;
|
|
copyRegion.extent = extent;
|
|
}
|
|
|
|
backendContext->vkFunctionTable.vkCmdCopyImage(vkCommandBuffer, vkResourceSrc, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vkResourceDst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ffxResourceSrc.resourceDescription.mipCount, imageCopies);
|
|
}
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
static FfxErrorCode executeGpuJobClearFloat(BackendContext_VK* backendContext, FfxGpuJobDescription* job, VkCommandBuffer vkCommandBuffer)
|
|
{
|
|
uint32_t idx = job->clearJobDescriptor.target.internalIndex;
|
|
BackendContext_VK::Resource ffxResource = backendContext->resources[idx];
|
|
|
|
if (ffxResource.resourceDescription.type != FFX_RESOURCE_TYPE_BUFFER)
|
|
{
|
|
addBarrier(backendContext, &job->clearJobDescriptor.target, FFX_RESOURCE_STATE_COPY_DEST);
|
|
flushBarriers(backendContext, vkCommandBuffer);
|
|
|
|
VkImage vkResource = ffxResource.imageResource;
|
|
|
|
VkClearColorValue clearColorValue = {};
|
|
|
|
clearColorValue.float32[0] = job->clearJobDescriptor.color[0];
|
|
clearColorValue.float32[1] = job->clearJobDescriptor.color[1];
|
|
clearColorValue.float32[2] = job->clearJobDescriptor.color[2];
|
|
clearColorValue.float32[3] = job->clearJobDescriptor.color[3];
|
|
|
|
VkImageSubresourceRange range;
|
|
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
range.baseMipLevel = 0;
|
|
range.levelCount = ffxResource.resourceDescription.mipCount;
|
|
range.baseArrayLayer = 0;
|
|
range.layerCount = 1;
|
|
|
|
backendContext->vkFunctionTable.vkCmdClearColorImage(vkCommandBuffer, vkResource, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColorValue, 1, &range);
|
|
}
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
FfxErrorCode ExecuteGpuJobsVK(FfxFsr2Interface* backendInterface, FfxCommandList commandList)
|
|
{
|
|
FFX_ASSERT(NULL != backendInterface);
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
|
|
FfxErrorCode errorCode = FFX_OK;
|
|
|
|
// execute all renderjobs
|
|
for (uint32_t i = 0; i < backendContext->gpuJobCount; ++i)
|
|
{
|
|
FfxGpuJobDescription* gpuJob = &backendContext->gpuJobs[i];
|
|
VkCommandBuffer vkCommandBuffer = reinterpret_cast<VkCommandBuffer>(commandList);
|
|
|
|
switch (gpuJob->jobType)
|
|
{
|
|
case FFX_GPU_JOB_CLEAR_FLOAT:
|
|
{
|
|
errorCode = executeGpuJobClearFloat(backendContext, gpuJob, vkCommandBuffer);
|
|
break;
|
|
}
|
|
case FFX_GPU_JOB_COPY:
|
|
{
|
|
errorCode = executeGpuJobCopy(backendContext, gpuJob, vkCommandBuffer);
|
|
break;
|
|
}
|
|
case FFX_GPU_JOB_COMPUTE:
|
|
{
|
|
errorCode = executeGpuJobCompute(backendContext, gpuJob, vkCommandBuffer);
|
|
break;
|
|
}
|
|
default:;
|
|
}
|
|
}
|
|
|
|
// check the execute function returned cleanly.
|
|
FFX_RETURN_ON_ERROR(
|
|
errorCode == FFX_OK,
|
|
FFX_ERROR_BACKEND_API_ERROR);
|
|
|
|
backendContext->gpuJobCount = 0;
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
FfxErrorCode DestroyResourceVK(FfxFsr2Interface* backendInterface, FfxResourceInternal resource)
|
|
{
|
|
FFX_ASSERT(backendInterface != nullptr);
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
|
|
if (resource.internalIndex != -1)
|
|
{
|
|
BackendContext_VK::Resource& res = backendContext->resources[resource.internalIndex];
|
|
|
|
if (res.resourceDescription.type == FFX_RESOURCE_TYPE_BUFFER)
|
|
{
|
|
if (res.bufferResource)
|
|
{
|
|
backendContext->vkFunctionTable.vkDestroyBuffer(backendContext->device, res.bufferResource, NULL);
|
|
res.bufferResource = nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (res.allMipsImageView)
|
|
{
|
|
backendContext->vkFunctionTable.vkDestroyImageView(backendContext->device, res.allMipsImageView, NULL);
|
|
res.allMipsImageView = nullptr;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < res.resourceDescription.mipCount; i++)
|
|
{
|
|
if (res.singleMipImageViews[i])
|
|
{
|
|
backendContext->vkFunctionTable.vkDestroyImageView(backendContext->device, res.singleMipImageViews[i], NULL);
|
|
res.singleMipImageViews[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
if (res.imageResource)
|
|
{
|
|
backendContext->vkFunctionTable.vkDestroyImage(backendContext->device, res.imageResource, NULL);
|
|
res.imageResource = nullptr;
|
|
}
|
|
}
|
|
|
|
if (res.deviceMemory)
|
|
{
|
|
backendContext->vkFunctionTable.vkFreeMemory(backendContext->device, res.deviceMemory, NULL);
|
|
res.deviceMemory = nullptr;
|
|
}
|
|
}
|
|
|
|
return FFX_OK;
|
|
}
|
|
|
|
FfxErrorCode DestroyPipelineVK(FfxFsr2Interface* backendInterface, FfxPipelineState* pipeline)
|
|
{
|
|
FFX_ASSERT(backendInterface != nullptr);
|
|
if (!pipeline)
|
|
return FFX_OK;
|
|
|
|
BackendContext_VK* backendContext = (BackendContext_VK*)backendInterface->scratchBuffer;
|
|
|
|
// destroy pipeline
|
|
VkPipeline computePipeline = reinterpret_cast<VkPipeline>(pipeline->pipeline);
|
|
if (computePipeline) {
|
|
backendContext->vkFunctionTable.vkDestroyPipeline(backendContext->device, computePipeline, nullptr);
|
|
pipeline->pipeline = nullptr;
|
|
}
|
|
|
|
BackendContext_VK::PipelineLayout* pipelineLayout = reinterpret_cast<BackendContext_VK::PipelineLayout*>(pipeline->rootSignature);
|
|
if (pipelineLayout) {
|
|
// destroy descriptor sets
|
|
for (uint32_t i = 0; i < FSR2_MAX_QUEUED_FRAMES; i++)
|
|
pipelineLayout->descriptorSets[i] = nullptr;
|
|
|
|
// destroy descriptor set layout
|
|
if (pipelineLayout->descriptorSetLayout)
|
|
{
|
|
backendContext->vkFunctionTable.vkDestroyDescriptorSetLayout(backendContext->device, pipelineLayout->descriptorSetLayout, nullptr);
|
|
pipelineLayout->descriptorSetLayout = nullptr;
|
|
}
|
|
|
|
// destroy pipeline layout
|
|
if (pipelineLayout->pipelineLayout)
|
|
{
|
|
backendContext->vkFunctionTable.vkDestroyPipelineLayout(backendContext->device, pipelineLayout->pipelineLayout, nullptr);
|
|
pipelineLayout->pipelineLayout = nullptr;
|
|
}
|
|
}
|
|
|
|
return FFX_OK;
|
|
}
|