Skip to content

Refactor API status handling and add occlusion culling - #95

Draft
ZhuRuoLing wants to merge 15 commits into
Anvil-Dev:dev/26.1from
ZhuRuoLing:feat/occusion_culling
Draft

Refactor API status handling and add occlusion culling#95
ZhuRuoLing wants to merge 15 commits into
Anvil-Dev:dev/26.1from
ZhuRuoLing:feat/occusion_culling

Conversation

@ZhuRuoLing

@ZhuRuoLing ZhuRuoLing commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR lays the first-stage foundation for occlusion culling in the rendering module and clarifies the visibility of selected internal APIs. It introduces the culler abstraction, GPU Query resources and draw path, and an OpenGL samples-query wrapper. The selectable Hi-Z and Query culling backends will be integrated in follow-up work.

Changes

  • Added OcclusionCuller, OcclusionKey, and OcclusionMethod to provide a common abstraction for rendering-feature submission, visibility decisions, and backend selection.
  • Added the occlusion-query render pipeline and its vertex and fragment shaders. Query geometry is rendered as a unit cube transformed to the feature AABB through FullTransformsUbo.
  • Extended the GPU device interface and the OpenGL implementation with samples-query creation and result retrieval, including the GlSamplesQuery lifecycle wrapper.
  • Added reusable GPU resource and ring-buffer foundations, with dedicated pools for Query objects and Query draw buffers to reduce allocation pressure across consecutive frames.
  • Added Query frame-state management: the current frame submits and executes queries, while the following frame reads their results to decide whether a feature should be drawn.
  • Changed compute-pass debug-group messages to lazy Supplier<String> values, avoiding unnecessary debug-text construction.
  • Updated ApiStatus.Internal annotations by removing them from mixin classes and explicitly marking rendering and sync classes, records, and extension interfaces that are internal implementations.

Impact and Compatibility

  • The occlusion-culling types currently provide the underlying infrastructure; runtime backend selection and the complete feature-culling flow are not wired in yet.
  • ALRComputePass.pushDebugGroup and its backend implementation now accept Supplier<String> instead of String; direct implementations and callers of this internal API must be updated accordingly.
  • The annotation changes clarify internal API boundaries and do not alter public feature behavior.

TODO

  • Add a Hierarchical Z based culling backend.
  • Add a Samples Query based culling backend.
  • Integrate occlusion culling with vanilla SubmitNodeStorage

@ZhuRuoLing
ZhuRuoLing marked this pull request as draft August 7, 2026 21:58
@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering 🔴 BC detected 5
space-select ✅ Compatible 0
font ✅ Compatible 0
util ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
wheel ✅ Compatible 0
main ✅ Compatible 0

🔴 rendering — 5 breaking change(s)

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:24

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.popDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:20

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.pushDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

cd /opt/data/workspace/AnvilLib && grep -rn "debugLabels\|pushDebugGroup" module.rendering/src/main/java/dev/anvilcraft/lib/v2/rendering/mixins/blaze3d/gl/GlDebugLabelMixin.java | head; echo "===GlDeb...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

⏱️ Rate limited. Waiting 2.9s (attempt 2/3)...

@Gugle2308

Copy link
Copy Markdown

⏱️ Rate limited. Waiting 4.9s (attempt 3/3)...

@Gugle2308

Copy link
Copy Markdown

❌ Rate limited after 3 retries — Concurrency limit exceeded for user, please retry later

@Gugle2308

Copy link
Copy Markdown

API call failed after 3 retries: Concurrency limit exceeded for user, please retry later

@Gugle2308

Copy link
Copy Markdown

分析完成。以下是审查意见。

代码审查摘要 — PR #95

操作: synchronize
范围: 75 个文件 (70 Java, 29 新增, 0 删除) / 3249 行 diff
模块: module.rendering (主要) + module.sync (仅注解规范化) + module.test
分支: feat/occusion_culling → dev/26.1

🔴 关键(合并前需修复)

  • GlSamplesQuery.end()GL46.glEndQuery(id) 传参错误query/gl/GlSamplesQuery.java:34
    LWJGL 的 glEndQuery(int target) 接收的是 target 枚举GL_ANY_SAMPLES_PASSED),不是 query id。当前把 id 当 target 传入 → 每次 GL_INVALID_ENUM,query 永不结束;后续 glBeginQuery 会因同 target 已有活跃 query 而 GL_INVALID_OPERATION所有查询全部失效。更严重的是 getValue()GL_QUERY_RESULT 同步读取一个永不结束的 query 会永久阻塞渲染线程——一旦接入渲染管线就会整帧卡死。修复:GL46.glEndQuery(GL46.GL_ANY_SAMPLES_PASSED)

  • GlSamplesQuery.getValue() — 4 字节分配 + 8 字节越界读query/gl/GlSamplesQuery.java:52-55
    stack.nmalloc(4) 只分配 4 字节(GL_ANY_SAMPLES_PASSED 结果是 GLuint),却用 MemoryAccess.getLong(buffer)Unsafe.getLong)读 8 字节——高 32 位读到的是栈上残留垃圾。结果不确定:query 为 0(被遮挡)时若垃圾位非零,shouldDraw 会误判为可见(浪费填充率);这是 OOB 读,属于未定义行为。修复:用 MemoryAccess.getInt 读 4 字节(或 nmalloc(8) + glGetQueryObjectui64v)。

这两处意味着 PR 声称已完成的 "[x] Samples Query backend" 实际不可用——虽然当前未接线不会立即崩溃,但接入即挂。建议合并前修复(都是两行改动)。

⚠️ 警告

  • ConvertDepthParamsUbo / IntSizeUbo — 宽高顺序与 GLSL 声明相反
    DEFINITION 顺序是 getHeight, getWidth,而对应 GLSL(depth_convert.csh 及 javadoc)声明为 uWidth; uHeight;BufferObjectLayoutDefinition.writeInto 按条目顺序写入,第一个 4 字节 = Height 会落到 uWidth。Hi-Z 接入且深度纹理非正方形时边界检查会错。统一 getter 顺序或 GLSL 顺序其一即可。

  • fetchResults()GL_QUERY_RESULT 同步读取 → 每帧全管线停顿
    FrameState.fetchResults() 对每个 key 做一次阻塞读,等于每帧强制 GPU 完成全部已提交工作——对以性能为目的的遮挡剔除是自毁。建议改 GL_QUERY_RESULT_AVAILABLE 轮询或延迟 2+ 帧读结果。另外注意:beginFrame() 中先 close()(把 query 归还池)再读新 previous frame 的结果,当前安全完全依赖 GL_QUERY_RESULT 的阻塞语义——若改成非阻塞读,归还顺序会变成悬垂复用风险,需一并调整。

  • 首帧/新 key 全部判不可见 → 闪烁风险
    GpuQueryOcclusionCuller.shouldDraw()previousFrameState == null 时返回 false,新出现的 key 默认 0 也不画。culler 创建后第一帧全屏消失、新进入视野的物体延迟 1-2 帧才出现。惯例是"未知即画"(保守)避免 pop-in。

  • OcclusionMethod.GPU_QUERY 无能力检查 + 硬转 (ALRGpuDeviceExtension) device
    isSupported() 无条件返回 true,createInstance 直接强转——若 device mixin 未生效或 RenderSystem.getDevice() 为 null,直接 CCE/NPE。建议 instanceof 守卫。ALRHICapabilities.getInstance() 的强转同理(ALRComputeCapabilities.init()GL.getCapabilities() 改为依赖 mixin,健壮性回退——时机本身安全,BloomPostEffect 在同一注入点已读 getDevice())。

  • ALROptions.SPD_OPTION_WAVE_INTEROP_LDS 默认值 + define 映射反转
    getPropertyBoolean 未设置属性时默认 true,而 define 映射是 ? 0 : 1(代码里自己都注释了 "weird reverted")→ 默认走 wave intrinsics 路径,shader #extension GL_KHR_shader_subgroup_quad : require + image2DArray[13](超出 GL 最低保证的 8 个 image unit)。FFX_SPD pipeline 已在 ALRComputePipelines 注册,每次资源重载都会编译——不支持的 GPU 上会持续刷编译错误(好在外层降级为 INVALID 不崩溃)。建议默认映射到兼容路径并修正命名语义。

  • 新增 compute shader 用 Vulkan 风格 layout(set = 0, binding = N)
    桌面 GLSL 无 set 限定符,严格驱动会编译报错(depth_convert.csh 还用了 #version 460)。现有降级路径能兜住,但建议在目标驱动栈上验证。

💡 建议

  • BufferObjectLayoutEntry.Builder.forSetter(...) — setter 存进 Builder 但 build() 从未使用(record 只有 type+getter),且全 diff 无调用者 → 死 API,接线或删除。
  • FrameState.keys HashSet(含 import)— 声明后从未使用,删掉。
  • HierarchicalZOcculusionCuller — 类名/文件名拼写 "Occulusion" 与包名 occlusion 不一致;空实现 stub 建议在 createInstance 显式返回 null 即可(现状已如此),后续实现时注意。
  • GlSamplesQuery.getValue() 异常消息 "GlTimerQuery is closed" — 从计时 query 类复制粘贴的残留。
  • SPDConstantBuffer 缺少 @ApiStatus.Internal,同批 Hi-Z UBO(ConvertDepthParamsUbo/IntSizeUbo)都有 — 注解边界不一致。
  • FrameState.runQueries// ChatGPT can make mistakes. Check important info. — 开发期注释,删除。
  • 每个 key 一个 createRenderPass,N 个 feature = N 个 pass;可在单个 pass 内连续 begin/draw/end 所有 query(buffer 已用 ring buffer 摊销,pass 是剩余的最大开销)。
  • ALROptimizations.occlusionCuller 静态懒加载无同步 — 目前仅渲染线程调用,安全;若将来多线程访问需 volatile/加锁。

🟢 看起来不错

  • 帧状态机设计正确:当前帧提交+执行、下一帧读结果;查询对象两帧后归还,配合阻塞读语义无复用竞态;池自动扩容处理 key 数增长。
  • 单位立方体 + FullTransformsUbo 方案很干净:288 字节顶点缓冲复用,每 key 只换 modelview;变换数学验证无误(translate(min) × scale(max-min) × viewRotation × -camPos)。
  • OCCLUSION_QUERY pipeline 配置正确:POSITION/QUADS、color WRITE_NONE、LEQUAL 深度测试且不写深度、6 个面全部 CCW 正面朝外配合 withCull(true)
  • Supplier<String> 懒调试组重构在整条链(ALRComputePass → Backend → device extension → GlDebugLabel)一致完成,顺带删掉了 popDebugGroup 未使用的参数。
  • ApiStatus.Internal 重新分配(从 mixin 类移除、加到真实内部实现/UBO/record)符合描述意图,sync 模块 8 个文件规范化一致。

📋 声称验证表

声称 状态 对应文件
OcclusionCuller/OcclusionKey/OcclusionMethod 抽象 optimization/occlusion/*.java
查询渲染管线 + 顶点/片元着色器 + FullTransformsUbo ALRPipelines.OCCLUSION_QUERY, occlusion_query.vsh/.fsh
GPU 设备接口 + OpenGL samples-query(GlSamplesQuery 生命周期) ⚠️ GlSamplesQuery 存在但含 2 个 🔴 bug
可复用 GPU 资源 + ring buffer 基础(Query 池 / Query 缓冲池) GpuReusableResource(RingBuffer), LoopResetPool, SimpleResetPool, QueryBufferPack(RingBuffer)
Query 帧状态管理(当前帧提交执行、下帧读结果) FrameState, GpuQueryOcclusionCuller
compute-pass debug-group 懒 Supplier ALRComputePass(+Backend), GlComputePassBackend, GlDeviceMixin
ApiStatus.Internal 注解调整 15 个 mixin 移除 + 内部实现类补注
[x] Samples Query backend ⚠️ 已实现但 glEndQuery/越界读使其实质不可用
[ ] Hi-Z backend ✅(按 TODO 未声称完成) 仅存 stub + depth_convert/ffx_spd shader + SPD pipeline 注册,未接线
[ ] 接入 vanilla SubmitNodeStorage ✅(未声称完成) 未实现,符合描述

结论: REQUEST_CHANGES — 基础设施和框架设计扎实,但 GlSamplesQuery 的两处错误(glEndQuery target 传参 + 4/8 字节越界读)会让已声称完成的 Samples Query 后端在接入时立即挂起渲染线程;修复后再合并。其余为 Hi-Z 接线前的潜在问题,建议顺手修正宽高顺序和默认 flag 映射。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown

💾 Self-improvement review: Skill 'rendering-gpu-pr-review' created.

@Gugle2308

Copy link
Copy Markdown

所有文件已审查完毕。汇总分析结果,输出审查意见:


代码审查摘要 — PR #95 (synchronize)

操作: synchronize
范围: 76 个文件(29 新增 / 47 修改 / 0 删除)/ 3358 行 diff
模块: module.rendering(occlusion culling 基础设施 + 注解整理)+ module.sync(纯注解规范化)

📋 声称验证表

声称 状态 对应文件
OcclusionCuller/Key/Method 抽象 optimization/occlusion/*
occlusion-query 管线 + 顶点/片元 shader ALRPipelines.OCCLUSION_QUERY, occlusion_query.{vsh,fsh}
GPU device 接口 + GL samples-query 扩展 ALRGpuDeviceExtension, GlDeviceMixin, GlSamplesQuery
GPU 资源池 / ring-buffer 基础 GpuReusableResourcePool, QueryInstancePool, DynamicUniformStorage
帧状态管理(本帧提交、下帧读结果) FrameState, GpuQueryOcclusionCuller
pushDebugGroup → Supplier<String> 惰性化 5 处调用/实现全部迁移,无残留 String 调用
ApiStatus.Internal 注解调整 mixin 类移除(12 个)、内部实现类添加(sync 7 + rendering 若干)
Samples Query 后端 GpuQueryOcclusionCuller(已实现)
Hi-Z 后端 ✅(骨架) HierarchicalZOcculusionCuller 空壳 + OcclusionMethod.HIERARCHICAL_Z.createInstance 返回 null
与 vanilla SubmitNodeStorage 集成 ✅(未接线) ALROptimizations.getOcclusionCuller() 无任何调用者

🔴 关键(接入即错的 latent bug,建议合并前修)

  • IntSizeUbo / ConvertDepthParamsUbo 宽高互换BufferObjectLayoutDefinition.writeInto 按 entries 顺序写入(已在本地 dev/26.1 克隆验证),但 IntSizeUbo.DEFINITIONgetHeightgetWidth,GLSL 声明却是 int uWidth; int uHeight;ConvertDepthParamsUbo 同理。结果:depth_convert.csh 的边界检查 idx.x >= uWidth || idx.y >= uHeight 拿到互换后的值,宽图时右/下半区域被错误填充 uPadValue=1(深度 1.0=最远),Hi-Z 深度数据损坏。当前 Hi-Z 未接线所以不触发,但 UBO 定义就是错的,接上即坏——建议把两个 UBO 的 entry 顺序改为 getWidth, getHeight
  • HierarchicalZOcculusionCuller.shouldDraw 返回 false(空壳) — 永不绘制。目前被 OcclusionMethod.HIERARCHICAL_Z.createInstance() → null 挡住,但任何人把该分支接通就会全屏消失。空壳至少应返回 true 或抛 UnsupportedOperationException,并加 @ApiStatus.Internal(或直接删除)。
  • OCCLUSION_QUERY pipeline withCull(true) — 相机位于 feature AABB 内部时(玩家走进大型结构/多方块建筑,很常见),单位立方体所有面都成背面被剔除 → 查询 0 samples → 下一帧 shouldDraw=false → 物体消失闪烁。遮挡查询的 box 应 cull(false):深度 LEQUAL 下背面样本无害(背面深度 ≥ 正面,正面通过则结果一致),而剔除背面则盒内相机场景必然误判。

⚠️ 警告

  • ffx_spd_downsample_pass.csh 是 Vulkan GLSL 混用 + 无条件注册layout (set = 0, binding = N) 是 Vulkan 语法(OpenGL 严格驱动直接编译报错);#extension GL_KHR_shader_subgroup_quad : require 使无 subgroup quad 的 GPU 编译失败;image2DArray rw_input_downsample_src_mips[13] 超出 GL 最低保证的 8 个 image unit。且该 pipeline 已在 ALRComputePipelines 注册(ALRComputeShaderManager 每次资源重载都会编译它),而 Hi-Z 未接线——为死代码付编译成本 + 兼容性风险双重问题。建议 Hi-Z 完成前不要注册。
  • ALRComputeCapabilities.init() 时机迁移的健壮性 — 从 ALRComputeShaderManager.apply(失败仅 log 降级)移到 MinecraftMixin.onCreateInstance(启动即崩路径)。ALRHICapabilities.getInstance() 硬转 (ALRGpuDeviceExtension) RenderSystem.getDevice(),mixin 未生效时 CCE。同注入点已有 ALRPostEffects.createPostEffects() 读 getDevice()(既有代码工作正常),大概率安全,但无回退。
  • OcclusionMethod.GPU_QUERY.isSupported() 无条件 true(假支持)createInstance(ALRGpuDeviceExtension) device 无 instanceof 守卫。由于 createInstance 优先走 Hi-Z 分支(恒 null)再落到 GPU_QUERY,任何 GPU 上都会创建 GPU_QUERY culler,isSupported 形同虚设。建议探测 device instanceof ALRGpuDeviceExtension
  • GL_QUERY_RESULT 同步阻塞读与池复用时序绑定GlSamplesQuery.getValue() 每帧对 N 个 query 逐个同步读(高负载下 GPU 落后即管线停顿,与遮挡剔除的性能目标相悖);更关键的是:FrameState.close() 释放 query 回池的安全性完全依赖阻塞读语义——当前时序(帧 N+1 读、帧 N+2 复用)恰好安全,但一旦改成 RESULT_AVAILABLE 非阻塞读,复用的 query id 可能仍在飞行中 → 悬垂复用。两者是一体的,请加注释锁定此依赖。
  • FFX_SPD_OPTION_WAVE_INTEROP_LDS define 反转 + 默认 trueALROptions.getPropertyBoolean 对未设置属性返回 true(默认走 wave interop 路径),配合反转映射 ? 0 : 1(注释 "weird inverted")和 shader 的 subgroup_quad : require,默认配置恰好导向兼容性最差的路径。建议默认改为纯 LDS 路径(define 1)。

💡 建议

  • MemoryAccesssun.misc.Unsafe 反射 — Java 21 模块系统下 theUnsafesetAccessible(true) 需要 jdk.unsupported 开放(launcher 的 --add-opens 不保证包含);GlSamplesQuery 只需 4 字节读,直接用 LWJGL MemoryUtil.memGetInt 更稳。
  • ALROptimizations.getOcclusionCuller()RenderSystem.getDevice() 可能在渲染线程外为 null → createInstance(null) 硬转 CCE,加 null 守卫。
  • QueryInstancePool(2) / GpuSampleQueryPool(16) 初始容量偏小 — 活跃 key 超过即触发 expand()(虽只建 CPU 侧对象,但 expand 后不缩容,峰值后池永久变大)。
  • BufferObjectLayoutEntry.Builder.forSetter 已加但 build() 未使用 — setter 字段被忽略,属半成品死代码。
  • BufferObject.createDynamicStorage@Deprecated(forRemoval) 且无调用方 — 可直接删除。

🟢 看起来不错

  • GlSamplesQuery GL 调用全部正确:glBeginQuery(GL_ANY_SAMPLES_PASSED, id) 双参 / glEndQuery(GL_ANY_SAMPLES_PASSED) 单参(target 而非 id)/ nmalloc(4) + getInt 宽度匹配(4 字节结果 4 字节读)。
  • FullTransformsUbo getter 顺序 ↔ GLSL 声明顺序一致;QueryInstance 矩阵链 translate(min)·scale(size) 验证正确(单位立方体 → inflate(0.1) 后的 AABB)。
  • 未知 key 首帧语义保守(results.getOrDefault(key, 1) → 默认画,previousFrameState == null 时画)✅ 符合遮挡剔除惯例。
  • 单 renderPass 内连续 begin/draw/end 所有 query(无 N-pass 开销)✅。
  • 资源池复用逻辑(LoopResetPool 循环扫描 + fail 兜底 expand)正确,无 NPE 路径。
  • ApiStatus.Internal 注解调整彻底且风格与 chore(annotations): 添加 ApiStatus.Internal 注解到内部接口和类 #93/refactor(common): 优化代码导入 #96 一致;FFX SPD shader 附 AMD 官方 license 头;BufferObject.write 增加 SSBO 误用 guard。
  • sync 模块 7 个文件均为纯注解规范化(@org.jetbrains... → import 后 @ApiStatus.Internal),无行为变更。

结论: COMMENT — 基础设施质量高(GL 传参、矩阵数学、池复用均正确),PR 明确未接线所以当前不会影响运行;但 3 个关键项(UBO 宽高互换、Hi-Z 空壳 shouldDraw=falsecull(true) 盒内误判)都是「接入即挂」的确定性 latent bug,建议合并前至少修复 UBO 顺序,其余项记录为集成 Hi-Z/SubmitNodeStorage 前的必改清单。


由 Hermes Agent 审查

@ZhuRuoLing
ZhuRuoLing force-pushed the feat/occusion_culling branch from 3c58b50 to 4cdf68c Compare August 13, 2026 22:34
@Gugle2308

Copy link
Copy Markdown

💾 Self-improvement review: Patched SKILL.md in skill 'rendering-gpu-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

unzip -l /tmp/nf26-sources.jar 2>/dev/null | head -5; echo "==="; python3 -c "
import zipfile
z = zipfile.ZipFile('/tmp/nf26-sources.jar')
names = [n for n in z.namelist() if 'DynamicUniformStorage' i...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering 🔴 BC detected 6
space-select ✅ Compatible 0
font ✅ Compatible 0
util ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
wheel ✅ Compatible 0
main ✅ Compatible 0

🔴 rendering — 6 breaking change(s)

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:24

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.popDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:20

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.pushDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308

Copy link
Copy Markdown

All verification complete. I have a full picture of the PR. Writing the review:

代码审查摘要 — PR #95

操作: synchronize
范围: 75 个文件(29 新增)/ 3297 行 diff
主题: 遮挡剔除第一阶段基础设施 + API 状态注解整理

🔴 关键(接入前必须修复)

  • IntSizeUbo.java / hiz/ConvertDepthParamsUbo.java — 宽高互换(确定性 bug)
    Java 侧 DEFINITION 的 entry 顺序是 getHeightgetWidth,而 BufferObjectLayoutDefinition.writeInto 严格按 entry 顺序写入(已用本地克隆 dev/26.1 分支核实),第一个 entry 落在 GLSL 第一个声明字段。两个 UBO 的 javadoc GLSL 均为 int uWidth; int uHeight; → 实际写入是 height→uWidth, width→uHeight。一旦 Hi-Z 接线,depth_convert.cshidx.x >= uWidth 判边界,互换后有效深度数据会落在错误区域、越界区反而被写入 pad 值 1.0。当前无调用方(latent),但这是接入即现的错误。修复:entry 顺序改为 getWidth, getHeight

  • ALRPipelines.OCCLUSION_QUERYwithCull(true) + 相机在 AABB 内部时查询恒为 0
    查询几何是包围盒立方体,开启背面剔除后,当相机位于 feature 的 AABB 内部(玩家站在被剔除建筑里是常态),6 个面全部为背面 → 0 样本通过 → shouldDraw 返回 false → 下一帧整个 feature 消失(pop-in)。遮挡查询 pass 的三角形数量(36 索引 × N)远小于剔除收益,建议 withCull(false),或至少加"相机在盒内强制绘制"守卫。

  • HierarchicalZOcculusionCuller.shouldDraw 恒返回 false
    占位桩的危险默认值:一旦 OcclusionMethod.HIERARCHICAL_Z.createInstance() 改为返回它(任务清单里 Hi-Z 是 next step),所有 feature 会被全部剔除。占位实现建议返回 true(保守画),与 Query 后端的语义保持一致。

⚠️ 警告

  • 硬转无 instanceof 守卫(latent CCE)OcclusionCuller.createInstanceALRHICapabilities.getInstance() 均直接 (ALRGpuDeviceExtension) RenderSystem.getDevice()。GpuDeviceMixin 未应用(mixin 冲突/插件禁用)→ CCE;早期调用时 getDevice() 可能为 null → NPE。且 OcclusionMethod.GPU_QUERY.isSupported() 无条件返回 true,是"假支持"探测——实际硬性前置只有 mixin 是否生效。建议 isSupported 里做 instanceof 检查或 try-catch。
  • ALRComputeCapabilities.init() 时机搬移引入 mixin 依赖 — 从资源重载(ALRComputeShaderManager)移到 MinecraftMixin 构造器 RETURN。与同注入点的 ALRPostEffects/CachedBlockEntityRenderingPipeline 一致,通常安全;但旧代码 GL.getCapabilities() 不依赖 mixin,新代码依赖注入接口 → mixin 失败从"重载时降级"变成"启动即崩"(AbstractMethodError/CCE)。可接受,但建议确认 mixin 应用的失败路径。
  • ComputeSupport.INSTANCE 现在可能为 nullmodule.testGuiTestScreen / AnvilLibTestClient 均直接 ComputeSupport.INSTANCE.xxx 无空检查 → 非 compute GPU 上测试直接 NPE(行为变更)。建议测试入口加空值守卫或改为可空延迟初始化。
  • QueryInstance.CreationContext 静态 vertexBuffer — static GpuBuffer 跨所有 culler/设备共享:设备重建(context loss)后悬垂;QueryInstance.close() 是空实现,GpuQueryOcclusionCuller 也没有 destroy/close 路径 → query 池、commandEncoder 永不释放。接线前需要生命周期钩子(如 OcclusionCuller.close() + 资源重载重建)。
  • ALROptions.SPD_OPTION_WAVE_INTEROP_LDS 命名反转 — 属性默认 true → define 0(LDS-only 兼容路径),效果与属性名相反(作者注释 "weird inverted")。且非默认路径启用 #extension GL_KHR_shader_subgroup_quad : require → 无 quad subgroup 的 GPU(GL < 4.6/旧硬件)编译失败;该 compute pipeline 已注册,每次资源重载都会编译并刷错误日志。建议反转映射或改名,并在 PR 描述中说明默认值语义。
  • 未接线状态的副作用ALROptimizations.getOcclusionCuller() 目前无任何调用者(AnvilLib 与 AnvilCraft 均 grep 不到),processFeatures 也无人调用 → query 从未 begin/end 时 fetchResults 读未使用 query(GL 结果实现定义,多数驱动返回 0)→ 全剔除。接线时必须同时接入 processFeatures 与 shouldDraw 的完整调用链,否则上述 latent 问题会全部同时爆发。

💡 建议

  • GlSamplesQuery.getValue() — 异常消息 "GlTimerQuery is closed" 是复制粘贴残留,应为 "GlSamplesQuery"。GL_QUERY_RESULT 阻塞读已通过"帧首读上一帧"缓解;若后续实测高负载帧停顿,可换 GL_QUERY_RESULT_AVAILABLE 轮询。
  • occlusion_query.fsh — pipeline 已 WRITE_NONE,fragColor 不会落屏,可用 discard 让意图更明确(可选)。
  • FrameState.runQueries — 单 RenderPass 内连续 begin/draw/end 全部 query,结构正确;getBuffer(6*6)(36 索引 = 6 面 QUADS)与 24 顶点 VB 匹配无误。
  • DynamicUniformStorage 512 容量 — MC 26.1 实现超限自动扩容(类内已有 resize 逻辑),无需担心。
  • BufferObject.createDynamicStorage16 — 经核实是修复:构造器第三参是 initialCapacity(非 usage),旧代码误传 USAGE_UNIFORM | USAGE_COPY_DST(=18)当容量;新代码显式 16 + SSBO 守卫 + @Deprecated(forRemoval) 一致。建议在 PR 描述中注明此修复。

🟢 看起来不错

  • GL query API 传参全部正确glBeginQuery(target, id) 双参、glEndQuery(GL_ANY_SAMPLES_PASSED) 只传 target(未把 id 当 target 传);nmalloc(4) 分配与 MemoryAccess.getInt 读取宽度一致(无越界读)。
  • 池复用时序安全 — 阻塞读发生在 release 之前、query 两帧后才归还池,无悬垂复用;FrameState.close()beginFrame 的帧间交接(S(n-2) 归还 → S(n-1) 读取 → S(n) 新建)自洽。
  • 未知 key 语义保守shouldDraw 对无结果 key 默认 1、首帧 previousFrameState 为 null 时返回 true,符合"未知即画"惯例,无首帧全屏消失问题。
  • FullTransformsUbo 字段顺序正确 — getter 顺序(ProjMat → ModelViewMat)与 occlusion_query.vsh 的 GLSL 声明一致(对比之下更凸显另两个 UBO 的互换)。
  • OCCLUSION_QUERY 管线配置正确WRITE_NONE + depth write false + LESS_THAN_OR_EQUAL,查询 pass 不污染主颜色/深度目标,且在地形之后执行,遮挡语义正确。
  • Supplier<String> debug group 重构完整 — 接口(ALRGpuDeviceExtension/ALRGpuDeviceBackendExtension)、后端(GlDeviceMixin/GlComputePassBackend)、调用点(ALRComputePass)全链路一致,popDebugGroup 去参干净。
  • ApiStatus.Internal 注解清理与描述一致 — mixin 类(MinecraftMixin/GuiRendererMixin/GpuDeviceMixin 等 ~12 个)移除注解,内部实现类/扩展接口/sync 模块 ~20 处补标,sync 模块 8 个文件为纯注解变更。

📋 声称验证表

声称 状态 对应文件
OcclusionCuller / OcclusionKey / OcclusionMethod 抽象 3 个新文件
occlusion-query 管线 + 顶点/片元着色器 ALRPipelines.OCCLUSION_QUERY + vsh/fsh
GPU 设备接口扩展 + GlSamplesQuery ALRGpuDeviceExtension(+Backend)、GlDeviceMixin、GpuDeviceMixin、ALRHICapabilities
GPU 资源/ring-buffer 池基础 GpuReusableResource(Pool)、LoopResetPool、SimpleResetPool
Query 帧状态管理(本帧提交、次帧读结果) FrameState、GpuQueryOcclusionCuller、QueryInstance
pushDebugGroup 改 Supplier ALRComputePass 全链路
ApiStatus.Internal 注解调整 渲染 + sync 模块
Samples Query 后端 ⚠️ 已实现但未接线 GpuQueryOcclusionCuller;ALROptimizations.getOcclusionCuller() 无调用者
Hi-Z 后端 ⚠️ 仅骨架 HierarchicalZOcculusionCuller 空实现 + ffx_spd/depth_convert + UBOs;createInstance 返回 null
集成 vanilla SubmitNodeStorage ❌ TODO 无对应代码

🧪 测试建议

被测目标 推荐测试场景 优先级
FrameState.runQueries/fetchResults 连续多帧 key 增删:池无重复 acquire、结果延迟一帧生效、释放后复用安全 🔴
UBO 布局(接入后) IntSizeUbo/ConvertDepthParamsUbo 写入字节与 GLSL 声明逐字段对照 🔴
GpuQueryOcclusionCuller.shouldDraw 相机在 AABB 内部 / 包围盒部分可见 / 完全被地形遮挡三场景 🟡
GlSamplesQuery.getValue 查询未 begin/end 时读取、release 后结果重置、close 后抛异常 🟡

结论: REQUEST_CHANGES — 基础设施质量整体很高(GL 调用、池时序、帧状态设计都正确),但三个 🔴 均为确定性缺陷:两个 UBO 宽高互换是接入即现的错误、withCull(true) 在相机入盒场景会错误剔除、Hi-Z 占位桩默认返回 false 是隐患。三者修复成本都极低(各一行级别),建议修复后再合入,避免错误语义沉淀到后续接线 PR。ALROptimizations 的接线入口建议在本 PR 内补一个最小调用链(哪怕只在 debug 模式),让 latent 问题能在真实帧循环中提前暴露。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown

💾 Self-improvement review: Patched SKILL.md in skill 'rendering-gpu-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering 🔴 BC detected 9
space-select ✅ Compatible 0
font ✅ Compatible 0
util ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
wheel ✅ Compatible 0
main ✅ Compatible 0

🔴 rendering — 9 breaking change(s)

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:66

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bind(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout<T>,T)
METHOD_RETURN_TYPE_ERASURE_CHANGED
✗ binary-breaking
✓ source-compatible

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:66

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bind(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout<T>,T)
METHOD_RETURN_TYPE_CHANGED_INCOMPATIBLE
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:24

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.popDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:20

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.pushDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308

Copy link
Copy Markdown

审查完成,所有关键路径已交叉验证(本地克隆 AnvilLib@dev/26.1、MC 26.1 反编译产物、shader、GL 常量值)。以下是审查意见:

代码审查摘要 — PR #95

操作: synchronize
范围: 83 个文件(78 Java,35 新增,0 删除)/ 3932 行 diff

🔴 关键(确定性代码缺陷,接入即触发)

  • ALRComputePass.bindAll — 首轮调用必崩ALRComputePass.java
    新代码 bindingPoint += this.bind(bindingPoint, binding, elements.get(bindingPoint - 1));bindingPoint 初始为 0 → 第一轮即 elements.get(-1)IndexOutOfBoundsException。旧代码是 bind(bindingPoint++, ..., elements.get(bindingPoint - 1))(先自增后取,等价 get(0)),语义在改签名时被破坏。应改为 elements.get(bindingPoint)。当前无调用者(已确认),但任何第一次 bindAll 调用都会崩。

  • QueryInstance.prepareMesh — x+ 面绕序反向QueryInstance.java
    x+ 面顶点 (1,0,0)→(1,0,1)→(1,1,1)→(1,1,0) 的叉积法线为 (-1,0,0),与 outward +x 相反(其余 5 面均正确)。配合 OCCLUSION_QUERYwithCull(true),从 +x 方向观察时正面被剔除,仅剩 4 个掠射面 → 大 AABB 正面直视时 samples 可能为 0 → 视野内的物体会被误剔除。修复:x+ 面改为 (1,y0,z1),(1,y0,z0),(1,y1,z0),(1,y1,z1) 或等价绕序。

  • ALRPipelines.OCCLUSION_QUERY 仍是 withCull(true) — 相机位于 feature AABB 内部(玩家走进大型结构)时立方体所有面均背面剔除 → 0 samples → shouldDraw=false → 物体消失/闪烁。遮挡查询盒应 cull(false)(深度 LEQUAL 下背面样本无害)。上轮已提出,本次未见修改。

  • IntSizeUbo — 宽高互换未修复IntSizeUbo.java
    DEFINITION 顺序 getHeight → getWidth,与自身 javadoc 的 GLSL 声明 uWidth; uHeight; 相反。同轮次 ConvertDepthParamsUbo 已改为 getWidth → getHeight ✅(与 depth_convert.csh 一致),唯独此类仍是反的。当前无调用者(dead code),但一旦用于任何尺寸参数即互换。

⚠️ 警告(latent,接入 Hi-Z / SubmitNodeStorage 时必踩)

  • FFX_SPD_DOWNSAMPLE_PASS 绑定布局与 shader 不一致
    shader 声明 binding=2rw_internal_global_atomic(std430 coherent buffer),但 pipeline builder 只注册了 cbFSR1@0、texture@1、mid_mip@2、mips@3——缺少原子计数器绑定bindAll 连续分配时 mid_mip/mips 整体错位 1 个 binding 点,且原子计数器永远不会被绑定(dispatch 时读未绑定 SSBO = UB)。另外 ImageArrayBinding 未覆写 applyOrdered(默认返回 1,13 个 image 只推进 1),且 Preconditions.checkElementIndex(size, resource.size(), ...) 参数颠倒——合法输入(resource.size()==size)抛异常,非法输入不抛。三者都是接入时才爆。

  • GlSamplesQuery.getValue() 每 key 每帧一次 GL_QUERY_RESULT 阻塞读 — 对以性能为目的的剔除是自伤模式(N 个 key = N 次管线停顿)。一帧延迟部分缓解,但注意:query 池的 release→reacquire 复用安全完全依赖本次阻塞读语义(F1 查询结果在 F2 读、F3 才归还复用,时序自洽 ✅)——将来改非阻塞读时必须同步调整归还顺序,否则变成悬垂复用。

  • MemoryAccess 仍用 sun.misc.Unsafe 反射Unsafe.theUnsafe + setAccessible(true),123 行只为读 4 字节)——GlSamplesQuery 只需 4 字节读,MemoryUtil.memGetInt 足够;Java 21 模块系统下 jdk.unsupported 未 open 时类加载即崩。本次 4 字节读本身宽度匹配正确(nmalloc(4) + getInt ✅),问题在机制本身。

  • ALROptimizations.create() 时序 + 空守卫
    MinecraftMixin.onCreateInstanceALROptimizations.create() 先于 ALRComputeCapabilities.init() 执行 → 启动时 HIERARCHICAL_Z.isSupported() 恒为 false → 后端选择永远是 GPU_QUERY,与 createInstance 内 HiZ 优先的意图矛盾;② getOcclusionCuller()RenderSystem.getDevice() 无 null 守卫(渲染线程外调用 → 硬转链 NPE);③ 创建的 culler 全库无人消费,静态持有 16 个 GL query + DynamicUniformStorage(512)(HiZ 路径则是 13 张纹理 + 3 个 buffer),纯浪费。

  • OcclusionCuller.createInstance 优先返回 HiZ 空壳 — compute 可用时选中 HierarchicalZOcclusionCuller(submit/process 均 no-op,shouldDraw=true ✅ 保守),实现完整的 Query 后端反而不被选中;该 stub 构造函数在未实现时已分配全部 GPU 资源,且无 close()/资源释放路径deleteTextures 只关纹理,spdParamsBuffer/convertParamsBuffer/spdGlobalAtomicCounterBuffer 三个 GpuBuffer 泄漏)。

  • ffx_spd_downsample_pass.csh 兼容性 — ① 桌面 GLSL 450 使用 Vulkan 风格 layout(set=0, binding=N):严格驱动编译报错,宽松驱动忽略(需在目标驱动栈验证);② 默认配置(SPD_OPTION_WAVE_INTEROP_LDS=true → define 0,注释已承认 "weird inverted")走 #extension GL_KHR_shader_subgroup_quad : require 路径——无 subgroup quad 的 GPU 编译失败;③ rw_input_downsample_src_mips[13] 超过 GL 最低保证的 8 个 image unit;④ 该 pipeline 已注册(资源重载即编译)但从未 dispatch,编译失败会持续刷日志。

  • MipLayer.getDefinition() 返回 null — 违反 BufferObject 基类契约,任何 size()/write() 调用即 NPE;作为纯数据 holder 不应继承 BufferObject

  • BufferObjectLayoutEntry.Builder.forSetter 是死 API — setter 字段从未传给 build()(record 无 setter 组件)。

  • GpuReusableResourcePool.fail(false) 返回 pool[0] 不检查可用性 — 双 acquire 悬垂复用(SimpleResetPool.acquire(false) 同路径;当前无调用者,dormant)。

  • QueryInstance.CreationContext 静态 vertexBuffer — record 内 static 可变字段,永不释放;设备重建/资源重载后悬垂。

💡 建议

  • GlDeviceMixin 用魔数 usage & 16 判 cubemap——已核实 MC 26.1 USAGE_CUBEMAP_COMPATIBLE == 16 ✅ 功能正确,但建议改用 GpuTexture.USAGE_CUBEMAP_COMPATIBLE 常量。
  • tonemappers.glsl(159 行)新增但 diff 与基线中均无任何引用——dead include。
  • sync 模块多处重复 import org.jetbrains.annotations.ApiStatus(如 LazySyncBytecodeInjector)。
  • 5 个文件缺末尾换行:MemoryAccess.javaLoopResetPool.javaSimpleResetPool.javadepth_convert.cshExtendedTextureFormat.java
  • 帧内 fetchResults 可先轮询 GL_QUERY_RESULT_AVAILABLE 减少无条件停顿。

🟢 看起来不错

  • glBeginQuery(target,id) / glEndQuery(target) / glGetQueryObjectuiv 传参全部正确(end 用 target 枚举 ✅),结果读取宽度匹配 ✅。
  • FullTransformsUbo(ProjMat→ModelViewMat)、ConvertDepthParamsUbo(width→height→padValue)、SPDConstantBuffer(mips→numWorkGroups→workGroupOffset→invInputSize)的 DEFINITION 顺序与各自 GLSL 声明逐字段一致writeInto 按 entries 顺序写入已验证)✅。
  • 未知 key 首帧语义保守:shouldDraw 首帧返回 true、getOrDefault(key, 1) 默认画 ✅;HiZ stub shouldDraw=true ✅(上轮意见已采纳)。
  • 查询 pass 单 RenderPass 批量提交全部 query(非每 feature 一个 pass)✅;debug group 惰性 Supplier 改造完整(接口/GL 后端/mixin 全链路一致)✅。
  • FrameState 双缓冲时序自洽:F1 提交 → F2 读结果 → F3 归还复用,阻塞读语义下池复用安全。
  • @ApiStatus.Internal 从 15 个 mixin 类移除、标注内部实现类,与 PR 描述一致 ✅。

📋 声称验证表

声称 状态 对应文件
OcclusionCuller/Key/Method 抽象 optimization/occlusion/*
查询管线 + 顶点/片元 shader + FullTransformsUbo ✅(含 🔴 绕序/cull 问题) ALRPipelines, occlusion_query.*, FullTransformsUbo
GPU device 接口 + GL samples-query 封装 GlSamplesQuery, GpuQueryObject, GlDeviceMixin
资源池 + ring buffer 基础 ✅(含 ⚠️ fail(false) 隐患) GpuReusableResourcePool, LoopResetPool, SimpleResetPool
帧状态管理(本帧提交/次帧读取) FrameState, GpuQueryOcclusionCuller
compute debug-group 惰性 Supplier ALRComputePass, GlComputePassBackend
ApiStatus.Internal 注解调整 15 个 mixin 移除 + 内部类标注
Hi-Z 后端 ⚠️ 仅 stub(TODO 未勾选,自认) HierarchicalZOcclusionCuller
Samples Query 后端 ⚠️ 已实现但未接线 GpuQueryOcclusionCuller
接入 vanilla SubmitNodeStorage ❌ TODO 未勾选(自认)

🧪 测试建议

被测目标 推荐场景 优先级
ALRComputePass.bindAll 首次调用即 IOOBE(当前必现) 🔴
ImageArrayBinding.apply resource.size() == size> size 两分支(当前逻辑颠倒) 🔴
QueryInstance 立方体 从 +x/-x 各方向 + 相机在 AABB 内部观察(samples 是否 >0) 🔴
GpuSampleQueryPool 连续 3 帧 key 数 > 池容量,验证 expand 与 release 时序 🟡
ALROptimizations 启动顺序(create 先于 compute init)下的后端选择断言 🟡

结论: REQUEST_CHANGES — 基础设施方向正确、多数问题因未接线而处于 latent;但 bindAll 是对既有 API 的确定性回归、x+ 面绕序与 IntSizeUbo 宽高互换是确定的代码缺陷,建议合并前修复;其余 ⚠️ 项作为接入 Hi-Z/SubmitNodeStorage 时的必办清单保留在 PR 中跟踪。


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown

💾 Self-improvement review: Patched SKILL.md in skill 'rendering-gpu-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

# 检查 12 个 feature mixins 是否有统一的"过滤链"(filterVisibleFeatures 在 renderXxx 里过滤 submits)
# 关键:这些 mixin 修改的是"渲染时读取的 submits",而 OcclusionSubmitNodeCollection.submitFeatureToCuller
# 在 submit 阶段收集 feature → c...

Reason: Security scan — [HIGH] Confusable Unicode characters in text: Content contains Unicode characters visually identical to ASCII (math alphanumerics, Cyrillic/Greek lookalikes) appearing near ASCII text, which may indicate a homoglyph attack

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

⚠️ Iteration budget exhausted (90/90) — asking model to summarise

@Gugle2308

Copy link
Copy Markdown

I reached the maximum iterations (90) but couldn't summarize. Error:

<title>504 Gateway Time-out</title>

504 Gateway Time-out


openresty

@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering 🔴 BC detected 9
space-select ✅ Compatible 0
font ✅ Compatible 0
util ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
wheel ✅ Compatible 0
main ✅ Compatible 0

🔴 rendering — 9 breaking change(s)

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:66

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bind(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout<T>,T)
METHOD_RETURN_TYPE_ERASURE_CHANGED
✗ binary-breaking
✓ source-compatible

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:66

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bind(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout<T>,T)
METHOD_RETURN_TYPE_CHANGED_INCOMPATIBLE
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:24

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.popDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:20

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.pushDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

cd /opt/data/workspace && head -c 200 nf-src.jar | xxd | head -5; echo "---try jar tf---"; jar tf nf-src.jar 2>/dev/null | head -5 || python3 -c "print('no jar tool')" 2>/dev/null; ls /opt/data/worksp...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

cd /opt/data/workspace && python3 -c "
import zipfile, re
z = zipfile.ZipFile('client261.jar')
data = z.read('net/minecraft/client/renderer/SubmitNodeCollection.class')
# crude string extraction
strin...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

⚠️ Iteration budget exhausted (90/90) — asking model to summarise

@Gugle2308

Copy link
Copy Markdown

I reached the maximum iterations (90) but couldn't summarize. Error:

<title>504 Gateway Time-out</title>

504 Gateway Time-out


openresty

@Gugle2308

Copy link
Copy Markdown

💾 Self-improvement review: Patched SKILL.md in skill 'rendering-gpu-pr-review' (1 replacement). · Patched references/pr95-occlusion-culling-review-matrix.md in skill 'rendering-gpu-pr-review' (1 replacement). · Patched references/class-file-constant-extraction.md in skill 'rendering-gpu-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ⚪ Skipped
collision ⚪ Skipped
config ⚪ Skipped
integration ⚪ Skipped
moveable-entity-block ⚪ Skipped
network ⚪ Skipped
rendering ⚪ Skipped
space-select ⚪ Skipped
font ⚪ Skipped
util ⚪ Skipped
explosion ⚪ Skipped
rpc ⚪ Skipped
multiblock ⚪ Skipped
recipe ⚪ Skipped
registrum ⚪ Skipped
sync ⚪ Skipped
wheel ⚪ Skipped
main ⚪ Skipped

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308

Copy link
Copy Markdown

分析完成,输出审查意见。

代码审查摘要 — PR #95

操作: synchronize
范围: 135 个文件 (120 Java, 70 新增, 0 删除) / 7172 行 diff
分支: feat/occusion_culling → dev/26.1
性质: 渲染模块基础设施 PR——遮挡剔除(Occlusion Culling)第一阶段:culler 抽象 + GPU Query 管线 + OpenGL samples-query 封装 + Hi-Z(SPD)降采样骨架 + 可复用 GPU 资源池 + ApiStatus.Internal 注解清理

📋 声称验证表

声称 状态 对应文件
OcclusionCuller/OcclusionKey/OcclusionMethod 抽象 optimization/occlusion/*.java
occlusion-query 渲染管线 + shader ALRPipelines.OCCLUSION_QUERY, core/occlusion_query.{vsh,fsh}
GPU device 接口 + GL samples-query 封装 ALRGpuDevice(Backend)Extension, GlSamplesQuery, GpuDeviceMixin, GlDeviceMixin
可复用 GPU 资源池 + Query 专用池 GpuReusableResource(Pool), LoopResetPool, SimpleResetPool, GpuSampleQueryPool, QueryInstancePool
Query 帧状态管理(隔帧读结果) GpuQueryOcclusionCuller, FrameState
compute debug-group 惰性 Supplier ALRComputePass(Backend), GlComputePassBackend, GlDeviceMixin
ApiStatus.Internal 注解调整 20+ mixin / extension / GUI 类
Hi-Z backend(TODO 声称未完成) ⚠️ HierarchicalZOcclusionCuller 等——与 PR 描述一致未接入,但存在真实缺陷(见下)
Samples Query backend + vanilla SubmitNodeStorage 集成 ⚠️ 已接入但未接线的死代码(见下)

🔴 关键

  • submitFeatureKey 的遮挡状态丢失(GPU_QUERY 路径)GpuQueryOcclusionCuller.shouldDraw()currentFrameState.getKey(feature)(当前帧收集的特征→key 映射)去查 previousFrameState.results(上一帧的查询结果)。若某特征上一帧被剔除、或上一帧新增/消失,则当前帧收集时它不在(被 OcclusionSubmitNodeCollection.endOcclusionRecord()if (this.wasUsed()) 排除 → 不 submit 到 culler),previousFrameState.getKey(feature) 返回 null → results.getOrDefault(null, 1) 恒返回 1 → 特征永久绘制。剔除从未生效,除非特征恰好每帧都被提交(但那样它已被上一帧渲染、不应被剔除——逻辑上自相矛盾)。这是"基于上一帧结果决定本帧是否提交"的经典单帧延迟死锁:查询结果只有在下一次提交之后才有机会被消费,而提交本身先被结果门控。需至少维护一个不参与剔除的"探测集"(每帧无条件提交一次 key 以刷新查询)。
  • OcclusionSubmitNodeCollection.beginOcclusionRecordclear() 后不重置 wasUsed — 被 submitFeatureToCuller()alrSetWasUsed(true)(仅置 true)与 endFrame()(未确认是否清 wasUsed)影响:同一 orderOcclusionSubmitNodeCollectionInt2ObjectAVLTreeMap 缓存复用的,上一帧已提交过的特征列表会残留clear() 只清收集器内容,不清 wasUsed),导致 wasUsed() 恒真、collectedFeatures 含陈旧引用(提交给 culler 的 keyAssociations 里可能带上一帧已消失的 feature)→ 渲染端过滤时对已消失对象调 shouldDraw。需要在 beginOcclusionRecord 中显式重置 wasUsed=false
  • FrameState.runQueriesdynamicStorage.writeUniforms(transforms) 数组含 null 元素FullTransformsUbo[] transforms 大小 = queries.size(),但 cameraInside 的 query 被 continue 跳过 → 对应槽位 null。writeUniforms 内部是否容忍 null 未在 diff 中确认,若按 UBO 字节拷贝会 NPE/脏数据。应改为过滤后收集或按索引跳过写入。
  • FrameState.runQueries 缺少 try/finally 资源释放 — 中途异常(如 writeUniforms NPE)会跳过 releaseInstance / dynamicStorage.endFrame() / alrPopDebugGroup(),造成 query/instance 池泄漏 + debug group 栈失衡(后续帧 KHRDebug 报错)。alrPushDebugGroup 与 render pass 之间建议 finally 包裹。

⚠️ 警告

  • Hi-Z 路径硬编码 GL46.glBeginQuery 未处理 OpenGL 版本 — 直接用 GL46 常量,若驱动仅支持 GL_ARB_occlusion_query(GL 3.2 之前)会抛 IllegalStateException。Minecraft 26.1 要求 GL 3.3+,但 GL_ANY_SAMPLES_PASSED 属于 GL 4.2+;建议用 GL33/ARB 常量或能力检测。
  • GlSamplesQuery.getValue() 使用 GL_QUERY_RESULT(阻塞)在渲染线程同步等待 — 下一帧 fetchResults() 才调用,等价于隐式 glFinish,会吃掉查询本应省下的 GPU 并行度;建议 GL_QUERY_RESULT_AVAILABLE + 延迟读回,或明确接受该帧延迟。注:GL_QUERY_RESULT 同步等待语义与 GL_ANY_SAMPLES_PASSED 的异步性质冲突,至少应确认 intent。
  • QueryInstance.CreationContext.vertexBufferprivate static — 跨多个 GpuQueryOcclusionCuller/CreationContext 共享(注释"should keep alive when game running")。静态单例 + prepareMesh 内的 if (vertexBuffer == null) 非线程安全初始化,且 GpuDevice 重建(如 GL context 重建、显示切换)后旧 buffer 会悬空。建议实例级持有或随 device 生命周期管理。
  • GpuReusableResourcePool.fail(true)expand()get(index) 拿新实例但未 onAcquire(与 LoopResetPool.acquire 行为不一致)fail() 直接 query.acquire() 而非 onAcquire(t),若子类 onAcquire 有额外状态初始化(当前 QueryInstance.acquire 只置 flag,尚无害,但契约不一致);且 GpuSampleQueryPool.acquireFrameState.addKey 中 key 重复时不会重复 acquire——keySamplesMap 复用同一 query,无问题。
  • IntSizeUbo.DEFINITION 字段顺序与 javadoc 不符 — javadoc 声称 uWidth 在前,实际 ofInt().forGetter(getHeight) 先于 getWidth。若 shader 按 javadoc 布局读取会错位(当前 Hi-Z 路径未用此类,暂无实际影响)。
  • ExtendedTextureFormatGlExtendedTexture 使用 TextureFormat.RGBA8 伪装真实格式 — 注释说明"不用作 framebuffer attachment",但 alrCreateExtendedTexturetexImage2D 用真实内部格式(R32F 等)分配,而 GlTexture 元数据为 RGBA8;GlComputePassBackend.setupImage 已用 getActualFormat() 走扩展路径,但其他走 GlConst.toGlInternalId(state.resource().getFormat()) 的调用点(若存在)会拿到 RGBA8 → GL 不匹配。已改的 setupImage 是唯一路径则安全,建议全局 grep 确认。
  • MipLayer.getDefinition() 返回 null + BufferObject.write 对 SSBO 抛 IllegalStateExceptionMipLayer 继承 BufferObject 且 usage=SSBO,若任何代码对 mipLayers[i] 调用 .write()/upload() 会 NPE 或抛异常;当前 mipLayers 数组仅赋值从未读取(dead code),spdDispatchtextures.addAll(Arrays.asList(mipTextures)) 只用纹理,MipLayer 完全未参与。建议删除 MipLayer 或补全布局定义。
  • SinglePassDownsampler.spdDispatchmidTex = textures.get(6) — 注释说 SPD 需要 7 个 slot 的中间 mip 作为 rw_input_downsample_src_mid_mip,但 textures 列表顺序是 [source, mip0, mip1, ..., mipN]get(6) 取的是 source 之后第 6 个 mip(mip5),而非"mid mip"(应为 mipLayerCount/2 附近)。且 mipLayerCount = min(floor(log2(max)), 12),当窗口 ≤4096 时 mipLayerCount 可能 <6 → textures.size() < 7midTex = source 分支,此时 SPD 的 mid-mip 绑定变成了输入纹理本身 → 结果错误(但当前 Hi-Z 未接入,无实运行时影响)。建议核对 FidelityFX SPD 的 mid-mip 语义。
  • ComputeSupport.INSTANCE = ALRComputeCapabilities.isComputeSupported() ? new ComputeSupport() : null — 若 isComputeSupported()MinecraftMixin.onCreateInstanceALRComputeCapabilities.init())之前被访问会返回 false(静态初始化顺序敏感)。且调用方需 null 检查(diff 中未全部覆盖)。
  • ALRComputePass.pushDebugGroup 签名破坏性变更StringSupplier<String> 是 internal API 变更,PR 已更新 module.test 调用点,但外部 addon 若直接调用会编译失败。PR 描述已声明此为 breaking change,属预期,仅提示注意同步下游。

💡 建议

  • GpuQueryOcclusionCuller.shouldDrawkey == null 应显式 return true(防御上一帧未注册的特征),而非依赖 getOrDefault(..., 1) 的隐式行为。
  • FrameState.fetchResults()value <= 0 计为 culled,但 GL_ANY_SAMPLES_PASSED 返回 0 表示"无样本通过"(完全遮挡),>0 表示"有样本通过"——逻辑正确;但 shouldDrawresults.getOrDefault(key, 1) > 0 对"从未查询过的 key"默认绘制是安全的,符合预期。
  • OcclusionSubmitNodeStorage.order()computeIfAbsentoriginal.order(order) 每次调用都会新建 OcclusionSubmitNodeCollectionoriginalorder() 可能非幂等,若返回同一实例则无碍)——确认 SubmitNodeCollector.order() 语义,避免每帧创建新包装对象。
  • ALROptimizations.create()MinecraftMixin.onCreateInstanceRenderSystem.getDevice() 可能为 null(device 尚未创建)——确认初始化时机,若 device 未就绪会 NPE。
  • 建议为 OcclusionSubmitNodeCollection.submitFeatureToCuller 的 11 种 feature 列表补充单元测试(至少覆盖 wasUsed 重置与 feature 集合增量)。

🟢 看起来不错

  • 帧状态机(FrameState)设计清晰:查询提交、结果读回、camera-inside/frustum 预判分离,注释完整。
  • OcclusionKeyidentityHashCode + == 保证跨帧稳定,设计正确。
  • GlDeviceMixin 修复了 compute shader 编译失败的资源泄漏(glDeleteShader/glDeleteProgram)和 glGetProgramInfoLog 用错句柄的 bug。
  • ALRComputePass.dispatchWorkgroups(Indirect) 增加 try/finally 保护 debug group 弹出,比之前健壮。
  • ApiStatus.Internal 从 mixin 类移除、加到 extension/record/内部类,API 边界语义更准确。
  • 引入 --renderDebugLabels 等 JVM 参数到 module.gradle 便于调试。

结论: REQUEST_CHANGES — 基础设施质量良好、文档详尽,但 GPU_QUERY 剔除存在"上一帧被剔除 → 永久绘制"的逻辑死锁(核心功能不可用),以及 wasUsed 未重置、null 数组写入、缺 try/finally 等资源安全问题。Hi-Z 部分符合 PR 描述(未接入),但其 SPD mid-mip 绑定和 MipLayer 死代码应在后续接入前清理。建议修复 🔴 关键项后合并。

🧪 测试建议

被测目标 推荐测试场景 优先级
GpuQueryOcclusionCuller.shouldDraw 特征上一帧被剔除后本帧是否仍被绘制(回归核心死锁) 🔴
OcclusionSubmitNodeCollection wasUsed 重置、feature 集合跨帧增量 🔴
FrameState.runQueries 中途异常时 query/instance 是否泄漏、debug group 栈平衡 🟡
GlSamplesQuery.getValue 查询未完成时返回语义、closed 后调用抛异常 🟡
SinglePassDownsampler.spdDispatch 不同窗口尺寸下 midTex 选择与 mip 数量边界 🟡
GpuReusableResourcePool 池满时 fail(true) 扩展、releaseAll/destroyAll 生命周期 🟢

由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown

💾 Self-improvement review: Patched SKILL.md in skill 'rendering-gpu-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

echo "=== 全部 commits(含日期) ==="; curl -sL "https://api.github.com/repos/Anvil-Dev/AnvilLib/pulls/95/commits?per_page=100" | python3 -c "
import json,sys
data=json.load(sys.stdin)
for c in data:
    pri...

Reason: Security scan — [HIGH] Pipe to interpreter: curl | python3: Command pipes output from 'curl' directly to interpreter 'python3'. Downloaded content will be executed without inspection.
Safer: tirith run https://api.github.com/repos/Anvil-Dev/AnvilLib/pulls/95/commits?per_page=100 — or: vet https://api.github.com/repos/Anvil-Dev/AnvilLib/pulls/95/commits?per_page=100 (https://getvet.sh); script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

echo "=== issue comments ==="; curl -sL "https://api.github.com/repos/Anvil-Dev/AnvilLib/issues/95/comments" | python3 -c "
import json,sys
data=json.load(sys.stdin)
for c in data:
    print('---', c[...

Reason: Security scan — [HIGH] Pipe to interpreter: curl | python3: Command pipes output from 'curl' directly to interpreter 'python3'. Downloaded content will be executed without inspection.
Safer: tirith run https://api.github.com/repos/Anvil-Dev/AnvilLib/issues/95/comments — or: vet https://api.github.com/repos/Anvil-Dev/AnvilLib/issues/95/comments (https://getvet.sh); [HIGH] Pipe to interpreter: curl | python3: Command pipes output from 'curl' directly to interpreter 'python3'. Downloaded content will be executed without inspection.
Safer: tirith run https://api.github.com/repos/Anvil-Dev/AnvilLib/pulls/95/comments — or: vet https://api.github.com/repos/Anvil-Dev/AnvilLib/pulls/95/comments (https://getvet.sh); script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering 🔴 BC detected 31
space-select ✅ Compatible 0
font ✅ Compatible 0
util ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
wheel ✅ Compatible 0
main ⚪ Skipped

🔴 rendering — 31 breaking change(s)

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:66

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bind(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout<T>,T)
METHOD_RETURN_TYPE_ERASURE_CHANGED
✗ binary-breaking
✓ source-compatible

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:66

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bind(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout<T>,T)
METHOD_RETURN_TYPE_CHANGED_INCOMPATIBLE
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:59

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bindAll(java.util.List<? extends java.lang.Object>)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:24

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.popDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:20

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.pushDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePassBackend.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePassBackend
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePassBackend.pushDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePipeline.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePipeline
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePipeline
TYPE_KIND_CHANGED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/AtomicCounterBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/AtomicCounterBinding.java:17

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding.apply(int,com.mojang.blaze3d.buffers.GpuBufferSlice,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ImageBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ImageBinding.java:26

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding.apply(int,com.mojang.blaze3d.textures.GpuTexture,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ShaderStorageBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ShaderStorageBinding.java:18

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding.apply(int,com.mojang.blaze3d.buffers.GpuBufferSlice,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/TextureBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/TextureBinding.java:18

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding.apply(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding$SamplerAndTexture,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/UniformBlockBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/UniformBlockBinding.java:17

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding.apply(int,com.mojang.blaze3d.buffers.GpuBufferSlice,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ShaderResourceType.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ShaderResourceType
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ShaderResourceType.IMAGE
FIELD_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ShaderResourceType.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ShaderResourceType
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ShaderResourceType.TEXTURE
FIELD_REMOVED
✗ binary-breaking
✗ source-breaking

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

grep -o '"sha": "[a-f0-9]\{40\}"' /tmp/pr95-commits.json | wc -l; echo "=== 最新 commit 信息 ==="; python3 -c "
import json
data=json.load(open('/tmp/pr95-commits.json'))
print('total commits:', len(data)...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

我已掌握全部情况。这是 PR #95 的第 8 轮审查(当前 diff 135 文件/7172 行,与七复规模一致,但新增了 9/1 的两个 commit「implement hiz depth convert」「make it compile」——Hi-Z 后端从骨架变为已接线)。现在输出审查意见。


代码审查摘要 — AnvilLib PR #95(synchronize · 第 8 轮)

操作: synchronize
范围: 135 文件(120 Java,~70 新增)/ 7172 行 diff
分支: feat/occusion_culling → dev/26.1
说明: 本轮新增 feat(rendering): implement hiz depth convert + feat(rendering): make it compile(09-01),Hi-Z 后端已从空壳变为已接线processFeatures 实际调用 downsampler)。已对照前 7 轮审查矩阵逐项复核,并新增对 Hi-Z 新代码的检查。

✅ 跨轮次修复确认(本轮核实已修)

问题 状态
ALRComputeCapabilities.init() 晚于 ALROptimizations.create()(§21) ✅ 已修:MinecraftMixin.onCreateInstance 中 init 在 create 之前
FFX_SPD 漏 AtomicCounterBinding(§15) ✅ 已修:.withShaderStorage("rw_internal_global_atomic") 已加入(非 bindless 与 bindless 两条链都有)
checkElementIndex 传反(§16/§18) ✅ 已修:改用 checkArgument(this.size >= resource.size())
ImageArrayBinding 未覆写 applyOrdered(§18) ✅ 已修:返回实际消耗槽数
GlSamplesQuery.glEndQuery(id) 传参错误(§1) ✅ 已修:glEndQuery(GL_ANY_SAMPLES_PASSED)getValue 改用 MemoryAccess.getInt 读 4 字节(§2 宽度匹配)
OCCLUSION_QUERY withCull(true)(§12) ✅ 已修:withCull(false)
ConvertDepthParamsUbo 宽高互换(§3) ✅ 已修:getSrcWidth→uSrcWidth 顺序与 GLSL 一致
ALRComputePass.dispatch 缺 try/finally(§27) ✅ 已修:dispatch/dispatchIndirect 均有 finally pop
Hi-Z stub shouldDraw 返回 false(§12) ✅ 已修:恒 true(保守)
camera-inside null 槽(§18) ✅ 已修:收集期 continue 跳过,transforms 数组无 null(与 §23 记录一致)

🔴 关键(仍需修复)

  • SinglePassDownsampler.spdDispatch() 是空方法,而 culler 调用的是它HierarchicalZOcclusionCuller.processFeaturesdownsampler.spdDispatch(commandEncoder, texture)spdDispatch 方法体为空)。实现体在 spdDispathBindless 里,但全库无调用者。结果:Hi-Z 被选中时每帧只做 depth convert,SPD 降采样从未执行 → Hi-Z 金字塔从未生成 → 该后端实际是空转(shouldDraw 恒 true 掩盖了这一点)。这是本轮最关键的接线缺口:要么让 culler 调 spdDispathBindless,要么把 spdDispatch 实现/删除。

  • FrameState.runQueries 索引越界仍存(§22):getBuffer(6 * 6) + drawIndexed(0, 0, 6 * 6, 1)。管线是 VertexFormat.Mode.QUADS,mesh 6 面 × 4 顶点 = 24 顶点(DEFAULT_VERTEX_BUFFER_SIZE = 3*4*4*6 = 288B,24 顶点 × 12B)。36 是 TRIANGLES 计数 → 索引 24..35 引用不存在的顶点,GPU 端越界读,查询结果不确定。应 getBuffer(6 * 4) + drawIndexed(0, 0, 6 * 4, 1)

  • TestOcclusionBlock.codec() 仍返回 simpleCodec(TestBloomBlock::new)(§25):应为 TestOcclusionBlock::new。反序列化/数据生成路径会创建错误的方块实例(构造器签名相同,编译期合法,运行期静默错实例)。

  • TestOcclusionTile 仍用 new AABB(worldPosition) 零尺寸退化盒(§25):min==max 单点,prepareTransformscale(max-min) 全 0 → 查询盒渲染退化;contains(camera.pos) 依赖单点边界语义。建议 new AABB(worldPosition).inflate(0.5)(或 AABB.ofSize)到整块体积。

  • IntSizeUbo 宽高互换仍存(§3):DEFINITION 顺序 getHeight → getWidth,GLSL 声明 int uWidth; int uHeight; → 写入是 height→uWidth、width→uHeight。同 PR 的 ConvertDepthParamsUbo 已修,此类漏改。当前无调用者(latent),接入即错。

  • QueryInstance.prepareMesh x+/x- 面反绕仍存(§14):x+ 面叉积 (-1,0,0)、x- 面叉积 (1,0,0),均与 outward normal 反向。虽 OCCLUSION_QUERYcull(false)(背面不剔除,样本仍计入),反绕不会导致 0 samples,但绕序错误本身就是缺陷,接入深度测试严格场景时会出错。建议修正顶点顺序。

⚠️ 警告

  • MemoryAccess 仍用 sun.misc.Unsafe 反射(§2/§16):Unsafe.class.getDeclaredField("theUnsafe") + setAccessible(true),Java 21 模块系统下依赖 jdk.unsupported 开放,launcher --add-opens 不保证包含。本 PR 只用它做 4 字节读写与 memset——MemoryUtil.memGetInt/memSet 足够,建议替换。

  • GL46.* 入口点硬依赖 4.6 context(§27):GlSamplesQuery 用 GL46 全家桶;depth_convert.csh/ffx_spd_*.csh#version 460 coreGL_ANY_SAMPLES_PASSED 本身是 3.2 core,但 compute shader 和 GL46 调用要求 4.6。支持 compute 但 GL 版本 4.3–4.5 的机器(isComputeSupported=true → 选中 Hi-Z)会在 shader 编译/GL 调用层失败。建议显式声明最低 GL 4.6,或能力探测时把 GL 版本一并纳入。

  • GPU_QUERY.isSupported() 仍恒 true(§8/§18):假能力探测。createInstance 硬转 (ALRGpuDeviceExtension) device,mixin 未生效时 CCE。至少应 device instanceof ALRGpuDeviceExtension

  • FrameState.runQueries 的 debug group 无 try/finally(§27):alrPushDebugGroup → render pass 循环(writeUniforms/begin/drawIndexed 都可能抛)→ alrPopDebugGroup。异常跳过 pop → KHRDebug 栈失衡 + query/instance 池泄漏(releaseInstance 也在 pop 之前)。建议整段包 try/finally。

  • GpuReusableResourcePool.fail() 绕过 onAcquire(§27):fail(false) 直接 get(0) + query.acquire(),与 LoopResetPool.acquireonAcquire 契约不一致;fail(false) 返回已占用对象 → 双 acquire。当前无 acquire(false) 调用者(dormant),但作为基础池 API 是隐患。

  • QueryInstance.CreationContext.vertexBufferprivate static(§27):跨实例共享 + 非线程安全初始化(if (vertexBuffer == null))+ GL context 重建(显示切换/驱动重置)后悬垂。应随 device 生命周期。

  • 提交门控死锁(latent)(§27):shouldDraw 依赖 previousFrameState.results(上一帧查询结果),而收集侧 endOcclusionRecordwasUsed() 门控提交——被剔除的特征本帧不提交 → 不再查询 → 恒画,剔除从未生效。当前 TestOcclusionTESR 无条件 begin/end record(isrs.submit 总会置 wasUsed)所以未爆;接入真实门控即死锁。

  • spdDispathBindless 是死代码:定义了完整实现但无任何调用者;同时 FFX_SPD_DOWNSAMPLE_PASS_BINDLESS pipeline 注册了但从不使用。注册即编译(资源重载时编译该 shader)→ 死代码付编译成本。建议与接线同 PR 落地。

  • 非 bindless FFX_SPD 的数组槽位不一致:builder .withArrayOfImage(..., 13)ImageArrayBinding.size=13),但 shader 声明 image2D rw_input_downsample_src_mips[SPD_MAX_MIP_LEVELS + 1] 且 define SPD_MAX_MIP_LEVELS=4数组只有 5 个槽applyOrdered 逐个 bindImage(bindingPointStart + i) 绑定 mipLayerCount+1(最多 13)个纹理 → 绑定到未声明的数组槽 = UB。bindless 变体(默认 12 → 13 槽)无此问题,但没被调用。

  • MipLayer.getDefinition() 返回 null(§16/§27):违反 BufferObject 基类契约,任何 size()/write() 调用即 NPE(当前被 SSBO 的 IllegalStateException 先抛掩盖);mipLayers[] 只赋值不读取(dead code)。接入前必须清理。

  • fetchResults 读未启动的 query(§20):camera-inside 的 key 在 runQueriescontinue 跳过(query 从未 begin/end),但 fetchResults 仍对全部 keySamplesMapgetValue() → 读未使用 query,结果实现定义(多数驱动 0)。被 shouldDrawcameraInside.contains 优先掩蔽不会误剔,但依赖 UB;建议显式跳过或记 1。

💡 建议

  • frustumCulled 的 key 仍被查询runQueries 中 frustum 不可见只 add(key)continue,查询仍执行(shouldDraw 侧返回 false 生效,但查询浪费)。frustum 剔除应在收集期直接跳过。
  • 每帧新建 OcclusionSubmitNodeStorage/Collection(§23):TestOcclusionTESR.submit 每次 wrapSubmitNodeStorage() new storage + order() new collection → 每帧每 TESR 每 order 对象压力;beginOcclusionRecord 里的 clear()/endFrame() 对刚新建的 collection 无意义。建议缓存或按帧复用。
  • alrSetWasUsed(true) 篡改 original collection(§23):影响 vanilla wasUsed 清理/复用语义,需实机验证。
  • 提交/消费侧 feature 实例一致性(§23):shouldDraw(feature)currentFrameState.getKey(feature),消费侧 mixin 过滤原始 collection 的 feature 列表——两侧拿到同一实例 key 才命中;建议加断言验证。
  • DynamicUniformStorage 512 容量边界(§20):单帧 occlusion key 数 > 512 时 writeUniforms 失败形态未定义,与渲染对象量级对照。

🟢 看起来不错

  • 后端选择回退链清晰:Hi-Z(compute 可用)→ GPU_QUERY → NoOp,stub 优先级问题已消除(Hi-Z 不再是空壳)。
  • ALRComputePassSupplier<String> 惰性化 + dispatch try/finally 是干净的改进。
  • ComputeSupport 改用显式 bindShaderStorage/bindUniformBlock/bindAtomicCounter 绕开 bindAll 的回归,是正确的规避。
  • OcclusionCuller 接口 javadoc(key 跨帧同一实例、相机在盒内恒画等语义)写得清楚,双帧状态机"未知恒画 + 退出多画一帧"保守正确(§20 正向确认)。
  • F3 debug 入口(OcclusionCullingDebugEntry)对调试验收很有用。

📋 声称验证表

声称 状态 对应文件
OcclusionCuller/Key/Method 抽象 optimization/occlusion/*
occlusion-query 管线 + shader ALRPipelines.OCCLUSION_QUERY, occlusion_query.{vsh,fsh}
GPU device 接口 + GlSamplesQuery ✅(§1/§2 已修) ALRGpuDeviceExtension, GlDeviceMixin, GlSamplesQuery
资源池 / ring-buffer 基础 ✅(fail() 契约 ⚠️ GpuReusableResourcePool, QueryInstancePool, GpuSampleQueryPool
帧状态管理(本帧提交、下帧读回) ✅(fetchResults UB ⚠️ FrameState, GpuQueryOcclusionCuller
debug-group 惰性 Supplier<String> ALRComputePass
ApiStatus.Internal 注解调整 mixin 移除 / 内部类添加
[x] Samples Query 后端 ✅(drawIndexed 越界 🔴) GpuQueryOcclusionCuller
[x] 集成 vanilla SubmitNodeStorage ✅ 已接线 TestOcclusionTESR + 12+ feature mixins + OcclusionSubmitNodeStorage/Collection
[ ] Hi-Z 后端 ⚠️ 半接线 HierarchicalZOcclusionCuller + SinglePassDownsampler:depth convert 已实现,SPD dispatch 是空方法,实际降采样未执行

结论: REQUEST_CHANGES

第 8 轮进展显著:8 个历史关键项确认修复(GL query 传参/宽度、UBO 宽高、applyOrdered、checkElementIndex、init 顺序、FFX_SPD atomic、try/finally、cull(false))。但仍有 6 个 🔴 确定性缺陷未修(drawIndexed 6*6 越界、codec 错类、零尺寸 AABB、IntSizeUbo 互换、x+/x- 反绕、spdDispatch 空方法使 Hi-Z 空转)——其中 spdDispatch 空方法 + bindless 死代码是本轮新增 commit 引入的核心接线缺口,建议优先处理:把 culler 接到 spdDispathBindless(或实现 spdDispatch),否则 Hi-Z 后端"已实现"名不副实。

(gh 未认证——token 无效,跳过标题修改;标题「Refactor API status handling and add occlusion culling」仍准确,无需改动。)


由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown

💾 Self-improvement review: Patched SKILL.md in skill 'rendering-gpu-pr-review' (1 replacement). · Patched references/pr95-occlusion-culling-review-matrix.md in skill 'rendering-gpu-pr-review' (1 replacement). · Patched SKILL.md in skill 'github-webhook-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ⚪ Skipped
collision ⚪ Skipped
config ⚪ Skipped
integration ⚪ Skipped
moveable-entity-block ⚪ Skipped
network ⚪ Skipped
rendering ⚪ Skipped
space-select ⚪ Skipped
font ⚪ Skipped
util ⚪ Skipped
explosion ⚪ Skipped
rpc ⚪ Skipped
multiblock ⚪ Skipped
recipe ⚪ Skipped
registrum ⚪ Skipped
sync ⚪ Skipped
wheel ⚪ Skipped
main ⚪ Skipped

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

curl -sL "https://api.github.com/repos/Anvil-Dev/AnvilLib/pulls/95/commits?per_page=100" -o /tmp/pr95-commits.json; python3 -c "
import json
data = json.load(open('/tmp/pr95-commits.json'))
print(f'to...

Reason: script execution via -e/-c flag

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

🌿 Roseau API Breaking Change Report

Module Status Breaking Changes
codec ✅ Compatible 0
collision ✅ Compatible 0
config ✅ Compatible 0
integration ✅ Compatible 0
moveable-entity-block ✅ Compatible 0
network ✅ Compatible 0
rendering 🔴 BC detected 36
space-select ✅ Compatible 0
font ✅ Compatible 0
util ✅ Compatible 0
explosion ✅ Compatible 0
rpc ✅ Compatible 0
multiblock ✅ Compatible 0
recipe ✅ Compatible 0
registrum ✅ Compatible 0
sync ✅ Compatible 0
wheel ✅ Compatible 0
main ⚪ Skipped

🔴 rendering — 36 breaking change(s)

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/ALRGpuDeviceExtension.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.ALRGpuDeviceExtension.alrCompileComputeShader(dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:66

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bind(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout<T>,T)
METHOD_RETURN_TYPE_ERASURE_CHANGED
✗ binary-breaking
✓ source-compatible

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:66

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bind(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout<T>,T)
METHOD_RETURN_TYPE_CHANGED_INCOMPATIBLE
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:59

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.bindAll(java.util.List<? extends java.lang.Object>)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:24

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.popDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePass.java:20

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass.pushDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePassBackend.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePassBackend
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePassBackend.pushDebugGroup(java.lang.String)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/ALRComputePipeline.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePipeline
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePipeline
TYPE_KIND_CHANGED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/AtomicCounterBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/AtomicCounterBinding.java:17

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding.apply(int,com.mojang.blaze3d.buffers.GpuBufferSlice,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.AtomicCounterBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ImageBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ImageBinding.java:26

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ImageBinding.apply(int,com.mojang.blaze3d.textures.GpuTexture,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ShaderStorageBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ShaderStorageBinding.java:18

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ShaderStorageBinding.apply(int,com.mojang.blaze3d.buffers.GpuBufferSlice,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/TextureBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/TextureBinding.java:18

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding.apply(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.TextureBinding$SamplerAndTexture,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/ComputeBindingLayout.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.ComputeBindingLayout.apply(int,T,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/UniformBlockBinding.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding
TYPE_NEW_ABSTRACT_METHOD
✓ binary-compatible
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/pipeline/bindings/UniformBlockBinding.java:17

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.bindings.UniformBlockBinding.apply(int,com.mojang.blaze3d.buffers.GpuBufferSlice,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.pipeline.ALRComputePass)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ALRComputeProgramInstance.java:6

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstance.<init>(int,dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ALRComputeProgramInstanceKey)
EXECUTABLE_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ShaderResourceType.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ShaderResourceType
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ShaderResourceType.IMAGE
FIELD_REMOVED
✗ binary-breaking
✗ source-breaking

dev/anvilcraft/lib/v2/rendering/extension/blaze3d/compute/shader/ShaderResourceType.java

dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ShaderResourceType
dev.anvilcraft.lib.v2.rendering.extension.blaze3d.compute.shader.ShaderResourceType.TEXTURE
FIELD_REMOVED
✗ binary-breaking
✗ source-breaking

Full CSVs: see the Artifacts section of this workflow run.

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

echo "=== 未修清单快速复核 ==="; echo "1. IntSizeUbo 宽高互换: 仍是 getHeight 先 → 🔴 未修"; echo "2. drawIndexed 6*6: 仍是 getBuffer(6*6) + drawIndexed(6*6) → 🔴 未修"; echo "3. codec 错类: simpleCodec(TestBloomBlock::new) →...

Reason: Security scan — [MEDIUM] Variation selector characters detected: Content contains Unicode variation selectors (VS1-256). These are commonly used in emoji sequences but may indicate steganographic encoding or obfuscation

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

cd /tmp && echo "=== javac 不可用;按 JLS 记忆:同一类型重复 import = 编译错误(duplicate import),保守标注 ===" && echo "=== 但 LazySyncBytecodeInjector 的 import 上下文行是 context(空格)而非新增 → 同文件重复确实存在 ===" && echo "=== 报告为 ⚠️ 编译期...

Reason: Security scan — [MEDIUM] Variation selector characters detected: Content contains Unicode variation selectors (VS1-256). These are commonly used in emoji sequences but may indicate steganographic encoding or obfuscation

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

⚠️ Iteration budget exhausted (90/90) — asking model to summarise

@Gugle2308

Copy link
Copy Markdown

代码审查摘要 — PR #95(九复 synchronize)

操作: synchronize
范围: 143 个文件(128 Java, 78 新增, 0 删除)/ 7616 行 diff
轮次定位: 八复(135 文件/7172 行)之后 commits API 显示 09-01 新增「make it compile / bindless texture」等提交——本轮为实质新变更,diff 规模与八复不同(143/7616 vs 135/7172)。

本轮相较八复的关键变化:新增 bindless texture 基础设施(BindlessTexturingSupport/GlBindlessTexturingSupport/TextureHandle 等 6 文件);spdDispathBindless 改为用非 bindless 管线 FFX_SPD_DOWNSAMPLE_PASS + bindBindlessImageArrayALRComputeCapabilities.initALRComputeShaderManager.apply 移到 MinecraftMixin.onCreateInstance 且位于 ALROptimizations.create() 之前;提交链已通过 TestOcclusionTESR 完整接线。


🔴 关键(需修复后合并)

  1. SinglePassDownsampler.spdDispatch 仍是空方法 + spdDispathBindless 无调用者(假接线,跨轮次未修)
    HierarchicalZOcclusionCuller.processFeaturesspdDispatch(方法体为空),完整实现在 spdDispathBindless(全库 0 调用者)→ 每帧只做 depth convert,SPD 降采样从未执行,Hi-Z 金字塔从未生成(shouldDraw 恒 true 掩盖)。SPD 是 Hi-Z 的核心,空转 = 整个 Hi-Z 后端是死壳。

  2. spdDispathBindless 调用的 bindBindlessImageArray 后端是 TODO 空实现
    GlComputePassBackend.bindBindlessImageArray//TODO: implement with ARB_bindless_texture,无任何 GL 调用 → 即便补上调用方,纹理数组也从不绑定 → 静默无效。同时 FFX_SPD_DOWNSAMPLE_PASS_BINDLESS pipeline 从未 registerPipeline(死注册,但 shader 也从不编译)。

  3. bindless shader 缺 #extension GL_ARB_bindless_texture 声明
    ffx_spd_downsample_pass_bindless.csh 使用 layout(bindless_image, r32f)(ARB_bindless_texture 扩展语法),文件内只有 GL_KHR_shader_subgroup_quad 声明,无 bindless 扩展声明 → 一旦注册/编译必定失败。当前因 pipeline 未注册侥幸不炸,接入时必爆。

  4. FrameState.runQueries 索引越界仍存(已接线 → 每帧实际执行,升格 active)
    getBuffer(6 * 6) + drawIndexed(0, 0, 6 * 6, 1)DEFAULT_VERTEX_BUFFER_SIZE = 3*4*4*6 = 288B = 24 顶点(6 面 × 4 QUADS),36 是 TRIANGLES 计数。索引 24..35 引用不存在的顶点 → GPU 端越界读,遮挡结果不确定。正确:getBuffer(6*4) + drawIndexed(0,0,6*4,1)

  5. IntSizeUbo 宽高互换仍未修(跨轮次)
    DEFINITIONforGetter(getHeight)forGetter(getWidth),GLSL javadoc 声明 uWidth; uHeight; → 字段反序写入。同 PR 的 ConvertDepthParamsUbo 已修,此为逐文件漏改型。

  6. test 方块两个数据 bug 仍未修(跨轮次)

    • TestOcclusionBlock.codec() 返回 simpleCodec(TestBloomBlock::new) —— 应为 TestOcclusionBlock::new,反序列化会创建错误方块实例(同文件 newBlockEntity 引用正确,单点漏改)。
    • TestOcclusionTile 构造 new AABB(worldPosition) 零尺寸退化盒:prepareTransformscale(max-min) 三轴全 0 → 查询几何退化,contains(camera.pos) 依赖单点盒边界语义。
  7. 提交门控死锁(latent,§27 未修)
    endOcclusionRecordif (wasUsed()) 门控 submitFeatureToCuller:被剔除的特征本帧不再提交 → 不再查询 → 结果恒 0 → 永久剔除。当前 TestOcclusionTESR 无条件提交未爆;接入真实门控即爆。


⚠️ 警告

  1. GL_QUERY_RESULT 每帧同步阻塞读fetchResultsgetValue):N 个 key = N 次管线停顿,对遮挡剔除是自毁。建议 RESULT_AVAILABLE / 延迟读 / PBO 回读。
  2. 池容量不足 + fail() 绕过 onAcquire(§27 未修)QueryInstancePool size=2、GpuSampleQueryPool size=16;单帧 key 超容量时 fail(false) 直接 get(0).acquire() 重复复用(未过 onAcquire 契约)→ 同一实例多 key 共享数据覆盖。当前仅 1 个测试 key 不爆。
  3. runQueries push/pop debug group 无 try/finally:中间 writeUniforms/acquireInstance 抛异常 → 跳过 pop + releaseInstance → debug 栈失衡 + 池泄漏。
  4. GPU_QUERY.isSupported() 恒 true(§18 未修):假能力探测;默认回退链 HIERARCHICAL_Z → GPU_QUERY,而 Hi-Z 当前是假接线(见 🌟 neoforge支持 #1)——支持 compute 的机器会优先选中不能工作的 Hi-Z 而非可用的 GPU_QUERY。
  5. ALRComputePass.setupState 对 INVALID program 改抛 IllegalStateException:原为安静返回。shader 编译失败(如 subgroup_quad 不支持、bindless 扩展缺失)时从刷日志升级为渲染崩溃——行为变更需确认意图。
  6. CreationContext.vertexBufferprivate static(§27 未修):跨实例共享 + 非线程安全懒初始化 + GL context 重建后悬垂。
  7. 每帧新建 storage/collection 的对象压力(§23 未修)TestOcclusionTESR.submit 每次 wrapSubmitNodeStorage() new storage,order() per-order computeIfAbsent new collection——每帧每 TESR 每 order 新建对象,beginOcclusionRecord 里的 clear()+endFrame() 对新建 collection 无意义。
  8. frustumCulled 仍发 queryOCCLUSION_QUERY_USE_FRUSTUM_PRE_PASS 分支只 frustumCulled.add(key)continue,不可见物体仍写入 transforms 并 begin/draw/end query(仅消费侧提前返回 false)→ 查询浪费。
  9. MipLayer 死代码 + 契约违例(§16/§27 未修)mipLayers[] 只赋值不读取;getDefinition() 返回 null 违反 BufferObject 基类契约(usage=SSBO 恰好先触发 IllegalStateException 而非 NPE)。
  10. GL46. 入口点硬依赖*(§27):glBeginQuery 等要求 4.6 context,GL_ANY_SAMPLES_PASSED 实为 3.2 core——需显式声明最低 GL 版本。
  11. bindless 管线扩展语义withArrayOfImage(..., 13) 与 shader 数组 [SPD_MAX_MIP_LEVELS+1=13](默认 define 12)一致 ✅,但 13 个 image 槽 > GL_MAX_IMAGE_UNITS 最低保证 8,注释已意识到但未做能力门槛检查(ALRHICapabilities 只记录 max 值)。

🟢 看起来不错

  • §21 init 顺序已修(跨轮次确认)MinecraftMixin.onCreateInstanceALRComputeCapabilities.init() 位于 ALROptimizations.create() 之前,Hi-Z 能力探测不再读到默认 false。
  • 提交/消费两侧已打通且实例一致TestOcclusionTESR 的 feature 经 wrapped collection 拷入 original 后被 16 个 *FeatureRendererMixin 过滤——两侧同一实例,shouldDraw 的 key 查找可命中。
  • OCCLUSION_QUERY 管线 withCull(false) + FullTransformsUbo DEFINITION 顺序与 vsh 声明(ProjMat/CameraViewMat/ModelViewMat)一致ConvertDepthParamsUbo 顺序与 depth_convert.csh 一致 ✅。
  • 双帧状态机语义保守正确:未知 key getOrDefault(1) 恒画、cameraInside 掩蔽、首帧 previousFrameState==null 短路——无 pop-in,可作正向确认。

📌 结论:REQUEST_CHANGES

基础设施方向清晰,但 Hi-Z 后端仍整体假接线(空 spdDispatch + 空 bindBindlessImageArray + 缺扩展声明的 bindless shader),GPU_QUERY 路径存在每帧实际执行的索引越界(#4)。建议先合并 GPU_QUERY 链路并修复索引计数与阻塞读,Hi-Z/bindless 部分待 spdDispatch 真正实现后再接线。

由 Hermes Agent 审查

@Gugle2308

Copy link
Copy Markdown

💾 Self-improvement review: Patched SKILL.md in skill 'rendering-gpu-pr-review' (1 replacement). · Patched references/pr95-occlusion-culling-review-matrix.md in skill 'rendering-gpu-pr-review' (1 replacement).

@Gugle2308

Copy link
Copy Markdown

⚠️ Dangerous command requires approval:

cd /tmp && echo "=== 最终核对清单 ===" && echo "🔴 spdDispatch 空方法(HiZ processFeatures 调用 → Hi-Z 每帧空转)" && echo "🔴 drawIndexed(0,0,6*6,1) + getBuffer(6*6):QUADS 24 顶点却 36 索引 → 越界(已接线 → active)" && echo "🔴 GL...

Reason: Security scan — [MEDIUM] Variation selector characters detected: Content contains Unicode variation selectors (VS1-256). These are commonly used in emoji sequences but may indicate steganographic encoding or obfuscation

Reply /approve to execute, /approve session to approve this pattern for the session, /approve always to approve permanently, or /deny to cancel.

@Gugle2308

Copy link
Copy Markdown

⚠️ Iteration budget exhausted (90/90) — asking model to summarise

@Gugle2308

Copy link
Copy Markdown

PR #95 复审查意见(synchronize 九复)

审查范围:143 文件 / 7625 行 diff;commits API 显示 09-01 新增 4 个提交(implement hiz depth convert / make it compile / bindless texture / make it compile x2),相对八复新增 bindless 纹理系列文件。gh 未认证(token invalid),标题未修改。

🔴 高优先级(已接线,每帧实际执行)

1. spdDispatch 空方法 → Hi-Z 假接线(八复未修)
HierarchicalZOcclusionCuller.processFeatures 调用 downsampler.spdDispatch(commandEncoder, texture),而 spdDispatch 方法体为空;完整实现在 spdDispathBindless,但全库无调用者。Hi-Z 每帧只做 depth convert,SPD 降采样从未执行,Hi-Z 金字塔从未生成(shouldDraw 恒 true 掩盖)。

2. drawIndexed(0, 0, 6*6, 1) 索引越界(八复未修,接线后已 active)
FrameState.runQueriessequentialBuffer.getBuffer(6 * 6) + drawIndexed(0, 0, 6 * 6, 1)。QueryInstance 网格是 QUADS 6 面 × 4 顶点 = 24 顶点DEFAULT_VERTEX_BUFFER_SIZE = 3*4*4*6 = 288B = 24 顶点 × 12B POSITION),36 是 TRIANGLES 计数 → 索引 24..35 引用不存在的顶点,GPU 端越界读、遮挡结果不确定。应 getBuffer(6*4) + drawIndexed(0, 0, 6*4, 1)

3. GL_QUERY_RESULT 每帧阻塞读(八复未修,接线后 active)
GlSamplesQuery.getValue() 同步 glGetQueryObjectuiv(GL_QUERY_RESULT)beginRenderingFrame → fetchResults 对每个 key 读一次 = N 次管线停顿,直至 GPU 完成全部已提交工作——对以性能为目的的遮挡剔除是自毁。建议 RESULT_AVAILABLE 轮询 / 延迟读 / PBO 异步回读;注意池复用安全性当前依赖阻塞读语义,改非阻塞后要一并处理(§4)。

4. TestOcclusionBlock.codec() 引用错类(六复未修)
simpleCodec(TestBloomBlock::new) 应为 TestOcclusionBlock::new。反序列化创建错误方块实例,存档数据静默损坏(编译期合法,单点静默 bug)。

⚠️ 中优先级

5. 零尺寸 AABB 遮挡键(六复未修)
TestOcclusionTilenew AABB(worldPosition)(单点退化盒),prepareTransforminflate(0.1) 后 scale 仍近零 → 查询几何退化;contains(camera.pos) 边界语义依赖。应 inflate(0.5) 到整块体积。

6. GL 入口点版本硬依赖(七复未修)
GlSamplesQueryGL46.glBeginQuery/glGetQueryObjectuiv 硬依赖 GL 4.6 context,而 occlusion_query shader 是 #version 330GL_ANY_SAMPLES_PASSED 实际是 3.2 core)。需显式声明项目最低 GL 版本,或改用 GL32C 入口点。

7. 提交门控死锁(latent,七复未修)
shouldDraw 用本帧 currentFrameState.getKey(feature) 查上帧 previousFrameState.results,而 endOcclusionRecordwasUsed() 门控提交——被剔除特征本帧不提交 → 不再查询 → 永久绘制。当前 TestOcclusionTESR 无条件 begin/end record 所以未爆;接入真实提交门控即爆。

8. 消费侧每帧全量复制(四复未修)
16 处 feature mixin 每帧调 filterVisibleFeatures → culler 非空且 keyAssociations 有提交时每帧 new ArrayList + addAll 全量复制。建议 culler 增加「本帧有无已提交 key」标记短路。

9. 重复 import
LazySyncBytecodeInjector.javaSyncBytecodeInjector.java 新增 import 后与已有 import org.jetbrains.annotations.ApiStatus 重复(同文件两条同类型 import),至少冗余、可能 javac 报 duplicate import。其余 sync 文件仅 1 处新增无重复。

10. CreationContext.vertexBuffer static 共享(七复未修)
prepared 静态 + if (vertexBuffer == null) 非线程安全初始化;GL context 重建(显示切换/驱动重置)后悬垂。建议实例级或随 device 生命周期。

11. 池 fail() 绕过 onAcquire(七复未修)
GpuReusableResourcePool.fail 直接 query.acquire(),与 LoopResetPool.acquireonAcquire 后再返回)契约不一致;子类 onAcquire 有额外状态时漏初始化。

💡 低优先级 / 死代码

  • HiZ stub 构造器副作用HierarchicalZOcclusionCuller 构造器建 downsampler + depthTexConverter(GPU buffer/纹理),shouldDraw 恒 true——建了全套资源却不消费;onResizecreateCommandEncoder 从不 submit(encoder 泄漏)。骨架实现应延后建资源。
  • GPU_QUERY.isSupported() 恒 true:假能力探测,至少应经 GL.getCapabilities()
  • frustumCulled 仍发 queryrunQueries 中 frustum 不可见只 add(key) 不 continue → 查询浪费(默认该选项关闭)。
  • runQueries 缺 try/finally:push/pop debug group 链中异常会跳过 pop + releaseInstance → debug group 栈失衡 + 池泄漏(ALRComputePass.dispatch 已修,此处仍缺)。
  • IntSizeUbo 宽高互换(死类):DEFINITION 先 getHeightgetWidth,与 GLSL uWidth; uHeight; 声明顺序相反;当前无使用点,接入即爆。
  • MipLayer.getDefinition() 返回 null + mipLayers[] 只赋值不读取:违反基类契约,死代码,接入前必须清理。

✅ 本轮确认已修(跨轮次核对)

init 顺序(ALRComputeCapabilities.init() 已移到 ALROptimizations.create() 之前);FFX_SPD 两条 pipeline 均补 withShaderStorage("rw_internal_global_atomic")checkElementIndex 传反 → checkArgument(this.size >= resource.size()) 语义正确;ImageArrayBinding/BindlessImageArrayBinding 均覆写 applyOrdered(返回实际槽数/0);GlSamplesQuery.glEndQuery 传 target 正确 + 4 字节读;ALRComputePass dispatch 补 try/finally;OCCLUSION_QUERY withCull(false)ConvertDepthParamsUbo 宽高顺序正确;withArrayOfImage(13) 与 shader [SPD_MAX_MIP_LEVELS+1](define=12 → 13 槽)一致,八复槽位问题已消除。

建议:优先修 1-4(假接线 / 索引越界 / 阻塞读 / codec 错类),这三项在 occlusion culling 实际开启时每帧生效或直接破坏存档数据。

@Gugle2308

Copy link
Copy Markdown

💾 Self-improvement review: Patched SKILL.md in skill 'rendering-gpu-pr-review' (1 replacement). · Patched references/pr95-occlusion-culling-review-matrix.md in skill 'rendering-gpu-pr-review' (1 replacement).

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.

2 participants