#include "RenderAPI.h" #include "PlatformBase.h" #if SUPPORT_VULKAN #include #include #include #include #include #include // 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(vkCreateRenderPass); \ apply(vkCreateRenderPass2KHR); \ 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 cur_vrs_Index = 0; static bool blend_state_hook_enable; 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[cur_vrs_Index]; 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[cur_vrs_Index]; // 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 VKAPI_ATTR VkResult VKAPI_CALL Hook_vkCreateRenderPass( // VkDevice device, // const VkRenderPassCreateInfo* pCreateInfo, // const VkAllocationCallbacks* pAllocator, // VkRenderPass* pRenderPass // ) // { // auto ret = vkCreateRenderPass(device, pCreateInfo, pAllocator, pRenderPass); // char buf[80]; // sprintf(buf, "vkCreateRenderPass---1: %p \n", *pRenderPass); // unityLog(buf); // return ret; // } static std::set need_create_pso_renderpasses; static std::set created_pipeline_shader_module; static VKAPI_ATTR VkResult VKAPI_CALL Hook_vkCreateRenderPass2KHR( VkDevice device, const VkRenderPassCreateInfo2* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass ) { //if (blend_state_hook_enable) //{ // unityLog("Hook_vkCreateRenderPass called."); // if (pCreateInfo->attachmentCount == 2) // { // const_cast(pCreateInfo)->attachmentCount = 3; // VkAttachmentDescription2 color_attachments[] = { // pCreateInfo->pAttachments[0], // pCreateInfo->pAttachments[0], // pCreateInfo->pAttachments[1], // }; // const_cast(pCreateInfo)->pAttachments = color_attachments; // const_cast(pCreateInfo->pSubpasses)->colorAttachmentCount = 2; // VkAttachmentReference2 color_attachment_refs[] = { // pCreateInfo->pSubpasses->pColorAttachments[0], // pCreateInfo->pSubpasses->pColorAttachments[0], // }; // color_attachment_refs[1].attachment = 1; // const_cast(pCreateInfo->pSubpasses)->pColorAttachments = color_attachment_refs; // } //} auto ret = vkCreateRenderPass2KHR(device, pCreateInfo, pAllocator, pRenderPass); if(const_cast(pCreateInfo)->attachmentCount > 2) { need_create_pso_renderpasses.insert(*pRenderPass); } return ret; } static VKAPI_ATTR VkResult VKAPI_CALL Hook_vkCreateGraphicsPipelines( VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines) { if (blend_state_hook_enable) { VkResult ret; auto shader_module = pCreateInfos->pStages[1].module; auto cur_pass = pCreateInfos->renderPass; if(!created_pipeline_shader_module.contains(shader_module)) { created_pipeline_shader_module.insert(shader_module); for (const auto i : need_create_pso_renderpasses) { const_cast(pCreateInfos)->renderPass = static_cast(i); char buf[80]; sprintf(buf, "vkCreateGraphicsPipelines---: %p : %p\n", pCreateInfos->pStages[1].module, pCreateInfos->renderPass); unityLog(buf); ret = vkCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines); } } // const_cast(pCreateInfos)->renderPass = static_cast(cur_pass); // ret = vkCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines); return ret; } return vkCreateGraphicsPipelines(device, pipelineCache, createInfoCount, pCreateInfos, pAllocator, pPipelines); } static int FindMemoryTypeIndex(VkPhysicalDeviceMemoryProperties const &physicalDeviceMemoryProperties, VkMemoryRequirements const &memoryRequirements, VkMemoryPropertyFlags memoryPropertyFlags) { uint32_t memoryTypeBits = memoryRequirements.memoryTypeBits; // Search memtypes to find first cur_vrs_Index 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); INTERCEPT(vkCreateGraphicsPipelines); INTERCEPT(vkCreateRenderPass2KHR); #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()) { vulkanInterface->AddInterceptInitialization(InterceptVulkanInitialization, NULL, 0); } else if (IUnityGraphicsVulkan *vulkanInterface = interfaces->Get()) { 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(void* data) 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; virtual void SetVKPSOHook(bool enable) override; private: typedef std::vector VulkanBuffers; typedef std::map 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 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(); 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 supported_extensions; uint32_t property_cnt; HMS_XEG_EnumerateDeviceExtensionProperties(physic_device, &property_cnt, nullptr); if (property_cnt > 0) { std::vector 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; } // 查询是否支持时域AI超分 if (std::find(supported_extensions.begin(), supported_extensions.end(), XEG_TEMPORAL_UPSCALE_EXTENSION_NAME) == supported_extensions.end()) { fl[GraphicsFeature::HW_AI_TEMPORAL_SR] = 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 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(void* data) { int vrs_enum = *(int*) data; cur_vrs_Index = vrs_enum; vrs_fragment_size = vrs_argment_size_table[vrs_enum]; 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() { } void RenderAPI_Vulkan::SetVKPSOHook(bool enable) { blend_state_hook_enable = enable; } #endif // #if SUPPORT_VULKAN