Массив популяций занимает nx*ny*9*4 байта и показывался шейдеру одной привязкой.
dzn объявляет max_storage_buffer_binding_size = 128 МиБ, поэтому потолок выходил
3.73 млн узлов: 14 прогонов кампании из 115 падали на создании bind group, а на
них приходится 70.7% её стоимости.
Ограничена при этом ровно привязка: max_buffer_size у dzn 2047 МиБ. Поэтому тот
же буфер теперь показывается девятью привязками, по одному направлению в каждой,
и потолок поднимается до 33.5 млн узлов — самая крупная сетка кампании
(4096x4096, 16.8 млн) проходит с запасом.
Как устроено:
* шаг между направлениями выровнен на 256 байт (dir_stride), потому что смещение
привязки обязано быть кратно min_storage_buffer_offset_alignment;
* обращения к популяциям в WGSL идут через fget/fset/pget/pset, а их тело
генерируется под вариант (build_shader);
* вариант выбирается по max_storage_buffer_binding_size адаптера — где предела
нет, собирается прежний общий, без switch в аксессорах;
* KBC2D_SPLIT_POPULATIONS=1 включает раздельные принудительно: иначе сверить два
варианта на одной карте нечем.
Проверено:
* на одном драйвере оба варианта дают одно и то же — Cd 2.39486, energy_end
5.74813e-05; раздельный стоит 5.7% пропускной способности;
* на сетке 2048x2048, где раздельные привязки и нужны, нативный прогон против
контейнерного: energy_end расходится на 2.4e-07, enstrophy_end на 1.3e-07;
* preflight в контейнере: 115 сценариев из 115, ни одного отказа (было 14);
* 29 собственных тестов решателя зелёные.
Образ опубликован как notbigghost/kbc2d:1.2.0 (он же latest).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>