diff --git a/common/base/SharedMemory_posix.cpp b/common/base/SharedMemory_posix.cpp index efd971c4a..b8eea914a 100644 --- a/common/base/SharedMemory_posix.cpp +++ b/common/base/SharedMemory_posix.cpp @@ -136,6 +136,12 @@ int SharedMemory::openInternal(int oflag, int mode, bool doMapping) { } if (oflag & O_CREAT) { +#if defined(__APPLE__) + // The descriptor keeps the object alive; the name is only a leak if the process dies. + if (mShareType == ShareType::SHARED_MEMORY) { + shm_unlink(mName.c_str()); + } +#endif if (HANDLE_EINTR(fstat(mFd, &sb)) == -1) { err = -errno; close(); diff --git a/host/vulkan/vk_decoder_global_state.cpp b/host/vulkan/vk_decoder_global_state.cpp index 14665afcd..0871ef225 100644 --- a/host/vulkan/vk_decoder_global_state.cpp +++ b/host/vulkan/vk_decoder_global_state.cpp @@ -6716,7 +6716,10 @@ class VkDecoderGlobalState::Impl { static_cast(alignedSize)); } localAllocInfo.allocationSize = alignedSize; - auto memory = SharedMemory("shared-memory-vk-" + std::to_string(sUniqueShmemId++), + // The name is per process: a server that was killed can leave its objects + // behind, and a name that already has a size cannot be resized. + auto memory = SharedMemory("shared-memory-vk-" + std::to_string(getpid()) + "-" + + std::to_string(sUniqueShmemId++), localAllocInfo.allocationSize); if (m_vkEmulation->getFeatures().VulkanAllocateHostVisibleAsUdmabuf.enabled()) { diff --git a/host/vulkan/vk_emulated_physical_device_memory.cpp b/host/vulkan/vk_emulated_physical_device_memory.cpp index 0006d0a7e..2a0fe6892 100644 --- a/host/vulkan/vk_emulated_physical_device_memory.cpp +++ b/host/vulkan/vk_emulated_physical_device_memory.cpp @@ -42,6 +42,42 @@ EmulatedPhysicalDeviceMemoryProperties::EmulatedPhysicalDeviceMemoryProperties( } mGuestColorBufferMemoryTypeIndex = hostColorBufferMemoryTypeIndex; + // With system blobs, host visible memory is shared memory imported as a host pointer, and + // a tiled image cannot be bound to that. A device whose every memory type is host visible + // leaves images nowhere else to go, so the guest gets a device local only type that + // allocates from the same host type without the sharing. It goes first: a strict subset + // of flags has to precede its superset, which is also what makes it the type an image + // is given. + if (features.SystemBlob.enabled() && mHostMemoryProperties.memoryTypeCount > 0 && + mHostMemoryProperties.memoryTypeCount < VK_MAX_MEMORY_TYPES) { + bool allHostVisible = true; + uint32_t hostDeviceLocalIndex = 0; + for (uint32_t i = 0; i < mHostMemoryProperties.memoryTypeCount; i++) { + const VkMemoryPropertyFlags flags = mHostMemoryProperties.memoryTypes[i].propertyFlags; + if (!(flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) { + allHostVisible = false; + } + if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && hostDeviceLocalIndex == 0) { + hostDeviceLocalIndex = i; + } + } + if (allHostVisible) { + for (uint32_t i = mGuestMemoryProperties.memoryTypeCount; i > 0; i--) { + mGuestMemoryProperties.memoryTypes[i] = mGuestMemoryProperties.memoryTypes[i - 1]; + mGuestToHostMemoryTypeIndexMap[i] = i - 1; + mHostToGuestMemoryTypeIndexMap[i - 1] = i; + } + mGuestMemoryProperties.memoryTypes[0] = VkMemoryType{ + .propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .heapIndex = mHostMemoryProperties.memoryTypes[hostDeviceLocalIndex].heapIndex, + }; + mGuestMemoryProperties.memoryTypeCount++; + mGuestToHostMemoryTypeIndexMap[0] = hostDeviceLocalIndex; + mGuestDeviceOnlyMemoryTypeIndex = 0; + mGuestColorBufferMemoryTypeIndex = hostColorBufferMemoryTypeIndex + 1; + } + } + // Limit max safe memory heap size if the VulkanMaxSafeHeapSize feature is set to a non-zero // value. const uint64_t maxSafeHeapSizeLimit = features.VulkanMaxSafeHeapSize.getValue().value_or(0); @@ -179,6 +215,15 @@ EmulatedPhysicalDeviceMemoryProperties::getHostMemoryInfoFromGuestMemoryTypeInde return std::nullopt; } + // The device only type is the host type with its host visibility withheld, so that an + // allocation from it is not given host visible emulation. + if (mGuestDeviceOnlyMemoryTypeIndex == guestMemoryTypeIndex) { + return HostMemoryInfo{ + .index = hostMemoryTypeIndex, + .memoryType = mGuestMemoryProperties.memoryTypes[guestMemoryTypeIndex], + }; + } + return getHostMemoryInfoFromHostMemoryTypeIndex(hostMemoryTypeIndex); } @@ -201,6 +246,14 @@ void EmulatedPhysicalDeviceMemoryProperties::transformToGuestMemoryRequirements( guestMemoryTypeBits |= (1u << guestMemoryTypeIndex); } + if (mGuestDeviceOnlyMemoryTypeIndex) { + const uint32_t hostMemoryTypeIndex = + mGuestToHostMemoryTypeIndexMap[*mGuestDeviceOnlyMemoryTypeIndex]; + if (hostMemoryTypeBits & (1u << hostMemoryTypeIndex)) { + guestMemoryTypeBits |= (1u << *mGuestDeviceOnlyMemoryTypeIndex); + } + } + memoryRequirements->memoryTypeBits = guestMemoryTypeBits; } diff --git a/host/vulkan/vk_emulated_physical_device_memory.h b/host/vulkan/vk_emulated_physical_device_memory.h index 9d344dc5f..87d85b411 100644 --- a/host/vulkan/vk_emulated_physical_device_memory.h +++ b/host/vulkan/vk_emulated_physical_device_memory.h @@ -68,6 +68,11 @@ class EmulatedPhysicalDeviceMemoryProperties { // try to import host ColorBuffer allocations // (e.g. vkGetAndroidHardwareBufferPropertiesANDROID()). uint32_t mGuestColorBufferMemoryTypeIndex; + + // A guest only memory type that allocates from a host visible host type without host + // visible emulation, so that images have somewhere to go on a device whose every memory + // type is host visible. + std::optional mGuestDeviceOnlyMemoryTypeIndex; }; } // namespace vk diff --git a/host/vulkan/vk_emulated_physical_device_memory_tests.cpp b/host/vulkan/vk_emulated_physical_device_memory_tests.cpp index cb8167f5b..b3d881b56 100644 --- a/host/vulkan/vk_emulated_physical_device_memory_tests.cpp +++ b/host/vulkan/vk_emulated_physical_device_memory_tests.cpp @@ -116,6 +116,67 @@ TEST(VkGuestMemoryUtilsTest, Passthrough) { EqsVkPhysicalDeviceMemoryProperties(hostMemoryProperties)); } +TEST(VkGuestMemoryUtilsTest, SystemBlobDeviceOnlyTypeWhenEverythingIsHostVisible) { + // A unified memory device, as Metal reports it. + const VkPhysicalDeviceMemoryProperties hostMemoryProperties = { + .memoryTypeCount = 1, + .memoryTypes = + { + { + .propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | + VK_MEMORY_PROPERTY_HOST_CACHED_BIT, + .heapIndex = 0, + }, + }, + .memoryHeapCount = 1, + .memoryHeaps = + { + { + .size = 0x1000000, + .flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT, + }, + }, + }; + + gfxstream::host::FeatureSet features; + features.SystemBlob.setEnabled(true); + features.GlDirectMem.setEnabled(true); + + EmulatedPhysicalDeviceMemoryProperties helper(hostMemoryProperties, 0, features); + + // The guest sees a device local only type first, then the host type. + VkPhysicalDeviceMemoryProperties expectedGuestMemoryProperties = hostMemoryProperties; + expectedGuestMemoryProperties.memoryTypeCount = 2; + expectedGuestMemoryProperties.memoryTypes[0] = { + .propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .heapIndex = 0, + }; + expectedGuestMemoryProperties.memoryTypes[1] = hostMemoryProperties.memoryTypes[0]; + EXPECT_THAT(helper.getGuestMemoryProperties(), + EqsVkPhysicalDeviceMemoryProperties(expectedGuestMemoryProperties)); + + // Both allocate from the one host type; the device only one is not host visible. + EXPECT_THAT(helper.getHostMemoryInfoFromGuestMemoryTypeIndex(0), + Optional(EqsHostMemoryInfo(EmulatedPhysicalDeviceMemoryProperties::HostMemoryInfo{ + .index = 0, + .memoryType = expectedGuestMemoryProperties.memoryTypes[0], + }))); + EXPECT_THAT(helper.getHostMemoryInfoFromGuestMemoryTypeIndex(1), + Optional(EqsHostMemoryInfo(EmulatedPhysicalDeviceMemoryProperties::HostMemoryInfo{ + .index = 0, + .memoryType = hostMemoryProperties.memoryTypes[0], + }))); + + // Anything the host type can hold, either guest type can. + VkMemoryRequirements requirements = {.memoryTypeBits = 0b1}; + helper.transformToGuestMemoryRequirements(&requirements); + EXPECT_EQ(requirements.memoryTypeBits, 0b11u); + + EXPECT_EQ(helper.getGuestColorBufferMemoryTypeIndex(), 1u); +} + TEST(VkGuestMemoryUtilsTest, ReserveAHardwareBuffer) { const VkPhysicalDeviceMemoryProperties hostMemoryProperties = { .memoryTypeCount = 2,