Imported from alexwbt/axgl (
AGENTS.md). Install upstream withnpx skills add alexwbt/axgl. Copyright stays with the author.
AGENTS.md
A C++ game development framework. Service-oriented core (interfaces in
axgl/include/axgl/interface/, default implementations in
axgl/include/axgl/impl/) over a component-based entity/realm tree; backends in
impl/, examples in demo/. See README.md for design philosophy.
Setup (do this first, or builds fail silently)
- Submodules:
git submodule update --init --recursive(ormake setup). All third-party deps live under_external/(glm, glfw, spdlog, assimp, asio, flatbuffers, tracy, freetype, args, cpptrace, utfcpp, stb, imgui, simdjson). They are built as part of the CMake tree, not fetched. - Git LFS is required.
core.hooksPathis set to.githooks; the hooks abort checkout/commit/merge/push ifgit-lfsis missing. LFS tracks*.png,*.ttf,*.assbin(see.gitattributes). Without LFS, demo assets indemo/**/res/will be pointer files, not real data.
Build
- Use the wrapper:
_scripts/build.sh [debug|release] [--target <target>] [--no-config] [--no-build](logs to_log/build.logplus a timestamped archive in_log/). The default preset isdebug.--no-configskips configure;--no-buildskips the build step (configure only).--target <name>builds a single target (e.g.--target demo_blending). Preset is a positional arg; flags can appear in any position. When testing a change, build thedebugpreset only (it is the default). - The
Makefilewraps the same scripts:make(=make debug),make release,make target TARGET=<name>,make run [TARGET=<name>],make demo_<name>(e.g.make demo_window),make clean,make format,make tidy. Default TARGET isdemo_playground; parallelism viaPARALLEL=N(exported asCMAKE_BUILD_PARALLEL_LEVEL). make rundoes not build first — it just runs_bin/<TARGET>and errors if it doesn't exist. Build (make/make target TARGET=...) beforemake run.- After a full debug build,
build.shruns thecompile_proxytool (see "compile_proxy" below) to post-processcompile_commands.jsonso clangd / clang-tidy get real entries for header-only TUs. This is skipped for--targetbuilds (database may be incomplete). - Check
_log/build.logfor errors/output instead of relying on stdout, which may be empty, truncated, or intermixed with runtime output. Use the Read tool (or Grep) on_log/build.log, nottail/catvia Bash. - Binaries go to
_bin/; build artifacts to_build/{Debug,Release}.CMAKE_EXPORT_COMPILE_COMMANDSis ON, socompile_commands.jsonlands in_build/Debug— this is what.clangdand clang-tidy (-p _build/Debug) read. If you wipe_build/, clangd/clang-tidy break until you reconfigure. - Clean:
_scripts/clean.sh(ormake clean) — removes_build/and_bin/. - Debug preset defines
AXGL_DEBUGandTRACY_ENABLE=ON; release turns both off. See "Debug/profiling instrumentation" below — these flags change behavior, not just optimization.
Format / lint
- Style is clang-format attach braces, 2-space, 80 col, pointers left
(
.clang-format). Match this exactly; do not reflow to LLVM defaults. clang-formatmajor version 22 is required (enforced by_scripts/format.sh; override withCLANG_FORMAT_SKIP_VERSION_CHECK=1).pre-commithook (in.githooks) auto-runsclang-format -ion staged.cpp/.hpp/.fs/.vsunderaxgl/,demo/,impl/,lib/and re-stages them. If you commit from a shell that doesn't run hooks, run_scripts/format.shfirst._scripts/format.sh(ormake tidy) also runsclang-tidy -fix -fix-errors(pass--no-tidy, or usemake format, to skip) across the same four trees, then clang-format. It requires a configured_build/Debugfor the compile database. Use--files <f1> <f2> ...to format only listed files (no tidy).- clang-tidy checks are pinned in
.clang-tidy(large bugprone/modernize/ performance/readability set). Don't disable checks ad hoc; edit the file.
Tests (opt-in via AXGL_BUILD_TESTS)
- Tests are off by default. The
debugCMake preset turnsAXGL_BUILD_TESTS=ON(seeCMakePresets.json);releaseleaves it off.make/make debugtherefore configure tests; a barecmakewithout the preset will not, unless you pass-DAXGL_BUILD_TESTS=ON. - Framework is doctest (
_external/doctest), withdoctest_with_mainprovidingmain(). Tests live next to the code they cover as atest/subdir registered viaadd_tests(<prefix> <dir> DEPS ...)from_cmake/tests.cmake. Each*.cppin the dir becomes its own executable + CTest entry, all gathered under theaxgl_testscustom target. Tests may skip themselves at runtime when a prerequisite (e.g. a GL context) is unavailable — guard with an availability check, don't fail. - Run:
make test(buildsaxgl_testswith--no-configthen runsctest --test-dir _build/Debug --output-on-failure). CTest filters work, e.g.ctest --test-dir _build/Debug -R '<pattern>'; a single test binary is_bin/<test_target>. - There is no top-level
test/dir; do not create one. Add tests by callingadd_tests(...)from the relevant library'sCMakeLists.txt(mirror an existing one likeimpl/opengl/CMakeLists.txt). - "Verification" still mostly means: the project builds and a demo runs. Tests are a supplement, not a gate.
Architecture
See README.md for the architecture design philosophy.
axgl/— header-only INTERFACE library, namespaceaxgl. Core types:Axgl(axgl.hpp) is the engine entrypoint withinitialize()/run()/terminate()and a fixed-timestep loop.configure_default(axgl)registers the default services. Services are split intoaxgl/include/axgl/interface/(abstract) andaxgl/include/axgl/impl/(default implementations).axgl/CMakeLists.txtlinksutil,glm,spdlog,cpptrace::cpptrace,Tracy::TracyClientas INTERFACE deps — include these viaaxgl, not directly from_external/.lib/— internal support libraries:net(asio wrapper, INTERFACE),glad,util,assbin,embedfile,bundlefile,compile_proxy.netis standalone (noaxgldependency) and used by the net demos.impl/— platform/backend implementations, built as libraries:axgl_glfw_impl(INTERFACE),axgl_opengl_impl(static, embeds itsres/),axgl_assimp_impl,axgl_bundlefile_impl. Demos link animpltarget, notaxgldirectly — the impl transitively pulls inaxgl.demo/— executables.demo/CMakeLists.txtauto-discovers subdirectories with aCMakeLists.txt, so each numbered demo (01_window...12_gui) anddemo/net/*is its own target.demo/playgroundis the scratch app. Target names:demo_<shortname>(e.g.demo_window,demo_blending,demo_playground); net demos aredemo_net_tcp_ping_client,demo_net_tcp_ping_server,demo_net_chatroom_server,demo_net_chatroom_client. Net demos linknetand, on MinGW/Windows, needws2_32 mswsock(guarded byWIN32 AND MINGWin theirCMakeLists.txt).
Codegen / resource pipeline (easy to miss)
_cmake/resource.cmake defines three CMake functions used across the tree. They
generate files into the build dir and add them as sources — a clean build is
required after editing inputs or the generated code goes stale.
embed_resource(target source_dir)— runs theembedfiletool to turnsource_dir/*into${CMAKE_CURRENT_BINARY_DIR}/resources/.../*.cpp/.hpp, added as a source oftarget. Used byaxgl_opengl_impl(res/) anddemo/playground(res/). Include path root is.../resources.bundle_resource(target source_dir)— runsbundlefileto produce_bin/${target}_${source_dir}.bin.compile_fbs(target source_dir)— runsflatc --cpp(theflatcbinary built in_bin/) on*.fbs, emitting*_fbs.hinto${CMAKE_CURRENT_BINARY_DIR}/flatbuffers/...and adding it to the target's include dirs. Used bylib/bundlefile(its ownbundle.fbs) anddemo/net/2_chatroom(fbs/). When you add a.fbs, rerun configure so the glob + custom command regenerate.
The flatc, embedfile, bundlefile, compile_proxy tools are built from
source as part of the normal build and placed in _bin/. Don't expect them on
PATH.
compile_proxy (header-only clangd support)
_cmake/compile_proxy.cmake defines
add_compile_proxy(target include_directory). It generates a stub .cpp per
.hpp under include_directory (each just #includes the header) and compiles
them into a static library, so compile_commands.json has an entry per header —
without this, clangd/clang-tidy have no compile commands for header-only
libraries like axgl. Only active in Debug builds
(CMAKE_BUILD_TYPE STREQUAL "Debug"). Used widely across the tree: axgl,
every impl/* target, net, util, bundlefile_lib, demo_playground, and
demo_net_chatroom_fbs (for generated flatbuffer headers). Because most public
libs here are header-only INTERFACE targets, nearly every header gets a compile
command this way.
After building, _scripts/build.sh runs the compile_proxy tool
(lib/compile_proxy) to rewrite the stub entries' file field to point at the
real header, so editors jump to the header instead of the stub.
Debug / profiling instrumentation (changes semantics)
axgl/include/axgl/common.hpp is included widely. Under
AXGL_DEBUG && TRACY_ENABLE (i.e. the debug preset) it:
- Defines
AXGL_PROFILE_SCOPE,AXGL_PLOT,AXGL_ALLOC,AXGL_FREEas real Tracy macros; otherwise they expand to nothing. (All four are guarded byAXGL_DEBUGalone at the source level, but the underlying Tracy macros are no-ops withoutTRACY_ENABLE, so both must be on for real profiling.) - Globally overrides
operator new/delete/ array forms to route allocations throughTracyAlloc/TracyFree. These are non-inline definitions in a header guarded by the combo flag — keep an eye on ODR issues if you includecommon.hppfrom many TUs in a debug build. - Sets
SPDLOG_ACTIVE_LEVELto DEBUG and definesAXGL_LOG_*macros (AXGL_LOG_DEBUG/INFO/WARN/ERROR) backed by spdlog. Log level is configurable via-DAXGL_LOG_LEVEL=<0..4>(0=DEBUG default in debug, 2=WARN default otherwise).
When adding code, prefer the AXGL_LOG_* / AXGL_PROFILE_SCOPE macros over raw
spdlog/Tracy calls so release builds stay zero-cost.
Conventions that differ from defaults
- Pointer type alias
axgl::ptr_t<T>=std::shared_ptr<T>; useaxgl::create_ptr<T>(...)andaxgl::ptr_cast<T1>(p)(dynamic) instead of rawmake_shared/dynamic_pointer_cast. There is alsoaxgl::ref_t<T>=std::weak_ptr<T>. - No comments in code unless explicitly requested; existing files are largely comment-free by convention.
.clangdremoves all-W*flags from the compile database and re-adds-Wall -Wextra— don't rely on per-target warning flags showing up in clangd diagnostics.- Use LF line endings in all files (enforced by
.gitattributes).