Loading libs/hwui/renderthread/VulkanManager.cpp +185 −7 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ #include "VulkanManager.h" #include <private/gui/SyncFeatures.h> #include "Properties.h" #include "RenderThread.h" #include "renderstate/RenderState.h" Loading @@ -41,6 +43,10 @@ VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { mRenderThread.setGrContext(nullptr); // We don't need to explicitly free the command buffer since it automatically gets freed when we // delete the VkCommandPool below. mDummyCB = VK_NULL_HANDLE; if (VK_NULL_HANDLE != mCommandPool) { mDestroyCommandPool(mDevice, mCommandPool, nullptr); mCommandPool = VK_NULL_HANDLE; Loading Loading @@ -226,6 +232,11 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(), instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data()); if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { this->destroy(); return false; } memset(&features, 0, sizeof(VkPhysicalDeviceFeatures2)); features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features.pNext = nullptr; Loading Loading @@ -313,6 +324,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe GET_DEV_PROC(DeviceWaitIdle); GET_DEV_PROC(CreateSemaphore); GET_DEV_PROC(DestroySemaphore); GET_DEV_PROC(ImportSemaphoreFdKHR); GET_DEV_PROC(GetSemaphoreFdKHR); GET_DEV_PROC(CreateFence); GET_DEV_PROC(DestroyFence); GET_DEV_PROC(WaitForFences); Loading Loading @@ -384,6 +397,14 @@ void VulkanManager::initialize() { &mCommandPool); SkASSERT(VK_SUCCESS == res); } LOG_ALWAYS_FATAL_IF(mCommandPool == VK_NULL_HANDLE); if (!setupDummyCommandBuffer()) { this->destroy(); return; } LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue); Loading Loading @@ -976,19 +997,176 @@ int VulkanManager::getAge(VulkanSurface* surface) { return surface->mCurrentTime - lastUsed; } bool VulkanManager::setupDummyCommandBuffer() { if (mDummyCB != VK_NULL_HANDLE) { return true; } VkCommandBufferAllocateInfo commandBuffersInfo; memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo)); commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; commandBuffersInfo.pNext = nullptr; commandBuffersInfo.commandPool = mCommandPool; commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; commandBuffersInfo.commandBufferCount = 1; VkResult err = mAllocateCommandBuffers(mDevice, &commandBuffersInfo, &mDummyCB); if (err != VK_SUCCESS) { // It is probably unnecessary to set this back to VK_NULL_HANDLE, but we set it anyways to // make sure the driver didn't set a value and then return a failure. mDummyCB = VK_NULL_HANDLE; return false; } VkCommandBufferBeginInfo beginInfo; memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo)); beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; mBeginCommandBuffer(mDummyCB, &beginInfo); mEndCommandBuffer(mDummyCB); return true; } status_t VulkanManager::fenceWait(sp<Fence>& fence) { //TODO: Insert a wait on fence command into the Vulkan command buffer. if (!hasVkContext()) { ALOGE("VulkanManager::fenceWait: VkDevice not initialized"); return INVALID_OPERATION; } if (SyncFeatures::getInstance().useWaitSync() && SyncFeatures::getInstance().useNativeFenceSync()) { // Block GPU on the fence. int fenceFd = fence->dup(); if (fenceFd == -1) { ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno); return -errno; } VkSemaphoreCreateInfo semaphoreInfo; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreInfo.pNext = nullptr; semaphoreInfo.flags = 0; VkSemaphore semaphore; VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); if (VK_SUCCESS != err) { ALOGE("Failed to create import semaphore, err: %d", err); return UNKNOWN_ERROR; } VkImportSemaphoreFdInfoKHR importInfo; importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; importInfo.pNext = nullptr; importInfo.semaphore = semaphore; importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; importInfo.fd = fenceFd; err = mImportSemaphoreFdKHR(mDevice, &importInfo); if (VK_SUCCESS != err) { ALOGE("Failed to import semaphore, err: %d", err); return UNKNOWN_ERROR; } LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; VkSubmitInfo submitInfo; memset(&submitInfo, 0, sizeof(VkSubmitInfo)); submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 1; // Wait to make sure aquire semaphore set above has signaled. submitInfo.pWaitSemaphores = &semaphore; submitInfo.pWaitDstStageMask = &waitDstStageFlags; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &mDummyCB; submitInfo.signalSemaphoreCount = 0; mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); // On Android when we import a semaphore, it is imported using temporary permanence. That // means as soon as we queue the semaphore for a wait it reverts to its previous permanent // state before importing. This means it will now be in an idle state with no pending // signal or wait operations, so it is safe to immediately delete it. mDestroySemaphore(mDevice, semaphore, nullptr); } else { // Block CPU on the fence. status_t err = fence->waitForever("VulkanManager::fenceWait"); if (err != NO_ERROR) { ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); return err; } } return OK; } status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence) { //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed. if (!hasVkContext()) { ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized"); return INVALID_OPERATION; } if (SyncFeatures::getInstance().useFenceSync()) { ALOGE("VulkanManager::createReleaseFence: Vk backend doesn't support non-native fences"); return INVALID_OPERATION; } if (!SyncFeatures::getInstance().useNativeFenceSync()) { return OK; } VkExportSemaphoreCreateInfo exportInfo; exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; exportInfo.pNext = nullptr; exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; VkSemaphoreCreateInfo semaphoreInfo; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreInfo.pNext = &exportInfo; semaphoreInfo.flags = 0; VkSemaphore semaphore; VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); if (VK_SUCCESS != err) { ALOGE("VulkanManager::createReleaseFence: Failed to create semaphore"); return INVALID_OPERATION; } LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); VkSubmitInfo submitInfo; memset(&submitInfo, 0, sizeof(VkSubmitInfo)); submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &mDummyCB; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &semaphore; mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); VkSemaphoreGetFdInfoKHR getFdInfo; getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; getFdInfo.pNext = nullptr; getFdInfo.semaphore = semaphore; getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; int fenceFd = 0; err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); if (VK_SUCCESS != err) { ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd"); return INVALID_OPERATION; } nativeFence = new Fence(fenceFd); // Exporting a semaphore with copy transference via vkGetSemahporeFdKHR, has the same effect of // destroying the semaphore and creating a new one with the same handle, and the payloads // ownership is move to the Fd we created. Thus the semahpore is in a state that we can delete // it and we don't need to wait on the command buffer we submitted to finish. mDestroySemaphore(mDevice, semaphore, nullptr); return OK; } Loading libs/hwui/renderthread/VulkanManager.h +6 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,8 @@ private: VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface); bool setupDummyCommandBuffer(); // simple wrapper class that exists only to initialize a pointer to NULL template <typename FNPTR_TYPE> class VkPtr { Loading Loading @@ -199,6 +201,8 @@ private: VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore; VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore; VkPtr<PFN_vkImportSemaphoreFdKHR> mImportSemaphoreFdKHR; VkPtr<PFN_vkGetSemaphoreFdKHR> mGetSemaphoreFdKHR; VkPtr<PFN_vkCreateFence> mCreateFence; VkPtr<PFN_vkDestroyFence> mDestroyFence; VkPtr<PFN_vkWaitForFences> mWaitForFences; Loading @@ -216,6 +220,8 @@ private: VkQueue mPresentQueue = VK_NULL_HANDLE; VkCommandPool mCommandPool = VK_NULL_HANDLE; VkCommandBuffer mDummyCB = VK_NULL_HANDLE; enum class SwapBehavior { Discard, BufferAge, Loading Loading
libs/hwui/renderthread/VulkanManager.cpp +185 −7 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ #include "VulkanManager.h" #include <private/gui/SyncFeatures.h> #include "Properties.h" #include "RenderThread.h" #include "renderstate/RenderState.h" Loading @@ -41,6 +43,10 @@ VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { mRenderThread.setGrContext(nullptr); // We don't need to explicitly free the command buffer since it automatically gets freed when we // delete the VkCommandPool below. mDummyCB = VK_NULL_HANDLE; if (VK_NULL_HANDLE != mCommandPool) { mDestroyCommandPool(mDevice, mCommandPool, nullptr); mCommandPool = VK_NULL_HANDLE; Loading Loading @@ -226,6 +232,11 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe grExtensions.init(getProc, mInstance, mPhysicalDevice, instanceExtensions.size(), instanceExtensions.data(), deviceExtensions.size(), deviceExtensions.data()); if (!grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) { this->destroy(); return false; } memset(&features, 0, sizeof(VkPhysicalDeviceFeatures2)); features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features.pNext = nullptr; Loading Loading @@ -313,6 +324,8 @@ bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe GET_DEV_PROC(DeviceWaitIdle); GET_DEV_PROC(CreateSemaphore); GET_DEV_PROC(DestroySemaphore); GET_DEV_PROC(ImportSemaphoreFdKHR); GET_DEV_PROC(GetSemaphoreFdKHR); GET_DEV_PROC(CreateFence); GET_DEV_PROC(DestroyFence); GET_DEV_PROC(WaitForFences); Loading Loading @@ -384,6 +397,14 @@ void VulkanManager::initialize() { &mCommandPool); SkASSERT(VK_SUCCESS == res); } LOG_ALWAYS_FATAL_IF(mCommandPool == VK_NULL_HANDLE); if (!setupDummyCommandBuffer()) { this->destroy(); return; } LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue); Loading Loading @@ -976,19 +997,176 @@ int VulkanManager::getAge(VulkanSurface* surface) { return surface->mCurrentTime - lastUsed; } bool VulkanManager::setupDummyCommandBuffer() { if (mDummyCB != VK_NULL_HANDLE) { return true; } VkCommandBufferAllocateInfo commandBuffersInfo; memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo)); commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; commandBuffersInfo.pNext = nullptr; commandBuffersInfo.commandPool = mCommandPool; commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; commandBuffersInfo.commandBufferCount = 1; VkResult err = mAllocateCommandBuffers(mDevice, &commandBuffersInfo, &mDummyCB); if (err != VK_SUCCESS) { // It is probably unnecessary to set this back to VK_NULL_HANDLE, but we set it anyways to // make sure the driver didn't set a value and then return a failure. mDummyCB = VK_NULL_HANDLE; return false; } VkCommandBufferBeginInfo beginInfo; memset(&beginInfo, 0, sizeof(VkCommandBufferBeginInfo)); beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT; mBeginCommandBuffer(mDummyCB, &beginInfo); mEndCommandBuffer(mDummyCB); return true; } status_t VulkanManager::fenceWait(sp<Fence>& fence) { //TODO: Insert a wait on fence command into the Vulkan command buffer. if (!hasVkContext()) { ALOGE("VulkanManager::fenceWait: VkDevice not initialized"); return INVALID_OPERATION; } if (SyncFeatures::getInstance().useWaitSync() && SyncFeatures::getInstance().useNativeFenceSync()) { // Block GPU on the fence. int fenceFd = fence->dup(); if (fenceFd == -1) { ALOGE("VulkanManager::fenceWait: error dup'ing fence fd: %d", errno); return -errno; } VkSemaphoreCreateInfo semaphoreInfo; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreInfo.pNext = nullptr; semaphoreInfo.flags = 0; VkSemaphore semaphore; VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); if (VK_SUCCESS != err) { ALOGE("Failed to create import semaphore, err: %d", err); return UNKNOWN_ERROR; } VkImportSemaphoreFdInfoKHR importInfo; importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; importInfo.pNext = nullptr; importInfo.semaphore = semaphore; importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT; importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; importInfo.fd = fenceFd; err = mImportSemaphoreFdKHR(mDevice, &importInfo); if (VK_SUCCESS != err) { ALOGE("Failed to import semaphore, err: %d", err); return UNKNOWN_ERROR; } LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; VkSubmitInfo submitInfo; memset(&submitInfo, 0, sizeof(VkSubmitInfo)); submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 1; // Wait to make sure aquire semaphore set above has signaled. submitInfo.pWaitSemaphores = &semaphore; submitInfo.pWaitDstStageMask = &waitDstStageFlags; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &mDummyCB; submitInfo.signalSemaphoreCount = 0; mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); // On Android when we import a semaphore, it is imported using temporary permanence. That // means as soon as we queue the semaphore for a wait it reverts to its previous permanent // state before importing. This means it will now be in an idle state with no pending // signal or wait operations, so it is safe to immediately delete it. mDestroySemaphore(mDevice, semaphore, nullptr); } else { // Block CPU on the fence. status_t err = fence->waitForever("VulkanManager::fenceWait"); if (err != NO_ERROR) { ALOGE("VulkanManager::fenceWait: error waiting for fence: %d", err); return err; } } return OK; } status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence) { //TODO: Create a fence that is signaled, when all the pending Vulkan commands are flushed. if (!hasVkContext()) { ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized"); return INVALID_OPERATION; } if (SyncFeatures::getInstance().useFenceSync()) { ALOGE("VulkanManager::createReleaseFence: Vk backend doesn't support non-native fences"); return INVALID_OPERATION; } if (!SyncFeatures::getInstance().useNativeFenceSync()) { return OK; } VkExportSemaphoreCreateInfo exportInfo; exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; exportInfo.pNext = nullptr; exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; VkSemaphoreCreateInfo semaphoreInfo; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreInfo.pNext = &exportInfo; semaphoreInfo.flags = 0; VkSemaphore semaphore; VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); if (VK_SUCCESS != err) { ALOGE("VulkanManager::createReleaseFence: Failed to create semaphore"); return INVALID_OPERATION; } LOG_ALWAYS_FATAL_IF(mDummyCB == VK_NULL_HANDLE); VkSubmitInfo submitInfo; memset(&submitInfo, 0, sizeof(VkSubmitInfo)); submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &mDummyCB; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = &semaphore; mQueueSubmit(mGraphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); VkSemaphoreGetFdInfoKHR getFdInfo; getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; getFdInfo.pNext = nullptr; getFdInfo.semaphore = semaphore; getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; int fenceFd = 0; err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); if (VK_SUCCESS != err) { ALOGE("VulkanManager::createReleaseFence: Failed to get semaphore Fd"); return INVALID_OPERATION; } nativeFence = new Fence(fenceFd); // Exporting a semaphore with copy transference via vkGetSemahporeFdKHR, has the same effect of // destroying the semaphore and creating a new one with the same handle, and the payloads // ownership is move to the Fd we created. Thus the semahpore is in a state that we can delete // it and we don't need to wait on the command buffer we submitted to finish. mDestroySemaphore(mDevice, semaphore, nullptr); return OK; } Loading
libs/hwui/renderthread/VulkanManager.h +6 −0 Original line number Diff line number Diff line Loading @@ -139,6 +139,8 @@ private: VulkanSurface::BackbufferInfo* getAvailableBackbuffer(VulkanSurface* surface); bool setupDummyCommandBuffer(); // simple wrapper class that exists only to initialize a pointer to NULL template <typename FNPTR_TYPE> class VkPtr { Loading Loading @@ -199,6 +201,8 @@ private: VkPtr<PFN_vkCreateSemaphore> mCreateSemaphore; VkPtr<PFN_vkDestroySemaphore> mDestroySemaphore; VkPtr<PFN_vkImportSemaphoreFdKHR> mImportSemaphoreFdKHR; VkPtr<PFN_vkGetSemaphoreFdKHR> mGetSemaphoreFdKHR; VkPtr<PFN_vkCreateFence> mCreateFence; VkPtr<PFN_vkDestroyFence> mDestroyFence; VkPtr<PFN_vkWaitForFences> mWaitForFences; Loading @@ -216,6 +220,8 @@ private: VkQueue mPresentQueue = VK_NULL_HANDLE; VkCommandPool mCommandPool = VK_NULL_HANDLE; VkCommandBuffer mDummyCB = VK_NULL_HANDLE; enum class SwapBehavior { Discard, BufferAge, Loading