From 40f0d465429865e4d3fcd753b7df9ffa8757722b Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Mon, 17 Aug 2026 18:14:35 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A0=D0=B5=D0=B3=D1=80=D0=B5=D1=81=D1=81=20?= =?UTF-8?q?=D1=82=D0=B0=D0=BA=D1=82=D0=BE=D0=B2=20=D0=BF=D0=BE=D1=81=D0=BB?= =?UTF-8?q?=D0=B5=20=D1=87=D0=B5=D1=82=D1=8B=D1=80=D1=91=D1=85=20=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=D0=BE=D0=B2:=20=D1=83=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=20+130=20000=20=D0=B2=20heal=20=D0=B8=20+130=20000=20?= =?UTF-8?q?=D0=B2=20=D1=86=D0=B8=D0=B0=D0=BD=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Замер на сцене каскада (ур.13 к.23) ПОСЛЕ багфиксов показал регресс, причём в том числе в СИНЕЙ фазе, которую фиксы не трогали: до фиксов после фиксов после этой правки работа 783 798 1 038 528 873 930 синяя 142 830 275 538 142 830 зелёная 414 456 415 284 417 630 циан 269 538 479 628 379 482 период (растр.) 4 5 4 (один кадр 5) Разбор синей зондом L (граница «ввод+heal» / «логика») сразу указал место: логика 88 020 не изменилась, а «ввод+heal» вырос 55 000 -> 187 500. Это pop_fore_heal чистит ОБЪЕДИНЁННЫЙ ov_mark: кромка потолка (полоса y 0..8) и оверлеи соседей (64x64 на тайл) от шести кусков сливались в прямоугольник на полкомнаты. Правки, каждая с замером: 1. Оверлей соседа рисуется в ОКНЕ = прямоугольник самого куска. Смысл оверлея — перекрыть кусок, а что лежит вне него, и так нарисовано правильным фоном. Даёт три вещи разом: пикселей в разы меньше; куски тайла, не задевающие кусок, отсеиваются предфильтром pop_blit_b даром (это и есть вертикальный гейт, отдельного не надо); ov_mark становится НЕ НУЖЕН — всё нарисованное лежит внутри heal-коридора куска, а он чистится каждый кадр. Для этого заведён ov_suppress. 2. Кромка потолка — тоже без ov_mark: её куски рисуются от dby = 2, то есть занимают строки 0..2, а помечает её только кусок и только пока достаёт (mob_y <= 18) — значит она внутри его же коридора. 3. Предусловия перенесены в РЕЗИДЕНТ, до вызова в банк 2: pop_mob_overlay_tile и разбор пометок живут в других банках, и каждый вызов платит межбанковый трамплин (~8 900), а условий всего два дешёвых чтения тайла (pop_tile_code — резидент, ~1 100). Гейты: тайл соседа не пуст и его габарит пересекается с габаритом куска; для полосы потолка — тайл полосы не пуст (а он там чаще всего пуст: кусок существует ровно потому, что плита из своей клетки выпала, и пустой тайл в переднем слое не рисует ничего). ОТРИЦАТЕЛЬНЫЙ РЕЗУЛЬТАТ, откачен: сдавать пометки ряда −1 одной МАСКОЙ вместо до двенадцати отдельных вызовов (один трамплин вместо двенадцати) — 873 930 -> 879 276, то есть хуже. Пометки ставятся редко, а цена копилки и разбора маски платится всегда. Итог: все три секции в бюджете 400 000 (синяя 158 880, циан 379 482), зелёная 417 630 — она была такой и ДО фиксов, это не регресс. Период вернулся к 4 растровым кадрам; ровно один логический кадр из 54 идёт за 5 (работа 873 930 при пороге 860 000). Цена четырёх багфиксов по итогу: +90 000 работы за кадр (+11 %), из них весь рост в циане. Тестовый образ теперь всегда собирается LEVEL=1 (решение пользователя: переход на нужный уровень он делает сам). Co-Authored-By: Claude Opus 5 --- applications/PoP/roomtest/pop_bg.c | 35 +++++++++++++++++++++++++-- applications/PoP/roomtest/pop_bg.h | 3 ++- applications/PoP/roomtest/pop_room.c | 36 +++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/applications/PoP/roomtest/pop_bg.c b/applications/PoP/roomtest/pop_bg.c index dff792f..9b404f3 100644 --- a/applications/PoP/roomtest/pop_bg.c +++ b/applications/PoP/roomtest/pop_bg.c @@ -34,9 +34,17 @@ * восстанавливаем фон на ОБЕИХ страницах (pop_fore_heal). */ static int ov_x0, ov_y0, ov_x1, ov_y1; static uint8_t ov_pending; /* биты страниц, где надо восстановить фон */ +/* Подавить ov_mark на время серии — для вызывающего, который САМ чистит эту + * область каждый кадр. Так работает падающий кусок: его heal-коридор + * прочёсывается всегда, а оверлей соседа и кромка потолка рисуются ВНУТРИ + * коридора (окно клипа = прямоугольник куска). Без этого объединение ov_mark + * по шести кускам растягивалось на полкомнаты, и pop_fore_heal стоил 187 500 + * такта в фазе «ввод+heal» против 55 000 (замер 2026-08-17). */ +static uint8_t ov_suppress; static void ov_mark(int x, int y, int w, int h) { + if (ov_suppress) return; if (!ov_pending) { ov_x0 = x; ov_y0 = y; ov_x1 = x + w; ov_y1 = y + h; } else { if (x < ov_x0) ov_x0 = x; @@ -837,14 +845,30 @@ static void other_overlay_tile(int row, int col) * * Ориентир сохраняем и возвращаем: pop_bg_obj_* принадлежит текущему * персонажу, и его читает ещё и mob_tick_one (решение defer). */ -void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col) __banked +void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col, + int mx, int my, uint8_t mh) __banked { int8_t saved_row = pop_bg_obj_row, saved_col = pop_bg_obj_col; pop_bg_obj_row = oref_row; pop_bg_obj_col = oref_col; + /* ОКНО = прямоугольник самого КУСКА. Смысл оверлея — перекрыть кусок, а + * то, что лежит вне него, и так нарисовано правильным фоном. Даёт сразу + * три вещи: + * - рисуем в разы меньше пикселей (тайл соседа шириной 32 обрезается до + * перекрытия с куском); + * - куски тайла, вовсе не задевающие кусок, отсеиваются предфильтром + * pop_blit_b даром — это и есть вертикальный гейт, отдельного не надо; + * - ov_mark становится НЕ НУЖЕН: всё нарисованное внутри heal-коридора + * куска, а он чистится каждый кадр. + * Замер 2026-08-17: без окна оверлеи шести кусков давали +210 000 в циане + * и +130 000 в «ввод+heal» (объединение ov_mark на полкомнаты). */ + ov_suppress = 1; + pop_t_win_set(mx, my - (int)mh + 1 + POP_YOFF, 64, (uint8_t)(mh + 1)); gfx_set_bank(GFX_BANK_SPRITE); other_overlay_tile(row, col); gfx_set_bank(GFX_BANK_NORMAL); + pop_t_win_clear(); + ov_suppress = 0; pop_bg_obj_row = saved_row; pop_bg_obj_col = saved_col; } @@ -946,7 +970,14 @@ void pop_fore_tile_b(int row, int col) __banked void pop_ceil_fore_tile_b(int col) __banked { int x = POP_COL_XH[col] * 8; - ov_mark(x, 0, 64, CEIL_BAND_H); + /* ov_mark ЗДЕСЬ НЕ НУЖЕН, и это важно для бюджета. Передние куски потолка + * рисуются от dby = 2, то есть занимают строки 0..2 поля, а пометку ставит + * ТОЛЬКО падающий кусок, и только пока он до этих строк достаёт + * (mob_y <= 18) — значит нарисованное целиком лежит в его heal-коридоре + * (mob_y−18 .. mob_y+1), который чистится каждый кадр на своей странице. + * Пока ov_mark тут стоял, его объединение с оверлеями соседей + * растягивалось на полкомнаты и pop_fore_heal стоил 187 500 такта вместо + * 55 000 (замер 2026-08-17). */ pop_t_clip_top = POP_YOFF; pop_t_win_set(x, POP_YOFF, 64, CEIL_BAND_H); ceil_over_kid_tile(col); diff --git a/applications/PoP/roomtest/pop_bg.h b/applications/PoP/roomtest/pop_bg.h index 3b275d3..caeb147 100644 --- a/applications/PoP/roomtest/pop_bg.h +++ b/applications/PoP/roomtest/pop_bg.h @@ -181,7 +181,8 @@ void pop_ceil_fore_tile_b(int col) __banked; /* Оверлей тайла (row,col) ПОВЕРХ падающего куска, чей тайл — (oref_row, * oref_col): порт set_redraw2 из draw_mob (seg007:1149). Порядок обхода * проверяется внутри. */ -void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col) __banked; +void pop_mob_overlay_tile(int row, int col, int8_t oref_row, int8_t oref_col, + int mx, int my, uint8_t mh) __banked; /* Смена комнаты: куски продолжают падать в СВОИХ комнатах (do_mobs крутит * mobs[] независимо от drawn_room), сбрасывается только heal-история. */ diff --git a/applications/PoP/roomtest/pop_room.c b/applications/PoP/roomtest/pop_room.c index 5db2a88..b609d20 100644 --- a/applications/PoP/roomtest/pop_room.c +++ b/applications/PoP/roomtest/pop_room.c @@ -1463,8 +1463,16 @@ static void mob_mark_neighbour(const mob_t *m) * сама структура таблиц: кусок идёт в midtable (draw_mobs), а полоса * потолка — в foretable, и та рисуется позже (draw_tables, seg008:1373). */ if (m->y <= 18) { - pop_set_redraw_fore_above((uint8_t)m->col); - if (m->col + 1 <= 9) pop_set_redraw_fore_above((uint8_t)(m->col + 1)); + /* Пустой тайл полосы потолка не рисует В ПЕРЕДНЕМ СЛОЕ ничего + * (bottom_id = 0, FORE_ANY = 0), а пометка стоит потом двух трамплинов + * — в банк 7 за разбором пометок и в банк 2 за самой отрисовкой, + * ~8 900 такта каждый. А пустой он тут как раз чаще всего: кусок + * существует ровно потому, что плита из своей клетки уже выпала. + * Проверяем pop_tile_code — он резидентный, ~1 100 такта. */ + if (pop_tile_code(-1, m->col)) + pop_set_redraw_fore_above((uint8_t)m->col); + if (m->col + 1 <= 9 && pop_tile_code(-1, m->col + 1)) + pop_set_redraw_fore_above((uint8_t)(m->col + 1)); } if (c > 9) return; if (r >= 0 && r <= 2) pop_set_redraw_fore((uint8_t)(r * 10 + c)); @@ -1481,11 +1489,27 @@ static void mob_overlay_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); + int ytop, ybot; if (c > 9) return; - if (r >= 0 && r <= 2) - pop_mob_overlay_tile(r, c, r, (int8_t)m->col); - if (rt != r && rt >= 0 && rt <= 2) - pop_mob_overlay_tile(rt, c, r, (int8_t)m->col); + ytop = m->y - (int)mob_spr[2] + 1; /* верх спрайта куска */ + ybot = m->y; + /* ПРЕДУСЛОВИЯ ПРОВЕРЯЕМ ЗДЕСЬ, до вызова в банк 2. pop_mob_overlay_tile + * живёт в банке 2, то есть каждый вызов из банка 7 платит межбанковый + * трамплин (~8 900 такта); а условий всего два — тайл не пуст и габарит + * тайла пересекается с габаритом куска. Оба дешёвые: pop_tile_code — + * резидент (~1 100). Замер 2026-08-17: без этого гейта двенадцать + * вызовов на шесть кусков стоили ~130 000 в циане, причём большинство + * ничего не рисовало. + * + * Габарит тайла берём ПОЛНЫМ (63*row+2 .. +66) — какая именно часть + * соседа окажется поверх куска, решит уже окно клипа внутри. Сужать по + * реальной высоте кусков нельзя: у колонн и зеркала база высокая. */ + if (r >= 0 && r <= 2 && pop_tile_code(r, c) && + ybot >= 63 * r + 2 && ytop <= 63 * r + 66) + pop_mob_overlay_tile(r, c, r, (int8_t)m->col, m->x, m->y, mob_spr[2]); + if (rt != r && rt >= 0 && rt <= 2 && pop_tile_code(rt, c) && + ybot >= 63 * rt + 2 && ytop <= 63 * rt + 66) + pop_mob_overlay_tile(rt, c, r, (int8_t)m->col, m->x, m->y, mob_spr[2]); } static void mob_draw_pass(uint8_t pg, uint8_t want_defer)