diff --git a/applications/PoP/roomtest/pop_cdraw.h b/applications/PoP/roomtest/pop_cdraw.h index f48db99..c6ace88 100644 --- a/applications/PoP/roomtest/pop_cdraw.h +++ b/applications/PoP/roomtest/pop_cdraw.h @@ -129,6 +129,14 @@ void pop_cd_clear(uint8_t p); /* снять метку стр void pop_cd_touch(int x, int y, int w, int h); #define POP_CD_TOUCH_ALL() pop_cd_touch(0, 0, 320, 256) +/* Пакетная пометка: между begin и end блиты КОПЯТ общий прямоугольник, и + * настоящая пометка ставится одна (на весь тайл), а не на каждый кусок. + * Скобку ставит draw_tile — выигрыш ~15% цены блита фона. Вкладывать + * скобки нельзя: аккумулятор один. */ +extern uint8_t pop_cd_batch; +void pop_cd_batch_begin(void); +void pop_cd_batch_end(void); + /* Кого в этом кадре можно НЕ перерисовывать: бит 0 — Кид, бит 1 — соперник. * Звать ДВАЖДЫ: перед heal (стирать нечего) и перед отрисовкой (тик мог * персонажа всё-таки сдвинуть — тогда бит снимется сам, а pop_char_draw diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index 0b69c2e..08166ad 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -298,6 +298,11 @@ static void draw_tile(int row, int col) rbl_mod = pop_tile_mod(row + 1, col - 1); } + /* Пометка «фон трогали» — ОДНА на весь тайл, а не на каждый его кусок + * (pop_cd_batch_begin/end). Досрочных выходов у draw_tile нет, так что + * скобка гарантированно закрывается. */ + pop_cd_batch_begin(); + /* --- draw_tile_anim_topright (seg008:0568): верх ВОРОТ снизу-слева. * Условие дословно: свой тайл пуст / верх большой колонны / верх двери, * а снизу-слева — ворота. --- */ @@ -472,6 +477,7 @@ static void draw_tile(int row, int col) } else if (t->fore_id) { pop_fore_b(t->fore_id, t->fore_x * 8 + x, t->fore_y + dmy); } + pop_cd_batch_end(); /* одна пометка на весь тайл */ } diff --git a/applications/PoP/roomtest/pop_state.c b/applications/PoP/roomtest/pop_state.c index 5d98df6..7a445c3 100644 --- a/applications/PoP/roomtest/pop_state.c +++ b/applications/PoP/roomtest/pop_state.c @@ -146,3 +146,19 @@ void pop_dbg_m15(void) { } * его вместе с тактами — так видно цену КАЖДОГО вида, а не число пометок. */ void pop_dbg_kind(uint8_t k) { (void)k; } void pop_dbg_m16(void) { } + +/* ВРЕМЕННО (разбор pop_blit_b 2026-08-13): сколько в одном блите занимают + * САМИ пиксели, а сколько пооспрайтовые накладные (map/unmap страницы + * атласа, чтение заголовка, пометка грязного прямоугольника). */ +void pop_dbg_b1(void) { } /* вход (спрайт прошёл отсев) */ +void pop_dbg_b2(void) { } /* atlas_image + gfx_w0_map */ +void pop_dbg_b3(void) { } /* сам блит */ +void pop_dbg_b4(void) { } /* pop_cd_touch */ +void pop_dbg_b5(void) { } /* gfx_w0_unmap (конец) */ +void pop_dbg_b6(void) { } /* взведён = пошли в gfx_blit_noclip, а не в _part */ + +/* ВРЕМЕННО (регрессия цены блита 2026-08-13): w и h упакованы в один + * 16-битный аргумент (arg1 -> HL при __sdcccall(1)), брейкпоинт логирует + * HL — дальше цена раскладывается как a + b*h + c*w*h, где b и есть + * построчные накладные ядра. */ +void pop_dbg_wh(uint16_t wh) { (void)wh; } diff --git a/applications/PoP/roomtest/pop_state.h b/applications/PoP/roomtest/pop_state.h index 841be8a..d10b78f 100644 --- a/applications/PoP/roomtest/pop_state.h +++ b/applications/PoP/roomtest/pop_state.h @@ -93,3 +93,6 @@ void pop_dbg_m11(void); void pop_dbg_m12(void); void pop_dbg_m13(void); void pop_dbg_m14(void); void pop_dbg_m15(void); void pop_dbg_kind(uint8_t k); void pop_dbg_m16(void); +void pop_dbg_b1(void); void pop_dbg_b2(void); void pop_dbg_b3(void); +void pop_dbg_b4(void); void pop_dbg_b5(void); void pop_dbg_b6(void); +void pop_dbg_wh(uint16_t wh); diff --git a/applications/PoP/roomtest/pop_tile.c b/applications/PoP/roomtest/pop_tile.c index a22c6dc..b50d5de 100644 --- a/applications/PoP/roomtest/pop_tile.c +++ b/applications/PoP/roomtest/pop_tile.c @@ -218,10 +218,47 @@ static uint16_t cd_cols_of(int x, int x1) /* Обе страницы помечаются сразу (персонаж чинится на каждой в свой кадр), * поэтому цикл развёрнут: индекс-переменная заставляла SDCC считать адрес * каждой ячейки сложением. Зовётся с КАЖДОГО блита фона. */ +/* Пакетная пометка (pop_cd_batch_begin/end). Один тайл рисуется пятью-шестью + * спрайтами, и каждый звал pop_cd_touch со своими четырьмя 16-битными + * аргументами — 2 866 тактов на спрайт, 15% всей цены блита (замер + * 2026-08-13). В пакете спрайты копят ОБЩИЙ прямоугольник, а настоящая + * пометка ставится одна на тайл. + * + * Объединение ЗАГРУБЛЯЕТ прямоугольник (накрывает и то, чего между кусками + * не касались), и это безопасно ровно в одну сторону: лишняя пометка стоит + * лишней перерисовки персонажа, пропущенная — стёртого и не восстановленного + * персонажа. Практически загрубление почти нулевое: все куски одного тайла + * и так лежат в тех же 1-2 колонках сетки 32px и том же ряду. */ +uint8_t pop_cd_batch; +static int cdb_x0, cdb_y0, cdb_x1, cdb_y1; + +void pop_cd_batch_begin(void) +{ + pop_cd_batch = 1; + cdb_x0 = cdb_y0 = 32767; + cdb_x1 = cdb_y1 = -32768; +} + +void pop_cd_batch_end(void) +{ + pop_cd_batch = 0; /* СНАЧАЛА снять: ниже настоящий вызов */ + if (cdb_x1 >= cdb_x0 && cdb_y1 >= cdb_y0) + pop_cd_touch(cdb_x0, cdb_y0, cdb_x1 - cdb_x0 + 1, cdb_y1 - cdb_y0 + 1); +} + void pop_cd_touch(int x, int y, int w, int h) { - uint16_t cols = cd_cols_of(x, x + w - 1); + uint16_t cols; uint8_t r0, r1; + if (pop_cd_batch) { /* копим, не разбирая на колонки/ряды */ + int x1 = x + w - 1, y1 = y + h - 1; + if (x < cdb_x0) cdb_x0 = x; + if (y < cdb_y0) cdb_y0 = y; + if (x1 > cdb_x1) cdb_x1 = x1; + if (y1 > cdb_y1) cdb_y1 = y1; + return; + } + cols = cd_cols_of(x, x + w - 1); if (!cols) return; r0 = cd_row_of(y); r1 = cd_row_of(y + h - 1); @@ -343,8 +380,11 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom) ybottom + POP_YOFF + 1 <= pop_t_fclip_ly0 || ybottom + POP_YOFF - 64 >= pop_t_fclip_ly1)) return; + pop_dbg_b1(); /* ВРЕМЕННО */ img = (const uint8_t *)atlas_image(a, idx); gfx_w0_map(a->page); + pop_dbg_b2(); /* ВРЕМЕННО */ + pop_dbg_wh((uint16_t)(((uint16_t)img[0] << 8) | img[2])); /* ВРЕМЕННО: w,h */ w = (uint16_t)(img[0] | ((uint16_t)img[1] << 8)); h = (uint16_t)(img[2] | ((uint16_t)img[3] << 8)); top = ybottom - (int)h + 1 + POP_YOFF; /* +YOFF: центрирование */ @@ -368,18 +408,23 @@ void pop_blit_b(atlas_t *a, uint8_t idx, int x, int ybottom) } if (x >= 0 && top >= 0 && w < 256 && h < 256 && x + (int)w <= 320 && top + (int)h <= 256) { + pop_dbg_b6(); /* ВРЕМЕННО: пометка «шли в noclip» */ if (pop_upside) gfx_blit_noclip_vflip(x, top, img); else gfx_blit_noclip(x, top, 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); } + pop_dbg_b3(); /* ВРЕМЕННО */ pop_cd_touch(x, top, (int)w, (int)h); + pop_dbg_b4(); /* ВРЕМЕННО */ } else { blit_b_clip(img, x, top, (int)w, (int)h); + pop_dbg_b3(); pop_dbg_b4(); /* ВРЕМЕННО: медленный путь целиком в b3 */ } } gfx_w0_unmap(); + pop_dbg_b5(); /* ВРЕМЕННО */ } 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); }