Shared debug overlay
The debug-overlay API gives addon developers one cooperative OpenVR/D3D diagnostic surface. Consumers publish copied value data; ROCK owns rendering, capacity, expiry, generation cleanup, and coexistence between publishers.
Use it for probes, contact normals, grip targets, collider outlines, labels, and short-lived runtime diagnostics. It is not a general user-interface API.
Capability and limits
Request DebugOverlayPublication. Publication and clear are game-thread-only
and should be called from an owner frame or animation callback.
Exact collider focus is separately gated by ColliderVisualizationOverride;
request it only for tools that use slots 88–89.
Current public limits:
| Limit | Value |
|---|---|
| Publishers | 8 |
| Lines per publisher | 1,024 |
| Text rows per publisher | 16 |
| Combined rendered lines | 2,048 |
| Combined rendered text rows | 64 |
| Text storage per row | 128 bytes, including the null terminator |
| Maximum publication lease | 120 frames |
The per-publisher limits are admission limits. Combined renderer limits mean a crowded overlay may display only a bounded subset; diagnostics must not depend on every primitive being rendered.
publishDebugOverlayV1 · slot 51
RockProviderResultV1 publishDebugOverlayV1(
std::uint64_t ownerToken,
const RockProviderDebugOverlayPublicationV1* publication);
Transactionally replaces this owner's complete retained publication. ROCK copies line and text arrays during the call and never retains the consumer's buffers.
The publication requires:
- exact structure size and supported version;
- a nonzero lease, clamped to 120 frames;
- matching optional generation guards;
- non-null arrays whenever their corresponding count is nonzero;
- counts within the per-publisher limits.
Publishing zero lines and zero text rows clears this owner's content and
returns Ok.
Lines
Each RockProviderDebugOverlayLineV1 contains game-space start/end points and
RGBA color. Every coordinate must be finite and every color component must be
between 0.0 and 1.0.
RockProviderDebugOverlayLineV1 normal{};
normal.startGame[0] = contact.x;
normal.startGame[1] = contact.y;
normal.startGame[2] = contact.z;
normal.endGame[0] = contact.x + surfaceNormal.x * 4.0f;
normal.endGame[1] = contact.y + surfaceNormal.y * 4.0f;
normal.endGame[2] = contact.z + surfaceNormal.z * 4.0f;
normal.color[0] = 0.25f;
normal.color[1] = 0.90f;
normal.color[2] = 0.70f;
normal.color[3] = 1.0f;
Text
Each RockProviderDebugOverlayTextV1 has null-terminated text, RGBA color, and
textSize between 0.5 and 8.0.
- With no flags,
xandyplace the text in overlay coordinates. - With
WorldAnchored,worldAnchorGamemust be finite and anchors the label in game space.
Use snprintf or another bounded writer so the 128-byte text buffer is always
null-terminated.
RockProviderDebugOverlayTextV1 label{};
label.flags = static_cast<std::uint32_t>(
RockProviderDebugOverlayTextFlagV1::WorldAnchored);
std::snprintf(label.text, sizeof(label.text), "magazine · body %u", bodyId);
label.textSize = 1.5f;
label.color[0] = 0.95f;
label.color[1] = 0.82f;
label.color[2] = 0.35f;
label.color[3] = 1.0f;
label.worldAnchorGame[0] = partPoint.x;
label.worldAnchorGame[1] = partPoint.y;
label.worldAnchorGame[2] = partPoint.z;
Complete publication
using namespace rock::provider;
std::array<RockProviderDebugOverlayLineV1, 1> lines{ normal };
std::array<RockProviderDebugOverlayTextV1, 1> text{ label };
RockProviderDebugOverlayPublicationV1 publication{};
publication.lineCount = static_cast<std::uint32_t>(lines.size());
publication.textCount = static_cast<std::uint32_t>(text.size());
publication.lines = lines.data();
publication.textEntries = text.data();
publication.worldGeneration = frame.worldGeneration;
publication.skeletonGeneration = frame.skeletonGeneration;
publication.providerGeneration = frame.providerGeneration;
publication.leaseFrames = 2;
const auto result = RockProviderApi::inst->publishDebugOverlayV1(
ownerToken, &publication);
The local arrays may go out of scope after the call returns. Refresh only when content changes or before the short lease expires; do not allocate or format large diagnostic batches every frame without need.
clearDebugOverlayV1 · slot 52
RockProviderResultV1 clearDebugOverlayV1(std::uint64_t ownerToken);
Removes this owner's retained geometry and text. The call is callback-only and requires the same capability. It is safe to clear explicitly before consumer unregister; generation change, lease expiry, callback fault, and provider loss are automatic cleanup paths.
Exact collider focus · slots 88–89
setColliderVisualizationOverrideV1 is a separate diagnostic authority. It
temporarily replaces the complete config-driven collider presentation with one
exact body from the current equipped-weapon generation.
RockProviderColliderVisualizationRequestV1 focus{};
focus.weaponGenerationKey = frame.weaponGenerationKey;
focus.bodyId = selectedBodyId;
focus.partKind = selectedPartKind; // descriptive only
focus.leaseFrames = 2;
focus.worldGeneration = frame.worldGeneration;
focus.skeletonGeneration = frame.skeletonGeneration;
focus.providerGeneration = frame.providerGeneration;
const auto focused =
RockProviderApi::inst->setColliderVisualizationOverrideV1(
ownerToken, &focus);
The body must exist in ROCK's complete generation-bound weapon evidence
catalog. It is not limited to the compact weaponBodyIds[8] frame prefix.
partKind is only a label; body ID plus weapon generation is authoritative.
Refresh from an owner callback while focus is desired. Call
clearColliderVisualizationOverrideV1(ownerToken) or let the rolling lease
expire to restore the unchanged normal overlay. Request the separate
ColliderVisualizationOverride capability and check
supportsColliderVisualizationOverrideV1().
Result handling
| Result | Typical cause |
|---|---|
Ok | Publication copied or owner content cleared. |
WrongThread | Called outside ROCK's owner callback boundary. |
InvalidSize | Publication structure is not the exact current size. |
UnsupportedVersion | Publication version is zero or newer than the provider. |
InvalidArgument | Bad count/pointer, zero lease, bad row, non-finite coordinate, out-of-range color/text size, or unterminated text. |
WorldNotReady / NotReady | A nonzero generation guard no longer matches or provider state is unavailable. |
NotReady | ROCK cannot currently render the publication. |
PermissionDenied | Capability was not granted. |
CapacityFull | All eight publisher slots are occupied. |
TargetUnavailable | Collider focus names an old weapon generation. |
TargetInvalid | Collider focus names a body outside the complete current weapon catalog. |
Good overlay behavior
- Give every subsystem a stable color and keep alpha readable in VR.
- Prefer short lines and a few labels over dense walls of text.
- Publish only diagnostics the user explicitly enabled.
- Keep generation guards populated so stale world geometry disappears.
- Avoid per-frame heap churn and string formatting in callbacks.
- Never use overlay visibility as gameplay state; it is diagnostic and bounded.