vulkan/gles interop: ensure consistent selection of physdevs / interop
This should solve two potential issues:
1. Use VkPhysicalDeviceIDProperties to advertise only the physical
device we selected to the guest. If we ended up selecting a different
physical device in VkCommonOperations versus on_vkCreateDevice from the
guest, problems can happen.
2. Ensure device UUID between gles/vk match. If they don't, we can't use
interop, even if the gl side reports support for the interop extensions
(think NV gl alongside Intel vk). The gles side should have a way to
query the UUID of the device (tested with native GL and GLES on Linux
and native GL on Windows); otherwise, we can't use zero-copy interop.
Bug: 190561902
Change-Id: I6c6e217a3c09566ab4c0d47a91432c58343efc54
diff --git a/stream-servers/FrameBuffer.cpp b/stream-servers/FrameBuffer.cpp
index b9966e0..daef6c0 100644
--- a/stream-servers/FrameBuffer.cpp
+++ b/stream-servers/FrameBuffer.cpp
@@ -354,20 +354,29 @@
// used by underlying EGL driver might become invalid,
// preventing new contexts from being created that share
// against those contexts.
+ goldfish_vk::VkEmulation* vkEmu = nullptr;
if (feature_is_enabled(kFeature_Vulkan)) {
auto dispatch = emugl::vkDispatch(false /* not for testing */);
- auto emu = goldfish_vk::createOrGetGlobalVkEmulation(dispatch);
+ vkEmu = goldfish_vk::createOrGetGlobalVkEmulation(dispatch);
bool useDeferredCommands =
android::base::getEnvironmentVariable("ANDROID_EMU_VK_DISABLE_DEFERRED_COMMANDS").empty();
bool useCreateResourcesWithRequirements =
android::base::getEnvironmentVariable("ANDROID_EMU_VK_DISABLE_USE_CREATE_RESOURCES_WITH_REQUIREMENTS").empty();
- goldfish_vk::setUseDeferredCommands(emu, useDeferredCommands);
- goldfish_vk::setUseCreateResourcesWithRequirements(emu, useCreateResourcesWithRequirements);
+ goldfish_vk::setUseDeferredCommands(vkEmu, useDeferredCommands);
+ goldfish_vk::setUseCreateResourcesWithRequirements(vkEmu, useCreateResourcesWithRequirements);
if (feature_is_enabled(kFeature_VulkanNativeSwapchain)) {
fb->m_displayVk = std::make_unique<DisplayVk>(
- *dispatch, emu->physdev, emu->queueFamilyIndex,
- emu->queueFamilyIndex, emu->device, emu->queue, emu->queue);
- fb->m_vkInstance = emu->instance;
+ *dispatch, vkEmu->physdev, vkEmu->queueFamilyIndex,
+ vkEmu->queueFamilyIndex, vkEmu->device, vkEmu->queue, vkEmu->queue);
+ fb->m_vkInstance = vkEmu->instance;
+ }
+ if (vkEmu->deviceInfo.supportsIdProperties) {
+ GL_LOG("Supports id properties, got a vulkan device UUID");
+ fprintf(stderr, "%s: Supports id properties, got a vulkan device UUID\n", __func__);
+ memcpy(fb->m_vulkanUUID, vkEmu->deviceInfo.idProps.deviceUUID, VK_UUID_SIZE);
+ } else {
+ GL_LOG("Doesn't support id properties, no vulkan device UUID");
+ fprintf(stderr, "%s: Doesn't support id properties, no vulkan device UUID\n", __func__);
}
}
@@ -624,6 +633,47 @@
fb->m_glRenderer = std::string((const char*)s_gles2.glGetString(GL_RENDERER));
fb->m_glVersion = std::string((const char*)s_gles2.glGetString(GL_VERSION));
+ // Attempt to get the device UUID of the gles and match with Vulkan. If
+ // they match, interop is possible. If they don't, then don't trust the
+ // result of interop query to egl and fall back to CPU copy, as we might
+ // have initialized Vulkan devices and GLES contexts from different
+ // physical devices.
+
+ bool vkglesUuidsGood = true;
+
+ // First, if the VkEmulation instance doesn't support ext memory capabilities,
+ // it won't support uuids.
+ if (!vkEmu || !vkEmu->deviceInfo.supportsIdProperties) {
+ vkglesUuidsGood = false;
+ }
+
+ s_gles2.glGetError();
+
+ GLint numDeviceUuids = 0;
+ s_gles2.glGetIntegerv(GL_NUM_DEVICE_UUIDS_EXT, &numDeviceUuids);
+
+ // If underlying gles doesn't support UUID query, we definitely don't
+ // support interop and should not proceed further.
+
+ if (!numDeviceUuids || 1 != numDeviceUuids) {
+ // If numDeviceUuids != 1 it's unclear what gles we're using (SLI? Xinerama?)
+ // and we shouldn't try to interop.
+ vkglesUuidsGood = false;
+ }
+
+ if (vkglesUuidsGood && 1 == numDeviceUuids) {
+ s_gles2.glGetUnsignedBytei_vEXT(GL_DEVICE_UUID_EXT, 0, fb->m_glesUUID);
+ GL_LOG("Underlying gles supports UUID");
+ if (0 == memcmp(fb->m_vulkanUUID, fb->m_glesUUID, VK_UUID_SIZE)) {
+ GL_LOG("vk/gles UUIDs match");
+ fprintf(stderr, "%s: vk/gles UUIDs match\n", __func__);
+ } else {
+ GL_LOG("vk/gles UUIDs do not match");
+ fprintf(stderr, "%s: vk/gles UUIDs do not match\n", __func__);
+ vkglesUuidsGood = false;
+ }
+ }
+
DBG("GL Vendor %s\n", fb->m_glVendor.c_str());
DBG("GL Renderer %s\n", fb->m_glRenderer.c_str());
DBG("GL Extensions %s\n", fb->m_glVersion.c_str());
@@ -641,6 +691,9 @@
if (s_egl.eglQueryVulkanInteropSupportANDROID) {
fb->m_vulkanInteropSupported =
s_egl.eglQueryVulkanInteropSupportANDROID();
+ if (!vkglesUuidsGood) {
+ fb->m_vulkanInteropSupported = false;
+ }
}
fprintf(stderr, "%s: interop? %d\n", __func__, fb->m_vulkanInteropSupported);
@@ -768,6 +821,8 @@
setDisplayPose(displayId, 0, 0, getWidth(), getHeight(), 0);
m_perfThread->start();
+ memset(m_vulkanUUID, 0x0, 16);
+ memset(m_glesUUID, 0x0, 16);
}
FrameBuffer::~FrameBuffer() {
diff --git a/stream-servers/FrameBuffer.h b/stream-servers/FrameBuffer.h
index 42ff034..79353ba 100644
--- a/stream-servers/FrameBuffer.h
+++ b/stream-servers/FrameBuffer.h
@@ -780,5 +780,10 @@
std::unique_ptr<DisplayVk> m_displayVk;
VkInstance m_vkInstance = VK_NULL_HANDLE;
VkSurfaceKHR m_vkSurface = VK_NULL_HANDLE;
+
+ // UUIDs of physical devices for Vulkan and GLES, respectively. In most
+ // cases, this determines whether we can support zero-copy interop.
+ uint8_t m_vulkanUUID[VK_UUID_SIZE];
+ uint8_t m_glesUUID[VK_UUID_SIZE];
};
#endif
diff --git a/stream-servers/glestranslator/GLES_V2/GLESv2Imp.cpp b/stream-servers/glestranslator/GLES_V2/GLESv2Imp.cpp
index b31fa34..a8cfd56 100644
--- a/stream-servers/glestranslator/GLES_V2/GLESv2Imp.cpp
+++ b/stream-servers/glestranslator/GLES_V2/GLESv2Imp.cpp
@@ -4476,11 +4476,13 @@
// Common between GL_EXT_memory_object and GL_EXT_semaphore
GL_APICALL void GL_APIENTRY glGetUnsignedBytevEXT(GLenum pname, GLubyte* data) {
GET_CTX_V2();
+ if (!ctx->dispatcher().glGetUnsignedBytevEXT) return;
ctx->dispatcher().glGetUnsignedBytevEXT(pname, data);
}
GL_APICALL void GL_APIENTRY glGetUnsignedBytei_vEXT(GLenum target, GLuint index, GLubyte* data) {
GET_CTX_V2();
+ if (!ctx->dispatcher().glGetUnsignedBytei_vEXT) return;
ctx->dispatcher().glGetUnsignedBytei_vEXT(target, index, data);
}
diff --git a/stream-servers/vulkan/VkCommonOperations.cpp b/stream-servers/vulkan/VkCommonOperations.cpp
index b0b37bf..18bda70 100644
--- a/stream-servers/vulkan/VkCommonOperations.cpp
+++ b/stream-servers/vulkan/VkCommonOperations.cpp
@@ -602,6 +602,18 @@
ivk->vkGetInstanceProcAddr(
sVkEmulation->instance,
"vkGetPhysicalDeviceImageFormatProperties2KHR"));
+ sVkEmulation->getPhysicalDeviceProperties2Func = reinterpret_cast<
+ PFN_vkGetPhysicalDeviceProperties2KHR>(
+ ivk->vkGetInstanceProcAddr(
+ sVkEmulation->instance,
+ "vkGetPhysicalDeviceProperties2KHR"));
+ if (!sVkEmulation->getPhysicalDeviceProperties2Func) {
+ sVkEmulation->getPhysicalDeviceProperties2Func = reinterpret_cast<
+ PFN_vkGetPhysicalDeviceProperties2KHR>(
+ ivk->vkGetInstanceProcAddr(
+ sVkEmulation->instance,
+ "vkGetPhysicalDeviceProperties2"));
+ }
}
if (sVkEmulation->instanceSupportsMoltenVK) {
@@ -664,6 +676,25 @@
if (sVkEmulation->instanceSupportsExternalMemoryCapabilities) {
deviceInfos[i].supportsExternalMemory = extensionsSupported(
deviceExts, externalMemoryDeviceExtNames);
+ deviceInfos[i].supportsIdProperties =
+ sVkEmulation->getPhysicalDeviceProperties2Func != nullptr;
+ if (!sVkEmulation->getPhysicalDeviceProperties2Func) {
+ fprintf(stderr, "%s: warning: device should support ID properties "
+ "but vkGetPhysicalDeviceProperties2 could not be found\n", __func__);
+ }
+ }
+
+ if (deviceInfos[i].supportsIdProperties) {
+ VkPhysicalDeviceIDPropertiesKHR idProps = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR, nullptr,
+ };
+ VkPhysicalDeviceProperties2KHR propsWithId = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, &idProps,
+ };
+ sVkEmulation->getPhysicalDeviceProperties2Func(
+ physdevs[i],
+ &propsWithId);
+ deviceInfos[i].idProps = idProps;
}
uint32_t queueFamilyCount = 0;
@@ -728,19 +759,33 @@
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;
+ if (deviceInfos[i].hasGraphicsQueueFamily) deviceScore += 10000;
+ if (deviceInfos[i].supportsExternalMemory) 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;
+ }
+ if (deviceInfos[i].physdevProps.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) {
+ deviceScore += 50;
+ }
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];
+ // If we don't support physical device ID properties,
+ // just pick the first physical device.
+ if (!sVkEmulation->instanceSupportsExternalMemoryCapabilities) {
+ fprintf(stderr, "%s: warning: instance doesn't support "
+ "external memory capabilities, picking first physical device\n", __func__);
+ maxScoringIndex = 0;
+ } else {
+ for (uint32_t i = 0; i < physdevCount; ++i) {
+ if (deviceScores[i] > maxScore) {
+ maxScoringIndex = i;
+ maxScore = deviceScores[i];
+ }
}
}
diff --git a/stream-servers/vulkan/VkCommonOperations.h b/stream-servers/vulkan/VkCommonOperations.h
index 14e2a17..5a8fb67 100644
--- a/stream-servers/vulkan/VkCommonOperations.h
+++ b/stream-servers/vulkan/VkCommonOperations.h
@@ -83,6 +83,8 @@
bool instanceSupportsExternalMemoryCapabilities = false;
PFN_vkGetPhysicalDeviceImageFormatProperties2KHR
getImageFormatProperties2Func = nullptr;
+ PFN_vkGetPhysicalDeviceProperties2KHR
+ getPhysicalDeviceProperties2Func = nullptr;
bool instanceSupportsMoltenVK = false;
PFN_vkSetMTLTextureMVK setMTLTextureFunc = nullptr;
@@ -125,6 +127,7 @@
bool hasGraphicsQueueFamily = false;
bool hasComputeQueueFamily = false;
bool supportsExternalMemory = false;
+ bool supportsIdProperties = false;
bool glInteropSupported = false;
std::vector<uint32_t> graphicsQueueFamilyIndices;
@@ -132,6 +135,7 @@
VkPhysicalDeviceProperties physdevProps;
VkPhysicalDeviceMemoryProperties memProps;
+ VkPhysicalDeviceIDPropertiesKHR idProps;
PFN_vkGetImageMemoryRequirements2KHR getImageMemoryRequirements2Func = nullptr;
PFN_vkGetBufferMemoryRequirements2KHR getBufferMemoryRequirements2Func = nullptr;
diff --git a/stream-servers/vulkan/VkDecoderGlobalState.cpp b/stream-servers/vulkan/VkDecoderGlobalState.cpp
index 2521a9d..3bf7eca 100644
--- a/stream-servers/vulkan/VkDecoderGlobalState.cpp
+++ b/stream-servers/vulkan/VkDecoderGlobalState.cpp
@@ -555,6 +555,8 @@
AutoLock lock(mLock);
+ VkPhysicalDevice advertisedPhysicalDevice = VK_NULL_HANDLE;
+
if (physicalDeviceCount && physicalDevices) {
// Box them up
for (uint32_t i = 0; i < *physicalDeviceCount; ++i) {
@@ -562,7 +564,6 @@
auto& physdevInfo = mPhysdevInfo[physicalDevices[i]];
-
physdevInfo.boxed =
new_boxed_VkPhysicalDevice(physicalDevices[i], vk, false /* does not own dispatch */);
@@ -588,7 +589,64 @@
physicalDevices[i], &queueFamilyPropCount,
physdevInfo.queueFamilyProperties.data());
+ VkPhysicalDevice physdevForIdQuery = physicalDevices[i];
physicalDevices[i] = (VkPhysicalDevice)physdevInfo.boxed;
+
+ if (m_emu->instanceSupportsExternalMemoryCapabilities) {
+ PFN_vkGetPhysicalDeviceProperties2KHR getPhysdevProps2Func = (PFN_vkGetPhysicalDeviceProperties2KHR)(
+ vk->vkGetInstanceProcAddr(
+ instance, "vkGetPhysicalDeviceProperties2KHR"));
+
+ if (!getPhysdevProps2Func) {
+ getPhysdevProps2Func = (PFN_vkGetPhysicalDeviceProperties2KHR)(
+ vk->vkGetInstanceProcAddr(
+ instance, "vkGetPhysicalDeviceProperties2"));
+ }
+
+ if (!getPhysdevProps2Func) {
+ PFN_vkGetPhysicalDeviceProperties2KHR khrFunc = vk->vkGetPhysicalDeviceProperties2KHR;
+ PFN_vkGetPhysicalDeviceProperties2KHR coreFunc = vk->vkGetPhysicalDeviceProperties2;
+
+ if (coreFunc) getPhysdevProps2Func = coreFunc;
+ if (!getPhysdevProps2Func && khrFunc) getPhysdevProps2Func = khrFunc;
+ }
+
+ if (getPhysdevProps2Func) {
+ // We can get the device UUID.
+ VkPhysicalDeviceIDPropertiesKHR idProps = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR, nullptr,
+ };
+ VkPhysicalDeviceProperties2KHR propsWithId = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR, &idProps,
+ };
+ getPhysdevProps2Func(physdevForIdQuery, &propsWithId);
+
+ // The device UUID matches the one in VkCommonOperations.
+ if (0 == memcmp(m_emu->deviceInfo.idProps.deviceUUID, idProps.deviceUUID, VK_UUID_SIZE)) {
+ advertisedPhysicalDevice = physicalDevices[i];
+ }
+ } else {
+ fprintf(stderr, "%s: warning: failed to vkGetPhysicalDeviceProperties2KHR\n", __func__);
+ }
+ } else {
+ // If we don't support ID properties then just advertise only the first physical device.
+ fprintf(stderr, "%s: device id properties not supported, using first physical device\n", __func__);
+ advertisedPhysicalDevice = physicalDevices[0];
+ }
+ }
+ }
+
+ if (physicalDeviceCount && (*physicalDeviceCount > 0)) {
+ // Hide all physical devices except for the advertised one (the one whose UUID matches
+ // the one in VkCommonOperations.cpp)
+ *physicalDeviceCount = 1;
+
+ if (physicalDevices) {
+ if (!advertisedPhysicalDevice) {
+ *physicalDevices = physicalDevices[0];
+ } else {
+ *physicalDevices = advertisedPhysicalDevice;
+ }
}
}
@@ -4802,6 +4860,7 @@
#endif
}
}
+
return res;
}