diff --git a/docs/50_Step记录/STEP-040_Android离线Fixture正式粒子最小闭环_v1.md b/docs/50_Step记录/STEP-040_Android离线Fixture正式粒子最小闭环_v1.md index 3aa65e4c5..b36800e6a 100644 --- a/docs/50_Step记录/STEP-040_Android离线Fixture正式粒子最小闭环_v1.md +++ b/docs/50_Step记录/STEP-040_Android离线Fixture正式粒子最小闭环_v1.md @@ -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 帧采样日志。Linux 精确编译、format 与 diff 已通过;Android OpenGL APK 已由本轮构建,最终实机验收仍由用户执行。 +正式 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`。 diff --git a/docs/报告/40_01_Android正式粒子Trail显示修复_Codex完成报告_v1.md b/docs/报告/40_01_Android正式粒子Trail显示修复_Codex完成报告_v1.md new file mode 100644 index 000000000..ec810e690 --- /dev/null +++ b/docs/报告/40_01_Android正式粒子Trail显示修复_Codex完成报告_v1.md @@ -0,0 +1,35 @@ +# 40-01 Android 正式粒子 Trail 显示修复 Codex 完成报告 v1 + +## 1. 结论 + +已修复“simulation 正常推进但正式 RasterParticle 屏幕主体静止”的 Composite 显示路径。40-01 仍为 `WAITING_MANUAL_VALIDATION`;未运行 Android Gradle、ADB 或真机。 + +## 2. 实际根因 + +`RenderRasterParticleLayer` 的 State A/B 与 Trail A/B 已按日志正常推进,Trail pass 也读取当前 State write texture。但是 `particle_gpu_validation` 的 Composite 只把 `u_previous`(Trail)的 RGB 折算为 alpha intensity,最终 RGB 仍无条件输出 30-03 的蓝色 stripe 和黄色 anchors;并且 ACTIVE Composite 的 legacy stripe frame 参数为零。于是 GPU 粒子没有成为可见输出。 + +## 3. 修复 + +- `ParticleGpuResources::formalDisplay` 只在正式 `RasterParticleLayer` 建立资源时设为 true,并经现有 UBO 的 `u_mask.z` 传入 shader;不增加第二套 simulation。 +- `FORMAL_PARTICLE` Composite 以当前 Trail texture RGB 为最终 premultiplied color,并以 Trail intensity 决定 alpha。uniform-east 的 State 位置经 Trail 进入 Composite,因此屏幕应可见连续向 east 的 marker/trail 运动;STATIC/FrozenComposite 继续采样最后一个 Trail read slot。 +- `LEGACY VALIDATION DISPLAY` 保留原有 30-03 stripe、anchors、mask、world-wrap 与 antimeridian 分支,不受正式显示模式影响。 +- shader 源唯一为 `shaders/particle_gpu_validation.fragment.glsl`;使用 `node shaders/generate_shader_code.mjs` 生成 `include/mbgl/shaders/gl/particle_gpu_validation.hpp`。生成器会同步规范化 `src/mbgl/shaders/shader_source.cpp`,已保留该生成物以保证二次运行无差异。 +- 增加一次性 `displayMode=FORMAL_PARTICLE|VALIDATION` 日志,以及低频 `trailStateSource` / `compositeTrailSource` 日志,诊断 State→Trail→Composite 的正式数据流。 + +未改变 Offline fixture adapter、RasterArraySource、RasterParticleLayer、field/NoData、State/Trail ping-pong、mask、ACTIVE/STATIC lifecycle、zero-step bootstrap、FrozenComposite、rotation resource recreation、world wrap、antimeridian 或 RasterArray source RenderItem contract。 + +## 4. 静态验证 + +- Linux OpenGL:`render_particle_validation_layer.cpp.o` 精确编译 PASS。 +- 相关现有 C++:`test/style/source.test.cpp.o` 精确编译 PASS。 +- shader generator:连续运行两次后 `particle_gpu_validation.hpp` 与 `shader_source.cpp` 哈希一致,PASS。 +- `clang-format --dry-run --Werror`(受影响 C++)和 `git diff --check`:PASS。 +- 未运行完整 `mbgl-test`:该 target 在本 build tree 仍需 312 个对象,属于与本次 shader 显示修复无关的广泛构建,未把未执行测试描述为 PASS。 + +## 5. 后续人工验收 + +用户下一轮仅验收 GLOBAL + ACTIVE + `uniform-east`:正式 RasterParticle 必须肉眼清楚、连续地向 east 移动。通过后再测试 STATIC、uniform-north、Rotation 与 T1-T12。 + +## 6. Git 收尾 + +本次修复 commit、push 结果与 `origin/particle/main` 最终 HEAD 在交付收尾后回填。 diff --git a/include/mbgl/shaders/gl/particle_gpu_validation.hpp b/include/mbgl/shaders/gl/particle_gpu_validation.hpp index 2585f2462..c4dd0541c 100644 --- a/include/mbgl/shaders/gl/particle_gpu_validation.hpp +++ b/include/mbgl/shaders/gl/particle_gpu_validation.hpp @@ -76,10 +76,20 @@ void main() { vec3 color = previous.rgb * u_marker.w + marker * vec3(0.08, 0.55, 1.0); fragColor = vec4(min(color, vec3(1.0)), 1.0); } else { - // Current-frame canonical world composite and shared Mask. + // The formal RasterParticle layer displays the Trail texture written by + // the current State -> Trail pass. Legacy ParticleValidation keeps its + // independent 30-03 stripe/anchor diagnostic display below. float intensity = max(previous.r, max(previous.g, previous.b)); float ocean = texture(u_mask_texture, v_world).a; if (u_pass.z > 0.5 && ocean < 0.5) discard; + if (u_mask.z > 0.5) { + float alpha = clamp(intensity * 1.25, 0.0, 0.96); + fragColor = vec4(previous.rgb * alpha, alpha); + return; + } + + // LEGACY VALIDATION DISPLAY: preserve the frozen 30-03 + // stripe/anchor/world-wrap diagnostic for ParticleValidationLayer. float stripe = 0.55 + 0.45 * sin((v_world.x + v_world.y) * 100.0 + u_pass.y * 0.07); // A=(0°,0°), B=(-90°,approximately 0°), C/D straddle ±180°. float anchors = 0.0; diff --git a/shaders/particle_gpu_validation.fragment.glsl b/shaders/particle_gpu_validation.fragment.glsl index 4a8002eed..6bb81a5c4 100644 --- a/shaders/particle_gpu_validation.fragment.glsl +++ b/shaders/particle_gpu_validation.fragment.glsl @@ -42,10 +42,20 @@ void main() { vec3 color = previous.rgb * u_marker.w + marker * vec3(0.08, 0.55, 1.0); fragColor = vec4(min(color, vec3(1.0)), 1.0); } else { - // Current-frame canonical world composite and shared Mask. + // The formal RasterParticle layer displays the Trail texture written by + // the current State -> Trail pass. Legacy ParticleValidation keeps its + // independent 30-03 stripe/anchor diagnostic display below. float intensity = max(previous.r, max(previous.g, previous.b)); float ocean = texture(u_mask_texture, v_world).a; if (u_pass.z > 0.5 && ocean < 0.5) discard; + if (u_mask.z > 0.5) { + float alpha = clamp(intensity * 1.25, 0.0, 0.96); + fragColor = vec4(previous.rgb * alpha, alpha); + return; + } + + // LEGACY VALIDATION DISPLAY: preserve the frozen 30-03 + // stripe/anchor/world-wrap diagnostic for ParticleValidationLayer. float stripe = 0.55 + 0.45 * sin((v_world.x + v_world.y) * 100.0 + u_pass.y * 0.07); // A=(0°,0°), B=(-90°,approximately 0°), C/D straddle ±180°. float anchors = 0.0; diff --git a/src/mbgl/renderer/layers/render_particle_validation_layer.cpp b/src/mbgl/renderer/layers/render_particle_validation_layer.cpp index 0b8c58e29..2594fbce8 100644 --- a/src/mbgl/renderer/layers/render_particle_validation_layer.cpp +++ b/src/mbgl/renderer/layers/render_particle_validation_layer.cpp @@ -154,6 +154,8 @@ public: bool contextDestroyed = false; bool lifecycleModeKnown = false; bool lifecycleActive = false; + bool formalDisplay = false; + bool displayModeLogged = false; bool staticFreezeLogged = false; bool cameraDrawLogged = false; bool worldWrapLogged = false; @@ -271,7 +273,10 @@ public: const shaders::ParticleGpuValidationUBO ubo = { .pass = {static_cast(pass), frame, maskMode == 0 ? 0.0f : 1.0f, static_cast(maskMode)}, .marker = {markerX, markerY, 0.035f, 0.965f}, - .mask = {static_cast(resources->maskGeneration), static_cast(worldWrap), 0.0f, 0.0f}, + .mask = {static_cast(resources->maskGeneration), + static_cast(worldWrap), + resources->formalDisplay ? 1.0f : 0.0f, + 0.0f}, .matrix = util::cast(parameters.matrixForTile({worldWrap, {0, 0, 0}})), }; drawable.mutableUniformBuffers().createOrUpdate(shaders::idParticleGpuValidationUBO, &ubo, parameters.context); @@ -279,6 +284,14 @@ public: if (pass == ValidationPass::Composite || pass == ValidationPass::FrozenComposite) { const auto center = parameters.state.getLatLng(LatLng::Unwrapped); const bool primaryWorldCopy = worldWrap == wrapForLongitude(center.longitude()); + if (primaryWorldCopy && !resources->displayModeLogged) { + Log::Info(Event::Render, + "ParticleRender: displayMode=" + + std::string(resources->formalDisplay ? "FORMAL_PARTICLE" : "VALIDATION") + + " generation=" + std::to_string(resources->generation) + + " compositeTrailRead=" + slotName(readSlot)); + resources->displayModeLogged = true; + } if (primaryWorldCopy && !resources->cameraDrawLogged) { std::ostringstream message; message << "ParticleValidation30_03: first camera-locked draw maskGeneration=" @@ -574,6 +587,7 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders, if (!resources) { auto owned = std::make_shared(); owned->generation = ++nextGpuGeneration; + owned->formalDisplay = formalSource; owned->shader = context.getGenericShader(shaders, ShaderName); owned->vertices = std::make_shared>( RenderStaticData::heatmapTextureVertices()); @@ -943,10 +957,13 @@ void RenderRasterParticleLayer::update(gfx::ShaderRegistry& shaders, if (resources->committedFrame < 3 || (resources->committedFrame + 1) % 60 == 0) { Log::Info(Event::Render, - "ParticleValidation30_02: schedule generation=" + std::to_string(resources->generation) + " frame=" + - std::to_string(resources->committedFrame + 1) + " state=" + slotName(resources->stateRead) + - "->" + slotName(stateWrite) + " trail=" + slotName(resources->trailRead) + "->" + - slotName(trailWrite) + " targetsDistinct=true cpuStateCopy=false"); + "ParticleValidation30_02: schedule generation=" + std::to_string(resources->generation) + + " frame=" + std::to_string(resources->committedFrame + 1) + + " state=" + slotName(resources->stateRead) + "->" + slotName(stateWrite) + + " trail=" + slotName(resources->trailRead) + "->" + slotName(trailWrite) + + " trailStateSource=" + slotName(stateWrite) + " compositeTrailSource=" + slotName(trailWrite) + + " displayMode=" + (resources->formalDisplay ? "FORMAL_PARTICLE" : "VALIDATION") + + " targetsDistinct=true cpuStateCopy=false"); } } diff --git a/src/mbgl/shaders/shader_source.cpp b/src/mbgl/shaders/shader_source.cpp index 6a15e2638..1a4420ff3 100644 --- a/src/mbgl/shaders/shader_source.cpp +++ b/src/mbgl/shaders/shader_source.cpp @@ -6,43 +6,44 @@ namespace mbgl { using namespace shaders; -MBGL_DEFINE_ENUM(BuiltIn, - {{BuiltIn::None, "None"}, - {BuiltIn::Prelude, "Prelude"}, - {BuiltIn::ClippingMaskProgram, "ClippingMaskProgram"}, - {BuiltIn::BackgroundShader, "BackgroundShader"}, - {BuiltIn::BackgroundPatternShader, "BackgroundPatternShader"}, - {BuiltIn::CircleShader, "CircleShader"}, - {BuiltIn::CollisionBoxShader, "CollisionBoxShader"}, - {BuiltIn::CollisionCircleShader, "CollisionCircleShader"}, - {BuiltIn::CustomGeometryShader, "CustomGeometryShader"}, - {BuiltIn::CustomSymbolIconShader, "CustomSymbolIconShader"}, - {BuiltIn::DebugShader, "DebugShader"}, - {BuiltIn::FillShader, "FillShader"}, - {BuiltIn::FillOutlineShader, "FillOutlineShader"}, - {BuiltIn::FillPatternShader, "FillPatternShader"}, - {BuiltIn::FillOutlinePatternShader, "FillOutlinePatternShader"}, - {BuiltIn::FillOutlineTriangulatedShader, "FillOutlineTriangulatedShader"}, - {BuiltIn::FillExtrusionShader, "FillExtrusionShader"}, - {BuiltIn::FillExtrusionInstancedShader, "FillExtrusionInstancedShader"}, - {BuiltIn::FillExtrusionPatternShader, "FillExtrusionPatternShader"}, - {BuiltIn::FillExtrusionPatternInstancedShader, "FillExtrusionPatternInstancedShader"}, - {BuiltIn::HeatmapShader, "HeatmapShader"}, - {BuiltIn::HeatmapTextureShader, "HeatmapTextureShader"}, - {BuiltIn::ParticleGpuValidationShader, "ParticleGpuValidationShader"}, - {BuiltIn::HillshadePrepareShader, "HillshadePrepareShader"}, - {BuiltIn::ColorReliefShader, "ColorReliefShader"}, - {BuiltIn::HillshadeShader, "HillshadeShader"}, - {BuiltIn::LineShader, "LineShader"}, - {BuiltIn::LineGradientShader, "LineGradientShader"}, - {BuiltIn::LinePatternShader, "LinePatternShader"}, - {BuiltIn::LocationIndicatorShader, "LocationIndicatorShader"}, - {BuiltIn::LocationIndicatorTexturedShader, "LocationIndicatorTexturedShader"}, - {BuiltIn::LineSDFShader, "LineSDFShader"}, - {BuiltIn::RasterShader, "RasterShader"}, - {BuiltIn::SymbolIconShader, "SymbolIconShader"}, - {BuiltIn::SymbolSDFShader, "SymbolSDFShader"}, - {BuiltIn::SymbolTextAndIconShader, "SymbolTextAndIconShader"}, - {BuiltIn::WideVectorShader, "WideVectorShader"}}); +MBGL_DEFINE_ENUM(BuiltIn, { +{BuiltIn::None, "None"}, +{BuiltIn::Prelude, "Prelude"}, +{BuiltIn::ClippingMaskProgram, "ClippingMaskProgram"}, +{BuiltIn::BackgroundShader, "BackgroundShader"}, +{BuiltIn::BackgroundPatternShader, "BackgroundPatternShader"}, +{BuiltIn::CircleShader, "CircleShader"}, +{BuiltIn::CollisionBoxShader, "CollisionBoxShader"}, +{BuiltIn::CollisionCircleShader, "CollisionCircleShader"}, +{BuiltIn::CustomGeometryShader, "CustomGeometryShader"}, +{BuiltIn::CustomSymbolIconShader, "CustomSymbolIconShader"}, +{BuiltIn::DebugShader, "DebugShader"}, +{BuiltIn::FillShader, "FillShader"}, +{BuiltIn::FillOutlineShader, "FillOutlineShader"}, +{BuiltIn::FillPatternShader, "FillPatternShader"}, +{BuiltIn::FillOutlinePatternShader, "FillOutlinePatternShader"}, +{BuiltIn::FillOutlineTriangulatedShader, "FillOutlineTriangulatedShader"}, +{BuiltIn::FillExtrusionShader, "FillExtrusionShader"}, +{BuiltIn::FillExtrusionInstancedShader, "FillExtrusionInstancedShader"}, +{BuiltIn::FillExtrusionPatternShader, "FillExtrusionPatternShader"}, +{BuiltIn::FillExtrusionPatternInstancedShader, "FillExtrusionPatternInstancedShader"}, +{BuiltIn::HeatmapShader, "HeatmapShader"}, +{BuiltIn::HeatmapTextureShader, "HeatmapTextureShader"}, +{BuiltIn::ParticleGpuValidationShader, "ParticleGpuValidationShader"}, +{BuiltIn::HillshadePrepareShader, "HillshadePrepareShader"}, +{BuiltIn::ColorReliefShader, "ColorReliefShader"}, +{BuiltIn::HillshadeShader, "HillshadeShader"}, +{BuiltIn::LineShader, "LineShader"}, +{BuiltIn::LineGradientShader, "LineGradientShader"}, +{BuiltIn::LinePatternShader, "LinePatternShader"}, +{BuiltIn::LocationIndicatorShader, "LocationIndicatorShader"}, +{BuiltIn::LocationIndicatorTexturedShader, "LocationIndicatorTexturedShader"}, +{BuiltIn::LineSDFShader, "LineSDFShader"}, +{BuiltIn::RasterShader, "RasterShader"}, +{BuiltIn::SymbolIconShader, "SymbolIconShader"}, +{BuiltIn::SymbolSDFShader, "SymbolSDFShader"}, +{BuiltIn::SymbolTextAndIconShader, "SymbolTextAndIconShader"}, +{BuiltIn::WideVectorShader, "WideVectorShader"} +}); } // namespace mbgl diff --git a/粒子开发记录.md b/粒子开发记录.md index 24c36a866..2925072a3 100644 --- a/粒子开发记录.md +++ b/粒子开发记录.md @@ -188,3 +188,12 @@ - ACTIVE 现在连续按 A→B、B→A 交换并让 `committedFrame` 递增;STATIC 在未 seed(初次或真实资源事件)时只做一次 zero-step bootstrap,已有状态时只走 FrozenComposite,不注册离屏 target、不 commit;STATIC→ACTIVE 复用既有 slot/frame 继续 simulation。没有改变 pass 次序 `State,Trail,Composite`、hazard=SAFE、fixture/resourceGeneration contract 或 public API。 - 日志现在在 ACTIVE/STATIC transition 输出 generation、fixture、frame 与 read slot;schedule/pass/commit 输出前 3 帧及每 60 帧,避免长期高频洪水,同时可明确验收 1→2→3 与 A/B 交替。 - Linux OpenGL `render_particle_validation_layer.cpp.o` 精确编译、`clang-format` 与 `git diff --check`:PASS。40-01 保持 `WAITING_MANUAL_VALIDATION`,等待用户用新 APK 完成 GLOBAL ACTIVE、ACTIVE→STATIC、STATIC→ACTIVE 手工验收。 + +## 2026-09-01 — 40-01 Formal RasterParticle Trail Composite 显示修复 + +- 实机已证明 generation 稳定、simulationFrame 连续递增、State/Trail A/B ping-pong 与 `hazard=SAFE` 正常;画面静止不再是 lifecycle/repaint/clock 问题。 +- 根因位于最终 Composite:Trail pass 确实从最新 State write 画入 Trail A/B,但 Composite 仅将 Trail RGB 计算为 alpha intensity,最终 RGB 始终是 30-03 legacy 的 stripe/anchor。因此正式 field simulation 没有成为屏幕主体。 +- `ParticleGpuResources::formalDisplay` 由 `RasterParticleLayer` 的 formal source 身份设置,并通过现有 UBO `u_mask.z` 传入 shader。正式 `FORMAL_PARTICLE` Composite 输出当前 Trail RGB 与由其 intensity 导出的 premultiplied alpha;legacy `ParticleValidationLayer` 继续走未改变的 `VALIDATION` stripe/anchor/world-wrap 分支。 +- `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 运动。