From 6faf81016aa08af84c1419acf1a73545f504aef3 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Wed, 19 Aug 2026 12:53:49 +0300 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D1=80=20=D1=86=D0=B8?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D0=BE=D0=B9=20=D1=84=D0=B0=D0=B7=D1=8B:=20?= =?UTF-8?q?=D0=BA=D1=80=D1=83=D0=BF=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=BB=D0=B8?= =?UTF-8?q?=D1=88=D0=BD=D0=B5=D0=B3=D0=BE=20=D0=B2=20=D0=BE=D1=82=D1=80?= =?UTF-8?q?=D0=B8=D1=81=D0=BE=D0=B2=D0=BA=D0=B5=20=D0=BF=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=D0=B6=D0=B0=20=D0=BD=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Циан 188 004 не двигался ни от P1, ни от P5, ни от P2b — разложил его зондами. Хорошая новость: Кид УЖЕ пропускается (204 такта на pop_char_draw), то есть надежда P3 сбылась после P1 — метка от чомпера до него больше не дотягивается. Страж же перерисовывается каждый кадр честно: пламя правого факела (0,7) рисуется в ячейке (0,8), где он стоит, и реально накрывает ему голову (пламя занимает y 5..22, страж 12..62). Отрисовка стража — 148 302: pop_char_fore (2 трамплина в банк 2 + обход тайлов) 62 778 42 % клинок (sword_draw + overlay_add + clip_add) 27 522 19 % блит спрайта + clip_char_right 20 982 14 % загрузка кадра и геометрия 12 696 9 % pop_clip_char_top (трамплин банк 4 -> банк 3) 8 658 6 % снимок прямоугольника + cd_clip_add 7 890 5 % gfx_w0_unmap + cd_sig_make 4 968 3 % вход + cd_heal 2 946 2 % Единственная явно лишняя статья — трамплин clip_char_top, и снять его непросто: функции нужны get_tile и таблицы деления из банка 3, перенос в резидент вернёт тот же трамплин внутрь. Остальное — работа, которую персонаж действительно делает. Зонды переставлены с уже закрытых замеров (loose_tick, физика) внутрь pop_cdraw; оснастка снимается позицией P12, когда оптимизация закончится. Co-Authored-By: Claude Opus 5 --- applications/PoP/docs/perf_registry.md | 38 ++++++++++++++++++++++++++ applications/PoP/roomtest/pop_cdraw.c | 9 ++++++ applications/PoP/roomtest/pop_map.c | 3 -- applications/PoP/roomtest/roomtest.c | 11 ++------ 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/applications/PoP/docs/perf_registry.md b/applications/PoP/docs/perf_registry.md index 9ecaa55..300a9a6 100644 --- a/applications/PoP/docs/perf_registry.md +++ b/applications/PoP/docs/perf_registry.md @@ -405,6 +405,44 @@ BUG-LATTICE-DOORTOP, BUG-SEAM-WEDGE-1) — только отдельным за --- +## 5б. Цианная фаза разложена [ЗАМЕР 2026-08-19] + +Фаза 188 004 тактов, и она НЕ менялась ни от P1, ни от P5, ни от P2b. + +| участок | такты | +|---|---:| +| `check_mirror` | 3 198 | +| `loose_mob_draw` + `guard_over_kid` + `skip_mask` | **34 374** | +| **`pop_char_draw(KID)`** | **204** | +| **соперник: `char_draw` + `char_fore`** | **145 896** | +| `fore_needed` + `hp_draw` | 2 598 | +| `char_fore(KID)` + борта | 1 758 | + +**Кид уже пропускается** — 204 такта, то есть надежда P3 всё-таки сбылась +после P1: метка от чомпера до него больше не дотягивается. А страж +перерисовывается каждый кадр, и это ЧЕСТНО: пламя правого факела (0,7) +рисуется в ячейке (0,8), где он стоит, и реально накрывает ему голову +(пламя занимает y 5..22, страж 12..62). + +Отрисовка стража (148 302) по частям: + +| участок | такты | доля | +|---|---:|---:| +| **`pop_char_fore`** (два трамплина в банк 2 + обход тайлов) | **62 778** | 42 % | +| клинок: `pop_sword_draw` + `cd_overlay_add` + `cd_clip_add` | 27 522 | 19 % | +| блит спрайта + `clip_char_right` | 20 982 | 14 % | +| загрузка кадра и геометрия | 12 696 | 9 % | +| **`pop_clip_char_top`** (трамплин банк 4 → банк 3) | 8 658 | 6 % | +| снимок прямоугольника + `cd_clip_add` | 7 890 | 5 % | +| `gfx_w0_unmap` + `cd_sig_make` | 4 968 | 3 % | +| вход + `cd_heal` | 2 946 | 2 % | + +**Вывод: крупного лишнего здесь нет.** Единственная явно лишняя статья — +трамплин `clip_char_top` (8 658), и убрать его непросто: функции нужны +`get_tile` и таблицы деления из банка 3, а перенос в резидент вернёт тот же +трамплин внутрь. Всё остальное — работа, которую персонаж действительно +делает: рисует себя, клинок и передний слой поверх себя. + ## 6. Иерархия референсов (уточнена 2026-08-19) Сравнение трёх реализаций луча видимости показало, что источники не diff --git a/applications/PoP/roomtest/pop_cdraw.c b/applications/PoP/roomtest/pop_cdraw.c index 343f514..252513b 100644 --- a/applications/PoP/roomtest/pop_cdraw.c +++ b/applications/PoP/roomtest/pop_cdraw.c @@ -23,6 +23,7 @@ #include "_pop_kdraw.h" #include "pop_vflip.h" /* зеркальные страницы атласов (зелье инверсии) */ /* меч + блит по id — сосед по банку 4 */ /* атласы/кадр Кида, pop_sword_draw, окна Char */ #include "pop_cdraw.h" +#include "pop_state.h" /* ВРЕМЕННО: зонды pop_dbg_b* */ #include "pop_bg.h" /* POP_YOFF, pop_fore_over_char, pop_fore_set_clip */ #include "_pop_draw.h" /* pop_onscreen_cols / pop_heal_fast */ #include "pop_map.h" /* hitp_curr/hitp_max — HP Кида; clip_char */ @@ -503,6 +504,7 @@ void pop_char_draw(uint8_t who) __banked * сдвинул. Здесь прошлый кадр уже точно надо стереть. Если heal в этом * кадре был, вызов бесплатен: valid/ovalid уже сняты. */ cd_heal(who); + pop_dbg_b1(); /* ЗАМЕР: cd_heal сделан */ s->cw = 0; /* окно fore-клипа: пусто, пока не нарисовали */ s->fpw = 0; /* габарит кадра: нечего перекрывать */ @@ -633,7 +635,9 @@ void pop_char_draw(uint8_t who) __banked * так что «убрать последние bcut строк вывода» = «не рисовать * первые bcut строк оригинала», а это ровно то, что просит * clip_char. */ + pop_dbg_b2(); /* ЗАМЕР: кадр загружен, геометрия готова */ ct = pop_clip_char_top(fp_x, obj_y, w, h); + pop_dbg_b3(); /* ЗАМЕР: clip_char_top (трамплин банк 4->3) */ if (!pop_upside) { if (ct > top) { skip = ct - top; @@ -691,6 +695,7 @@ void pop_char_draw(uint8_t who) __banked (uint8_t)(vis_w == w ? 0 : vis_w)); } else rows = 0; gfx_set_bank(GFX_BANK_NORMAL); + pop_dbg_b4(); /* ЗАМЕР: спрайт выведен */ /* heal чистит ТОЛЬКО нарисованное: иначе стирается кромка пола над * срезом (мусор/дыра в кладке на второй странице дабл-буфера). */ s->x[dp] = bx + lskip; s->y[dp] = top; @@ -700,6 +705,7 @@ void pop_char_draw(uint8_t who) __banked s->fpw = w; s->fph = h; /* габарит КАДРА (не обрезанный): */ /* по нему считается футпринт */ } + pop_dbg_m15(); /* ЗАМЕР: снимок прямоугольника сделан */ if (hurt) cd_splash(s, who, pages, fp_x, obj_y); /* add_sword_to_objtable (seg006:1798): клинок отдельным спрайтом поверх * персонажа — со своим прямоугольником heal. */ @@ -710,6 +716,7 @@ void pop_char_draw(uint8_t who) __banked cd_clip_add(s, sx, sy, sw, sh); } } + pop_dbg_m16(); /* ЗАМЕР: splash + клинок сделаны */ gfx_w0_unmap(); /* Что именно нарисовано на ЭТОЙ странице — снимок для пропуска * следующих кадров (DRAW-COST). Только если кадр РЕАЛЬНО рисовали: @@ -722,6 +729,7 @@ void pop_char_draw(uint8_t who) __banked * между ними (BUG-CHAR-STALE-PAGE). Без снимка слот просто не будет * «тихим», и следующий кадр начнётся с heal — старое сотрётся. */ if (w && h) cd_sig_make(who, &cd_sig[who][dp]); + pop_dbg_b5(); /* ЗАМЕР: pop_char_draw целиком */ } void pop_char_fore(uint8_t who) __banked @@ -738,6 +746,7 @@ void pop_char_fore(uint8_t who) __banked if (s->fpw) pop_fore_over_char(who == POP_CH_KID ? &Kid : &Guard, s->fpx, s->fpy, s->fpw, s->fph); + pop_dbg_b6(); /* ЗАМЕР: pop_char_fore целиком */ } /* ---- ОТРАЖЕНИЕ В ЗЕРКАЛЕ (check_mirror, seg003:0798) ---------------- * diff --git a/applications/PoP/roomtest/pop_map.c b/applications/PoP/roomtest/pop_map.c index bcd1d71..4843edb 100644 --- a/applications/PoP/roomtest/pop_map.c +++ b/applications/PoP/roomtest/pop_map.c @@ -2776,11 +2776,8 @@ void pop_loose_tick(void) __banked } loose_any = found; } - pop_dbg_m10(); /* ЗАМЕР: оба цикла по тайлам пройдены */ pop_loose_mob_tick(); /* продвинуть+нарисовать падающий кусок (после тайлов) */ - pop_dbg_m11(); /* ЗАМЕР: pop_loose_mob_tick сделан */ check_loose_fall_on_kid(); /* seg007:1192 — плита падает Киду на голову */ - pop_dbg_m12(); /* ЗАМЕР: check_loose_fall_on_kid сделан */ /* Отложенная допечка ПРОШЛОГО приземления на вторую страницу — ПЕРЕД * разбором нового: иначе при двух плитах, севших подряд, land_bake * затирался новым и на одной странице оставался старый пол. */ diff --git a/applications/PoP/roomtest/roomtest.c b/applications/PoP/roomtest/roomtest.c index d97db23..9451eaf 100644 --- a/applications/PoP/roomtest/roomtest.c +++ b/applications/PoP/roomtest/roomtest.c @@ -365,7 +365,6 @@ int main(void) pop_check_skel(); /* спецсобытие ур.3: скелет встаёт */ pop_check_mouse(); /* спецсобытие ур.8: мышь жмёт кнопку */ pop_check_killed_shadow(); /* спецсобытие ур.12: смерть тени = смерть Кида */ - pop_dbg_b6(); /* ЗАМЕР: три спецсобытия уровней сделаны */ pop_frame_timers(); /* timers (seg003:0735): вспышка слияния */ pop_dbg_m9(); /* ЗАМЕР: pop_frame_timers сделан */ pop_check_can_guard_see_kid(); /* луч видимости — ДО логики персонажей */ @@ -375,32 +374,25 @@ int main(void) * тот на ней уже нарисован правильно: ни стирать, ни рисовать заново * не надо (DRAW-COST, контракт в pop_cdraw.h). Решение * перепроверяется перед самой отрисовкой — тик ещё впереди. */ - pop_dbg_m15(); /* ЗАМЕР: pop_ctrl_tick сделан */ skip = pop_char_skip_mask(); if (!(skip & 1)) pop_char_heal(POP_CH_KID); /* стереть прошлый кадр ЭТОЙ страницы */ if (!(skip & 2)) pop_char_heal(POP_CH_OPP); /* и прошлый кадр соперника */ /* Отражение в зеркале — свой heal: у него нет слота pop_cd и в * skip-маске он не участвует (рисуется/стирается безусловно, живёт * только пока Кид стоит на тайле зеркала). */ - pop_dbg_m16(); /* ЗАМЕР: heal обоих персонажей сделан */ pop_mirror_heal(); pop_fore_heal(); /* + следы оверлея (шире прямоугольника Kid) */ PROF(2); /* логика (тот же синий) */ pop_dbg_m1(); /* ЗАМЕР: начало логики */ kid_tick(); /* play_seq: следующий кадр */ - pop_dbg_b1(); /* ЗАМЕР: kid_tick сделан */ pop_phys_tick(); /* K3: падение/приземление/стена */ - pop_dbg_b2(); /* ЗАМЕР: pop_phys_tick сделан */ pop_guard_tick(); /* логика стража — В БАНКЕ (guards.c) */ - pop_dbg_b3(); /* ЗАМЕР: pop_guard_tick сделан */ pop_guard_phys_tick(); /* и его физика — та же, что у Кида (play_guard_frame) */ /* Боёвка, порядок как в play_frame (seg000:872): сначала пометить * попадания с обеих сторон, потом применить, потом свести HP. */ - pop_dbg_b4(); /* ЗАМЕР: pop_guard_phys_tick сделан */ pop_check_sword_hurting(); pop_check_sword_hurt(); pop_do_delta_hp(); - pop_dbg_b5(); /* ЗАМЕР: боёвка сделана */ pop_guard_fallout(); /* страж провалился ниже комнаты — убрать (seg002:241) */ if (pop_fell_out) { /* Kid пересёк нижнюю границу комнаты */ uint8_t down = pop_room_link(cur_room, LINK_DOWN); @@ -647,6 +639,7 @@ int main(void) * фон кадра (перепечатки тайлов, шов) обязан быть готов ДО любого * спрайта, иначе перепечатка ложится поверх уже нарисованного куска — * так окно задней стены и передняя грань пола оказывались на плите. */ + pop_dbg_m10(); /* ЗАМЕР: check_mirror сделан */ pop_loose_mob_draw(); { /* Кто позже — тот поверх; порядок задаёт обход тайлов, а не роль * персонажа (см. guard_over_kid). Отрисовка одна на всех Char @@ -659,6 +652,7 @@ int main(void) if (!g_after && !(skip & 2)) { pop_char_draw(POP_CH_OPP); pop_char_fore(POP_CH_OPP); } PROF(6); /* кадр Кида (тот же циан) */ if (!(skip & 1)) pop_char_draw(POP_CH_KID); /* спрайт + брызги + клинок */ + pop_dbg_m11(); /* ЗАМЕР: Кид нарисован */ #if DBG_KIDOBJ /* ВРЕМЕННО: трасса для сверки с SDLPoP кадр в кадр. Место — то * же, что у оригинала (конец add_kid_to_objtable), и ВНЕ skip: @@ -679,6 +673,7 @@ int main(void) /* ПЕРЕДНИЙ СЛОЙ по пометкам — последним в кадре, как foretable у * оригинала (draw_tables, seg008:1373). Пометки ставят сами * объекты вокруг себя (pop_redraw.h). */ + pop_dbg_m12(); /* ЗАМЕР: соперник + mob поверх сделаны */ pop_fore_needed(); pop_hp_draw(); /* полоса HP — только при изменении */ PROF(6); /* fore поверх Kid + борта */