libbgi: спрайтовый движок v2 + accel-блит/heal leaf'ы + noclip-путь
Спрайтовая графика поверх accel block-copy (docs/sprite-api-design.md): - Ядро блиттинга: leaf'ы _bgi_blit_rows_raw (dst фикс, только src-страйд) / _bgi_copy_rows_raw (getimage) / _bgi_heal_rows_raw (src==dst). DI один на спрайт (санкция: малый спрайт под одним DI аудио не рвёт); src[0]-фикс снят (точная MAME подавляет CPU-байт триггера записи — на железе перепроверить; для heal был избыточен и снят безусловно). - Общие bracket-free ядра _gfx_blit_full/_gfx_heal_full (полная ширина: клип по экрану + split >256 для putimage) + лин _gfx_blit_sprite/ _gfx_heal_sprite (кадр ≤64, без split, 8-бит w/h) + noclip-варианты (клип-кода нет → полный codegen-win). Имя *_full (не *_clip) — «clip» двусмысленно (sprite-ядра тоже клипуют; различитель — ширина/split). - Движок retained-модели <sprite.h>: sprite_init/update/flip + inline move/frame/show/hide/touch; drawn[2] per-page внутри структуры; кадр — двухпроходно heal ВСЕ -> блит ВСЕ под одной W3-скобкой/банком на проход. - Флаг gfx_sprite_clip(on/off): приложение, само следящее за границами, отключает клип (~+19% на анимации; диспетч пока через if — funcptr далее). - putsprite/movesprite/gfx_blit/putimage(COPY)/getimage переведены на ядро. Тесты: examples/balls (движок, дабл-буфер, boundary-тест клипа), tests/sprites (RAM PASS, клип 4 края, атлас), tests/blitw (trig-leak), tests/spriteclip (hardware-probe: железо НЕ режет за краем -> клип нужен), tests/blitperf, tests/gfxbanks. size-baseline обновлён (53 программы). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+104
-13
@@ -84,27 +84,110 @@ Quick wins:
|
||||
- [ ] кэширование block-size между burst'ами (accel помнит размер)
|
||||
|
||||
Новые возможности:
|
||||
- [ ] **пакетное чтение/запись массива байт через акселератор** —
|
||||
отдельные ф-ии, которые в ОДНОМ вызове читают/пишут строку или
|
||||
столбец пикселей (гориз./верт.), а не по пикселю через
|
||||
_bgi_read_raw/_bgi_plot_raw. Аналогично тому, как сейчас рисуются
|
||||
линии (_bgi_hspan_raw/_bgi_vspan_raw задают Port_Y и адрес один раз,
|
||||
далее burst). Цель — ускорить блиттинг: getimage/putimage сейчас
|
||||
зовут _bgi_read_raw/_gfx_getpixel256_raw по-пиксельно, из-за чего на
|
||||
каждый пиксель повторяются out Port_Y + ld bc,(base) + bounds-check
|
||||
(см. разбор _bgi_read_raw). Дать `_bgi_hread_raw`/`_bgi_hwrite_raw`
|
||||
(+ vertical) с ABI как у fill-сегментов (HL=addr, B=len, E=y, буфер
|
||||
в DE) и перевести getimage/putimage на них.
|
||||
- [ ] `gfx_blit` / `gfx_blit_transparent` — block copy (LD L,L / LD A,A),
|
||||
прозрачность через bank 0x58 («FF is transparent»)
|
||||
- [x] ~~пакетное чтение/запись массива байт через акселератор~~ —
|
||||
сделано 2026-07-11 (Фаза A спрайтового дизайна,
|
||||
docs/sprite-api-design.md): leaf `_bgi_copy_rows_raw` (accel
|
||||
block-copy LD L,L, до 256 байт/burst, размер блока армируется
|
||||
один раз) + ядро `gfx_blit_part` (клиппинг, полосы ≤256);
|
||||
putimage(COPY_PUT) и getimage переведены (регресс tests/bgi_img
|
||||
1:1 с per-pixel эталоном, подрежимы банков на accel-пути —
|
||||
tests/gfxbanks).
|
||||
- [ ] **вертикальный copy-leaf `_bgi_copy_cols_raw`** (режим LD A,A —
|
||||
вертикальная копия, Port_Y двигается сам как у vfill). Анализ
|
||||
2026-07-12: для СПРАЙТОВ требует column-major хранения (читать
|
||||
линейный буфер вертикально нельзя — Port_Y не действует вне
|
||||
видеоокна; референс docs/samples/balls пишет колонками и потому
|
||||
выводит спрайт ТРАНСПОНИРОВАННЫМ — незаметно только на
|
||||
симметричном шаре) → формат несовместим с getimage, не делать.
|
||||
А вот **вертикальный HEAL формат-независим** (экран→экран) и
|
||||
выгоден для узких высоких областей: L-полоска 2×16 при
|
||||
горизонтальном движении спрайта = 2 burst'а вместо 16 (8×
|
||||
меньше оверхеда); выбор ориентации по форме — как в
|
||||
_gfx_rectfill256. Нюанс: между read и write колонки Port_Y
|
||||
надо вернуть на y0 (стоп → OUT → ре-арм, ~20Т/колонку).
|
||||
Делать вместе с L-strip оптимизацией heal.
|
||||
- [ ] **паттерны через акселератор + FF-прозрачность** (идея 2026-07-11):
|
||||
всё, что сейчас рисуется по-пиксельно из-за «дырок», можно гнать
|
||||
burst'ами через банк 0x58 — дырки паттерна кодируются 0xFF и
|
||||
отбрасываются железом на записи:
|
||||
- стилизованные линии (_bgi_styled_line: DOTTED/DASHED/CENTER/
|
||||
USERBIT сейчас per-pixel): построить 16-байтовый шаблон строки
|
||||
из 16-бит маски (бит=цвет, 0=0xFF) и повторять accel-copy;
|
||||
- fill-паттерны (_bgi_fill_span, LTSLASH_FILL и пр.): 8-байтовые
|
||||
строки-шаблоны 8×8 паттерна тем же способом;
|
||||
- ВНИМАНИЕ: на MAME 0.283 запись FF через 0x58 портит теневое
|
||||
ОЗУ (частичный скип, см. sprite-api-design «Результаты Фазы 0»)
|
||||
— включать после подтверждения полного подавления на железе,
|
||||
либо через 0x5C + пере-heal.
|
||||
- [x] ~~gfx_blit / putsprite / gfx_heal / movesprite~~ — Фаза B
|
||||
сделана 2026-07-11 (docs/sprite-api-design.md §3): GFX_BANK_*
|
||||
константы + gfx_blit/gfx_blit_part/gfx_heal в gfx.h,
|
||||
putsprite/movesprite в graphics.h; проверено tests/sprites
|
||||
(прозрачность, клиппинг 4 краёв, heal src==dst, чистый след
|
||||
movesprite, атлас). Осталась Фаза C — пример-курсор.
|
||||
- [ ] **managed-движок спрайтов v2** — sprite_t (drawn-координаты
|
||||
per-page ВНУТРИ структуры) + retained-модель:
|
||||
sprite_move/frame/show/hide меняют только state,
|
||||
sprite_update(arr,n) делает «heal все → блит все» на текущей
|
||||
draw-странице, sprite_flip — сахар с vsync+флипом; z-order =
|
||||
индекс массива. Интегрирует спрайты со сменой страниц —
|
||||
снимает с приложения учёт drawn[2][N] (источник багов,
|
||||
examples/balls 2026-07-11). Полный эскиз:
|
||||
docs/sprite-api-design.md §9.1.
|
||||
- [ ] `gfx_xor_rect` / `gfx_or_rect` / `gfx_and_rect` / `gfx_invert_rect`
|
||||
- [ ] шрифты ≠ 8×8: gfx_set_font_data(ptr,w,h,advance), proportional,
|
||||
8×16/16×16, отдельный font_id API; font-quad для 640×256
|
||||
(per-cell палитра через дескрипторы 0x0300..0x039F)
|
||||
|
||||
Оптимизации (не сейчас):
|
||||
- [x] ~~stride-арифметика в _bgi_copy_rows_raw — вон из горячего
|
||||
цикла~~ — сделано 2026-07-12 (профиль examples/balls vs
|
||||
docs/samples/balls): универсальный leaf — SMC-патч страйдов в
|
||||
8-битные add/adc-цепочки при входе (140Т → 73Т/строку, BC/push/
|
||||
pop/ex ушли); heal — отдельный `_bgi_heal_rows_raw` без адресной
|
||||
арифметики вообще (77Т/строку против 204Т; референсный уровень).
|
||||
Шар 16×16: ~9.6 → ~6.6 кТ (heal 16×173 + блит 16×236). Регресс:
|
||||
blitw/bgi_img/sprites/balls — 1:1. Остатки разрыва с asm-
|
||||
референсом — C-обвязка вызовов (batch-пасс, см. ниже) и спец-
|
||||
blit-leaf (dstride 0: −30Т/строку — делать по замеру).
|
||||
- [ ] **batch-пасс для спрайтов** (движок v2, §9.1) — В РАБОТЕ 2026-07-12.
|
||||
Разрыв ~2× с asm-референсом (docs/samples/balls) — это НЕ
|
||||
W3-скобка (_bgi_begin = 5 инструкций, ~35Т), а per-call
|
||||
C-обвязка, повторяемая 2×N раз: полный клип (528/362 Б),
|
||||
парс заголовка img, save/restore банка, SDCC-фрейм 7-арг вызова.
|
||||
Референс всё это платит ОДИН раз на проход: W3 замаплен на видео
|
||||
на всю программу (в цикле не трогает), банк-подрежим ставит один
|
||||
`out` на проход (0x5C рисовать всё → 0x50 лечить всё), DI держит
|
||||
на весь проход. Движок v2 повторяет это (per-pass W3/банк/клип-
|
||||
fast-path, адресация из кэша w,h в структуре), НО DI оставляем
|
||||
гранулярным (по строке в leaf) — референс держит длинный DI
|
||||
только потому, что у него нет аудио-ISR; под CBL/IM2 длинный DI
|
||||
сорвёт звук (docs/accel-fill-budget.md).
|
||||
- [ ] клип fast-path «прямоугольник целиком на экране» в gfx_blit_part
|
||||
(4 сравнения вместо полного пути) — делается в _gfx_blit_full/
|
||||
_gfx_heal_full вместе с batch-пассом.
|
||||
- [ ] размер: общий clip-хелпер для gfx_blit_part (528 Б) и gfx_heal
|
||||
(362 Б) — клиппинг сейчас продублирован; кандидат −300..400 Б.
|
||||
- [ ] gfx_line через accel для пологих диагоналей (runs ≥ 4-5 px)
|
||||
- [ ] композитные примитивы с одним W3-swap на операцию
|
||||
- [ ] ~~**спрайт-анимация: heal только открывшейся L-полоски**~~ —
|
||||
ОТВЕРГНУТО 2026-07-12 для ПРОЗРАЧНЫХ спрайтов (наш случай).
|
||||
L-полоска (bbox старой позиции минус новой) корректна ТОЛЬКО для
|
||||
непрозрачного full-box спрайта: тогда зону перекрытия целиком
|
||||
перекрашивает новый блит. У прозрачного спрайта (0xFF через
|
||||
0x5C) в перекрытии дырки нового кадра НЕ перекрывают старые
|
||||
непрозрачные пиксели → на хвосте остаётся «полумесяц» старого
|
||||
изображения ВНУТРИ bbox-перекрытия, куда L-полоска не достаёт.
|
||||
Точный «новооткрытый» набор = old_opaque AND NOT new_opaque —
|
||||
это heal-с-маской по форме, а не по bbox, на Z80 дороже самого
|
||||
heal. Подтверждено чтением референса docs/samples/balls:
|
||||
restore_bg лечит ПОЛНЫЙ 16×16 (ld b,16 + 16 байт/строку),
|
||||
L-полоску не использует. Итог: heal остаётся full-box; экономия
|
||||
только через batch-пасс (амортизация обвязки), не через L-полоску.
|
||||
L-полоска годна лишь для непрозрачных тайлов фона — не спрайтов.
|
||||
Дисциплина «heal ВСЕ → блит ВСЕ» по-прежнему обязательна (слияние
|
||||
heal+блит per-sprite выкусывает соседа на перекрытии — проверено
|
||||
examples/balls 2026-07-11).
|
||||
- [ ] **span-примитивы для узких прямоугольников**: при узкой стороне
|
||||
≤ 8 линий chunked-rectfill проигрывает простому циклу
|
||||
_bgi_hspan_raw/_bgi_vspan_raw (подготовка+precompute ~340Т уходят
|
||||
@@ -134,6 +217,14 @@ Quick wins:
|
||||
Y..Y+15): в MAME 0.283 НЕ работает (2026-06-01). На железе:
|
||||
dual-write тест → если работает, кэшировать Port_Y в putpixel
|
||||
(~8× меньше OUT для Брезенхэма); если нет — вычистить из доков.
|
||||
- [ ] **Подрежимы видеостраниц #50..#5F** (tests/gfxbanks +
|
||||
tests/sprites, дизайн docs/sprite-api-design.md): перепрогнать
|
||||
на железе и сверить со скриншотами MAME. Три открытых вопроса: (1) 0x58 — полное
|
||||
подавление FF-записи (док/master-MAME) или FF протекает в
|
||||
теневое ОЗУ (MAME 0.283)? (2) FF в VRAM — цвет 255 (MAME) или
|
||||
display-подстановка фона из ОЗУ (тогда дешёвое стирание
|
||||
FF-заливкой, tests/fferase)? (3) accel-путь = CPU-пути (в ПЛМ
|
||||
пути физически разные).
|
||||
- [ ] fdmax: лимит манипуляторов и зависание 9-го OPEN — MAME vs железо.
|
||||
- [ ] CBL: щелчок перед первым проигрыванием звука за сессию (tests/
|
||||
cbltest, tests/cblwav) — воспроизводится ТОЛЬКО на первом запуске
|
||||
|
||||
Reference in New Issue
Block a user