diff --git a/docs/报告/V26_Metal混合Segment视觉错误_Retained保留修复调查报告_v2.md b/docs/报告/V26_Metal混合Segment视觉错误_Retained保留修复调查报告_v2.md new file mode 100644 index 000000000..cb02f0c32 --- /dev/null +++ b/docs/报告/V26_Metal混合Segment视觉错误_Retained保留修复调查报告_v2.md @@ -0,0 +1,73 @@ +# V26 Metal 混合 Segment 视觉错误——保留 Retained 的修复调查报告 v2 + +日期:2026-10-13 +状态:**iPad F 组真机 FAIL(2026-10-13 18:51 JST);CommandBuffer 修正候选不能作为修复完成证据。Native 编译/安装/启动通过,但 Retained 视觉验收不通过。禁止发布 V27。** +前一版:`V26_Metal混合Segment视觉错误_安全降级调查报告_v1.md` **REJECTED**,禁止发布其中关闭 Metal Retained 的方案。 + +## 1. 用户强制约束 + +- 不允许关闭整个 Retained、不允许把有效 Retained Plan 隐式改成全量 Live。 +- Retained 必须保持 GPU 离屏绘制、缓存 Build/Reuse、Cache Texture 合成;Continuous 执行频率保持独立。 +- Android/iOS 使用同一套 JS Plan 和用户步骤,不以 `Platform.OS` 分配不同实验语义。 +- 以 Style Z-order 为唯一图层顺序,不改本船、AIS 或天气业务数据。 +- 不新增私有 RN/Native API,不改 Particle Contract。 +- 实际执行:2026-10-13 经用户授权,ChatGPT 通过 RDC 在 Mac 远程执行 iOS Metal 构建、签名、覆盖安装与启动;Android 原生编译和安装未执行。iPad 视觉验收由用户人工确认。 + +## 2. 已取得的实际复现 + +来源:Pleiades iPad mini 5,已安装 V26;Windows NavSea Metro `192.168.200.100:8081`。 + +- E:全 115 层 CONTINUOUS,Retained 总开关开启,0 Retained Segment,iPad 无闪烁,图标/本船航行线/AIS 航线/色场/粒子正确。快照 `/tmp/navsea-v26-ipad-render-20261013/E-0746/start.json`。 +- F:R0 仅 `background`,C1 其余 114 层,Native `valid=true`, `fallback=false`,`actualExecution=[composite,live]`,R0 build 96/reuse 2778。冷启动即图标过大、本船/AIS 航线消失;天气仍可见且不闪。快照 `/tmp/navsea-v26-ipad-render-20261013/F-0740/start.json`。 +- 原始 RC:R0 层 0..30,C1 层 31..114;天气 ON 后闪烁,OFF 后持续。旧 RCR 预检的末端 Layer ID 硬编码与实际 Style 不符,现场最终跑 RC。 +- A:关闭 Retained 不闪;B:Native 天气隐藏不闪;C 和 D 分别只显示色场或粒子都闪。C/D 使用相同 Native Runtime 数据提交,不代表单独 shader 根因。 + +F 在仅最底层缓存时也失败,说明“缩小缓存至简单图层”不能作为修复。 + +### 2026-10-13 新构建真机复测(候选 FAIL) + +- RDC Mac Bazel `bazel build -c opt --//:renderer=metal //platform/ios:MapLibre.dynamic`:943/943,exit 0。新候选 ZIP SHA-256:`b89bc06bf03eba20a5790da21215fe7e167da20b3fdfa6a34782420bd503a354`。 +- 新 XCFramework 从本地 ZIP 替换至暂存 NavSea.app,codesign 重新签名和验证 PASS,Pleiades 覆盖安装及启动 PASS;没有发布/替换正式 SwiftPM V26。 +- 用户报告新候选 E:地图、天气 ON/OFF 都正常(E 不执行 Retained Cache)。 +- 用户报告新候选 F:**FAIL**,冷启动光弧等图形巨大、不闪、航向线消失、天气开关无可见效果。不能将无闪烁当作正确。 +- 新 F Native 快照:`/tmp/navsea-v27-retained-ipad-stage/F-1851/start.json` 和 `end.json`,时间 `2026-10-13T09:51:04Z`、generation 3、mapReactTag 802、118 Style Layers;R0 `[0,1)` `background`、`actualExecution=composite`、`build=13`、`reuse=1069`、`invalidation=13`;C1 `[1,118)` `actualExecution=live`;`valid=true/fallback=false`。证明缓存确实工作,但画面仍错。 +- 结论:CommandBuffer 原实现存在执行序不明确的风险,然而此候选未修复视觉缺陷,**无法将它认定为根因或 V27 修复**。后续重点分析 Metal Composite 的绘制状态、RenderPass、Stencil/Depth 和 Live 绘制的相互作用。 +- 新 F 完成后 Windows Metro 同一 Android/iOS Debug 开关已改回 E,iOS Bundle HTTP 200;**用户于 2026-10-13 20:43 JST 确认 iPad 重新运行 E 后显示 OK**。由此 E 稳定恢复;F 明确失败。候选仍留在 iPad,仅切回 JS E,不代表回装了旧 V26。 +- 后续本轮只读源代码调查:`renderer_impl.cpp` 的 F/E 共同走标准 Live `drawableOpaquePass/drawableTranslucentPass`,区别是 F 额外执行了 `RetainedUnderlayCache::beginBuild/finishBuild/composite`;`mtl::RenderPass` 和 `PaintParameters::beginRenderTarget()` 会重建 Pass 状态及清理 tile clipping mask ID。需重点验证 Cache Composite 的真实 Metal state、Stencil/Depth、顶点/纹理坐标以及 Live drawable 的状态恢复;仅从快照计数不能推断根因。 + +## 3. 已从实际代码确认的候选根因 + +V26 `src/mln/mtl/offscreen_texture.cpp` 的 `OffscreenTextureResource::bind` 以 `context.getBackend().getCommandQueue()->commandBuffer()` 单独取得离屏 CommandBuffer;`swap()` 对它独立 `commit + waitUntilCompleted`。 + +iOS `platform/ios/src/MLNMapView+Metal.mm` 的默认画面 `MLNMapViewMetalRenderableResource` 自建 `commandQueue` / `commandBuffer`,直到帧末 `swap` 才提交。 + +`renderer_impl.cpp` 先用默认画面 CommandBuffer 组织上传,再绘制 Retained 离屏缓存、调用其 `present`,之后在默认画面绘制缓存纹理及 Continuous。**这两条独立命令队列并不建立“主帧上传 → 离屏构建 → 主帧合成”的 GPU 顺序保证。** 这是确定存在的同步风险;是否是图标放大/航线丢失的唯一原因仍需真机验证。 + +## 4. 第二版实现(保留 Retained) + +- `include/mln/mtl/context.hpp` 和 `src/mln/mtl/context.cpp`:提供帧内借用的主 CommandBuffer 指针,在 beginFrame/endFrame 清理,不持有跨帧引用。 +- `src/mln/mtl/command_encoder.cpp`:默认画面 UploadPass 创建时,将它的 CommandBuffer 登记为当前帧 CommandBuffer。 +- `include/mln/mtl/offscreen_texture.hpp`、`src/mln/mtl/offscreen_texture.cpp`:增加仅供 Retained Cache 显式启用的帧内排序开关。启用后其离屏 RenderPass **复用当前帧主 CommandBuffer**;离屏 `swap` 在共享模式只清理引用,**不提前提交主帧**,由默认画面帧末统一 commit。未启用此开关的离屏纹理(包括 Snapshot/其它 RenderTarget),即使同帧存在主 CommandBuffer,也维持原先的独立 commit/wait。 +- `src/mln/renderer/retained_underlay_cache.cpp`:仅在 Metal Retained Cache 分配或使用 offscreenTexture 时显式启用上述开关;OpenGL 路径不变。 +- `test/renderer/mtl_retained_attachments.test.cpp`:新增共享 CommandBuffer、离屏 swap 后仍允许添加同帧 RenderPass 的针对性 Metal 测试。 + +预期顺序:一个 Metal CommandBuffer 内 `Upload → Retained offscreen render → main composite → live Continuous → final present`。各 Pass 的独立 Color/Depth/Stencil attachment 继续保留。 +**没有修改 `RetainedUnderlayCache` 的 Build/Reuse/Composite 语义,也没有跳过任意合法 Retained Segment。** + +## 5. 验收和停止条件 + +1. 检查改动的 C++ 格式与差异;本轮不自动运行 Native 编译或设备操作。 +2. 用户手工构建 Metal Candidate XCFramework(使用现有已验证命令 `bazel build -c opt --//:renderer=metal //platform/ios:MapLibre.dynamic`),在独立测试版中接入。**不能覆盖正式 V26、不能用旧 SwiftPM binary 冒充新补丁。** +3. 先运行 MetalRetainedAttachments 的针对性单测,确认共享 CommandBuffer 不提前 commit,独立离屏路径不退化。 +4. iPad 首先复测 F:保留 `[R(background),C(rest)]` 的 Native Plan,不允许 Native 临时回退全部 Continuous;要求 `build>0`、`reuse>0`、`actualExecution=[composite,live]`,并且图标大小和所有航线正常。 +5. F 通过后再测 RC:天气 OFF/ON/OFF,色场、粒子、本船/AIS/海图视觉完整,无异常闪烁;对照 Android OpenGL 同一 JS Plan。 +6. 检查帧率、CPU/GPU 时间、缓存重建/复用、内存;保证 Retained 的性能价值。 +7. F 如果仍错误,**立即停止**,采集 Metal GPU Capture 对比两个 RenderPass 的资源绑定与混合/Depth/Stencil,不能宣布 bug 已修好,也不得重新关闭 Retained。 + +## 6. 当前工程和发行状态 + +Mac 仓库 `/Users/tei/dev/MapLibreParticleLab` branch `particle/main`、当前起点 HEAD `b0f34cc9a299a75af1f7e708c63d75eaa42e9ca2`。源码改动尚未 commit/push/tag/publish;正式 V26 SOURCE_LOCK 指向 `fce326a9009d3fc21eb91c5df362f0769163d07e`,它是旧发行身份,不代表此次候选已发布。 + +Windows NavSea 当前 JS 已取消 iOS 独享 E:Debug Android/iOS 均选 E,Release BASELINE。正式修复后需在新 Native 实际安装成功、用户批准测试窗口后,把共用 JS Test Mode 切到 F/RC 进行对照;不能在旧 V26 上提前切换造成已知异常。 + +当前明确结论:**这是保留 Retained 的候选修复,尚未通过 Native 编译与设备画面确认,不能写成 CLOSED/PASS。** diff --git a/docs/报告/V26_Metal混合Segment视觉错误_Retained保留修复调查报告_v3.md b/docs/报告/V26_Metal混合Segment视觉错误_Retained保留修复调查报告_v3.md new file mode 100644 index 000000000..413ec0d5d --- /dev/null +++ b/docs/报告/V26_Metal混合Segment视觉错误_Retained保留修复调查报告_v3.md @@ -0,0 +1,45 @@ +# V26 Metal 混合 Segment 视觉缺陷——保留 Retained 修复调查报告 v3 + +日期:2026-10-13 +状态:**第二版 Stencil 状态修正 F 真机 FAIL / ABORTED;图标仍异常放大,且用户报告地图无法移动。已保存证据、恢复共用 JS E,并归档/撤销失败的源码补丁。不可发布 V27。** +正式发行:**NOT RELEASED,不得标记 V27 PASS;保留 Retained 构建/复用/合成** +上版:`V26_Metal混合Segment视觉错误_Retained保留修复调查报告_v2.md`(CommandBuffer 候选 F 真机失败) + +## 1. 已证实事实 + +- Pleiades iPad mini 5;Native 旧 V26 F,及第一版 CommandBuffer 候选 F,均真机 FAIL:背景 Retained 真实 `Build/Reuse/Composite`,但光弧或图标被放大、航向线消失、天气开关异常。旧版本 E(全 Continuous)多次由用户确认正常。 +- 第一次候选 Native F 快照保留于 `/tmp/navsea-v27-retained-ipad-stage/F-1851/start.json` 与 `end.json`,其中 Retained R0 `[0,1)`、C1 `[1,118)`、`build=13`、`reuse=1069`、`actualExecution=composite`、`valid=true`、`fallback=false`。 +- 不能据此认定 CommandBuffer 是根因。原候选 `MapLibre.dynamic.xcframework.zip` SHA-256 为 `b89bc06bf03eba20a5790da21215fe7e167da20b3fdfa6a34782420bd503a354`;旧候选补丁备份在 `/tmp/navsea-v27-retained-ipad-stage/patches/V27_candidate1_commandbuffer_plus_current_context.patch`。 + +## 2. 本次单变量改动 + +恢复前一版 CommandBuffer 改动的七个源码/测试文件,原 Candidate 1 不再参与当前源码编译。仅两个文件相对 `particle/main` HEAD `b0f34cc9a2` 发生功能代码修改: + +- `src/mln/mtl/context.cpp`:`Context::renderTileClippingMasks` 的 Metal Stencil ID 设置改走 `mtlRenderPass.setStencilReference(...)`,不再绕过 RenderPass 缓存直接调用 `encoder->setStencilReferenceValue`。 +- `src/mln/mtl/tile_layer_group.cpp`:3D Stencil Reference 也走 RenderPass 缓存,删除不再使用的局部 encoder 引用。 + +代码已确认存在 Metal 原先绕过 RenderPass 状态缓存的路径;其与 F 画面错误的因果关系**尚待真机结果**。没有变更 Retained Cache 或共享 RN API,未禁用 Retained,未引入 JS Android/iOS 平台分流。 + +## 3. 新 Native 制品与安装 + +- Mac `/Users/tei/dev/MapLibreParticleLab`,`bazel build -c opt --//:renderer=metal //platform/ios:MapLibre.dynamic`,exit 0,约 49 秒;`git diff --check` PASS。 +- 新 Metal ZIP SHA-256:`5a8bef4e8419ae2b8c5bf22253361d40a4ed7ffd7c156ea73d5df8eae72b3393`。 +- 独立保留于 `/tmp/navsea-v27-retained-ipad-stage/candidate2_stencil/MapLibre.dynamic.xcframework.zip`;最小源码 patch 同目录 `candidate2_stencil_only.patch`。 +- RDC Mac 使用现有 `scripts/ios-install-test-device.sh` 的 `scripts/pass.txt` 所对应 Keychain 与签名身份,在暂存 App 替换完整 MapLibre.framework,重新签名并严格验证;覆盖安装 Pleiades `com.navsea.app` 成功,`devicectl process launch` 成功。 +- 无卸载、无清数据、无 SwiftPM V26 正式仓库/Package 更改、无发布、无 Git Commit/Tag。 +- 安装后 E Native 快照:`/tmp/navsea-v27-retained-ipad-stage/candidate2_stencil/E-preF/start.json`,时间 `2026-10-13T12:00:50Z`,`valid=true`,0 Retained / 1 Continuous、`[0,118)` Live。仅验证了运行时模式,并非用户视觉结果。 + +## 4. 第二版 F 真机结果(失败,已停止测试) + +- 用户于 2026-10-13 21:09~21:10 JST 确认 **F 仍有大图标,没有改进,并补充报告“也不移动了”**。目前“无法移动”是手势没有作用,还是相机已变但视觉没有更新,仍需通过输入事件/相机指标判定,不得臆测。 +- 从 Pleiades 实际取得 `Library/Caches/navsea-render-segment-start.json` 与 `end.json`,已保存在 `/tmp/navsea-v27-retained-ipad-stage/candidate2_stencil/F-failed-20261013-2110/`。其中 capturedAt `2026-10-13T12:10:12Z`、`generation=4`、`mapReactTag=804`、`styleLayerCount=118`、`valid=true/fallback=false`;R0 `[0,1)` `background`,`actualExecution=composite`、`build=14`、`reuse=6076`、`invalidation=14`;C1 `[1,118)` `actualExecution=live`;`fullCpuFrame=6231`。帧计数不等于相机更新证明。 +- 用户报告画面失败,Native 配置/执行链未发生全 Live 回退,因此第二版 Stencil 修正 **FAIL**。没有进入 RC,没有发布 V27。 +- Windows Metro 当前统一 Debug 开关已经恢复为 `E_CONTINUOUS_ONLY`,iOS Bundle HTTP 200,Android 同一 JS 源配置;用户需冷启动 iPad 才能加载 E。**用户 2026-10-13 21:18 JST 已再次确认本次切回 E 后正常,包括地图移动;因此 E 恢复 PASS。** +- 失败的第二版源码 diff 另存 `/tmp/navsea-v27-retained-ipad-stage/candidate2_stencil/candidate2_failed_stencil_source.patch`。已只针对两个改动的 Metal 源文件 `git restore` 回 `particle/main` HEAD;无其他功能文件变更,报告保留,iPad 上仍是已安装的 Candidate 2 二进制(仅 JS 回 E),不等于已更换 Native。 +- 下一轮调查把相机/手势事件、Native 相机矩阵变化、Retained 的 `camera-or-boundary` invalidation、主画面真实绘制与 GPU 合成可视化分开验证,再制定单一变量修复;禁止无验证继续叠加补丁。 + +## 5. 范围与发布约束 + +- 仅本地候选,不代表 V27 已发行。 +- 不接受关闭 Retained、全 Continuous 降级或 JS 平台分流作为最终修复。 +- 若 F 依旧失败,下一轮应进一步检查 Retained Composite 顶点/纹理与 Metal Encoder 状态、Stencil/Depth、主画面 RenderPass,并用证据区分构建与合成副作用。 diff --git a/docs/报告/V26_Metal混合Segment视觉错误_安全降级调查报告_v1.md b/docs/报告/V26_Metal混合Segment视觉错误_安全降级调查报告_v1.md new file mode 100644 index 000000000..11f79bc42 --- /dev/null +++ b/docs/报告/V26_Metal混合Segment视觉错误_安全降级调查报告_v1.md @@ -0,0 +1,58 @@ +# V26 Metal 混合 Segment 视觉错误与第一阶段安全降级调查报告 v1 + +**已撤回:2026-10-13 用户明确要求必须保留 Retained 实际构建/复用/合成。本文安全降级代码已在 Particle 工作区撤销,禁止按本文编译或发布。请以同主题 v2 修复调查报告为准。** + +日期:2026-10-13 +状态:**已撤回(REJECTED)——原代码级安全降级方案不可用;保留本文件仅作历史记录** +目标仓库:`MapLibreParticleLab` +冻结 V26 源码:`fce326a9009d3fc21eb91c5df362f0769163d07e`(SOURCE_LOCK),当前工作分支 `particle/main`,修改前 HEAD `b0f34cc9a299a75af1f7e708c63d75eaa42e9ca2`。 + +## 一、iPad 实测事实 + +iPad:Pleiades(iPad mini 5);iOS 已安装 V26 Native。实验使用 Windows CARBON7i `D:\React\NavSea` 的 Metro `192.168.200.100:8081`,而非 Mac 本地 Metro。 + +- 原始 RC:天气 ON 后本船/AIS/海图闪烁,天气 OFF 后仍异常。 +- A(关闭 Retained,完整天气):不闪烁。 +- B(保留 Retained,隐藏 Native 天气):不闪烁;色场与粒子均无显示。 +- C(只有色场,保留 Retained):色场出现,闪烁。 +- D(只有粒子,保留 Retained):闪烁。 +- E(Retained 总开关开、实际 0 Retained + 1 Continuous):不闪、天气色场和粒子完整,本船/AIS 图标尺寸与航线正常。 +- F(最底层 background 单层 Retained + 其余 114 层 Continuous):冷启动即图标异常放大、本船航行线与 AIS 航线消失;不闪;天气色场与粒子正常。**F 是失败,不得据其不闪而判 PASS**。 + +iPad Native 快照原始保存于 Mac: +`/tmp/navsea-v26-ipad-render-20261013/E-0746/start.json`、 +`/tmp/navsea-v26-ipad-render-20261013/F-0740/start.json`; +F:`enabled=true`, `valid=true`, `fallback=false`; R0 `[0,1)` 实际 `composite`,C1 `[1,115)` 实际 `live`; +E:`enabled=true`, `valid=true`, `fallback=false`; 单 Continuous `[0,115)` 实际 `live`。 +旧 RCR 实验曾因 JS 预检将最后 Layer 硬编码为 `chartDangerScanCenterLineLayer`,但实际最后 Layer 是 `route-history-dotted-line`,回退到 RC。不要把当时现场当成 RCR Native 执行证明。 + +## 二、已核对的 Native 代码路径 + +`src/mln/renderer/renderer_impl.cpp` 中,实际含 Retained Segment 的绘制在独立 RenderPass 中构建离屏缓存,调用 `RetainedUnderlayCache::composite`,然后绘制 Continuous 图层。 +`src/mln/renderer/retained_underlay_cache.cpp` 的缓存创建与合成对应 `prepareResources`、`beginBuild`、`finishBuild`、`composite`。 +iOS Metal `src/mln/mtl/render_pass.cpp` 已有 Color/Depth/Stencil Clear 逻辑,尚不能证明清屏本身出错。 +现有证据将异常定位在 **实际执行 Retained 离屏构建/合成和连续图层共存时**,但尚未证明具体 GPU 状态或 shader 错误。 + +## 三、第一阶段保护性补丁(尚未发布) + +1. `include/mln/renderer/render_segment.hpp`:新增 `requiresMetalRetainedCompositeSafetyFallback`,针对合法 Plan 内任何 RETAINED Segment + Metal 后端启用安全回退;全 Continuous 和非 Metal 保留原逻辑。 +2. `src/mln/renderer/renderer_impl.cpp`:Native 检测到上述情形时,禁止 Retained 离屏资源构建、Reuse、合成;释放历史缓存;完整 Style 范围做常规 Live Opaque+Translucent 绘制。阻止 Particle FastFrame 在该组合误准入。Native 诊断 `globalLiveFallbackReason` 记录 `metal-retained-composite-safety`,`actualExecution` 应全部为 `live`。 +3. `test/renderer/retained_underlay_cache.test.cpp`:加入 OpenGL 不启用、Metal R-C 启用、Metal全Continuous不启用、Metal全Retained启用、无效Plan不启用的安全策略单测。 + +**此补丁属于 correctness-first 的暂时降级方案,不是 Retained 合成根因修复,也不提供 Retained GPU 加速。** 禁止在未做真实 Metal 回归前声称问题已关闭。Native 公共 JS API 与输入 Contract 不变;Renderer 层仍以 Style Z-order 为唯一图层顺序。 + +## 四、待用户手工验收 + +1. 保留冻结 V26 artifact,不覆盖正式发行版本;从当前修改源码建立新的测试版 iOS Metal XCFramework / Test Host,不把未验证产物标成 V26 正式版本。用户手工完成 iOS 构建、安装、启动。 +2. 用统一 JS 测试配置加载含 Retained Segment 的 RC/F 方案,确认 Native 诊断显示 `global-live`、`metal-retained-composite-safety`,且无 Retained texture build/composite;不能只凭 JS mode badge 判断。 +3. 冷启动关天气、ON、OFF,检查本船/AIS 图标尺寸、航行线、海图层、天气色场、粒子、图层 Z-order、视觉稳定。 +4. Android OpenGL 使用完全相同的 JS Plan 对照,确认没有触发 Metal 安全回退;记录 native 版本差异,版本未对齐时不得直接宣称平台差异。 +5. 记录帧率、CPU、GPU、内存和耗电;补丁性能可能低于 Retained 缓存方案。 +6. 后续通过 Metal GPU Capture/离屏纹理、Blend、Depth/Stencil 和 Drawables 生命周期调查根因,修复后再移除安全降级,并对 R-C/R-C-R、多 Retained 段复测。 + +## 五、执行边界与状态 + +- 本轮没有执行 iOS/Android 原生编译、APK/XCFramework 安装、ADB、真机自动操作,也没有发布 tag/制品。 +- 粒子仓库工作区变更先保持未提交,避免污染冻结 V26。 +- Windows NavSea Debug JS 的平台分流已移除:Android/iOS 共用 `E_CONTINUOUS_ONLY`,Release 仍为 `BASELINE`。这项改动仍需 Android 用户手工真机核验。 +- 全部修复最终验收需要跨项目 Integration 按既定 Contract/Status 更新,不由 NavSea 独自宣告 Particle 发布完成。 diff --git a/src/mln/shaders/mtl/retained_underlay_composite.cpp b/src/mln/shaders/mtl/retained_underlay_composite.cpp index 4c81753ae..a7834aefe 100644 --- a/src/mln/shaders/mtl/retained_underlay_composite.cpp +++ b/src/mln/shaders/mtl/retained_underlay_composite.cpp @@ -4,7 +4,7 @@ namespace mln::shaders { using Source = ShaderSource; const std::array Source::attributes = { - AttributeInfo{0, gfx::AttributeDataType::Short2, 0, idRetainedUnderlayCompositePosVertexAttribute}, + AttributeInfo{0, gfx::AttributeDataType::Short2, maxUBOCountPerShader + 0, idRetainedUnderlayCompositePosVertexAttribute}, }; const std::array Source::textures = { TextureInfo{0, idRetainedUnderlayCompositeTexture},