Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Problem
Tile-based GPUs keep transient attachments (MSAA color and depth that are cleared at the start of a pass and discarded at the end) in on-chip tile memory. Vulkan exposes this through
VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BITplus aVK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BITmemory type. MoltenVK on Apple GPUs maps that type to Metal memoryless storage, and Adreno exposes it as well. Both validation devices report it (DEVICE_LOCAL | LAZILY_ALLOCATED, flags0x11).On master,
VulkanImagealways picks the first type matching the requested properties, which is plainDEVICE_LOCAL(0x1). As a result:MultiSamplingDemoalready setsTRANSIENT_ATTACHMENTusage, but still gets fully backed memory.VulkanSwapChain::CreateDepthResources, used by DepthTesting/LoadModel/DrawTexture) and the depth attachments created by MultiSampling/Physics/FPSShooter/CarControl are fully backed too. All of them useloadOp = CLEARandstoreOp = DONT_CAREand are never sampled or copied.Measured on master: 4 sets of 4x MSAA RGBA8 color plus 4x D32 depth at 1080p cost 388.5 MiB of Metal device memory on macOS and 258 MiB of per-process GPU memory on Android. That memory holds data that is never read back.
Solution
VulkanImage: whenusagecontainsVK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, choose the first memory type in the image'smemoryTypeBitsthat has the requested properties plusLAZILY_ALLOCATED. If there is none, fall back to the previousFindMemoryType(properties)choice, so devices without lazy memory behave exactly as before. The chosen type's property flags are stored, exposed asVulkanImage::memory_properties(), and carried over by move assignment. Images withoutTRANSIENTusage are unaffected (the spec excludes lazily allocated types from theirmemoryTypeBits).VulkanSwapChain::CreateDepthResourcesand the depth images in MultiSampling/Physics/FPSShooter/CarControl now addTRANSIENT_ATTACHMENTusage. The MultiSampling MSAA color image benefits without any change. Every affected attachment uses CLEAR load and DONT_CARE store (VulkanRenderPassdepth, andVulkanDynamicRenderingdepth plus resolved MSAA color), which is what memoryless storage requires.TRANSIENT_ATTACHMENTusage.VulkanImagepicks lazily allocated memory automatically, andmemory_properties()reports what was selected.Alternatives considered:
LAZILY_ALLOCATEDexplicitly. Rejected: it makes callers handle missing support, and the transient usage bit already states the intent.VulkanContext::FindMemoryType. Rejected because buffers and other callers use it too.Validation
GPT-6.1-sol (high) review gate: 3 rounds, all findings fixed, final
GATE: PASS. The findings covered the compute-only queue family check, separating unavailable telemetry from 0, removing comments, and checking D32 4x format support in the benchmark.macOS (Apple M6, macOS 27.0.1, Vulkan SDK 1.4.350.0 / MoltenVK, validation layer enabled in the new tests):
./scripts/run.sh -t macos -r tests→ exit 0. 6 test executables, 63 passed / 0 failed / 0 skipped (vulkan_tests 43, including the 3 new tests).vk_multi_sampling_demo,vk_physics_demo,vk_fps_demo,vk_car_control_demo,vk_depth_demo,vk_model_demo,vk_texture_demoall ran until stopped, with 0 validation messages.Android (only authorized physical device
2210132C(nuwa, Qualcomm Adreno 740), Android 13 / API 33, NDK 28.2.13676358, arm64-v8a, android-34):./scripts/run.sh -t arm64-v8a -r tests→ exit 0. 7 test executables ran on the device, 65 passed / 0 failed / 0 skipped (vulkan_tests 43, including the 3 new tests).New tests (
VulkanTransientAttachmentTest):DEVICE_LOCAL | LAZILY_ALLOCATEDexactly when the image'smemoryTypeBitsoffer such a type (both devices:0x11).memory_properties()survives move assignment.0x1.Resource benchmark
vulkan_transient_attachment_benchmark. Setup: 4 sets of 4x RGBA8 color plus 4x D32 depth, each cleared in one pass, same process. 1 warm-up per mode, then 7 rounds alternating mode order, reporting median/min/max. 3 independent invocations each of a baseline binary built from master'sVulkanImage.cppand the final binary. Metric: device-local heap usage fromVK_EXT_memory_budgeton macOS; per-process GPU memory fromdumpsys gpu --gpumemon Android, where Adreno does not expose the memory budget extension. Every sample had min == max, and all 3 invocations matched.Persistent images (no TRANSIENT usage) are identical in both binaries. Limitations:
vkGetDeviceMemoryCommitmentreports the full allocation size, so heap usage is used as the metric instead.Changes
12 files, +687 / −12 (699 lines):
vulkan/include/VulkanImage.h+3,vulkan/src/VulkanImage.cpp+21/−2,vulkan/src/VulkanSwapChain.cpp+2/−2, plus demosMultiSamplingDemo/main.cpp+4/−2,PhysicsDemo/main.cpp,FPSShooterDemo/main.cpp,CarControlDemo/main.cpp+3/−2 each. Subtotal +39/−12.tests/vulkan/VulkanTransientAttachmentTest.cpp+385,TransientAttachment.vert+11,TransientAttachment.frag+10,benchmarks/TransientAttachmentBenchmark.cpp+239,tests/vulkan/CMakeLists.txt+3. Subtotal +648/−0.Risk
TRANSIENTusage take exactly the old path.TRANSIENT_ATTACHMENTon an image they later load, store, sample, or copy were already violating the usage contract. With lazily allocated memory, such misuse now shows up as lost contents instead of working by accident. All in-tree users were audited.main.cppfiles; the re-created images should also getTRANSIENTusage). feature(vulkan): persist validated pipeline caches across pipelines #137 touches other lines of CarControl/FPS/Physicsmain.cpp.🤖 Generated with Claude Code