fix(glsm): add double-angle glRotatef redirect target used by NTM-CE - #65
Merged
Conversation
HBM's Nuclear Tech (Community Edition) calls net.minecraft.client.renderer.GlStateManager.rotate(double, float, float, float) from its tile entity item renderers. The GLSM redirector rewrites vanilla GlStateManager calls to GLStateManager by method name while preserving the caller's descriptor, so the (DFFF)V rotate call became GLStateManager.glRotatef(DFFF)V — which did not exist, crashing with NoSuchMethodError when rendering the Industrial Turbine item (issue #64). Add the missing glRotatef(double, float, float, float) overload next to the existing double-parameter glScalef/glTranslatef compat overloads; it delegates to the float form. Also extend the redirector tests: GLSMRedirectorTest now asserts a synthetic GlStateManager.rotate(DFFF) call is rewritten to GLStateManager.glRotatef with the descriptor preserved, and GLStateManagerRedirectContractTest asserts both the (FFFF)V and (DFFF)V glRotatef forms exist on the compiled class (394 tests, 0 failures).
HBM-CE's MixinDefaultChunkRenderer redirects reads inside fillCommandBuffer, a method that existed in upstream Celeritas but was dropped in Actinium's rewritten renderer. The mixin then aborts with a Critical injection error, poisoning DefaultChunkRenderer and breaking world loading (issue #47): a NoClassDefFoundError on VintageRenderSectionManager\ surfaces in Minecraft.loadWorld and the client player never spawns, cascading into the player-null NPE storm. Restore a fillCommandBuffer(CameraTransform) compatibility seam (never invoked by the render path) so the redirects find their getfield targets and apply cleanly, preserving the unsafe camera access they also apply to setModelMatrixUniforms. MixinOcclusionCuller still targets the old Celeritas OcclusionCuller API that Actinium does not provide, so dev runs enable -Dmixin.env.ignoreConstraints to downgrade that Critical failure to a skip instead of poisoning the class.
HBM-CE's MixinOcclusionCuller wraps the pre-lattice upstream culling API: isWithinFrustum(Viewport, OcclusionNode) call sites in isSectionVisible and tryVisitNode, plus isWithinRenderDistance(CameraTransform, OcclusionNode, float) in isSectionVisible. Actinium's lattice-based rewrite dropped the object-node API, so the critical injections failed, poisoned OcclusionCuller, and aborted the world-join task with NoClassDefFoundError before mc.player was created — surfacing as the issue #47 player-null NPE storm. Reintroduce a minimal OcclusionNode data holder and the four legacy members as dead injection targets, mirroring upstream b07278d0. Verified in the dev client: all HBM celeritas mixins now apply cleanly and the world loads.
Chunk-builder workers read the live light NibbleArrays while the light engine keeps mutating them on the main thread, so a torn read bakes transient darkness into the mesh (water and fluidlogged quads turning black after a relight burst). Copy the block/sky light arrays eagerly while the section is being cloned on the main thread.
HBM-CE machine TESRs leak depth/blend/texture state during the render loop. The FastTESR batch flush relies on ambient GL state, so with machines in view the leaked state turned HBM water reeds into opaque black crosses that also punched holes in the water surface behind them. Wrap the block-entity region in a GLSM attrib push/pop and restore the clean entry state right before drawBatch, in both the main path and the mod-injected setTileEntities path.
…rom upstream Sync upstream commits 9b4256d0, daaefb72, 7c7c8228, d96b07f3 and the FFC/snapshot portion of 8fdbdb49 (0a3624bc..d96b07f3 + 8fdbdb49). - OcclusionCuller: explicit allowFrustumClamping parameter (7-arg findVisible), branchless integer angle refinement, aperture-based frustum clamping via FastFrustumClamping, multi-root search disables clamping. Public/package-visible signatures (isWithinFrustum, isWithinRenderDistance, tryVisitNode, Visitor) preserved. - FastFrustumClamping: new SWAR-based packed aperture culler (upstream). - RegionCullCache: reset-based classification with UNCOMPUTED and PARTIAL_DISTANCE_IN/PARTIAL_FRUSTUM_IN sub-classifications plus cached(). - SectionLattice: visibility snapshot reduced from long[] to int[] visibleFrames with SnapshotBuffers; findVisible gains the clamping flag. - RenderListManager/RenderSectionManager: thread the clamping flag through (enabled for the main pass, disabled for the orthographic shadow pass). Not included: b8c1079a shadow-occlusion refactor (deferred to the next phase), HBM-CE OcclusionNode seam (lives on fix/issue64-ntm-glsm, not in this branch).
Sync the algorithm/data portion of upstream b8c1079a (receiver-driven shadow occlusion culling). - ShadowOcclusionCuller: new receiver-driven shadow search rooted at the main pass's visible sections, traversing toward the light (upstream). - ShadowSearchFrustum: new optional capability interface for shadow frustums that support the receiver-driven search (upstream). - SectionLattice: constructor gains hasShadowPass; adds shadowVisitState, shadowCuller, visibleCells/visibleCount; splits snapshot buffers into main/shadow pairs; findVisible records visible cells when a shadow pass exists; adds findShadowVisible. - OcclusionCuller: findVisible gains visitState and recordVisible parameters; static members made package-visible for ShadowOcclusionCuller; records visible cells during the search. - Viewport: adds getFrustum(). RenderListManager/SectionGraph wiring is deferred to the next phase; the tree is intentionally not fully buildable until then.
Finish the common-side sync of upstream b8c1079a by adopting SectionGraph and the frame-ordered shadow search in the renderer layer. - SectionGraph: new shared lattice + single search thread + deferred lattice updates (upstream). - RenderListManager: constructor takes (SectionGraph, shadow, AsyncOcclusionMode, SectionTicker); attach/detach/updateSectionMetadata and the search thread move into SectionGraph; adds startShadowGraphUpdate and the shadow flag; startGraphUpdate rejects the shadow manager. - RenderSectionManager: both list managers share one SectionGraph; update() and updateForShadowPass() split by frame order (didShadowPassRunThisFrame); attach/detach/metadata fan out through the graph; the local getCurrentRenderListManager() seam is kept for the render/rebuild paths, while terrain search uses the terrain manager and shadow search uses updateForShadowPass. - SimpleWorldRenderer: setupTerrain/setupShadowTerrain/prepareFrame split, with the local shadow render-distance switch retained in setupShadowTerrain; chunk events apply while the graph is quiescent. - VintageRenderSectionManager/ActiniumWorldRenderer: drop the now-redundant finishAllGraphUpdates() in the shadow render path (setupShadowTerrain already joins). Build and all 423 tests pass.
Wire the common-side shadow search (b8c1079a) into the Iris shader module. - WorldRendererCompat: add setupShadowTerrain and getLastViewport so the shadow pass can run the frame-ordered shadow search with the player viewport captured by the preceding terrain pass. - ShadowRenderer: call setupShadowTerrain(playerViewport, shadowViewport) instead of setupTerrain in the celeritas integration block; pass resolved near/far planes and the interval size to the advanced shadow frustum (legacy perspective packs skip depth planes); update culling info strings. - AdvancedShadowCullingFrustum: implement ShadowSearchFrustum and add depth planes (toward/away) using SHADOW_CAMERA_OFFSET. - SafeZoneCullingFrustum: override supportsOcclusionSearch() to false (safe-zone semantics are incompatible with the receiver-driven search). - ShadowMatrices: extract SHADOW_CAMERA_OFFSET constant. - ActiniumWorldRenderer: implement setupShadowTerrain/getLastViewport. Shadow occlusion now runs end-to-end: the shadow search consumes the terrain pass's visible-cell root set and expands toward the light. Build and all 423 tests pass; runtime shadow verification still pending.
Sync the upstream f3c5642e benchmark harness and adapt it to Actinium's LWJGL 3.4.1 (the version Cleanroom and the mod's dependency script use). - celeritas-common/src/jmh: voxel/occlusion/multidraw benchmark sources from upstream, including the GL-backed OcclusionCullerBench and MultiDrawBench with their @benchmark methods. SectionLattice/findVisible calls updated for the post-b8 3-arg constructor and 7-arg findVisible. - build.gradle: jmh source set, jmhImplementation (jmh-core + annotation processor) and LWJGL 3.4.1 (lwjgl/lwjgl-opengl/lwjgl-egl) with the same native-classifier logic as GTNHLib; lwjgl-egl ships no natives classifier, matching upstream. - gradle.properties: jmh_version. HeadlessGl opens a system EGL context (mesa), so the GL benchmarks need EGL available at runtime (not bundled); pure-CPU benchmarks compile and run regardless. Build and all 423 tests pass.
…3.4.1" This reverts commit 16eaec9.
StreamingUploader's MAP_BUFFER_RANGE path fed the glMapBufferRange result straight into MemoryUtilities.memAddress0 without a null check. When the driver fails the mapping (returns NULL), Unsafe.getLong(null, 0x10) kills the JVM with EXCEPTION_ACCESS_VIOLATION reading address 0x10 (hs_err on the splash render thread during startup; MAP_BUFFER_RANGE is the default streaming upload strategy). Restore upstream Angelica's guarded form: RenderBackend gains mapBufferRangeAddress (returns 0L on failed mapping) and the uploader falls back to bufferData with a one-time WARN. The fallback is per-call, so transient mapping failures recover automatically on the next upload.
# Conflicts: # gradle/scripts/dependencies.gradle # src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLSMRedirectorTest.java # src/test/java/com/gtnewhorizons/angelica/glsm/redirect/GLStateManagerRedirectContractTest.java
…update HBM-CE MixinRenderSectionManager (hbm.mod.mixin.json) registers @reDIrect injections on the update(Viewport, int, boolean) method body that rewrite the CameraTransform x/y/z getfields to its unsafe accessors. The b8c1079a shadow occlusion sync delegated that camera bookkeeping to the new private updateCameraPosition helper, leaving the redirector with zero scanned targets; the mixin application aborts with InjectionError, poisons the whole RenderSectionManager class and crashes mod construction with NoClassDefFoundError (observed as an Actinium onConstruct crash). Inline the camera reads back into update (updateCameraPosition stays for the shadow pass, which HBM-CE does not redirect) and lock the binding contract with HbmCameraRedirectContractTest, which asserts the three GETFIELD instructions against the compiled class bytes (same pattern as GLStateManagerRedirectContractTest).
HBM-CE uploads OBJ models into VAOs while its glEnableClientState calls (redirected to GLSM) leak bits such as COLOR_BIT into ShaderManager's globally tracked currentVertexFlags without a matching VAO attribute. Raw GL draws (glDrawElements and friends, used by HBM's VAO path and by ShaderManager.preDraw()) then pick a shader variant that declares an attribute the VAO does not provide; the shader reads the default (0,0,0,1) and the model renders black. Because the leak is global and never restored, every machine rendered after the first model upload is affected and the pollution also breaks GUI item icons that share the FFP pipeline, independent of the fast-lit item / display-list / multidraw options. Derive the vertex-flag mask from VertexAttribState's per-VAO attribute enablement instead: ShaderManager.preDraw() and onBindVertexArray() for VAOs unknown to the vanilla VertexFormat setup path now use VertexAttribState.deriveVertexFlags(), which always matches the real GL state. Locked down by VertexAttribStateDeriveVertexFlagsTest.
Route HBM render-state scopes through the GLSM state stack and synchronize world lightmap coordinates before tile-entity rendering. Derive fixed-function vertex flags from the bound VAO so raw HBM models do not inherit stale attributes. Add conditional mixins, regression tests, and compatibility documentation. Test: ./gradlew build --no-daemon
Raise the Gradle heap and direct-memory limits to 8G and cap worker concurrency at two so GitHub Actions builds do not exhaust runner memory during compilation and remapping. Test: ./gradlew compileJava --no-daemon
Collect tile entities after the shadow render lists are updated instead of during the later terrain setup.
Closed
Conflict resolution highlights: - VertexAttribState: unify the parallel per-VAO FFP vertex-flag derivations (deriveVertexFlags from this branch, currentClientArrayVertexFlags from main's BPR fix) into the single currentClientArrayVertexFlags, keeping this branch's null guard; ShaderManager call sites and the branch's test updated to the unified name. - VertexAttribStateTest: use a direct buffer for the client pointer, as VertexAttribState.set now captures the native address. - docs/compatibility-matrix.md, dependencies.gradle, mixin condition/test lists: union of both sides.
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
Fixes #64 — placing HBM's Nuclear Tech (Community Edition) machines crashes the client:
根因 / Root Cause
Confirmed by decompiling
NTM-CE-1.12.2-2.5.0.5.jar:RenderIndustrialTurbine$1.renderCommoninvokesnet.minecraft.client.renderer.GlStateManager.rotate:(DFFF)V— the double-angle rotate overload (rotate(double, float, float, float)) used by its item renderers.GlStateManagercalls toGLStateManagerby method name while preserving the caller's descriptor (rotate/func_179114_b/func_187444_a→glRotatef), so the(DFFF)Vrotate call becameGLStateManager.glRotatef(DFFF)V.GLStateManageralready had double-parameterglScalef(double,double,double)andglTranslatef(double,double,double)compat overloads, but noglRotatef(double,float,float,float)— the link failed withNoSuchMethodError.修复 / Fix
Add
GLStateManager.glRotatef(double angle, float x, float y, float z)next to the existing double-parameter compat overloads; it delegates to the float form. No behavior change for the float path.验证 / Verification
GLSMRedirectorTest: a syntheticGlStateManager.rotate(DFFF)Vcall is rewritten toGLStateManager.glRotatefwith the(DFFF)Vdescriptor preserved.GLStateManagerRedirectContractTest: asserts both(FFFF)Vand(DFFF)VglRotatefforms exist on the compiled class (same ASM-based pattern as the issue [Bug] HammerLib fails to initialize shader programs #40glGetActiveUniformcontract test, sinceGLStateManager's static initializer needs a live GL context).Note on the "depth buffer breaks / HUD flashing" symptoms reported right before the crash: those were the aftermath of the aborted NTM item render (state pushed and never restored because the call threw mid-render). With the method present this path completes normally; if rendering-state corruption still shows up standalone, it would be a separate state-tracking issue worth a fresh report.
Closes #64
后续修复 / Follow-up Fixes
The original double-angle glRotatef link fix exposed additional rendering-state and compatibility issues in the same HBM-CE/iTRP scenario. The final branch includes the following follow-up work that was not covered by the original description.
Compatibility and loading
Rendering correctness
Shadow pipeline synchronization
The branch also contains the Celeritas shadow-occlusion synchronization used by the updated terrain path: fast frustum visibility handling, receiver-driven shadow occlusion wiring, and activation of the shadow occlusion culler (099a74f, d8dde88, 106b7e3, f375bab). The associated upstream-sync planning and completion notes are recorded in 8cb2251 and 92c55ff.
The temporary iTRP/HBM diagnostic collector used during investigation was removed before the final commit. The functional HBM compatibility layer remains because it adapts HBM's private attribute-stack semantics and lightmap requirements.
Final Verification / 最终验证
The final cleanup and verification commit is 007f377 (fix(glsm): resync shader state after foreign draws).
Additional branch maintenance / 其他分支维护
Creative inventory item render-state isolation / 创意物品栏物品渲染状态隔离
RenderItem's built-in renderer path with a GLSMglPushAttrib(GL_ALL_ATTRIB_BITS)boundary and foreign-draw synchronization.WrappedTEISRModelinvokes a built-in item renderer that can modify lighting, culling, blending, texture, color, shade, and lightmap state. The previous GUI-level restoration was too coarse: after scrolling past the Death Detonator, later slots could render solid white with alpha lost.