gfxstream: Add Vulkan external memory resource import implementation for host. Developed for QNX use-case, but applies generally to any platform that supports external memory for import but not for export. Some robustness around VK_QNX_external_memory_screen_buffer API usage added with the updateExternalMemoryInfo() function. BUG=319510663 TEST=compile && launch_cvd --gpu_mode=gfxstream_guest_angle Change-Id: I7b10b70da024df60881c8b27ea29bd44101b95f6
diff --git a/host/vulkan/VkCommonOperations.cpp b/host/vulkan/VkCommonOperations.cpp index 3a9fc12..a48d4e2 100644 --- a/host/vulkan/VkCommonOperations.cpp +++ b/host/vulkan/VkCommonOperations.cpp
@@ -96,6 +96,7 @@ static android::base::StaticLock sVkEmulationLock; +#if !defined(__QNX__) VK_EXT_MEMORY_HANDLE dupExternalMemory(VK_EXT_MEMORY_HANDLE h) { #ifdef _WIN32 auto myProcessHandle = GetCurrentProcess(); @@ -109,6 +110,7 @@ return dup(h); #endif } +#endif bool getStagingMemoryTypeIndex(VulkanDispatch* vk, VkDevice device, const VkPhysicalDeviceMemoryProperties* memProps, @@ -545,6 +547,8 @@ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, #ifdef _WIN32 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, +#elif defined(__QNX__) + VK_QNX_EXTERNAL_MEMORY_SCREEN_BUFFER_EXTENSION_NAME, #else VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, #endif @@ -767,12 +771,18 @@ ivk->vkEnumerateDeviceExtensionProperties(physdevs[i], nullptr, &deviceExtensionCount, deviceExts.data()); - deviceInfos[i].supportsExternalMemory = false; + deviceInfos[i].supportsExternalMemoryImport = false; + deviceInfos[i].supportsExternalMemoryExport = false; deviceInfos[i].glInteropSupported = 0; // set later if (sVkEmulation->instanceSupportsExternalMemoryCapabilities) { - deviceInfos[i].supportsExternalMemory = - extensionsSupported(deviceExts, externalMemoryDeviceExtNames); + deviceInfos[i].supportsExternalMemoryExport = + deviceInfos[i].supportsExternalMemoryImport = + extensionsSupported(deviceExts, externalMemoryDeviceExtNames); +#if defined(__QNX__) + // External memory export not supported on QNX + deviceInfos[i].supportsExternalMemoryExport = false; +#endif deviceInfos[i].supportsIdProperties = sVkEmulation->getPhysicalDeviceProperties2Func != nullptr; deviceInfos[i].supportsDriverProperties = @@ -847,10 +857,23 @@ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, }; vk_append_struct(&features2Chain, &samplerYcbcrConversionFeatures); +#if defined(__QNX__) + VkPhysicalDeviceExternalMemoryScreenBufferFeaturesQNX extMemScreenBufferFeatures = { + .sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_SCREEN_BUFFER_FEATURES_QNX, + }; + vk_append_struct(&features2Chain, &extMemScreenBufferFeatures); +#endif sVkEmulation->getPhysicalDeviceFeatures2Func(physdevs[i], &features2); deviceInfos[i].supportsSamplerYcbcrConversion = samplerYcbcrConversionFeatures.samplerYcbcrConversion == VK_TRUE; +#if defined(__QNX__) + deviceInfos[i].supportsExternalMemoryImport = + extMemScreenBufferFeatures.screenBufferImport == VK_TRUE; + } else { + deviceInfos[i].supportsExternalMemoryImport = false; +#endif } uint32_t queueFamilyCount = 0; @@ -911,7 +934,9 @@ for (uint32_t i = 0; i < physdevCount; ++i) { uint32_t deviceScore = 0; if (deviceInfos[i].hasGraphicsQueueFamily) deviceScore += 10000; - if (deviceInfos[i].supportsExternalMemory) deviceScore += 1000; + if (deviceInfos[i].supportsExternalMemoryImport || + deviceInfos[i].supportsExternalMemoryExport) + deviceScore += 1000; if (deviceInfos[i].physdevProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU || deviceInfos[i].physdevProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU) { deviceScore += 100; @@ -987,7 +1012,8 @@ std::unordered_set<const char*> selectedDeviceExtensionNames_; - if (sVkEmulation->deviceInfo.supportsExternalMemory) { + if (sVkEmulation->deviceInfo.supportsExternalMemoryImport || + sVkEmulation->deviceInfo.supportsExternalMemoryExport) { for (auto extension : externalMemoryDeviceExtNames) { selectedDeviceExtensionNames_.emplace(extension); } @@ -1027,6 +1053,20 @@ }); vk_append_struct(&deviceCiChain, samplerYcbcrConversionFeatures.get()); } +#if defined(__QNX__) + std::unique_ptr<VkPhysicalDeviceExternalMemoryScreenBufferFeaturesQNX> + extMemScreenBufferFeaturesQNX = nullptr; + if (sVkEmulation->deviceInfo.supportsExternalMemoryImport) { + extMemScreenBufferFeaturesQNX = std::make_unique< + VkPhysicalDeviceExternalMemoryScreenBufferFeaturesQNX>( + VkPhysicalDeviceExternalMemoryScreenBufferFeaturesQNX{ + .sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_SCREEN_BUFFER_FEATURES_QNX, + .screenBufferImport = VK_TRUE, + }); + vk_append_struct(&deviceCiChain, extMemScreenBufferFeaturesQNX.get()); + } +#endif ivk->vkCreateDevice(sVkEmulation->physdev, &dCi, nullptr, &sVkEmulation->device); @@ -1051,7 +1091,7 @@ } } - if (sVkEmulation->deviceInfo.supportsExternalMemory) { + if (sVkEmulation->deviceInfo.supportsExternalMemoryImport) { sVkEmulation->deviceInfo.getImageMemoryRequirements2Func = reinterpret_cast<PFN_vkGetImageMemoryRequirements2KHR>( dvk->vkGetDeviceProcAddr(sVkEmulation->device, "vkGetImageMemoryRequirements2KHR")); @@ -1066,6 +1106,8 @@ VK_EMU_INIT_RETURN_OR_ABORT_ON_ERROR(ABORT_REASON_OTHER, "Cannot find vkGetBufferMemoryRequirements2KHR"); } + } + if (sVkEmulation->deviceInfo.supportsExternalMemoryExport) { #ifdef _WIN32 sVkEmulation->deviceInfo.getMemoryHandleFunc = reinterpret_cast<PFN_vkGetMemoryWin32HandleKHR>( @@ -1359,7 +1401,7 @@ auto allocInfoChain = vk_make_chain_iterator(&allocInfo); - if (sVkEmulation->deviceInfo.supportsExternalMemory && actuallyExternal) { + if (sVkEmulation->deviceInfo.supportsExternalMemoryExport && actuallyExternal) { vk_append_struct(&allocInfoChain, &exportAi); } @@ -1441,10 +1483,11 @@ return false; } - if (!sVkEmulation->deviceInfo.supportsExternalMemory || !actuallyExternal) { + if (!sVkEmulation->deviceInfo.supportsExternalMemoryExport || !actuallyExternal) { return true; } + VkResult exportRes = VK_SUCCESS; #ifdef _WIN32 VkMemoryGetWin32HandleInfoKHR getWin32HandleInfo = { VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR, @@ -1452,28 +1495,26 @@ info->memory, VK_EXT_MEMORY_HANDLE_TYPE_BIT, }; - VkResult exportRes = sVkEmulation->deviceInfo.getMemoryHandleFunc( - sVkEmulation->device, &getWin32HandleInfo, &info->exportedHandle); -#else + exportRes = sVkEmulation->deviceInfo.getMemoryHandleFunc( + sVkEmulation->device, &getWin32HandleInfo, &info->externalHandle); +#elif !defined(__QNX__) VkMemoryGetFdInfoKHR getFdInfo = { VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, 0, info->memory, VK_EXT_MEMORY_HANDLE_TYPE_BIT, }; - VkResult exportRes = sVkEmulation->deviceInfo.getMemoryHandleFunc( - sVkEmulation->device, &getFdInfo, &info->exportedHandle); + exportRes = sVkEmulation->deviceInfo.getMemoryHandleFunc(sVkEmulation->device, &getFdInfo, + &info->externalHandle); #endif - if (exportRes != VK_SUCCESS) { + if (exportRes != VK_SUCCESS || VK_EXT_MEMORY_HANDLE_INVALID == info->externalHandle) { // LOG(VERBOSE) << "allocExternalMemory: Failed to get external memory " // "native handle: " // << exportRes; return false; } - info->actuallyExternal = true; - return true; } @@ -1497,13 +1538,13 @@ info->memory = VK_NULL_HANDLE; - if (info->exportedHandle != VK_EXT_MEMORY_HANDLE_INVALID) { + if (info->externalHandle != VK_EXT_MEMORY_HANDLE_INVALID) { #ifdef _WIN32 - CloseHandle(info->exportedHandle); -#else - close(info->exportedHandle); + CloseHandle(info->externalHandle); +#elif !defined(__QNX__) + close(info->externalHandle); #endif - info->exportedHandle = VK_EXT_MEMORY_HANDLE_INVALID; + info->externalHandle = VK_EXT_MEMORY_HANDLE_INVALID; } } @@ -1514,15 +1555,20 @@ VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, 0, VK_EXT_MEMORY_HANDLE_TYPE_BIT, - info->exportedHandle, + info->externalHandle, 0, }; +#elif defined(__QNX__) + VkImportScreenBufferInfoQNX importInfo = { + VK_STRUCTURE_TYPE_IMPORT_SCREEN_BUFFER_INFO_QNX, + info->externalHandle, + }; #else VkImportMemoryFdInfoKHR importInfo = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, 0, VK_EXT_MEMORY_HANDLE_TYPE_BIT, - dupExternalMemory(info->exportedHandle), + dupExternalMemory(info->externalHandle), }; #endif VkMemoryAllocateInfo allocInfo = { @@ -1557,15 +1603,21 @@ VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, &dedicatedInfo, VK_EXT_MEMORY_HANDLE_TYPE_BIT, - info->exportedHandle, + info->externalHandle, 0, }; +#elif defined(__QNX__) + VkImportScreenBufferInfoQNX importInfo = { + VK_STRUCTURE_TYPE_IMPORT_SCREEN_BUFFER_INFO_QNX, + &dedicatedInfo, + info->externalHandle, + }; #else VkImportMemoryFdInfoKHR importInfo = { VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, &dedicatedInfo, VK_EXT_MEMORY_HANDLE_TYPE_BIT, - info->exportedHandle, + info->externalHandle, }; #endif VkMemoryAllocateInfo allocInfo = { @@ -1794,6 +1846,43 @@ return generateColorBufferVkImageCreateInfo_locked(format, width, height, tiling); } +static bool updateExternalMemoryInfo(VK_EXT_MEMORY_HANDLE extMemHandle, + const VkMemoryRequirements* pMemReqs, + VkEmulation::ExternalMemoryInfo* pInfo) { + // Set externalHandle on the output info + pInfo->externalHandle = extMemHandle; + +#if defined(__QNX__) + VkScreenBufferPropertiesQNX screenBufferProps = { + VK_STRUCTURE_TYPE_SCREEN_BUFFER_PROPERTIES_QNX, + 0, + }; + auto vk = sVkEmulation->dvk; + VkResult queryRes = + vk->vkGetScreenBufferPropertiesQNX(sVkEmulation->device, extMemHandle, &screenBufferProps); + if (VK_SUCCESS != queryRes) { + VK_COMMON_ERROR("Failed to get QNX Screen Buffer properties, VK error: %d", queryRes); + return false; + } + if (!((1 << pInfo->typeIndex) & screenBufferProps.memoryTypeBits)) { + VK_COMMON_ERROR("QNX Screen buffer can not be imported to memory (typeIndex=%d): %d", + pInfo->typeIndex); + return false; + } + if (screenBufferProps.allocationSize < pMemReqs->size) { + VK_COMMON_ERROR( + "QNX Screen buffer allocationSize (0x%lx) is not large enough for ColorBuffer image " + "size requirements (0x%lx)", + screenBufferProps.allocationSize, pMemReqs->size); + return false; + } + // Use the actual allocationSize for VkDeviceMemory object creation + pInfo->size = screenBufferProps.allocationSize; +#endif + + return true; +} + // TODO(liyl): Currently we can only specify required memoryProperty // for a color buffer. // @@ -1810,27 +1899,37 @@ // buffers of one type index for image and one type index for buffer // to begin with, via filtering from the host. -bool setupVkColorBufferLocked(uint32_t width, uint32_t height, GLenum internalFormat, - FrameworkFormat frameworkFormat, uint32_t colorBufferHandle, - bool vulkanOnly, uint32_t memoryProperty) { - if (!isFormatVulkanCompatible(internalFormat)) { - VK_COMMON_VERBOSE("Failed to create Vk ColorBuffer: format:%d not compatible.", - internalFormat); +bool initializeVkColorBufferLocked( + uint32_t colorBufferHandle, VK_EXT_MEMORY_HANDLE extMemHandle = VK_EXT_MEMORY_HANDLE_INVALID) { + auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle); + // Not initialized + if (!infoPtr) { return false; } - - auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle); - - // Already setup - if (infoPtr) { + // Already initialized Vulkan memory and other related Vulkan objects + if (infoPtr->initialized) { return true; } + if (!isFormatVulkanCompatible(infoPtr->internalFormat)) { + VK_COMMON_VERBOSE("Failed to create Vk ColorBuffer: format:%d not compatible.", + infoPtr->internalFormat); + return false; + } + + if ((VK_EXT_MEMORY_HANDLE_INVALID != extMemHandle) && + (!sVkEmulation->deviceInfo.supportsExternalMemoryImport)) { + VK_COMMON_ERROR( + "Failed to initialize Vk ColorBuffer -- extMemHandle provided, but device does " + "not support externalMemoryImport"); + return false; + } + VkFormat vkFormat; - bool glCompatible = (frameworkFormat == FRAMEWORK_FORMAT_GL_COMPATIBLE); - switch (frameworkFormat) { + bool glCompatible = (infoPtr->frameworkFormat == FRAMEWORK_FORMAT_GL_COMPATIBLE); + switch (infoPtr->frameworkFormat) { case FrameworkFormat::FRAMEWORK_FORMAT_GL_COMPATIBLE: - vkFormat = glFormat2VkFormat(internalFormat); + vkFormat = glFormat2VkFormat(infoPtr->internalFormat); break; case FrameworkFormat::FRAMEWORK_FORMAT_NV12: vkFormat = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM; @@ -1843,24 +1942,16 @@ vkFormat = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM; break; default: - VK_COMMON_ERROR("WARNING: unhandled framework format %d\n", frameworkFormat); - vkFormat = glFormat2VkFormat(internalFormat); + VK_COMMON_ERROR("WARNING: unhandled framework format %d\n", infoPtr->frameworkFormat); + vkFormat = glFormat2VkFormat(infoPtr->internalFormat); break; } - VkEmulation::ColorBufferInfo res; - - res.handle = colorBufferHandle; - - // TODO - res.frameworkFormat = frameworkFormat; - res.frameworkStride = 0; - - VkImageTiling tiling = (memoryProperty & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + VkImageTiling tiling = (infoPtr->memoryProperty & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) ? VK_IMAGE_TILING_LINEAR : VK_IMAGE_TILING_OPTIMAL; - std::unique_ptr<VkImageCreateInfo> imageCi = - generateColorBufferVkImageCreateInfo_locked(vkFormat, width, height, tiling); + std::unique_ptr<VkImageCreateInfo> imageCi = generateColorBufferVkImageCreateInfo_locked( + vkFormat, infoPtr->width, infoPtr->height, tiling); // pNext will be filled later. if (imageCi == nullptr) { // it can happen if the format is not supported @@ -1880,7 +1971,8 @@ VkExternalMemoryImageCreateInfo* extImageCiPtr = nullptr; - if (sVkEmulation->deviceInfo.supportsExternalMemory) { + if (sVkEmulation->deviceInfo.supportsExternalMemoryImport || + sVkEmulation->deviceInfo.supportsExternalMemoryExport) { extImageCiPtr = &extImageCi; } @@ -1889,18 +1981,19 @@ auto vk = sVkEmulation->dvk; VkResult createRes = - vk->vkCreateImage(sVkEmulation->device, imageCi.get(), nullptr, &res.image); + vk->vkCreateImage(sVkEmulation->device, imageCi.get(), nullptr, &infoPtr->image); if (createRes != VK_SUCCESS) { // LOG(VERBOSE) << "Failed to create Vulkan image for ColorBuffer " // << colorBufferHandle; return false; } - bool useDedicated = sVkEmulation->useDedicatedAllocations; + bool useDedicated = + sVkEmulation->useDedicatedAllocations || (VK_EXT_MEMORY_HANDLE_INVALID != extMemHandle); - res.imageCreateInfoShallow = vk_make_orphan_copy(*imageCi); - res.currentLayout = res.imageCreateInfoShallow.initialLayout; - res.currentQueueFamilyIndex = sVkEmulation->queueFamilyIndex; + infoPtr->imageCreateInfoShallow = vk_make_orphan_copy(*imageCi); + infoPtr->currentLayout = infoPtr->imageCreateInfoShallow.initialLayout; + infoPtr->currentQueueFamilyIndex = sVkEmulation->queueFamilyIndex; if (!useDedicated && vk->vkGetImageMemoryRequirements2KHR) { VkMemoryDedicatedRequirements dedicated_reqs{ @@ -1908,28 +2001,28 @@ VkMemoryRequirements2 reqs{VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, &dedicated_reqs}; VkImageMemoryRequirementsInfo2 info{VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, - nullptr, res.image}; + nullptr, infoPtr->image}; vk->vkGetImageMemoryRequirements2KHR(sVkEmulation->device, &info, &reqs); useDedicated = dedicated_reqs.requiresDedicatedAllocation; - res.memReqs = reqs.memoryRequirements; + infoPtr->memReqs = reqs.memoryRequirements; } else { - vk->vkGetImageMemoryRequirements(sVkEmulation->device, res.image, &res.memReqs); + vk->vkGetImageMemoryRequirements(sVkEmulation->device, infoPtr->image, &infoPtr->memReqs); } // Currently we only care about two memory properties: DEVICE_LOCAL // and HOST_VISIBLE; other memory properties specified in // rcSetColorBufferVulkanMode2() call will be ignored for now. - memoryProperty = memoryProperty & - (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + infoPtr->memoryProperty = infoPtr->memoryProperty & (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); - res.memory.size = res.memReqs.size; + infoPtr->memory.size = infoPtr->memReqs.size; // Determine memory type. - if (memoryProperty) { - res.memory.typeIndex = - lastGoodTypeIndexWithMemoryProperties(res.memReqs.memoryTypeBits, memoryProperty); + if (infoPtr->memoryProperty) { + infoPtr->memory.typeIndex = lastGoodTypeIndexWithMemoryProperties( + infoPtr->memReqs.memoryTypeBits, infoPtr->memoryProperty); } else { - res.memory.typeIndex = lastGoodTypeIndex(res.memReqs.memoryTypeBits); + infoPtr->memory.typeIndex = lastGoodTypeIndex(infoPtr->memReqs.memoryTypeBits); } // LOG(VERBOSE) << "ColorBuffer " << colorBufferHandle @@ -1941,23 +2034,39 @@ // .propertyFlags // << ", requested memory property: " << memoryProperty; - bool isHostVisible = memoryProperty & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - Optional<uint64_t> deviceAlignment = - isHostVisible ? Optional<uint64_t>(res.memReqs.alignment) : kNullopt; - Optional<VkImage> dedicatedImage = useDedicated ? Optional<VkImage>(res.image) : kNullopt; - bool allocRes = allocExternalMemory(vk, &res.memory, true /*actuallyExternal*/, deviceAlignment, - kNullopt, dedicatedImage); - - if (!allocRes) { - // LOG(VERBOSE) << "Failed to allocate ColorBuffer with Vulkan backing."; - return false; + Optional<VkImage> dedicatedImage = useDedicated ? Optional<VkImage>(infoPtr->image) : kNullopt; + if (VK_EXT_MEMORY_HANDLE_INVALID != extMemHandle) { + if (!updateExternalMemoryInfo(extMemHandle, &infoPtr->memReqs, &infoPtr->memory)) { + VK_COMMON_ERROR( + "Failed to update external memory info for ColorBuffer: %d\n", + colorBufferHandle); + return false; + } + if (!importExternalMemoryDedicatedImage(vk, sVkEmulation->device, &infoPtr->memory, + *dedicatedImage, &infoPtr->memory.memory)) { + VK_COMMON_ERROR( + "Failed to import external memory with dedicated Image for colorBuffer: %d\n", + colorBufferHandle); + return false; + } + } else { + bool isHostVisible = infoPtr->memoryProperty & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + Optional<uint64_t> deviceAlignment = + isHostVisible ? Optional<uint64_t>(infoPtr->memReqs.alignment) : kNullopt; + bool allocRes = allocExternalMemory(vk, &infoPtr->memory, true /*actuallyExternal*/, + deviceAlignment, kNullopt, dedicatedImage); + if (!allocRes) { + // LOG(VERBOSE) << "Failed to allocate ColorBuffer with Vulkan backing."; + return false; + } } - res.memory.pageOffset = reinterpret_cast<uint64_t>(res.memory.mappedPtr) % kPageSize; - res.memory.bindOffset = res.memory.pageOffset ? kPageSize - res.memory.pageOffset : 0u; + infoPtr->memory.pageOffset = reinterpret_cast<uint64_t>(infoPtr->memory.mappedPtr) % kPageSize; + infoPtr->memory.bindOffset = + infoPtr->memory.pageOffset ? kPageSize - infoPtr->memory.pageOffset : 0u; - VkResult bindImageMemoryRes = vk->vkBindImageMemory(sVkEmulation->device, res.image, - res.memory.memory, res.memory.bindOffset); + VkResult bindImageMemoryRes = vk->vkBindImageMemory( + sVkEmulation->device, infoPtr->image, infoPtr->memory.memory, infoPtr->memory.bindOffset); if (bindImageMemoryRes != VK_SUCCESS) { fprintf(stderr, "%s: Failed to bind image memory. %d\n", __func__, bindImageMemoryRes); @@ -1968,9 +2077,9 @@ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, .pNext = nullptr, .flags = 0, - .image = res.image, + .image = infoPtr->image, .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = res.imageCreateInfoShallow.format, + .format = infoPtr->imageCreateInfoShallow.format, .components = { .r = VK_COMPONENT_SWIZZLE_IDENTITY, @@ -1987,7 +2096,8 @@ .layerCount = 1, }, }; - createRes = vk->vkCreateImageView(sVkEmulation->device, &imageViewCi, nullptr, &res.imageView); + createRes = + vk->vkCreateImageView(sVkEmulation->device, &imageViewCi, nullptr, &infoPtr->imageView); if (createRes != VK_SUCCESS) { // LOG(VERBOSE) << "Failed to create Vulkan image for ColorBuffer " // << colorBufferHandle; @@ -1996,15 +2106,39 @@ #if defined(VK_MVK_moltenvk) && defined(__APPLE__) if (sVkEmulation->instanceSupportsMoltenVK) { - sVkEmulation->getMTLTextureFunc(res.image, &res.mtlTexture); - if (!res.mtlTexture) { + sVkEmulation->getMTLTextureFunc(infoPtr->image, &infoPtr->mtlTexture); + if (!infoPtr->mtlTexture) { fprintf(stderr, "%s: Failed to get MTLTexture.\n", __func__); } - CFRetain(res.mtlTexture); + CFRetain(infoPtr->mtlTexture); } #endif + infoPtr->initialized = true; + + return true; +} + +bool createVkColorBufferLocked(uint32_t width, uint32_t height, GLenum internalFormat, + FrameworkFormat frameworkFormat, uint32_t colorBufferHandle, + bool vulkanOnly, uint32_t memoryProperty) { + auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle); + // Already initialized + if (infoPtr) { + return true; + } + + VkEmulation::ColorBufferInfo res; + + res.handle = colorBufferHandle; + res.width = width; + res.height = height; + res.memoryProperty = memoryProperty; + res.internalFormat = internalFormat; + res.frameworkFormat = frameworkFormat; + res.frameworkStride = 0; + if (vulkanOnly) { res.vulkanMode = VkEmulation::VulkanMode::VulkanOnly; } @@ -2013,16 +2147,30 @@ return true; } -bool setupVkColorBuffer(uint32_t width, uint32_t height, GLenum internalFormat, - FrameworkFormat frameworkFormat, uint32_t colorBufferHandle, - bool vulkanOnly, uint32_t memoryProperty) { +bool createVkColorBuffer(uint32_t width, uint32_t height, GLenum internalFormat, + FrameworkFormat frameworkFormat, uint32_t colorBufferHandle, + bool vulkanOnly, uint32_t memoryProperty) { if (!sVkEmulation || !sVkEmulation->live) { GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "VkEmulation not available."; } AutoLock lock(sVkEmulationLock); - return setupVkColorBufferLocked(width, height, internalFormat, frameworkFormat, - colorBufferHandle, vulkanOnly, memoryProperty); + if (!createVkColorBufferLocked(width, height, internalFormat, frameworkFormat, + colorBufferHandle, vulkanOnly, memoryProperty)) { + return false; + } + + const auto& deviceInfo = sVkEmulation->deviceInfo; + if (!deviceInfo.supportsExternalMemoryExport && deviceInfo.supportsExternalMemoryImport) { + /* Returns, deferring initialization of the Vulkan components themselves. + * Platforms that support import but not export of external memory must + * use importExtMemoryHandleToVkColorBuffer(). Otherwise, the colorBuffer + * memory can not be externalized. + */ + return true; + } + + return initializeVkColorBufferLocked(colorBufferHandle); } std::optional<VkColorBufferMemoryExport> exportColorBufferMemory(uint32_t colorBufferHandle) { @@ -2033,7 +2181,8 @@ AutoLock lock(sVkEmulationLock); const auto& deviceInfo = sVkEmulation->deviceInfo; - if (!deviceInfo.supportsExternalMemory || !deviceInfo.glInteropSupported) { + if ((!(deviceInfo.supportsExternalMemoryExport || !deviceInfo.supportsExternalMemoryImport)) || + (!deviceInfo.glInteropSupported)) { return std::nullopt; } @@ -2046,7 +2195,8 @@ return std::nullopt; } - ManagedDescriptor descriptor(dupExternalMemory(info->memory.exportedHandle)); +#if !defined(__QNX__) + ManagedDescriptor descriptor(dupExternalMemory(info->memory.externalHandle)); info->glExported = true; @@ -2056,6 +2206,9 @@ .linearTiling = info->imageCreateInfoShallow.tiling == VK_IMAGE_TILING_LINEAR, .dedicatedAllocation = info->memory.dedicatedAllocation, }; +#else + return std::nullopt; +#endif } bool teardownVkColorBufferLocked(uint32_t colorBufferHandle) { @@ -2067,20 +2220,22 @@ if (!infoPtr) return false; - auto& info = *infoPtr; - { - android::base::AutoLock lock(*sVkEmulation->queueLock); - VK_CHECK(vk->vkQueueWaitIdle(sVkEmulation->queue)); - } - vk->vkDestroyImageView(sVkEmulation->device, info.imageView, nullptr); - vk->vkDestroyImage(sVkEmulation->device, info.image, nullptr); - freeExternalMemoryLocked(vk, &info.memory); + if (infoPtr->initialized) { + auto& info = *infoPtr; + { + android::base::AutoLock lock(*sVkEmulation->queueLock); + VK_CHECK(vk->vkQueueWaitIdle(sVkEmulation->queue)); + } + vk->vkDestroyImageView(sVkEmulation->device, info.imageView, nullptr); + vk->vkDestroyImage(sVkEmulation->device, info.image, nullptr); + freeExternalMemoryLocked(vk, &info.memory); #ifdef __APPLE__ - if (info.mtlTexture) { - CFRelease(info.mtlTexture); - } + if (info.mtlTexture) { + CFRelease(info.mtlTexture); + } #endif + } sVkEmulation->colorBuffers.erase(colorBufferHandle); @@ -2094,6 +2249,21 @@ return teardownVkColorBufferLocked(colorBufferHandle); } +bool importExtMemoryHandleToVkColorBuffer(uint32_t colorBufferHandle, uint32_t type, + VK_EXT_MEMORY_HANDLE extMemHandle) { + if (!sVkEmulation || !sVkEmulation->live) { + GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER)) << "VkEmulation not available."; + } + if (VK_EXT_MEMORY_HANDLE_INVALID == extMemHandle) { + return false; + } + + AutoLock lock(sVkEmulationLock); + // Initialize the colorBuffer with the external memory handle + // Note that this will fail if the colorBuffer memory was previously initialized. + return initializeVkColorBufferLocked(colorBufferHandle, extMemHandle); +} + VkEmulation::ColorBufferInfo getColorBufferInfo(uint32_t colorBufferHandle) { VkEmulation::ColorBufferInfo res; @@ -2528,7 +2698,7 @@ return VK_EXT_MEMORY_HANDLE_INVALID; } - return infoPtr->memory.exportedHandle; + return infoPtr->memory.externalHandle; } bool setColorBufferVulkanMode(uint32_t colorBuffer, uint32_t vulkanMode) { @@ -2684,7 +2854,8 @@ }; VkExternalMemoryBufferCreateInfo* extBufferCiPtr = nullptr; - if (sVkEmulation->deviceInfo.supportsExternalMemory) { + if (sVkEmulation->deviceInfo.supportsExternalMemoryImport || + sVkEmulation->deviceInfo.supportsExternalMemoryExport) { extBufferCiPtr = &extBufferCi; } @@ -2818,7 +2989,7 @@ return VK_EXT_MEMORY_HANDLE_INVALID; } - return infoPtr->memory.exportedHandle; + return infoPtr->memory.externalHandle; } bool readBufferToBytes(uint32_t bufferHandle, uint64_t offset, uint64_t size, void* outBytes) { @@ -3021,6 +3192,15 @@ res &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA; res |= VK_EXT_MEMORY_HANDLE_TYPE_BIT; } + +#if defined(__QNX__) + // QNX only: Replace DMA_BUF_BIT_EXT with SCREEN_BUFFER_BIT_QNX for host calls + if (bits & VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT) { + res &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; + res |= VK_EXT_MEMORY_HANDLE_TYPE_BIT; + } +#endif + return res; } @@ -3316,12 +3496,16 @@ constexpr const uint32_t kArbitraryWidth = 64; constexpr const uint32_t kArbitraryHeight = 64; constexpr const uint32_t kArbitraryHandle = std::numeric_limits<uint32_t>::max(); - if (!setupVkColorBufferLocked(kArbitraryWidth, kArbitraryHeight, GL_RGBA8, - FrameworkFormat::FRAMEWORK_FORMAT_GL_COMPATIBLE, kArbitraryHandle, - true, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + if (!createVkColorBufferLocked(kArbitraryWidth, kArbitraryHeight, GL_RGBA8, + FrameworkFormat::FRAMEWORK_FORMAT_GL_COMPATIBLE, + kArbitraryHandle, true, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { ERR("Failed to setup memory type index test ColorBuffer."); return std::nullopt; } + if (!initializeVkColorBufferLocked(kArbitraryHandle)) { + ERR("Failed to initialize memory type index test ColorBuffer."); + return std::nullopt; + } uint32_t memoryTypeIndex = 0; if (!getColorBufferAllocationInfoLocked(kArbitraryHandle, nullptr, &memoryTypeIndex, nullptr,