CLAUDE.md вне версионирования: один файл на все ветки
Файл описывает всё дерево целиком, а ветки содержат разные его части (порт на Rust, исследование CFD, базовый C++-редактор). Отслеживаемая копия при каждом переключении подменялась бы версией своей ветки, тогда как нужна одна общая. Содержимое на всех ветках было идентично, так что расхождений открепление не теряет. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B9Gcr11JJJyf8NnWsXjDzZ
This commit is contained in:
@@ -60,3 +60,6 @@ target/
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
desktop.ini
|
desktop.ini
|
||||||
|
|
||||||
|
# Общие заметки Claude Code: один файл на все ветки, вне версионирования
|
||||||
|
/CLAUDE.md
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
# CLAUDE.md
|
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
**SimVulcan** is a minimal **Vulkan 1.3 / C++20** 3D model editor. It renders a
|
|
||||||
3D editor space — three reference grid planes (XY, XZ, YZ) through the origin plus
|
|
||||||
coloured X/Y/Z axes — viewed through an orbit camera, and loads/displays `.obj`
|
|
||||||
models with selectable display modes (solid, wireframe, solid + wireframe).
|
|
||||||
|
|
||||||
The C++ side runs no simulation: it is a focused rendering skeleton with an ImGui
|
|
||||||
interface (a Viewport control panel and a Mesh load panel). The repository *also*
|
|
||||||
carries an unrelated Python research prototype under `docs/theory/` — see
|
|
||||||
[Research prototype](#research-prototype-docstheory) below; it is not part of the
|
|
||||||
CMake build.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
Prerequisites: **Vulkan SDK 1.3.290+** (provides `glslangValidator` for offline
|
|
||||||
shader compilation), **CMake 3.26+**, **Ninja**, a C++20 compiler (MSVC 19.36+,
|
|
||||||
gcc 11+, clang 14+). On macOS, Vulkan is via MoltenVK (Apple Silicon only).
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cmake --preset windows-msvc-release
|
|
||||||
cmake --build --preset windows-msvc-release
|
|
||||||
```
|
|
||||||
|
|
||||||
Presets: `windows-msvc-debug`, `windows-msvc-release`, `linux-gcc-release`,
|
|
||||||
`linux-clang-release`, `macos-arm64-release` (all Ninja, one dir per preset under
|
|
||||||
`build/<presetName>/`). The first configure fetches dependencies via FetchContent
|
|
||||||
(GLFW, GLM, volk, vk-bootstrap, VulkanMemoryAllocator, Dear ImGui, spdlog,
|
|
||||||
tinyobjloader, Catch2) and needs network access. `VK_NO_PROTOTYPES` is set
|
|
||||||
project-wide; Vulkan entry points load through **volk**.
|
|
||||||
|
|
||||||
Warnings come from `simv_set_warnings` (`/W4 /permissive-`, or
|
|
||||||
`-Wall -Wextra -Wpedantic -Wshadow -Wold-style-cast …`); they are **not** errors.
|
|
||||||
`cmake/Sanitizers.cmake` defines `simv_enable_sanitizers` (Debug-only ASan/UBSan)
|
|
||||||
but no target currently calls it — wire it in manually when chasing memory bugs.
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
The executable resolves SPIR-V relative to the working directory (`FindSpvPath`
|
|
||||||
probes `spirv/<rel>` and `current_path()/spirv/<rel>`). The `SimVulcan` POST_BUILD
|
|
||||||
step copies the compiled `spirv/` tree and `assets/` next to the executable, so
|
|
||||||
**run from the executable's own directory** (`build/<preset>/src/app/`). `main.cpp`
|
|
||||||
also probes several `../` ancestors for `assets/meshes`. Meshes load at runtime
|
|
||||||
through the ImGui Mesh panel.
|
|
||||||
|
|
||||||
Running writes two files into the CWD: `pipeline_cache.bin` (serialised
|
|
||||||
`VkPipelineCache`, reloaded on the next start) and ImGui's `imgui.ini`. Both are
|
|
||||||
disposable — delete them if pipeline creation or the panel layout misbehaves.
|
|
||||||
|
|
||||||
`ContextOptions::enableValidation` / `enableDebugUtils` default to **true** in
|
|
||||||
every build config, so the Khronos validation layer is requested even in Release;
|
|
||||||
messages (error + warning severity) go through spdlog.
|
|
||||||
|
|
||||||
`run.bat` at the repo root is **stale**: it launches
|
|
||||||
`build/vs2022/src/app/Release/SimVulcan.exe`, a path the Ninja presets never
|
|
||||||
produce. Do not point users at it without fixing the path first.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Catch2 unit tests, pure CPU/math (mesh bounds + welding). No GPU required.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
cmake --build --preset windows-msvc-debug --target simv_tests
|
|
||||||
ctest --preset windows-msvc-debug
|
|
||||||
```
|
|
||||||
|
|
||||||
`windows-msvc-debug` is the only preset with a `testPreset` — for the other
|
|
||||||
configs, invoke `ctest` in `build/<preset>/` directly.
|
|
||||||
|
|
||||||
Single test / subset — either through CTest (each `TEST_CASE` is registered
|
|
||||||
individually by `catch_discover_tests`):
|
|
||||||
|
|
||||||
```sh
|
|
||||||
ctest --preset windows-msvc-debug -R "WeldVertices" -V
|
|
||||||
```
|
|
||||||
|
|
||||||
or by running the binary with a Catch2 name or tag filter:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
./build/windows-msvc-debug/tests/simv_tests.exe "[decimator]"
|
|
||||||
./build/windows-msvc-debug/tests/simv_tests.exe --list-tests
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Library / target map
|
|
||||||
|
|
||||||
```
|
|
||||||
SimVulcan (exe) → simv_core, simv_vk, simv_mesh, simv_editor
|
|
||||||
simv_core → simv_vk (App owns Window + vk::Renderer; no Vulkan calls)
|
|
||||||
simv_editor → simv_core, simv_mesh (Camera, input, ImGui panels; no Vulkan)
|
|
||||||
simv_mesh → simv_core (CPU mesh only; no Vulkan)
|
|
||||||
simv_vk → third-party (volk, vk-bootstrap, VMA, GLFW, GLM, spdlog, imgui)
|
|
||||||
simv_shaders → glslangValidator (GLSL → SPIR-V)
|
|
||||||
```
|
|
||||||
|
|
||||||
Namespaces follow directories: `simv::core`, `simv::vk`, `simv::mesh`,
|
|
||||||
`simv::editor`. Each library exports `src/` as its include root, so includes are
|
|
||||||
written module-qualified (`#include "mesh/Mesh.h"`, `#include "vk/Renderer.h"`).
|
|
||||||
|
|
||||||
### Frame loop
|
|
||||||
|
|
||||||
`main.cpp` creates `core::App`, which owns the `core::Window` and a
|
|
||||||
`vk::Renderer`, then runs the loop. Each frame `Renderer::DrawFrame` calls the UI
|
|
||||||
callback (between ImGui NewFrame/Render), then records the scene: grid + mesh into
|
|
||||||
one dynamic-rendering pass with a depth attachment, followed by ImGui, then
|
|
||||||
presents (2 frames in flight, sync2 submits).
|
|
||||||
|
|
||||||
`main.cpp` wires the editor via the UI callback: it draws the panels, applies
|
|
||||||
mouse input to the `editor::Camera`, and pushes the resulting state into the
|
|
||||||
renderer (`SetViewProj`, `SetRenderMode`, `SetGridVisible`). Mesh loads go through
|
|
||||||
`MeshLoadPanel`'s callback → `Renderer::SetMeshCpu`.
|
|
||||||
|
|
||||||
### Mesh load path
|
|
||||||
|
|
||||||
`MeshLoadPanel` lists `*.obj` in the mesh directory and kicks off
|
|
||||||
`mesh::LoadObjAsync` (worker thread, `std::future`). The future is **drained on
|
|
||||||
the main thread** at the top of `MeshLoadPanel::Draw`, so the `OnLoaded` callback
|
|
||||||
— and therefore the GPU upload — always runs on the render thread. Loader
|
|
||||||
exceptions surface as the panel's status string.
|
|
||||||
|
|
||||||
`main.cpp`'s `OnLoaded` welds the mesh (`WeldVertices`, tolerance `1e-4`), logs
|
|
||||||
the counts, uploads via `SetMeshCpu`, and reframes the camera to the bbox radius.
|
|
||||||
Despite the file name, `mesh/MeshDecimator.h` implements **only** spatial-hash
|
|
||||||
vertex welding (collapse near-duplicates, drop degenerate triangles, recompute
|
|
||||||
bounds) — there is no LOD/decimation.
|
|
||||||
|
|
||||||
### Non-obvious invariants (read before editing)
|
|
||||||
|
|
||||||
- **All Vulkan lives in `src/vk/`.** `core/`, `mesh/`, `editor/` and `app/` make no
|
|
||||||
Vulkan API calls. `vk::Renderer`'s public header is deliberately Vulkan-free
|
|
||||||
(pImpl + glm/`RenderMode` only) so `App` can own it without pulling in volk. Keep
|
|
||||||
it that way — do not leak `Vk*` types into the public interfaces of those modules.
|
|
||||||
- **Single render pass with depth.** Scene (grid + mesh) and ImGui draw into one
|
|
||||||
`vkCmdBeginRendering` pass that has both a colour and a `D32_SFLOAT` depth
|
|
||||||
attachment. The ImGui backend is initialised with `depthAttachmentFormat` set so
|
|
||||||
its pipeline matches the pass; the depth buffer is recreated with the swapchain.
|
|
||||||
- **Wireframe needs `fillModeNonSolid`.** `MeshRenderer` builds a fill pipeline and
|
|
||||||
a `VK_POLYGON_MODE_LINE` pipeline; the line pipeline uses a small depth bias so
|
|
||||||
the overlay sits on top of the fill. The device feature is requested in `Context`.
|
|
||||||
Culling is off (`VK_CULL_MODE_NONE`) — loaded models may have mixed winding.
|
|
||||||
- **Camera is fixed on the origin.** `editor::Camera` orbits (yaw/pitch/distance)
|
|
||||||
the world origin; loaded models are recentred there via a translate-only model
|
|
||||||
matrix. Projection uses Vulkan clip space (`GLM_FORCE_DEPTH_ZERO_TO_ONE` + Y flip,
|
|
||||||
isolated to `Camera.cpp`).
|
|
||||||
- **Buffers.** `vk::GpuMesh` owns the model's vertex/index buffers (staged upload on
|
|
||||||
the transfer queue). `GridRenderer` builds a static host-visible line buffer once.
|
|
||||||
Both scene renderers use only a push constant — no descriptor sets.
|
|
||||||
- **Push-constant layout is a cross-file contract.** `MeshRenderer.cpp`'s anonymous
|
|
||||||
`MeshPC { mat4 mvp; vec4 color; }` must stay byte-identical to the
|
|
||||||
`push_constant` block in `mesh.vert`/`mesh.frag`; `color.a` is a *flag*, not
|
|
||||||
alpha (1 = flat-shaded, 0 = constant colour for wireframe). `GridRenderer` pushes
|
|
||||||
a bare `mat4` (vertex stage only). Change either side and you must change both.
|
|
||||||
- **Swapchain recreation rebuilds sync objects.** `RecreateSwapDependent` recreates
|
|
||||||
the per-frame `imageAvailable` semaphores (a failed acquire can leave one
|
|
||||||
signalled) *and* the per-swapchain-image `renderFinished` semaphores (the image
|
|
||||||
count may change), then re-ensures both pipelines. Keep that ordering if you
|
|
||||||
touch resize handling.
|
|
||||||
- **Mesh upload stalls the device.** `SetMeshCpu`/`ClearMesh` call `WaitIdle`
|
|
||||||
before touching `GpuMesh` — acceptable because loads are rare; do not copy that
|
|
||||||
pattern into per-frame paths.
|
|
||||||
|
|
||||||
### Shaders
|
|
||||||
|
|
||||||
GLSL under `shaders/editor/` (`mesh.{vert,frag}`, `grid.{vert,frag}`). `simv_shaders`
|
|
||||||
compiles each to `build/<preset>/spirv/editor/<name>.spv` targeting `vulkan1.3`,
|
|
||||||
with `shaders/` as the `-I` root (so `#include "common/foo.glsl"` would resolve).
|
|
||||||
Adding a shader under that globbed dir is picked up automatically
|
|
||||||
(`CONFIGURE_DEPENDS`). `mesh.frag` reconstructs a flat normal from screen-space
|
|
||||||
derivatives, so the vertex stream carries only positions (tight `float3`, one
|
|
||||||
binding, one attribute).
|
|
||||||
|
|
||||||
## Logging
|
|
||||||
|
|
||||||
`simv::core::Logger` (spdlog-backed, singleton) with category-based throttling.
|
|
||||||
Log via `LogFmt(LogCategory, LogLevel, fmt, args...)`. Categories: `Core`,
|
|
||||||
`Vulkan`, `MeshIO`, `UI`, `Test`. Per-category level, throttle interval and
|
|
||||||
on/off are settable at runtime (`SetMinLevel` / `SetThrottle` / `SetEnabled`).
|
|
||||||
Some low-level Vulkan code still calls `spdlog::` directly.
|
|
||||||
|
|
||||||
## Research prototype (`docs/theory/`)
|
|
||||||
|
|
||||||
Separate from the C++ application and from the CMake build: a Python **Lattice
|
|
||||||
Boltzmann (D2Q9, KBC-N1)** research codebase — cylinder flow with a ×2 nested AMR
|
|
||||||
patch and SDF+Bouzidi boundaries. **GPU/CuPy only, no CPU fallback.** Its
|
|
||||||
documentation and the running experiment log are in Russian.
|
|
||||||
|
|
||||||
- `docs/theory/solver_2x_sdf/` — the component-split solver (`run.py`,
|
|
||||||
`run_blockage.py`, `run_factors.py`); its `README.md` is the authoritative
|
|
||||||
status/experiment log and maps every file to the physics it owns.
|
|
||||||
- `docs/theory/demos_gpu/` — demo runs that produce the notebook's figures/GIFs;
|
|
||||||
they import physics from `solver_2x_sdf`, never duplicate it.
|
|
||||||
- `docs/theory/kbc_lbm.ipynb` — the write-up; it embeds pre-rendered artefacts and
|
|
||||||
does not execute the simulations.
|
|
||||||
- `docs/origins/` — source PDFs behind the theory.
|
|
||||||
|
|
||||||
Do not fold this into the CMake build, and do not treat it as dead code — it is
|
|
||||||
active research with a documented experiment history.
|
|
||||||
Reference in New Issue
Block a user