Files
Sprinter-SDCC/libbgi/common/_gfx_blit_sprite_noclip.c
T
snark13 b9ddce8d34 libbgi: спрайты — кэш адреса кадра, DDA+asm тик, Y-сортировка со слоями
Оптимизации A+B (профиль rpgwalk-15: активная часть кадра 410К → 307К
тактов из 430080; лимит спрайтов 16×16 на стабильные 48.8 fps: 14 → ~21):

- (B) sprite_t.src/stride — готовый адрес кадра: считают только
  sprite_frame (теперь функция, одно умножение на СМЕНУ кадра) и тикер
  (±an_step БАЙТ инкрементально); блит-ядра принимают src+stride,
  img/sx/sy из сигнатуры ушли.  Блит 177К → 146К на кадр.
- (A) тик 100К → 37.7К: tween переформулирован Брезенхэм → беззнаковый
  DDA (mv_rem/mv_acc, «приехали» = rem==0 — без знаковых 16-бит
  сравнений), tick_move и tick_anim — ручной asm (SDCC спиллит такие
  функции в IX-фрейм ~100 обращений; C-реструктуризации не помогали —
  проверено кодогеном).  Биты an_flags переименованы по категориям
  (_SPR_STRIP_HORZ, _SPR_PP_BACK).

Y-сортировка (gfx_sprite_ysort, идеи пользователя — 8-бит ключ,
персистентность):

- painter's algorithm по ключу {layer:8, clamp_y:8}; поле
  sprite_t.layer (в КОНЦЕ структуры — asm-офсеты не сдвигает): слои
  сцены в одном массиве/одном sprite_update;
- ПЕРСИСТЕНТНАЯ asm-таблица {key16, ptr16}: resort порядка прошлого
  кадра (почти линейно), rebuild при смене arr/count; массив
  приложения не трогается; ~28К/15 спрайтов (с нуля было 44К);
- компоненты YSORT_Y/YSORT_LAYER отключаемы независимо масками ключа
  (без ветвлений в сортировщике); ВНИМАНИЕ: mode=1 значит Y-only,
  полный порядок = YSORT_Y|YSORT_LAYER;
- funcptr-DCE: выключено = код и таблица не линкуются (rpgwalk −190 Б);
- ПРАВИЛО в sprite.h: два sprite_update на страницу запрещены (heal
  второй группы стирает спрайты первой — ОЗУ-копия чистая).

Попутные фиксы:

- libbgi/Makefile: .rel зависят от заголовков (HDRS) — stale .rel со
  старой раскладкой sprite_t молча ломал рантайм;
- rpgprof: --memory small (перерос tiny: BSS вылезал за W2 → мгновенный
  «Unexpected application termination»; mkexe это пока не ловит);
- tests/spranim: проверки переведены на кэш src, добавлены T6 (reframe
  после тикера) и T7 (Y-сортировка: порядок, слои, персистентный
  resort, LAYER-only) — 7/7 PASS в MAME.

Доки: §9д — новый бюджет (19.5К/спрайт), §9е — ПЛАН FPS-делителя
(frame pacing, gfx_set_fps_div); TODO — дизайн цепочки irq-обработчиков.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 17:19:24 +03:00

31 lines
2.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* _gfx_blit_sprite_noclip — блит спрайта БЕЗ клипа по экрану. Как
* _gfx_blit_sprite, но клип-математики нет ВООБЩЕ (не скип проверки —
* кода нет физически): приложение ГАРАНТИРУЕТ, что спрайт целиком на
* экране (флаг gfx_sprite_clip(0)). Отдельная функция, а не ветка в
* _gfx_blit_sprite: без клип-кода SDCC тесней раскладывает регистры
* (меньше спиллов) — отсюда полный выигрыш ~+19% на анимации (замер
* A/B на examples/balls 2026-07-12), а не только скип сравнений.
*
* ВНИМАНИЕ: запись за край экрана портит соседнюю память (Port_Y за 256
* заворачивается наверх, x за 320 — в соседнюю строку/страницу; см.
* tests/spriteclip). Вызывать только когда спрайт заведомо в пределах.
*
* src — ГОТОВЫЙ адрес первого пикселя кадра, stride — шаг строки
* источника (полная ширина img); считает вызывающий один раз (кэш
* sprite_t.src/stride ведут sprite_frame и тикер; putsprite — из
* заголовка). До 2026-07-14 здесь были img+sx/sy с пересчётом адреса
* на КАЖДЫЙ блит (цикл O(sy), потом __mulint) — профиль rpgwalk снёс
* весь пересчёт в редкие точки смены кадра.
*
* БЕЗ _bgi_begin/_bgi_end и смены банка (держит вызывающий).
*/
#include "../_bgi.h"
void _gfx_blit_sprite_noclip(int x, int y, const uint8_t *src,
uint16_t stride, uint8_t w, uint8_t h)
{
_bgi_blit_rows_raw(src, (uint8_t *)(_gfx_addr_base + x),
w, h, (int)stride, (uint8_t)y);
}