diff --git a/applications/PoP/roomtest/pop_bg.c b/applications/PoP/roomtest/pop_bg.c index 6129138..3b0b1af 100644 --- a/applications/PoP/roomtest/pop_bg.c +++ b/applications/PoP/roomtest/pop_bg.c @@ -889,6 +889,24 @@ static void char_footprint(int obj_x, int obj_y, uint16_t w, uint16_t h, * obj_x — ЛОГИЧЕСКАЯ X кадра (до ×8/7), obj_y — низ спрайта, w/h — размер * кадра; всё остальное берётся из самого персонажа (ch). Окно fore-клипа к * этому моменту уже стоит: его задаёт тот, кто нарисовал спрайт. */ +/* Передний слой ОДНОГО тайла — вход для прохода по пометкам + * (pop_fore_needed). Без окна fore-клипа: пометка уже сузила работу до + * нужных тайлов, а кусок тайла обязан лечь целиком — у оригинала foretable + * ничем не режется. */ +void pop_fore_tile_b(int row, int col) __banked +{ + uint8_t saved = pop_t_fclip_on; + pop_t_fclip_on = 0; + fore_tile(row, col); + pop_t_fclip_on = saved; +} + +/* Банк на ВЕСЬ проход пометок, а не на каждый тайл: помеченных тайлов за + * кадр бывает больше десятка, и две смены банка на каждый — заметная доля + * прохода, который сам по себе дешёвый. */ +void pop_fore_pass_begin(void) __banked { gfx_set_bank(GFX_BANK_SPRITE); } +void pop_fore_pass_end(void) __banked { gfx_set_bank(GFX_BANK_NORMAL); } + void pop_fore_over_char(const pop_char_t *ch, int obj_x, int obj_y, uint16_t w, uint16_t h) __banked { diff --git a/applications/PoP/roomtest/pop_bg.h b/applications/PoP/roomtest/pop_bg.h index 7531b29..0212c83 100644 --- a/applications/PoP/roomtest/pop_bg.h +++ b/applications/PoP/roomtest/pop_bg.h @@ -176,6 +176,11 @@ void pop_loose_mob_room_changed(void) __banked; * seg008:1561). Звать после отрисовки персонажей, перед fore-слоем. */ /* Отрисовка падающих кусков, идущих ПОД Kid. Звать ПОСЛЕ pop_redraw_needed() * — фон кадра обязан быть готов до любого спрайта. */ +/* Передний слой ОДНОГО тайла — зовёт pop_fore_needed (pop_redraw.c). */ +void pop_fore_tile_b(int row, int col) __banked; +void pop_fore_pass_begin(void) __banked; +void pop_fore_pass_end(void) __banked; + void pop_loose_mob_draw(void) __banked; void pop_loose_mob_draw_over(void) __banked; diff --git a/applications/PoP/roomtest/pop_redraw.c b/applications/PoP/roomtest/pop_redraw.c index 6444cc8..8d0c308 100644 --- a/applications/PoP/roomtest/pop_redraw.c +++ b/applications/PoP/roomtest/pop_redraw.c @@ -18,6 +18,10 @@ static uint8_t rda_kind[NCOLS], rda_cnt[NCOLS]; /* Сколько всего непустых пометок: без него проход по 40 ячейкам крутился бы * КАЖДЫЙ кадр впустую (замер 2026-07-27 на прежнем rest-цикле: ~12 % кадра). */ static uint8_t rd_pending; +/* Пометки ПЕРЕДНЕГО слоя — однокадровые, отдельно от rd_*: те живут по + * страницам дабл-буфера (запекают фон), а эти транзиентны. */ +static uint8_t rdf[NTILES]; +static uint8_t rdf_pending; void pop_redraw_reset(void) __banked { @@ -25,6 +29,31 @@ void pop_redraw_reset(void) __banked for (i = 0; i < NTILES; i++) { rd_kind[i] = 0; rd_cnt[i] = 0; } for (i = 0; i < NCOLS; i++) { rda_kind[i] = 0; rda_cnt[i] = 0; } rd_pending = 0; + for (i = 0; i < NTILES; i++) rdf[i] = 0; + pop_fore_pass_end(); + rdf_pending = 0; +} + +void pop_set_redraw_fore(uint8_t tilepos) __banked +{ + if (tilepos >= NTILES) return; + if (!rdf[tilepos]) rdf_pending++; + rdf[tilepos] = 1; +} + +/* Разбор пометок переднего слоя: тайлы обходятся в том же порядке, что и + * фон, и рисуются ПОСЛЕ всех объектов кадра — как foretable у оригинала + * (draw_tables, seg008:1373: back -> mid -> fore). */ +void pop_fore_needed(void) __banked +{ + uint8_t i, row = 0, col = 0; + if (!rdf_pending) return; + pop_fore_pass_begin(); + for (i = 0; i < NTILES; i++) { + if (rdf[i]) { rdf[i] = 0; pop_fore_tile_b(row, col); } + if (++col == 10) { col = 0; row++; } + } + rdf_pending = 0; } void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked diff --git a/applications/PoP/roomtest/pop_redraw.h b/applications/PoP/roomtest/pop_redraw.h index c333139..a53a459 100644 --- a/applications/PoP/roomtest/pop_redraw.h +++ b/applications/PoP/roomtest/pop_redraw.h @@ -40,6 +40,17 @@ * дабл-буфера перерисовать (1 = только текущая, 2 = обе). Повторная * пометка перекрывает вид и берёт максимум счётчика. */ void pop_set_redraw(uint8_t tilepos, uint8_t kind, uint8_t pages) __banked; + +/* ПЕРЕДНИЙ СЛОЙ по пометкам — порт redraw_frames_fore/set_redraw_fore + * (seg007:0550). В оригинале эти пометки ставят ОБЪЕКТЫ, каждый вокруг + * себя: персонаж — прямоугольник футпринта (redraw_at_char, seg003:0576), + * падающий кусок — соседа справа и второй тайл на границе рядов (draw_mob, + * seg007:1147), анимация тайла — свой тайл (draw_trob). Пометка + * ОДНОКАДРОВАЯ: передний слой транзиентен, его снимает heal. + * + * Разбор (pop_fore_needed) звать ПОСЛЕ отрисовки всех объектов кадра. */ +void pop_set_redraw_fore(uint8_t tilepos) __banked; +void pop_fore_needed(void) __banked; void pop_set_redraw_above(uint8_t col, uint8_t kind, uint8_t pages) __banked; /* Разобрать пометки: нарисовать всё помеченное на ТЕКУЩЕЙ (back) странице и diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index e459901..251d429 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -21,6 +21,7 @@ #include "pop_tile.h" /* общие листья слоя фона (резидент): блит, тайлы, таблица */ #include "_pop_draw.h" /* pop_onscreen_cols / pop_heal_fast */ #include "pop_state.h" +#include "pop_redraw.h" /* пометки тайлов: сосед падающего куска (draw_mob) */ #include "pop_geom.h" #include "pop_level.h" /* pop_doorlink2 — состояние нажатия кнопки */ #include "pop_kid.h" /* Kid.curr_col/curr_row — порядок оверлеев */ @@ -1165,6 +1166,30 @@ static void mob_tick_one(mob_t *m, uint8_t pg) * * Порядок «кусок против Кида» здесь ни при чём — его решает defer выше, и там * действует обычное правило. */ +/* Пометки СОСЕДА СПРАВА — порт draw_mob (seg007:1147). Оригинал каждый + * кадр помечает тайл col+1 в ряду куска сразу двумя пометками: + * + * set_redraw2(tilepos, 1); // фон соседа — рисуется ДО куска + * set_redraw_fore(tilepos, 1); // его передние части — ПОСЛЕ куска + * + * и повторяет их для второго ряда, если кусок висит на границе (ypos-18). + * Так правая часть куска (спрайт 72 лежит в столбце col+1) прячется за + * передней гранью соседнего пола, а передние части чужого тайла — колонна, + * например, — ложатся поверх куска. + * + * Здесь была подпорка: целый соседний тайл рисовался ПОВЕРХ куска прямо в + * mob_render. Она тащила на плиту чужой узор и окно и стоила по полной + * отрисовке тайла на кусок за кадр (MOB-CLIP-RIGHT). */ +static void mob_mark_neighbour(const mob_t *m) +{ + int8_t r = pop_y_to_row((int16_t)m->y); + int8_t rt = pop_y_to_row((int16_t)(m->y - 18)); + int8_t c = (int8_t)(m->col + 1); + if (c > 9) return; + if (r >= 0 && r <= 2) pop_set_redraw_fore((uint8_t)(r * 10 + c)); + if (rt != r && rt >= 0 && rt <= 2) pop_set_redraw_fore((uint8_t)(rt * 10 + c)); +} + static void mob_draw_pass(uint8_t pg, uint8_t want_defer) { uint8_t order[MOB_MAX], n = 0, i, j; @@ -1177,7 +1202,18 @@ static void mob_draw_pass(uint8_t pg, uint8_t want_defer) order[j] = i; n++; } - for (i = 0; i < n; i++) mob_render(&mobs[order[i]], pg); + for (i = 0; i < n; i++) { + /* Пометки соседа ставятся ЗДЕСЬ, а не в тике: у оригинала draw_mob + * (seg007:1147) считает ряд от той же координаты, по которой кусок + * рисуется. Стояли они в цикле тика — то есть по позиции ДО + * mob_tick_one, а рисовался кусок уже по новой. Пока он летит + * внутри ряда, старый и новый тайл совпадают и разницы не видно; но + * в кадр пересечения границы пометка указывала на прежний тайл, и + * передняя часть колонны кусок не перекрывала — ровно ОДИН кадр, + * как и наблюдалось (прогон 2026-08-13, комната 23, колонка 8). */ + mob_mark_neighbour(&mobs[order[i]]); + mob_render(&mobs[order[i]], pg); + } } void pop_loose_mob_tick(void) __banked diff --git a/applications/PoP/roomtest/pop_state.c b/applications/PoP/roomtest/pop_state.c index e47a943..33d354d 100644 --- a/applications/PoP/roomtest/pop_state.c +++ b/applications/PoP/roomtest/pop_state.c @@ -103,3 +103,13 @@ uint8_t pop_seamless; * КОМНАТЫ ОБЫЧНЫМ ХОДОМ и стартом уровня. */ uint8_t pop_nav_hold; + + +/* ВРЕМЕННО (замер фаз кадра 2026-08-13): четыре маркера-пустышки в + * РЕЗИДЕНТЕ. Брейкпоинт на каждом + printf totalcycles даёт длительность + * блоков: m1..m2 — логика (синий), m2..m3 — фон (зелёный), m3..m4 — + * спрайты (циан). В банк брейкпоинт ставить нельзя: 0xC000 общее окно. */ +void pop_dbg_m1(void) { } +void pop_dbg_m2(void) { } +void pop_dbg_m3(void) { } +void pop_dbg_m4(void) { } diff --git a/applications/PoP/roomtest/pop_state.h b/applications/PoP/roomtest/pop_state.h index 7b90ac1..33d4918 100644 --- a/applications/PoP/roomtest/pop_state.h +++ b/applications/PoP/roomtest/pop_state.h @@ -75,3 +75,8 @@ void pop_dbg_trap(void); * Межбанковый: пишет pop_map, читает pop_map же, но через главный цикл. */ extern int8_t pop_jumped_mirror; + + +/* ВРЕМЕННО: маркеры фаз кадра (замер). */ +void pop_dbg_m1(void); void pop_dbg_m2(void); +void pop_dbg_m3(void); void pop_dbg_m4(void); diff --git a/applications/PoP/roomtest/roomtest.c b/applications/PoP/roomtest/roomtest.c index 2dfbcbb..4d98eb6 100644 --- a/applications/PoP/roomtest/roomtest.c +++ b/applications/PoP/roomtest/roomtest.c @@ -372,6 +372,7 @@ int main(void) pop_mirror_heal(); pop_fore_heal(); /* + следы оверлея (шире прямоугольника Kid) */ PROF(2); /* логика (тот же синий) */ + pop_dbg_m1(); /* ЗАМЕР: начало логики */ kid_tick(); /* play_seq: следующий кадр */ pop_phys_tick(); /* K3: падение/приземление/стена */ pop_guard_tick(); /* логика стража — В БАНКЕ (guards.c) */ @@ -447,6 +448,7 @@ int main(void) } PROF(4); /* слой фона */ + pop_dbg_m2(); /* ЗАМЕР: конец логики / начало фона */ pop_loose_tick(); /* loose-полы: анимация + перерисовка (СЛОЙ ФОНА */ /* — ДО Kid, чтобы Kid всегда был поверх плиты) */ @@ -613,6 +615,7 @@ int main(void) } update_kid_render_dx(); /* straddle: смещение спрайта, если kid_room≠cur_room */ PROF(6); /* спрайты: страж, Kid, fore поверх */ + pop_dbg_m3(); /* ЗАМЕР: конец фона / начало спрайтов */ /* check_mirror (seg003:0798) — ПЕРВАЯ строка draw_people * (seg008:228A), до отрисовки кого бы то ни было: если Кид только что * пролетел сквозь зеркало, тень должна родиться ДО того, как слот @@ -645,6 +648,10 @@ int main(void) if (pop_kid_hurt) pop_kid_hurt = 0; } pop_loose_mob_draw_over(); /* падающая плита НИЖЕ Kid → поверх него (y-порядок) */ + /* ПЕРЕДНИЙ СЛОЙ по пометкам — последним в кадре, как foretable у + * оригинала (draw_tables, seg008:1373). Пометки ставят сами + * объекты вокруг себя (pop_redraw.h). */ + pop_fore_needed(); pop_hp_draw(); /* полоса HP — только при изменении */ PROF(6); /* fore поверх Kid + борта */ /* Fore-слой Кида — В КОНЦЕ, после плиты и полосы HP: окно fore-клипа @@ -673,6 +680,7 @@ int main(void) pop_dbg_room_label(cur_room); /* номер комнаты палочками — в банке */ #endif + pop_dbg_m4(); /* ЗАМЕР: конец спрайтов */ gfx_wait_vsync(); if (flash_on) { /* строб: цвет держался один кадр растра */ flash_bg(0, 0, 0);