diff --git a/.github/workflows/presubmit.yaml b/.github/workflows/presubmit.yaml index 94d990daf..60eb19870 100644 --- a/.github/workflows/presubmit.yaml +++ b/.github/workflows/presubmit.yaml @@ -86,10 +86,13 @@ jobs: //host:gfxstream_backend_static //host:gfxstream_backend_shared //host:gfxstream_backend - # Nothing is run yet: this job exists to keep the macOS host build - # compiling. Tests need a graphics driver environment that macOS does not - # have here yet. - run-tests: false + # Only tests that need no graphics driver run: macOS has no driver + # environment here yet. The WorkerThread case is not run anywhere yet + # and fails on every platform, so it stays out until it is fixed. + test-targets: >- + common/base:gfxstream_common_base_tests + additional-bazel-args: >- + --test_arg=--gtest_filter=-WorkerThread.TheReturnedFutureFromEnqueueShouldBeReadyBeforeTheWorkerStarts run-gfxstream-cmake-build: runs-on: ubuntu-22.04 diff --git a/.github/workflows/presubmit_bazel.yml b/.github/workflows/presubmit_bazel.yml index 004befec5..caae4a0e0 100644 --- a/.github/workflows/presubmit_bazel.yml +++ b/.github/workflows/presubmit_bazel.yml @@ -25,6 +25,18 @@ on: type: boolean default: true + # Which tests to run. Defaults to the full suite; platforms that can only + # build part of the target graph can override this with a curated list. + test-targets: + type: string + default: >- + host:gfxstream_framebuffer_tests + host/vulkan:gfxstream_compositorvk_tests + host/vulkan:gfxstream_emulatedphysicalmemory_tests + host/vulkan:vk_common_operations_tests + host/vulkan:vk_format_utils_tests + tests/end2end:gfxstream_end2end_tests + permissions: contents: read @@ -91,16 +103,11 @@ jobs: - name: Test if: ${{ inputs.run-tests }} env: + TEST_TARGETS: ${{ inputs.test-targets }} ADDITIONAL_BAZEL_ARGS: ${{ inputs.additional-bazel-args }} run: | - bazel test \ + bazel test $TEST_TARGETS \ --disk_cache=$HOME/bazel-disk-cache \ --test_output=streamed \ --verbose_failures \ - host:gfxstream_framebuffer_tests \ - host/vulkan:gfxstream_compositorvk_tests \ - host/vulkan:gfxstream_emulatedphysicalmemory_tests \ - host/vulkan:vk_common_operations_tests \ - host/vulkan:vk_format_utils_tests \ - tests/end2end:gfxstream_end2end_tests \ $ADDITIONAL_BAZEL_ARGS diff --git a/common/base/SharedMemory_posix.cpp b/common/base/SharedMemory_posix.cpp index efd971c4a..40cf525f3 100644 --- a/common/base/SharedMemory_posix.cpp +++ b/common/base/SharedMemory_posix.cpp @@ -115,7 +115,15 @@ int SharedMemory::openInternal(int oflag, int mode, bool doMapping) { struct stat sb; if (mShareType == ShareType::SHARED_MEMORY) { #if defined(__APPLE__) - mFd = ::shm_open(mName.c_str(), oflag, mode); + // An object left behind by a dead process cannot be resized, and one another process + // still uses must not be shared by accident: create fresh, replacing whatever has the + // name. Its owner, if any, keeps it through its descriptor. + const int createFlags = (oflag & O_CREAT) ? (oflag | O_EXCL) : oflag; + mFd = ::shm_open(mName.c_str(), createFlags, mode); + if (mFd == -1 && errno == EEXIST && (oflag & O_CREAT)) { + shm_unlink(mName.c_str()); + mFd = ::shm_open(mName.c_str(), createFlags, mode); + } #elif defined(HAVE_MEMFD_CREATE) mFd = memfd_create(mName.c_str(), MFD_CLOEXEC | MFD_ALLOW_SEALING); #else diff --git a/host/vulkan/vk_decoder.cpp b/host/vulkan/vk_decoder.cpp index 2de23adb7..c63e49d14 100644 --- a/host/vulkan/vk_decoder.cpp +++ b/host/vulkan/vk_decoder.cpp @@ -11747,14 +11747,12 @@ size_t VkDecoder::Impl::decode(void* buf, size_t len, IOStream* ioStream, VkDevice device; const VkDeviceBufferMemoryRequirements* pInfo; VkMemoryRequirements2* pMemoryRequirements; - // Begin non wrapped dispatchable handle unboxing for device; + // Begin global wrapped dispatchable handle unboxing for device; uint64_t cgen_var_0; memcpy((uint64_t*)&cgen_var_0, *readStreamPtrPtr, 1 * 8); *readStreamPtrPtr += 1 * 8; *(VkDevice*)&device = (VkDevice)(VkDevice)((VkDevice)(*&cgen_var_0)); - auto unboxed_device = unbox_VkDevice(device); auto vk = dispatch_VkDevice(device); - // End manual dispatchable handle unboxing for device; vkReadStream->alloc((void**)&pInfo, sizeof(const VkDeviceBufferMemoryRequirements)); reservedunmarshal_VkDeviceBufferMemoryRequirements( vkReadStream, VK_STRUCTURE_TYPE_MAX_ENUM, @@ -11780,8 +11778,8 @@ size_t VkDecoder::Impl::decode(void* buf, size_t len, IOStream* ioStream, (unsigned long long)pMemoryRequirements); } if (CC_LIKELY(vk)) { - vk->vkGetDeviceBufferMemoryRequirements(unboxed_device, pInfo, - pMemoryRequirements); + m_state->on_vkGetDeviceBufferMemoryRequirements( + &m_pool, snapshotApiCallHandle, device, pInfo, pMemoryRequirements); } vkStream->unsetHandleMapping(); if (pMemoryRequirements) { @@ -17634,14 +17632,12 @@ size_t VkDecoder::Impl::decode(void* buf, size_t len, IOStream* ioStream, VkDevice device; const VkDeviceBufferMemoryRequirements* pInfo; VkMemoryRequirements2* pMemoryRequirements; - // Begin non wrapped dispatchable handle unboxing for device; + // Begin global wrapped dispatchable handle unboxing for device; uint64_t cgen_var_0; memcpy((uint64_t*)&cgen_var_0, *readStreamPtrPtr, 1 * 8); *readStreamPtrPtr += 1 * 8; *(VkDevice*)&device = (VkDevice)(VkDevice)((VkDevice)(*&cgen_var_0)); - auto unboxed_device = unbox_VkDevice(device); auto vk = dispatch_VkDevice(device); - // End manual dispatchable handle unboxing for device; vkReadStream->alloc((void**)&pInfo, sizeof(const VkDeviceBufferMemoryRequirements)); reservedunmarshal_VkDeviceBufferMemoryRequirements( vkReadStream, VK_STRUCTURE_TYPE_MAX_ENUM, @@ -17668,8 +17664,8 @@ size_t VkDecoder::Impl::decode(void* buf, size_t len, IOStream* ioStream, (unsigned long long)pMemoryRequirements); } if (CC_LIKELY(vk)) { - vk->vkGetDeviceBufferMemoryRequirementsKHR(unboxed_device, pInfo, - pMemoryRequirements); + m_state->on_vkGetDeviceBufferMemoryRequirementsKHR( + &m_pool, snapshotApiCallHandle, device, pInfo, pMemoryRequirements); } vkStream->unsetHandleMapping(); if (pMemoryRequirements) { diff --git a/host/vulkan/vk_decoder_global_state.cpp b/host/vulkan/vk_decoder_global_state.cpp index fb37464df..ad7e98088 100644 --- a/host/vulkan/vk_decoder_global_state.cpp +++ b/host/vulkan/vk_decoder_global_state.cpp @@ -2768,13 +2768,6 @@ class VkDecoderGlobalState::Impl { GFXSTREAM_FATAL("%s: function implementation cannot be found!"); } - const VkFormat format = pInfo->pCreateInfo->format; - bool needDecompression = isEtc2(format) || isAstc(format); - if (!needDecompression) { - // No modifications needed - return; - } - std::lock_guard lock(mMutex); auto* deviceInfo = gfxstream::base::find(mDeviceInfo, device); @@ -2783,9 +2776,20 @@ class VkDecoderGlobalState::Impl { return; } - needDecompression = deviceInfo->needEmulatedDecompression(format); + auto* physicalDeviceInfo = gfxstream::base::find(mPhysdevInfo, deviceInfo->physicalDevice); + if (!physicalDeviceInfo) { + GFXSTREAM_ERROR("Failed to find physical device info for physical device:%p", + deviceInfo->physicalDevice); + return; + } + auto& physicalDeviceMemHelper = physicalDeviceInfo->memoryPropertiesHelper; + + const VkFormat format = pInfo->pCreateInfo->format; + const bool needDecompression = + (isEtc2(format) || isAstc(format)) && deviceInfo->needEmulatedDecompression(format); if (!needDecompression) { - // No modifications needed + physicalDeviceMemHelper->transformToGuestImageMemoryRequirements( + pInfo->pCreateInfo->tiling, &pMemoryRequirements->memoryRequirements); return; } @@ -2808,6 +2812,34 @@ class VkDecoderGlobalState::Impl { pMemoryRequirements->memoryRequirements = cmpInfo.getMemoryRequirements(); cmpInfo.destroy(vk); + physicalDeviceMemHelper->transformToGuestImageMemoryRequirements( + pInfo->pCreateInfo->tiling, &pMemoryRequirements->memoryRequirements); + } + + void on_vkGetDeviceBufferMemoryRequirements(gfxstream::base::BumpPool* pool, + VkSnapshotApiCallHandle apiCallHandle, + VkDevice boxed_device, + const VkDeviceBufferMemoryRequirements* pInfo, + VkMemoryRequirements2* pMemoryRequirements) { + auto device = unbox_VkDevice(boxed_device); + auto vk = dispatch_VkDevice(boxed_device); + + if (vk->vkGetDeviceBufferMemoryRequirements) { + vk->vkGetDeviceBufferMemoryRequirements(device, pInfo, pMemoryRequirements); + } else if (vk->vkGetDeviceBufferMemoryRequirementsKHR) { + vk->vkGetDeviceBufferMemoryRequirementsKHR(device, pInfo, pMemoryRequirements); + } else { + GFXSTREAM_FATAL("%s: function implementation cannot be found!", __func__); + } + + std::lock_guard lock(mMutex); + + auto* deviceInfo = gfxstream::base::find(mDeviceInfo, device); + if (!deviceInfo) { + GFXSTREAM_ERROR("%s: Failed to find device info for device: %p", __func__, device); + return; + } + auto* physicalDeviceInfo = gfxstream::base::find(mPhysdevInfo, deviceInfo->physicalDevice); if (!physicalDeviceInfo) { GFXSTREAM_ERROR("Failed to find physical device info for physical device:%p", @@ -2815,8 +2847,7 @@ class VkDecoderGlobalState::Impl { return; } - auto& physicalDeviceMemHelper = physicalDeviceInfo->memoryPropertiesHelper; - physicalDeviceMemHelper->transformToGuestMemoryRequirements( + physicalDeviceInfo->memoryPropertiesHelper->transformToGuestMemoryRequirements( &pMemoryRequirements->memoryRequirements); } @@ -5602,7 +5633,8 @@ class VkDecoderGlobalState::Impl { auto& physicalDeviceMemHelper = physicalDeviceInfo->memoryPropertiesHelper; updateImageMemoryRequirementsLocked(device, image, pMemoryRequirements); - physicalDeviceMemHelper->transformToGuestMemoryRequirements(pMemoryRequirements); + physicalDeviceMemHelper->transformToGuestImageMemoryRequirements( + imageTilingLocked(image), pMemoryRequirements); } // A driver that defers the layout also reports rowPitch=0; answer with the AHB's stride. @@ -5668,8 +5700,8 @@ class VkDecoderGlobalState::Impl { auto& physicalDeviceMemHelper = physicalDeviceInfo->memoryPropertiesHelper; updateImageMemoryRequirementsLocked(device, pInfo->image, &pMemoryRequirements->memoryRequirements); - physicalDeviceMemHelper->transformToGuestMemoryRequirements( - &pMemoryRequirements->memoryRequirements); + physicalDeviceMemHelper->transformToGuestImageMemoryRequirements( + imageTilingLocked(pInfo->image), &pMemoryRequirements->memoryRequirements); } void on_vkGetBufferMemoryRequirements(gfxstream::base::BumpPool* pool, VkSnapshotApiCallHandle, @@ -6855,15 +6887,25 @@ class VkDecoderGlobalState::Impl { #endif } else if (m_vkEmulation->getFeatures().SystemBlob.enabled() || m_vkEmulation->getFeatures().VulkanAllocateHostVisibleAsUdmabuf.enabled()) { + // A system blob is imported as a host pointer, and a driver can ask for a + // coarser alignment than a page: Apple silicon pages are 16KB. + uint64_t blobAlignment = kPageSizeforBlob; + if (m_vkEmulation->supportsExternalMemoryHostProperties()) { + blobAlignment = std::max(blobAlignment, + m_vkEmulation->externalMemoryHostProperties() + .minImportedHostPointerAlignment); + } + // Ensure size is page-aligned. - VkDeviceSize alignedSize = ALIGN(localAllocInfo.allocationSize, kPageSizeforBlob); + VkDeviceSize alignedSize = ALIGN(localAllocInfo.allocationSize, blobAlignment); if (alignedSize != localAllocInfo.allocationSize) { GFXSTREAM_ERROR("Warning: Aligning allocation size from %llu to %llu", static_cast(localAllocInfo.allocationSize), static_cast(alignedSize)); } localAllocInfo.allocationSize = alignedSize; - auto memory = SharedMemory("shared-memory-vk-" + std::to_string(sUniqueShmemId++), + auto memory = SharedMemory("shared-memory-vk-" + std::to_string(getpid()) + "-" + + std::to_string(sUniqueShmemId++), localAllocInfo.allocationSize); if (m_vkEmulation->getFeatures().VulkanAllocateHostVisibleAsUdmabuf.enabled()) { @@ -6908,8 +6950,7 @@ class VkDecoderGlobalState::Impl { return VK_ERROR_OUT_OF_HOST_MEMORY; } mappedPtr = memory.get(); - int mappedPtrAlignment = - reinterpret_cast(mappedPtr) % kPageSizeforBlob; + int mappedPtrAlignment = reinterpret_cast(mappedPtr) % blobAlignment; if (mappedPtrAlignment != 0) { GFXSTREAM_ERROR( "Warning: Mapped shared memory pointer is not aligned to page size, " @@ -10495,6 +10536,11 @@ class VkDecoderGlobalState::Impl { return false; } + VkImageTiling imageTilingLocked(VkImage image) REQUIRES(mMutex) { + auto* imageInfo = gfxstream::base::find(mImageInfo, image); + return imageInfo ? imageInfo->imageCreateInfoShallow.tiling : VK_IMAGE_TILING_OPTIMAL; + } + void updateImageMemoryRequirementsLocked(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements) REQUIRES(mMutex) { @@ -11767,6 +11813,20 @@ void VkDecoderGlobalState::on_vkGetDeviceImageMemoryRequirementsKHR( pMemoryRequirements); } +void VkDecoderGlobalState::on_vkGetDeviceBufferMemoryRequirements( + gfxstream::base::BumpPool* pool, VkSnapshotApiCallHandle apiCallHandle, VkDevice device, + const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + mImpl->on_vkGetDeviceBufferMemoryRequirements(pool, apiCallHandle, device, pInfo, + pMemoryRequirements); +} + +void VkDecoderGlobalState::on_vkGetDeviceBufferMemoryRequirementsKHR( + gfxstream::base::BumpPool* pool, VkSnapshotApiCallHandle apiCallHandle, VkDevice device, + const VkDeviceBufferMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + mImpl->on_vkGetDeviceBufferMemoryRequirements(pool, apiCallHandle, device, pInfo, + pMemoryRequirements); +} + void VkDecoderGlobalState::on_vkDestroyDevice(gfxstream::base::BumpPool* pool, VkSnapshotApiCallHandle apiCallHandle, VkDevice device, const VkAllocationCallbacks*) { diff --git a/host/vulkan/vk_decoder_global_state.h b/host/vulkan/vk_decoder_global_state.h index add118d3f..2a97a004e 100644 --- a/host/vulkan/vk_decoder_global_state.h +++ b/host/vulkan/vk_decoder_global_state.h @@ -245,6 +245,17 @@ class VkDecoderGlobalState { const VkDeviceImageMemoryRequirements* pInfo, VkMemoryRequirements2* pMemoryRequirements); + void on_vkGetDeviceBufferMemoryRequirements(gfxstream::base::BumpPool* pool, + VkSnapshotApiCallHandle apiCallHandle, + VkDevice device, + const VkDeviceBufferMemoryRequirements* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + void on_vkGetDeviceBufferMemoryRequirementsKHR(gfxstream::base::BumpPool* pool, + VkSnapshotApiCallHandle apiCallHandle, + VkDevice device, + const VkDeviceBufferMemoryRequirements* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + void on_vkDestroyDevice(gfxstream::base::BumpPool* pool, VkSnapshotApiCallHandle apiCallHandle, VkDevice device, const VkAllocationCallbacks* pAllocator); diff --git a/host/vulkan/vk_emulated_physical_device_memory.cpp b/host/vulkan/vk_emulated_physical_device_memory.cpp index 0006d0a7e..d50962cf2 100644 --- a/host/vulkan/vk_emulated_physical_device_memory.cpp +++ b/host/vulkan/vk_emulated_physical_device_memory.cpp @@ -24,7 +24,23 @@ namespace host { namespace vk { namespace { -static constexpr const uint32_t kInvalidMemoryTypeIndex = std::numeric_limits::max(); +#if defined(__APPLE__) +// The first device local type, if every type is host visible. +std::optional FindDeviceLocalMemoryTypeIfAllAreHostVisible( + const VkPhysicalDeviceMemoryProperties& memoryProperties) { + std::optional deviceLocalIndex; + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) { + const VkMemoryPropertyFlags flags = memoryProperties.memoryTypes[i].propertyFlags; + if (!(flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) { + return std::nullopt; + } + if (!deviceLocalIndex && (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { + deviceLocalIndex = i; + } + } + return deviceLocalIndex; +} +#endif } // namespace @@ -34,12 +50,6 @@ EmulatedPhysicalDeviceMemoryProperties::EmulatedPhysicalDeviceMemoryProperties( // Start with the original host memory properties: mHostMemoryProperties = hostMemoryProperties; mGuestMemoryProperties = hostMemoryProperties; - std::fill_n(mGuestToHostMemoryTypeIndexMap, VK_MAX_MEMORY_TYPES, kInvalidMemoryTypeIndex); - std::fill_n(mHostToGuestMemoryTypeIndexMap, VK_MAX_MEMORY_TYPES, kInvalidMemoryTypeIndex); - for (uint32_t i = 0; i < mHostMemoryProperties.memoryTypeCount; i++) { - mGuestToHostMemoryTypeIndexMap[i] = i; - mHostToGuestMemoryTypeIndexMap[i] = i; - } mGuestColorBufferMemoryTypeIndex = hostColorBufferMemoryTypeIndex; // Limit max safe memory heap size if the VulkanMaxSafeHeapSize feature is set to a non-zero @@ -129,29 +139,83 @@ EmulatedPhysicalDeviceMemoryProperties::EmulatedPhysicalDeviceMemoryProperties( } } + for (uint32_t i = 0; i < mGuestMemoryProperties.memoryTypeCount; i++) { + mGuestMemoryTypes.push_back(EmulatedGuestMemoryType{ + .hostMemoryTypeIndex = i, + .memoryType = mGuestMemoryProperties.memoryTypes[i], + }); + } + +#if defined(__APPLE__) + const std::optional hostDeviceLocalIndex = + features.SystemBlob.enabled() + ? FindDeviceLocalMemoryTypeIfAllAreHostVisible(hostMemoryProperties) + : std::nullopt; + if (hostDeviceLocalIndex && mGuestMemoryTypes.size() < VK_MAX_MEMORY_TYPES) { + const VkMemoryType memoryType = { + .propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .heapIndex = hostMemoryProperties.memoryTypes[*hostDeviceLocalIndex].heapIndex, + }; + const uint32_t index = findIndexForNewMemoryType(memoryType.propertyFlags); + mGuestMemoryTypes.insert(mGuestMemoryTypes.begin() + index, + EmulatedGuestMemoryType{ + .hostMemoryTypeIndex = *hostDeviceLocalIndex, + .memoryType = memoryType, + }); + mGuestAppleSystemBlobMemoryTypeIndex = index; + if (index <= mGuestColorBufferMemoryTypeIndex) { + mGuestColorBufferMemoryTypeIndex++; + } + } +#endif + // If enabled, reserve an additional memory type for AHB backed buffers and images // so that the host can control its memory properties. This ensures that the guest // only sees `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT` and will not try to map the // memory. if (features.VulkanUseDedicatedAhbMemoryType.enabled()) { - if (mGuestMemoryProperties.memoryTypeCount == VK_MAX_MEMORY_TYPES) { + if (mGuestMemoryTypes.size() == VK_MAX_MEMORY_TYPES) { GFXSTREAM_FATAL( "Unable to create emulated AHB memory type because VK_MAX_MEMORY_TYPES " "already in use."); } - uint32_t ahbMemoryTypeIndex = mGuestMemoryProperties.memoryTypeCount; - ++mGuestMemoryProperties.memoryTypeCount; - - VkMemoryType& ahbMemoryType = mGuestMemoryProperties.memoryTypes[ahbMemoryTypeIndex]; - ahbMemoryType.propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - ahbMemoryType.heapIndex = - mHostMemoryProperties.memoryTypes[hostColorBufferMemoryTypeIndex].heapIndex; + const VkMemoryType memoryType = { + .propertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + .heapIndex = + mHostMemoryProperties.memoryTypes[hostColorBufferMemoryTypeIndex].heapIndex, + }; + const uint32_t index = findIndexForNewMemoryType(memoryType.propertyFlags); + mGuestMemoryTypes.insert(mGuestMemoryTypes.begin() + index, + EmulatedGuestMemoryType{ + .hostMemoryTypeIndex = hostColorBufferMemoryTypeIndex, + .isReservedForAhbAllocations = true, + .memoryType = memoryType, + }); + mGuestColorBufferMemoryTypeIndex = index; + if (mGuestAppleSystemBlobMemoryTypeIndex && + index <= *mGuestAppleSystemBlobMemoryTypeIndex) { + ++*mGuestAppleSystemBlobMemoryTypeIndex; + } + } - mGuestToHostMemoryTypeIndexMap[ahbMemoryTypeIndex] = hostColorBufferMemoryTypeIndex; + mGuestMemoryProperties.memoryTypeCount = static_cast(mGuestMemoryTypes.size()); + for (uint32_t i = 0; i < mGuestMemoryProperties.memoryTypeCount; i++) { + mGuestMemoryProperties.memoryTypes[i] = mGuestMemoryTypes[i].memoryType; + } +} - mGuestColorBufferMemoryTypeIndex = ahbMemoryTypeIndex; +uint32_t EmulatedPhysicalDeviceMemoryProperties::findIndexForNewMemoryType( + VkMemoryPropertyFlags propertyFlags) const { + // A memory type whose flags are a strict subset of another's must come before it, see + // https://docs.vulkan.org/refpages/latest/refpages/source/VkPhysicalDeviceMemoryProperties.html + for (uint32_t i = 0; i < mGuestMemoryTypes.size(); i++) { + const VkMemoryPropertyFlags existingFlags = mGuestMemoryTypes[i].memoryType.propertyFlags; + if (existingFlags != propertyFlags && (existingFlags & propertyFlags) == propertyFlags) { + return i; + } } + return static_cast(mGuestMemoryTypes.size()); } std::optional @@ -174,9 +238,14 @@ EmulatedPhysicalDeviceMemoryProperties::getHostMemoryInfoFromGuestMemoryTypeInde return std::nullopt; } - uint32_t hostMemoryTypeIndex = mGuestToHostMemoryTypeIndexMap[guestMemoryTypeIndex]; - if (hostMemoryTypeIndex == kInvalidMemoryTypeIndex) { - return std::nullopt; + uint32_t hostMemoryTypeIndex = mGuestMemoryTypes[guestMemoryTypeIndex].hostMemoryTypeIndex; + + // The host type with its host visibility withheld, so no host visible emulation. + if (guestMemoryTypeIndex == mGuestAppleSystemBlobMemoryTypeIndex) { + return HostMemoryInfo{ + .index = hostMemoryTypeIndex, + .memoryType = mGuestMemoryProperties.memoryTypes[guestMemoryTypeIndex], + }; } return getHostMemoryInfoFromHostMemoryTypeIndex(hostMemoryTypeIndex); @@ -187,14 +256,14 @@ void EmulatedPhysicalDeviceMemoryProperties::transformToGuestMemoryRequirements( uint32_t guestMemoryTypeBits = 0; const uint32_t hostMemoryTypeBits = memoryRequirements->memoryTypeBits; - for (uint32_t hostMemoryTypeIndex = 0; - hostMemoryTypeIndex < mHostMemoryProperties.memoryTypeCount; hostMemoryTypeIndex++) { + for (uint32_t guestMemoryTypeIndex = 0; guestMemoryTypeIndex < mGuestMemoryTypes.size(); + guestMemoryTypeIndex++) { + uint32_t hostMemoryTypeIndex = mGuestMemoryTypes[guestMemoryTypeIndex].hostMemoryTypeIndex; if (!(hostMemoryTypeBits & (1u << hostMemoryTypeIndex))) { continue; } - uint32_t guestMemoryTypeIndex = mHostToGuestMemoryTypeIndexMap[hostMemoryTypeIndex]; - if (guestMemoryTypeIndex == kInvalidMemoryTypeIndex) { + if (mGuestMemoryTypes[guestMemoryTypeIndex].isReservedForAhbAllocations) { continue; } @@ -204,6 +273,21 @@ void EmulatedPhysicalDeviceMemoryProperties::transformToGuestMemoryRequirements( memoryRequirements->memoryTypeBits = guestMemoryTypeBits; } +void EmulatedPhysicalDeviceMemoryProperties::transformToGuestImageMemoryRequirements( + VkImageTiling tiling, VkMemoryRequirements* memoryRequirements) const { + transformToGuestMemoryRequirements(memoryRequirements); + if (tiling == VK_IMAGE_TILING_LINEAR || !mGuestAppleSystemBlobMemoryTypeIndex) { + return; + } + + // The type is set only when the host allows the host type behind it, so a tiled image + // is left that alone; without it, what the host allows is better than nothing. + const uint32_t tiledMemoryTypeBit = 1u << *mGuestAppleSystemBlobMemoryTypeIndex; + if (memoryRequirements->memoryTypeBits & tiledMemoryTypeBit) { + memoryRequirements->memoryTypeBits = tiledMemoryTypeBit; + } +} + void EmulatedPhysicalDeviceMemoryProperties::clampMemoryBudgetToGuestHeapSizes( VkPhysicalDeviceMemoryBudgetPropertiesEXT* budgetProps) const { if (budgetProps == nullptr) { diff --git a/host/vulkan/vk_emulated_physical_device_memory.h b/host/vulkan/vk_emulated_physical_device_memory.h index 9d344dc5f..344669bd8 100644 --- a/host/vulkan/vk_emulated_physical_device_memory.h +++ b/host/vulkan/vk_emulated_physical_device_memory.h @@ -17,6 +17,7 @@ #include #include +#include #include "gfxstream/host/features.h" @@ -54,20 +55,37 @@ class EmulatedPhysicalDeviceMemoryProperties { void transformToGuestMemoryRequirements(VkMemoryRequirements* hostMemoryRequirements) const; + // As above, but a tiled image is left only the types reserved for it. + void transformToGuestImageMemoryRequirements( + VkImageTiling tiling, VkMemoryRequirements* hostMemoryRequirements) const; + // Clamp heapBudget/heapUsage to the guest-visible heap sizes. No-op if budgetProps is null. void clampMemoryBudgetToGuestHeapSizes( VkPhysicalDeviceMemoryBudgetPropertiesEXT* budgetProps) const; private: + uint32_t findIndexForNewMemoryType(VkMemoryPropertyFlags propertyFlags) const; + VkPhysicalDeviceMemoryProperties mGuestMemoryProperties; VkPhysicalDeviceMemoryProperties mHostMemoryProperties; - uint32_t mGuestToHostMemoryTypeIndexMap[VK_MAX_MEMORY_TYPES]; - uint32_t mHostToGuestMemoryTypeIndexMap[VK_MAX_MEMORY_TYPES]; + + struct EmulatedGuestMemoryType { + uint32_t hostMemoryTypeIndex; + // Reserved for AHBs with the VulkanUseDedicatedAhbMemoryType feature. + bool isReservedForAhbAllocations = false; + // The memory properties reported to the guest. + VkMemoryType memoryType; + }; + std::vector mGuestMemoryTypes; // The memory type index reported to the guest for VkDeviceMemory requirements which would // try to import host ColorBuffer allocations // (e.g. vkGetAndroidHardwareBufferPropertiesANDROID()). uint32_t mGuestColorBufferMemoryTypeIndex; + + // The memory type index reported to the guest for tiled images on Apple with system + // blobs: device local only, so that the host never imports shared memory for them. + std::optional mGuestAppleSystemBlobMemoryTypeIndex; }; } // 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..0b3744b18 100644 --- a/host/vulkan/vk_emulated_physical_device_memory_tests.cpp +++ b/host/vulkan/vk_emulated_physical_device_memory_tests.cpp @@ -116,6 +116,54 @@ TEST(VkGuestMemoryUtilsTest, Passthrough) { EqsVkPhysicalDeviceMemoryProperties(hostMemoryProperties)); } +#if defined(__APPLE__) +TEST(VkGuestMemoryUtilsTest, SystemBlobDeviceLocalTypeWhenEverythingIsHostVisible) { + const VkPhysicalDeviceMemoryProperties hostMemoryProperties = { + .memoryTypeCount = 2, + .memoryTypes = + { + {.propertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT}, + {.propertyFlags = + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT}, + }, + .memoryHeapCount = 1, + .memoryHeaps = {{.size = 0x1000000, .flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT}}, + }; + + gfxstream::host::FeatureSet features; + features.SystemBlob.setEnabled(true); + EmulatedPhysicalDeviceMemoryProperties helper(hostMemoryProperties, 1, features); + + // Device local alone is a strict subset of the second host type, so it goes before it. + VkPhysicalDeviceMemoryProperties expectedGuestMemoryProperties = hostMemoryProperties; + expectedGuestMemoryProperties.memoryTypeCount = 3; + expectedGuestMemoryProperties.memoryTypes[1] = {.propertyFlags = + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT}; + expectedGuestMemoryProperties.memoryTypes[2] = hostMemoryProperties.memoryTypes[1]; + EXPECT_THAT(helper.getGuestMemoryProperties(), + EqsVkPhysicalDeviceMemoryProperties(expectedGuestMemoryProperties)); + EXPECT_THAT(helper.getHostMemoryInfoFromGuestMemoryTypeIndex(1), + Optional(EqsHostMemoryInfo(EmulatedPhysicalDeviceMemoryProperties::HostMemoryInfo{ + .index = 1, + .memoryType = expectedGuestMemoryProperties.memoryTypes[1], + }))); + EXPECT_EQ(helper.getGuestColorBufferMemoryTypeIndex(), 2u); + + VkMemoryRequirements requirements = {.memoryTypeBits = 0b11}; + helper.transformToGuestImageMemoryRequirements(VK_IMAGE_TILING_LINEAR, &requirements); + EXPECT_EQ(requirements.memoryTypeBits, 0b111u); + + requirements = {.memoryTypeBits = 0b11}; + helper.transformToGuestImageMemoryRequirements(VK_IMAGE_TILING_OPTIMAL, &requirements); + EXPECT_EQ(requirements.memoryTypeBits, 0b010u); + + // Nothing reserved is allowed, so what the host allows stays. + requirements = {.memoryTypeBits = 0b01}; + helper.transformToGuestImageMemoryRequirements(VK_IMAGE_TILING_OPTIMAL, &requirements); + EXPECT_EQ(requirements.memoryTypeBits, 0b001u); +} +#endif + TEST(VkGuestMemoryUtilsTest, ReserveAHardwareBuffer) { const VkPhysicalDeviceMemoryProperties hostMemoryProperties = { .memoryTypeCount = 2,