Repository navigation
arch(vulkan): record image transfers in caller command buffers - #125
Merged
chuzcjoe merged 1 commit intoOct 23, 2026
Merged
Conversation
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
VulkanImage::TransitionImageLayout,VulkanBuffer::CopyToImageandVulkanImage::GenerateMipmapseach create, submit and synchronously complete their own command buffer. The existing DrawTexture, CubeMap and Mipmap loaders therefore require three submission/completion lifecycles for one logical upload. DrawTexture and Mipmap already identify this limitation in TODOs. Callers cannot compose these operations with each other or choose the completion boundary.This PR addresses that concrete Vulkan execution boundary. It changes no shared dependency or independent graphics/compute backend and does not overlap open #124 (push constants).
Solution
VkCommandBufferoverloads for color layout transitions, buffer-to-image copies and mipmap generation. They only record commands; the existing synchronous signatures delegate to the same implementation and keep their blocking behavior.The resource/submission counts follow directly from the call paths, rather than timing measurements. This is an architecture change; no latency, bandwidth, frame-rate or measured RSS improvement is claimed.
For a single-layer mipmapped texture, the existing Mipmap loader now uses:
For a non-mipmapped texture, record the existing transfer-destination to shader-read transition instead of
GenerateMipmaps; CubeMap supplies six layers to the transition and copy overloads. Callers can also record multiple uploads and submit with their own fence, as the integration test does.A dedicated uploader would combine staging ownership and submission policy, increasing the lifetime/API surface for these existing callers. Native command-buffer composition matches the existing swapchain recording API and keeps one implementation for synchronous and caller-controlled paths. Pitched/partial image uploads were also evaluated but need a concrete streaming caller plus a format/block/pitch contract. Gaussian-blur optimization has a real 3000x4000 compute caller, but representative nonconstant workloads and repeated GPU baselines are still needed before choosing tiled or separable algorithms. Neither alternative was replaced with a guard-only PR.
Validation
Both complete builds and default test runs used the final source tree committed as
c2572ca.scripts/run.shrequests Debug; top-level CMake actually selects Release with-O3 -DNDEBUG; the core warning flags include-Werror. All pinned submodules were initialized in this new Orca worktree. The existing ignored PNG fixture was copied read-only from the main checkout with matching SHA-256. No build ran in the user's checkout../scripts/run.sh -t macos -r tests./scripts/run.sh -t arm64-v8a -r testsBoth commands exited 0. Checked each expected executable's actual invocation, GoogleTest start/completion/pass counts, and every RUN/OK pair. The Android runner uses
set -e; reaching the next executable and final pull with exit 0 confirms successfuladb shellprocess exit propagation, not just compilation.macOS environment: Apple M6, MoltenVK 1.4.1, Apple Clang 21.0.0, macOS SDK 27.0, CMake 4.4.4, Vulkan SDK 1.4.350.0. Android environment: sole authorized USB physical
2210132C/nuwa, serial1dda2807, Android 13 / API 33, Qualcomm hardware, emptyro.kernel.qemu;ANDROID_SERIALfixed throughout. Existing NDK 28.2.13676358 / r28c, Android build API 34, JDK 17.0.20.1 and ADB 37.0.1. Preflight ranadb devices -lbefore device validation. Shell-local SDK setup explicitly supplied CMake, adb, glslc, slangc and xxd. No installation, license acceptance, shell-profile edits, APK installation or device-setting changes.Three new GPU integration tests executed on both platforms:
The tests check completion fence results and require a queue family supporting graphics for the inherited fragment-stage barriers. Native Vulkan validation was requested; macOS lists the Khronos layer, and no validation errors were observed. Android layer availability was not independently established. The three graphical examples compiled in the default macOS build and are macOS-only CMake targets; windowed rendering was not visually exercised. Their upload operations were exercised headlessly by the integration tests on both platforms. No benchmark was required or run for this architecture acceptance.
An initial macOS run failed because this fresh worktree lacked
tests/data/core.png; after copying and hashing the existing fixture, the entire macOS command was rerun successfully. The failed log is retained and excluded from passing counts. No source changes followed the two successful final runs. Separate complete logs, exit statuses, preserved macOS tmp outputs, device properties, verified test summaries and tested tree ID are retained underbuild/daily-image-recording-20261023/in this worktree.git diff --checkpassed and the complete diff was reviewed.Changes
examples/vulkan/CubeMapDemo/RenderCubeMap.cppexamples/vulkan/DrawTextureDemo/RenderTexture.cppexamples/vulkan/MipmapDemo/RenderModel.cpptests/vulkan/VulkanImageRecordingTest.cppvulkan/include/VulkanBuffer.hvulkan/include/VulkanImage.hvulkan/src/VulkanBuffer.cppvulkan/src/VulkanImage.cppImplementation/API/examples: +67 / -35. Tests: +213 / -0. Benchmark/build/docs: 0. Total: 8 files, +280 / -35 = 315 changed lines, relative to freshly fetched
origin/masterf7070c0d9648b2dc7e583fe07e4868b80a57f609, below the 1000-line limit. No project Markdown or README edits, new dependency, generated binary or image asset is included.Risk
Existing calls and blocking semantics remain available; class layouts and virtual interfaces are unchanged. Adding overloads means an untyped address-of-member expression must now specify its intended signature. Code choosing the new overloads must use an actively recording command buffer from the same device and an appropriate queue family, outside rendering, and keep the context, images, staging memory and command pool alive until GPU completion. Queue/command access requires external synchronization. Layout transitions are recorded intent, not a host-side state change; abandoning recording does not transition an image.
The inherited upload contract still requires correct usage flags, image sizes/formats, packed source/layer spacing and valid old layouts; this PR does not add general format/range validation. Color transitions retain their existing fragment-shader destination scope, so dedicated transfer/compute-only queues are not newly supported. Mipmap generation retains its existing single-layer, linear-blittable color-image contract and is not a cubemap mip generator. Cross-queue ownership transfer and asynchronous resource ownership are outside this change. The migrated callers explicitly wait before local staging destruction.