From 17c41b32de69aef9b35db81aa78857210c1f4081 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Wed, 19 Aug 2026 15:40:30 +0300 Subject: [PATCH] =?UTF-8?q?P15=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8?= =?UTF-8?q?=D1=81=D0=B0=D0=BD:=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA=D1=80?= =?UTF-8?q?=D1=8B=D1=82=D0=B8=D1=8F=20=D0=BD=D0=B5=D1=82,=20=D0=B2=D0=B8?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=B0=D1=82=D0=B0=20=D0=B3=D1=80=D1=83=D0=B1?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D1=8C=20=D0=BC=D0=B5=D1=82=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Пользователь поймал ошибку в моём расчёте, глядя на экран: страж целиком правее пламени, пересекаться может только меч. Проверка по памяти машины подтвердила и уточнила: страж, спрайт x 257..284 y 18..56 страж, клинок x 241..261 y 31..37 пламя факела x 232..247 y 5..22 Ошибок было две. Первая: координаты пламени я взял по предположению «факел в колонке 7», а он в колонке 6 (пламя рисуется в ячейке правого соседа). Вторая, содержательная: ФИЗИЧЕСКОГО ПЕРЕКРЫТИЯ НЕТ ВООБЩЕ — по x клинок и пламя пересекаются, но по y между ними девять пикселей зазора. Настоящая причина: pop_cd_touch хранит метку как маску КОЛОНОК по 32 px на ТРИ ряда по 63 px (cd_row_of). Пламя (y 5..22) и клинок (y 31..37) попадают в один ряд 0 и одну колонку 7 — cd_quiet считает слот задетым. 148 302 такта, 23 % кадра, за ложную тревогу. Решение стало проще и точнее: хранить на колонку диапазон y вместо номера ряда (10 x 2 байта x 2 страницы = 40 байт). Расчётом проверено, что это спасает стража и НЕ спасает Кида в тяжёлой позиции — там перекрытие настоящее, и он честно перерисовывается. Вариант с 8-пиксельными полосами тоже работает, 16-пиксельные уже нет. Прежние предложения (частичная перерисовка по пересечению, обрезка фона под персонажем) записаны как НЕ НУЖНЫЕ: они решали задачу, которой нет. Co-Authored-By: Claude Opus 5 --- applications/PoP/docs/perf_registry.md | 38 +++++++----- applications/PoP/roomtest/TASKS_OPEN.md | 78 +++++++++++++------------ 2 files changed, 64 insertions(+), 52 deletions(-) diff --git a/applications/PoP/docs/perf_registry.md b/applications/PoP/docs/perf_registry.md index 7170e06..d76d7f4 100644 --- a/applications/PoP/docs/perf_registry.md +++ b/applications/PoP/docs/perf_registry.md @@ -150,26 +150,34 @@ Новое, найдено 2026-08-19. -### P15. Неподвижный персонаж перерисовывается целиком — ОБЯЗАТЕЛЬНО [замер 2026-08-19] +### P15. Метка «фон трогали» слишком груба — ОБЯЗАТЕЛЬНО [замер 2026-08-19] **Постановка пользователя:** проверять, нужна ли отрисовка стража, когда он -не двигается. Если движется — лишние ~150 000 приемлемы: в оригинале во -время боя число физических кадров на логический тоже растёт на единицу. +не двигается. Если движется — лишние ~150 000 приемлемы (в оригинале при +бое число физических кадров на логический тоже растёт на единицу). -Измерено чтением `pop_cd` из памяти машины: - -| | прямоугольник | размер | +| объект | x | y | |---|---|---| -| страж | x 257..284, y 18..56 | 28 × 39 | -| пламя факела (0,7) | x 264..279, y 5..22 | 16 × 18 | -| **пересечение** | **x 264..279, y 18..22** | **16 × 5** | +| страж, спрайт | 257..284 | 18..56 | +| страж, клинок | 241..261 | 31..37 | +| пламя факела (колонка 6 → ячейка 7) | 232..247 | 5..22 | -**148 302 такта (23 % кадра) тратятся из-за 80 задетых пикселей на макушке.** -Пересечение при этом НАСТОЯЩЕЕ — дело не в грубости маски меток. +**Физического перекрытия нет:** по x клинок и пламя пересекаются, по y между +ними девять пикселей зазора. Стража будит гранулярность метки — +`pop_cd_touch` хранит маску колонок по 32 px на ТРИ ряда по 63 px, и пламя с +клинком слипаются в одном ряду. Цена ложной тревоги — **148 302 такта, +23 % кадра**. -Полная постановка с двумя вариантами решения (частичная перерисовка против -обрезки фона под персонажем) — `TASKS_OPEN.md`, якорь `char-partial-redraw`. -Смежное: **P14** — вторая половина той же цены. +Решение: хранить на колонку диапазон y вместо номера ряда (10 × 2 байта × 2 +страницы = 40 байт). Проверено расчётом, что это спасает стража и НЕ +спасает Кида в тяжёлой позиции — там перекрытие настоящее. + +**Осторожно с историей вопроса:** первая редакция этой позиции утверждала, +что перекрытие реальное (16 × 5 пикселей на макушке) и предлагала частичную +перерисовку. Это была ошибка — координаты пламени взяты по предположению +«факел в колонке 7» без проверки, а пользователь увидел на экране, что +страж целиком правее пламени. Полная постановка — `TASKS_OPEN.md`, якорь +`char-partial-redraw`. ### P14. Fore-проход персонажа — 62 778…89 000 НА ПЕРСОНАЖА [замер 2026-08-19] @@ -381,7 +389,7 @@ BUG-LATTICE-DOORTOP, BUG-SEAM-WEDGE-1) — только отдельным за | # | что | ожидание | риск | комментарий | |---|---|---:|---|---| -| **P15** | **неподвижный персонаж: не перерисовывать целиком** | **до −130 000** | средний | **ОБЯЗАТЕЛЬНАЯ** (пользователь, 2026-08-19). 148 302 из-за пересечения 16×5 | +| **P15** | **точность метки «фон трогали»: диапазон y на колонку** | **−148 302** | низкий | **ОБЯЗАТЕЛЬНАЯ** (пользователь, 2026-08-19). Перекрытия НЕТ, ложная тревога из-за рядов по 63 px | | **P14** | **fore-проход персонажа** — 62 778 у стража, ~89 000 у Кида | **до −80 000** | высокий | самая дорогая статья кадра (20 %). Включает P10 как первый шаг | | P4 | накладные блита (4 правки) | −28 000 | низкий | 6 126 фиксированных на любой блит | | P11 | мелочи с известной ценой | −26 000 | низкий | `clip_char_top` 8 658 подтверждён замером | diff --git a/applications/PoP/roomtest/TASKS_OPEN.md b/applications/PoP/roomtest/TASKS_OPEN.md index 49ca7a6..2d80050 100644 --- a/applications/PoP/roomtest/TASKS_OPEN.md +++ b/applications/PoP/roomtest/TASKS_OPEN.md @@ -261,58 +261,62 @@ MEM-BANK2, MEM-BANK5, CLIP-1, KBD-1, DBG-CHEATS. ## P0 — делаем сейчас -### CHAR-PARTIAL-REDRAW. Неподвижный персонаж перерисовывается целиком из-за нескольких пикселей — ОБЯЗАТЕЛЬНО +### CHAR-PARTIAL-REDRAW. Неподвижный персонаж перерисовывается зря: метка «фон трогали» слишком груба — ОБЯЗАТЕЛЬНО **Постановка (пользователь, 2026-08-19).** Проверять, нужна ли отрисовка стража, когда он НЕ ДВИГАЕТСЯ. Если движется — лишние ~150 000 тактов приемлемы: в оригинале во время боя число физических кадров на логический -тоже растёт на единицу, и прийти к тому же решению не страшно. +тоже растёт на единицу. -**Измерено** (сцена 11/15, MAME, чтение `pop_cd` из памяти): +**Измерено** (сцена 11/15, чтение `pop_cd` из памяти машины): -| | прямоугольник | размер | +| объект | x | y | |---|---|---| -| страж | x 257..284, y 18..56 | 28 × 39 | -| пламя правого факела (0,7) | x 264..279, y 5..22 | 16 × 18 | -| **пересечение** | **x 264..279, y 18..22** | **16 × 5** | +| страж, спрайт | 257..284 | 18..56 | +| страж, клинок (накладной) | 241..261 | 31..37 | +| пламя факела (колонка 6 → рисуется в ячейке 7) | 232..247 | 5..22 | -То есть пламя задевает страже только макушку — 80 пикселей, — а -перерисовывается он ЦЕЛИКОМ и стоит **148 302 такта** (85 524 сам спрайт с -клинком и снимком, 62 778 fore-проход). Это 23 % работы кадра. +**Физического перекрытия НЕТ.** По x клинок и пламя пересекаются +(241..247), но по y расходятся: пламя кончается на 22, клинок начинается с +31 — девять пикселей чистого зазора. Пользователь увидел это на экране +раньше, чем я в числах: «страж полностью правее пламени, только меч может +пересекаться». -Механика: `pop_torch_draw` запекает пламя в фон каждый кадр (`TORCH_ANIM_DIV -= 1`), `pop_cd_touch` метит область, `cd_quiet` видит пересечение с bbox -слота и снимает «тихий» бит — дальше `pop_char_draw` + `pop_char_fore` -идут полностью. Метка тут ни при чём: пересечение НАСТОЯЩЕЕ (проверено -числами выше, а не по грубой маске колонок). +**Почему тогда он перерисовывается.** Метка «фон трогали» (`pop_cd_touch` +в `pop_tile.c`) хранится как битовая маска КОЛОНОК по 32 px, отдельно на +каждый из ТРЁХ рядов по 63 px (`cd_row_of`). Пламя и клинок попадают в +один ряд 0 и в одну колонку 7 — и `cd_quiet` считает слот задетым. +Расплата: **148 302 такта, 23 % работы кадра** (85 524 спрайт с клинком и +снимком + 62 778 fore-проход) за перекрытие, которого нет. -**Варианты решения** (оба нуждаются в прототипе и замере): +**Решение — поднять вертикальную точность метки.** Вместо «маска колонок × +3 ряда» хранить на каждую колонку ДИАПАЗОН y (`ymin`/`ymax`): 10 колонок × +2 байта × 2 страницы = 40 байт. `pop_cd_touch` расширяет диапазон +затронутых колонок, `pop_cd_hit` проверяет пересечение диапазонов. -- **A. Частичная перерисовка.** Если сигнатура слота не изменилась, но - фон задет — восстановить только пересечение: блит нужных колонок/строк - спрайта (`gfx_blit_cols_part_w` это умеет) и fore-проход только по - задетым тайлам. Ожидание: вместо 148 302 порядка 20 000. - Риск: надо аккуратно с клинком и брызгами (у них свой прямоугольник) и с - порядком слоёв внутри пересечения. -- **B. Не рисовать фон там, где он всё равно перекрыт.** Пламя обрезать по - прямоугольнику неподвижного персонажа — визуально результат тот же (эти - строки под ним и не видны), а персонажа не надо трогать вовсе: - экономия все 148 302. **Опасность:** обрезанное пламя попадает в - ОЗУ-копию, и когда персонаж сдвинется, `heal` вернёт фон С ДЫРОЙ. - Годится, только если запекать в копию полное пламя, а обрезать лишь - запись в видео-ОЗУ — то есть нужен блит с двумя разными масками. +Проверка решения на обоих случаях сцены: -**Порядок:** сначала A (безопаснее и укладывается в существующий контракт -`pop_cd`), B — только если A не хватит. +- **страж:** клинок в колонках 7-8. В колонке 7 у метки лежит y 5..22 + (пламя), у клинка 31..37 — не пересекаются, колонку 8 пламя не трогало. + Слот остаётся «тихим» → экономия 148 302; +- **Кид в тяжёлой позиции:** пламя левого факела y 5..22, Кид y 15..55 — + пересечение НАСТОЯЩЕЕ, и он честно перерисовывается. Так и должно быть. -**Проверка:** замер 11/15 в ОБЕИХ позициях Кида (лёгкая x = 99 и тяжёлая -x = 106, разбор в [`../docs/perf_l11_room15.md`](../docs/perf_l11_room15.md) -§7) плюс визуальный прогон боя: персонаж не должен оставлять хвостов и -не должен просвечивать сквозь пламя. +Вариант с 8-пиксельными полосами вместо диапазонов тоже работает +(24 полосы), но 16-пиксельные УЖЕ НЕТ: пламя и клинок снова слипаются в +одной полосе. Диапазон на колонку точнее и не требует битовой возни. + +**Чего делать НЕ надо** (проверено расчётом, не повторять): частичную +перерисовку персонажа по пересечению и обрезку фона под ним. Обе правки +решают задачу, которой нет — перекрытия не существует. + +**Проверка:** замер 11/15 в обеих позициях Кида (лёгкая x = 99, тяжёлая +x = 106; разбор — [`../docs/perf_l11_room15.md`](../docs/perf_l11_room15.md) +§7), плюс визуальный прогон боя и прохода Кида под факелами: персонаж не +должен оставлять хвостов и не должен просвечивать сквозь пламя. **Место в очереди — [`../docs/perf_registry.md`](../docs/perf_registry.md), -позиция P15.** Смежное: P14 (fore-проход персонажа — вторая половина той же -цены). +позиция P15.** Смежное: P14 (fore-проход — вторая половина той же цены). ### HEAL-WIDTH. Ширина точечных heal'ов не совпадает со следом перерисовки — ОБЯЗАТЕЛЬНО