Files
Sprinter-SDCC/tests/blitperf/blitperf.c
T
snark13 72ce66275e 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>
2026-07-12 21:51:52 +03:00

132 lines
4.3 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.
/*
* blitperf — микробенчмарк спрайтового пути: операций в секунду (RTC)
* для каждого слоя цепочки putsprite/gfx_heal. Диагностика разрыва
* с asm-референсом (examples/balls ~1.5 мс/шар при теории ~0.3 мс):
* отделяет стоимость leaf'ов от C-обвязки (клип/банк/заголовок) и
* даёт опорные точки (bar — давно оптимизированный accel-путь).
*
* Методика: измеряется число вызовов за одну RTC-секунду (старт по
* смене секунды; время проверяется раз в 8 операций). Строка «empty»
* — цена самого цикла (вызов пустышки + getdatetime/8) — вычитать из
* остальных: t_op = 1/n_op - 1/n_empty.
*
* ВНИМАНИЕ: использует ВНУТРЕННИЕ символы libbgi (прототипы
* продублированы ниже — сверять с libbgi/_bgi.h при изменениях ABI).
*
* Запуск:
* python3 toolchain/mame_interactive.py tests/blitperf/blitperf.exe \
* --snap 22 --timeout 24
*/
#include <graphics.h>
#include <gfx.h>
#include <time.h>
/* --- внутренности libbgi (копия из _bgi.h, НЕ публичный API) ------- */
extern uint16_t _gfx_addr_base;
void _bgi_begin(void);
void _bgi_end(void);
void _bgi_heal_rows_raw(uint8_t *scr, int y0, uint8_t w, uint8_t h);
void _bgi_copy_rows_raw(const uint8_t *src, uint8_t *dst,
uint8_t w, uint8_t h,
int dstride, int sstride, uint8_t y0);
/* ------------------------------------------------------------------- */
#define BW 16
static uint8_t spr[4 + BW * BW];
static void build_sprite(void)
{
uint8_t *p = spr;
int i;
*p++ = BW; *p++ = 0; *p++ = BW; *p++ = 0;
for (i = 0; i < BW * BW; i++)
*p++ = (uint8_t)(LIGHTRED + ((i & 7) == 0)); /* без 0xFF */
}
/* --- операции ------------------------------------------------------ */
static void op_empty(void) { }
static void op_leaf_heal(void) /* leaf + W3-скобка, без C-обвязки */
{
_bgi_begin();
_bgi_heal_rows_raw((uint8_t *)(_gfx_addr_base + 100), 100, BW, BW);
_bgi_end();
}
static void op_leaf_blit(void) /* copy-leaf + скобка, без обвязки */
{
_bgi_begin();
_bgi_copy_rows_raw(spr + 4, (uint8_t *)(_gfx_addr_base + 100),
BW, BW, 0, BW, 100);
_bgi_end();
}
static void op_gfx_heal(void) { gfx_heal(100, 100, BW, BW); }
static void op_blit_part(void) { gfx_blit_part(100, 100, spr, 0, 0, BW, BW); }
static void op_putsprite(void) { putsprite(100, 100, spr); }
static void op_bar(void) /* опорная точка: accel-fill 16×16 */
{
bar(100, 100, 100 + BW - 1, 100 + BW - 1);
}
/* --- измеритель ---------------------------------------------------- */
static uint16_t bench(void (*op)(void))
{
datetime_t dt;
uint8_t s0, k;
uint16_t n = 0;
getdatetime(&dt);
s0 = dt.second;
do { getdatetime(&dt); } while (dt.second == s0); /* синхр. к границе */
s0 = dt.second;
for (;;) {
for (k = 0; k < 8; k++)
op();
n += 8;
getdatetime(&dt);
if (dt.second != s0)
return n;
}
}
static void say_num(int y, const char *tag, uint16_t v)
{
char buf[16];
uint8_t i;
uint16_t d = 10000;
for (i = 0; tag[i] && i < 9; i++)
buf[i] = tag[i];
while (i < 10) buf[i++] = ' ';
for (; d; d /= 10)
buf[i++] = (char)('0' + (v / d) % 10);
buf[i] = 0;
setcolor(WHITE);
outtextxy(4, y, buf);
}
int main(void)
{
build_sprite();
initgraph();
cleardevice();
setcolor(WHITE);
outtextxy(4, 4, "BLITPERF ops/sec (16x16)");
say_num(20, "empty", bench(op_empty));
say_num(30, "leafheal", bench(op_leaf_heal));
say_num(40, "leafblit", bench(op_leaf_blit));
say_num(50, "gfx_heal", bench(op_gfx_heal));
say_num(60, "blitpart", bench(op_blit_part));
say_num(70, "putsprite", bench(op_putsprite));
say_num(80, "bar", bench(op_bar));
outtextxy(4, 96, "done");
for (;;) { }
}