Композит падающего куска: циан в бюджете (632 000 -> 273 078)
Три части падающей плиты (env 70/74/72) складываются в ОДИН getimage-блоб
при загрузке тайлсета, и кусок рисуется одним блитом вместо трёх. Блоб
лежит в обычной памяти (W2), поэтому вызов идёт без atlas_image и без
gfx_w0_map/unmap — ещё ~1 350 такта. Прозрачность соблюдена: части
ПЕРЕКРЫВАЮТСЯ (74 и 70 обе от mob_x), поэтому композит собирается
попиксельно с пропуском 0xFF, то есть точно как три прозрачных блита.
Мотив: у блита ~8 800 такта постоянных накладных против ~5 000 на пиксели.
Шесть кусков в воздухе = 18 вызовов = 258 708 такта, больше половины
цианового блока. Резервный путь на три блита оставлен (mob_spr_ok).
циан пик 435 180 -> 273 078 (цель 400 000 — ВЫПОЛНЕНА)
работа 1 037 250 -> 931 782
зелёная 558 498 -> 557 706 (не затронута, ею занимаемся дальше)
Заодно НАЙДЕН И ПОФИКШЕН БАГ КОРИДОРА heal. Снятые из каталогов реальные
габариты частей оказались другими, чем в комментарии, И РАЗНЫМИ у тайлсетов:
подземелье 70 = 32x16 74 = 26x15 72 = 26x16
дворец 70 = 32x13 74 = 25x15 72 = 31x13
То есть во ДВОРЦЕ (уровни 4-6, 10, 11, 13, 14) кусок достаёт до mob_x+62, а
коридор heal был mob_x-4 .. mob_x+59 — правые три пикселя не стирались
никогда. Четыре пикселя слева при этом чистились впустую: левее mob_x
кусок не рисует ничего. Коридор стал mob_x .. mob_x+63, площадь та же.
Высоту (24 строки при следе 19) НЕ сужаем: 24 — это след подземелья плюс
две строки поля с каждой стороны, «сужение по палаццовому следу» сломало бы
подземелье.
Ещё три правки того же захода:
- pop_blit_b: аргументы в file-scope. Третий и дальше SDCC передаёт стеком,
и каждое чтение шло через `-N(ix)` — 76 обращений. Стало 11.
- pop_loose_mob_tick: пометки всех кусков одним пакетом (было по 4 502 такта
на кусок). mobTk 176 772 -> 168 600.
- pop_floor_bake: окно клипа проверено ВТОРОЙ раз (после того как
blit_b_clip подешевел) и снова хуже — 182 124 -> 188 460. Причина
записана в коде: у этого тайла клипа нет, блиты идут быстрым путём, а окно
их уводит в клипованный и при этом не отсеивает ни одного куска и не
режет ни одной строки. Не пробовать в третий раз.
Новый лист pop_mem_b (резидент): блит блоба из обычной памяти с теми же
клипом полосы у потолка и окном перерисовки, что у pop_blit_b.
Проверено: 8 наборов tests-host зелёные; в MAME кадр с летящими плитами и
итоговая картинка совпадают с прежними.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -476,51 +476,61 @@ static void blit_b_oversize(const uint8_t *img, int x, int ybottom)
|
||||
if (!pop_t_fclip_on) pop_cd_touch(x, top, (int)w, (int)h);
|
||||
}
|
||||
|
||||
/* Рабочие переменные pop_blit_b — file-scope, не локали и не стековые
|
||||
* аргументы (см. пролог функции). Реентерабельности нет и не требуется:
|
||||
* pop_blit_b себя не зовёт, а blit_b_clip держит СВОЙ набор (bc_*). */
|
||||
static atlas_t *pb_a;
|
||||
static uint8_t pb_idx, pb_w, pb_h;
|
||||
static int pb_x, pb_yb, pb_top;
|
||||
static const uint8_t *pb_img;
|
||||
|
||||
void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
||||
{
|
||||
const uint8_t *img;
|
||||
uint8_t w, h; /* БАЙТЫ, а не uint16_t — см. ниже */
|
||||
int top;
|
||||
if (idx >= a->count)
|
||||
/* Аргументы сразу в file-scope: третий и дальше SDCC передаёт СТЕКОМ, и
|
||||
* дальше каждое их чтение — `-N(ix)` (замер 2026-08-17: 76 таких
|
||||
* обращений в теле, ~3 500 такта из 14 500 цены блита). То же лечение,
|
||||
* что у draw_tile и blit_b_clip. */
|
||||
pb_a = a; pb_idx = idx; pb_x = x; pb_yb = ybottom;
|
||||
if (pb_idx >= pb_a->count)
|
||||
return;
|
||||
/* Грубый отсев ДО маппинга страницы: реальные w/h лежат в заголовке
|
||||
/* Грубый отсев ДО маппинга страницы: реальные pb_w/pb_h лежат в заголовке
|
||||
* ленты, то есть в EMM-странице, и чтобы их прочитать, надо сделать
|
||||
* gfx_w0_map. Берём заведомо больший габарит (кадры фона не крупнее
|
||||
* 64×64) и, если он не задевает окно, уходим не заплатив ни за
|
||||
* atlas_image, ни за map/unmap. */
|
||||
if (pop_t_fclip_on &&
|
||||
(x >= pop_t_fclip_x1 || x + 64 <= pop_t_fclip_x0 ||
|
||||
ybottom + POP_YOFF + 1 <= pop_t_fclip_ly0 ||
|
||||
ybottom + POP_YOFF - 64 >= pop_t_fclip_ly1))
|
||||
(pb_x >= pop_t_fclip_x1 || pb_x + 64 <= pop_t_fclip_x0 ||
|
||||
pb_yb + POP_YOFF + 1 <= pop_t_fclip_ly0 ||
|
||||
pb_yb + POP_YOFF - 64 >= pop_t_fclip_ly1))
|
||||
return;
|
||||
pop_dbg_b1(); /* ВРЕМЕННО */
|
||||
img = (const uint8_t *)atlas_image(a, idx);
|
||||
gfx_w0_map(a->page);
|
||||
pb_img = (const uint8_t *)atlas_image(pb_a, pb_idx);
|
||||
gfx_w0_map(pb_a->page);
|
||||
pop_dbg_b2(); /* ВРЕМЕННО */
|
||||
/* Габарит читаем БАЙТАМИ. С `uint16_t w = img[0] | (img[1] << 8)` SDCC
|
||||
/* Габарит читаем БАЙТАМИ. С `uint16_t pb_w = pb_img[0] | (pb_img[1] << 8)` SDCC
|
||||
* разворачивал каждую такую строку в два десятка IX-относительных
|
||||
* пересылок: 16-битные w/h не влезали в регистры, и функция целиком
|
||||
* пересылок: 16-битные pb_w/pb_h не влезали в регистры, и функция целиком
|
||||
* уезжала в стековый кадр (пролог `ld iy,#-14 / add iy,sp / ld sp,iy`,
|
||||
* дальше всё через -N(ix) по 19 тактов). Замер 2026-08-13: до вызова
|
||||
* ядра блита уходило ~6 700 тактов при том, что работы там — прочитать
|
||||
* четыре байта заголовка и сравнить границы.
|
||||
* Байтовый габарит корректен по построению: обе ветки ниже И ТАК требуют
|
||||
* w < 256 && h < 256 (иначе ядро не примет), а кадры наших атласов не
|
||||
* pb_w < 256 && pb_h < 256 (иначе ядро не примет), а кадры наших атласов не
|
||||
* крупнее 32x63. Формат .atl допускает больше — такой кадр уходит на
|
||||
* общий путь выше. */
|
||||
if (img[1] | img[3]) { /* кадр больше 255 — редкий путь */
|
||||
blit_b_oversize(img, x, ybottom);
|
||||
if (pb_img[1] | pb_img[3]) { /* кадр больше 255 — редкий путь */
|
||||
blit_b_oversize(pb_img, pb_x, pb_yb);
|
||||
gfx_w0_unmap();
|
||||
return;
|
||||
}
|
||||
w = img[0];
|
||||
h = img[2];
|
||||
top = ybottom - (int)h + 1 + POP_YOFF; /* +YOFF: центрирование */
|
||||
pb_w = pb_img[0];
|
||||
pb_h = pb_img[2];
|
||||
pb_top = pb_yb - (int)pb_h + 1 + POP_YOFF; /* +YOFF: центрирование */
|
||||
/* Переворот применяем ЗДЕСЬ, до клипа: дальше вся геометрия (окно
|
||||
* fore-слоя, полоса у потолка, отсев по экрану) считается уже в
|
||||
* экранных координатах результата. */
|
||||
if (pop_upside) top = FLIP_TOP(top, h);
|
||||
if (w && h) {
|
||||
if (pop_upside) pb_top = FLIP_TOP(pb_top, pb_h);
|
||||
if (pb_w && pb_h) {
|
||||
if (!pop_t_fclip_on && !pop_t_clip_top) {
|
||||
/* БЫСТРЫЙ путь: клипа нет вовсе. Так идут все блиты фона вне
|
||||
* fore-прохода. Линейное ядро без клипа — ~2.9× быстрее общего
|
||||
@@ -529,26 +539,26 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
||||
* ставит сама libbgi: из кода с --w3 её вызывать нельзя. */
|
||||
/* В перевёрнутом виде быстрый путь годится, только если спрайт
|
||||
* целиком внутри поля: иначе он залезет в борт (см. blit_b_clip). */
|
||||
if (pop_upside && (top < POP_YOFF || top + (int)h > POP_CLIP_BOTTOM)) {
|
||||
blit_b_clip(img, x, top, w, h);
|
||||
if (pop_upside && (pb_top < POP_YOFF || pb_top + (int)pb_h > POP_CLIP_BOTTOM)) {
|
||||
blit_b_clip(pb_img, pb_x, pb_top, pb_w, pb_h);
|
||||
gfx_w0_unmap();
|
||||
return;
|
||||
}
|
||||
/* w<256 && h<256 больше не проверяем — гарантировано типом. */
|
||||
if (x >= 0 && top >= 0 &&
|
||||
x + (int)w <= 320 && top + (int)h <= 256) {
|
||||
/* pb_w<256 && pb_h<256 больше не проверяем — гарантировано типом. */
|
||||
if (pb_x >= 0 && pb_top >= 0 &&
|
||||
pb_x + (int)pb_w <= 320 && pb_top + (int)pb_h <= 256) {
|
||||
pop_dbg_b6(); /* ВРЕМЕННО: пометка «шли в noclip» */
|
||||
if (pop_upside) gfx_blit_noclip_vflip(x, top, img);
|
||||
else gfx_blit_noclip(x, top, img);
|
||||
if (pop_upside) gfx_blit_noclip_vflip(pb_x, pb_top, pb_img);
|
||||
else gfx_blit_noclip(pb_x, pb_top, pb_img);
|
||||
} else {
|
||||
if (pop_upside) gfx_blit_part_vflip(x, top, img, 0, 0, (int)w, (int)h);
|
||||
else gfx_blit_part(x, top, img, 0, 0, (int)w, (int)h);
|
||||
if (pop_upside) gfx_blit_part_vflip(pb_x, pb_top, pb_img, 0, 0, (int)pb_w, (int)pb_h);
|
||||
else gfx_blit_part(pb_x, pb_top, pb_img, 0, 0, (int)pb_w, (int)pb_h);
|
||||
}
|
||||
pop_dbg_b3(); /* ВРЕМЕННО */
|
||||
pop_cd_touch(x, top, (int)w, (int)h);
|
||||
pop_cd_touch(pb_x, pb_top, (int)pb_w, (int)pb_h);
|
||||
pop_dbg_b4(); /* ВРЕМЕННО */
|
||||
} else {
|
||||
blit_b_clip(img, x, top, w, h);
|
||||
blit_b_clip(pb_img, pb_x, pb_top, pb_w, pb_h);
|
||||
pop_dbg_b3(); pop_dbg_b4(); /* ВРЕМЕННО: медленный путь целиком в b3 */
|
||||
}
|
||||
}
|
||||
@@ -556,6 +566,34 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom)
|
||||
pop_dbg_b5(); /* ВРЕМЕННО */
|
||||
}
|
||||
|
||||
/* Блит getimage-блоба из ОБЫЧНОЙ памяти (W2), а не из EMM-страницы атласа:
|
||||
* те же клип полосы у потолка и окно перерисовки, что у pop_blit_b, но без
|
||||
* atlas_image и без gfx_w0_map/unmap (это ещё ~1 350 такта). Заведено под
|
||||
* композитный спрайт падающего куска (pop_room.c: три части сложены в один
|
||||
* блоб, один вызов блита вместо трёх). */
|
||||
void pop_mem_b(const uint8_t *img, int x, int ybottom)
|
||||
{
|
||||
uint8_t w = img[0], h = img[2];
|
||||
int top;
|
||||
if (pop_t_fclip_on &&
|
||||
(x >= pop_t_fclip_x1 || x + 64 <= pop_t_fclip_x0 ||
|
||||
ybottom + POP_YOFF + 1 <= pop_t_fclip_ly0 ||
|
||||
ybottom + POP_YOFF - 64 >= pop_t_fclip_ly1))
|
||||
return;
|
||||
if (!w || !h) return;
|
||||
top = ybottom - (int)h + 1 + POP_YOFF;
|
||||
if (pop_upside) top = FLIP_TOP(top, h);
|
||||
if (!pop_t_fclip_on && !pop_t_clip_top &&
|
||||
!(pop_upside && (top < POP_YOFF || top + (int)h > POP_CLIP_BOTTOM)) &&
|
||||
x >= 0 && top >= 0 && x + (int)w <= 320 && top + (int)h <= 256) {
|
||||
if (pop_upside) gfx_blit_noclip_vflip(x, top, img);
|
||||
else gfx_blit_noclip(x, top, img);
|
||||
pop_cd_touch(x, top, (int)w, (int)h);
|
||||
} else {
|
||||
blit_b_clip(img, x, top, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
void pop_env_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_env[id >> POP_ENV_SHIFT], (uint8_t)(id & POP_ENV_MASK), x, yb); }
|
||||
void pop_wall_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_wall_at, id, x, yb); }
|
||||
void pop_fore_b(uint8_t id, int x, int yb) { pop_blit_b(&pop_fore_at, id, x, yb); }
|
||||
|
||||
Reference in New Issue
Block a user