fix: seed particle state explicitly
This commit is contained in:
@@ -36,4 +36,4 @@
|
||||
|
||||
## 当前结果、阻塞与下一步
|
||||
|
||||
正式 Source/Layer/RenderLayer 已不依赖 source-less `particle-validation` Layer;历史 validation Layer 只保留为既有验证工具。Android 首次实际启动的 RasterArray source RenderItem 空指针已在 source 内补为合法 no-op RenderItem,保持全局 RenderSource 契约。随后实机日志发现 ACTIVE/STATIC 每帧重置:RenderOrchestrator 的按 source 可渲染性扫描会短暂调用 `markLayerRenderable(false)`,它不是持久 disabled;粒子层原先却在该回调中 `resetSequence()`,每帧清空跨 render 的 frame/slot/seed 状态。已删除该错误 reset 边界,真实重建仍限于 field、viewport、context 与 layer removal;新增状态转换及前 3 帧采样日志。最新实机日志确认 simulation 已正常推进但画面静止,根因是正式 layer 的 Composite 仍输出旧 validation stripe/anchors,Trail RGB 只影响 alpha。已以 GLSL source 为唯一输入重新生成 OpenGL header;正式 display mode 现在输出当前 Trail RGB,legacy validation display 保留原 stripe/anchor/world-wrap 分支。Linux 精确编译、shader generator 幂等、format 与 diff 已通过;Android 实机验收仍由用户执行,40-01 保持 `WAITING_MANUAL_VALIDATION`。
|
||||
正式 Source/Layer/RenderLayer 已不依赖 source-less `particle-validation` Layer;历史 validation Layer 只保留为既有验证工具。Android 首次实际启动的 RasterArray source RenderItem 空指针已在 source 内补为合法 no-op RenderItem,保持全局 RenderSource 契约。随后实机日志发现 ACTIVE/STATIC 每帧重置:RenderOrchestrator 的按 source 可渲染性扫描会短暂调用 `markLayerRenderable(false)`,它不是持久 disabled;粒子层原先却在该回调中 `resetSequence()`,每帧清空跨 render 的 frame/slot/seed 状态。已删除该错误 reset 边界,真实重建仍限于 field、viewport、context 与 layer removal;新增状态转换及前 3 帧采样日志。正式 Composite 已改为显示 Trail RGB,legacy validation display 保留原 stripe/anchor/world-wrap 分支。最新实机仍不可见的根因进一步确认是 State seed:MapLibre RenderTarget 新 target 清为 opaque black,原 shader 错把 `previous.a` 当 unseeded 标记,令所有 State texel 从 `(0,0)` 开始。现以 `ParticleGpuResources::stateSeeded` 为权威,使用 `u_mask.w` 显式指示 `SEED_STATE`;ACTIVE 与 STATIC zero-step bootstrap 都从各自 `v_uv` 分散初始化,后续 frame 继续 State read。Linux 精确编译、shader generator 幂等、GLSL/header parity、format 与 diff 已通过;Android 实机验收仍由用户执行,40-01 保持 `WAITING_MANUAL_VALIDATION`。
|
||||
|
||||
37
docs/报告/40_01_Android正式粒子StateSeed修复_Codex完成报告_v1.md
Normal file
37
docs/报告/40_01_Android正式粒子StateSeed修复_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 40-01 Android 正式粒子 State Seed 修复 Codex 完成报告 v1
|
||||
|
||||
## 1. 结论
|
||||
|
||||
已修复正式粒子的 State 初始化契约。40-01 保持 `WAITING_MANUAL_VALIDATION`;未运行 Android Gradle、ADB 或实体机。
|
||||
|
||||
## 2. 根因
|
||||
|
||||
`RenderTarget::render()` 按 MapLibre 既有 contract 将每个 offscreen pass clear 为 `(0,0,0,1)`。State shader 原本把 `previous.a < 0.5` 当作“尚未 seed”,但新的 State A/B target 因此是 opaque black,条件恒为 false。所有 16×16 State texel 都从 `(0,0)` 开始;uniform-east 仅移动 x,四个 deterministic marker 聚在 canonical world 顶缘,当前视口通常不可见。
|
||||
|
||||
没有修改 `src/mbgl/renderer/render_target.cpp`。
|
||||
|
||||
## 3. Seed contract
|
||||
|
||||
- `ParticleGpuResources::stateSeeded` 是唯一生命周期权威。
|
||||
- State pass 使用已有 UBO 的 `u_mask.w`:`1.0 = SEED_STATE`,`0.0 = CONTINUE_STATE`。
|
||||
- shader 在 `SEED_STATE` 时让每个 texel 从自身 `v_uv` 初始化,再采样 field;在 `CONTINUE_STATE` 时从 State read texture 的 `previous.rg` 继续。`field.a` 仍仅表示 Field/NoData validity。
|
||||
- Composite commit 后既有 `commit()` 将 `stateSeeded=true`。新 resource、context recreation 和 fixture/field reset 令其恢复 false;普通 ACTIVE、Camera、STATIC 与 transient `markLayerRenderable(false)` 不会重 seed。
|
||||
- STATIC zero-step bootstrap 使用相同 seed flag,但 `simulationStep=0`,因此建立分散的 deterministic state 而不推进 simulation。
|
||||
|
||||
Trail/Composite 的 State write → Trail write wiring、slot swap、hazard=SAFE、formal display、legacy validation display、fixture、mask、world wrap、antimeridian、rotation resource lifecycle 均未改变。
|
||||
|
||||
## 4. 诊断与静态验证
|
||||
|
||||
- 新增低频 `ParticleRender: stateSeed=INITIALIZE|CONTINUE`,含 generation、State A/B 和 fixture。
|
||||
- shader source:`shaders/particle_gpu_validation.fragment.glsl`;由 `node shaders/generate_shader_code.mjs` 生成 OpenGL header,未手改 generated header。
|
||||
- generator 二次运行幂等:PASS;GLSL source 与 generated fragment parity:PASS。
|
||||
- Linux OpenGL `render_particle_validation_layer.cpp.o`、相关 `test/style/source.test.cpp.o` 精确编译:PASS。
|
||||
- `clang-format --dry-run --Werror` 与 `git diff --check`:PASS。
|
||||
|
||||
## 5. 下一步
|
||||
|
||||
用户下一次只验收 GLOBAL + ACTIVE + `uniform-east`:四个 deterministic marker/trail 应分散在 world 内,且连续向 east 移动。通过前不声明 40-01 PASS。
|
||||
|
||||
## 6. Git 收尾
|
||||
|
||||
最终代码 commit、push 与 `origin/particle/main` HEAD 在交付收尾后回填。
|
||||
@@ -54,8 +54,9 @@ void main() {
|
||||
// 40-01: State A/B samples the uploaded OfflineFixture Particle Field.
|
||||
// RG stores field-normalized position, B stores age, and the Field
|
||||
// texture encodes canonical U-east/V-north at that geographic sample.
|
||||
vec2 position = previous.rg;
|
||||
if (previous.a < 0.5) position = v_uv;
|
||||
// u_mask.w is the explicit State lifecycle flag: new RenderTargets are
|
||||
// cleared opaque by MapLibre, so sampled alpha cannot identify a seed.
|
||||
vec2 position = u_mask.w > 0.5 ? v_uv : previous.rg;
|
||||
vec4 field = texture(u_field_texture, position);
|
||||
vec2 velocity = field.rg * 2.0 - 1.0;
|
||||
if (field.a < 0.5) {
|
||||
|
||||
@@ -20,8 +20,9 @@ void main() {
|
||||
// 40-01: State A/B samples the uploaded OfflineFixture Particle Field.
|
||||
// RG stores field-normalized position, B stores age, and the Field
|
||||
// texture encodes canonical U-east/V-north at that geographic sample.
|
||||
vec2 position = previous.rg;
|
||||
if (previous.a < 0.5) position = v_uv;
|
||||
// u_mask.w is the explicit State lifecycle flag: new RenderTargets are
|
||||
// cleared opaque by MapLibre, so sampled alpha cannot identify a seed.
|
||||
vec2 position = u_mask.w > 0.5 ? v_uv : previous.rg;
|
||||
vec4 field = texture(u_field_texture, position);
|
||||
vec2 velocity = field.rg * 2.0 - 1.0;
|
||||
if (field.a < 0.5) {
|
||||
|
||||
@@ -270,13 +270,16 @@ public:
|
||||
const float frame = simulationStep;
|
||||
const float markerX = 0.5f + 0.32f * std::sin(frame * 0.071f);
|
||||
const float markerY = 0.5f + 0.32f * std::cos(frame * 0.053f);
|
||||
// RenderTarget clear is opaque black. State initialization must therefore
|
||||
// be an explicit lifecycle signal, never inferred from sampled alpha.
|
||||
const bool seedState = pass == ValidationPass::State && !resources->stateSeeded;
|
||||
const shaders::ParticleGpuValidationUBO ubo = {
|
||||
.pass = {static_cast<float>(pass), frame, maskMode == 0 ? 0.0f : 1.0f, static_cast<float>(maskMode)},
|
||||
.marker = {markerX, markerY, 0.035f, 0.965f},
|
||||
.mask = {static_cast<float>(resources->maskGeneration),
|
||||
static_cast<float>(worldWrap),
|
||||
resources->formalDisplay ? 1.0f : 0.0f,
|
||||
0.0f},
|
||||
seedState ? 1.0f : 0.0f},
|
||||
.matrix = util::cast<float>(parameters.matrixForTile({worldWrap, {0, 0, 0}})),
|
||||
};
|
||||
drawable.mutableUniformBuffers().createOrUpdate(shaders::idParticleGpuValidationUBO, &ubo, parameters.context);
|
||||
@@ -381,6 +384,13 @@ public:
|
||||
" frame=" + std::to_string(resources->committedFrame + 1) + " read=" + slotName(readSlot) +
|
||||
" write=" + slotName(writeSlot) + " distinct=true");
|
||||
}
|
||||
if (pass == ValidationPass::State &&
|
||||
(!resources->stateSeeded || resources->committedFrame < 3 || (resources->committedFrame + 1) % 60 == 0)) {
|
||||
Log::Info(Event::Render,
|
||||
"ParticleRender: stateSeed=" + std::string(seedState ? "INITIALIZE" : "CONTINUE") +
|
||||
" generation=" + std::to_string(resources->generation) + " state=" + slotName(readSlot) +
|
||||
"->" + slotName(writeSlot) + " fixture=" + resources->field.fixtureID);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -197,3 +197,11 @@
|
||||
- `ParticleRender` 新增一次性 displayMode 日志;既有低频 schedule 日志补充 `trailStateSource`、`compositeTrailSource` 和 displayMode,可证明 State write → Trail write → Composite Trail write 的数据关系,而不每帧刷屏。
|
||||
- `shaders/particle_gpu_validation.fragment.glsl` 为唯一 shader source,使用 `node shaders/generate_shader_code.mjs` 正常生成 OpenGL header;二次生成哈希一致。生成器同时规范化 `src/mbgl/shaders/shader_source.cpp`,该生成物一并保留以保证生成后工作树稳定。
|
||||
- Linux OpenGL 受影响 RenderLayer object、相关 `source.test.cpp.o` 均精确编译;C++ format、diff check 通过。按任务边界未运行 Android Gradle、ADB 或真机;40-01 继续为 `WAITING_MANUAL_VALIDATION`,下一轮只验收 GLOBAL + ACTIVE + uniform-east 的可见 eastward 运动。
|
||||
|
||||
## 2026-09-01 — 40-01 Formal RasterParticle 显式 State Seed 修复
|
||||
|
||||
- 实机日志确认 formal display、State/Trail/Composite slot wiring、simulation frame 与 hazard 均正确,但正式粒子仍不可见。`RenderTarget::render()` 的既有全局 clear 是 opaque black `(0,0,0,1)`;未修改该 MapLibre contract。
|
||||
- 原 State shader 用 `previous.a < 0.5` 判断是否第一次 seed,但新 State target alpha 为 1,导致全部 16×16 texel 从 `(0,0)` 而非各自 `v_uv` 开始。uniform-east 只推动 x,marker 聚集在 canonical world 顶缘、常在当前视口外。
|
||||
- `ParticleGpuResources::stateSeeded` 现在是唯一权威。State pass 把 `!stateSeeded` 通过现有 UBO 的 `u_mask.w` 传入;shader 仅以该 flag 选择 `v_uv` 或 `previous.rg`,不再以 texture alpha 推断生命周期。Field `alpha` 仍仅表示 NoData validity。
|
||||
- ACTIVE 第一次 State pass 与 STATIC zero-step bootstrap 都对每个 texel 进行 `v_uv` seed;Composite commit 后 `stateSeeded=true`,之后继续 read texture。新 resources、context recreation 和 fixture/field `resetSequence()` 保持重新 seed;普通 ACTIVE、Camera、STATIC 与 transient source renderability 不会 seed。
|
||||
- 新增低频 `ParticleRender: stateSeed=INITIALIZE|CONTINUE` 日志,含 generation、State A/B 与 fixture。shader source/header generator parity、生成器幂等、Linux Core 与 Source test object 编译、format 和 diff 均通过;未运行 Android Gradle/ADB/真机,状态仍为 `WAITING_MANUAL_VALIDATION`。
|
||||
|
||||
Reference in New Issue
Block a user