客户端渲染服务(client/rendering)
基本信息
| 属性 | 值 |
|---|---|
| 包 | com.gtnewhorizons.angelica.client.rendering |
| 本条目覆盖 | 6 个文件(目录共 7 个;BlockRenderListManager.java 已被其它条目提及) |
| 总行数 | 203 + 109 + 55 + 46 + 23 + 22 = 458 |
| 上游 | org.embeddedt.embeddium.impl.*(内嵌 Embeddium/Sodium)、net.coderbot.iris(内嵌 Iris) |
| 上游改编 | ⚠️ TextureTracker.java 改编自 Iris(:9-11 注释) |
client/ 共 23 个文件 = client/font/ 9 + client/gui/ 7 + client/rendering/ 7。client/font/ 已被 字体 覆盖;client/gui/ 见 动态灯光。
| # | 文件 | 行数 | 可见性 |
|---|---|---|---|
| 1 | DeferredDrawBatcher.java |
203 | public final |
| 2 | DeferredBatchTessellator.java |
109 | public final |
| 3 | AngelicaFogService.java |
55 | public class(INSTANCE 单例) |
| 4 | TextureTracker.java |
46 | public(INSTANCE 单例) |
| 5 | GlUniformFloat2v.java |
23 | public class |
| 6 | ModelViewDelta.java |
22 | public final |
ModelViewDelta:modelview 差分的逆矩阵
22 行,public final。本条目完整读过 —— 这是全仓最短的实质类之一。
private final Matrix4f base = new Matrix4f(); // :7
private final Matrix4f baseInverse = new Matrix4f(); // :8
public void snapshot() { // :10
base.set(GLStateManager.getModelViewMatrix()); // :11
base.invert(baseInverse); // :12
}
public Matrix4fc deltaOrNull(Matrix4f scratch) { // :15
final Matrix4f current = GLStateManager.getModelViewMatrix(); // :16
if (current.equals(base)) return null; // :17
return baseInverse.mul(current, scratch); // :18
}
两个矩阵都是 final 字段,一次分配、永久复用(无 GC 压力)。
⚠️ base.invert(baseInverse) 在 snapshot() 里无条件求逆(:12)—— modelview 奇异(不可逆)时 JOML 返回零矩阵且不抛异常(JOML 的 invert 契约),baseInverse 变零矩阵 → 后续 mul 结果全零 → 顶点被画到原点。无奇异性检查。
⚠️ base.invert(...) 有两种重载:invert(Matrix4f dest)(用 dest)和 invert()(原地)。这里用的是前者(:12),正确(不破坏 base)。
⚠️ current.equals(base) 是 JOML 的 Matrix4f.equals —— 逐元素 == 比较(含精确浮点相等)。⚠️ 不是 epsilon 比较 —— 任何微小浮点差异都会让 deltaOrNull 返回非 null,即使视觉上无差别。这是性能敏感的路径(每次 interceptDraw 都调),且语义是「精确相同才返回 null」。
唯一调用方:BakedTransformCapture.interceptDraw(BakedTransformCapture.java:54 transform = delta.deltaOrNull(deltaMatrix))—— 见 TESR 网格捕获。⚠️ 只有 1 个调用方(delta 是 BakedTransformCapture 的实例字段 :19)。
snapshot() 同样只有 1 个调用方:BakedTransformCapture.begin()(BakedTransformCapture.java:33 delta.snapshot())。
GlUniformFloat2v:GLSL uniform2f 的包装
23 行,public class(非 final)。本条目完整读过。
public class GlUniformFloat2v extends GlUniform<float[]> { // :5
public GlUniformFloat2v(int index) { super(index); } // :7-9
@Override public void set(float[] value) { // :11
if (value.length != 2) throw new IllegalArgumentException("value.length != 2"); // :12-14
GLStateManager.glUniform2f(this.index, value[0], value[1]); // :15
}
public void set(float x, float y) { // :19
GLStateManager.glUniform2f(this.index, x, y); // :20
}
}
⚠️ 继承 org.embeddedt.embeddium.impl.gl.shader.uniform.GlUniform<float[]>(:4)—— 内嵌 Embeddium/Sodium 的类。这是「补一个上游没提供的 uniform 类型」,父类的 set 抽象方法被覆写。
⚠️ 显式数组版在每次 set 时都检查 value.length != 2 并抛 IllegalArgumentException(:12-14)—— 在渲染热路径上抛异常。消息 "value.length != 2" 简洁但不含实际长度,调试时需自己数。
⚠️ 两个 set 重载(float[] 与 float, float)—— 父类只有数组版,两个 set 的 float 版是本类新增的便捷重载。⚠️ 若调用方传 null 给数组版,value.length NPE(无 null 检查)。
⚠️ 类非 final(:5)—— 可被继承。INSTANCE 不存在(每次 new)。
AngelicaFogService:内嵌 Embeddium 的雾服务实现
55 行,public class implements FogService(org.embeddedt.embeddium.impl.render.chunk.fog.FogService,:5)。
| 成员 | 行 | 实现 |
|---|---|---|
INSTANCE |
:6 |
public static final 单例 |
fogColorArray |
:8 |
private final float[4] |
getFogEnd() |
:10-13 |
GLStateManager.getFogState().getEnd() |
getFogStart() |
:15-18 |
GLStateManager.getFogState().getStart() |
getFogDensity() |
:20-23 |
GLStateManager.getFogState().getDensity() |
getFogShapeIndex() |
:25-28 |
return 0; // 1.7.10 only has one fog shape |
⚠️ getFogShapeIndex() 硬编码 0 并有注释(:27)—— 1.7.10 的 GL 没有多种雾形状(现代版支持 GL_FOG_SHAPE 的球/柱/平面)。这是本仓少见的「有注释地承认能力缺失」的写法。
⚠️ 三个 getter 每次都调 GLStateManager.getFogState()(:12、:17、:22)—— 无缓存。若着色器每帧查多次雾参数,会有重复的对象获取。getFogState() 是否返回缓存对象本条目不可判定(GLSM 子项目)。
⚠️ fogColorArray(:8)是 float[4] 实例字段 —— 供 getFogColor 之类的接口方法填充后返回。⚠️ 它是 final 数组(长度不可变但内容可变)且非静态 —— 单例下等价于静态。
⚠️ 类非 final(:5)但有 INSTANCE 单例 —— 可被继承出不同实现。
TextureTracker:改编自 Iris
46 行,public,有明确的上游归属注释:
/**
* Adapted from Iris for use in GLSM // :9-11
*/
⚠️ 这是内嵌 Iris 源码的改写版,放进 client/rendering 是因为它服务 GLSM 而非 Iris 管线。读本文件时应把它视为「Iris 代码 + GLSM 适配」,不是 Angelica 原创。
静态块接管 Iris 回调(:17-19)
private static Runnable bindTextureListener; // :15
static {
StateUpdateNotifiers.bindTextureNotifier = listener -> bindTextureListener = listener; // :18
}
⚠️ 在静态初始化时写 net.coderbot.iris.gl.state.StateUpdateNotifiers.bindTextureNotifier(内嵌 Iris 的静态字段)—— 这是「猴补丁」内嵌上游的全局回调。⚠️ 无条件覆盖,无「保留原值」逻辑 —— 若 Iris 侧或另一个 mod 也设了这个 notifier,后设者获胜,先设者永久失去回调。这是一个单槽全局回调的争抢。
⚠️ bindTextureListener 是 static Runnable 非 volatile —— 静态块里赋值(类初始化时,同线程安全),但读取方可能在其它线程。若 Iris 在工作线程通知纹理绑定,无 happens-before 保证。
纹理表(:21)
private final Int2ObjectMap<AbstractTexture> textures = new Int2ObjectOpenHashMap<>(); // :21
int(GL texture id)→ AbstractTexture 的映射。⚠️ 强引用键值(Int2ObjectOpenHashMap)—— 纹理永不从表中移除(本条目只读到 :30,releaseTexture / remove 之类的方法在 :31-46 未读)。⚠️ 若 GL texture id 被复用(glGenTextures 回收后重发),且旧条目未清,会映射到错误的 AbstractTexture。
⚠️ trackTexture(int id, AbstractTexture)(:27-29)无条件 put —— 同一 id 重复 track 覆盖旧值(正确的方向)。
方法 INSTANCE(:13):eager 初始化的静态单例,构造器私有(:25-27)。
DeferredDrawBatcher:延迟绘制批处理器
203 行,public final。⚠️ 本条目只读到第 30 行(import 区 + 类头),203 行中约 175 行未读。这是本条目最大的缺口。
已确认的 import:
| import | 用途 |
|---|---|
com.gtnewhorizons.angelica.glsm.GLStateManager |
GLSM |
com.gtnewhorizons.angelica.glsm.hooks.BatchStateGuard |
GLSM hook(与 BatchStateFallback 共用,见 TESR 管线) |
org.embeddedt.embeddium.impl.* |
内嵌 Embeddium/Sodium |
已确认的调用点(来自 ParticleRunSplitter 与 ParticleInstancer,grep 确认):
| 调用 | 位置 | 语义 |
|---|---|---|
DeferredDrawBatcher.isActive() |
ParticleRunSplitter.java:36 |
是否活跃 |
DeferredDrawBatcher.exitAndFlush() |
ParticleRunSplitter.java:36 |
退出并 flush |
DeferredDrawBatcher.enter() |
ParticleRunSplitter.java:47、ParticleInstancer.beginLayer 分支 3 |
进入 |
⚠️ BatchStateGuard 被 import 但本条目未读是否使用 —— 与 BatchStateFallback.install() 写同一全局单槽回调(BatchStateFallback.java:12)。⚠️ 若两者都写 BatchStateGuard.flush,存在覆盖关系。本条目无法判定。
⚠️ 与 ParticleCaptureTessellator 的关系:ParticleCaptureTessellator extends Tessellator(见 粒子实例化)—— 两者可能共享「延迟绘制」机制(DeferredBatchTessellator 名字也带 Deferred)。DeferredBatchTessellator 与 DeferredDrawBatcher 的分工本条目未读。
DeferredBatchTessellator:批处理版 Tessellator
109 行,public final。⚠️ 本条目只读到第 30 行,约 80 行未读。
⚠️ 类名暗示它 extends Tessellator(与 ParticleCaptureTessellator 同思路),但本条目未读到类声明行(在 :30 之后)。不下结论。
已知问题 / 风险
TextureTracker.java改编自 Iris(:9-11),不是 Angelica 原创 —— 应按上游代码对待。TextureTracker的静态块无条件覆盖 Iris 的StateUpdateNotifiers.bindTextureNotifier(:18),单槽回调争抢,无「保留原值」。TextureTracker.bindTextureListener非 volatile(:15),跨线程通知无 happens-before 保证。TextureTracker.textures是强引用 int→纹理表(:21),本条目未读到移除方法 —— GL texture id 复用时可能映射错误。ModelViewDelta.snapshot()无奇异性检查(:12)—— modelview 不可逆时 JOML 返回零矩阵,后续顶点画到原点。ModelViewDelta.deltaOrNull用精确浮点相等(:17)—— 微小差异也会返回非 null,且这是每 draw 调用的热路径。GlUniformFloat2v.set(float[])在热路径抛IllegalArgumentException(:12-14),消息不含实际长度;数组版无 null 检查。AngelicaFogService.getFogShapeIndex()硬编码 0(:27,有注释说明 1.7.10 只有一种雾形状)。AngelicaFogService三个 getter 每次都调getFogState()(:12、:17、:22),无缓存。DeferredDrawBatcher203 行中约 175 行未读、DeferredBatchTessellator109 行中约 80 行未读 —— 本条目最大的缺口。两者的分工与BatchStateGuard的归属本条目无法判定。DeferredDrawBatcher与BatchStateFallback都可能写 GLSM 的BatchStateGuard.flush单槽回调(后者见BatchStateFallback.java:12)—— 覆盖关系未确认。
相关条目
- utils 运行时工具 -
MipmapGenerator等的同族 - 粒子实例化 -
DeferredDrawBatcher的主要调用方;ParticleCaptureTessellator的对照 - Tessellator / 线程世界访问 / 渲染队列 -
DeferredDrawBatcher的归属条目 - TESR 实例化管线 -
BatchStateGuard的另一个使用者 - Iris(内嵌) -
StateUpdateNotifiers/GlUniform/FogService的来源 - Subprojects(内嵌子项目) - GLSM 的
GLStateManager/getFogState() - 动态灯光 -
client/gui/的 3 个未覆盖文件见该条目 - 字体 -
client/font/9 个文件的归属