Skip to content

arch(vulkan): scope model staging to initialization - #133

Draft
chuzcjoe wants to merge 1 commit into
masterfrom
daily/arch-vulkan-scope-upload-staging-20261102
Draft

chuzcjoe wants to merge 1 commit into
masterfrom
daily/arch-vulkan-scope-upload-staging-20261102

Conversation

@chuzcjoe

@chuzcjoe chuzcjoe commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Problem

LoadModelDemo, MipmapDemo, and MultiSamplingDemo retain host-visible vertex/index staging buffers as renderer members until model destruction. Their draw paths use only device-local geometry after the synchronous uploads complete. Initialization also performs two separate geometry submissions and fence waits.

This architecture change gives those existing upload resources a completion-bounded lifetime. Candidate comparison: descriptor rebinding could enable compute-pipeline reuse but needs a wider update contract and concrete caller migration; partial/pitched texture uploads still need an incremental-update workload and format/pitch requirements. Scoped mesh staging has three actual OBJ-model consumers, clear ownership boundaries, and directly verifiable resource benefits. Open #132 concerns compute reductions and does not overlap; recent timestamp, image-recording, and specialization work is already on master. Scope is Vulkan only, with no shared dependency or other backend implementation changes.

Solution

Remove each renderer's two staging members and their allocations from CreateBuffers(). Allocate/map both staging buffers locally in Init(), sized from the existing device-local destinations, then upload both through the existing VulkanBuffer::CopyBuffers API. Its checked fence wait completes before local staging destruction; its memory barriers make transfer writes available to subsequent geometry reads.

Each demo automatically uses this through its unchanged Init(...), UpdateUniformBuffer(...), and Render(...) calls. Device-local geometry, uniform/texture ownership, and CPU mesh vectors retain their existing lifetimes. Compared with explicitly resetting members after two separate copies, local ownership directly expresses the lifetime and the batch supplies one completion boundary. A new uploader abstraction or asynchronous lifetime manager is unnecessary for these synchronous callers. Resource release follows the Vulkan memory completion requirement.

Validation

The final source tree was unchanged between these full runs and the commit. All default enabled targets/examples built. Actual core flags include -O3 -DNDEBUG and warnings as errors, despite the runner requesting Debug.

Platform Actual command Executed tests Result
macOS native ./scripts/run.sh -t macos -r tests mat 3, threadpool 4, stb 4, opencl 8, vulkan 41, metal 1 61 passed, 0 failed, 0 skipped; 6 executables/24 GoogleTest suites; exit 0
Android physical arm64-v8a ./scripts/run.sh -t arm64-v8a -r tests mat 3, threadpool 4, stb 4, opencl 8, vulkan 41, gles 2, ahardwarebuffer 1 63 passed, 0 failed, 0 skipped; 7 device executables/26 suites; exit 0

Verified exact local/adb shell invocations, matching individual RUN/OK pairs, start/completion/pass summaries, and successful set -e runner execution. Platform logs and macOS temporary outputs were preserved separately. Initial full runs also passed; both platforms were repeated after the final test-format/include adjustment.

The new default Vulkan test links the actual MipmapDemo renderer and shaders. It generates deterministic OBJ/PPM files, calls the unchanged Init, deletes the files, then renders three independently created/destroyed model instances for two offscreen frames each. Command/fence reuse, depth testing, indexed geometry fetch, and image readback are exercised after staging leaves scope. Independent CPU edge equations check 3,724 RGBA pixels/frame, including interior and cleared exterior; pixels close to edges are excluded to avoid rasterization-edge convention dependence. No external asset download or window is required.

macOS: Apple M6/MoltenVK 1.4.1, macOS 27.0.1, installed Xcode SDK 27.2 selected with shell-local SDKROOT, Apple Clang 21.0.0 (clang-2100.4.1.1), CMake 4.4.4, Vulkan SDK 1.4.350.0, glslc 2026.2, Slang 2026.8. Khronos validation available/requested; no observed validation errors or warnings.

Android: adb devices -l before each run confirmed the sole authorized USB physical target, 2210132C/nuwa, serial 1dda2807, Android 13/API 33, qcom/Adreno 740, empty emulator property. Fixed ANDROID_SERIAL=1dda2807. Reused compatible installed NDK 28.2.13676358/r28c, Android Clang 19.0.1, target API 34, ADB 37.0.1 and JDK 17. Explicit SDK activation/PATH supplied CMake, adb, glslc, slangc, xxd. Android validation-layer availability was not independently established. No dependency installation, new license acceptance, sudo, profile change, APK installation, or device-setting change.

Resource and lifetime evidence

Same macOS device/toolchain/core configuration, original-master versus final production code. API-dump traces track every allocation/free handle and submit between initialization markers; all traced allocations are eventually freed. The synthetic fixture ran three lifecycles per version. Separate original/final probes also initialized existing examples/data/viking_room.{obj,png}: 3,566 unique vertices, 11,484 indices.

Initialization measurement Original Final
Geometry submissions / fence waits 2 / 2 1 / 1
Total submissions including MipmapDemo texture 3 2
Retained geometry staging allocations after Init 2 0
Total retained model allocations after Init 6 4
Viking retained allocation bytes including texture/uniform 5,956,896 5,796,848

Viking releases 160,048 bytes of Vulkan allocations before rendering: 114,112 vertex bytes + 45,936 index bytes. The synthetic case releases 120 bytes across each lifecycle. These are observed Vulkan allocation sizes/call counts on MoltenVK, not RSS or physical-heap measurements. Peak staging demand and CPU mesh retention are unchanged. The same source-level ownership change applies to the other two migrated callers.

The synthetic trace is reproducible with the SDK's API-dump layer:

VK_INSTANCE_LAYERS=VK_LAYER_LUNARG_api_dump ./build/macos/tests/vulkan/vulkan_tests '--gtest_filter=VulkanModelUploadTest.*'

Raw baseline/final traces, original/final probe sources/commands, fixture hashes, build/test logs, and verification summaries are retained in this worktree's ignored build/daily-mesh-upload-20261102/. The Viking probe initializes resources without rendering that asset. Android allocation counters were not instrumented. No latency, FPS, throughput, or cross-vendor performance claim is made.

Changes

File Added Deleted Category
examples/vulkan/LoadModelDemo/RenderModel.cpp 21 12 implementation/caller
examples/vulkan/LoadModelDemo/RenderModel.h 0 2 ownership
examples/vulkan/MipmapDemo/RenderModel.cpp 21 12 implementation/caller
examples/vulkan/MipmapDemo/RenderModel.h 0 2 ownership
examples/vulkan/MultiSamplingDemo/RenderModel.cpp 21 12 implementation/caller
examples/vulkan/MultiSamplingDemo/RenderModel.h 0 2 ownership
tests/vulkan/CMakeLists.txt 11 0 test integration
tests/vulkan/VulkanModelUploadTest.cpp 207 0 integration test

Implementation/ownership +63/-42; tests/build +218/-0; benchmarks 0; project documentation +0/-0. Total 8 files, +281/-42 = 323 changed lines, relative to freshly fetched origin/master at 0dc12da8ee3f6b69ed18a0e6c817e9e436e85906.

Risk

Initialization/render signatures and synchronous behavior are preserved. Example class layouts shrink, so consumers compiling these example headers must rebuild. Callers still synchronize queue access and wait before destroying renderers referenced by pending draws. Reinitializing an already initialized renderer remains outside the existing supported lifecycle.

The MipmapDemo path is rendered/read back on both platforms. The other two windowed model renderers were built on macOS; their draw paths were not exercised. Allocation/device-loss injection, windowed visual behavior, and additional GPU vendors were not tested. Texture uploads, parsing, shaders, uniform updates, multisampling configuration, and library APIs are unchanged. Full default tests, including other backends, ran on both platforms.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant