Skip to content

Add custom uniform block (ChunkFix) passthrough API (#2974) - #3203

Draft
Wiz1991 wants to merge 1 commit into
IrisShaders:26.2from
Wiz1991:feat/malilib-chunkfix-ubo
Draft

Add custom uniform block (ChunkFix) passthrough API (#2974)#3203
Wiz1991 wants to merge 1 commit into
IrisShaders:26.2from
Wiz1991:feat/malilib-chunkfix-ubo

Conversation

@Wiz1991

@Wiz1991 Wiz1991 commented Jul 3, 2026

Copy link
Copy Markdown

Summary

Implements the custom-UBO (ChunkFix) passthrough requested in #2974, so a mod can have its own std140 uniform block forwarded to the program Iris substitutes for its terrain pipeline when a shader pack is active.

Status: draft — compiles cleanly (:fabric:build) but is not yet verified in-game. Opening for design review and to give the assignee something concrete to build on. Details and open questions below.

The problem (per #2974)

When a shader pack is active and a mod assigns a terrain pipeline via IrisApi.assignPipeline(...), Iris substitutes its own ExtendedShader for that pipeline. That substituted program declares only Iris's own uniform blocks (ExtendedShader bind-group layouts), so a custom block the mod bound to the render pass via RenderPass.setUniform("ChunkFix", buffer) is silently discarded — nothing in Iris reads unknown UBO names off the pass. This is what breaks MaLiLib/Litematica's ChunkFix (texture-atlas size) block under shaders.

Approach

New API (minor revision bumped 3 → 4):

void registerCustomUniformBlock(RenderPipeline pipeline, String blockName, String glslDeclaration);

The mod passes the block name and its exact std140 GLSL declaration (Iris can't derive the layout, and the mod can't edit Iris's generated program, so both pieces cross the API). Then:

  1. Registry (IrisPipelines) — resolves the pipeline to its ShaderKey at registration and stores the block(s) under that key.
  2. Declaration injection (ShaderCreator) — injects the block's GLSL after the #version line of the substituted program's source, so the block exists in the linked program.
  3. Optional per-draw bind (ExtendedShader + MixinGlCommandEncoder) — after Iris sets up the substituted program, for each registered block present on this draw's pass, binds the mod's GpuBufferSlice (GlBuffer.handle() + glBindBufferRange) to a binding point clear of Iris's own blocks.

Why this is safe for shared programs

A mod's terrain pipeline resolves to the same ShaderKey (e.g. TERRAIN_SOLID) that vanilla chunk terrain uses, so the substituted program is shared. The bind is therefore made optional: it only fires for draws whose GlRenderPass.uniforms actually contains the block. Vanilla terrain draws never setUniform("ChunkFix", …), so they are provably untouched — the block is declared-but-unbound-and-unused for them.

Files

  • api/v0/IrisApi + apiimpl/IrisApiV0Impl — new method, revision → 4
  • pipeline/IrisPipelines — registry keyed by resolved ShaderKey
  • pipeline/programs/ShaderCreator — inject declaration after #version
  • pipeline/programs/ExtendedShader + pipeline/programs/IrisProgram + mixin/MixinGlCommandEncoder — optional per-draw bind
  • gl/IrisRenderSystembindBufferRange helper
  • iris.accesswidener — widen GlRenderPass.uniforms (mirrors existing .samplers)

Open questions for review

  • Binding-point selection is a fixed base (12) beyond Iris's own blocks rather than dynamically queried — is there a canonical free range to use?
  • Declaration injection is a string insert after #version; a transformer-level injection may be preferable. An unused injected block could in principle be optimized out (then the bind is a safe no-op).
  • Consumption semantics: this makes the data available to the substituted (shader-pack) terrain program, but that program won't call MaLiLib's sampleNearest(TextureSize) — so this is the plumbing half; the visual result still depends on the mod side. Paired with a MaLiLib change that registers the block; happy to iterate.

Refs #2974.

🤖 Generated with Claude Code

When a shader pack is active, Iris substitutes its own program for a mod's
assigned terrain pipeline and declares only its own uniform blocks, so a mod's
custom UBO (bound to the render pass via RenderPass.setUniform) is silently
dropped. This is the ChunkFix case reported in IrisShaders#2974 for MaLiLib/Litematica.

Add IrisApi.registerCustomUniformBlock(pipeline, blockName, glslDeclaration)
(API minor revision -> 4). Iris injects the given std140 block declaration into
the substituted program's source and, at draw time, binds the mod-provided
buffer to it -- but only for draws that actually supply the block on their pass,
so other draws that share the substituted program (e.g. vanilla terrain) are
unaffected.

- IrisApi / IrisApiV0Impl: new method, minor API revision -> 4
- IrisPipelines: registry of custom blocks keyed by resolved ShaderKey
- ShaderCreator: inject block declaration after #version into program source
- ExtendedShader / IrisProgram / MixinGlCommandEncoder: optional per-draw bind
  of the pass-provided buffer (GlBuffer.handle() + glBindBufferRange)
- IrisRenderSystem: bindBufferRange helper
- accesswidener: widen GlRenderPass.uniforms (mirrors existing .samplers)

Untested in-game; opening as a draft for review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NzV8TMASPtN4nZY9dUeiBP
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