fix: fail closed on particle target creation exceptions
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled

This commit is contained in:
OpenAI Codex
2026-09-05 09:22:44 +08:00
parent f8027312e6
commit 7f66b10013
3 changed files with 57 additions and 14 deletions

View File

@@ -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 下粒子运动。

View File

@@ -36,6 +36,7 @@
#include <atomic>
#include <cmath>
#include <chrono>
#include <exception>
#include <limits>
#include <sstream>
#include <vector>
@@ -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,

View File

@@ -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 验收。