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>
This commit is contained in:
2026-07-13 21:54:37 +03:00
parent 02f7afe765
commit 0eec977630
5 changed files with 342 additions and 10 deletions
+4 -3
View File
@@ -39,10 +39,11 @@ void _gfx_blit_full(int x, int y, const void *img,
if (w <= 0 || h <= 0) return;
}
/* src = img + 4 + sy*img_w + sx — без __mulint (обычно sy == 0). */
/* src = img + 4 + sy*img_w + sx; sy*img_w умножением (__mulint,
* O(1)) — цикл сложений был O(sy), см. _gfx_blit_sprite.c. */
src = p + 4 + sx;
while (sy--)
src += img_w;
if (sy)
src += (uint16_t)sy * (uint16_t)img_w;
dst = _gfx_addr_base + x;
while (w > 256) {
+8 -5
View File
@@ -54,12 +54,15 @@ void _gfx_blit_sprite(int x, int y, const void *img,
}
/* Адрес первого пикселя кадра: заголовок(4) + sx вправо, затем sy
* строк вниз (по img_w на строку) — циклом сложений, без __mulint
* (обычно sy == 0: целый спрайт или горизонтальная лента). */
/* Адрес первого пикселя кадра: заголовок(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;
while (sy--)
src += img_w;
if (sy)
src += (uint16_t)sy * (uint16_t)img_w;
dst = _gfx_addr_base + x;
+7 -2
View File
@@ -23,9 +23,14 @@ void _gfx_blit_sprite_noclip(int x, int y, const void *img,
const uint8_t *src;
int img_w = p[0] | (p[1] << 8); /* ширина картинки/ленты = sstride */
/* sy*img_w умножением (__mulint, O(1) ~300Т): вертикальные ленты
* атласов сделали sy != 0 нормой — старый цикл сложений был O(sy),
* до ~64К тактов на блит кадра 11 (профиль rpgwalk 2026-07-13).
* if (sy): горизонтальные ленты/одиночные спрайты (sy == 0) не
* платят и за умножение. */
src = p + 4 + sx;
while (sy--)
src += img_w;
if (sy)
src += (uint16_t)sy * (uint16_t)img_w;
_bgi_blit_rows_raw(src, (uint8_t *)(_gfx_addr_base + x),
w, h, img_w, (uint8_t)y);