72ce66275e
Спрайтовая графика поверх 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>
132 lines
4.3 KiB
C
132 lines
4.3 KiB
C
/*
|
||
* 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 (;;) { }
|
||
}
|