2024-12-11 17:42:08 +08:00

556 lines
20 KiB
C++

#include "RenderAPI.h"
#include "PlatformBase.h"
#if SUPPORT_VULKAN
#include <string>
#include <map>
#include <vector>
#include <math.h>
#include <algorithm>
// This plugin does not link to the Vulkan loader, easier to support multiple APIs and systems that don't have Vulkan support
#define VK_NO_PROTOTYPES
#include "Unity/IUnityGraphicsVulkan.h"
#define UNITY_USED_VULKAN_API_FUNCTIONS(apply) \
apply(vkCreateInstance); \
apply(vkCmdBeginRenderPass); \
apply(vkCreateBuffer); \
apply(vkGetPhysicalDeviceMemoryProperties); \
apply(vkGetBufferMemoryRequirements); \
apply(vkMapMemory); \
apply(vkBindBufferMemory); \
apply(vkAllocateMemory); \
apply(vkDestroyBuffer); \
apply(vkFreeMemory); \
apply(vkUnmapMemory); \
apply(vkQueueWaitIdle); \
apply(vkDeviceWaitIdle); \
apply(vkCmdCopyBufferToImage); \
apply(vkFlushMappedMemoryRanges); \
apply(vkCreatePipelineLayout); \
apply(vkCreateShaderModule); \
apply(vkDestroyShaderModule); \
apply(vkCreateGraphicsPipelines); \
apply(vkCmdBindPipeline); \
apply(vkCmdDraw); \
apply(vkCmdPushConstants); \
apply(vkCmdBindVertexBuffers); \
apply(vkDestroyPipeline); \
apply(vkCmdSetFragmentShadingRateKHR); \
apply(vkCmdBindDescriptorSets); \
apply(vkCreateDevice); \
apply(vkGetPhysicalDeviceFeatures2); \
apply(vkGetPhysicalDeviceProperties2); \
apply(vkEnumerateDeviceExtensionProperties); \
apply(vkGetPhysicalDeviceProperties2KHR); \
apply(vkDestroyPipelineLayout);
#define VULKAN_DEFINE_API_FUNCPTR(func) static PFN_##func func
VULKAN_DEFINE_API_FUNCPTR(vkGetInstanceProcAddr);
UNITY_USED_VULKAN_API_FUNCTIONS(VULKAN_DEFINE_API_FUNCPTR);
#undef VULKAN_DEFINE_API_FUNCPTR
#if OHOS
#include "xengine/xeg_vulkan_adaptive_vrs.h"
#include "xengine/xeg_vulkan_spatial_upscale.h"
#include "xengine/xeg_vulkan_extension.h"
#endif
extern void unityLog(const char *msg);
enum VRSPluginShadingRate
{
X1_PER_PIXEL,
X1_PER_2X1_PIXELS,
X1_PER_1X2_PIXELS,
X1_PER_2X2_PIXELS,
X1_PER_4X2_PIXELS,
X1_PER_2X4_PIXELS,
X1_PER_4X4_PIXELS,
};
static VkExtent2D vrs_argment_size_table[] = {
VkExtent2D{1, 1},
VkExtent2D{2, 1},
VkExtent2D{1, 2},
VkExtent2D{2, 2},
VkExtent2D{4, 2},
VkExtent2D{2, 4},
VkExtent2D{4, 4},
};
static VkExtent2D vrs_fragment_size = vrs_argment_size_table[0];
static bool vrs_enable;
static int curVrsIndex = 0;
static void LoadVulkanAPI(PFN_vkGetInstanceProcAddr getInstanceProcAddr, VkInstance instance)
{
unityLog("LoadVulkanAPI called.");
if (!vkGetInstanceProcAddr && getInstanceProcAddr)
vkGetInstanceProcAddr = getInstanceProcAddr;
if (!vkCreateInstance)
vkCreateInstance = (PFN_vkCreateInstance)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance");
#define LOAD_VULKAN_FUNC(fn) \
if (!fn) \
fn = (PFN_##fn)vkGetInstanceProcAddr(instance, #fn)
UNITY_USED_VULKAN_API_FUNCTIONS(LOAD_VULKAN_FUNC);
#undef LOAD_VULKAN_FUNC
}
static VKAPI_ATTR void VKAPI_CALL Hook_vkCmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo *pRenderPassBegin, VkSubpassContents contents)
{
// Change this to 'true' to override the clear color with green
const bool allowOverrideClearColor = false;
if (pRenderPassBegin->clearValueCount <= 16 && pRenderPassBegin->clearValueCount > 0 && allowOverrideClearColor)
{
VkClearValue clearValues[16] = {};
memcpy(clearValues, pRenderPassBegin->pClearValues, pRenderPassBegin->clearValueCount * sizeof(VkClearValue));
VkRenderPassBeginInfo patchedBeginInfo = *pRenderPassBegin;
patchedBeginInfo.pClearValues = clearValues;
for (unsigned int i = 0; i < pRenderPassBegin->clearValueCount - 1; ++i)
{
clearValues[i].color.float32[0] = 0.0;
clearValues[i].color.float32[1] = 1.0;
clearValues[i].color.float32[2] = 0.0;
clearValues[i].color.float32[3] = 1.0;
}
vkCmdBeginRenderPass(commandBuffer, &patchedBeginInfo, contents);
}
else
{
vkCmdBeginRenderPass(commandBuffer, pRenderPassBegin, contents);
}
}
static VKAPI_ATTR VkResult VKAPI_CALL Hook_vkCreateInstance(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance)
{
unityLog("Hook_vkCreateInstance called.");
if (pCreateInfo->ppEnabledExtensionNames)
{
for (size_t i = 0; i < pCreateInfo->enabledExtensionCount; i++)
{
unityLog(pCreateInfo->ppEnabledExtensionNames[i]);
}
}
vkCreateInstance = (PFN_vkCreateInstance)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vkCreateInstance");
VkResult result = vkCreateInstance(pCreateInfo, pAllocator, pInstance);
if (result == VK_SUCCESS)
LoadVulkanAPI(vkGetInstanceProcAddr, *pInstance);
return result;
}
static VKAPI_ATTR VkResult VKAPI_CALL Hook_vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDevice *pDevice)
{
if (pCreateInfo->pNext)
{
unityLog("pCreateInfo->pNext");
}
return vkCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice);
}
static VKAPI_ATTR void VKAPI_CALL Hook_vkCmdBindDescriptorSets(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint,
VkPipelineLayout layout, uint32_t firstSet,
uint32_t descriptorSetCount, const VkDescriptorSet *pDescriptorSets,
uint32_t dynamicOffsetCount, const uint32_t *pDynamicOffsets)
{
if (vrs_enable)
{
VkExtent2D fragment_size = vrs_argment_size_table[curVrsIndex];
VkFragmentShadingRateCombinerOpKHR combiner_ops[2];
// If shading rate from attachment is enabled, we set the combiner, so that the values from the attachment are used
// Combiner for pipeline (A) and primitive (B) - Not used in this sample
combiner_ops[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_KHR;
// Combiner for pipeline (A) and attachment (B), replace the pipeline default value (fragment_size) with the fragment sizes stored in the attachment
combiner_ops[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_KHR;
vkCmdSetFragmentShadingRateKHR(commandBuffer, &fragment_size, combiner_ops);
}
vkCmdBindDescriptorSets(commandBuffer, pipelineBindPoint, layout, firstSet, descriptorSetCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets);
}
static VKAPI_ATTR void VKAPI_CALL Hook_vkCmdBindPipeline(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline)
{
// if (vrs_enable)
//{
// VkExtent2D fragment_size = vrs_argment_size_table[curVrsIndex];
// VkFragmentShadingRateCombinerOpKHR combiner_ops[2];
// // If shading rate from attachment is enabled, we set the combiner, so that the values from the attachment are used
// // Combiner for pipeline (A) and primitive (B) - Not used in this sample
// combiner_ops[0] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_KHR;
// // Combiner for pipeline (A) and attachment (B), replace the pipeline default value (fragment_size) with the fragment sizes stored in the attachment
// combiner_ops[1] = VK_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_KHR;
// vkCmdSetFragmentShadingRateKHR(commandBuffer, &fragment_size, combiner_ops);
//}
vkCmdBindPipeline(commandBuffer, pipelineBindPoint, pipeline);
}
static int FindMemoryTypeIndex(VkPhysicalDeviceMemoryProperties const &physicalDeviceMemoryProperties, VkMemoryRequirements const &memoryRequirements, VkMemoryPropertyFlags memoryPropertyFlags)
{
uint32_t memoryTypeBits = memoryRequirements.memoryTypeBits;
// Search memtypes to find first curVrsIndex with those properties
for (uint32_t memoryTypeIndex = 0; memoryTypeIndex < VK_MAX_MEMORY_TYPES; ++memoryTypeIndex)
{
if ((memoryTypeBits & 1) == 1)
{
// Type is available, does it match user properties?
if ((physicalDeviceMemoryProperties.memoryTypes[memoryTypeIndex].propertyFlags & memoryPropertyFlags) == memoryPropertyFlags)
return memoryTypeIndex;
}
memoryTypeBits >>= 1;
}
return -1;
}
static VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL Hook_vkGetInstanceProcAddr(VkInstance device, const char *funcName)
{
if (!funcName)
return NULL;
unityLog(funcName);
#define INTERCEPT(fn) \
if (strcmp(funcName, #fn) == 0) \
return (PFN_vkVoidFunction) & Hook_##fn
INTERCEPT(vkCreateInstance);
INTERCEPT(vkCreateDevice);
INTERCEPT(vkCmdBindPipeline);
INTERCEPT(vkCmdBindDescriptorSets);
#undef INTERCEPT
return (PFN_vkVoidFunction)vkGetInstanceProcAddr(device, funcName);
}
static PFN_vkGetInstanceProcAddr UNITY_INTERFACE_API InterceptVulkanInitialization(PFN_vkGetInstanceProcAddr getInstanceProcAddr, void *)
{
vkGetInstanceProcAddr = getInstanceProcAddr;
return Hook_vkGetInstanceProcAddr;
}
extern "C" void RenderAPI_Vulkan_OnPluginLoad(IUnityInterfaces *interfaces)
{
if (IUnityGraphicsVulkanV2 *vulkanInterface = interfaces->Get<IUnityGraphicsVulkanV2>())
{
vulkanInterface->AddInterceptInitialization(InterceptVulkanInitialization, NULL, 0);
}
else if (IUnityGraphicsVulkan *vulkanInterface = interfaces->Get<IUnityGraphicsVulkan>())
{
vulkanInterface->InterceptInitialization(InterceptVulkanInitialization, NULL);
}
}
struct VulkanBuffer
{
VkBuffer buffer;
VkDeviceMemory deviceMemory;
void *mapped;
VkDeviceSize sizeInBytes;
VkDeviceSize deviceMemorySize;
VkMemoryPropertyFlags deviceMemoryFlags;
};
class RenderAPI_Vulkan : public RenderAPI
{
public:
RenderAPI_Vulkan();
virtual ~RenderAPI_Vulkan() {}
virtual void processDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces *interfaces) override;
virtual bool getUsesReverseZ() override { return true; }
virtual void enableVRS(int vrsEnum) override;
virtual void disableVRS() override;
virtual void enableFGExtrapolation(void* data) override;
virtual void preFGExtrapolation() override;
virtual bool doFGExtrapolation(void *src, void *data, void *dst) override;
virtual void postFGExtrapolation() override;
virtual void disableFGExtrapolation() override;
private:
typedef std::vector<VulkanBuffer> VulkanBuffers;
typedef std::map<unsigned long long, VulkanBuffers> DeleteQueue;
private:
bool createVulkanBuffer(size_t bytes, VulkanBuffer *buffer, VkBufferUsageFlags usage);
void immediateDestroyVulkanBuffer(const VulkanBuffer &buffer);
void safeDestroy(unsigned long long frameNumber, const VulkanBuffer &buffer);
void garbageCollect(bool force = false);
virtual void initSupportFeature() override;
private:
IUnityGraphicsVulkan *unity_vulkan;
UnityVulkanInstance unity_vk_instance;
std::map<unsigned long long, VulkanBuffers> m_DeleteQueue;
};
RenderAPI *CreateRenderAPI_Vulkan()
{
return new RenderAPI_Vulkan();
}
RenderAPI_Vulkan::RenderAPI_Vulkan()
: unity_vulkan(NULL)
{
}
void RenderAPI_Vulkan::processDeviceEvent(UnityGfxDeviceEventType type, IUnityInterfaces *interfaces)
{
switch (type)
{
case kUnityGfxDeviceEventInitialize:
unityLog("ProcessDeviceEvent called. kUnityGfxDeviceEventInitialize");
unity_vulkan = interfaces->Get<IUnityGraphicsVulkan>();
unity_vk_instance = unity_vulkan->Instance();
// Make sure Vulkan API functions are loaded
LoadVulkanAPI(unity_vk_instance.getInstanceProcAddr, unity_vk_instance.instance);
UnityVulkanPluginEventConfig config_1;
config_1.graphicsQueueAccess = kUnityVulkanGraphicsQueueAccess_DontCare;
config_1.renderPassPrecondition = kUnityVulkanRenderPass_EnsureInside;
config_1.flags = kUnityVulkanEventConfigFlag_EnsurePreviousFrameSubmission | kUnityVulkanEventConfigFlag_ModifiesCommandBuffersState;
unity_vulkan->ConfigureEvent(1, &config_1);
// alternative way to intercept API
unity_vulkan->InterceptVulkanAPI("vkCmdBeginRenderPass", (PFN_vkVoidFunction)Hook_vkCmdBeginRenderPass);
initSupportFeature();
break;
case kUnityGfxDeviceEventShutdown:
unityLog("ProcessDeviceEvent called. kUnityGfxDeviceEventShutdown");
if (unity_vk_instance.device != VK_NULL_HANDLE)
{
garbageCollect(true);
}
unity_vulkan = NULL;
unity_vk_instance = UnityVulkanInstance();
break;
default:
break;
}
}
bool RenderAPI_Vulkan::createVulkanBuffer(size_t sizeInBytes, VulkanBuffer *buffer, VkBufferUsageFlags usage)
{
if (sizeInBytes == 0)
return false;
VkBufferCreateInfo bufferCreateInfo;
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferCreateInfo.pNext = NULL;
bufferCreateInfo.pQueueFamilyIndices = &unity_vk_instance.queueFamilyIndex;
bufferCreateInfo.queueFamilyIndexCount = 1;
bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
bufferCreateInfo.usage = usage;
bufferCreateInfo.flags = 0;
bufferCreateInfo.size = sizeInBytes;
*buffer = VulkanBuffer();
if (vkCreateBuffer(unity_vk_instance.device, &bufferCreateInfo, NULL, &buffer->buffer) != VK_SUCCESS)
return false;
VkPhysicalDeviceMemoryProperties physicalDeviceProperties;
vkGetPhysicalDeviceMemoryProperties(unity_vk_instance.physicalDevice, &physicalDeviceProperties);
VkMemoryRequirements memoryRequirements;
vkGetBufferMemoryRequirements(unity_vk_instance.device, buffer->buffer, &memoryRequirements);
const int memoryTypeIndex = FindMemoryTypeIndex(physicalDeviceProperties, memoryRequirements, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
if (memoryTypeIndex < 0)
{
immediateDestroyVulkanBuffer(*buffer);
return false;
}
VkMemoryAllocateInfo memoryAllocateInfo;
memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memoryAllocateInfo.pNext = NULL;
memoryAllocateInfo.memoryTypeIndex = memoryTypeIndex;
memoryAllocateInfo.allocationSize = memoryRequirements.size;
if (vkAllocateMemory(unity_vk_instance.device, &memoryAllocateInfo, NULL, &buffer->deviceMemory) != VK_SUCCESS)
{
immediateDestroyVulkanBuffer(*buffer);
return false;
}
if (vkMapMemory(unity_vk_instance.device, buffer->deviceMemory, 0, VK_WHOLE_SIZE, 0, &buffer->mapped) != VK_SUCCESS)
{
immediateDestroyVulkanBuffer(*buffer);
return false;
}
if (vkBindBufferMemory(unity_vk_instance.device, buffer->buffer, buffer->deviceMemory, 0) != VK_SUCCESS)
{
immediateDestroyVulkanBuffer(*buffer);
return false;
}
buffer->sizeInBytes = sizeInBytes;
buffer->deviceMemoryFlags = physicalDeviceProperties.memoryTypes[memoryTypeIndex].propertyFlags;
buffer->deviceMemorySize = memoryAllocateInfo.allocationSize;
return true;
}
void RenderAPI_Vulkan::immediateDestroyVulkanBuffer(const VulkanBuffer &buffer)
{
if (buffer.buffer != VK_NULL_HANDLE)
vkDestroyBuffer(unity_vk_instance.device, buffer.buffer, NULL);
if (buffer.mapped && buffer.deviceMemory != VK_NULL_HANDLE)
vkUnmapMemory(unity_vk_instance.device, buffer.deviceMemory);
if (buffer.deviceMemory != VK_NULL_HANDLE)
vkFreeMemory(unity_vk_instance.device, buffer.deviceMemory, NULL);
}
void RenderAPI_Vulkan::safeDestroy(unsigned long long frameNumber, const VulkanBuffer &buffer)
{
m_DeleteQueue[frameNumber].push_back(buffer);
}
void RenderAPI_Vulkan::garbageCollect(bool force /*= false*/)
{
UnityVulkanRecordingState recordingState;
if (force)
recordingState.safeFrameNumber = ~0ull;
else if (!unity_vulkan->CommandRecordingState(&recordingState, kUnityVulkanGraphicsQueueAccess_DontCare))
return;
DeleteQueue::iterator it = m_DeleteQueue.begin();
while (it != m_DeleteQueue.end())
{
if (it->first <= recordingState.safeFrameNumber)
{
for (size_t i = 0; i < it->second.size(); ++i)
immediateDestroyVulkanBuffer(it->second[i]);
m_DeleteQueue.erase(it++);
}
else
++it;
}
}
#if OHOS
void checkXEngine(VkPhysicalDevice physic_device, SupportFeatureList &fl)
{
std::vector<std::string> supported_extensions;
uint32_t property_cnt;
HMS_XEG_EnumerateDeviceExtensionProperties(physic_device, &property_cnt, nullptr);
if (property_cnt > 0)
{
std::vector<XEG_ExtensionProperties> properties(property_cnt);
if (HMS_XEG_EnumerateDeviceExtensionProperties(physic_device, &property_cnt,
&properties.front()) == VK_SUCCESS)
{
for (auto ext : properties)
{
supported_extensions.push_back(ext.extensionName);
}
}
}
if (std::find(supported_extensions.begin(), supported_extensions.end(), XEG_SPATIAL_UPSCALE_EXTENSION_NAME) !=
supported_extensions.end())
{
fl[GraphicsFeature::HW_SPATIAL_SR] = true;
}
if (std::find(supported_extensions.begin(), supported_extensions.end(), XEG_ADAPTIVE_VRS_EXTENSION_NAME) !=
supported_extensions.end())
{
fl[GraphicsFeature::HW_ADAPTIVE_VRS] = true;
}
}
#endif
void RenderAPI_Vulkan::initSupportFeature()
{
auto physic_device = unity_vk_instance.physicalDevice;
uint32_t extension_cnt;
vkEnumerateDeviceExtensionProperties(physic_device, nullptr, &extension_cnt, nullptr);
std::vector<VkExtensionProperties> available_extensions(extension_cnt);
vkEnumerateDeviceExtensionProperties(physic_device, nullptr, &extension_cnt, available_extensions.data());
VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_feature{};
vrs_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR;
VkPhysicalDeviceFeatures2 features2{};
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
features2.pNext = &vrs_feature;
vkGetPhysicalDeviceFeatures2(physic_device, &features2);
support_features[GraphicsFeature::VRS_DRAW] = vrs_feature.pipelineFragmentShadingRate;
support_features[GraphicsFeature::VRS_PRIMITIVE] = vrs_feature.primitiveFragmentShadingRate;
support_features[GraphicsFeature::VRS_ATTACHMENT] = vrs_feature.attachmentFragmentShadingRate;
#if OHOS
checkXEngine(physic_device, support_features);
#endif
// char buf[40];
// sprintf(buf, "initSupportFeature VRS %d %d %d \n", (int)support_vrs_draw, (int)support_vrs_primitive, (int)support_vrs_attachment);
// unityLog(buf);
}
void RenderAPI_Vulkan::enableVRS(int vrsEnum)
{
curVrsIndex = vrsEnum;
vrs_fragment_size = vrs_argment_size_table[vrsEnum];
vrs_enable = true;
}
void RenderAPI_Vulkan::disableVRS()
{
vrs_enable = false;
}
void RenderAPI_Vulkan::enableFGExtrapolation(void* data)
{
}
void RenderAPI_Vulkan::preFGExtrapolation()
{
}
bool RenderAPI_Vulkan::doFGExtrapolation(void *src, void *data, void *dst)
{
// ???
// unity_vulkan->EnsureOutsideRenderPass();
UnityVulkanImage image;
if (unity_vulkan->AccessTexture(src, UnityVulkanWholeImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT, kUnityVulkanResourceAccess_PipelineBarrier, &image))
{
}
return false;
}
void RenderAPI_Vulkan::postFGExtrapolation()
{
}
void RenderAPI_Vulkan::disableFGExtrapolation()
{
}
#endif // #if SUPPORT_VULKAN