diff options
Diffstat (limited to 'libs/hwui/renderthread/VulkanManager.cpp')
-rw-r--r-- | libs/hwui/renderthread/VulkanManager.cpp | 717 |
1 files changed, 584 insertions, 133 deletions
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 21c91a26745b..f96b1f897921 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -16,7 +16,6 @@ #include "VulkanManager.h" -#include "DeviceInfo.h" #include "Properties.h" #include "RenderThread.h" #include "renderstate/RenderState.h" @@ -25,50 +24,293 @@ #include <GrBackendSurface.h> #include <GrContext.h> #include <GrTypes.h> +#include <GrTypes.h> +#include <vk/GrVkExtensions.h> #include <vk/GrVkTypes.h> namespace android { namespace uirenderer { namespace renderthread { -#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F) -#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F) +#define GET_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F) +#define GET_INST_PROC(F) m##F = (PFN_vk##F)vkGetInstanceProcAddr(mInstance, "vk" #F) +#define GET_DEV_PROC(F) m##F = (PFN_vk##F)vkGetDeviceProcAddr(mDevice, "vk" #F) VulkanManager::VulkanManager(RenderThread& thread) : mRenderThread(thread) {} void VulkanManager::destroy() { - if (!hasVkContext()) return; - - mRenderThread.renderState().onVkContextDestroyed(); 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(mBackendContext->fDevice, mCommandPool, nullptr); + mDestroyCommandPool(mDevice, mCommandPool, nullptr); mCommandPool = VK_NULL_HANDLE; } - mBackendContext.reset(); + + if (mDevice != VK_NULL_HANDLE) { + mDeviceWaitIdle(mDevice); + mDestroyDevice(mDevice, nullptr); + } + + if (mInstance != VK_NULL_HANDLE) { + mDestroyInstance(mInstance, nullptr); + } + + mGraphicsQueue = VK_NULL_HANDLE; + mPresentQueue = VK_NULL_HANDLE; + mDevice = VK_NULL_HANDLE; + mPhysicalDevice = VK_NULL_HANDLE; + mInstance = VK_NULL_HANDLE; } -void VulkanManager::initialize() { - if (hasVkContext()) { - return; +bool VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFeatures2& features) { + VkResult err; + + constexpr VkApplicationInfo app_info = { + VK_STRUCTURE_TYPE_APPLICATION_INFO, // sType + nullptr, // pNext + "android framework", // pApplicationName + 0, // applicationVersion + "android framework", // pEngineName + 0, // engineVerison + VK_MAKE_VERSION(1, 1, 0), // apiVersion + }; + + std::vector<const char*> instanceExtensions; + { + GET_PROC(EnumerateInstanceExtensionProperties); + + uint32_t extensionCount = 0; + err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); + if (VK_SUCCESS != err) { + return false; + } + std::unique_ptr<VkExtensionProperties[]> extensions( + new VkExtensionProperties[extensionCount]); + err = mEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.get()); + if (VK_SUCCESS != err) { + return false; + } + bool hasKHRSurfaceExtension = false; + bool hasKHRAndroidSurfaceExtension = false; + for (uint32_t i = 0; i < extensionCount; ++i) { + instanceExtensions.push_back(extensions[i].extensionName); + if (!strcmp(extensions[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) { + hasKHRSurfaceExtension = true; + } + if (!strcmp(extensions[i].extensionName,VK_KHR_ANDROID_SURFACE_EXTENSION_NAME)) { + hasKHRAndroidSurfaceExtension = true; + } + } + if (!hasKHRSurfaceExtension || !hasKHRAndroidSurfaceExtension) { + this->destroy(); + return false; + } + } + + const VkInstanceCreateInfo instance_create = { + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType + nullptr, // pNext + 0, // flags + &app_info, // pApplicationInfo + 0, // enabledLayerNameCount + nullptr, // ppEnabledLayerNames + (uint32_t) instanceExtensions.size(), // enabledExtensionNameCount + instanceExtensions.data(), // ppEnabledExtensionNames + }; + + GET_PROC(CreateInstance); + err = mCreateInstance(&instance_create, nullptr, &mInstance); + if (err < 0) { + this->destroy(); + return false; } - auto canPresent = [](VkInstance, VkPhysicalDevice, uint32_t) { return true; }; + GET_INST_PROC(DestroyInstance); + GET_INST_PROC(EnumeratePhysicalDevices); + GET_INST_PROC(GetPhysicalDeviceProperties); + GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties); + GET_INST_PROC(GetPhysicalDeviceFeatures2); + GET_INST_PROC(CreateDevice); + GET_INST_PROC(EnumerateDeviceExtensionProperties); + GET_INST_PROC(CreateAndroidSurfaceKHR); + GET_INST_PROC(DestroySurfaceKHR); + GET_INST_PROC(GetPhysicalDeviceSurfaceSupportKHR); + GET_INST_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR); + GET_INST_PROC(GetPhysicalDeviceSurfaceFormatsKHR); + GET_INST_PROC(GetPhysicalDeviceSurfacePresentModesKHR); + + uint32_t gpuCount; + err = mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr); + if (err) { + this->destroy(); + return false; + } + if (!gpuCount) { + this->destroy(); + return false; + } + // Just returning the first physical device instead of getting the whole array. Since there + // should only be one device on android. + gpuCount = 1; + err = mEnumeratePhysicalDevices(mInstance, &gpuCount, &mPhysicalDevice); + // VK_INCOMPLETE is returned when the count we provide is less than the total device count. + if (err && VK_INCOMPLETE != err) { + this->destroy(); + return false; + } - mBackendContext.reset(GrVkBackendContext::Create(vkGetInstanceProcAddr, vkGetDeviceProcAddr, - &mPresentQueueIndex, canPresent)); - LOG_ALWAYS_FATAL_IF(!mBackendContext.get()); + VkPhysicalDeviceProperties physDeviceProperties; + mGetPhysicalDeviceProperties(mPhysicalDevice, &physDeviceProperties); + if (physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) { + this->destroy(); + return false; + } - // Get all the addresses of needed vulkan functions - VkInstance instance = mBackendContext->fInstance; - VkDevice device = mBackendContext->fDevice; - GET_PROC(CreateAndroidSurfaceKHR); - GET_PROC(DestroySurfaceKHR); - GET_PROC(GetPhysicalDeviceSurfaceSupportKHR); - GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR); - GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR); - GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR); + // query to get the initial queue props size + uint32_t queueCount; + mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr); + if (!queueCount) { + this->destroy(); + return false; + } + + // now get the actual queue props + std::unique_ptr<VkQueueFamilyProperties[]> queueProps(new VkQueueFamilyProperties[queueCount]); + mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, queueProps.get()); + + // iterate to find the graphics queue + mGraphicsQueueIndex = queueCount; + for (uint32_t i = 0; i < queueCount; i++) { + if (queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { + mGraphicsQueueIndex = i; + break; + } + } + if (mGraphicsQueueIndex == queueCount) { + this->destroy(); + return false; + } + + // All physical devices and queue families on Android must be capable of + // presentation with any native window. So just use the first one. + mPresentQueueIndex = 0; + + std::vector<const char*> deviceExtensions; + { + uint32_t extensionCount = 0; + err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount, + nullptr); + if (VK_SUCCESS != err) { + this->destroy(); + return false; + } + std::unique_ptr<VkExtensionProperties[]> extensions( + new VkExtensionProperties[extensionCount]); + err = mEnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr, &extensionCount, + extensions.get()); + if (VK_SUCCESS != err) { + this->destroy(); + return false; + } + bool hasKHRSwapchainExtension = false; + for (uint32_t i = 0; i < extensionCount; ++i) { + deviceExtensions.push_back(extensions[i].extensionName); + if (!strcmp(extensions[i].extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) { + hasKHRSwapchainExtension = true; + } + } + if (!hasKHRSwapchainExtension) { + this->destroy(); + return false; + } + } + + auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) { + if (device != VK_NULL_HANDLE) { + return vkGetDeviceProcAddr(device, proc_name); + } + return vkGetInstanceProcAddr(instance, proc_name); + }; + 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; + + // Setup all extension feature structs we may want to use. + void** tailPNext = &features.pNext; + + if (grExtensions.hasExtension(VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME, 2)) { + VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT* blend; + blend = (VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT*) malloc( + sizeof(VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT)); + LOG_ALWAYS_FATAL_IF(!blend); + blend->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT; + blend->pNext = nullptr; + *tailPNext = blend; + tailPNext = &blend->pNext; + } + + // query to get the physical device features + mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features); + // this looks like it would slow things down, + // and we can't depend on it on all platforms + features.features.robustBufferAccess = VK_FALSE; + + float queuePriorities[1] = { 0.0 }; + + const VkDeviceQueueCreateInfo queueInfo[2] = { + { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType + nullptr, // pNext + 0, // VkDeviceQueueCreateFlags + mGraphicsQueueIndex, // queueFamilyIndex + 1, // queueCount + queuePriorities, // pQueuePriorities + }, + { + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType + nullptr, // pNext + 0, // VkDeviceQueueCreateFlags + mPresentQueueIndex, // queueFamilyIndex + 1, // queueCount + queuePriorities, // pQueuePriorities + } + }; + uint32_t queueInfoCount = (mPresentQueueIndex != mGraphicsQueueIndex) ? 2 : 1; + + const VkDeviceCreateInfo deviceInfo = { + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, // sType + &features, // pNext + 0, // VkDeviceCreateFlags + queueInfoCount, // queueCreateInfoCount + queueInfo, // pQueueCreateInfos + 0, // layerCount + nullptr, // ppEnabledLayerNames + (uint32_t) deviceExtensions.size(), // extensionCount + deviceExtensions.data(), // ppEnabledExtensionNames + nullptr, // ppEnabledFeatures + }; + + err = mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice); + if (err) { + this->destroy(); + return false; + } + + GET_DEV_PROC(GetDeviceQueue); + GET_DEV_PROC(DeviceWaitIdle); + GET_DEV_PROC(DestroyDevice); GET_DEV_PROC(CreateSwapchainKHR); GET_DEV_PROC(DestroySwapchainKHR); GET_DEV_PROC(GetSwapchainImagesKHR); @@ -88,39 +330,103 @@ void VulkanManager::initialize() { 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); GET_DEV_PROC(ResetFences); + return true; +} + +static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) { + // All Vulkan structs that could be part of the features chain will start with the + // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader + // so we can get access to the pNext for the next struct. + struct CommonVulkanHeader { + VkStructureType sType; + void* pNext; + }; + + void* pNext = features.pNext; + while (pNext) { + void* current = pNext; + pNext = static_cast<CommonVulkanHeader*>(current)->pNext; + free(current); + } +} + +void VulkanManager::initialize() { + if (mDevice != VK_NULL_HANDLE) { + return; + } + + GET_PROC(EnumerateInstanceVersion); + uint32_t instanceVersion = 0; + LOG_ALWAYS_FATAL_IF(mEnumerateInstanceVersion(&instanceVersion)); + LOG_ALWAYS_FATAL_IF(instanceVersion < VK_MAKE_VERSION(1, 1, 0)); + + GrVkExtensions extensions; + VkPhysicalDeviceFeatures2 features; + LOG_ALWAYS_FATAL_IF(!this->setupDevice(extensions, features)); + + mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); + + auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) { + if (device != VK_NULL_HANDLE) { + return vkGetDeviceProcAddr(device, proc_name); + } + return vkGetInstanceProcAddr(instance, proc_name); + }; + + GrVkBackendContext backendContext; + backendContext.fInstance = mInstance; + backendContext.fPhysicalDevice = mPhysicalDevice; + backendContext.fDevice = mDevice; + backendContext.fQueue = mGraphicsQueue; + backendContext.fGraphicsQueueIndex = mGraphicsQueueIndex; + backendContext.fInstanceVersion = instanceVersion; + backendContext.fVkExtensions = &extensions; + backendContext.fDeviceFeatures2 = &features; + backendContext.fGetProc = std::move(getProc); + // create the command pool for the command buffers if (VK_NULL_HANDLE == mCommandPool) { VkCommandPoolCreateInfo commandPoolInfo; memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo)); commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; // this needs to be on the render queue - commandPoolInfo.queueFamilyIndex = mBackendContext->fGraphicsQueueIndex; + commandPoolInfo.queueFamilyIndex = mGraphicsQueueIndex; commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - SkDEBUGCODE(VkResult res =) mCreateCommandPool(mBackendContext->fDevice, &commandPoolInfo, - nullptr, &mCommandPool); + SkDEBUGCODE(VkResult res =) mCreateCommandPool(mDevice, &commandPoolInfo, nullptr, + &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(mBackendContext->fDevice, mPresentQueueIndex, 0, &mPresentQueue); + mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue); GrContextOptions options; options.fDisableDistanceFieldPaths = true; - mRenderThread.cacheManager().configureContext(&options); - sk_sp<GrContext> grContext(GrContext::MakeVulkan(mBackendContext, options)); + // TODO: get a string describing the SPIR-V compiler version and use it here + mRenderThread.cacheManager().configureContext(&options, nullptr, 0); + sk_sp<GrContext> grContext(GrContext::MakeVulkan(backendContext, options)); LOG_ALWAYS_FATAL_IF(!grContext.get()); mRenderThread.setGrContext(grContext); - DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize()); + + free_features_extensions_structs(features); if (Properties::enablePartialUpdates && Properties::useBufferAge) { mSwapBehavior = SwapBehavior::BufferAge; } - - mRenderThread.renderState().onVkContextCreated(); } // Returns the next BackbufferInfo to use for the next draw. The function will make sure all @@ -138,8 +444,7 @@ VulkanSurface::BackbufferInfo* VulkanManager::getAvailableBackbuffer(VulkanSurfa // Before we reuse a backbuffer, make sure its fences have all signaled so that we can safely // reuse its commands buffers. - VkResult res = - mWaitForFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX); + VkResult res = mWaitForFences(mDevice, 2, backbuffer->mUsageFences, true, UINT64_MAX); if (res != VK_SUCCESS) { return nullptr; } @@ -153,12 +458,12 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { VkResult res; - res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences); + res = mResetFences(mDevice, 2, backbuffer->mUsageFences); SkASSERT(VK_SUCCESS == res); // The acquire will signal the attached mAcquireSemaphore. We use this to know the image has // finished presenting and that it is safe to begin sending new commands to the returned image. - res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX, + res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX, backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex); @@ -173,11 +478,11 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { return nullptr; } backbuffer = getAvailableBackbuffer(surface); - res = mResetFences(mBackendContext->fDevice, 2, backbuffer->mUsageFences); + res = mResetFences(mDevice, 2, backbuffer->mUsageFences); SkASSERT(VK_SUCCESS == res); // acquire the image - res = mAcquireNextImageKHR(mBackendContext->fDevice, surface->mSwapchain, UINT64_MAX, + res = mAcquireNextImageKHR(mDevice, surface->mSwapchain, UINT64_MAX, backbuffer->mAcquireSemaphore, VK_NULL_HANDLE, &backbuffer->mImageIndex); @@ -189,13 +494,11 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { // set up layout transfer from initial to color attachment VkImageLayout layout = surface->mImageInfos[backbuffer->mImageIndex].mImageLayout; SkASSERT(VK_IMAGE_LAYOUT_UNDEFINED == layout || VK_IMAGE_LAYOUT_PRESENT_SRC_KHR == layout); - VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) - ? VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT - : VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - VkAccessFlags srcAccessMask = - (VK_IMAGE_LAYOUT_UNDEFINED == layout) ? 0 : VK_ACCESS_MEMORY_READ_BIT; - VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkAccessFlags srcAccessMask = 0; + VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType @@ -205,7 +508,7 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { layout, // oldLayout VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout mPresentQueueIndex, // srcQueueFamilyIndex - mBackendContext->fGraphicsQueueIndex, // dstQueueFamilyIndex + mGraphicsQueueIndex, // dstQueueFamilyIndex surface->mImages[backbuffer->mImageIndex], // image {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange }; @@ -236,14 +539,17 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { submitInfo.signalSemaphoreCount = 0; // Attach first fence to submission here so we can track when the command buffer finishes. - mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[0]); + mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[0]); // We need to notify Skia that we changed the layout of the wrapped VkImage - GrVkImageInfo* imageInfo; sk_sp<SkSurface> skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface; - skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo, - SkSurface::kFlushRead_BackendHandleAccess); - imageInfo->updateImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget( + SkSurface::kFlushRead_BackendHandleAccess); + if (!backendRT.isValid()) { + SkASSERT(backendRT.isValid()); + return nullptr; + } + backendRT.setVkImageLayout(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); surface->mBackbuffer = std::move(skSurface); return surface->mBackbuffer.get(); @@ -252,17 +558,14 @@ SkSurface* VulkanManager::getBackbufferSurface(VulkanSurface* surface) { void VulkanManager::destroyBuffers(VulkanSurface* surface) { if (surface->mBackbuffers) { for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) { - mWaitForFences(mBackendContext->fDevice, 2, surface->mBackbuffers[i].mUsageFences, true, - UINT64_MAX); + mWaitForFences(mDevice, 2, surface->mBackbuffers[i].mUsageFences, true, UINT64_MAX); surface->mBackbuffers[i].mImageIndex = -1; - mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mAcquireSemaphore, - nullptr); - mDestroySemaphore(mBackendContext->fDevice, surface->mBackbuffers[i].mRenderSemaphore, - nullptr); - mFreeCommandBuffers(mBackendContext->fDevice, mCommandPool, 2, - surface->mBackbuffers[i].mTransitionCmdBuffers); - mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[0], 0); - mDestroyFence(mBackendContext->fDevice, surface->mBackbuffers[i].mUsageFences[1], 0); + mDestroySemaphore(mDevice, surface->mBackbuffers[i].mAcquireSemaphore, nullptr); + mDestroySemaphore(mDevice, surface->mBackbuffers[i].mRenderSemaphore, nullptr); + mFreeCommandBuffers(mDevice, mCommandPool, 2, + surface->mBackbuffers[i].mTransitionCmdBuffers); + mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[0], 0); + mDestroyFence(mDevice, surface->mBackbuffers[i].mUsageFences[1], 0); } } @@ -279,29 +582,27 @@ void VulkanManager::destroySurface(VulkanSurface* surface) { if (VK_NULL_HANDLE != mPresentQueue) { mQueueWaitIdle(mPresentQueue); } - mDeviceWaitIdle(mBackendContext->fDevice); + mDeviceWaitIdle(mDevice); destroyBuffers(surface); if (VK_NULL_HANDLE != surface->mSwapchain) { - mDestroySwapchainKHR(mBackendContext->fDevice, surface->mSwapchain, nullptr); + mDestroySwapchainKHR(mDevice, surface->mSwapchain, nullptr); surface->mSwapchain = VK_NULL_HANDLE; } if (VK_NULL_HANDLE != surface->mVkSurface) { - mDestroySurfaceKHR(mBackendContext->fInstance, surface->mVkSurface, nullptr); + mDestroySurfaceKHR(mInstance, surface->mVkSurface, nullptr); surface->mVkSurface = VK_NULL_HANDLE; } delete surface; } void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExtent2D extent) { - mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount, - nullptr); + mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, nullptr); SkASSERT(surface->mImageCount); surface->mImages = new VkImage[surface->mImageCount]; - mGetSwapchainImagesKHR(mBackendContext->fDevice, surface->mSwapchain, &surface->mImageCount, - surface->mImages); + mGetSwapchainImagesKHR(mDevice, surface->mSwapchain, &surface->mImageCount, surface->mImages); SkSurfaceProps props(0, kUnknown_SkPixelGeometry); @@ -320,7 +621,9 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt VulkanSurface::ImageInfo& imageInfo = surface->mImageInfos[i]; imageInfo.mSurface = SkSurface::MakeFromBackendRenderTarget( - mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin, nullptr, &props); + mRenderThread.getGrContext(), backendRT, kTopLeft_GrSurfaceOrigin, + surface->mColorMode == ColorMode::WideColorGamut ? kRGBA_F16_SkColorType + : kRGBA_8888_SkColorType, nullptr, &props); } SkASSERT(mCommandPool != VK_NULL_HANDLE); @@ -350,15 +653,15 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt for (uint32_t i = 0; i < surface->mImageCount + 1; ++i) { SkDEBUGCODE(VkResult res); surface->mBackbuffers[i].mImageIndex = -1; - SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr, + SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &surface->mBackbuffers[i].mAcquireSemaphore); - SkDEBUGCODE(res =) mCreateSemaphore(mBackendContext->fDevice, &semaphoreInfo, nullptr, + SkDEBUGCODE(res =) mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &surface->mBackbuffers[i].mRenderSemaphore); - SkDEBUGCODE(res =) mAllocateCommandBuffers(mBackendContext->fDevice, &commandBuffersInfo, + SkDEBUGCODE(res =) mAllocateCommandBuffers(mDevice, &commandBuffersInfo, surface->mBackbuffers[i].mTransitionCmdBuffers); - SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr, + SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr, &surface->mBackbuffers[i].mUsageFences[0]); - SkDEBUGCODE(res =) mCreateFence(mBackendContext->fDevice, &fenceInfo, nullptr, + SkDEBUGCODE(res =) mCreateFence(mDevice, &fenceInfo, nullptr, &surface->mBackbuffers[i].mUsageFences[1]); SkASSERT(VK_SUCCESS == res); } @@ -368,35 +671,35 @@ void VulkanManager::createBuffers(VulkanSurface* surface, VkFormat format, VkExt bool VulkanManager::createSwapchain(VulkanSurface* surface) { // check for capabilities VkSurfaceCapabilitiesKHR caps; - VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mBackendContext->fPhysicalDevice, + VkResult res = mGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice, surface->mVkSurface, &caps); if (VK_SUCCESS != res) { return false; } uint32_t surfaceFormatCount; - res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface, + res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface, &surfaceFormatCount, nullptr); if (VK_SUCCESS != res) { return false; } FatVector<VkSurfaceFormatKHR, 4> surfaceFormats(surfaceFormatCount); - res = mGetPhysicalDeviceSurfaceFormatsKHR(mBackendContext->fPhysicalDevice, surface->mVkSurface, + res = mGetPhysicalDeviceSurfaceFormatsKHR(mPhysicalDevice, surface->mVkSurface, &surfaceFormatCount, surfaceFormats.data()); if (VK_SUCCESS != res) { return false; } uint32_t presentModeCount; - res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice, + res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice, surface->mVkSurface, &presentModeCount, nullptr); if (VK_SUCCESS != res) { return false; } FatVector<VkPresentModeKHR, VK_PRESENT_MODE_RANGE_SIZE_KHR> presentModes(presentModeCount); - res = mGetPhysicalDeviceSurfacePresentModesKHR(mBackendContext->fPhysicalDevice, + res = mGetPhysicalDeviceSurfacePresentModesKHR(mPhysicalDevice, surface->mVkSurface, &presentModeCount, presentModes.data()); if (VK_SUCCESS != res) { @@ -435,37 +738,27 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - // Pick our surface format. For now, just make sure it matches our sRGB request: - VkFormat surfaceFormat = VK_FORMAT_UNDEFINED; + VkFormat surfaceFormat = VK_FORMAT_R8G8B8A8_UNORM; VkColorSpaceKHR colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; - - bool wantSRGB = false; -#ifdef ANDROID_ENABLE_LINEAR_BLENDING - wantSRGB = true; -#endif + if (surface->mColorMode == ColorMode::WideColorGamut) { + surfaceFormat = VK_FORMAT_R16G16B16A16_SFLOAT; + colorSpace = VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT; + } + bool foundSurfaceFormat = false; for (uint32_t i = 0; i < surfaceFormatCount; ++i) { - // We are assuming we can get either R8G8B8A8_UNORM or R8G8B8A8_SRGB - VkFormat desiredFormat = wantSRGB ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM; - if (desiredFormat == surfaceFormats[i].format) { - surfaceFormat = surfaceFormats[i].format; - colorSpace = surfaceFormats[i].colorSpace; + if (surfaceFormat == surfaceFormats[i].format + && colorSpace == surfaceFormats[i].colorSpace) { + foundSurfaceFormat = true; + break; } } - if (VK_FORMAT_UNDEFINED == surfaceFormat) { + if (!foundSurfaceFormat) { return false; } - // If mailbox mode is available, use it, as it is the lowest-latency non- - // tearing mode. If not, fall back to FIFO which is always available. + // FIFO is always available and will match what we do on GL so just pick that here. VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR; - for (uint32_t i = 0; i < presentModeCount; ++i) { - // use mailbox - if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) { - mode = presentModes[i]; - break; - } - } VkSwapchainCreateInfoKHR swapchainCreateInfo; memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR)); @@ -478,8 +771,8 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { swapchainCreateInfo.imageArrayLayers = 1; swapchainCreateInfo.imageUsage = usageFlags; - uint32_t queueFamilies[] = {mBackendContext->fGraphicsQueueIndex, mPresentQueueIndex}; - if (mBackendContext->fGraphicsQueueIndex != mPresentQueueIndex) { + uint32_t queueFamilies[] = {mGraphicsQueueIndex, mPresentQueueIndex}; + if (mGraphicsQueueIndex != mPresentQueueIndex) { swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; swapchainCreateInfo.queueFamilyIndexCount = 2; swapchainCreateInfo.pQueueFamilyIndices = queueFamilies; @@ -495,19 +788,18 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { swapchainCreateInfo.clipped = true; swapchainCreateInfo.oldSwapchain = surface->mSwapchain; - res = mCreateSwapchainKHR(mBackendContext->fDevice, &swapchainCreateInfo, nullptr, - &surface->mSwapchain); + res = mCreateSwapchainKHR(mDevice, &swapchainCreateInfo, nullptr, &surface->mSwapchain); if (VK_SUCCESS != res) { return false; } // destroy the old swapchain if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) { - mDeviceWaitIdle(mBackendContext->fDevice); + mDeviceWaitIdle(mDevice); destroyBuffers(surface); - mDestroySwapchainKHR(mBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr); + mDestroySwapchainKHR(mDevice, swapchainCreateInfo.oldSwapchain, nullptr); } createBuffers(surface, surfaceFormat, extent); @@ -515,14 +807,14 @@ bool VulkanManager::createSwapchain(VulkanSurface* surface) { return true; } -VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) { +VulkanSurface* VulkanManager::createSurface(ANativeWindow* window, ColorMode colorMode) { initialize(); if (!window) { return nullptr; } - VulkanSurface* surface = new VulkanSurface(); + VulkanSurface* surface = new VulkanSurface(colorMode); VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); @@ -531,20 +823,18 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) { surfaceCreateInfo.flags = 0; surfaceCreateInfo.window = window; - VkResult res = mCreateAndroidSurfaceKHR(mBackendContext->fInstance, &surfaceCreateInfo, nullptr, - &surface->mVkSurface); + VkResult res = mCreateAndroidSurfaceKHR(mInstance, &surfaceCreateInfo, nullptr, + &surface->mVkSurface); if (VK_SUCCESS != res) { delete surface; return nullptr; } SkDEBUGCODE(VkBool32 supported; res = mGetPhysicalDeviceSurfaceSupportKHR( - mBackendContext->fPhysicalDevice, mPresentQueueIndex, - surface->mVkSurface, &supported); - // All physical devices and queue families on Android must be capable of - // presentation with any - // native window. - SkASSERT(VK_SUCCESS == res && supported);); + mPhysicalDevice, mPresentQueueIndex, surface->mVkSurface, &supported); + // All physical devices and queue families on Android must be capable of + // presentation with any native window. + SkASSERT(VK_SUCCESS == res && supported);); if (!createSwapchain(surface)) { destroySurface(surface); @@ -555,17 +845,19 @@ VulkanSurface* VulkanManager::createSurface(ANativeWindow* window) { } // Helper to know which src stage flags we need to set when transitioning to the present layout -static VkPipelineStageFlags layoutToPipelineStageFlags(const VkImageLayout layout) { +static VkPipelineStageFlags layoutToPipelineSrcStageFlags(const VkImageLayout layout) { if (VK_IMAGE_LAYOUT_GENERAL == layout) { return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; } else if (VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL == layout || VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL == layout) { return VK_PIPELINE_STAGE_TRANSFER_BIT; - } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout || - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout || - VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout || - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) { - return VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + } else if (VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL == layout) { + return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + } else if (VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL == layout || + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL == layout) { + return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + } else if (VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL == layout) { + return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; } else if (VK_IMAGE_LAYOUT_PREINITIALIZED == layout) { return VK_PIPELINE_STAGE_HOST_BIT; } @@ -601,26 +893,31 @@ static VkAccessFlags layoutToSrcAccessMask(const VkImageLayout layout) { void VulkanManager::swapBuffers(VulkanSurface* surface) { if (CC_UNLIKELY(Properties::waitForGpuCompletion)) { ATRACE_NAME("Finishing GPU work"); - mDeviceWaitIdle(mBackendContext->fDevice); + mDeviceWaitIdle(mDevice); } SkASSERT(surface->mBackbuffers); VulkanSurface::BackbufferInfo* backbuffer = surface->mBackbuffers + surface->mCurrentBackbufferIndex; - GrVkImageInfo* imageInfo; + SkSurface* skSurface = surface->mImageInfos[backbuffer->mImageIndex].mSurface.get(); - skSurface->getRenderTargetHandle((GrBackendObject*)&imageInfo, - SkSurface::kFlushRead_BackendHandleAccess); + GrBackendRenderTarget backendRT = skSurface->getBackendRenderTarget( + SkSurface::kFlushRead_BackendHandleAccess); + SkASSERT(backendRT.isValid()); + + GrVkImageInfo imageInfo; + SkAssertResult(backendRT.getVkImageInfo(&imageInfo)); + // Check to make sure we never change the actually wrapped image - SkASSERT(imageInfo->fImage == surface->mImages[backbuffer->mImageIndex]); + SkASSERT(imageInfo.fImage == surface->mImages[backbuffer->mImageIndex]); // We need to transition the image to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR and make sure that all // previous work is complete for before presenting. So we first add the necessary barrier here. - VkImageLayout layout = imageInfo->fImageLayout; - VkPipelineStageFlags srcStageMask = layoutToPipelineStageFlags(layout); + VkImageLayout layout = imageInfo.fImageLayout; + VkPipelineStageFlags srcStageMask = layoutToPipelineSrcStageFlags(layout); VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; VkAccessFlags srcAccessMask = layoutToSrcAccessMask(layout); - VkAccessFlags dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + VkAccessFlags dstAccessMask = 0; VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType @@ -629,7 +926,7 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { dstAccessMask, // inputMask layout, // oldLayout VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout - mBackendContext->fGraphicsQueueIndex, // srcQueueFamilyIndex + mGraphicsQueueIndex, // srcQueueFamilyIndex mPresentQueueIndex, // dstQueueFamilyIndex surface->mImages[backbuffer->mImageIndex], // image {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // subresourceRange @@ -661,7 +958,7 @@ void VulkanManager::swapBuffers(VulkanSurface* surface) { submitInfo.pSignalSemaphores = &backbuffer->mRenderSemaphore; // Attach second fence to submission here so we can track when the command buffer finishes. - mQueueSubmit(mBackendContext->fQueue, 1, &submitInfo, backbuffer->mUsageFences[1]); + mQueueSubmit(mGraphicsQueue, 1, &submitInfo, backbuffer->mUsageFences[1]); // Submit present operation to present queue. We use a semaphore here to make sure all rendering // to the image is complete and that the layout has been change to present on the graphics @@ -697,6 +994,160 @@ 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) { + if (!hasVkContext()) { + ALOGE("VulkanManager::fenceWait: VkDevice not initialized"); + return INVALID_OPERATION; + } + + // 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); + return OK; +} + +status_t VulkanManager::createReleaseFence(sp<Fence>& nativeFence) { + if (!hasVkContext()) { + ALOGE("VulkanManager::createReleaseFence: VkDevice not initialized"); + return INVALID_OPERATION; + } + + 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; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ |