Class IrisRenderBatch

java.lang.Object
com.hbm_m.client.render.shader.IrisRenderBatch
All Implemented Interfaces:
AutoCloseable

public final class IrisRenderBatch extends Object implements AutoCloseable
  • Method Details

    • active

      public static IrisRenderBatch active()
      Returns:
      the currently-open batch, or null if no batch is active.
    • isActive

      public static boolean isActive()
      Returns:
      true if a batch session is currently open. When true per-part renderers should call drawCompanion(com.hbm_m.client.render.IrisCompanionMesh, org.joml.Matrix4f, int) on active() instead of running their full standalone apply/clear path.
    • begin

      public static IrisRenderBatch begin(boolean shadowPass, org.joml.Matrix4f projectionMatrix)
      Opens a batch session for the given pass. Returns null if no usable Iris ExtendedShader could be resolved (e.g. shader pack disabled, Iris not loaded, reflection failed) - in which case the caller MUST fall back to per-call rendering instead of relying on drawCompanion(com.hbm_m.client.render.IrisCompanionMesh, org.joml.Matrix4f, int).

      Both passes batched. Shadow AND main pass each open one persistent batch per frame. The returned handle is always NOOP_NESTED on success — the caller's try-with-resources is decorative; the underlying batch outlives it. Subsequent BEs in the same pass piggyback on the same shader.apply() via active(). The batch is torn down when:

      • a later begin(...) call's pass differs from the active batch's isShadowPass — typical case is the shadow→main transition inside one frame, but it also handles the rare (debug-paused / single-stepped) main→shadow inverse, or
      • closePersistentIfActive() fires at AFTER_LEVEL — the safety net for the LAST batch of every frame (which has no follow-up begin() this frame to trigger pass-change close).
      This collapses N × apply()/clear() pairs into exactly TWO per frame total (one shadow, one main) regardless of machine count. On a 400-machine farm under BSL the savings dominate the per-pass CPU budget — Iris's apply() is the single most expensive call on this path because it binds the framebuffer, pushes every CustomUniform, and re-binds every sampler.

      Per-draw work is still issued eagerly inside drawCompanion(com.hbm_m.client.render.IrisCompanionMesh, org.joml.Matrix4f, int) (see the field-level note on isPersistent for why deferred sort-by-VAO flushes are unsafe).

      Parameters:
      shadowPass - whether we are inside Iris's shadow pass - selects SHADOW_* variants of the entity shader
      projectionMatrix - projection matrix to upload once into the shader
    • closePersistentIfActive

      public static void closePersistentIfActive()
      Closes any persistent batch (shadow or main) still active at end-of-frame. Called from RenderLevelStageEvent.AFTER_LEVEL as the safety net for the LAST batch of every frame — its pass-change close in begin(boolean, org.joml.Matrix4f) never fires because no follow-up begin() happens this frame. Also covers leak-into-next-frame edge cases (e.g. player turned away so no main BE dispatch but shadow camera still captured them).
    • isShadowPass

      public boolean isShadowPass()
      Whether this batch is rendering into Iris's shadow pass. Lets callers skip the expensive 8-corner LightSampleCache sampling and the writeInstanceLightmap/uploadLightmapRange pair that produces no visible output in shadow (depth-only) but dominates the frame's CPU profile and poisons the light cache with shadow-camera state. Use together with com.hbm_m.client.render.compat.ShaderCompatibilityDetector#isRenderingShadowPass() when no batch is active.
    • runVanillaOverlay

      public static void runVanillaOverlay(Runnable draw)
      Runs a short vanilla draw (recipe icon, item BER overlay) while a persistent Iris batch is open. Re-applies the batch shader afterward so the next drawCompanion(com.hbm_m.client.render.IrisCompanionMesh, org.joml.Matrix4f, int) still hits the correct program.
    • drawCompanion

      public void drawCompanion(IrisCompanionMesh companion, org.joml.Matrix4f modelView, int packedLight)
      Issues a single draw using the active batch shader. Updates only the per-instance uniforms (ModelViewMat, iris_ModelViewMatInverse, iris_NormalMat) and the per-draw lightmap UV2 attribute constant.

      Must be called from within a begin(boolean, org.joml.Matrix4f)/close() pair on the OUTER batch returned by active(). The work happens eagerly under the caller's still-bound shader program — deferring to teardown is unsafe because Iris swaps framebuffer + program between shadow and main passes before our lazy actuallyClose() runs.

    • drawCompanionWithPerVertexLight

      public void drawCompanionWithPerVertexLight(IrisCompanionMesh companion, org.joml.Matrix4f modelView, float[] cornerUV16, int packedLightFallback)
      Variant of drawCompanion(IrisCompanionMesh, Matrix4f, int) that uses per-vertex lightmap UV2 derived by trilinear interpolation from the 8 world-space corner samples in cornerUV16.

      The companion mesh must support the per-vertex lightmap path (IrisCompanionMesh.supportsPerVertexLightmap()) — it bakes trilinear weights per vertex at build time so this call only pays the per-instance combine + single glBufferSubData. When the mesh doesn't support it (build failed, unusual vertex format) we fall back transparently to the legacy constant-UV2 path using packedLightFallback.

      Why this is the right default under Iris: pack shaders read vaUV2 per vertex anyway, so supplying per-vertex values gives a smooth in-mesh gradient at zero GPU cost over the constant-UV2 path — we only trade a few dozen kilobytes of per-frame CPU arithmetic for proper block-light response across multi-block machines. A torch on one side of an Advanced Assembler now visibly brightens just that side.

      Parameters:
      companion - the companion mesh to draw (must be built)
      modelView - per-instance ModelView (same as the constant path); iris_ModelViewMatInverse / iris_NormalMat are derived from it
      cornerUV16 - [c0.blockU, c0.skyV, c1.blockU, ... c7.skyV] — 16 floats, typically produced by LightSampleCache.getOrSample8
      packedLightFallback - packed light to use when the per-vertex path can't run (companion mesh doesn't support it); ignored on the happy path
    • drawCompanionWithSlicedPerVertexLight

      public void drawCompanionWithSlicedPerVertexLight(IrisCompanionMesh companion, org.joml.Matrix4f modelView, float[] probeUV32, int packedLightFallback)
    • close

      public void close()
      Specified by:
      close in interface AutoCloseable
    • invalidateCaches

      public static void invalidateCaches()
      Drops cached uniform handles. Call after a shader pipeline rebuild (F3+T, shader pack swap) so the next begin(boolean, org.joml.Matrix4f) re-resolves them against the fresh program ID. Also force-closes any leftover persistent shadow batch so we never reuse a batch built against a now-deleted shader program.