test: add runtime field duplicate noop control
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
Some checks failed
validate-scripts / validate-scripts (push) Has been cancelled
This commit is contained in:
@@ -33,3 +33,10 @@
|
||||
- 新增 Source 定向测试对象覆盖 A/B→B/C→C/D、重复 Snapshot NOOP、无效 Snapshot fail-closed,并已精确编译;当前 build tree 缺少大量无关测试依赖,未把完整 `mbgl-test` 二进制执行误报为通过。
|
||||
- 未运行 Android Gradle、ADB、APK 安装、设备测试或 Jest。
|
||||
- 实现提交:`45502a53f4fc486bd24cb0008d1ab0ff221e7e63`(`feat: add runtime particle field updates`);文档收尾另行提交。
|
||||
|
||||
## T-RUNTIME-05 Duplicate NOOP 补验收入口
|
||||
|
||||
- 用户已完成 T-RUNTIME-01~04 与 T-RUNTIME-06 Android 手工验证;本轮只补 T-RUNTIME-05 的真实触发入口,状态仍为 `IMPLEMENTED / WAITING FOR ANDROID VALIDATION`。
|
||||
- `DUPLICATE T1` 按钮经 `MapLibreMap -> NativeMapView -> JNI -> RasterArraySource::repeatRuntimeFieldWindow()` 重提 Source 当前 T1;它不调用 `setStyle()`、不推进 candidate queue,也不改变 Kotlin 的 MIX/窗口状态。
|
||||
- `repeatRuntimeFieldWindow()` 只把已存在的 `Impl::nextField` 交给既有 `updateRuntimeFieldWindow()`。在 `T0=vortex/T1=nodata`,content identity 命中既有 T1,正式 API 返回 `Noop`,不发 Source change,因而不触发 Field upload、GPU slot 交换、simulation reset 或 trail clear。
|
||||
- 新增 Source test assertion 在 C/D 后调用该入口并验证返回 `Noop`、T0/T1 未变、`sourceChangedCount` 未增加。Linux/OpenGL 的 `raster_array_source.cpp.o` 与 `source.test.cpp.o` 以 `-Werror` 精确编译;Android Gradle、ADB、设备和 Jest 未执行。
|
||||
|
||||
36
docs/报告/40-04_T-RUNTIME-05_Duplicate_NOOP_Codex完成报告_v1.md
Normal file
36
docs/报告/40-04_T-RUNTIME-05_Duplicate_NOOP_Codex完成报告_v1.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# 40-04 T-RUNTIME-05 Duplicate NOOP 补验收 Codex 完成报告
|
||||
|
||||
## 状态
|
||||
|
||||
`40-04:IMPLEMENTED / WAITING FOR ANDROID VALIDATION`
|
||||
|
||||
用户已手工通过 T-RUNTIME-01~04、T-RUNTIME-06;本轮只补 T-RUNTIME-05 的 Android 验证入口,未执行 Android 构建或设备操作。
|
||||
|
||||
## 实现
|
||||
|
||||
- 新增 `RasterArraySource::repeatRuntimeFieldWindow()`。它读取 Source 权威 `Impl::nextField` 并调用既有正式 `updateRuntimeFieldWindow()`,不直接操作 renderer 或 GPU。
|
||||
- 当当前窗口为 `T0=vortex / T1=nodata`,这次重提交命中 `sameFieldGpuContent(currentT1, newT1)`,返回 `Noop`;不调用 `onSourceChanged()`,所以没有 Field upload、slot swap、simulation reset、trail clear、Style reload 或 Layer/Source recreate。
|
||||
- Android 增加 `DUPLICATE T1`。调用链为 `MapLibreMap -> NativeMapView -> JNI -> Style::getSource() -> RasterArraySource`;Kotlin/JNI 不持有 T0/T1,也不消费预加载候选。
|
||||
- JNI 只在返回 `Noop` 时报告 duplicate bridge 成功。日志应出现 `FIELD_WINDOW_UPDATE_DUPLICATE_REQUEST`、Core 的 `FIELD_WINDOW_UPDATE_APPLIED result=NOOP` 与 bridge `result=NOOP`。
|
||||
|
||||
## 本地检查
|
||||
|
||||
- Linux/OpenGL:`raster_array_source.cpp.o`、`source.test.cpp.o` 精确 `-Werror` 编译通过。
|
||||
- Source test 补充 C/D 后 duplicate assertion:结果 `Noop`、T0/T1 保持 vortex/nodata、Source changed count 不增加。
|
||||
- `clang-format --dry-run --Werror`、`git diff --check` 通过。
|
||||
- Android Gradle、ADB、APK、实体机和 Jest:均未执行。
|
||||
|
||||
## 用户手工验收
|
||||
|
||||
在已通过 A/B→B/C→C/D 后,保持 `T0=vortex / T1=nodata`,点击 `DUPLICATE T1` 一次。必须确认:
|
||||
|
||||
```text
|
||||
FIELD_WINDOW_UPDATE_APPLIED result=NOOP
|
||||
FIELD_UPLOAD delta=0
|
||||
SIMULATION_RESET delta=0
|
||||
trail clear delta=0
|
||||
Style reload delta=0
|
||||
当前 T0/T1、MIX、frame 持续不变/继续运行
|
||||
```
|
||||
|
||||
随后仍可点击普通 `ADVANCE WINDOW`,它应继续保留既有 T-RUNTIME-06 的 `NO_PRELOADED_FIELD` fail-closed 语义。
|
||||
@@ -52,6 +52,9 @@ public:
|
||||
// Validation convenience: advance using the next Source-owned preloaded
|
||||
// candidate. Android supplies no T0/T1 state and no GPU resource here.
|
||||
RasterArrayRuntimeFieldUpdateResult advanceRuntimeFieldWindow();
|
||||
// Validation convenience: resubmit the current T1 through the formal
|
||||
// update API. This must return Noop without changing the Source window.
|
||||
RasterArrayRuntimeFieldUpdateResult repeatRuntimeFieldWindow();
|
||||
bool supportsLayerType(const LayerTypeInfo*) const override;
|
||||
mapbox::base::WeakPtr<Source> makeWeakPtr() override { return weakFactory.makeWeakPtr(); }
|
||||
|
||||
|
||||
@@ -1164,6 +1164,23 @@ jni::jboolean NativeMapView::advanceRasterArraySourceFieldWindow(JNIEnv& env, co
|
||||
return success;
|
||||
}
|
||||
|
||||
jni::jboolean NativeMapView::repeatRasterArraySourceFieldWindow(JNIEnv& env, const jni::String& sourceId) {
|
||||
const auto id = jni::Make<std::string>(env, sourceId);
|
||||
auto* source = map->getStyle().getSource(id);
|
||||
auto* rasterArraySource = dynamic_cast<mbgl::style::RasterArraySource*>(source);
|
||||
if (!rasterArraySource) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI,
|
||||
"ParticleField: event=FIELD_WINDOW_UPDATE_REJECTED reason=SOURCE_NOT_FOUND sourceId=" + id);
|
||||
return false;
|
||||
}
|
||||
const auto result = rasterArraySource->repeatRuntimeFieldWindow();
|
||||
const bool success = result == mbgl::style::RasterArrayRuntimeFieldUpdateResult::Noop;
|
||||
mbgl::Log::Info(mbgl::Event::JNI,
|
||||
"ParticleField: event=FIELD_WINDOW_UPDATE_DUPLICATE_BRIDGE sourceId=" + id +
|
||||
" result=" + (success ? "NOOP" : "REJECTED"));
|
||||
return success;
|
||||
}
|
||||
|
||||
void NativeMapView::addLayer(JNIEnv& env, jlong nativeLayerPtr, const jni::String& before) {
|
||||
assert(nativeLayerPtr != 0);
|
||||
|
||||
@@ -1549,6 +1566,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) {
|
||||
METHOD(&NativeMapView::getLayer, "nativeGetLayer"),
|
||||
METHOD(&NativeMapView::setStyleLayerFloatProperty, "nativeSetStyleLayerFloatProperty"),
|
||||
METHOD(&NativeMapView::advanceRasterArraySourceFieldWindow, "nativeAdvanceRasterArraySourceFieldWindow"),
|
||||
METHOD(&NativeMapView::repeatRasterArraySourceFieldWindow, "nativeRepeatRasterArraySourceFieldWindow"),
|
||||
METHOD(&NativeMapView::addLayer, "nativeAddLayer"),
|
||||
METHOD(&NativeMapView::addLayerAbove, "nativeAddLayerAbove"),
|
||||
METHOD(&NativeMapView::addLayerAt, "nativeAddLayerAt"),
|
||||
|
||||
@@ -283,6 +283,7 @@ public:
|
||||
jni::Local<jni::Object<Layer>> getLayer(JNIEnv&, const jni::String&);
|
||||
jni::jboolean setStyleLayerFloatProperty(JNIEnv&, const jni::String&, const jni::String&, jni::jfloat);
|
||||
jni::jboolean advanceRasterArraySourceFieldWindow(JNIEnv&, const jni::String&);
|
||||
jni::jboolean repeatRasterArraySourceFieldWindow(JNIEnv&, const jni::String&);
|
||||
|
||||
void addLayer(JNIEnv&, jlong, const jni::String&);
|
||||
|
||||
|
||||
@@ -296,6 +296,11 @@ public final class MapLibreMap {
|
||||
return nativeMapView.advanceRasterArraySourceFieldWindow(sourceId);
|
||||
}
|
||||
|
||||
/** Resubmits the Source-owned current T1 to validate the Runtime Field NOOP path. */
|
||||
public boolean repeatRasterArraySourceFieldWindow(String sourceId) {
|
||||
return nativeMapView.repeatRasterArraySourceFieldWindow(sourceId);
|
||||
}
|
||||
|
||||
// Style
|
||||
|
||||
/**
|
||||
|
||||
@@ -143,6 +143,8 @@ interface NativeMap {
|
||||
|
||||
boolean advanceRasterArraySourceFieldWindow(String sourceId);
|
||||
|
||||
boolean repeatRasterArraySourceFieldWindow(String sourceId);
|
||||
|
||||
boolean removeLayer(@NonNull String layerId);
|
||||
|
||||
boolean removeLayer(@NonNull Layer layer);
|
||||
|
||||
@@ -962,6 +962,12 @@ final class NativeMapView implements NativeMap {
|
||||
&& nativeAdvanceRasterArraySourceFieldWindow(sourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean repeatRasterArraySourceFieldWindow(String sourceId) {
|
||||
return !checkState("repeatRasterArraySourceFieldWindow")
|
||||
&& nativeRepeatRasterArraySourceFieldWindow(sourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLayer(@NonNull Layer layer) {
|
||||
if (checkState("addLayer")) {
|
||||
@@ -1699,6 +1705,9 @@ final class NativeMapView implements NativeMap {
|
||||
@Keep
|
||||
private native boolean nativeAdvanceRasterArraySourceFieldWindow(String sourceId);
|
||||
|
||||
@Keep
|
||||
private native boolean nativeRepeatRasterArraySourceFieldWindow(String sourceId);
|
||||
|
||||
@Keep
|
||||
private native void nativeAddLayer(long layerPtr, String before) throws CannotAddLayerException;
|
||||
|
||||
|
||||
@@ -107,6 +107,9 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
||||
findViewById<Button>(R.id.validationAdvanceWindow).setOnClickListener {
|
||||
if (this::maplibreMap.isInitialized) advanceRuntimeFieldWindow()
|
||||
}
|
||||
findViewById<Button>(R.id.validationDuplicateWindow).setOnClickListener {
|
||||
if (this::maplibreMap.isInitialized) duplicateRuntimeFieldWindow()
|
||||
}
|
||||
findViewById<Button>(R.id.validationDateline).setOnClickListener {
|
||||
maplibreMap.cameraPosition = CameraPosition.Builder().target(LatLng(0.0, 179.0)).zoom(2.0).build()
|
||||
Log.i(TAG, "ParticleValidation30_03: DATELINE preset center=0,179 zoom=2")
|
||||
@@ -213,6 +216,25 @@ class ParticleCoreLayerValidationActivity : AppCompatActivity() {
|
||||
Log.i(TAG, "ParticleField: event=FIELD_WINDOW_UPDATE_APPLIED styleEpoch=$styleLoadEpoch")
|
||||
}
|
||||
|
||||
private fun duplicateRuntimeFieldWindow() {
|
||||
val rejectReason = when {
|
||||
!styleReady -> "STYLE_LOADING"
|
||||
!enabled -> "DISABLED"
|
||||
!timeWindowMode -> "TIME_WINDOW_OFF"
|
||||
else -> null
|
||||
}
|
||||
if (rejectReason != null) {
|
||||
Log.i(TAG, "ParticleField: event=FIELD_WINDOW_UPDATE_REJECTED reason=$rejectReason styleEpoch=$styleLoadEpoch")
|
||||
return
|
||||
}
|
||||
Log.i(TAG, "ParticleField: event=FIELD_WINDOW_UPDATE_DUPLICATE_REQUEST sourceId=$PARTICLE_SOURCE_ID styleEpoch=$styleLoadEpoch")
|
||||
if (!maplibreMap.repeatRasterArraySourceFieldWindow(PARTICLE_SOURCE_ID)) {
|
||||
Log.i(TAG, "ParticleField: event=FIELD_WINDOW_UPDATE_REJECTED reason=CORE_SOURCE_REJECTED styleEpoch=$styleLoadEpoch")
|
||||
return
|
||||
}
|
||||
Log.i(TAG, "ParticleField: event=FIELD_WINDOW_UPDATE_APPLIED result=NOOP styleEpoch=$styleLoadEpoch")
|
||||
}
|
||||
|
||||
private fun statusText(): String {
|
||||
val mask = when (maskMode) {
|
||||
MASK_OCEAN_A -> "OCEAN A"
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
<Button android:id="@+id/validationUniformNorth" style="@style/ParticleValidationButton" android:layout_width="0dp" android:layout_weight="1" android:text="@string/particle_validation_uniform_north" />
|
||||
<Button android:id="@+id/validationTimeWindow" style="@style/ParticleValidationButton" android:layout_width="0dp" android:layout_weight="1" android:text="@string/particle_validation_time_window" />
|
||||
<Button android:id="@+id/validationAdvanceWindow" style="@style/ParticleValidationButton" android:layout_width="0dp" android:layout_weight="1" android:text="@string/particle_validation_advance_window" />
|
||||
<Button android:id="@+id/validationDuplicateWindow" style="@style/ParticleValidationButton" android:layout_width="0dp" android:layout_weight="1" android:text="@string/particle_validation_duplicate_window" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<string name="particle_validation_uniform_north">UNIFORM NORTH</string>
|
||||
<string name="particle_validation_time_window">T0 EAST / T1 NORTH</string>
|
||||
<string name="particle_validation_advance_window">ADVANCE WINDOW</string>
|
||||
<string name="particle_validation_duplicate_window">DUPLICATE T1</string>
|
||||
<string name="particle_validation_mix_0">MIX 0%</string>
|
||||
<string name="particle_validation_mix_50">MIX 50%</string>
|
||||
<string name="particle_validation_mix_100">MIX 100%</string>
|
||||
|
||||
@@ -261,6 +261,13 @@ RasterArrayRuntimeFieldUpdateResult RasterArraySource::advanceRuntimeFieldWindow
|
||||
return result;
|
||||
}
|
||||
|
||||
RasterArrayRuntimeFieldUpdateResult RasterArraySource::repeatRuntimeFieldWindow() {
|
||||
if (!loaded || !impl().nextField) {
|
||||
return rejectRuntimeFieldUpdate(RasterArrayRuntimeFieldUpdateResult::RejectedNotLoaded, "SOURCE_NOT_LOADED");
|
||||
}
|
||||
return updateRuntimeFieldWindow(*impl().nextField);
|
||||
}
|
||||
|
||||
bool RasterArraySource::supportsLayerType(const LayerTypeInfo* info) const {
|
||||
return info && std::string(info->type) == "raster-particle";
|
||||
}
|
||||
|
||||
@@ -306,6 +306,11 @@ TEST(Source, RasterArrayRuntimeFieldWindowAdvancesWithoutReplacingTheSource) {
|
||||
EXPECT_EQ(source.impl().nextField->fixtureID, "nodata");
|
||||
EXPECT_EQ(sourceChangedCount, 3u);
|
||||
EXPECT_NE(source.impl().field->generation, initialT0Generation);
|
||||
|
||||
EXPECT_EQ(source.repeatRuntimeFieldWindow(), style::RasterArrayRuntimeFieldUpdateResult::Noop);
|
||||
EXPECT_EQ(source.impl().field->fixtureID, "vortex");
|
||||
EXPECT_EQ(source.impl().nextField->fixtureID, "nodata");
|
||||
EXPECT_EQ(sourceChangedCount, 3u);
|
||||
}
|
||||
|
||||
TEST(Source, RasterArrayTimeWindowKeepsIndependentCompatibleSnapshots) {
|
||||
|
||||
@@ -292,3 +292,9 @@
|
||||
- 重复当前内容明确 NOOP;无效、未加载、不兼容、非单调或无候选更新 fail closed,不污染当前有效窗口。没有每帧 Field copy/hash/upload,也未修改 40-03 full particle draw。
|
||||
- Linux/OpenGL 受影响 Core/conversion/source test object 精确编译;定向窗口推进 test object 覆盖已加入,完整 `mbgl-test` 因 build tree 缺失大量无关依赖未执行,未将其误报为通过。格式与 diff 检查结果在完成报告收尾记录。未执行 Android Gradle、ADB、APK、实体机或 Jest;当前状态仅为 `IMPLEMENTED / WAITING FOR ANDROID VALIDATION`。
|
||||
- 实现提交:`45502a53f4fc486bd24cb0008d1ab0ff221e7e63`(`feat: add runtime particle field updates`);文档收尾提交另行记录。
|
||||
|
||||
## 2026-09-03 — 40-04 T-RUNTIME-05 Duplicate NOOP 补验收入口
|
||||
|
||||
- 已有 Runtime API 的 `sameFieldGpuContent(currentT1, newT1)` NOOP 分支正确,但 Android 只有 ADVANCE(候选耗尽后为 T-RUNTIME-06 reject),无法真实重提当前 T1。
|
||||
- 新增 Source-owned `repeatRuntimeFieldWindow()` 和最小 Android/JNI 薄桥;`DUPLICATE T1` 只把当前 `Impl::nextField` 重交正式 update API,在 vortex/nodata 窗口返回 NOOP,不推进 candidate queue,不改变 Source window,也不触发 `onSourceChanged()`。
|
||||
- C++ Source test 在 C/D 后断言 Noop、T0/T1 不变及 source changed count 不增;Linux/OpenGL 精确 Core/test object 编译、format/diff check 通过。Android Gradle、ADB、设备和 Jest 未执行;状态继续 `IMPLEMENTED / WAITING FOR ANDROID VALIDATION`。
|
||||
|
||||
Reference in New Issue
Block a user