Files
Sprinter-SDCC/libbgi/common/sprite_frame.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

35 lines
1.8 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.
/*
* sprite_frame — сменить кадр атласа: запомнить (sx,sy) и пересчитать
* кэш src = img + 4 + sx + sy*stride (адрес первого пикселя кадра).
*
* Кэш src — то, что реально использует блит (без чтения заголовка и
* умножения на каждый кадр экрана); здесь единственное место, где
* (sx,sy) конвертируются в адрес «дорогим» путём — одно 16-бит
* умножение (__mulint; при sy < 256 он сам идёт коротким 8-итерационным
* путём). Смена кадра — редкая операция рядом с блитом.
*
* Указатель НЕ разыменовывается — для W0-атласов пересчёт безопасен
* без подключения страницы (вся арифметика на кэше stride из init).
*
* Была inline-функцией (только sx/sy+dirty) до появления кэша src
* (2026-07-14, оптимизация B: blit 11.8К → ~10К тактов/спрайт).
*/
#include "../_bgi.h"
#include <sprite.h>
void sprite_frame(sprite_t *s, int sx, int sy)
{
/* «Кадр не сменился» определяет src, НЕ (sx,sy): тикер анимации
* двигает src, оставляя sx/sy нетронутыми, поэтому старые sx/sy
* могут совпасть с запрошенными при уехавшем src. */
const uint8_t *src = (const uint8_t *)s->img + 4 + sx
+ (uint16_t)sy * s->stride;
s->sx = sx;
s->sy = sy;
if (s->src != src) {
s->src = src;
s->flags |= _SPR_DIRTY;
}
}