diff --git a/docs/50_Step记录/STEP-055_Android真实WeatherV2风场综合可视化_v3.md b/docs/50_Step记录/STEP-055_Android真实WeatherV2风场综合可视化_v3.md new file mode 100644 index 000000000..3198e36ec --- /dev/null +++ b/docs/50_Step记录/STEP-055_Android真实WeatherV2风场综合可视化_v3.md @@ -0,0 +1,26 @@ +# STEP-055 Android 真实 WeatherV2 风场综合可视化 v3 + +状态:**IMPLEMENTED / WAITING_ANDROID_VALIDATION** + +Starting HEAD:`f8027312e6e06d539e9ad222b60ac9a496b1758d` + +## Float State fail-closed 小修复 + +`createValidationTarget()` 现在将 RenderTarget 的创建、FBO validation、sampler 设置和 LayerGroup 接线置于局部 `try` 边界内。 + +- `std::exception`(包括 `std::runtime_error` 和 `std::bad_alloc`)记录 `stage=exception error=...` 后返回空 target。 +- 未知异常记录 `stage=unknown_exception` 后返回空 target。 +- validation 失败记录 `stage=validation` 后返回空 target。 +- 没有回退到 `UnsignedByte`:State A/B 仍为 `Float`,Trail A/B 仍为 `UnsignedByte`。 + +调用方已有空 target 检查,因而 Float framebuffer 创建异常会 fail-closed,不继续向 Renderer 上层传播异常。 + +## 不变边界 + +未修改 canonical Field、Timeline、Runtime Field Window、WeatherColorLayer、REAL WEATHER UI 或 `visualSpeedScale=6000`。 + +## 验证 + +Linux OpenGL 精确编译 `render_particle_validation_layer.cpp` 及既有非 Jest `style_layer.test.cpp` 均通过;`git diff --check` 通过。未执行 Android Gradle、APK、ADB、设备测试或 Jest。 + +下一步仍是用户 Android 手工验收 Float State target 创建与 Pause 下粒子运动。 diff --git a/src/mbgl/renderer/layers/render_particle_validation_layer.cpp b/src/mbgl/renderer/layers/render_particle_validation_layer.cpp index e00709e9d..a71ecc0bc 100644 --- a/src/mbgl/renderer/layers/render_particle_validation_layer.cpp +++ b/src/mbgl/renderer/layers/render_particle_validation_layer.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -616,27 +617,40 @@ RenderTargetPtr createValidationTarget(gfx::Context& context, const gfx::TextureFilterType filter, const gfx::TextureChannelDataType type, const char* targetKind) { - auto target = context.createRenderTarget(size, type); - if (!target || !target->isRenderable() || !target->getTexture()) { + try { + auto target = context.createRenderTarget(size, type); + if (!target || !target->isRenderable() || !target->getTexture()) { + Log::Error(Event::Render, + "ParticleStateTarget: event=CREATE_FAILED kind=" + std::string(targetKind) + + " dataType=" + textureDataTypeName(type) + " name=" + name + " stage=validation"); + return {}; + } + target->getTexture()->setSamplerConfiguration( + {.filter = filter, .wrapU = gfx::TextureWrapType::Clamp, .wrapV = gfx::TextureWrapType::Clamp}); + auto group = context.createLayerGroup(0, 1, name); + if (!group || !target->addLayerGroup(std::move(group), true)) { + Log::Error(Event::Render, + "ParticleStateTarget: event=CREATE_FAILED kind=" + std::string(targetKind) + + " dataType=" + textureDataTypeName(type) + " name=" + name + " reason=LAYER_GROUP"); + return {}; + } + Log::Info(Event::Render, + "ParticleStateTarget: event=CREATE_SUCCESS kind=" + std::string(targetKind) + + " dataType=" + textureDataTypeName(type) + " name=" + name + " size=" + + std::to_string(size.width) + "x" + std::to_string(size.height)); + return target; + } catch (const std::exception& error) { Log::Error(Event::Render, "ParticleStateTarget: event=CREATE_FAILED kind=" + std::string(targetKind) + - " dataType=" + textureDataTypeName(type) + " name=" + name); + " dataType=" + textureDataTypeName(type) + " name=" + name + " stage=exception error=" + + error.what()); return {}; - } - target->getTexture()->setSamplerConfiguration( - {.filter = filter, .wrapU = gfx::TextureWrapType::Clamp, .wrapV = gfx::TextureWrapType::Clamp}); - auto group = context.createLayerGroup(0, 1, name); - if (!group || !target->addLayerGroup(std::move(group), true)) { + } catch (...) { Log::Error(Event::Render, "ParticleStateTarget: event=CREATE_FAILED kind=" + std::string(targetKind) + - " dataType=" + textureDataTypeName(type) + " name=" + name + " reason=LAYER_GROUP"); + " dataType=" + textureDataTypeName(type) + " name=" + name + " stage=unknown_exception"); return {}; } - Log::Info(Event::Render, - "ParticleStateTarget: event=CREATE_SUCCESS kind=" + std::string(targetKind) + - " dataType=" + textureDataTypeName(type) + " name=" + name + " size=" + - std::to_string(size.width) + "x" + std::to_string(size.height)); - return target; } bool buildQuad(gfx::Context& context, diff --git a/粒子开发记录.md b/粒子开发记录.md index b72d0a57b..e489974cf 100644 --- a/粒子开发记录.md +++ b/粒子开发记录.md @@ -423,3 +423,6 @@ - Android 日志已将“真实 U/V、非零 world displacement、State A/B ping-pong 均存在但粒子静止”收敛为 RGBA8 State 量化:单步 `3e-6–8e-6 world` 小于 UByte `1/255` 的可表示间隔。 - State A/B 现在为 Float,Trail A/B 保持 UnsignedByte;初次创建、context recreation 与 particleCount replacement 均通过同一 Float target 路径,FBO 创建失败明确 fail-closed,不静默降级。 - 现有 OpenGL `Float → RGBA32F/GL_FLOAT → FBO check` 支持直接复用,未改 backend、canonical/Timeline/Window/WeatherColorLayer 或 visualSpeedScale=6000。下一次 Android 的首要检查是 Timeline Pause 下真实固定场粒子是否持续移动,并核对 `ParticleStateTarget` 类型日志。 +## STEP-055 Float State fail-closed 小修复 + +在 Particle Validation `createValidationTarget()` 增加局部标准异常与未知异常捕获。Float State RenderTarget/FBO 创建失败会记录结构化错误并返回空 target,不会向上抛出或降级到 UnsignedByte;State A/B=Float、Trail A/B=UnsignedByte 保持。Linux 精确编译通过,仍待 Android 验收。