Files
Emil dd02f9cae8
Native and manual checks / native (ubuntu-24.04) (push) Waiting to run
Native and manual checks / native (windows-2025) (push) Waiting to run
Native and manual checks / manual (push) Waiting to run
Windows editor and software Vulkan / windows-graphics (push) Waiting to run
Complete MVP acceptance, publish platform evidence and finalize the manual
2026-09-18 06:23:32 +03:00

222 lines
43 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Stack, Row, Grid, H1, H2, H3, Text, Pill, Button, Table, Divider, Card, CardHeader, CardBody, CollapsibleSection, useHostTheme, useCanvasState, useCanvasAction } from "cursor/canvas";
const root = "docs/studies/";
const ue = "../UnrealEngine/Engine/";
const reports = [
{ name: "Выводы и устройство нашего движка", path: "14-engine-blueprint.md" },
{ name: "Unreal · графика", path: "07-unreal-graphics-source-study.md" },
{ name: "Godot · UX", path: "08-godot-ux-source-study.md" },
{ name: "Unity · UX и расширения", path: "09-unity-ux-extensibility-study.md" },
{ name: "Blender · инструменты", path: "10-blender-editor-patterns.md" },
{ name: "ECS · применение и удобство", path: "11-ecs-and-ergonomics.md" },
{ name: "MCP и Blender integration", path: "12-mcp-and-blender-integration.md" },
{ name: "Сборка игры и выбор стека", path: "13-build-pipeline-and-stack.md" },
{ name: "15 · GPU renderer: проходы, барьеры и история", path: "15-renderer-implementation-notes.md" },
{ name: "16 · C++: компоненты, метаданные и языковые модули", path: "16-native-gameplay-and-metadata.md" },
{ name: "17 · Asset pipeline и Blender roundtrip", path: "17-asset-pipeline-and-blender-roundtrip.md" },
{ name: "18 · Сборка, cook и доставка игры", path: "18-build-cook-and-delivery.md" },
];
const graphics = [
{ id: "hzb", name: "GPU Scene + two-pass HZB", stage: "P2 · после MVP", benefit: "GPU сам выбирает видимые объекты и повторно проверяет ранее скрытые.", fact: "Main cull/raster использует temporal visibility; текущая глубина строит новый HZB для post pass. Постоянные таблицы instances обновляются частично.", start: "Resident instance tables → frustum culling → indirect draw → HZB → очередь повторной проверки. Обычный hardware raster достаточно.", risk: "Консервативные bounds, previous transforms, near plane, reverse-Z и переполнение очередей. В открытой сцене дополнительный проход может проиграть.", check: "Закрытая и открытая сцены, быстрый pan, дверь и teleport. Сравнить depth/ID с HZB-off и полное GPU время.", source: "Source/Runtime/Renderer/Private/Nanite/NaniteCullRaster.cpp", line: 7008 },
{ id: "visibility", name: "Visibility buffer + material bins", stage: "Дальнейшее исследование", benefit: "Разделяет обработку геометрии и дорогой material shading.", fact: "Nanite сохраняет surface ID/depth, восстанавливает атрибуты и группирует shading work. Материалам с derivatives нужны корректные quads.", start: "Hardware depth + integer ID target, фиксированные PBR-классы, затем compute resolve и material bins.", risk: "UV derivatives, mip selection и деформация. Binning и вспомогательные buffers имеют стоимость; не каждый материал выигрывает.", check: "Сравнение geometry + binning + resolve с G-buffer при росте overdraw и числа классов материалов.", source: "Shaders/Private/Nanite/NaniteShadeBinning.usf", line: 959 },
{ id: "geometry", name: "Кластерный LOD и streaming", stage: "P4 · после обычного mesh LOD", benefit: "Детализация выбирается по экранной ошибке, память ограничивается пулом страниц.", fact: "Согласованные группы упрощаются offline; runtime держит drawable coarse fallback и учитывает зависимости страниц при eviction.", start: "Сначала обычный mesh LOD с hysteresis. Отдельно исследовать resident cluster LOD с корректным cut; затем coarse root, pool, page table, feedback и budgeted upload.", risk: "Независимый LOD соседних meshlets даёт щели. LRU без зависимостей ломает decoding. Software raster — отдельный поздний этап.", check: "Контент больше пула, задержки I/O, поворот камеры: нет дыр, ограничена VRAM, видимы page churn и LOD error.", source: "Source/Runtime/Engine/Private/Rendering/NaniteStreamingManager.cpp", line: 2579 },
{ id: "lumen", name: "Lumen: hybrid tracing + caches", stage: "P4 · исследование GI", benefit: "Сокращает дорогие world traces и повторную оценку освещения.", fact: "Экранные hits проверяются; незавершённые rays уплотняются и передаются HWRT или software-пути. Probes/cache получают бюджет обновлений.", start: "Сначала baked indirect light и reflection probes. Затем screen-space дополнения с fallback; world tracer, diffuse probes и budgeted cache — отдельное исследование.", risk: "Screen/world mismatch, stale lighting, утечки через стены, teleport. HWRT и mesh SDF — альтернативы, не обязательная цепочка.", check: "Тонкая стена, свет за камерой, дверь и смена emissive. Замерить переходы backend и кадры до сходимости.", source: "Source/Runtime/Renderer/Private/Lumen/LumenScreenProbeTracing.cpp", line: 678 },
{ id: "mega", name: "MegaLights: выборка света", stage: "После clustered lights и temporal", benefit: "Ограничивает число дорогих shadow samples и оценивает свет стохастически.", fact: "Weighted sampling и history visibility guiding выбирают lights; confidence-aware temporal/spatial denoising восстанавливает сигнал.", start: "Clustered light lists, небольшой sample budget, visibility backend, корректная компенсация вероятностей, rejection и denoiser.", risk: "Обход кандидатов остаётся. Фиксированный бюджет лучей не равен постоянной стоимости всей системы; при плотном свете растёт шум.", check: "10/100/1000 lights, включение яркого света, glossy floor. Отдельно считать selection, tracing, denoise и temporal error.", source: "Shaders/Private/MegaLights/MegaLightsSampling.usf", line: 409 },
{ id: "vsm", name: "Virtual Shadow Maps", stage: "P4 · после shadow atlas", benefit: "Обновляются востребованные теневые страницы, статика переиспользуется.", fact: "GPU отмечает pages, выделяет physical backing и отдельно учитывает static/dynamic invalidation. Dirty flags сохраняются вне видимого кадра.", start: "Один directional light, page table, фиксированный atlas, dirty list и обычный depth raster. Затем clipmaps.", risk: "Поворот солнца, деформации и маленький пул могут уничтожить пользу cache. Нужна диагностика invalidations.", check: "Прогрев, движущийся объект вне кадра, возврат камеры, teleport и pool exhaustion; сравнить с full refresh.", source: "Shaders/Private/VirtualShadowMaps/VirtualShadowMapPhysicalPageManagement.usf", line: 280 },
{ id: "tsr", name: "TSR: доверие к истории", stage: "P3 · после MVP", benefit: "Восстанавливает детализацию и подавляет aliasing при меньшем render resolution.", fact: "Depth-aware velocity dilation, disocclusion/rejection, clamp и validity дополняют историю цвета. Мерцание анализируется отдельно.", start: "Правильные velocity/jitter, reprojection, depth rejection, neighborhood clamp, validity и spatial fallback.", risk: "Ghosting против стабильности. Нельзя исправить неверные motion vectors одним фильтром. TSR не заменяет lighting denoiser.", check: "Провода, pan, раскрытие фона, particles, emissive, exposure и camera cut. Проверять последовательности кадров.", source: "Shaders/Private/TemporalSuperResolution/TSRUpdateHistory.usf", line: 1249 },
{ id: "substrate", name: "Substrate: материал по бюджету", stage: "Дальнейшее исследование", benefit: "Простые области используют дешёвый shading, сложность материалов ограничена.", fact: "Компилятор упрощает граф под bytes/closures budget; GPU классифицирует тайлы simple/single/complex и строит indirect lists.", start: "Два класса PBR/clear coat, feature flags и simple/complex tiles. Небольшое число closures вместо произвольного графа.", risk: "Один complex pixel может усложнить тайл; scattered materials уменьшают пользу. Compile-time budget не равен runtime timing.", check: "Simple-only, локальные complex и шахматная смесь: classification + lighting целиком, качество и память.", source: "Shaders/Private/Substrate/SubstrateMaterialClassification.usf", line: 358 },
{ id: "glints", name: "Glints: фильтруемые микроблики", stage: "После MVP · BRDF эксперимент", benefit: "Характерный вид краски с частицами, блестящих покрытий и снега.", fact: "Распределение микрофасеток фильтруется с учётом UV footprint и LOD; интеграция содержит переход к GGX.", start: "Один opaque материал, tangent basis, derivatives, LUT и один свет. Система слоёв Substrate не обязательна.", risk: "Фильтрация математически сложнее случайных ярких точек. Устойчивость видна только при движении и изменении масштаба.", check: "Движение света/камеры, roughness/density sweep и supersampled reference; энергия, мерцание, добавочное GPU время.", source: "Shaders/Private/Substrate/Glint/GlintThirdParty.ush", line: 425 },
{ id: "rdg", name: "Render graph", stage: "M2 · минимальный Render Graph", benefit: "Описывает зависимости проходов, барьеры и время жизни временных ресурсов.", fact: "RDG удаляет ненужные проходы, компилирует transitions и учитывает overlap GPU очередей при работе с памятью.", start: "MVP: одна очередь, явные reads/writes, barriers, external outputs и lifetime. Позже pass culling, resource reuse, aliasing и async compute.", risk: "CPU-порядок не гарантирует завершение GPU. Persistent history и frames in flight требуют отдельного lifetime.", check: "MVP: корректные transitions и deferred destruction, validation чиста, видны CPU/GPU cost и bytes. После pass culling — удаление ненужных предшественников.", source: "Source/Runtime/RenderCore/Private/RenderGraphBuilder.cpp", line: 1327 },
];
const decisions = [
["Платформа и графика", "Linux/Windows, 2D/3D. SDL3; собственные Vulkan 1.3 backend и Render Graph. Slang → SPIR-V, совместимый HLSL. Базовый путь без обязательного RT."],
["Ядро и gameplay", "C++, EnTT; Box2D для 2D и Box3D (erincatto/box3d) для 3D. Gameplay — static library в dev/release Player. После MVP обязательно добавить Lua; каждой игре разрешено обходиться без Lua."],
["Редактор", "Собственный retained UI на C++: декларативные layout/styles, тёмная тема по умолчанию. Dear ImGui — только инструменты отладки. Inspector, Undo и MCP используют один authoring service."],
["Данные", "JSON authoring со stable IDs; бинарные cooked assets. Вложенные scene templates со sparse overrides. Generated outputs не заменяют вручную настроенные экземпляры."],
["Процессы и расширения", "Player — отдельный процесс и окно. C++: stop → build → restart. Editor plugins — DLL/SO под точный SDK, смена с перезапуском; schema export — helper process."],
["Сборка", "CMake + Ninja; Clang на Linux, clang-cl + Windows SDK на Windows. Code compile, shader compile, cook и package — отдельные стадии с логами и отменой."],
];
const steps = [
{ name: "M0 · Воспроизводимый фундамент", task: "C++ targets Core/Runtime/Editor/Player/SchemaExporter, CMake/Ninja, Clang/clang-cl, dev/release, dependency pins и CI Linux/Windows.", pass: "Чистая сборка и запуск на обеих ОС; Runtime/Player не зависят от Editor/MCP, версии и notices записаны." },
{ name: "M1 · Документы, метаданные и команды", task: "Stable IDs, типизированные C++ schemas, JSON, AuthoringService, revisions, transactions, Undo/Redo и миграции без GUI.", pass: "Roundtrip сохраняет смысл/ID; неверная транзакция не применяется частично; старый revision даёт конфликт." },
{ name: "M2 · Платформа и базовая графика", task: "SDL3, Vulkan 1.3, Render Graph на одной очереди, Slang/reflection; direct draws/CPU frustum, 2D layers/sprites, 3D simple PBR и обычные тени.", pass: "Работают resize/minimize и безопасная замена shader; validation без ошибок. Player получает SPIR-V/metadata без compiler." },
{ name: "M3 · Runtime, gameplay, физика и Player", task: "EnTT, gameplay static library, schema helper, lifecycle/tick и Box2D/Box3D. Player — отдельные процесс/окно, C++ stop/build/restart.", pass: "C++-компонент виден в Inspector, влияет на объект/физику; Stop сохраняет authoring-сцену. Ошибка build не выдаёт старую сборку за новую; MCP в Player нет." },
{ name: "M4 · Собственная UI-основа", task: "Retained tree, input/focus, текст/DPI, declarative layout/styles dark-first, widgets и минимальный docking. ImGui только для debug.", pass: "Клавиатура, кириллица/IME и DPI проверены; layout/styles обновляются без C++ rebuild; один drag — один Undo." },
{ name: "M5 · Ресурсы и Blender", task: "AssetId, dependency/cache/artifact manifests, обычный GLB без addon; optional Blender addon для стабильных IDs и согласованного export generation.", pass: "Reimport сохраняет компоненты/overrides; rename со stable ID сохраняет связь, удаление даёт конфликт. Ошибка/отмена сохраняет последний рабочий artifact." },
{ name: "M6 · Редактор и повторно используемые сцены", task: "Scene Tree, Inspector, Asset Browser, viewports и Console; nested templates, sparse overrides, origin/Revert, duplicate с remap внутренних ссылок.", pass: "Без ручного JSON создать два экземпляра, изменить один, обновить источник, отменить и переоткрыть без потери identity. Apply/variants позже." },
{ name: "M7 · MCP редактора и расширения", task: "Authoring/query/schema, transactions, jobs/import/build, Play/Stop и editor diagnostics. Headless authoring/build; editor DLL/SO под exact SDK с restart.", pass: "Ручные и MCP-операции эквивалентны. Нет runtime-entity read/write через MCP; transport отсутствует в Player/SchemaExporter. Неизвестные данные пакета сохраняются." },
{ name: "M8 · Экспорт законченных примеров", task: "BuildService: validate → C++/schema → Slang/cook → package. Обе небольшие 2D/3D-игры с C++ gameplay и понятным управлением.", pass: "Все четыре сочетания 2D/3D × Linux/Windows запускаются без Editor, MCP, Blender и исходного дерева; runtime-зависимости документированы." },
{ name: "M9 · Приёмка MVP", task: "Новая установка, authoring руками/MCP, conflict/Undo/recovery, reimport и ошибка Player; измерить startup/edit/build/run, CPU/GPU и память.", pass: "Устранены блокеры UX/форматов/export, записаны оборудование, результаты и ограничения. Только после проверок — MVP tag." },
{ name: "P1 · Lua и скорость итераций", task: "Обязательный Lua-модуль после MVP, optional per game: API/handles/schemas, diagnostics и lifecycle reload. Улучшать измеренный build/edit цикл.", pass: "C++ и Lua используют одни данные/фазы; C++-only export без Lua runtime. P1 не обязан ждать завершения графических веток." },
{ name: "P2 · GPU-driven visibility и LOD", task: "GPU instance IDs/frustum → fixed indirect batches → current HZB/debug view → two-pass occlusion → обычный mesh LOD/hysteresis. Direct reference сохраняется.", pass: "Нет дыр при открытии двери/исчезновении заслона/cut; проверены пустые/полные bins, удаление, teleport/resize. Полный кадр измерен на открытой и закрытой сценах." },
{ name: "P3 · Освещение, тени и temporal reconstruction", task: "Local lights, clustered/Forward+ по необходимости, cascaded sun shadows и local atlas; затем motion vectors/jitter/rejection → TAA → upscaling.", pass: "Shadow views имеют собственную видимость/бюджеты. Проверены disocclusion, тонкая геометрия, движение, resize/cut; видны ghosting и полная цена эффекта." },
{ name: "P4 · Непрямой свет и продвинутая геометрия", task: "Baked indirect/reflection probes → исследование динамической GI. Cluster LOD, streaming, visibility buffer, VSM/VT — самостоятельные проекты после профилирования.", pass: "Проверены свет вне экрана, заслоны, утечки и бюджеты. RT дополнительный; эквивалент Nanite/Lumen не обещается, порядок веток зависит от игры." },
{ name: "P5 · Инструменты создания игр", task: "Animation/skinning, игровой UI/audio, tilemaps/2D tools, material editor, variants/Apply и task scheduler после профилирования.", pass: "Объём выбирается по нуждам игр; новые системы сохраняют общий authoring-контракт и согласованные фазы. Это не обязательства MVP." },
{ name: "P6 · Доставка и экосистема", task: "Проверяемый native ABI/SDK, migrations, accessibility/localization, несколько окон, embedded Player, Blender live link; другие backend/targets отдельно.", pass: "Совместимость подтверждается для выбранных сценариев. Multiplayer/rollback требуют отдельного контракта; MCP остаётся редакторской интеграцией." },
];
function Architecture() {
const t = useHostTheme();
const boxes = [
[20, 15, 245, "Ручной C++ редактор", "Retained UI · Inspector · gizmos"],
[290, 15, 245, "MCP / CLI редактора", "Authoring · import · build · Play/Stop"],
[560, 15, 245, "Официальный Blender", "Обычный GLB + optional addon"],
[20, 120, 515, "AuthoringService", "Schema · validation · revision · transaction · Undo"],
[560, 120, 245, "Asset pipeline", "Source + recipe → cooked artifacts"],
[20, 225, 245, "JSON scene templates", "Stable IDs · sparse overrides"],
[290, 225, 245, "Scene compiler", "Authoring → binary runtime data"],
[560, 225, 245, "Player · отдельный процесс", "EnTT · C++ · physics · без MCP"],
[560, 330, 245, "Vulkan 1.3 renderer", "MVP: direct draws + CPU frustum"],
] as const;
return <div style={{ overflowX: "auto" }}><svg viewBox="0 0 825 425" role="img" aria-label="Принятая архитектура и границы подсистем Faset" style={{ width: "100%", minWidth: 650 }}>
<defs><marker id="engine-arrow" markerWidth="7" markerHeight="7" refX="6" refY="3.5" orient="auto"><path d="M0 0 L7 3.5 L0 7" fill={t.text.tertiary}/></marker></defs>
<g fill="none" stroke={t.stroke.primary} strokeWidth="1.5" markerEnd="url(#engine-arrow)">
<path d="M140 80 V118 M410 80 V118 M685 80 V118 M560 155 H537 M140 185 V223 M265 258 H288 M535 258 H558 M685 185 V223 M685 290 V328"/>
</g>
{boxes.map(([x,y,w,title,sub]) => <g key={title}>
<rect x={x} y={y} width={w} height={65} rx={5} fill={title === "AuthoringService" ? t.fill.secondary : t.bg.elevated} stroke={t.stroke.secondary}/>
<text x={x + 13} y={y + 25} fontSize="13" fontWeight="600" fill={title === "AuthoringService" ? t.accent.primary : t.text.primary}>{title}</text>
<text x={x + 13} y={y + 47} fontSize="10.5" fill={t.text.secondary}>{sub}</text>
</g>)}
<text x="20" y="350" fontSize="12" fill={t.text.secondary}>MCP читает и меняет только authoring-документы.</text>
<text x="20" y="373" fontSize="12" fill={t.text.secondary}>Нет доступа к runtime worlds / игровым сессиям.</text>
<text x="20" y="413" fontSize="11" fill={t.text.tertiary}>Запланированный поток данных; Play/Stop управление дочерним процессом со стороны Editor.</text>
</svg></div>;
}
export default function EngineResearch() {
const t = useHostTheme();
const dispatch = useCanvasAction();
const [tab, setTab] = useCanvasState("engine-research-tab", "Решение");
const [graphicsId, setGraphicsId] = useCanvasState("engine-graphics-id", "rdg");
const [client, setClient] = useCanvasState("engine-client", "Руками");
const selected = graphics.find(g => g.id === graphicsId) ?? graphics[0];
const open = (path: string, line?: number) => dispatch({ type: "openFile", path, ...(line ? { selection: { startLineNumber: line, startColumn: 1, endLineNumber: line, endColumn: 1 } } : {}) });
const sourceUrl = "https://github.com/EpicGames/UnrealEngine/blob/16d75d84714512edfb744e1fd0a59e9c74d57873/Engine/" + selected.source + "#L" + selected.line;
return <Stack gap={20} style={{ maxWidth: 1120, margin: "0 auto", padding: 24, color: t.text.primary }}>
<Row justify="space-between" align="start" wrap>
<Stack gap={5}><Text size="small" tone="tertiary">FASET ENGINE · РЕШЕНИЯ 18.09.2026</Text><H1>Архитектура, MVP и развитие графики</H1><Text tone="secondary">Linux + Windows · 2D / 3D · C++ Editor · Blender</Text></Stack>
<Row gap={8} wrap><Button onClick={() => open("README.md")}>О проекте</Button><Button onClick={() => open("PLAN.md")}>План до / после MVP</Button><Button onClick={() => open("docs/ARCHITECTURE.md")}>Архитектура</Button></Row>
</Row>
<Card><CardBody><Text><strong>C++ MVP принят в зафиксированных профилях Linux и Windows.</strong> Эта карта работающий просмотрщик исследования, не редактор Faset. Результаты и ограничения опубликованы в docs/validation/mvp-acceptance.md: Windows проверен через software Vulkan, физические Windows GPU и системный IME требуют отдельного покрытия. Исследовательские графические направления не считаются готовыми функциями. MCP предусмотрен строго в Editor; Player не содержит MCP и не предоставляет ему runtime worlds или игровые сессии.</Text></CardBody></Card>
<Row gap={7} wrap>{["Решение", "Графика", "ECS и данные", "MCP и Blender", "Стек и экспорт", "Прототипы", "Источники"].map(name => <span key={name}><Pill active={tab === name} onClick={() => setTab(name)}>{name}</Pill></span>)}</Row>
<Divider/>
{tab === "Решение" && <Stack gap={22}>
<H2>Принятые решения</H2>
<Grid columns="repeat(auto-fit, minmax(280px, 1fr))" gap={24}>{decisions.map(([name, description]) => <Stack gap={8} key={name}><H3>{name}</H3><Text>{description}</Text></Stack>)}</Grid>
<H2>Зачем изучались другие движки</H2>
<Table headers={["Источник", "Механизм", "Применение в Faset"]} rows={[
["Godot", "Scene/Resource, overrides, короткий Play loop", "Scene templates, shared property/Undo слой"],
["Unity", "Serialized edits, tools, binding, plugin boundaries", "Multi-edit Inspector и явные границы editor plugins"],
["Unreal", "GPU scheduling, budgets, temporal validity", "Минимальный graph в MVP; GPU visibility и сложная графика позже"],
["Blender", "Operator registry, modal preview, отмена", "Общие authoring-команды для меню, hotkey и MCP редактора"],
]}/>
<Card><CardHeader>Запланированный пользовательский контракт</CardHeader><CardBody><Stack gap={8}><H3>Понятно, откуда пришло значение и что изменится</H3><Text>Shared/local asset, наследование template, sparse override, preview и Undo видны в редакторе. Диагностика импорта и сборки объясняет входы, ошибки и результат. Вложенность сцен не требует знать внутреннее устройство ECS.</Text></Stack></CardBody></Card>
<Button onClick={() => open(root + reports[0].path)}>Обоснование архитектуры по исходникам</Button>
</Stack>}
{tab === "Графика" && <Stack gap={18}>
<H2>В MVP direct renderer; сложные механизмы после него</H2>
<Text>Vulkan 1.3 + собственный Render Graph, CPU frustum, sprites/layers/HUD для 2D и static meshes/simple PBR/обычные тени для 3D. Далее: GPU frustum/indirect HZB обычный LOD расширение света/теней temporal GI. Карточки ниже сохраняют результаты изучения UE, а не список уже реализованных функций.</Text>
<Row gap={6} wrap>{graphics.map(g => <span key={g.id}><Pill active={g.id === selected.id} onClick={() => setGraphicsId(g.id)}>{g.name}</Pill></span>)}</Row>
<Row justify="space-between" wrap><H2>{selected.name}</H2><Pill>{selected.stage}</Pill></Row>
<Text weight="bold">{selected.benefit}</Text>
<Grid columns="repeat(auto-fit, minmax(300px, 1fr))" gap={24}>
<Stack gap={8}><H3>Подтверждено кодом UE</H3><Text>{selected.fact}</Text><a href={sourceUrl} target="_blank" rel="noreferrer">Upstream · строка {selected.line}</a><Button onClick={() => open(ue + selected.source, selected.line)}>Опциональный локальный checkout</Button><Text size="small" tone="tertiary">{selected.source} · Для upstream UE требуется доступ Epic.</Text></Stack>
<Stack gap={8}><H3>Планируемый минимальный эксперимент</H3><Text>{selected.start}</Text><H3>Ограничение</H3><Text>{selected.risk}</Text></Stack>
</Grid>
<Divider/><H3>Будущий критерий проверки</H3><Text>{selected.check}</Text>
<Row gap={8} wrap><Button onClick={() => open(root + reports[1].path)}>Полное исследование UE</Button><Button onClick={() => open(root + reports[8].path)}>Проходы, барьеры и история GPU renderer</Button><Button onClick={() => open("PLAN.md")}>Граница MVP и порядок этапов</Button></Row>
<Text size="small" tone="tertiary">UE 5.8.2 · commit 16d75d847145 · статический разбор C++ / HLSL. Полное воспроизведение Nanite/Lumen не обещается; минимальные GPU и driver matrix ещё требуют проверки.</Text>
</Stack>}
{tab === "ECS и данные" && <Stack gap={20}>
<H2>EnTT в runtime; сцены и метаданные в authoring</H2>
<Architecture/>
<Grid columns="repeat(auto-fit, minmax(290px, 1fr))" gap={24}>
<Stack gap={8}><H3>Authoring принято</H3><Text>JSON, stable SceneEntityId и asset IDs, nested scene templates, sparse overrides, revision, Undo и save. Generated imports отделены от пользовательских настроек. Схема компонентов экспортируется helper process.</Text></Stack>
<Stack gap={8}><H3>Runtime запланировано</H3><Text>EnTT, generational handles, компактные компоненты, queries и structural commands. Scene compiler выдаёт бинарные cooked данные; authoring ID не равен временной позиции entity в ECS. UI/Undo/GPU allocator не обязаны храниться в ECS.</Text></Stack>
</Grid>
<Table headers={["Операция", "Контракт", "Граница"]} rows={[
["Изменить template в Inspector/MCP", "Authoring transaction + revision + Undo", "Меняет документ; не редактирует запущенную игру"],
["Обновить Position в C++ системе", "Runtime-компоненты по контракту scheduler", "Текущая фаза / render extraction внутри Player"],
["Spawn/despawn во время query", "Runtime structural command buffer", "Документированный sync point; не editor Undo"],
["Изменить C++ gameplay", "Gameplay static library → Player link", "Stop → build → restart; без обещания C++ hot reload"],
]}/>
<Text>Lua обязательно появится после MVP как отдельный модуль с ограниченным API. Использование каждой игрой необязательно; произвольный C++ не получает bindings автоматически.</Text>
<Row gap={8} wrap><Button onClick={() => open(root + reports[5].path)}>Исследование ECS</Button><Button onClick={() => open(root + reports[9].path)}>C++ и метаданные</Button></Row>
</Stack>}
{tab === "MCP и Blender" && <Stack gap={20}>
<H2>MCP только редактор и его authoring-сервисы</H2>
<Text>Разрешённый планируемый охват: документы и транзакции, validation, import/build jobs, Play/Stop и логи Editor; headless для тех же доступных без UI операций. Play запускает отдельный Player, Stop завершает его. Ни runtime world inspection/mutation, ни API игровых сессий, ни MCP server в Player не предусмотрены.</Text>
<Row gap={8}>{["Руками", "Через MCP"].map(v => <span key={v}><Pill active={client === v} onClick={() => setClient(v)}>{v}</Pill></span>)}</Row>
<Grid columns="repeat(auto-fit, minmax(290px, 1fr))" gap={24}>
<Stack gap={8}><H3>{client === "Руками" ? "Переместить пять источников в документе" : "Сместить пять lights authoring batch-операцией"}</H3><Text>{client === "Руками" ? "Выбрать объекты → начать drag → видеть preview → отпустить мышь. Один commit хранит исходные и конечные transforms." : "Прочитать IDs/revision → отправить batch → получить новую revision и affected IDs. Конфликт не должен оставлять частичные правки; повтор проверяется по idempotency key и payload в пределах срока журнала."}</Text></Stack>
<Stack gap={8}><H3>Общий запланированный результат</H3><Text>Один валидный authoring-документ, одно Undo и обновлённые panels. MCP не получает доступа к запущенным runtime entities; изменения входят в следующий подготовленный запуск игры.</Text></Stack>
</Grid>
<Divider/><H2>Официальный Blender; addon необязателен</H2>
<Table headers={["Этап", "Контракт", "Критерий"]} rows={[
["Обычный GLB import", "Работает без addon и особой сборки Blender", "Units, axes, transforms и поддерживаемый material profile"],
["Optional addon / manifest", "Stable source/subasset IDs для надёжного matching", "Переименование и перестановка не меняют identity; имён недостаточно"],
["Import", "Recipe, dependencies, versioned derived artifacts", "Ошибка сохраняет последний валидный результат"],
["Scene instances / reimport", "References + sparse local overrides отдельно от cache", "Удалённые/неоднозначные outputs видны; нет молчаливой подмены"],
["Game build", "Готовые бинарные runtime assets", "Blender, addon и Editor не нужны Player"],
]}/>
<Text>Live link возможное дальнейшее развитие после надёжного файлового roundtrip; plain GLB не даёт автоматической гарантии matching любых изменённых subassets.</Text>
<Row gap={8} wrap><Button onClick={() => open(root + reports[6].path)}>MCP и Blender: границы</Button><Button onClick={() => open(root + reports[10].path)}>Импорт, UID и roundtrip</Button></Row>
</Stack>}
{tab === "Стек и экспорт" && <Stack gap={20}>
<H2>Принятый стек; интеграция запланирована</H2>
<Table headers={["Область", "Выбор", "Граница"]} rows={[
["Platform / runtime", "SDL3 · C++ · EnTT · Box2D/Box3D", "Linux + Windows desktop, 2D/3D"],
["Renderer", "Собственный Vulkan 1.3 backend + Render Graph", "База без обязательного RT; features/formats/limits проверяются"],
["Shaders", "Slang → SPIR-V, совместимый HLSL", "Один pinned compiler в editor/cook; .spv + Faset metadata в Player"],
["Editor UI", "Собственный retained C++ UI, declarative layout/styles", "Dark-first; Dear ImGui только debug"],
["Native build", "CMake + Ninja; Clang / clang-cl + Windows SDK", "Сборка и запуск проверяются на каждой целевой ОС"],
["Gameplay", "Static library, dev/release Player", "Отдельные процесс/окно; C++ stop/build/restart"],
["Editor plugins", "DLL/SO под точный SDK", "Restart при смене; schema export helper process"],
["Lua после MVP", "Обязательный этап развития, optional per game", "C++-игра сможет собираться без Lua runtime"],
]}/>
<Divider/><H2>Четыре разные стадии сборки</H2>
<Table headers={["Стадия", "Результат", "Входы для invalidation"]} rows={[
["Compile code", "Gameplay static library + linked dev/release Player", "Код, flags, dependencies, toolchain"],
["Compile shaders", "SPIR-V + reflection metadata", "Shader/includes, permutations, compiler version, profile"],
["Cook assets", "Binary mesh/texture/scene data", "Source, recipe, dependencies, target profile"],
["Package + validate", "Автономный каталог игры + manifest", "Состав и версии готовых outputs"],
]}/>
<Text>UI, CLI и MCP редактора запускают один BuildRequest. Компиляторы работают в дочерних процессах; staging публикуется после проверки. Планируемый контракт отмены сохраняет последнюю успешную сборку; cache корректность и повторяемость требуют тестов, а не только hash-ключей.</Text>
<Text>Windows и Linux сборки проверяются в своих окружениях. Player не включает Editor/MCP, shader compiler или editor plugins. Драйвер всё равно создаёт GPU pipelines; shader hot reload требует собственной проверки bindings/layout и безопасной замены ресурсов.</Text>
<Text size="small" tone="secondary">Версии закреплены в dependencies.lock.json и документации toolchain; фактически проверенные GPU/driver окружения указаны в docs/validation. C#/.NET и Rust + wgpu остаются историей сравнения вариантов, не параллельными реализациями Faset.</Text>
<Row gap={8} wrap><Button onClick={() => open(root + reports[7].path)}>Исследование сборки и вариантов стека</Button><Button onClick={() => open(root + reports[11].path)}>Build service, cache и delivery</Button></Row>
</Stack>}
{tab === "Прототипы" && <Stack gap={18}>
<H2>M0M9 / P1P6 · все этапы запланированы</H2>
<Text>Этапы синхронизированы с PLAN.md: M0M9 составляют MVP; P1P6 идут после него. P1P3 могут развиваться параллельно, P4 требует зрелого renderer. Обе демки и доставка на двух ОС обязательны для MVP.</Text>
{steps.map((s, i) => <div key={s.name}><CollapsibleSection title={s.name} defaultOpen={i === 0}><Stack gap={8}><Text>{s.task}</Text><Text weight="bold">Будущий критерий приёмки</Text><Text>{s.pass}</Text></Stack></CollapsibleSection></div>)}
<Divider/><H3>Общие измерения</H3><Text>Время от правки до результата, cold/warm Play, build/import latency, ошибки UX; CPU/GPU времена, память и качество последовательностей кадров. Каждый эффект сравнивается с воспроизводимым baseline. Даты и ускорения пока не обещаются.</Text>
<Button onClick={() => open("PLAN.md")}>Полный PLAN.md с критериями этапов</Button>
</Stack>}
{tab === "Источники" && <Stack gap={18}>
<H2>Исследовательские snapshots и границы доступа</H2>
<Table headers={["Репозиторий", "Версия / commit", "Объём доступа"]} rows={[
["UnrealEngine", "5.8.2 · 16d75d847145", "Выбранные локальные C++/shaders; upstream требует доступа Epic"],
["godot", "4.8.0 dev · 9c776068d6ed", "Source checkout, shallow Git history"],
["UnityCsReference", "6000.7.0a6 · 6b50e5544f6e", "C# reference source; native internals не включены"],
["blender-source", "5.3.0 alpha · 28d47268bddc", "Shallow/sparse; выбранные редакторские подсистемы"],
]}/>
<Text tone="secondary">Карта и отчёты открываются без локальных копий движков. Optional source viewer использует sibling checkout; исходники чужих движков не входят в Faset. Dev/alpha snapshots материалы исследования, не выбранные production-зависимости.</Text>
<Row gap={8} wrap><Button onClick={() => open("README.md")}>README</Button><Button onClick={() => open("PLAN.md")}>PLAN</Button><Button onClick={() => open("docs/ARCHITECTURE.md")}>Архитектура</Button><Button onClick={() => open(root + "source-manifest.json")}>Полные source SHA и охват</Button></Row>
<Stack gap={7}>{reports.map(r => <div key={r.path}><Row><Button onClick={() => open(root + r.path)}>{r.name}</Button></Row></div>)}</Stack>
<Text size="small" tone="tertiary">В исследованиях прочитаны выбранные тела функций и официальные документы. Сравнительные UX-тесты и GPU-бенчмарки изучаемых движков не проводились. Проверки реализованных функций Faset описаны отдельно в досье приёмки.</Text>
</Stack>}
</Stack>;
}