Graphics debugging architecture and implementation plan¶
Status: implementation contract, 2026-10-20. Native local-editor P0/P1 recording and local P2 pass-duration counters are implemented: CPU/GPU/Both controls, the Record Profiling shortcut (Ctrl+Alt+F9), frame timing and shared pass scopes. Hosted transport, resource naming, P3 and native Metal/RenderDoc debugger capture remain planned. Implementation availability is distinct from platform/hardware qualification.
Shader and renderer developers should be able to run the selected project in Ludus, capture the GPU work responsible for its image, and inspect that capture in an external graphics debugger. Ludus supplies meaningful names, correct capture boundaries, process selection and reproducible artifacts. External tools own resource inspection, pixel history, shader stepping and hardware analysis. The editor also starts and stops CPU, GPU or combined Perfetto recordings so developers can investigate frame costs without launching a vendor capture tool.
Existing owners and baseline¶
The RHI contract owns device, command, resource and completion lifetime; the public rendering guide records implemented rendering profiles. Native backends are Vulkan on Linux and Metal on macOS and Direct3D 12 on Windows. Browsers use WebGPU or WebGL 2. Windows supports external PIX GPU capture with optional Ludus command-list events. Mobile capture remains a separate platform qualification.
The reconciled profiling architecture owns CPU traces, counters and the GPU timing data contract. This design owns external graphics-debugger integration and the editor recording workflow. GPU annotation must work without CPU trace recording or timestamp queries; a CPU scope around a draw does not measure GPU execution time. Existing reference-image capture is a rendering product, not an external debugger capture.
The developer tools design owns external process launching and local tool preferences. The editor GUI systems contract owns hosted producer/consumer images and viewport publication. Capture adapters must preserve those owners rather than introduce another resource registry, process supervisor or viewport publication transaction.
At this baseline, graph pass descriptions already carry authored diagnostic names. Broad native object naming, nested GPU annotations, a graphics-capture request service and editor capture controls remain work in this plan. Finding an installed tool or a single existing Metal label does not establish support. The RHI already exposes an optional single-sample, nonblocking frame-duration probe, and viewport Stats already consumes it. Those durations are device-local; they are exported as Perfetto GPU samples by the native local-editor recording session. Recording and Stats share the existing sampler owner.
Decisions and initial scope¶
- Implement backend-neutral GPU object names and command scopes first. Carry authored pass identity through graph compilation and pass merging.
- Deliver Metal capture on macOS and RenderDoc capture on Linux/Vulkan first. They share request/status semantics but retain distinct native adapters.
- Separate capture enablement at process startup from a later capture request. Missing injection or capture configuration can require restarting the target.
- Keep frame capture, timing traces and GPU crash dumps separate capabilities. No universal capture-file format or universal begin/end API is assumed.
- Deliver basic Perfetto GPU duration counters and CPU/GPU/Both editor recording independently of vendor-tool installation. Add timed pass scopes and calibrated GPU execution lanes incrementally; coarse timing must remain honestly labeled.
- Keep tool installation optional. Opening projects, running doctor and normal builds must not install debuggers, elevate privileges or launch viewers.
- Preserve rendering when tooling is unavailable or capture fails. Capture does not change the selected backend or enable a different renderer silently.
The first external-debugger release covers a single submitted raster frame, including its uploads and compute work where supported by the selected backend. Shader source display is a qualification target; original Slang source stepping, ray-tracing debugging, multi-frame traces and hosted shared-image replay are separately qualified.
Responsibility boundaries¶
| Owner | Responsibility |
|---|---|
| GraphicsRhi | Resource names; command/queue scopes; native frame boundaries; private capture hooks tied to the live device session. |
| GraphicsRenderer | Meaningful pass, view, material and shader identities; graph-to-executed-work correspondence. |
| Graphics capture service | Bounded requests, session identity, state transitions, adapter selection and artifact metadata; no draw/resource lifetime ownership. |
| Tooling and editor launch services | Tool discovery, startup environment/injection, target process selection, local preferences and opening a completed artifact. |
| Existing editor/game transport | Versioned capture requests and replies to the rendering process; no native GPU handles crossing IPC. |
| FoundationProfiling | CPU trace recording, transport-neutral measurement records, correlation metadata and Perfetto export; RHI retains native GPU query ownership. |
| Editor profiling controller | Recording mode/target, start/stop coordination, bounded finalization, summaries and Perfetto export; reuses existing tracer and GPU sampler owners. |
Implement the capture service initially as a narrow responsibility beside the RHI session, with private platform translation units. Do not create a new module or general diagnostics framework just for dispatch. Expose only the minimal Ludus-owned request/capability/status vocabulary needed by SDK consumers. Native handles, RenderDoc/PIX headers, Objective-C objects and loader details remain private. Confirm dependency direction before implementation; FoundationBase and FoundationProfiling must not acquire a graphics or tool-installation dependency.
All engine operations use explicit status results and noexcept where
applicable. Public declarations require meaningful Doxygen contracts. Use
existing bounded text/views, containers, checked numeric conversions and process
facilities; no STL container, formatting or exception-based convenience layer.
Shared GPU names and scopes¶
Naming and identity¶
Name devices/queues, command buffers, buffers, images/views, shaders and
pipelines where the backend supports it. Names identify an engine role and a
stable diagnostic ID; native addresses are not persistent identity. Examples
include World/MainView/Opaque, Material/<identity>/BaseColor and
Frame/<sequence>/Upload. Avoid constructing per-draw strings by default.
Reuse existing graph pass-name ownership and bounds. Introduce a single owner for any additional bounded diagnostic text type, with documented byte encoding, termination and overflow behavior. Copy borrowed names before their storage can expire. Reject invalid text at the public boundary; when native display limits require shortening, retain the distinguishing ID and report shortening outside the recording hot path. Renaming must not alter resource identity or lifetime.
Renderer identity generation happens at resource creation, graph preparation or an explicit rename. Recording reuses prepared names, performs no engine heap allocation and does no file I/O. Measure enabled/disabled overhead on the same workload; native tool overhead is additional and must not be described as free.
Scope placement and pairing¶
Record frame, graph, physical pass, logical pass, upload and dispatch scopes at the command-recording sites that execute the work. Include queue and frame identity for correlation. Keep physical pass groups and authored logical pass names distinguishable after merging; culled passes belong in graph diagnostics, not as fictitious GPU execution. Backend encoder constraints determine legal nesting and may require several native regions for one logical operation.
Pair begin/end on the same recording context and owner thread. Do not span coroutine suspension, command-buffer reset, submission or device shutdown with an RAII scope. Early failures must unwind the recorded scope stack. Bound nesting and diagnose mismatches without corrupting commands or changing rendering.
| Backend | Native mapping |
|---|---|
| Vulkan | Optional VK_EXT_debug_utils; object names, command labels and queue labels through loaded extension entry points. Missing extension support disables annotations only. |
| Metal | Object label properties and legal command-buffer/encoder debug groups or signposts. Labels do not extend borrowed resource lifetime. |
| WebGPU | Object labels and encoder/pass debug groups where available through the pinned port. |
| WebGL 2 | Capability-checked annotation facilities or tool-side metadata. Never require a desktop GL extension. |
| D3D12 | Device, direct-queue and frame command-list names. The optional WinPixEventRuntime adds a frame region and the existing named physical pass regions to the frame command list. |
Name/scope capability is independent of debugger presence, capture ability, timestamps and validation. Annotation enablement must not require recording a CPU trace. Validation layers and shader validation have separate controls and their overhead must be recorded in capture metadata.
Capture request and lifetime contract¶
Capabilities are queried for a particular process, device session, backend and tool activity. Distinguish installed tooling, runtime attachment/enablement, frame capture, explicit-region capture, timing, offscreen workloads and viewer availability. An installed viewer does not imply a capturable live session.
Each request has a monotonically changing session generation and request ID, an explicit rendering target, capture kind, bounded destination and frame selection. The initial policy admits one active capture per process and rejects competing tools/requests with Busy; do not compose capture layers without a separately tested combination. Metadata records the native capture scope when a device-wide capture includes more work than the selected view.
| State | Meaning and permitted next transition |
|---|---|
| Idle | No request owns the capture adapter; admit or reject a request. |
| Armed | Validated and waiting for an eligible frame; move to Capturing, Cancelled or Failed. |
| Capturing | Native capture has started; complete recording, or perform adapter cleanup after cancellation/failure. |
| Finalizing | Native stop was issued; verify completion and collect artifacts before releasing request ownership. |
| Complete | Capture artifact and its manifest are usable; opening the viewer is a separate operation. |
| Failed or Cancelled | Terminal outcome with a reason and any explicitly identified partial artifact; never report a previous capture as this request's success. |
Execute native transitions on the thread owning the RHI session. Marshal UI/IPC requests to that owner rather than calling vendor APIs from arbitrary threads. Invalidate stale replies/callbacks using session and request identity. Shutdown must stop or abandon a capture using the adapter's documented cleanup before releasing the native objects it borrows. The adapter must not stop a capture owned by an external user/tool session.
An eligible frame has an acquired drawable/offscreen target and reaches the normal recording/submission path. Hidden, zero-size, skipped or unready frames do not consume an armed request. Apply a bounded deadline so a permanently hidden target cannot remain armed indefinitely. Report which frame was actually captured, separately from the frame displayed when the request was made.
Start before creating/recording the commands to include; stop after their submission at the adapter's required boundary. Metal requires command buffers to be created and committed inside the capture interval. Capture-specific draining, if required by a tool, is explicit and recorded; do not add a normal per-frame GPU wait or synchronous timestamp readback.
Errors distinguish UnsupportedBackend, ToolUnavailable, RestartRequired, Busy, InvalidTarget, InvalidDestination, DeadlineExceeded, DeviceLost and NativeCaptureFailed, with native details in bounded diagnostics. Names are design vocabulary, not declarations of an existing SDK API. Failed/cancelled captures preserve the last drawable viewport and do not publish a pending or failed replacement view.
Write into a fresh request-owned output directory under ignored out/ by
default. Never overwrite captures or unrelated files. Publish a manifest only
after the adapter's completion condition and artifact verification; a directory
entry alone is insufficient. If the vendor format has no semantic completion
query, document the native stop/file checks used and retain viewer-open acceptance
as a separate qualification gate. Preserve the capture if manifest export fails,
and report that outcome explicitly.
The versioned manifest records engine revision/build profile, tool/runtime identity, OS/GPU/driver, process role, backend, device session, requested/actual frame IDs, world/view identity, renderer profile, shader artifact hashes, validation/debug/optimization settings and relative artifact locations. Copy only the relevant launch settings; do not serialize credentials or the process environment wholesale. Packaging and deletion are explicit user actions.
Initial native adapters¶
Metal on macOS¶
Use MTLCaptureManager and MTLCaptureDescriptor to capture the selected device
or queue to MTLCaptureDestinationGPUTraceDocument, after checking destination
support. Save a .gputrace and open it in Xcode only on request. Use the
Objective-C status/NSError path; do not translate Apple's Swift exception
examples into C++ exceptions.
Enable programmatic capture through the development app's MetalCaptureEnabled
property, or MTL_CAPTURE_ENABLED=1 on macOS 14 and later. Supply startup
configuration before Metal initialization. Keep final distribution enablement
an explicit build choice. A process already being captured is Busy; do not stop
another capture. Check the complete create/commit interval for uploads, render
and compute command buffers, including work without a presentation call.
File capture does not require an open Xcode GUI. Xcode provides the supported interactive inspection workflow; feature availability depends on the installed Xcode/macOS/GPU combination. New Apple command-line capture/debugging tools may be exposed later after availability checks, without replacing this adapter.
RenderDoc on Linux and Vulkan¶
Launch the intended rendering executable under the selected RenderDoc capture
environment before Vulkan initialization. Detect its already loaded runtime and
negotiate RENDERDOC_GetAPI once, early and on the owning thread. Pin the
reviewed application header/API minimum in the integration; record the actual
runtime version. Do not assume loading a library into an already initialized
Vulkan process retroactively intercepts its device.
Use StartFrameCapture/EndFrameCapture for explicit RHI frame intervals,
including offscreen frames. Supply the documented Vulkan device-pointer form
and relevant window, rather than assuming every native handle can be cast to
the application's generic device pointer. Use the API's capture result/path
queries to identify the new .rdc; never scan for the newest unrelated file.
The editor/CLI launcher owns injection and viewer launch. The runtime adapter owns only capture control and borrowed API pointers. Missing runtime attachment returns RestartRequired with the external-launch remedy. Negotiate only calls available in the selected API version; capture/replay of extensions, ray queries and shared external memory requires explicit feature qualification.
PIX and DRED on Windows¶
D5b uses the external PIX launcher and viewer. For a capture, use a windowed
D3D12 executable and select Launch For GPU Capture in PIX before the
executable creates its D3D12 device. Select the process that records the GPU
work. Capture a submitted frame with the GPU Capture button and save it to a
fresh local path under out/. To qualify the artifact, open it in PIX and
check the rendered output, the queue, the recorded operations and the
Ludus/Frame region with its authored pass names. External capture of
standalone presented frames is the D5b workflow. The in-application request
service and qualification of offscreen and hosted replay are later work.
PIX capture instructions
To see the regions, place a trusted, architecture-matched
WinPixEventRuntime.dll beside the executable before launch. The Windows
adapter uses the documented command-list ABI and requires both the begin and
end exports. The marker runtime alone cannot inject the GPU capturer. The
application runs and collects GPU durations when PIX, the marker DLL, or the
Graphics Tools debug layer is absent. With the marker DLL absent, an external
capture can still show D3D12 API calls. Normal builds and SDK installations
have no PIX dependency.
WinPixEventRuntime installation and ABI
The existing EnableGpuTiming request enables optional frame and pass probes.
Each measures bottom-of-pipe intervals on the direct queue. The frame sample
covers the acquired frame's clear, transitions and commands through the final
target transition. Named physical pass samples bracket raster pass encoding or
one compute dispatch and carry the logical pass count. The backend resolves
64-bit ticks into private readback storage and polls the submission fence
before mapping. Skipped frames keep both probes armed. A probe cannot be
rearmed while pending, and a full pass batch drops only the excess samples.
A failed frequency query or readback yields Failed. A changed frequency or
invalid interval yields Disjoint. The reported tick size comes from the queue
frequency. Samples have no CPU-clock alignment and exclude presentation.
A failed shutdown drain retains unfinished query storage. A new session can
render and retry the affected probe after completion or device removal.
Debug and Development force DRED auto-breadcrumbs and page-fault tracking before device creation when the runtime exposes the settings interface. Profile and Release use the system's DRED settings. On detected device removal, Ludus logs the native HRESULT and collects the available DRED reports before device teardown. Logs include a bounded prefix of command-list progress, the first uncompleted operation, the fault address and matching live and recently freed allocation records. An unavailable report includes its HRESULT. Enable an ordinary console, debugger or file log sink to retain the output. Preserve that log alongside the matching executable and shader artifacts, then recreate the device to resume rendering. Microsoft's DRED guide
For a controlled removal, run the existing Windows RHI test executable with the
[d5b] filter. The fixture calls the native removal API after submitting a
frame and verifies that the pending measurement is invalidated and a new
device can render. Windows CI retains its console report in the CTest log.
A forced removal can have an empty page-fault report. Record physical adapter,
driver, OS, build configuration and PIX/runtime versions alongside any
hardware capture and removal artifacts.
Editor, hosted game and SDK workflow¶
Offer explicit target roles: editor scene viewport, hosted game producer and editor UI/compositor. Show backend, selected tool and capability before arming. Route a hosted-game request over the existing versioned transport to the game; the editor's capture of a sampled shared texture cannot reveal the producer's draws. Include producer frame sequence and surface generation in correlation metadata. Do not imply one process capture includes the other process's work.
Qualify hosted external-memory capture and replay independently on each native backend. A tool limitation must have a visible reason and a standalone launch of the same project/world/renderer as the fallback. Retain shared-image producer/consumer fencing and leases while capturing. Resize, project switch, process exit and device loss must cancel or finish requests without releasing a surface still used by the other process.
The editor exposes launch with capture support, capture next frame, cancel, capture status, reveal artifacts and open in viewer. Persist tool paths and machine choices only in local ignored settings. Reuse existing process argv, environment and working-directory handling; never assemble a shell command from project data. Explicit invalid preferences fail instead of selecting another tool. SDK applications and future CLI controls use the same request contract; this proposal does not claim new commands already exist.
Shader artifacts and performance interpretation¶
Keep generated MSL/SPIR-V/WGSL, entry points, source/include identity, compiler versions/options, reflection and hashes with the existing shader artifact owner. Capture manifests reference that identity instead of creating a competing shader cache. Preserve required source/debug sidecars for the lifetime of a saved capture, with explicit packaging and pruning behavior.
Shader-debug mode preserves source/debug information and uses the appropriate optimization policy; performance mode retains representative optimization and useful names/symbols. Record the mode and any unavailable source mapping. Generated MSL display does not prove original Slang stepping. Verify vertex, fragment and compute source visibility, then qualify stepping separately.
Frame debuggers can perturb scheduling and replay workloads. Reproduce an image defect there; validate performance conclusions on representative optimized runs with appropriate timing/profiling tools. Shared names connect those workflows without conflating their data or completion conditions.
Perfetto recording in the editor¶
Provide a small Profiling tool window and a Record/Stop action available from the Tools menu and shared command catalogue. This is capture control plus a summary; detailed timelines remain in Perfetto. The profiling architecture owns sample semantics, clocks, buffering and export. It is independent of the vendor capture state machine above and works without RenderDoc or Xcode running.
| Control | Contract |
|---|---|
| Mode | CPU, GPU or Both; show effective build/device capability and reasons for unavailable choices. Freeze selection during recording/finalization. |
| Target | Editor process, editor scene viewport or hosted game producer, with explicit process/device scope. Start with the local editor and its scene viewport. |
| Record and Stop | One shared action whose label, enabled state and checked state agree in menu, toolbar, tool window and shortcut. Stop ends admission and starts bounded finalization. |
| Automatic stop | Optional duration or submitted-frame limit, with a finite configured memory budget. Skipped viewport frames do not count as submitted GPU frames. |
| Status | Idle, Arming, Recording, Finalizing, Complete, Cancelled or Failed; elapsed recording time, requested/effective sources, sample coverage and export errors. |
| Summary | Available GPU sample count and clearly scoped frame-duration statistics; CPU trace health. Missing/pending timings are unavailable, never zero. |
| Result | Save/reveal the trace, open in Perfetto explicitly, and retain the previous successful result if another recording fails. |
Ctrl+Alt+F9 is the shared Record/Stop action in the native editor command catalogue, in every keymap profile (F9 itself toggles a breakpoint when a code editor has focus; the resolution was decided 2026-11-03, see the code-editor decision log). Its native key label appears in the existing Keyboard Shortcuts UI. Future shortcut overrides follow the editor's shortcut policy. The shortcut uses the last configured mode and target and never starts a second recording while Arming or Finalizing. Opening the tool window exposes configuration before the first recording. Menu access remains available on Macs whose function keys require Fn, and typing/IME/viewport input behavior must pass the existing interaction acceptance.
CPU means instrumented thread scopes in the target process, not statistical CPU sampling or a viewport-only filter. GPU means the selected device/queue workload, not all graphics work on the machine. Both combines those sources in one trace with frame IDs and the declared clock relationship. Do not label the editor compositor or a command-buffer total as time for one scene pass.
CPU-only recording does not request new GPU timing samples. GPU-only recording does not enable ordinary CPU scope collection; it retains only GPU data and the minimal frame/session metadata needed to interpret it. Existing viewport Stats can independently request timing, but one sampler fans completed results to its consumers. Hiding Stats must not stop an active recording, and opening Stats must not steal its pending probe. Repeated polls of a terminal result emit it once.
Arming checks every requested source and reserves buffers before collection. Both fails explicitly if either requested source is unavailable, with an action to choose a supported mode; it never silently produces a CPU-only trace. A disabled profiling build reports the build remedy without rebuilding/restarting automatically. Existing active application/CLI recording returns Busy rather than being stopped or having its unexported data overwritten.
Stopping closes the recorded interval and disarms new probes. Resolve already
submitted samples without waiting on the GPU in the UI thread, under a bounded
finalization deadline; record any unresolved samples as incomplete. CPU
EndCapture joins event writers, so schedule lifecycle/export through one
serialized coordinator without promising an instant UI-thread call. Preserve
the frozen CPU/GPU export snapshot until the write succeeds or the user discards
it; a new recording must not erase it after an export failure. Heavy serialization
and file I/O run outside UI painting and frame recording. Save into ignored local
output and make file/viewer errors recoverable without losing World edits.
Hosted recording uses the producer's profiler and versioned transport with session/request IDs. Start/stop acknowledgements report effective frame/time boundaries; an editor recording cannot stand in for game CPU/GPU work. Initially export a separate trace per process with shared session/frame identity. Combine process traces on one time axis only after explicit clock-offset/error validation and unique process/track identities; identical synthetic process IDs or nearby timestamps are not evidence of alignment. Report disconnect and partial results.
Vendor capture and Perfetto recording initially exclude one another per target with a clear Busy reason. Simultaneous operation is a later qualified option whose additional instrumentation is recorded in the manifest. The ordinary Perfetto workflow does not depend on the vendor capture adapter milestones.
The delivered local-editor workflow and current limits are in Recording CPU and GPU profiling. The first delivery uses fixed storage/time limits and a new destination per recording. Local physical-pass timing reuses those controls, and Metal and Vulkan devices with a validated clock calibration add calibrated execution lanes. Source/process expansion and configurable limits remain separate work.
Mac developer setup¶
Install a compatible full Xcode distribution for interactive Metal inspection. Open it once and complete its requested setup and components. The standalone Command Line Tools package does not include the Xcode Metal debugger UI. CMake/Ninja and Ludus's pinned compiler policy remain the build workflow; using Xcode to inspect captures does not require migrating the engine to an Xcode project or replacing the reference LLVM toolchain.
Inspect the currently selected tools with xcode-select -p. To check a specific
Xcode without changing the machine-wide selection, use a per-command developer
directory, adjusting the path to the installed app:
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -version
Tool discovery should report missing Xcode/components and a remedy, without
automatically downloading them or changing xcode-select. Once the Metal phase
is delivered, capture from Ludus and explicitly open the generated .gputrace
in Xcode. Before then, use Xcode's external executable capture workflow where
applicable; installing Xcode alone does not implement Ludus's capture button.
RenderDoc is the initial Linux/Vulkan workflow, not the debugger for Ludus's
native Metal backend. PIX GPU capture requires Windows/D3D12.
Phased implementation and acceptance¶
P0/P1 and local P2 physical-pass counters are implemented for the native editor.
Shared pass scopes supply the annotation part of G0; resource naming, nested
scopes, hosted recording transport and capture adapters remain pending. Implement changes in isolated reviewable branches targeting
develop, following CI qualification.
Acceptance into develop is distinct from full batch/platform qualification.
The Perfetto track below can ship alongside GPU annotations and native capture adapters. It does not wait for G2/G3 or for a full pass-timing implementation.
| Phase | Deliverable and dependency | Exit gate |
|---|---|---|
| P0 Editor CPU recording | Shared profiling controller, tool window, CPU mode, Record/Stop action and its Ctrl+Alt+F9 shortcut; reuse the current CPU tracer/exporter. | Menu/toolbar/shortcut share availability; bounded capture exports and opens in Perfetto; Busy, disabled build and export-retry behavior preserve data and editor responsiveness. |
| P1 Coarse GPU Perfetto recording | GPU and Both modes; shared viewport probe ownership, typed frame-duration samples, coverage and single-trace export. Reuse existing RHI measurement support; no vendor debugger prerequisite. | Metal and Vulkan recordings show validated duration counters and CPU scope filtering; delayed/skipped/disjoint samples, Stats on/off, cancellation and finalization produce honest coverage without GPU waits. |
| P2 Pass durations and hosted recording | Local Metal/Vulkan bounded native queries and shared physical-pass scopes implemented; producer-side hosted transport remains pending. | Named pass counters survive pass merging, query pressure and restart; unsupported precision remains explicit; hosted traces identify their producer and preserve image leases. |
| P3 Correlated GPU execution lanes | Backend CPU/GPU clock mapping with uncertainty, queue tracks and submission correlation. Metal per-batch calibration, Vulkan calibrated timestamps and overlap lanes implemented. | Actual GPU slices align within the stated measured error, nest legally in Perfetto, preserve overlap on separate queues and degrade to duration-only data if calibration fails. |
| Phase | Deliverable and dependency | Exit gate |
|---|---|---|
| G0 Shared GPU annotations | Pass-scope subset implemented: owned RHI names, authored graph identity and Metal/optional Vulkan labels. Resource naming and nesting remain pending. | Native captures show useful resource and logical/physical pass names; disabled/missing annotation paths preserve rendering. |
| G1 Capture contract | Session/request ownership, bounded state machine, capabilities, destinations and manifest publication. Depends on G0 identities. | Production-path tests cover admission, skipped frames, deadlines, cancellation, stale replies, shutdown and output preservation. |
| G2 Metal capture | Programmatic .gputrace, startup enablement and standalone SDK fixture. Depends on G1; first local platform adapter. |
A real Mac capture opens in Xcode, contains the intended frame and exposes named resources/passes and recorded shader-source capability. |
| G3 RenderDoc capture | Linux/Vulkan launch attachment, version negotiation and explicit frame capture. Depends on G1; can follow the same contract independently of G2. | A real .rdc replays in the tested RenderDoc version with named resources/passes, correct offscreen boundaries and explicit feature limits. |
| G4 Editor and hosted target control | Shared launch/request controls, IPC identities, target selection and standalone fallback. Depends on qualified G2/G3 adapters. | Selected-world viewport and hosted-game captures target the correct process through edits/navigation/resize; failed capture retains the last drawable view. |
| G5 Shader investigation and automation | Debug/performance shader artifacts, source mapping evidence, CLI/SDK controls and reproducible capture bundles. | Installed-SDK reproduction opens without the developer's original build tree; source-display/stepping claims and overhead have recorded evidence. |
| G6 Additional platform and vendor adapters | PIX/D3D12, Android, browser tooling, vendor profiling and crash diagnostics. Depends on each platform and feature qualification. | Each adapter has its own tested capability matrix, lifecycle tests, artifact/viewer acceptance and documented limitations. |
G0 and G1 implementation slices¶
- Inventory existing resource records, authored graph names, native command boundaries and browser APIs. Define name capacity/ownership and capability behavior in the RHI owner; add public comments with their declaration change.
- Add native naming at creation/rename and nested scopes at real execution boundaries. Test merged/culled passes, early failure and resource destruction. Verify an external capture manually before extending naming granularity.
- Add the minimal capture session/request owner with private adapter hooks. Exercise it through the real frame/session path, using a controlled adapter for deterministic failures; a test-only imitation of the state machine is insufficient. Keep tool discovery and file publication off recording paths.
- Test Busy, external capture ownership, invalid destination, unavailable tool, no eligible frame, device loss, restart, stale generation and cancellation during finalization. Specify adapter cleanup before adding UI controls.
Native adapter and editor evidence¶
For G2/G3, cover onscreen and offscreen frames, uploads and compute where supported, minimized/zero-size windows, repeated capture, resize and native capture failure. Inspect the resulting image, event hierarchy, buffers/textures and shader source in the actual viewer. A nonempty file and passing mock tests do not establish replay, source stepping or GPU correctness.
For G4, exercise the selected project's current World and camera during edit, navigation, shader reload, resize and project replacement. Independently test editor rendering and hosted producer rendering, including surface generation retirement and process disconnect. Capture failure must preserve the last drawable view and the existing Pending/Ready publication semantics.
Each implementation PR supplies focused tests, required formatting/source policy/static analysis, relevant native/browser builds, sanitizer evidence and installed-SDK acceptance. Record tool/runtime versions, GPU/driver, OS, shader profile, capture scope and viewer result in the owning validation record when qualification is performed. Mark unavailable hardware/tool checks pending; never infer Windows, Android, browser or ray-tracing support from a Mac capture.
Later adapters and decision gates¶
| Tool/activity | Planned integration boundary |
|---|---|
| PIX | D5b uses external GPU capture and optional command-list regions. Programmatic capture control and PIX Timing Capture have separate tool and qualification requirements. |
| Nsight Graphics | Start with external launch and standard annotations. Optional SDK control for graphics capture/GPU traces remains version-gated while the API is beta; initialize before the graphics context and keep external launch available. |
| Radeon tools | Standard labels plus tested Radeon Developer Panel/CLI recipes for RGP, RMV and RRA. Driver and activity support are separate capabilities. |
| Android APA and AGI | Qualify the Android backend, development packaging, device/layer setup, Vulkan/ATrace annotations and artifact retrieval. APA is recommended for system profiling; its experimental frame profiler is not a universal baseline. Retain tested RenderDoc/AGI recipes. |
| Browser tools | WebGPU Inspector for WebGPU and Spector.js for WebGL; optional development-only instrumentation before API creation. Qualify workers/iframes and the actual pinned Emscripten port. |
| Hardware-specific profiling | Arm Performance Studio, Snapdragon Profiler and Intel GPA initially use shared names plus documented supported-device workflows. |
| GPU crash analysis | Optional Aftermath, Radeon GPU Detective and deeper DRED analysis under crash-report ownership. Keep matching shader artifacts; do not label a crash dump as a frame capture. |
Before implementation, resolve any missing native capture API version, artifact completion condition or hosted replay support with a small feasibility fixture. Record the result in this design's relevant contract or the existing validation owner. Do not weaken renderer synchronization, disable a supported backend or add an SDK runtime dependency merely to make one debugger attach.
References and attribution¶
Thanks to the following authors and organizations for the capture, annotation and investigation contracts adapted above. Future implementation must retain concise attribution beside the relevant native adapter and link back here.
- Khronos Group, Vulkan Debug Utilities Extension: object names and command/queue regions.
- Baldur Karlsson and RenderDoc contributors, In-application API and application API header: runtime negotiation, explicit capture boundaries and capture enumeration.
- Apple, Capturing a Metal workload programmatically and MTLCaptureManager: enablement, command-buffer intervals and trace destinations.
- Apple, Metal developer tools: Xcode/Metal inspection and platform tool roles.
- Microsoft PIX team, Programmatic Capture: startup loading, GPU/timing separation and target-window selection.
- NVIDIA, Nsight Graphics SDK User Guide: optional beta SDK and context initialization; Nsight Aftermath: GPU crash dumps.
- AMD GPUOpen, Radeon Developer Panel and RGP User Debug Markers: activity-specific capture and marker interoperability.
- Android Developers, AGI system profiling and APA Frame Profiler: system/frame distinction and current experimental scope.
- Brendan Duncan, WebGPU Inspector, and Babylon.js contributors, Spector.js: browser API inspection and capture.
- Arm, Performance Studio; Qualcomm, Snapdragon Profiler; Intel, GPA: optional hardware-specific investigation.
- Microsoft, Device Removed Extended Data, and AMD, Radeon GPU Detective: later crash-analysis workflows.