Files
Sprinter-SDCC/libbgi/common/_gfx_blit_sprite.c
T
snark13 0eec977630 libbgi: sy*img_w умножением вместо O(sy)-цикла — rpgwalk 24 → стабильные 48 fps
Профилирование rpgwalk в dev-MAME (watchpoint на OUT-маркеры +
totalcycles) показало: blit 8 спрайтов ел 17 мс из 20.5 — 85% в цикле
`while (sy--) src += img_w;` blit-обёрток (писался под «обычно sy==0»,
а вертикальные ленты атласов дают sy до 176 → до 64К тактов на кадр).

Фикс: src += sy*img_w через __mulint (O(1); __mul16 адаптивен — при
sy < 256 крутит 8 итераций, ~500Т) + if (sy): горизонтальные ленты и
одиночные спрайты не платят и за умножение.  Blit: 45К → 11.8К
тактов/спрайт.

Замерен бюджет кадра (docs/sprite-api-design.md §9д): кадр 48.83 Гц =
430080 тактов @21МГц; спрайт 16×16 ≈ 26К (тик 7.3К + heal 6.4К +
blit 11.8К) → лимит стабильных 48 fps = 14 спрайтов (15 — 94% кадров,
16 — на грани).  FPS-плашка bar+outtextxy стоит ~210К (полкадра!) —
HUD рисовать putimage-заготовкой.

examples/rpgwalk/rpgprof.c — профилировочная копия демо: визуальный
профайлер (цвет бордера по секциям кадра) + маркеры для тактового
профайла через wpiset дебаггера (рецепт в шапке и §9д).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 21:54:37 +03:00

72 lines
4.4 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 — лин-ядро блита СПРАЙТА (кадр ≤ 64×64, см. sprite.h):
* то же, что _gfx_blit_full, но БЕЗ split'а на полосы >256 (спрайт
* заведомо ≤256 байт в строке) и с 8-битными w/h (дешевле передача
* аргументов). Один вызов _bgi_blit_rows_raw. БЕЗ _bgi_begin/_bgi_end
* и смены банка — вызывающий (sprite_update/putsprite) держит скобку и
* банк 0x5C. Проверок размера НЕТ (контракт ≤64 — в доке, не в коде).
*
* Клип по ЭКРАНУ остаётся (это функциональность — спрайты штатно уходят
* за края): fast-path «целиком на экране» = 4 сравнения.
*
* ФОРМАТ getimage и параметры атласа
* ----------------------------------
* img указывает на getimage-буфер: 4-байтовый заголовок + пиксели.
* Заголовок = uint16 width, uint16 height (little-endian):
* p[0]=width lo, p[1]=width hi, p[2]=height lo, p[3]=height hi.
* Дальше (p+4) — пиксели ПОСТРОЧНО, по width байт на строку.
*
* int img_w = p[0] | (p[1] << 8);
* — это ПОЛНАЯ ширина картинки/ленты-атласа (склейка двух байтов
* заголовка в 16-бит). Используется как ШАГ между строками источника
* (sstride): чтобы спуститься на строку ниже в буфере, надо прибавить к
* адресу целую ширину картинки. Для одиночного спрайта img_w == ширине
* кадра; для ЛЕНТЫ-АТЛАСА (много кадров в ряд) img_w — ширина всей ленты
* (> ширины кадра w), поэтому img_w остаётся int (лента может быть шире
* 64, хотя КАДР ≤ 64).
*
* (sx, sy) — смещение КАДРА (под-прямоугольника) ВНУТРИ картинки:
* левый-верхний угол вырезаемого кадра в пикселях. Для целого спрайта
* sx = sy = 0. Для кадра N горизонтальной ленты: sx = N*FRAME_W, sy = 0
* (вертикальные атласы — sy). Отсюда адрес первого пикселя кадра:
* src = p + 4 (пропустить заголовок)
* + sx (сместиться на sx пикселей вправо в первой строке)
* + sy*img_w (спуститься на sy строк — цикл сложений ниже, без
* умножения: обычно sy == 0).
*/
#include "../_bgi.h"
void _gfx_blit_sprite(int x, int y, const void *img,
int sx, int sy, uint8_t w, uint8_t h)
{
const uint8_t *p = (const uint8_t *)img;
const uint8_t *src;
uint16_t dst;
int img_w = p[0] | (p[1] << 8); /* ширина картинки/ленты = sstride */
int cw = w, ch = h; /* рабочие w,h кадра (для клипа) */
if (x < 0 || y < 0 || x + cw > _bgi_maxx + 1 || y + ch > _bgi_maxy + 1) {
if (x < 0) { sx -= x; cw += x; x = 0; }
if (y < 0) { sy -= y; ch += y; y = 0; }
if (x + cw > _bgi_maxx + 1) cw = _bgi_maxx + 1 - x;
if (y + ch > _bgi_maxy + 1) ch = _bgi_maxy + 1 - y;
if (cw <= 0 || ch <= 0) return;
}
/* Адрес первого пикселя кадра: заголовок(4) + sx вправо + sy строк
* вниз. sy*img_w умножением (__mulint, O(1) ~300Т): вертикальные
* ленты атласов сделали sy != 0 нормой — цикл сложений был O(sy),
* до ~64К тактов на блит кадра 11 (профиль rpgwalk 2026-07-13).
* if (sy): горизонтальные ленты/одиночные спрайты не платят и за
* умножение. */
src = p + 4 + sx;
if (sy)
src += (uint16_t)sy * (uint16_t)img_w;
dst = _gfx_addr_base + x;
_bgi_blit_rows_raw(src, (uint8_t *)dst, (uint8_t)cw, (uint8_t)ch,
img_w, (uint8_t)y);
}