Add gfx api headers, start building vulkan generated sources
bug: 146066070
Change-Id: If167038177b683a1cd07368ed75fe44a2c05a519
diff --git a/stream-servers/vulkan/VkCommonOperations.cpp b/stream-servers/vulkan/VkCommonOperations.cpp
new file mode 100644
index 0000000..56d6066
--- /dev/null
+++ b/stream-servers/vulkan/VkCommonOperations.cpp
@@ -0,0 +1,1816 @@
+// Copyright 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expresso or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "VkCommonOperations.h"
+
+#include "android/base/containers/Lookup.h"
+#include "android/base/containers/StaticMap.h"
+#include "android/base/memory/LazyInstance.h"
+#include "android/base/synchronization/Lock.h"
+#include "android/base/Log.h"
+
+#include "FrameBuffer.h"
+#include "VulkanDispatch.h"
+
+#include "common/goldfish_vk_dispatch.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <vulkan/vk_enum_string_helper.h>
+
+#include <iomanip>
+#include <ostream>
+#include <sstream>
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <fcntl.h>
+#endif
+
+#ifdef __APPLE__
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+using android::base::AutoLock;
+using android::base::LazyInstance;
+using android::base::StaticLock;
+using android::base::StaticMap;
+
+namespace goldfish_vk {
+
+static LazyInstance<StaticMap<VkDevice, uint32_t>>
+sKnownStagingTypeIndices = LAZY_INSTANCE_INIT;
+
+static android::base::StaticLock sVkEmulationLock;
+
+VK_EXT_MEMORY_HANDLE dupExternalMemory(VK_EXT_MEMORY_HANDLE h) {
+#ifdef _WIN32
+ auto myProcessHandle = GetCurrentProcess();
+ VK_EXT_MEMORY_HANDLE res;
+ DuplicateHandle(
+ myProcessHandle, h, // source process and handle
+ myProcessHandle, &res, // target process and pointer to handle
+ 0 /* desired access (ignored) */,
+ true /* inherit */,
+ DUPLICATE_SAME_ACCESS /* same access option */);
+ return res;
+#else
+ return dup(h);
+#endif
+}
+
+bool getStagingMemoryTypeIndex(
+ VulkanDispatch* vk,
+ VkDevice device,
+ const VkPhysicalDeviceMemoryProperties* memProps,
+ uint32_t* typeIndex) {
+
+ auto res = sKnownStagingTypeIndices->get(device);
+
+ if (res) {
+ *typeIndex = *res;
+ return true;
+ }
+
+ VkBufferCreateInfo testCreateInfo = {
+ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 0, 0,
+ 4096,
+ // To be a staging buffer, it must support being
+ // both a transfer src and dst.
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT |
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ // TODO: See if buffers over shared queues need to be
+ // considered separately
+ VK_SHARING_MODE_EXCLUSIVE,
+ 0, nullptr,
+ };
+
+ VkBuffer testBuffer;
+ VkResult testBufferCreateRes =
+ vk->vkCreateBuffer(device, &testCreateInfo, nullptr, &testBuffer);
+
+ if (testBufferCreateRes != VK_SUCCESS) {
+ LOG(ERROR) <<
+ "Could not create test buffer "
+ "for staging buffer query. VkResult: " <<
+ testBufferCreateRes;
+ return false;
+ }
+
+ VkMemoryRequirements memReqs;
+ vk->vkGetBufferMemoryRequirements(device, testBuffer, &memReqs);
+
+ // To be a staging buffer, we need to allow CPU read/write access.
+ // Thus, we need the memory type index both to be host visible
+ // and to be supported in the memory requirements of the buffer.
+ bool foundSuitableStagingMemoryType = false;
+ uint32_t stagingMemoryTypeIndex = 0;
+
+ for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) {
+ const auto& typeInfo = memProps->memoryTypes[i];
+ bool hostVisible =
+ typeInfo.propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
+ bool hostCached =
+ typeInfo.propertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
+ bool allowedInBuffer = (1 << i) & memReqs.memoryTypeBits;
+ if (hostVisible && hostCached && allowedInBuffer) {
+ foundSuitableStagingMemoryType = true;
+ stagingMemoryTypeIndex = i;
+ break;
+ }
+ }
+
+ vk->vkDestroyBuffer(device, testBuffer, nullptr);
+
+ if (!foundSuitableStagingMemoryType) {
+ std::stringstream ss;
+ ss <<
+ "Could not find suitable memory type index " <<
+ "for staging buffer. Memory type bits: " <<
+ std::hex << memReqs.memoryTypeBits << "\n" <<
+ "Available host visible memory type indices:" << "\n";
+ for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) {
+ if (memProps->memoryTypes[i].propertyFlags &
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+ ss << "Host visible memory type index: %u" << i << "\n";
+ }
+ if (memProps->memoryTypes[i].propertyFlags &
+ VK_MEMORY_PROPERTY_HOST_CACHED_BIT) {
+ ss << "Host cached memory type index: %u" << i << "\n";
+ }
+ }
+
+ LOG(ERROR) << ss.str();
+
+ return false;
+ }
+
+ sKnownStagingTypeIndices->set(device, stagingMemoryTypeIndex);
+ *typeIndex = stagingMemoryTypeIndex;
+
+ return true;
+}
+
+static VkEmulation* sVkEmulation = nullptr;
+
+static bool extensionsSupported(
+ const std::vector<VkExtensionProperties>& currentProps,
+ const std::vector<const char*>& wantedExtNames) {
+
+ std::vector<bool> foundExts(wantedExtNames.size(), false);
+
+ for (uint32_t i = 0; i < currentProps.size(); ++i) {
+ LOG(VERBOSE) << "has extension: " << currentProps[i].extensionName;
+ for (size_t j = 0; j < wantedExtNames.size(); ++j) {
+ if (!strcmp(wantedExtNames[j], currentProps[i].extensionName)) {
+ foundExts[j] = true;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < wantedExtNames.size(); ++i) {
+ bool found = foundExts[i];
+ LOG(VERBOSE) << "needed extension: " << wantedExtNames[i]
+ << " found: " << found;
+ if (!found) {
+ LOG(VERBOSE) << wantedExtNames[i] << " not found, bailing.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// For a given ImageSupportInfo, populates usageWithExternalHandles and
+// requiresDedicatedAllocation. memoryTypeBits are populated later once the
+// device is created, beacuse that needs a test image to be created.
+// If we don't support external memory, it's assumed dedicated allocations are
+// not needed.
+// Precondition: sVkEmulation instance has been created and ext memory caps known.
+// Returns false if the query failed.
+static bool getImageFormatExternalMemorySupportInfo(
+ VulkanDispatch* vk,
+ VkPhysicalDevice physdev,
+ VkEmulation::ImageSupportInfo* info) {
+
+ // Currently there is nothing special we need to do about
+ // VkFormatProperties2, so just use the normal version
+ // and put it in the format2 struct.
+ VkFormatProperties outFormatProps;
+ vk->vkGetPhysicalDeviceFormatProperties(
+ physdev, info->format, &outFormatProps);
+
+ info->formatProps2 = {
+ VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, 0,
+ outFormatProps,
+ };
+
+ if (!sVkEmulation->instanceSupportsExternalMemoryCapabilities) {
+ info->supportsExternalMemory = false;
+ info->requiresDedicatedAllocation = false;
+
+ VkImageFormatProperties outImageFormatProps;
+ VkResult res = vk->vkGetPhysicalDeviceImageFormatProperties(
+ physdev, info->format, info->type, info->tiling,
+ info->usageFlags, info->createFlags, &outImageFormatProps);
+
+ if (res != VK_SUCCESS) {
+ if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
+ info->supported = false;
+ return true;
+ } else {
+ fprintf(stderr,
+ "%s: vkGetPhysicalDeviceImageFormatProperties query "
+ "failed with %d "
+ "for format 0x%x type 0x%x usage 0x%x flags 0x%x\n",
+ __func__, res, info->format, info->type,
+ info->usageFlags, info->createFlags);
+ return false;
+ }
+ }
+
+ info->supported = true;
+
+ info->imageFormatProps2 = {
+ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, 0,
+ outImageFormatProps,
+ };
+
+ LOG(VERBOSE) << "Supported (not externally): "
+ << string_VkFormat(info->format) << " "
+ << string_VkImageType(info->type) << " "
+ << string_VkImageTiling(info->tiling) << " "
+ << string_VkImageUsageFlagBits(
+ (VkImageUsageFlagBits)info->usageFlags);
+
+ return true;
+ }
+
+ VkPhysicalDeviceExternalImageFormatInfo extInfo = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, 0,
+ VK_EXT_MEMORY_HANDLE_TYPE_BIT,
+ };
+
+ VkPhysicalDeviceImageFormatInfo2 formatInfo2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, &extInfo,
+ info->format, info->type, info->tiling,
+ info->usageFlags, info->createFlags,
+ };
+
+ VkExternalImageFormatProperties outExternalProps = {
+ VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES,
+ 0,
+ {
+ (VkExternalMemoryFeatureFlags)0,
+ (VkExternalMemoryHandleTypeFlags)0,
+ (VkExternalMemoryHandleTypeFlags)0,
+ },
+ };
+
+ VkImageFormatProperties2 outProps2 = {
+ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, &outExternalProps,
+ {
+ { 0, 0, 0},
+ 0, 0,
+ 1, 0,
+ }
+ };
+
+ VkResult res = sVkEmulation->getImageFormatProperties2Func(
+ physdev,
+ &formatInfo2,
+ &outProps2);
+
+ if (res != VK_SUCCESS) {
+ if (res == VK_ERROR_FORMAT_NOT_SUPPORTED) {
+ info->supported = false;
+ return true;
+ } else {
+ fprintf(stderr,
+ "%s: vkGetPhysicalDeviceImageFormatProperties2KHR query "
+ "failed "
+ "for format 0x%x type 0x%x usage 0x%x flags 0x%x\n",
+ __func__, info->format, info->type, info->usageFlags,
+ info->createFlags);
+ return false;
+ }
+ }
+
+ info->supported = true;
+
+ VkExternalMemoryFeatureFlags featureFlags =
+ outExternalProps.externalMemoryProperties.externalMemoryFeatures;
+
+ VkExternalMemoryHandleTypeFlags exportImportedFlags =
+ outExternalProps.externalMemoryProperties.exportFromImportedHandleTypes;
+
+ // Don't really care about export form imported handle types yet
+ (void)exportImportedFlags;
+
+ VkExternalMemoryHandleTypeFlags compatibleHandleTypes =
+ outExternalProps.externalMemoryProperties.compatibleHandleTypes;
+
+ info->supportsExternalMemory =
+ (VK_EXT_MEMORY_HANDLE_TYPE_BIT & compatibleHandleTypes) &&
+ (VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT & featureFlags) &&
+ (VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT & featureFlags);
+
+ info->requiresDedicatedAllocation =
+ (VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT & featureFlags);
+
+ info->imageFormatProps2 = outProps2;
+ info->extFormatProps = outExternalProps;
+ info->imageFormatProps2.pNext = &info->extFormatProps;
+
+ LOG(VERBOSE) << "Supported: "
+ << string_VkFormat(info->format) << " "
+ << string_VkImageType(info->type) << " "
+ << string_VkImageTiling(info->tiling) << " "
+ << string_VkImageUsageFlagBits(
+ (VkImageUsageFlagBits)info->usageFlags)
+ << " "
+ << "supportsExternalMemory? " << info->supportsExternalMemory
+ << " "
+ << "requiresDedicated? " << info->requiresDedicatedAllocation;
+
+ return true;
+}
+
+static std::vector<VkEmulation::ImageSupportInfo> getBasicImageSupportList() {
+ std::vector<VkFormat> formats = {
+ // Cover all the gralloc formats
+ VK_FORMAT_R8G8B8A8_UNORM,
+ VK_FORMAT_R8G8B8_UNORM,
+
+ VK_FORMAT_R5G6B5_UNORM_PACK16,
+
+ VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_FORMAT_R16G16B16_SFLOAT,
+
+ VK_FORMAT_B8G8R8A8_UNORM,
+
+ VK_FORMAT_R8_UNORM,
+
+ VK_FORMAT_A2R10G10B10_UINT_PACK32,
+ VK_FORMAT_A2R10G10B10_UNORM_PACK32,
+
+ // Compressed texture formats
+ VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK,
+ VK_FORMAT_ASTC_4x4_UNORM_BLOCK,
+
+ // TODO: YUV formats used in Android
+ // Fails on Mac
+ // VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
+ // VK_FORMAT_G8_B8R8_2PLANE_422_UNORM,
+ };
+
+ std::vector<VkImageType> types = {
+ VK_IMAGE_TYPE_2D,
+ };
+
+ std::vector<VkImageTiling> tilings = {
+ VK_IMAGE_TILING_LINEAR,
+ VK_IMAGE_TILING_OPTIMAL,
+ };
+
+ std::vector<VkImageUsageFlags> usageFlags = {
+ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
+ VK_IMAGE_USAGE_SAMPLED_BIT,
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT,
+ };
+
+ std::vector<VkImageCreateFlags> createFlags = {
+ 0,
+ };
+
+ std::vector<VkEmulation::ImageSupportInfo> res;
+
+ // Currently: 12 formats, 2 tilings, 4 usage flags -> 96 cases
+ // to check
+ for (auto f : formats) {
+ for (auto t : types) {
+ for (auto ti : tilings) {
+ for (auto u : usageFlags) {
+ for (auto c : createFlags) {
+ VkEmulation::ImageSupportInfo info;
+ info.format = f;
+ info.type = t;
+ info.tiling = ti;
+ info.usageFlags = u;
+ info.createFlags = c;
+ res.push_back(info);
+ }
+ }
+ }
+ }
+ }
+
+ return res;
+}
+
+VkEmulation* createOrGetGlobalVkEmulation(VulkanDispatch* vk) {
+ AutoLock lock(sVkEmulationLock);
+
+ if (sVkEmulation) return sVkEmulation;
+
+ if (!emugl::vkDispatchValid(vk)) {
+ return nullptr;
+ }
+
+ sVkEmulation = new VkEmulation;
+
+ sVkEmulation->gvk = vk;
+ auto gvk = vk;
+
+ std::vector<const char*> externalMemoryInstanceExtNames = {
+ "VK_KHR_external_memory_capabilities",
+ "VK_KHR_get_physical_device_properties2",
+ };
+
+ std::vector<const char*> moltenVKInstanceExtNames = {
+ "VK_MVK_moltenvk",
+ };
+
+ std::vector<const char*> externalMemoryDeviceExtNames = {
+ "VK_KHR_dedicated_allocation",
+ "VK_KHR_get_memory_requirements2",
+ "VK_KHR_external_memory",
+#ifdef _WIN32
+ "VK_KHR_external_memory_win32",
+#else
+ "VK_KHR_external_memory_fd",
+#endif
+ };
+
+ uint32_t extCount = 0;
+ gvk->vkEnumerateInstanceExtensionProperties(nullptr, &extCount, nullptr);
+ std::vector<VkExtensionProperties> exts(extCount);
+ gvk->vkEnumerateInstanceExtensionProperties(nullptr, &extCount, exts.data());
+
+ bool externalMemoryCapabilitiesSupported =
+ extensionsSupported(exts, externalMemoryInstanceExtNames);
+ bool moltenVKSupported =
+ extensionsSupported(exts, moltenVKInstanceExtNames);
+
+ VkInstanceCreateInfo instCi = {
+ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ 0, 0, nullptr, 0, nullptr,
+ 0, nullptr,
+ };
+
+ if (externalMemoryCapabilitiesSupported) {
+ instCi.enabledExtensionCount =
+ externalMemoryInstanceExtNames.size();
+ instCi.ppEnabledExtensionNames =
+ externalMemoryInstanceExtNames.data();
+ }
+
+ if (moltenVKSupported) {
+ // We don't need both moltenVK and external memory. Disable
+ // external memory if moltenVK is supported.
+ externalMemoryCapabilitiesSupported = false;
+ instCi.enabledExtensionCount =
+ moltenVKInstanceExtNames.size();
+ instCi.ppEnabledExtensionNames =
+ moltenVKInstanceExtNames.data();
+ }
+
+ VkResult res = gvk->vkCreateInstance(&instCi, nullptr, &sVkEmulation->instance);
+
+ if (res != VK_SUCCESS) {
+ LOG(ERROR) << "Failed to create Vulkan instance.";
+ return sVkEmulation;
+ }
+
+ sVkEmulation->instanceSupportsExternalMemoryCapabilities =
+ externalMemoryCapabilitiesSupported;
+ sVkEmulation->instanceSupportsMoltenVK = moltenVKSupported;
+
+ // Postcondition: sVkEmulation instance has been created and ext memory caps known.
+
+ // Create instance level dispatch.
+ sVkEmulation->ivk = new VulkanDispatch;
+ init_vulkan_dispatch_from_instance(
+ vk, sVkEmulation->instance, sVkEmulation->ivk);
+
+ auto ivk = sVkEmulation->ivk;
+
+ if (!vulkan_dispatch_check_instance_VK_VERSION_1_0(ivk)) {
+ fprintf(stderr, "%s: Warning: Vulkan 1.0 APIs missing from instance\n", __func__);
+ }
+
+ if (ivk->vkEnumerateInstanceVersion) {
+ uint32_t instanceVersion;
+ VkResult enumInstanceRes = ivk->vkEnumerateInstanceVersion(&instanceVersion);
+ if (enumInstanceRes >= VK_MAKE_VERSION(1, 1, 0)) {
+ if (!vulkan_dispatch_check_instance_VK_VERSION_1_1(ivk)) {
+ fprintf(stderr, "%s: Warning: Vulkan 1.1 APIs missing from instance\n", __func__);
+ }
+ }
+ }
+
+ if (sVkEmulation->instanceSupportsExternalMemoryCapabilities) {
+ sVkEmulation->getImageFormatProperties2Func = reinterpret_cast<
+ PFN_vkGetPhysicalDeviceImageFormatProperties2KHR>(
+ ivk->vkGetInstanceProcAddr(
+ sVkEmulation->instance,
+ "vkGetPhysicalDeviceImageFormatProperties2KHR"));
+ }
+
+ if (sVkEmulation->instanceSupportsMoltenVK) {
+ sVkEmulation->useIOSurfaceFunc = reinterpret_cast<PFN_vkUseIOSurfaceMVK>(
+ vk->vkGetInstanceProcAddr(
+ sVkEmulation->instance, "vkUseIOSurfaceMVK"));
+ if (!sVkEmulation->useIOSurfaceFunc) {
+ LOG(ERROR) << "Cannot find vkUseIOSurfaceMVK";
+ return sVkEmulation;
+ }
+ sVkEmulation->getIOSurfaceFunc = reinterpret_cast<PFN_vkGetIOSurfaceMVK>(
+ ivk->vkGetInstanceProcAddr(
+ sVkEmulation->instance, "vkGetIOSurfaceMVK"));
+ if (!sVkEmulation->getIOSurfaceFunc) {
+ LOG(ERROR) << "Cannot find vkGetIOSurfaceMVK";
+ return sVkEmulation;
+ }
+ LOG(VERBOSE) << "Instance supports VK_MVK_moltenvk.";
+ }
+
+ uint32_t physdevCount = 0;
+ ivk->vkEnumeratePhysicalDevices(sVkEmulation->instance, &physdevCount,
+ nullptr);
+ std::vector<VkPhysicalDevice> physdevs(physdevCount);
+ ivk->vkEnumeratePhysicalDevices(sVkEmulation->instance, &physdevCount,
+ physdevs.data());
+
+ LOG(VERBOSE) << "Found " << physdevCount << " Vulkan physical devices.";
+
+ if (physdevCount == 0) {
+ LOG(VERBOSE) << "No physical devices available.";
+ return sVkEmulation;
+ }
+
+ std::vector<VkEmulation::DeviceSupportInfo> deviceInfos(physdevCount);
+
+ for (int i = 0; i < physdevCount; ++i) {
+ ivk->vkGetPhysicalDeviceProperties(physdevs[i],
+ &deviceInfos[i].physdevProps);
+
+ LOG(VERBOSE) << "Considering Vulkan physical device " << i << ": "
+ << deviceInfos[i].physdevProps.deviceName;
+
+ // It's easier to figure out the staging buffer along with
+ // external memories if we have the memory properties on hand.
+ ivk->vkGetPhysicalDeviceMemoryProperties(physdevs[i],
+ &deviceInfos[i].memProps);
+
+ uint32_t deviceExtensionCount = 0;
+ ivk->vkEnumerateDeviceExtensionProperties(
+ physdevs[i], nullptr, &deviceExtensionCount, nullptr);
+ std::vector<VkExtensionProperties> deviceExts(deviceExtensionCount);
+ ivk->vkEnumerateDeviceExtensionProperties(
+ physdevs[i], nullptr, &deviceExtensionCount, deviceExts.data());
+
+ deviceInfos[i].supportsExternalMemory = false;
+ deviceInfos[i].glInteropSupported =
+ FrameBuffer::getFB()->isVulkanInteropSupported();
+
+ if (sVkEmulation->instanceSupportsExternalMemoryCapabilities) {
+ deviceInfos[i].supportsExternalMemory = extensionsSupported(
+ deviceExts, externalMemoryDeviceExtNames);
+ }
+
+ uint32_t queueFamilyCount = 0;
+ ivk->vkGetPhysicalDeviceQueueFamilyProperties(
+ physdevs[i], &queueFamilyCount, nullptr);
+ std::vector<VkQueueFamilyProperties> queueFamilyProps(queueFamilyCount);
+ ivk->vkGetPhysicalDeviceQueueFamilyProperties(
+ physdevs[i], &queueFamilyCount, queueFamilyProps.data());
+
+ for (uint32_t j = 0; j < queueFamilyCount; ++j) {
+ auto count = queueFamilyProps[j].queueCount;
+ auto flags = queueFamilyProps[j].queueFlags;
+
+ bool hasGraphicsQueueFamily =
+ (count > 0 && (flags & VK_QUEUE_GRAPHICS_BIT));
+ bool hasComputeQueueFamily =
+ (count > 0 && (flags & VK_QUEUE_COMPUTE_BIT));
+
+ deviceInfos[i].hasGraphicsQueueFamily =
+ deviceInfos[i].hasGraphicsQueueFamily ||
+ hasGraphicsQueueFamily;
+
+ deviceInfos[i].hasComputeQueueFamily =
+ deviceInfos[i].hasComputeQueueFamily ||
+ hasComputeQueueFamily;
+
+ if (hasGraphicsQueueFamily) {
+ deviceInfos[i].graphicsQueueFamilyIndices.push_back(j);
+ LOG(VERBOSE) << "Graphics queue family index: " << j;
+ }
+
+ if (hasComputeQueueFamily) {
+ deviceInfos[i].computeQueueFamilyIndices.push_back(j);
+ LOG(VERBOSE) << "Compute queue family index: " << j;
+ }
+ }
+ }
+
+ // Of all the devices enumerated, find the best one. Try to find a device
+ // with graphics queue as the highest priority, then ext memory, then
+ // compute.
+
+ // Graphics queue is highest priority since without that, we really
+ // shouldn't be using the driver. Although, one could make a case for doing
+ // some sorts of things if only a compute queue is available (such as for
+ // AI), that's not really the priority yet.
+
+ // As for external memory, we really should not be running on any driver
+ // without external memory support, but we might be able to pull it off, and
+ // single Vulkan apps might work via CPU transfer of the rendered frames.
+
+ // Compute support is treated as icing on the cake and not relied upon yet
+ // for anything critical to emulation. However, we might potentially use it
+ // to perform image format conversion on GPUs where that's not natively
+ // supported.
+
+ // Another implicit choice is to select only one Vulkan device. This makes
+ // things simple for now, but we could consider utilizing multiple devices
+ // in use cases that make sense, if/when they come up.
+
+ std::vector<uint32_t> deviceScores(physdevCount, 0);
+
+ for (uint32_t i = 0; i < physdevCount; ++i) {
+ uint32_t deviceScore = 0;
+ if (deviceInfos[i].hasGraphicsQueueFamily) deviceScore += 100;
+ if (deviceInfos[i].supportsExternalMemory) deviceScore += 10;
+ if (deviceInfos[i].hasComputeQueueFamily) deviceScore += 1;
+ deviceScores[i] = deviceScore;
+ }
+
+ uint32_t maxScoringIndex = 0;
+ uint32_t maxScore = 0;
+
+ for (uint32_t i = 0; i < physdevCount; ++i) {
+ if (deviceScores[i] > maxScore) {
+ maxScoringIndex = i;
+ maxScore = deviceScores[i];
+ }
+ }
+
+ sVkEmulation->physdev = physdevs[maxScoringIndex];
+ sVkEmulation->deviceInfo = deviceInfos[maxScoringIndex];
+
+ // Postcondition: sVkEmulation has valid device support info
+
+ // Ask about image format support here.
+ // TODO: May have to first ask when selecting physical devices
+ // (e.g., choose between Intel or NVIDIA GPU for certain image format
+ // support)
+ sVkEmulation->imageSupportInfo = getBasicImageSupportList();
+ for (size_t i = 0; i < sVkEmulation->imageSupportInfo.size(); ++i) {
+ getImageFormatExternalMemorySupportInfo(
+ ivk, sVkEmulation->physdev, &sVkEmulation->imageSupportInfo[i]);
+ }
+
+ if (!sVkEmulation->deviceInfo.hasGraphicsQueueFamily) {
+ LOG(VERBOSE) << "No Vulkan devices with graphics queues found.";
+ return sVkEmulation;
+ }
+
+ auto deviceVersion = sVkEmulation->deviceInfo.physdevProps.apiVersion;
+
+ LOG(VERBOSE) << "Vulkan device found: "
+ << sVkEmulation->deviceInfo.physdevProps.deviceName;
+ LOG(VERBOSE) << "Version: "
+ << VK_VERSION_MAJOR(deviceVersion) << "." << VK_VERSION_MINOR(deviceVersion) << "." << VK_VERSION_PATCH(deviceVersion);
+ LOG(VERBOSE) << "Has graphics queue? "
+ << sVkEmulation->deviceInfo.hasGraphicsQueueFamily;
+ LOG(VERBOSE) << "Has external memory support? "
+ << sVkEmulation->deviceInfo.supportsExternalMemory;
+ LOG(VERBOSE) << "Has compute queue? "
+ << sVkEmulation->deviceInfo.hasComputeQueueFamily;
+
+ float priority = 1.0f;
+ VkDeviceQueueCreateInfo dqCi = {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, 0, 0,
+ sVkEmulation->deviceInfo.graphicsQueueFamilyIndices[0],
+ 1, &priority,
+ };
+
+ uint32_t selectedDeviceExtensionCount = 0;
+ const char* const* selectedDeviceExtensionNames = nullptr;
+
+ if (sVkEmulation->deviceInfo.supportsExternalMemory) {
+ selectedDeviceExtensionCount = externalMemoryDeviceExtNames.size();
+ selectedDeviceExtensionNames = externalMemoryDeviceExtNames.data();
+ }
+
+ VkDeviceCreateInfo dCi = {
+ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 0, 0,
+ // TODO: add compute queue as well if appropriate
+ 1, &dqCi,
+ 0, nullptr, // no layers
+ selectedDeviceExtensionCount,
+ selectedDeviceExtensionNames, // no layers
+ nullptr, // no features
+ };
+
+ ivk->vkCreateDevice(sVkEmulation->physdev, &dCi, nullptr,
+ &sVkEmulation->device);
+
+ if (res != VK_SUCCESS) {
+ LOG(ERROR) << "Failed to create Vulkan device.";
+ return sVkEmulation;
+ }
+
+ // device created; populate dispatch table
+ sVkEmulation->dvk = new VulkanDispatch;
+ init_vulkan_dispatch_from_device(
+ ivk, sVkEmulation->device, sVkEmulation->dvk);
+
+ auto dvk = sVkEmulation->dvk;
+
+ // Check if the dispatch table has everything 1.1 related
+ if (!vulkan_dispatch_check_device_VK_VERSION_1_0(dvk)) {
+ fprintf(stderr, "%s: Warning: Vulkan 1.0 APIs missing from device.\n", __func__);
+ }
+ if (deviceVersion >= VK_MAKE_VERSION(1, 1, 0)) {
+ if (!vulkan_dispatch_check_device_VK_VERSION_1_1(dvk)) {
+ fprintf(stderr, "%s: Warning: Vulkan 1.1 APIs missing from device\n", __func__);
+ }
+ }
+
+ if (sVkEmulation->deviceInfo.supportsExternalMemory) {
+ sVkEmulation->deviceInfo.getImageMemoryRequirements2Func =
+ reinterpret_cast<PFN_vkGetImageMemoryRequirements2KHR>(
+ dvk->vkGetDeviceProcAddr(
+ sVkEmulation->device, "vkGetImageMemoryRequirements2KHR"));
+ if (!sVkEmulation->deviceInfo.getImageMemoryRequirements2Func) {
+ LOG(ERROR) << "Cannot find vkGetImageMemoryRequirements2KHR";
+ return sVkEmulation;
+ }
+ sVkEmulation->deviceInfo.getBufferMemoryRequirements2Func =
+ reinterpret_cast<PFN_vkGetBufferMemoryRequirements2KHR>(
+ dvk->vkGetDeviceProcAddr(
+ sVkEmulation->device, "vkGetBufferMemoryRequirements2KHR"));
+ if (!sVkEmulation->deviceInfo.getBufferMemoryRequirements2Func) {
+ LOG(ERROR) << "Cannot find vkGetBufferMemoryRequirements2KHR";
+ return sVkEmulation;
+ }
+#ifdef _WIN32
+ sVkEmulation->deviceInfo.getMemoryHandleFunc =
+ reinterpret_cast<PFN_vkGetMemoryWin32HandleKHR>(
+ dvk->vkGetDeviceProcAddr(sVkEmulation->device,
+ "vkGetMemoryWin32HandleKHR"));
+#else
+ sVkEmulation->deviceInfo.getMemoryHandleFunc =
+ reinterpret_cast<PFN_vkGetMemoryFdKHR>(
+ dvk->vkGetDeviceProcAddr(sVkEmulation->device,
+ "vkGetMemoryFdKHR"));
+#endif
+ if (!sVkEmulation->deviceInfo.getMemoryHandleFunc) {
+ LOG(ERROR) << "Cannot find vkGetMemory(Fd|Win32Handle)KHR";
+ return sVkEmulation;
+ }
+ }
+
+ LOG(VERBOSE) << "Vulkan logical device created and extension functions obtained.\n";
+
+ dvk->vkGetDeviceQueue(
+ sVkEmulation->device,
+ sVkEmulation->deviceInfo.graphicsQueueFamilyIndices[0], 0,
+ &sVkEmulation->queue);
+
+ sVkEmulation->queueFamilyIndex =
+ sVkEmulation->deviceInfo.graphicsQueueFamilyIndices[0];
+
+ LOG(VERBOSE) << "Vulkan device queue obtained.";
+
+ VkCommandPoolCreateInfo poolCi = {
+ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, 0,
+ VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+ sVkEmulation->queueFamilyIndex,
+ };
+
+ VkResult poolCreateRes = dvk->vkCreateCommandPool(
+ sVkEmulation->device, &poolCi, nullptr, &sVkEmulation->commandPool);
+
+ if (poolCreateRes != VK_SUCCESS) {
+ LOG(ERROR) << "Failed to create command pool. Error: " << poolCreateRes;
+ return sVkEmulation;
+ }
+
+ VkCommandBufferAllocateInfo cbAi = {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, 0,
+ sVkEmulation->commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1,
+ };
+
+ VkResult cbAllocRes = dvk->vkAllocateCommandBuffers(
+ sVkEmulation->device, &cbAi, &sVkEmulation->commandBuffer);
+
+ if (cbAllocRes != VK_SUCCESS) {
+ LOG(ERROR) << "Failed to allocate command buffer. Error: " << cbAllocRes;
+ return sVkEmulation;
+ }
+
+ VkFenceCreateInfo fenceCi = {
+ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, 0, 0,
+ };
+
+ VkResult fenceCreateRes = dvk->vkCreateFence(
+ sVkEmulation->device, &fenceCi, nullptr,
+ &sVkEmulation->commandBufferFence);
+
+ if (fenceCreateRes != VK_SUCCESS) {
+ LOG(ERROR) << "Failed to create fence for command buffer. Error: " << fenceCreateRes;
+ return sVkEmulation;
+ }
+
+ // At this point, the global emulation state's logical device can alloc
+ // memory and send commands. However, it can't really do much yet to
+ // communicate the results without the staging buffer. Set that up here.
+ // Note that the staging buffer is meant to use external memory, with a
+ // non-external-memory fallback.
+
+ VkBufferCreateInfo bufCi = {
+ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 0, 0,
+ sVkEmulation->staging.size,
+ VK_BUFFER_USAGE_TRANSFER_DST_BIT |
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
+ VK_SHARING_MODE_EXCLUSIVE,
+ 0, nullptr,
+ };
+
+ VkResult bufCreateRes =
+ dvk->vkCreateBuffer(sVkEmulation->device, &bufCi, nullptr,
+ &sVkEmulation->staging.buffer);
+
+ if (bufCreateRes != VK_SUCCESS) {
+ LOG(ERROR) << "Failed to create staging buffer index";
+ return sVkEmulation;
+ }
+
+ VkMemoryRequirements memReqs;
+ dvk->vkGetBufferMemoryRequirements(sVkEmulation->device,
+ sVkEmulation->staging.buffer, &memReqs);
+
+ sVkEmulation->staging.memory.size = memReqs.size;
+
+ bool gotStagingTypeIndex = getStagingMemoryTypeIndex(
+ dvk, sVkEmulation->device, &sVkEmulation->deviceInfo.memProps,
+ &sVkEmulation->staging.memory.typeIndex);
+
+ if (!gotStagingTypeIndex) {
+ LOG(ERROR) << "Failed to determine staging memory type index";
+ return sVkEmulation;
+ }
+
+ if (!((1 << sVkEmulation->staging.memory.typeIndex) &
+ memReqs.memoryTypeBits)) {
+ LOG(ERROR) << "Failed: Inconsistent determination of memory type "
+ "index for staging buffer";
+ return sVkEmulation;
+ }
+
+ if (!allocExternalMemory(dvk, &sVkEmulation->staging.memory, false /* not external */)) {
+ LOG(ERROR) << "Failed to allocate memory for staging buffer";
+ return sVkEmulation;
+ }
+
+ VkResult stagingBufferBindRes = dvk->vkBindBufferMemory(
+ sVkEmulation->device,
+ sVkEmulation->staging.buffer,
+ sVkEmulation->staging.memory.memory, 0);
+
+ if (stagingBufferBindRes != VK_SUCCESS) {
+ LOG(ERROR) << "Failed to bind memory for staging buffer";
+ return sVkEmulation;
+ }
+
+ LOG(VERBOSE) << "Vulkan global emulation state successfully initialized.";
+ sVkEmulation->live = true;
+
+ return sVkEmulation;
+}
+
+void setUseDeferredCommands(VkEmulation* emu, bool useDeferredCommands) {
+ if (!emu) return;
+ if (!emu->live) return;
+
+ LOG(VERBOSE) << "Using deferred Vulkan commands: " << useDeferredCommands;
+ emu->useDeferredCommands = useDeferredCommands;
+}
+
+void setUseCreateResourcesWithRequirements(VkEmulation* emu, bool useCreateResourcesWithRequirements) {
+ if (!emu) return;
+ if (!emu->live) return;
+
+ LOG(VERBOSE) << "Using deferred Vulkan commands: " << useCreateResourcesWithRequirements;
+ emu->useCreateResourcesWithRequirements = useCreateResourcesWithRequirements;
+}
+
+VkEmulation* getGlobalVkEmulation() {
+ if (sVkEmulation && !sVkEmulation->live) return nullptr;
+ return sVkEmulation;
+}
+
+void teardownGlobalVkEmulation() {
+ if (!sVkEmulation) return;
+
+ // Don't try to tear down something that did not set up completely; too risky
+ if (!sVkEmulation->live) return;
+
+ freeExternalMemory(sVkEmulation->dvk, &sVkEmulation->staging.memory);
+
+ sVkEmulation->ivk->vkDestroyDevice(sVkEmulation->device, nullptr);
+ sVkEmulation->gvk->vkDestroyInstance(sVkEmulation->instance, nullptr);
+}
+
+// Precondition: sVkEmulation has valid device support info
+bool allocExternalMemory(
+ VulkanDispatch* vk, VkEmulation::ExternalMemoryInfo* info,
+ bool actuallyExternal) {
+
+ VkExportMemoryAllocateInfo exportAi = {
+ VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, 0,
+ VK_EXT_MEMORY_HANDLE_TYPE_BIT,
+ };
+
+ VkExportMemoryAllocateInfo* exportAiPtr = nullptr;
+
+ if (sVkEmulation->deviceInfo.supportsExternalMemory &&
+ actuallyExternal) {
+ exportAiPtr = &exportAi;
+ }
+
+ VkMemoryAllocateInfo allocInfo = {
+ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ exportAiPtr,
+ info->size,
+ info->typeIndex,
+ };
+
+ VkResult allocRes = vk->vkAllocateMemory(
+ sVkEmulation->device,
+ &allocInfo, nullptr,
+ &info->memory);
+
+ if (allocRes != VK_SUCCESS) {
+ LOG(VERBOSE) << "allocExternalMemory: failed in vkAllocateMemory: "
+ << allocRes;
+ return false;
+ }
+
+ if (sVkEmulation->deviceInfo
+ .memProps
+ .memoryTypes[info->typeIndex]
+ .propertyFlags &
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+ VkResult mapRes = vk->vkMapMemory(sVkEmulation->device, info->memory, 0,
+ info->size, 0, &info->mappedPtr);
+ if (mapRes != VK_SUCCESS) {
+ LOG(VERBOSE) << "allocExternalMemory: failed in vkMapMemory: "
+ << mapRes;
+ return false;
+ }
+ }
+
+ if (!sVkEmulation->deviceInfo.supportsExternalMemory ||
+ !actuallyExternal) {
+ return true;
+ }
+
+#ifdef _WIN32
+ VkMemoryGetWin32HandleInfoKHR getWin32HandleInfo = {
+ VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR, 0,
+ info->memory, VK_EXT_MEMORY_HANDLE_TYPE_BIT,
+ };
+ VkResult exportRes =
+ sVkEmulation->deviceInfo.getMemoryHandleFunc(
+ sVkEmulation->device, &getWin32HandleInfo,
+ &info->exportedHandle);
+#else
+ 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);
+#endif
+
+ if (exportRes != VK_SUCCESS) {
+ LOG(VERBOSE) << "allocExternalMemory: Failed to get external memory "
+ "native handle: "
+ << exportRes;
+ return false;
+ }
+
+ info->actuallyExternal = true;
+
+ return true;
+}
+
+void freeExternalMemory(VulkanDispatch* vk,
+ VkEmulation::ExternalMemoryInfo* info) {
+ if (!info->memory)
+ return;
+
+ if (sVkEmulation->deviceInfo.memProps.memoryTypes[info->typeIndex]
+ .propertyFlags &
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
+ vk->vkUnmapMemory(sVkEmulation->device, info->memory);
+ info->mappedPtr = nullptr;
+ }
+
+ vk->vkFreeMemory(sVkEmulation->device, info->memory, nullptr);
+
+ info->memory = VK_NULL_HANDLE;
+
+ if (info->exportedHandle != VK_EXT_MEMORY_HANDLE_INVALID) {
+#ifdef _WIN32
+ CloseHandle(info->exportedHandle);
+#else
+ close(info->exportedHandle);
+#endif
+ info->exportedHandle = VK_EXT_MEMORY_HANDLE_INVALID;
+ }
+}
+
+bool importExternalMemory(VulkanDispatch* vk,
+ VkDevice targetDevice,
+ const VkEmulation::ExternalMemoryInfo* info,
+ VkDeviceMemory* out) {
+#ifdef _WIN32
+ VkImportMemoryWin32HandleInfoKHR importInfo = {
+ VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, 0,
+ VK_EXT_MEMORY_HANDLE_TYPE_BIT,
+ info->exportedHandle,
+ 0,
+ };
+#else
+ VkImportMemoryFdInfoKHR importInfo = {
+ VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, 0,
+ VK_EXT_MEMORY_HANDLE_TYPE_BIT,
+ dupExternalMemory(info->exportedHandle),
+ };
+#endif
+ VkMemoryAllocateInfo allocInfo = {
+ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ &importInfo,
+ info->size,
+ info->typeIndex,
+ };
+
+ VkResult res = vk->vkAllocateMemory(targetDevice, &allocInfo, nullptr, out);
+
+ if (res != VK_SUCCESS) {
+ LOG(ERROR) << "importExternalMemory: Failed with " << res;
+ return false;
+ }
+
+ return true;
+}
+
+bool importExternalMemoryDedicatedImage(
+ VulkanDispatch* vk,
+ VkDevice targetDevice,
+ const VkEmulation::ExternalMemoryInfo* info,
+ VkImage image,
+ VkDeviceMemory* out) {
+
+ VkMemoryDedicatedAllocateInfo dedicatedInfo = {
+ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, 0,
+ image,
+ VK_NULL_HANDLE,
+ };
+
+#ifdef _WIN32
+ VkImportMemoryWin32HandleInfoKHR importInfo = {
+ VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR,
+ &dedicatedInfo,
+ VK_EXT_MEMORY_HANDLE_TYPE_BIT,
+ info->exportedHandle,
+ 0,
+ };
+#else
+ VkImportMemoryFdInfoKHR importInfo = {
+ VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR,
+ &dedicatedInfo,
+ VK_EXT_MEMORY_HANDLE_TYPE_BIT,
+ info->exportedHandle,
+ };
+#endif
+ VkMemoryAllocateInfo allocInfo = {
+ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ &importInfo,
+ info->size,
+ info->typeIndex,
+ };
+
+ VkResult res = vk->vkAllocateMemory(targetDevice, &allocInfo, nullptr, out);
+
+ if (res != VK_SUCCESS) {
+ LOG(ERROR) << "importExternalMemoryDedicatedImage: Failed with " << res;
+ return false;
+ }
+
+ return true;
+}
+
+static VkFormat glFormat2VkFormat(GLint internalformat) {
+ switch (internalformat) {
+ case GL_LUMINANCE:
+ return VK_FORMAT_R8_UNORM;
+ case GL_RGB:
+ case GL_RGB8:
+ return VK_FORMAT_R8G8B8_UNORM;
+ case GL_RGB565:
+ return VK_FORMAT_R5G6B5_UNORM_PACK16;
+ case GL_RGB16F:
+ return VK_FORMAT_R16G16B16_SFLOAT;
+ case GL_RGBA:
+ case GL_RGBA8:
+ return VK_FORMAT_R8G8B8A8_UNORM;
+ case GL_RGB5_A1_OES:
+ return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
+ case GL_RGBA4_OES:
+ return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
+ case GL_RGB10_A2:
+ case GL_UNSIGNED_INT_10_10_10_2_OES:
+ return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
+ case GL_RGBA16F:
+ return VK_FORMAT_R16G16B16A16_SFLOAT;
+ case GL_BGRA_EXT:
+ case GL_BGRA8_EXT:
+ return VK_FORMAT_B8G8R8A8_UNORM;;
+ default:
+ return VK_FORMAT_R8G8B8A8_UNORM;
+ }
+};
+
+bool isColorBufferVulkanCompatible(uint32_t colorBufferHandle) {
+ auto fb = FrameBuffer::getFB();
+
+ int width;
+ int height;
+ GLint internalformat;
+
+ if (!fb->getColorBufferInfo(colorBufferHandle, &width, &height,
+ &internalformat)) {
+ return false;
+ }
+
+ VkFormat vkFormat = glFormat2VkFormat(internalformat);
+
+ for (const auto& supportInfo : sVkEmulation->imageSupportInfo) {
+ if (supportInfo.format == vkFormat && supportInfo.supported) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static uint32_t lastGoodTypeIndex(uint32_t indices) {
+ for (uint32_t i = 31; i >= 0; --i) {
+ if (indices & (1 << i)) {
+ return i;
+ }
+ }
+ return 0;
+}
+
+bool setupVkColorBuffer(uint32_t colorBufferHandle, bool vulkanOnly, bool* exported, VkDeviceSize* allocSize) {
+ if (!isColorBufferVulkanCompatible(colorBufferHandle)) return false;
+
+ auto vk = sVkEmulation->dvk;
+
+ auto fb = FrameBuffer::getFB();
+
+ int width;
+ int height;
+ GLint internalformat;
+
+ if (!fb->getColorBufferInfo(colorBufferHandle, &width, &height,
+ &internalformat)) {
+ return false;
+ }
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+
+ // Already setup
+ if (infoPtr) {
+ // Update the allocation size to what the host driver wanted, or we
+ // might get VK_ERROR_OUT_OF_DEVICE_MEMORY and a host crash
+ if (allocSize) *allocSize = infoPtr->memory.size;
+ return true;
+ }
+
+ VkFormat vkFormat = glFormat2VkFormat(internalformat);
+
+ VkEmulation::ColorBufferInfo res;
+
+ res.handle = colorBufferHandle;
+
+ // TODO
+ res.frameworkFormat = 0;
+ res.frameworkStride = 0;
+
+ res.extent = { (uint32_t)width, (uint32_t)height, 1 };
+ res.format = vkFormat;
+ res.type = VK_IMAGE_TYPE_2D;
+ res.tiling = VK_IMAGE_TILING_OPTIMAL;
+ res.usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+ VK_IMAGE_USAGE_SAMPLED_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ res.createFlags = 0;
+
+ res.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+
+ // Create the image. If external memory is supported, make it external.
+ VkExternalMemoryImageCreateInfo extImageCi = {
+ VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, 0,
+ VK_EXT_MEMORY_HANDLE_TYPE_BIT,
+ };
+
+ VkExternalMemoryImageCreateInfo* extImageCiPtr = nullptr;
+
+ if (sVkEmulation->deviceInfo.supportsExternalMemory) {
+ extImageCiPtr = &extImageCi;
+ }
+
+ VkImageCreateInfo imageCi = {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, extImageCiPtr,
+ res.createFlags,
+ res.type,
+ res.format,
+ res.extent,
+ 1, 1,
+ VK_SAMPLE_COUNT_1_BIT,
+ res.tiling,
+ res.usageFlags,
+ VK_SHARING_MODE_EXCLUSIVE, 0, nullptr,
+ VK_IMAGE_LAYOUT_UNDEFINED,
+ };
+
+ VkResult createRes = vk->vkCreateImage(sVkEmulation->device, &imageCi,
+ nullptr, &res.image);
+ if (createRes != VK_SUCCESS) {
+ LOG(VERBOSE) << "Failed to create Vulkan image for ColorBuffer "
+ << colorBufferHandle;
+ return false;
+ }
+
+ vk->vkGetImageMemoryRequirements(sVkEmulation->device, res.image,
+ &res.memReqs);
+
+ res.memory.size = res.memReqs.size;
+ res.memory.typeIndex = lastGoodTypeIndex(res.memReqs.memoryTypeBits);
+
+ LOG(VERBOSE) << "ColorBuffer " << colorBufferHandle
+ << "allocation size and type index: " << res.memory.size
+ << ", " << res.memory.typeIndex;
+
+ bool allocRes = allocExternalMemory(vk, &res.memory);
+
+ if (!allocRes) {
+ LOG(VERBOSE) << "Failed to allocate ColorBuffer with Vulkan backing.";
+ }
+
+ VkResult bindImageMemoryRes =
+ vk->vkBindImageMemory(sVkEmulation->device, res.image, res.memory.memory, 0);
+
+ if (bindImageMemoryRes != VK_SUCCESS) {
+ fprintf(stderr, "%s: Failed to bind image memory. %d\n", __func__,
+ bindImageMemoryRes);
+ return bindImageMemoryRes;
+ }
+
+ if (sVkEmulation->instanceSupportsMoltenVK) {
+ // Create IOSurface by passing null surface argument.
+ VkResult useIOSurfaceRes = sVkEmulation->useIOSurfaceFunc(res.image, nullptr);
+ if (useIOSurfaceRes != VK_SUCCESS) {
+ fprintf(stderr, "%s: Failed to create IOSurface. %d\n", __func__,
+ useIOSurfaceRes);
+ return false;
+ }
+ // Retrieve a reference to the IOSurface created above.
+ sVkEmulation->getIOSurfaceFunc(res.image, &res.ioSurface);
+ if (!res.ioSurface) {
+ fprintf(stderr, "%s: Failed to get IOSurface.\n", __func__);
+ return false;
+ }
+#ifdef __APPLE__
+ CFRetain(res.ioSurface);
+#endif
+ }
+
+ if (sVkEmulation->deviceInfo.supportsExternalMemory &&
+ sVkEmulation->deviceInfo.glInteropSupported &&
+ FrameBuffer::getFB()->importMemoryToColorBuffer(
+ dupExternalMemory(res.memory.exportedHandle),
+ res.memory.size,
+ false /* dedicated */,
+ res.tiling == VK_IMAGE_TILING_LINEAR,
+ vulkanOnly,
+ colorBufferHandle)) {
+ res.glExported = true;
+ }
+
+ if (exported) *exported = res.glExported;
+ if (allocSize) *allocSize = res.memory.size;
+
+ sVkEmulation->colorBuffers[colorBufferHandle] = res;
+
+ return allocRes;
+}
+
+bool teardownVkColorBuffer(uint32_t colorBufferHandle) {
+ if (!sVkEmulation || !sVkEmulation->live) return false;
+
+ auto vk = sVkEmulation->dvk;
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+
+ if (!infoPtr) return false;
+
+ auto& info = *infoPtr;
+
+ vk->vkDestroyImage(sVkEmulation->device, info.image, nullptr);
+ freeExternalMemory(vk, &info.memory);
+
+#ifdef __APPLE__
+ if (info.ioSurface) {
+ CFRelease(info.ioSurface);
+ }
+#endif
+
+ sVkEmulation->colorBuffers.erase(colorBufferHandle);
+
+ return true;
+}
+
+VkEmulation::ColorBufferInfo getColorBufferInfo(uint32_t colorBufferHandle) {
+ VkEmulation::ColorBufferInfo res;
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+
+ if (!infoPtr) return res;
+
+ res = *infoPtr;
+ return res;
+}
+
+bool updateColorBufferFromVkImage(uint32_t colorBufferHandle) {
+ if (!sVkEmulation || !sVkEmulation->live) return false;
+
+ auto vk = sVkEmulation->dvk;
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+
+ if (!infoPtr) {
+ // Color buffer not found; this is usually OK.
+ return false;
+ }
+
+ if (!infoPtr->image) {
+ fprintf(stderr, "%s: error: ColorBuffer 0x%x has no VkImage\n", __func__, colorBufferHandle);
+ return false;
+ }
+
+ if (infoPtr->glExported ||
+ (infoPtr->vulkanMode == VkEmulation::ColorBufferInfo::VulkanMode::VulkanOnly)) {
+ // No sync needed if exported to GL or in Vulkan-only mode
+ return true;
+ }
+
+ // Record our synchronization commands.
+ VkCommandBufferBeginInfo beginInfo = {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0,
+ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ nullptr /* no inheritance info */,
+ };
+
+ vk->vkBeginCommandBuffer(
+ sVkEmulation->commandBuffer,
+ &beginInfo);
+
+ // From the spec: If an application does not need the contents of a resource
+ // to remain valid when transferring from one queue family to another, then
+ // the ownership transfer should be skipped.
+
+ // We definitely need to transition the image to
+ // VK_TRANSFER_SRC_OPTIMAL and back.
+
+ VkImageMemoryBarrier presentToTransferSrc = {
+ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0,
+ 0,
+ VK_ACCESS_HOST_READ_BIT,
+ infoPtr->currentLayout,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_QUEUE_FAMILY_IGNORED,
+ VK_QUEUE_FAMILY_IGNORED,
+ infoPtr->image,
+ {
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ 0, 1, 0, 1,
+ },
+ };
+
+ vk->vkCmdPipelineBarrier(
+ sVkEmulation->commandBuffer,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &presentToTransferSrc);
+
+ infoPtr->currentLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+
+ // Copy to staging buffer
+ uint32_t bpp = 4; /* format always rgba8...not */
+ switch (infoPtr->format) {
+ case VK_FORMAT_R5G6B5_UNORM_PACK16:
+ bpp = 2;
+ break;
+ case VK_FORMAT_R8G8B8_UNORM:
+ bpp = 3;
+ break;
+ default:
+ case VK_FORMAT_R8G8B8A8_UNORM:
+ bpp = 4;
+ break;
+ }
+ VkBufferImageCopy region = {
+ 0 /* buffer offset */,
+ infoPtr->extent.width,
+ infoPtr->extent.height,
+ {
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ 0, 0, 1,
+ },
+ { 0, 0, 0 },
+ infoPtr->extent,
+ };
+
+ vk->vkCmdCopyImageToBuffer(
+ sVkEmulation->commandBuffer,
+ infoPtr->image,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ sVkEmulation->staging.buffer,
+ 1, ®ion);
+
+ vk->vkEndCommandBuffer(sVkEmulation->commandBuffer);
+
+ VkSubmitInfo submitInfo = {
+ VK_STRUCTURE_TYPE_SUBMIT_INFO, 0,
+ 0, nullptr,
+ nullptr,
+ 1, &sVkEmulation->commandBuffer,
+ 0, nullptr,
+ };
+
+ vk->vkQueueSubmit(
+ sVkEmulation->queue,
+ 1, &submitInfo,
+ sVkEmulation->commandBufferFence);
+
+ static constexpr uint64_t ANB_MAX_WAIT_NS =
+ 5ULL * 1000ULL * 1000ULL * 1000ULL;
+
+ vk->vkWaitForFences(
+ sVkEmulation->device, 1, &sVkEmulation->commandBufferFence,
+ VK_TRUE, ANB_MAX_WAIT_NS);
+ vk->vkResetFences(
+ sVkEmulation->device, 1, &sVkEmulation->commandBufferFence);
+
+ VkMappedMemoryRange toInvalidate = {
+ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
+ sVkEmulation->staging.memory.memory,
+ 0, VK_WHOLE_SIZE,
+ };
+
+ vk->vkInvalidateMappedMemoryRanges(
+ sVkEmulation->device, 1, &toInvalidate);
+
+ FrameBuffer::getFB()->
+ replaceColorBufferContents(
+ colorBufferHandle,
+ sVkEmulation->staging.memory.mappedPtr,
+ bpp * infoPtr->extent.width * infoPtr->extent.height);
+
+ return true;
+}
+
+bool updateVkImageFromColorBuffer(uint32_t colorBufferHandle) {
+ if (!sVkEmulation || !sVkEmulation->live) return false;
+
+ auto vk = sVkEmulation->dvk;
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBufferHandle);
+
+ if (!infoPtr) {
+ // Color buffer not found; this is usually OK.
+ return false;
+ }
+
+ if (infoPtr->glExported ||
+ (infoPtr->vulkanMode == VkEmulation::ColorBufferInfo::VulkanMode::VulkanOnly)) {
+ // No sync needed if exported to GL or in Vulkan-only mode
+ return true;
+ }
+
+ size_t cbNumBytes = 0;
+ bool readRes = FrameBuffer::getFB()->
+ readColorBufferContents(
+ colorBufferHandle, &cbNumBytes, nullptr);
+
+ if (!readRes) {
+ fprintf(stderr, "%s: Failed to read color buffer 0x%x\n",
+ __func__, colorBufferHandle);
+ return false;
+ }
+
+ if (cbNumBytes > sVkEmulation->staging.memory.size) {
+ fprintf(stderr,
+ "%s: Not enough space to read to staging buffer. "
+ "Wanted: 0x%llx Have: 0x%llx\n", __func__,
+ (unsigned long long)cbNumBytes,
+ (unsigned long long)(sVkEmulation->staging.memory.size));
+ return false;
+ }
+
+ readRes = FrameBuffer::getFB()->
+ readColorBufferContents(
+ colorBufferHandle, &cbNumBytes,
+ sVkEmulation->staging.memory.mappedPtr);
+
+ if (!readRes) {
+ fprintf(stderr, "%s: Failed to read color buffer 0x%x (at glReadPixels)\n",
+ __func__, colorBufferHandle);
+ return false;
+ }
+
+ // Record our synchronization commands.
+ VkCommandBufferBeginInfo beginInfo = {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, 0,
+ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ nullptr /* no inheritance info */,
+ };
+
+ vk->vkBeginCommandBuffer(
+ sVkEmulation->commandBuffer,
+ &beginInfo);
+
+ // From the spec: If an application does not need the contents of a resource
+ // to remain valid when transferring from one queue family to another, then
+ // the ownership transfer should be skipped.
+
+ // We definitely need to transition the image to
+ // VK_TRANSFER_SRC_OPTIMAL and back.
+
+ VkImageMemoryBarrier presentToTransferSrc = {
+ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, 0,
+ 0,
+ VK_ACCESS_HOST_READ_BIT,
+ infoPtr->currentLayout,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_QUEUE_FAMILY_IGNORED,
+ VK_QUEUE_FAMILY_IGNORED,
+ infoPtr->image,
+ {
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ 0, 1, 0, 1,
+ },
+ };
+
+ infoPtr->currentLayout =
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+
+ vk->vkCmdPipelineBarrier(
+ sVkEmulation->commandBuffer,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &presentToTransferSrc);
+
+ // Copy to staging buffer
+ uint32_t bpp = 4; /* format always rgba8...not */
+ switch (infoPtr->format) {
+ case VK_FORMAT_R5G6B5_UNORM_PACK16:
+ bpp = 2;
+ break;
+ case VK_FORMAT_R8G8B8_UNORM:
+ bpp = 3;
+ break;
+ default:
+ case VK_FORMAT_R8G8B8A8_UNORM:
+ bpp = 4;
+ break;
+ }
+ VkBufferImageCopy region = {
+ 0 /* buffer offset */,
+ infoPtr->extent.width,
+ infoPtr->extent.height,
+ {
+ VK_IMAGE_ASPECT_COLOR_BIT,
+ 0, 0, 1,
+ },
+ { 0, 0, 0 },
+ infoPtr->extent,
+ };
+
+ vk->vkCmdCopyBufferToImage(
+ sVkEmulation->commandBuffer,
+ sVkEmulation->staging.buffer,
+ infoPtr->image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ 1, ®ion);
+
+ vk->vkEndCommandBuffer(sVkEmulation->commandBuffer);
+
+ VkSubmitInfo submitInfo = {
+ VK_STRUCTURE_TYPE_SUBMIT_INFO, 0,
+ 0, nullptr,
+ nullptr,
+ 1, &sVkEmulation->commandBuffer,
+ 0, nullptr,
+ };
+
+ vk->vkQueueSubmit(
+ sVkEmulation->queue,
+ 1, &submitInfo,
+ sVkEmulation->commandBufferFence);
+
+ static constexpr uint64_t ANB_MAX_WAIT_NS =
+ 5ULL * 1000ULL * 1000ULL * 1000ULL;
+
+ vk->vkWaitForFences(
+ sVkEmulation->device, 1, &sVkEmulation->commandBufferFence,
+ VK_TRUE, ANB_MAX_WAIT_NS);
+ vk->vkResetFences(
+ sVkEmulation->device, 1, &sVkEmulation->commandBufferFence);
+
+ VkMappedMemoryRange toInvalidate = {
+ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, 0,
+ sVkEmulation->staging.memory.memory,
+ 0, VK_WHOLE_SIZE,
+ };
+
+ vk->vkInvalidateMappedMemoryRanges(
+ sVkEmulation->device, 1, &toInvalidate);
+ return true;
+}
+
+VK_EXT_MEMORY_HANDLE getColorBufferExtMemoryHandle(uint32_t colorBuffer) {
+ if (!sVkEmulation || !sVkEmulation->live) return VK_EXT_MEMORY_HANDLE_INVALID;
+
+ auto vk = sVkEmulation->dvk;
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBuffer);
+
+ if (!infoPtr) {
+ // Color buffer not found; this is usually OK.
+ return VK_EXT_MEMORY_HANDLE_INVALID;
+ }
+
+ return infoPtr->memory.exportedHandle;
+}
+
+bool setColorBufferVulkanMode(uint32_t colorBuffer, uint32_t vulkanMode) {
+ if (!sVkEmulation || !sVkEmulation->live) return VK_EXT_MEMORY_HANDLE_INVALID;
+
+ auto vk = sVkEmulation->dvk;
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBuffer);
+
+ if (!infoPtr) {
+ return false;
+ }
+
+ infoPtr->vulkanMode = static_cast<VkEmulation::ColorBufferInfo::VulkanMode>(vulkanMode);
+
+ return true;
+}
+
+IOSurfaceRef getColorBufferIOSurface(uint32_t colorBuffer) {
+ if (!sVkEmulation || !sVkEmulation->live) return nullptr;
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto infoPtr = android::base::find(sVkEmulation->colorBuffers, colorBuffer);
+
+ if (!infoPtr) {
+ // Color buffer not found; this is usually OK.
+ return nullptr;
+ }
+
+#ifdef __APPLE__
+ CFRetain(infoPtr->ioSurface);
+#endif
+ return infoPtr->ioSurface;
+}
+
+VkExternalMemoryHandleTypeFlags
+transformExternalMemoryHandleTypeFlags_tohost(
+ VkExternalMemoryHandleTypeFlags bits) {
+
+ VkExternalMemoryHandleTypeFlags res = bits;
+
+ // Transform Android/Fuchsia/Linux bits to host bits.
+ if (bits & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT) {
+ res &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
+ }
+
+ if (bits & VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID) {
+ res &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
+ res |= VK_EXT_MEMORY_HANDLE_TYPE_BIT;
+ }
+
+ if (bits & VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA) {
+ res &= ~VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA;
+ res |= VK_EXT_MEMORY_HANDLE_TYPE_BIT;
+ }
+ return res;
+}
+
+VkExternalMemoryHandleTypeFlags
+transformExternalMemoryHandleTypeFlags_fromhost(
+ VkExternalMemoryHandleTypeFlags hostBits,
+ VkExternalMemoryHandleTypeFlags wantedGuestHandleType) {
+
+ VkExternalMemoryHandleTypeFlags res = hostBits;
+
+ if (res & VK_EXT_MEMORY_HANDLE_TYPE_BIT) {
+ res &= ~VK_EXT_MEMORY_HANDLE_TYPE_BIT;
+ res |= wantedGuestHandleType;
+ }
+
+ return res;
+}
+
+VkExternalMemoryProperties
+transformExternalMemoryProperties_tohost(
+ VkExternalMemoryProperties props) {
+ VkExternalMemoryProperties res = props;
+ res.exportFromImportedHandleTypes =
+ transformExternalMemoryHandleTypeFlags_tohost(
+ props.exportFromImportedHandleTypes);
+ res.compatibleHandleTypes =
+ transformExternalMemoryHandleTypeFlags_tohost(
+ props.compatibleHandleTypes);
+ return res;
+}
+
+VkExternalMemoryProperties
+transformExternalMemoryProperties_fromhost(
+ VkExternalMemoryProperties props,
+ VkExternalMemoryHandleTypeFlags wantedGuestHandleType) {
+ VkExternalMemoryProperties res = props;
+ res.exportFromImportedHandleTypes =
+ transformExternalMemoryHandleTypeFlags_fromhost(
+ props.exportFromImportedHandleTypes,
+ wantedGuestHandleType);
+ res.compatibleHandleTypes =
+ transformExternalMemoryHandleTypeFlags_fromhost(
+ props.compatibleHandleTypes,
+ wantedGuestHandleType);
+ return res;
+}
+
+} // namespace goldfish_vk