Repository navigation
Conversation
On NVIDIA on Windows, a guest allocation that asks only for HOST_VISIBLE memory is made from the host's HOST_VISIBLE | HOST_COHERENT type, which is not HOST_CACHED (write-combined on this driver, as far as we can tell). The guest writes its uploads straight into that memory through its mapping, and under WHPX those writes are slow: in one Unity game the upload copy loop took about a third of the render thread, and libc memcpy 45% of it with ANGLE (google#198). Add the VulkanAllocateUncachedHostVisibleAsCached feature, off by default. With it, a guest allocation from a host visible memory type that is neither host cached nor device local is made from a HOST_VISIBLE | HOST_COHERENT | HOST_CACHED type on the same heap instead, if the host has one. Imported ColorBuffer and Buffer memory and blobs keep their memory type, and a dedicated allocation gets the replacement only if its image or buffer allows it. This assumes that resources that allow the original memory type also allow the replacement, which the Vulkan spec does not guarantee. It only applies when gfxstream allocates host visible memory itself and maps it with vkMapMemory(). With ExternalBlob, SystemBlob, VulkanAllocateHostVisibleAsUdmabuf or VulkanAllocateHostMemory, host visible memory keeps its memory type and a warning is logged once. The memory types reported to the guest do not change. Test: VkGuestMemoryUtilsTest.* (12/12), built from this tree with clang-cl and the Android Emulator's flags and linked against that build's libraries; the new test fails on the AMD device uncached row and the four external memory rows when those checks are removed. Test: Android Emulator (Windows 11 + WHPX, RTX 4070 SUPER, x86_64 android-34) with this change and the feature turned on: Honkai: Star Rail, Vulkan renderer, same spot, render thread 88-92% -> 44-46% (63-72% in earlier runs of previous revisions), 46-50 -> 62 fps (at the 60 fps cap), the upload copy loop 35% -> 2.7% of the render thread's samples. Assisted-by: Claude Code:Claude-Opus-5.5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #198.
On NVIDIA on Windows (RTX 4070 SUPER), a guest allocation that asks only for
HOST_VISIBLEmemory is made from the host'sHOST_VISIBLE | HOST_COHERENTtype. That type is notHOST_CACHED(write-combined on this driver as far as we can tell), and under WHPX the guest's writes into it through its mapping are slow: with Honkai: Star Rail's Vulkan renderer, the upload copy loop takes about a third of the render thread.Change
A new feature,
VulkanAllocateUncachedHostVisibleAsCached, off by default: a guest allocation from a host visible type that is neither host cached nor device local is made from aHOST_VISIBLE | HOST_COHERENT | HOST_CACHEDtype on the same heap, if there is one.vkMapMemory(); withExternalBlob,SystemBlob,VulkanAllocateHostVisibleAsUdmabuforVulkanAllocateHostMemoryit does nothing and warns once.memoryInfo.cachingfollows the type that was actually allocated.It could instead use the
EmulatedGuestMemoryTypemapping from 8136433, so thattransformToGuestMemoryRequirements()hides the type from resources that do not allow it. Happy to switch if you prefer that.To enable it:
VulkanAllocateUncachedHostVisibleAsCached:enabledinSTREAM_RENDERER_PARAM_RENDERER_FEATURES. The Android Emulator also needs afeaturecontrolentry and akAemuToGfxstreamFeatureMapline inopengles.cpp; we used a local environment variable there.Existing features
VulkanEnsureCachedCoherentMemoryAvailable(Add VulkanEnsureCachedCoherentMemoryAvailable feature #64) only changes reported flags when there is no cached coherent type, so it does nothing on NVIDIA.VulkanDisableCoherentMemoryAndEmulate: the game still lands on the uncached type, and the flushes made it slower, 52.0 → 45.1 fps (Guest uploads into host-visible memory are slow when it is backed by NVIDIA's write-combined type (Windows/WHPX); a cached coherent type fixes it #198).VulkanAllocateHostMemorybacks host visible allocations with process-private memory imported throughVK_EXT_external_memory_host, so it may avoid the write-combined type as well; the Android Emulator already turns it on for NVIDIA with AEHD (b/382621412), but not with WHPX. We have no numbers for it: with-feature VulkanAllocateHostMemory, the game did not get past start-up on our build (the same crash in its render thread in both attempts), and we have not looked into whether that is related to gfxstream. If you would rather use that path here, I can look into it.Why off by default
Write-combined memory is usually right for memory the CPU only writes; Dawn, for example, prefers a non-cached coherent type for write-only mapped buffers with
BufferMapExtendedUsages(https://dawn.googlesource.com/dawn/+/9729a6380ccabcd595d415afbe83427072091dd7). We do not know why guest writes into it are this slow under WHPX. Risks:VUID-vkBindBufferMemory-memory-01035,VUID-vkBindImageMemory-memory-01047), and the same set of messages with and without the change.VulkanAllocateDeviceMemoryOnlyby default, so non device local types are reported to the guest without any property flags, and Linux hosts with Intel or AMD GPUs turn onVulkanAllocateHostMemory, where this does nothing.Results
Windows 11 + WHPX, NVIDIA RTX 4070 SUPER (driver 610.88; #198 was measured on 591.86), x86_64 android-34 guest, Star Rail's Vulkan renderer, cold boot and login with no further input (same spot and camera), 10 s samples:
(all samples in the loop's code range)
The render thread share with the change varied between runs: 44–46% here, and 63–72% in earlier runs of previous revisions, which are the same on this path. Every run with the change stayed well below every run without it (88–92%), and the frame rate and the copy loop share (2–3%) were consistent across them. We could not hold every host condition constant between runs (other processes, clocks, the game's own state), so read that row as a range.
Our emulator build carries other local patches, the same in both columns, including a guest libvulkan_enc fix that Star Rail's Vulkan renderer needs on this image. A local version of this change has been on by default in our builds since 2026-10-01, and we have not traced any problem to it.
Testing
VkGuestMemoryUtilsTest.VulkanAllocateUncachedHostVisibleAsCached(Bazelgfxstream_emulatedphysicalmemory_tests, CMakeVulkan_unittests), built on Windows with clang-cl and the Android Emulator's flags:VkGuestMemoryUtilsTest.*12/12 pass, and the new test fails when the AMD or external memory checks are removed.vk_decoder_global_state.cppcompiles with the same flags; I have not run a full CMake or Bazel build ofmain.HOST_VISIBLEallocations on the cached type and no gfxstream errors.This change was developed with AI assistance (Claude Code; see the
Assisted-bytrailer). The tests above were run on my machine, and I am responsible for the change.