Skip to content

refactor(rhi): delete the u32 facade forms — Phase 2 step 3 complete, facade_native_id_params 67 → 0 (#691) - #737

Merged
drsnuggles8 merged 1 commit into
masterfrom
feature/vulkan-rhi-phase2-native-id-tail-691
Aug 2, 2026
Merged

refactor(rhi): delete the u32 facade forms — Phase 2 step 3 complete, facade_native_id_params 67 → 0 (#691)#737
drsnuggles8 merged 1 commit into
masterfrom
feature/vulkan-rhi-phase2-native-id-tail-691

Conversation

@drsnuggles8

Copy link
Copy Markdown
Owner

Closes Phase 2 step 3 of #691 (item 4 — the native-id tail). #691 itself stays open: Phases 3–8 remain.

Counter at 26d2fec0 now
facade_native_id_params 67 0
sweep_renderer_id 345 168
sweep_gl_calls / sweep_glad_includes 0 / 0 0 / 0
debug_escape_hatch / backend_resolve_hatch 0 / 0 0 / 0

RendererAPI.h declares no u32 <name>ID parameter. That zero is the completion criterion ADR 0011 set for step 3, and it is the same kind of guarantee sweep_glad_includes gives one level down: a translation unit in the sweep bucket that holds a native GL name has nothing to pass it to, so the boundary is compiler-enforced rather than measured.

The design question item 4 carried: queries become identities

RHI::ResourceKind::Query already existed in the registry when the mint landed, so leaving queries on u32 kept that enumerator dead. The concrete hazard is sharper than uniformity: OcclusionQueryPool hands a query issued in frame N to BeginConditionalRender in frame N+1, so a Shutdown() + Initialize() in between frees the names and GL may reissue one — gating a draw on an unrelated occlusion result, which reads as a plausible frame rather than a broken one. A retired handle resolves to 0 and the conditional render is skipped instead.

CreateQueries is where §4's .data() trap applies literally (that warning was written about this function): the out-span never reaches the driver — the backend creates into its own std::vector<GLuint>, registers each name, and writes handles back. DeleteQueries resolves and unregisters, with the same wrong-kind guard the other Delete* virtuals carry. IsQueryResultAvailable reports false for a stale handle rather than reading query 0, because for occlusion a zero result reads as "fully occluded" — the honest-looking answer is the one that deletes geometry.

Three silent defects, same shape

None is a type error; all three compile and none failed a test.

  1. DeclareTransientTexture(name, desc, backingTextureID) set only the native id. That call publishes ShadowMapCSM / ShadowMapAtlas / the compare-off raw views into the frame graph. Consumers read them through ResolveTextureHandle, which answers null for an entry with no Handle, so DeferredLightingPass would have fallen through to the CSM placeholder every frame — shadows gone, silently.
  2. The transient planner's alias fan-out set only the native id. Pass 1 (WillAllocate) sets both since slice 7; pass 2 — entries that inherit a sibling's physical resource under aliasing — still set TextureID alone. Worse than (1) because it is load-dependent: it only appears when the planner decides to alias.
  3. ResolveTextureHandle bailed on texture subresource and multisample-resolve views. Under MSAA, SceneDepth / SceneNormals / every G-Buffer attachment on the blackboard is a CreateTextureMultisampleResolveView, and every CSM cascade is a CreateTextureArrayLayerView. With the u32 bind form deleted, those would have bound nothing — a black frame under MSAA. It now mirrors ResolveTexture's branch case for case.

The rule they share corrects PhysicalTexture's own comment: "TextureID and Handle are ALTERNATIVES, exactly one is set" is right for an import — the importer holds one currency and neither is derivable from the other. It is wrong everywhere the graph itself does the setting, because there it holds the resource object and reads both off one pointer in one statement. Slice 7 learned this for the transient acquire and fixed that one site; item 4 found three more governed by the same rule.

Two worklist "blockers" were phantoms

  • "VertexBuffer / IndexBuffer expose no GetRHIHandle()" — true and irrelevant. Their accessor is GetBufferHandle(), used only inside Platform/OpenGL/; what Renderer3DMeshSubmission and VirtualMeshRegistry actually hold is a Ref<StorageBuffer>, which has minted handles since slice 2. No producer needed adding. Grep for the accessor the caller uses, not the one the type family suggests.
  • "An import may only move to ImportTextureHandle after the diagnostics can read one" — still true, and already satisfied. RHI Phase 2 step 3: attachment consumers + the command-layer bind cache (#691) #736's Debug::NativeTextureIdForDiagnostics unblocked six imports at once (DDGI's atlases, the colour-grading LUT, the fluid intermediates, the fog volumes, the TAA/clouds history, the virtual-geometry debug target). A sequencing constraint that has been met reads exactly like one still blocking.

Also gone: the IBL trio's second currency. SetGlobalIBL took four identities and three native ids; the natives existed for exactly three lines of RenderPipeline's graph.ImportTexture(...). Switching those to ImportTextureHandle removed the parameters, three Renderer3DData fields, three accessors, and the "both currencies clear together" comment that existed to keep them in step.

Deliberately not done

  • GetRendererID() still exists on the resource classes — it is what Platform/ and Renderer/Debug/ are supposed to use, and the graph's diagnostics path reads it on purpose. Item 4 removes every way for the sweep bucket to use one, not the accessor. Deleting it is Phase 8, with the tools relocation.
  • The creators keep their ...Handle suffix — Phase 3 reshapes that family for ViewHandle anyway.
  • shaderRendererID keeps its field name on the POD command structs; it has been an RHI::ResourceHandle since slice 6, and those 114 mentions are the bulk of the remaining 168.

Verification

  • OloEngine, OloEngine-Tests, OloEditor all build with 0 errors.
  • Full suite: 5231 tests, 5227 passed, 0 failed, 4 environmental skips — the RHI Phase 2 step 3: attachment consumers + the command-layer bind cache (#691) #736 baseline exactly. All 7 RHIBoundaryRatchet tests green against the lowered baselines.
  • Evidence PNGs: six regenerated, each pixel-diffed against HEAD with a per-image noise floor measured on the identical binary. Every delta sits inside its own band (Fluid_* 0.24–0.58% vs a 0.24–0.74% floor; WorldOriginRebase_far_before byte-identical in an isolated run). OcclusionCull_Deferred differs by 2 isolated pixels — the drift already recorded for that image. Churn reverted, per a0ff3ba6's precedent.
  • Live editor: olo_render_validateok: true, 0 hazards, 0 resolve failures, 0 consumed-but-unbacked. Every migrated target captures with real content — ShadowMapCSM (4-layer, depth 0.33–1.0), ShadowMapCSMCascade0/3, ShadowCSMRaw, ShadowAtlasRaw, SceneDepth, SceneNormals, Velocity, WaterRefraction, IrradianceMap. Three posed angles inspected (shadows cast on water, correct materials and lighting). OloEngine.log error-free; olo_shader_errors 0.
  • The deferred + MSAA path — the one the view-resolve fix guards — is covered by the passing GPU test VirtualGeometryVisualEvidence.RendersUnderDeferredMSAA.

Full narrative in OloEngine/tests/Rendering/rhi_boundary_baseline.json and docs/agent-rules/rhi-abstraction-boundary.md (new "Item 4 (the tail)" section; the step-3 worklist is ticked and its two wrong predictions recorded rather than quietly corrected).

🤖 Generated with Claude Code

facade_native_id_params 67 -> 0; sweep_renderer_id 345 -> 168.

RendererAPI.h declares no `u32 <name>ID` parameter. That is the completion
criterion ADR 0011 set for Phase 2 step 3, and it is the same kind of guarantee
sweep_glad_includes gives one level down: a translation unit in the sweep bucket
that holds a native GL name has nothing to pass it to, so the boundary is
compiler-enforced rather than measured.

The counter did NOT drain per entry point as the worklist predicted. The last
caller of nearly every u32 form is another form's caller (ResolveTexture feeds
BindTexture, GetColorAttachmentRendererID feeds CopyImageSubData,
CreateTexture2D feeds SetTextureFilter), so the facade flipped in one edit and
~250 call sites were repaired against the compiler's own (file, line) output.

QUERIES BECAME IDENTITIES — the design question this item carried.
RHI::ResourceKind::Query already existed in the registry, and the recycled-name
hazard is concrete: OcclusionQueryPool hands a query issued in frame N to
BeginConditionalRender in frame N+1, so a Shutdown()+Initialize() in between
frees the names and GL may reissue one, gating a draw on an unrelated occlusion
result. CreateQueries never hands its out-span to the driver (§4's .data() trap
was written about this function); DeleteQueries resolves AND unregisters.

THREE SILENT DEFECTS, same shape, none a type error and none caught by a test:

1. DeclareTransientTexture(name, desc, backingTextureID) set only the native id.
   That publishes ShadowMapCSM / ShadowMapAtlas / the compare-off raw views, and
   consumers read them through ResolveTextureHandle — null without a Handle — so
   DeferredLightingPass would have bound the CSM placeholder every frame.
2. The transient planner's alias fan-out set only the native id. Pass 1 sets both
   since slice 7; pass 2 (entries inheriting a sibling under aliasing) did not,
   so the aliased half of any plan resolved to a null identity.
3. ResolveTextureHandle bailed on texture subresource and multisample-resolve
   views. Under MSAA the whole G-Buffer on the blackboard is a resolve view and
   every CSM cascade is a layer view — they would have bound nothing.

The rule these share corrects PhysicalTexture's own comment: "TextureID and
Handle are ALTERNATIVES" is right for an IMPORT, and wrong everywhere the graph
itself does the setting, because there it holds the resource object and reads
both off one pointer in one statement.

Two worklist "blockers" were phantoms. VertexBuffer/IndexBuffer's missing
GetRHIHandle() is real and irrelevant — their accessor is GetBufferHandle(),
backend-only, and the consumers hold Ref<StorageBuffer>, which has minted
handles since slice 2. And "an import may only move to ImportTextureHandle once
the diagnostics can read one" was already satisfied by #736, which unblocked six
imports at once.

Also gone: the IBL trio's second currency. SetGlobalIBL took four identities AND
three native ids; the natives existed for three lines of RenderPipeline's
graph.ImportTexture(...). ImportTextureHandle removed the parameters, three
Renderer3DData fields, three accessors and the comment that kept them in step.

Not done deliberately: GetRendererID() survives on the resource classes (it is
what Platform/ and Renderer/Debug/ are for, and the graph's diagnostics path
reads it); the creators keep their ...Handle suffix; shaderRendererID keeps its
field name — it has been an RHI::ResourceHandle since slice 6 and those 114
mentions are the bulk of the remaining 168.

Verification: OloEngine / OloEngine-Tests / OloEditor all build clean. Full
suite 5231 tests, 5227 passed, 0 failed, 4 environmental skips — the #736
baseline exactly — with all 7 RHIBoundaryRatchet tests green against the lowered
baselines. Six evidence PNGs pixel-diffed against HEAD with a per-image noise
floor measured on the identical binary; every delta sits inside its own band and
the churn is reverted. Live editor: olo_render_validate ok, 0 hazards, 0 resolve
failures, 0 consumed-but-unbacked; ShadowMapCSM, ShadowMapCSMCascade0/3,
ShadowCSMRaw, ShadowAtlasRaw, SceneDepth, SceneNormals, Velocity,
WaterRefraction and IrradianceMap all capture with real content; three posed
angles inspected; OloEngine.log error-free.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 113 files, which is 13 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4216a3d2-0a04-4d8e-b7b8-359935530769

📥 Commits

Reviewing files that changed from the base of the PR and between 26d2fec and 35a8794.

📒 Files selected for processing (113)
  • OloEditor/src/MCP/McpToolsRender.cpp
  • OloEngine/src/OloEngine/Animation/MorphTargets/MorphTargetEvaluator.cpp
  • OloEngine/src/OloEngine/Animation/MorphTargets/MorphTargetEvaluator.h
  • OloEngine/src/OloEngine/Particle/ParticleBatchRenderer.cpp
  • OloEngine/src/OloEngine/Particle/ParticleBatchRenderer.h
  • OloEngine/src/OloEngine/Precipitation/PrecipitationSystem.cpp
  • OloEngine/src/OloEngine/Precipitation/PrecipitationSystem.h
  • OloEngine/src/OloEngine/Renderer/CloudNoise.cpp
  • OloEngine/src/OloEngine/Renderer/CloudNoise.h
  • OloEngine/src/OloEngine/Renderer/Commands/CommandDispatch.cpp
  • OloEngine/src/OloEngine/Renderer/Commands/RenderCommand.h
  • OloEngine/src/OloEngine/Renderer/DDGI/DDGIProbeUpdatePass.cpp
  • OloEngine/src/OloEngine/Renderer/DDGI/DDGIProbeUpdatePass.h
  • OloEngine/src/OloEngine/Renderer/FluidRenderData.h
  • OloEngine/src/OloEngine/Renderer/FrameBlackboard.h
  • OloEngine/src/OloEngine/Renderer/Framebuffer.h
  • OloEngine/src/OloEngine/Renderer/GBuffer.cpp
  • OloEngine/src/OloEngine/Renderer/GBuffer.h
  • OloEngine/src/OloEngine/Renderer/HZBGenerator.cpp
  • OloEngine/src/OloEngine/Renderer/HZBGenerator.h
  • OloEngine/src/OloEngine/Renderer/Instancing/GPUFrustumCuller.cpp
  • OloEngine/src/OloEngine/Renderer/Instancing/GPUFrustumCuller.h
  • OloEngine/src/OloEngine/Renderer/LightCulling/TiledForwardPlus.cpp
  • OloEngine/src/OloEngine/Renderer/LightCulling/TiledForwardPlus.h
  • OloEngine/src/OloEngine/Renderer/Occlusion/OcclusionCuller.cpp
  • OloEngine/src/OloEngine/Renderer/Occlusion/OcclusionQueryPool.cpp
  • OloEngine/src/OloEngine/Renderer/Occlusion/OcclusionQueryPool.h
  • OloEngine/src/OloEngine/Renderer/Ocean/OceanFFTGpu.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/AOApplyRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/BloomRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/ChromaticAberrationRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/CloudscapeRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/CloudscapeRenderPass.h
  • OloEngine/src/OloEngine/Renderer/Passes/ColorGradingRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/ColorGradingRenderPass.h
  • OloEngine/src/OloEngine/Renderer/Passes/ContactShadowRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/DOFRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/DecalRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/DeferredGPUOcclusionPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/DeferredLightingPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/DeferredLightingPass.h
  • OloEngine/src/OloEngine/Renderer/Passes/DeferredOpaqueDecalPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/DepthVelocityUpscalePass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/EASURenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/FXAARenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/FinalRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/FluidCompositePass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/FluidIntermediatesPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/FluidIntermediatesPass.h
  • OloEngine/src/OloEngine/Renderer/Passes/FogRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/ForwardOverlayRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/GPUDrivenOcclusionPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/GTAORenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/GTAORenderPass.h
  • OloEngine/src/OloEngine/Renderer/Passes/MotionBlurRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/OITPrepareRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/OITResolveRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/PrecipitationRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/SSGIRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/SSRRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/SSSRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/SceneRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/SelectionOutlineRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/ShadowRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/TAARenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/ToneMapRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/ToneMapRenderPass.h
  • OloEngine/src/OloEngine/Renderer/Passes/UICompositeRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/UpscalerRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/VignetteRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/VolumetricFogPass.cpp
  • OloEngine/src/OloEngine/Renderer/Passes/VolumetricFogPass.h
  • OloEngine/src/OloEngine/Renderer/Passes/WaterRenderPass.cpp
  • OloEngine/src/OloEngine/Renderer/PostProcessSettings.h
  • OloEngine/src/OloEngine/Renderer/RGBuilder.cpp
  • OloEngine/src/OloEngine/Renderer/RGBuilder.h
  • OloEngine/src/OloEngine/Renderer/RGCommandContext.cpp
  • OloEngine/src/OloEngine/Renderer/RGCommandContext.h
  • OloEngine/src/OloEngine/Renderer/RenderCommand.h
  • OloEngine/src/OloEngine/Renderer/RenderGraph.cpp
  • OloEngine/src/OloEngine/Renderer/RenderGraph.h
  • OloEngine/src/OloEngine/Renderer/RenderPipeline.cpp
  • OloEngine/src/OloEngine/Renderer/Renderer3D.h
  • OloEngine/src/OloEngine/Renderer/Renderer3DDrawHelpers.h
  • OloEngine/src/OloEngine/Renderer/Renderer3DFrameExecution.cpp
  • OloEngine/src/OloEngine/Renderer/Renderer3DMeshSubmission.cpp
  • OloEngine/src/OloEngine/Renderer/Renderer3DState.cpp
  • OloEngine/src/OloEngine/Renderer/Renderer3DUtilityDraws.cpp
  • OloEngine/src/OloEngine/Renderer/RendererAPI.h
  • OloEngine/src/OloEngine/Renderer/TransientPool.cpp
  • OloEngine/src/OloEngine/Renderer/TransientPool.h
  • OloEngine/src/OloEngine/Renderer/VirtualGeometry/VirtualGeometryPass.cpp
  • OloEngine/src/OloEngine/Renderer/VirtualGeometry/VirtualGeometryShadow.cpp
  • OloEngine/src/OloEngine/Renderer/VirtualGeometry/VirtualMeshRegistry.cpp
  • OloEngine/src/OloEngine/Renderer/VirtualGeometry/VirtualMeshRegistry.h
  • OloEngine/src/OloEngine/Scene/Scene.cpp
  • OloEngine/src/OloEngine/Snow/SnowAccumulationSystem.cpp
  • OloEngine/src/OloEngine/Snow/SnowAccumulationSystem.h
  • OloEngine/src/OloEngine/Terrain/Editor/TerrainErosion.cpp
  • OloEngine/src/OloEngine/Wind/WindSystem.cpp
  • OloEngine/src/Platform/OpenGL/OpenGLFramebuffer.cpp
  • OloEngine/src/Platform/OpenGL/OpenGLFramebuffer.h
  • OloEngine/src/Platform/OpenGL/OpenGLRendererAPI.cpp
  • OloEngine/src/Platform/OpenGL/OpenGLRendererAPI.h
  • OloEngine/tests/Rendering/DDGI/DDGIVisualEvidenceTest.cpp
  • OloEngine/tests/Rendering/FluidRenderMathTest.cpp
  • OloEngine/tests/Rendering/MockRendererAPI.h
  • OloEngine/tests/Rendering/PropertyTests/DrawIndexedRawOffsetTest.cpp
  • OloEngine/tests/Rendering/PropertyTests/RHIHandleNativeIdentityTest.cpp
  • OloEngine/tests/Rendering/PropertyTests/VirtualGeometryVisualEvidenceTest.cpp
  • OloEngine/tests/Rendering/RenderGraphTest.cpp
  • OloEngine/tests/Rendering/rhi_boundary_baseline.json
  • docs/agent-rules/rhi-abstraction-boundary.md

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

sonarqubecloud Bot commented Aug 2, 2026

Copy link
Copy Markdown

@drsnuggles8
drsnuggles8 merged commit 2f0503b into master Aug 2, 2026
10 checks passed
@drsnuggles8
drsnuggles8 deleted the feature/vulkan-rhi-phase2-native-id-tail-691 branch August 2, 2026 11:46
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