From 2fac7a999f1da1e501fee9357f4025347ca52b15 Mon Sep 17 00:00:00 2001 From: Niels Eppenhof Date: Fri, 14 Aug 2026 03:29:48 +0200 Subject: [PATCH 1/2] Fixed errors after switch to Tracy 0.14.0 and hard reset. --- .claude/settings.json | 5 - .../eppo-application-framework/SKILL.md | 25 -- .../references/architecture.md | 107 ----- .../skills/eppo-assets-and-projects/SKILL.md | 28 -- .../references/architecture.md | 117 ----- .../skills/eppo-editor-development/SKILL.md | 25 -- .../references/architecture.md | 119 ----- .../skills/eppo-physics-integration/SKILL.md | 25 -- .../references/architecture.md | 101 ----- .../skills/eppo-rendering-pipeline/SKILL.md | 25 -- .../references/architecture.md | 113 ----- .../skills/eppo-scene-ecs-lifecycle/SKILL.md | 25 -- .../references/architecture.md | 130 ------ .../eppo-scripting-integration/SKILL.md | 27 -- .../references/architecture.md | 116 ----- CLAUDE.md | 151 ------- Dependencies/Ports/tracy/build-tools.patch | 94 +++- .../Ports/tracy/downgrade-capstone-5.patch | 88 +++- .../Ports/tracy/fix-imgui-patch.patch | 69 ++- .../Ports/tracy/fix-vendor-versions.patch | 405 +++++++++++++++++- Dependencies/Ports/tracy/portfile.cmake | 92 +++- Dependencies/Ports/tracy/vcpkg.json | 94 +++- EppoEditor/imgui.ini | 126 +++--- EppoEngine/Source/Core/Base.cpp | 60 +-- 24 files changed, 929 insertions(+), 1238 deletions(-) delete mode 100644 .claude/settings.json delete mode 100644 .claude/skills/eppo-application-framework/SKILL.md delete mode 100644 .claude/skills/eppo-application-framework/references/architecture.md delete mode 100644 .claude/skills/eppo-assets-and-projects/SKILL.md delete mode 100644 .claude/skills/eppo-assets-and-projects/references/architecture.md delete mode 100644 .claude/skills/eppo-editor-development/SKILL.md delete mode 100644 .claude/skills/eppo-editor-development/references/architecture.md delete mode 100644 .claude/skills/eppo-physics-integration/SKILL.md delete mode 100644 .claude/skills/eppo-physics-integration/references/architecture.md delete mode 100644 .claude/skills/eppo-rendering-pipeline/SKILL.md delete mode 100644 .claude/skills/eppo-rendering-pipeline/references/architecture.md delete mode 100644 .claude/skills/eppo-scene-ecs-lifecycle/SKILL.md delete mode 100644 .claude/skills/eppo-scene-ecs-lifecycle/references/architecture.md delete mode 100644 .claude/skills/eppo-scripting-integration/SKILL.md delete mode 100644 .claude/skills/eppo-scripting-integration/references/architecture.md delete mode 100644 CLAUDE.md diff --git a/.claude/settings.json b/.claude/settings.json deleted file mode 100644 index 1f73a2b0..00000000 --- a/.claude/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "worktree": { - "bgIsolation": "none" - } -} diff --git a/.claude/skills/eppo-application-framework/SKILL.md b/.claude/skills/eppo-application-framework/SKILL.md deleted file mode 100644 index e042b518..00000000 --- a/.claude/skills/eppo-application-framework/SKILL.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: eppo-application-framework -description: Develop and diagnose Eppo's application framework across entry-point creation, Application and layer lifecycle, frame ordering, window and GLFW event delivery, input backends and simulated input, device and renderer startup, resize and minimization, ImGui frame integration, writable and resource directory resolution, the deployed EppoRuntime player, and the App harness. Use for changes to Application, Window, Layer, EntryPoint, Input, SimulatedInput, Event, ImGui, platform window/input code, EppoEditor/Source/EppoEditor.cpp, EppoRuntime/Source, or application-level tests; do not trigger for unrelated Core utilities such as UUID or Hash. ---- - -# Eppo Application Framework - -Read [references/architecture.md](references/architecture.md) before changing startup, frame order, events, input, or ImGui integration. Follow ownership from `main` through `CreateApplication`, `Application`, the window/device, layers, and shutdown. - -## Workflow - -1. Trace the exact lifecycle phase affected: construction, layer attach, event pump, update, ImGui frame, render submission, present, resize, close, or destruction. -2. Preserve the order dependencies between window creation, required Vulkan extensions, device initialization, renderer initialization, ImGui attachment, and user layers. -3. Keep event-driven state and polled input coherent. Update the real and simulated input paths together when adding input behavior. -4. Add deterministic coverage through `Application::StepFrame` and the support harness when the behavior can be observed by frame count or state. -5. Use headless unit tests for isolated core types; use `App` for real window, device, and repeated-frame behavior, and the `Renderer` suite's `SceneRendering` tests for renderer, input, or camera behavior across frames. -6. Run the editor from `EppoEditor/` or tests through CTest so source resources resolve from the working directory; runtime data remains executable-relative. - -## Guardrails - -- Maintain the single live `Application` invariant. -- Do not update or present while minimized or after frame acquisition fails. -- Dispatch events through layers in their current stack order and stop once handled. -- Gate gameplay/editor polled input through the established viewport-input mechanism. -- Shut down GPU and ImGui users before destroying the window or device they depend on. diff --git a/.claude/skills/eppo-application-framework/references/architecture.md b/.claude/skills/eppo-application-framework/references/architecture.md deleted file mode 100644 index d02fa85a..00000000 --- a/.claude/skills/eppo-application-framework/references/architecture.md +++ /dev/null @@ -1,107 +0,0 @@ -# Application framework architecture - -## Startup and ownership - -`Core/EntryPoint.h` supplies `Eppo::RunApplication(argc, argv)` — initialize logging, call the application-specific `CreateApplication(argc, argv)`, own the result in a `ScopedPtr`, `Run`, destroy — plus a default `main` that calls it. Defining `EP_CUSTOM_ENTRY_POINT` before including the header suppresses that `main` so a target can supply its own. - -Two targets implement the factory: - -- `EppoEditor/Source/EppoEditor.cpp` uses the default `main` and pushes `EditorLayer`. -- `EppoRuntime/Source/EppoRuntime.cpp` defines `EP_CUSTOM_ENTRY_POINT` and calls `RunApplication` from its own `WinMain` (Windows) or `main`, so startup sits inside a try/catch that reports failures through `ErrorDialog` instead of terminating silently. Its `CreateApplication` deserializes `Game.eppak` **before** constructing the application, moves the packed shaders and includes into `ApplicationParams`, and hands the remaining `GameData` to `RuntimeLayer`. Reading the package cannot be deferred to the layer: the shaders are consumed during `Application` construction. - -`Application` is a singleton during its lifetime and owns, in dependency order: - -- `Window` and its platform backend; -- `DeviceManager` and NVRHI renderer; -- application layers; -- `ImGuiLayer` and its renderer integration. - -`Layer` exposes attach, detach, update, UI render, and event hooks. `PushLayer` constructs a layer, stores shared ownership, and immediately calls `OnAttach`. - -## Construction order - -1. Set the singleton and initialize logging/profiling prerequisites. -2. Create the GLFW-backed `Window` and install the application event callback. -3. Create the API-specific `DeviceManager`; the Vulkan backend gathers GLFW's required instance extensions while constructing its instance. -4. Initialize the device manager's surface and swapchain. -5. Initialize `Renderer` after the NVRHI device is live. -6. Call `Renderer::LoadShaders` with `ApplicationParams::PackedShaders` / `PackedShaderIncludes` (empty in the editor and tests, which compile from `Resources/Shaders`). This must precede ImGui, whose renderer resolves `GetShader("imgui")` during `OnAttach`. -7. Create/attach `ImGuiLayer` after renderer services exist. -8. Let the application factory push editor/runtime layers. - -Reverse dependency order during destruction. Wait for GPU idle before releasing GPU users when required. - -## Frame order - -`Run` computes a wall-clock timestep and repeatedly calls `StepFrame`. `StepFrame` exists so tests can drive deterministic fixed timesteps and frame counts. - -The effective frame phases are: - -1. Poll window events. -2. If minimized, avoid normal device/update/present work. -3. Begin/acquire the device frame. -4. Call `OnUpdate(timestep)` on layers in insertion order. -5. Begin ImGui. -6. Call `OnUIRender()` on layers. -7. End/render ImGui. -8. Present the device frame. - -Respect a failed `BeginFrame`; do not record or present against an unavailable swapchain image. - -## Event flow - -Window callbacks construct typed events such as resize, close, key, mouse button, mouse move, and scroll. `Application::OnEvent` first dispatches application-owned events, then forwards remaining events through the layer stack in insertion order. `ImGuiLayer` is pushed during application construction, so this order lets it capture input before later layers. Stop propagation when `Handled` becomes true. - -Window close marks the app not running. Resize currently updates minimized state only; swapchain recreation is handled by its own acquire/present behavior rather than directly from `Application::OnWindowResize`. - -When adding an event: - -1. Define its type/category and payload under `Event`. -2. Emit it from the platform window callback. -3. Update stateful input backend data if applicable. -4. Handle it in application/ImGui/layers in the correct priority order. -5. Add unit or application-harness coverage. - -## Input model - -`Input` exposes static polled queries through an `InputBackend`. The normal backend reads platform/GLFW state. `SimulatedInput` supports deterministic tests and controlled scenarios. - -Editor code gates polled input through `Input::SetViewportInputEnabled`: editor camera and running scripts should remain inactive while users type or click in other panels. Event delivery and polled input are related but not interchangeable; preserve both when adding keys/buttons. - -Key and mouse numeric values are shared with C# scripting. Update `Core/KeyCodes.h`, managed `KeyCodes.cs`, platform mapping, and tests together when changing them. - -## Window and filesystem assumptions - -`Window` owns the native GLFW window and provides framebuffer size, event callback, VSync/fullscreen/decorated state, native handle, and icon operations. Vulkan surface extensions and framebuffer sizing originate here. - -The deployed runtime resolves `Game.eppak`, loose assets, managed files, logs, and its shader cache relative to the executable directory. The editor resolves `Resources/` and `Projects/` from its `EppoEditor/` working directory, while managed files remain executable-relative. CTest uses `EppoEditor/` as the test working directory and loads its deployed managed assemblies beside the test executable. - -Writes are separately configurable. `FS::ConfigureWritableDirectory(path)` establishes the root returned by `FS::GetWritableDirectory`, which `FS::GetShaderCacheDirectory` and logging resolve against; unconfigured, the shader cache falls back to `Resources/Shaders/Cache`. The runtime configures it to `FS::GetExecutableDirectory()` before anything else runs, so a shipped game keeps its log and shader cache beside itself rather than inside a read-only install tree. Configure it before the first write, not after. - -## ImGui integration - -`ImGuiLayer` owns context/frame setup, docking and multi-viewport configuration, event blocking policy, and `ImGuiRenderer`. `ImGuiRenderer` translates draw lists into NVRHI buffers, pipeline bindings, scissor rectangles, texture descriptors, command recording, and swapchain framebuffer output. - -Keep ImGui GPU resources synchronized with back-buffer count and viewport/swapchain changes. Application UI phase must enclose every layer's `OnUIRender`. - -## Core conventions - -`Core/Base.h` defines the ownership and style vocabulary used across the engine: `Ref`/`CreateRef` (shared_ptr), `ScopedPtr`/`CreateScopedPtr` (unique_ptr), `WeakRef` (weak_ptr), `EP_ASSERT(cond, msg)` — a `constexpr` function, not a macro — config macros `EP_DEBUG`/`EP_RELEASE`/`EP_DIST`, and Tracy profiling (`EP_PROFILE_FN`). Engine code uses trailing-return-type style (`auto Foo() -> void`) universally. Sibling core utilities: `Core/Log.h`, `UUID.h`, `Hash.h`. - -`Core/Buffer/` is the binary serialization layer the rest of the engine writes through. `Buffer` is the raw owning byte span; `StreamWriter`/`StreamReader` are the abstract interfaces, implemented by `BufferWriter`/`BufferReader` (in memory) and `FileStreamWriter`/`FileStreamReader` (on disk). Both bases offer `WriteRaw`/`ReadRaw` for trivially-copyable values, `WriteString`/`ReadString`, `WriteBuffer`/`ReadBuffer`, and `WriteMap`/`ReadMap` that dispatch per element on `std::is_trivially_copyable_v`. Non-trivial types opt in through the paired `StreamSerializable` / `StreamDeserializable` concepts by providing static `Serialize(writer, value)` / `Deserialize(reader, value)`, reached via `WriteObject`/`ReadObject`. Every operation returns `bool`; callers propagate failure rather than asserting, which is what lets `GameData` reject a truncated package cleanly. - -## Testing infrastructure - -`EppoEngineTesting/Source/Support/AppHarness` boots a real `Application`, window, device, renderer, and resources. It can advance a deterministic number of frames. `TestContext` and `ScenarioLayer` build on it for multi-frame scenarios and simulated input; they are consumed by the `Renderer` suite's `SceneRendering` tests. - -Test routing: - -- headless `Core`: buffers, streams, hashes, UUIDs, filesystem, process/file-watch, and isolated non-window logic; -- graphical `App`: boot, live window/device, and repeated frame advancement; -- graphical `Renderer`: direct GPU abstraction behavior, plus the `SceneRendering` tests covering state changes across frames, editor camera, input, scene loading, and rendering. - -Graphical suites require a real display and GPU and are excluded by headless CI. Run them from CTest so the configured working directory is correct. - -## Change checklist - -For frame/startup changes, verify construction and destruction order, minimized and failed-acquire paths, repeated fixed-step frames, and renderer availability. For input/event changes, verify native callbacks, event propagation/handling, polled state, simulated state, viewport gating, and managed key-code parity. For ImGui changes, verify application frame bracketing, back-buffer resource ownership, docking/multi-viewport behavior, and resize. diff --git a/.claude/skills/eppo-assets-and-projects/SKILL.md b/.claude/skills/eppo-assets-and-projects/SKILL.md deleted file mode 100644 index 2822670b..00000000 --- a/.claude/skills/eppo-assets-and-projects/SKILL.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: eppo-assets-and-projects -description: Develop and diagnose Eppo asset and project workflows across project lifecycle, asset handles and metadata, registry persistence, relative path normalization, lazy loading, import/export dispatch, generated runtime assets, scene ownership, ContentBrowserPanel operations, project templates, asset-related serialization, and Game.eppak packaging and export. Use for changes under EppoEngine/Source/Asset, EppoEngine/Source/Project, EppoEditor/Source/Panels/ContentBrowserPanel, project templates, asset registry behavior, or the pack format consumed by EppoRuntime. ---- - -# Eppo Assets and Projects - -Read [references/architecture.md](references/architecture.md) before changing handles, paths, registry persistence, importers, or content-browser mutations. Treat the file on disk, registry metadata, loaded object, and editor presentation as distinct states. - -## Workflow - -1. Identify which identity is authoritative: project path, asset-relative path, stable `AssetHandle`, loaded `Asset`, or generated reserved handle. -2. Define disk and registry effects before editing. Keep move, rename, delete, import, and save operations consistent across both. -3. Add or extend engine APIs for reusable behavior; keep file-picker and ImGui orchestration in the editor. -4. Update type deduction, importer/exporter dispatch, icons, serialization, and opening behavior together when adding an asset type. -5. Preserve active-project preconditions and avoid holding references across project close or replacement. -6. When changing what gets packed, update `GameData`'s layout, its documented byte map, the matching `PackFormat` version, and the runtime's consumption together. -7. Test path/registry logic headlessly in `Project`; use graphical `ProjectExport` only when a live renderer is genuinely required. - -## Guardrails - -- Store asset paths relative to the active project's `Assets` directory. -- Reserve handle `0` as null and low handles for generated runtime primitives. -- Never delete a source file merely by removing registry metadata. -- Serialize registry mutations after releasing its mutex. -- Do not treat a registered asset as necessarily loaded, or a filesystem entry as necessarily registered. -- Gather packed data inside `Export` like every other section; do not special-case a payload with its own option flag, constructor parameter, or out-of-band capture. -- Bump the relevant `PackFormat` version with any layout change, and keep reads failing cleanly rather than asserting on a truncated or foreign package. diff --git a/.claude/skills/eppo-assets-and-projects/references/architecture.md b/.claude/skills/eppo-assets-and-projects/references/architecture.md deleted file mode 100644 index 81969e3a..00000000 --- a/.claude/skills/eppo-assets-and-projects/references/architecture.md +++ /dev/null @@ -1,117 +0,0 @@ -# Asset and project architecture - -## Core identities - -Keep four states distinct: - -1. A filesystem entry under a project's `Assets` directory. -2. `AssetMetadata` in `AssetRegistry.json` with handle, type, and relative path. -3. A loaded `Asset` object in `AssetManager::m_LoadedAssets`. -4. An editor representation in `ContentBrowserPanel`. - -A file may be unregistered. Registered metadata may be unloaded. A generated asset may have no file or serialized registry entry. - -`AssetHandle` is a UUID value. Handle `0` is null. Reserved low values generate built-in mesh primitives at runtime; normal imported assets use generated UUIDs. - -## File map - -| Area | Files | -| --- | --- | -| Asset base/types | `Asset/Asset.h`, `AssetType.h`, `AssetMetadata.h` | -| Registry/cache | `Asset/AssetManager.*` | -| Dispatch | `Asset/AssetImporter.*` | -| Project context | `Project/Project.*`, `ProjectSerializer.*` | -| Packaging | `Project/GameData.*`, `Project/ProjectExporter.*`, `Asset/PackFormat.h` | -| Scene asset format | `Scene/SceneSerializer.*` | -| Editor filesystem UI | `EppoEditor/Source/Panels/ContentBrowserPanel.*` | -| Templates | `EppoEditor/Resources/Templates/NewProject` | - -## Project lifecycle - -`Project` holds `ProjectSpecification` and one `AssetManager`; `Project::s_ActiveProject` is the global project context used by path and asset APIs. - -Opening a project deserializes the `.epproj`, sets its directory, publishes it as active, constructs the asset manager, and loads `Assets/AssetRegistry.json`. The editor then builds/loads scripts and opens the start scene. - -Saving serializes registered scene assets, the asset registry, and the project specification. Closing saves, unloads the user assembly, clears active editor scenes, and releases the active project. - -Functions such as `GetAssetsDirectory` assert an active project. Guard UI/background paths that can run during startup, failed open, or close. - -## Path contract - -- Project file: `/.epproj`. -- Assets root: `/Assets`. -- Scripts root: `/Scripts`. -- Registry metadata stores paths relative to `Assets`. -- `Project::GetAssetFilepath` maps metadata to disk. -- `Project::GetAssetRelativeFilepath` normalizes absolute editor selections before registration. - -Normalize at API boundaries. Do not compare an absolute content-browser path directly with stored relative metadata. - -## Registry and loading - -`CreateAsset` deduces type from extension, assigns the existing object's handle or a new UUID, inserts metadata under a mutex, then serializes the registry. `GetOrLoadAsset` returns cached objects, generates reserved primitives, or invokes the importer selected by metadata type. - -`RemoveAsset` removes metadata and any loaded cache entry, then serializes. It does not delete the source file. `UpdateAssetPath` changes metadata after a disk move/rename and then serializes. - -Registry serialization skips empty paths and runtime-generated assets. Release the registry lock before filesystem writes to avoid extending critical sections or deadlocking through future callbacks. - -The asynchronous loading parameter and `Tick` are currently scaffolding; do not claim async loading works without implementing synchronization, completion publication, and tests. - -## Import/export dispatch - -`AssetImporter` holds three dispatch tables keyed by `AssetType`: disk import, **packed** import, and export. Scene is implemented in all three via `SceneSerializer`; Mesh has disk import/export but **no packed importer**, so meshes cannot yet be loaded out of a package even though `PackFormat::Mesh` reserves a magic for them. A packed-mesh path needs the artifact model decided first (processed mesh data, not the glTF source) — do not wire a registration that would resolve to an unimplemented reader. - -Adding an asset type normally requires: - -1. Add enum/string conversions and extension deduction. -2. Add metadata/import/export dispatch. -3. Implement the actual asset class and loader. -4. Add content-browser icon and open behavior. -5. Add serialization/reference behavior for consumers. -6. Add registry round-trip and load tests. - -Do not register an extension as supported if its importer always returns null. - -## Content browser coordination - -The content browser synchronizes its root/current directory when the active project changes. It displays directories and files, assigns icons by registered or inferred type, and provides import/open/move/rename/delete operations. - -Mutation sequence matters: - -- Move/rename on disk first only if failure can be handled; then update metadata for registered assets. -- Delete the selected disk path and remove metadata when registered; do not conflate the two operations. -- Import external files into the project assets tree before registering the project-relative destination. -- Open scenes through the callback owned by `EditorLayer`, not by replacing panel context locally. - -## Packaging to `Game.eppak` - -`GameData` is the in-memory form of the package and the authority on its byte layout, which is documented as a field-by-field map in the header comment of `Project/GameData.h` — update that comment with any change. Every section is read and written through `Core/Buffer/` streams (`FileStreamWriter`/`FileStreamReader`), so a truncated or foreign file fails as a `false` return rather than an assert or a crash. - -`Asset/PackFormat.h` holds the four-character magic + version pairs: `EPAK` (package), `ESHD` (shaders), `EMSH` (mesh), `ESCN` (scene). Bump the version of whichever payload you changed. - -`ProjectExporter::Export` is a single ordered pass; `ProjectExportOptions` carries the configurations, build toggles and a progress callback, and `ProjectExportResult` accumulates warnings and errors instead of throwing: - -1. `ValidateProject` — name, configurations, start scene, output path. Nothing touches the filesystem until it passes. -2. Pack scenes. A packed scene is the `.epscene` file's **raw bytes** carried as a `PackedAssetData` payload; runtime-generated assets are skipped. -3. Pack shaders from the live renderer (`GetAllShaders()` — their sources are already in memory), then walk `Resources/Shaders` for `.hlsli` includes, keyed by path relative to that directory because that is how the sources `#include` them. This walk must stay in step with `ReadIncludesFromDisk` in `VulkanShader.cpp`, which hashes the same set for the shader cache key. -4. Build (optional) and validate the standalone runtime per configuration. -5. Create the output tree. **From here on every failure wipes the partial export** through the local `fail()` helper — preserve that, a half-written game directory is worse than none. -6. Per configuration: compile the user's C# scripts into the output via `dotnet`, stage the runtime executable + native dependencies + managed core, copy the loose `Assets` tree, then `gameData.Serialize(outputDirectory / GameData::Filename)`. - -Two consequences worth holding on to. First, packing shaders needs a live renderer, which is why exporter tests are graphical (`ProjectExport`) rather than unit. Second, gathering happens **inside** `Export` for every payload — do not add an option flag, constructor parameter, or externally-captured argument for one section, because that makes it the odd one out. - -## Consuming the package - -`EppoRuntime` deserializes `Game.eppak` inside `CreateApplication`, before the `Application` exists, then splits it: shaders and includes move into `ApplicationParams` (the renderer owns them from that point), and the rest goes to `RuntimeLayer`. - -`AssetManager` has a packed constructor taking owned metadata and `PackedAssetData` payloads. In that mode `GetOrLoadAsset` deserializes lazily from the in-memory payload instead of reading disk. There is no `PackedAssetManager` class — `EppoEngineTesting/Source/Project/PackedAssetManager.cpp` exercises `AssetManager`'s packed mode, and its tests are named accordingly. - -## Scene asset ownership - -`Scene` derives from `Asset` and carries its handle. Saving a previously unregistered scene creates registry metadata using that existing handle, then loads/caches it. Editor active-scene paths and project start-scene handles must remain consistent when using Save As or opening by filesystem path versus handle. - -## Testing strategy - -The headless `Project` suite (`EppoEngineTesting/Source/Project/`) owns `GameData` round-trips, packed-asset loading through `AssetManager`, and registry/path behavior. The graphical `ProjectExport` suite owns `ProjectExporter`, because packing shaders reads them from a live renderer; each of its tests guards on `Testing::AppHarness::IsAvailable()` and returns early when no GPU is present. - -Use `Testing::TempDir` for project directories and restore the previously active project after each test. Never mutate checked-in editor projects or their registries. Scene persistence belongs in `Scene`; end-to-end opening and rendering belongs in the `Renderer` suite's `SceneRendering` tests. diff --git a/.claude/skills/eppo-editor-development/SKILL.md b/.claude/skills/eppo-editor-development/SKILL.md deleted file mode 100644 index d857af9a..00000000 --- a/.claude/skills/eppo-editor-development/SKILL.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: eppo-editor-development -description: Extend and diagnose EppoEditor workflows across EditorLayer, edit/play scene state, panels and shared selection, viewport rendering and input focus, gizmos, project and scene commands, content browsing, docking, editor resources, and editor-to-engine boundaries. Use for changes under EppoEditor/Source or EppoEditor/Resources and for engine APIs introduced specifically to support editor behavior. ---- - -# Eppo Editor Development - -Read [references/architecture.md](references/architecture.md) before changing `EditorLayer` or a panel. Decide first whether the behavior belongs in the reusable engine or only in the editor shell. - -## Workflow - -1. Place reusable scene, asset, physics, scripting, or rendering behavior in `EppoEngine`; keep orchestration and authoring UI in `EppoEditor`. -2. Trace editor state through `m_EditorScene`, `m_ActiveScene`, `SceneState`, `PanelManager` scene context, selection, and `SceneRenderer` scene context. -3. Preserve UUID-based remapping whenever a scene copy or replacement invalidates EnTT handles and `Entity` wrappers. -4. Route panel-wide scene context and selection through `PanelManager`. Route operations requiring editor authority, such as opening a scene, back through `EditorLayer` callbacks. -5. Keep polled input gated by viewport focus and keep gizmo interaction from also moving the editor camera. -6. Test extracted engine behavior in its matching headless suite. Use `App` for real boot and frame advancement, or the `Renderer` suite's `SceneRendering` tests when the change requires the real renderer, viewport, or editor-camera path. - -## Guardrails - -- Start play from a copy of the editor scene; never mutate the authored scene as runtime state. -- Stop runtime and clear script contexts before dropping the runtime scene. -- Resolve editor resources and projects relative to the `EppoEditor/` working directory; keep managed binaries executable-relative. -- Apply docking-layout restoration before submitting windows for the frame. -- Preserve panel names referenced by the default docking layout. diff --git a/.claude/skills/eppo-editor-development/references/architecture.md b/.claude/skills/eppo-editor-development/references/architecture.md deleted file mode 100644 index a8ad1a56..00000000 --- a/.claude/skills/eppo-editor-development/references/architecture.md +++ /dev/null @@ -1,119 +0,0 @@ -# Editor architecture - -## Ownership map - -`EppoEditor.cpp` implements `CreateApplication` and pushes `EditorLayer`. `EditorLayer` is the editor shell and owns: - -- `m_EditorScene`: the authored scene; -- `m_ActiveScene`: the scene currently displayed and updated; -- `m_SceneState`: edit or play; -- `SceneRenderer` and `EditorCamera`; -- `PanelManager`, toolbar icons, viewport state, gizmo state, and project/scene commands; -- the export-game command, which collects `ProjectExportOptions` from the UI and hands them to `ProjectExporter(project).Export(options)` — the editor supplies paths and toggles and reports progress, it does not gather packed payloads itself. - -`PanelManager` owns panels and centralizes scene context plus selected `Entity`. Panels receive a non-owning manager pointer through `Panel`. Current panels are: - -- `SceneHierarchyPanel`: tree display, selection, entity creation/deletion, and hierarchy interaction; -- `PropertyPanel`: component editing, script fields, component addition/removal, collider fitting; -- `ContentBrowserPanel`: filesystem navigation, asset icons, importing, moving/renaming/deleting, and opening scenes through a callback; -- `LogPanel`: level/source/text filtering over engine log output. - -`LogPanel` reads through `LogSink`, an editor-only bounded ring buffer (`LOG_BUFFER_CAPACITY`) attached to the loggers via `Log::AddSink`. A shipped runtime never installs it, so it never retains log text in memory — keep it that way. The panel syncs from the sink by version rather than re-reading every frame, and recomputes its filtered index list only when the filter or the entry set changed; preserve that when adding filters. - -## Edit/play state machine - -In edit state, `m_ActiveScene == m_EditorScene`. The editor camera renders the authored scene, selection highlighting is active, and editing commands operate on authored data. - -Play transition: - -1. Capture selected UUID before replacing the scene. -2. Set state to play. -3. `Scene::Copy(m_EditorScene)` into `m_ActiveScene`. -4. Update panel scene context. -5. Resolve selection by UUID in the runtime copy. -6. Start runtime physics and scripts. -7. Set script scene context to the runtime scene. - -Play update steps runtime, then renders from the primary scene camera. If no primary camera exists, the editor camera renders as a fallback and the viewport displays a notice rather than a stale frame. - -Stop transition: - -1. Clear script scene context while the runtime scene is alive. -2. Stop the runtime scene. -3. Capture the selected runtime UUID before releasing the copied scene. -4. Restore `m_ActiveScene = m_EditorScene` and edit state. -5. Update panels and resolve selection by UUID in the authored scene. - -Never retain an `Entity` across scene replacement: it contains an EnTT handle and raw `Scene*`. - -## Per-frame ordering - -`OnUpdate` consumes UI state from the previous UI pass. It: - -1. Pulls selection from `PanelManager`. -2. Propagates viewport size to cameras, both scenes, and `SceneRenderer`. -3. Gates polled input using last frame's viewport-focus state. -4. Refreshes the renderer's scene reference and edit-mode highlight. -5. Updates the editor camera or runtime scene and renders. - -`OnUIRender` applies deferred layout restoration before any windows begin, builds the dockspace/menu, renders the viewport image, records viewport bounds/focus/hover, draws toolbar/notices, updates panels, and handles popups. - -One-frame lag for focus or selection is intentional where documented. Avoid mixing same-frame UI mutation into render-update state unless the ordering is deliberately redesigned. - -## Project and scene flow - -Opening a project: - -1. Close/save the previous project and unload its collectible user assembly. -2. Deserialize the `.epproj` and asset registry. -3. Build the project C# assembly with the current `EppoScriptCore.dll` path. -4. Initialize scripting and load the user assembly. -5. Open the start scene only after script class metadata exists, so script fields deserialize correctly. - -Saving a project saves the active scene, assigns the start scene if absent, serializes registered scenes, writes the asset registry, and writes the project file. - -Opening scenes must go through `EditorLayer`, even when initiated in `ContentBrowserPanel`, because the layer owns active/editor scene bookkeeping and scripting assumptions. - -## Panel extension checklist - -To add a panel: - -1. Derive from `Panel` and implement `RenderGui`. -2. Register it in `EditorLayer::OnAttach` through `PanelManager::AddPanel`. -3. Use manager-provided scene and selection instead of storing a divergent authoritative copy. -4. Add its window toggle to the editor menu. -5. If it belongs in the default dock layout, update `Resources/Layouts/DefaultLayout.ini` and keep its window name stable. -6. Load icons/resources from `FS::GetResourcesDirectory()`. - -Use callbacks to request editor-authoritative operations rather than giving a panel broad access to `EditorLayer` internals. - -Entity duplication is already routed from `SceneHierarchyPanel` to `Scene::DuplicateEntity`; extend the engine operation and its tests before adding editor-side duplication logic. - -## Property editing checklist - -When adding a component editor: - -- Match the component's native units and coordinate conventions. -- Use `DrawComponent` for consistent header/removal behavior. -- Disable or guard operations that require another component or loaded asset. -- Route collider auto-fit to `Scene`, where runtime-independent mesh-bound logic belongs. -- For script fields, edit `ScriptEngine`'s stored field map, not a live runtime instance. -- Consider whether editing should be allowed in play mode and whether it should persist after stop. - -## Content browser model - -The content browser displays both registered and unregistered filesystem entries. `AssetManager::GetHandleForPath` determines registration. File mutations must coordinate filesystem state with registry state: - -- import/create registers supported types; -- move/rename updates registered metadata paths; -- delete removes registry metadata and separately deletes the selected filesystem entry; -- opening a scene calls back into `EditorLayer` by handle; -- icons derive from `AssetType`, with generic file/directory fallbacks. - -## Resources and sample project - -`EppoEditor/Resources/` owns shaders, fonts, icons, layouts, and project templates. The editor and graphical tests run with `EppoEditor/` as their working directory and read these files in place through `FS::GetResourcesDirectory()`; builds never stage a copy. Panels use the engine's `ImGui/ScopedBegin.h` and `ImExt.h` helpers; toolbar hit-testing goes through `Utils::IsInsideRoundedRect` so clicks in rounded-corner gaps are ignored. A sample project lives at `EppoEditor/Projects/Test/Test.epproj` and is likewise opened directly from the source tree. - -## Testing boundaries - -Prefer tests in engine suites for behavior extracted from editor UI: scene hierarchy, serialization, asset registry, collider fitting, scripting fields, and input semantics. Use the `Renderer` suite's `SceneRendering` tests for editor-camera and scene-render behavior over frames. `App` verifies real application/window/device boot. Direct editor UI automation is not currently part of the repository test harness, so keep UI handlers thin and engine behavior testable. diff --git a/.claude/skills/eppo-physics-integration/SKILL.md b/.claude/skills/eppo-physics-integration/SKILL.md deleted file mode 100644 index 2938f61e..00000000 --- a/.claude/skills/eppo-physics-integration/SKILL.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: eppo-physics-integration -description: Develop and diagnose Eppo's Box3D integration across rigid bodies, collider shapes, hierarchy-aware collider gathering, world/local transform and scale conversion, runtime simulation and scene synchronization, collider fitting and debug rendering, managed physics APIs, and physics regressions. Use for changes under EppoEngine/Source/Physics, physics-related scene components or runtime code, physics ScriptGlue APIs, property-panel collider editing, or Physics tests. ---- - -# Eppo Physics Integration - -Read [references/architecture.md](references/architecture.md) before changing collider dimensions, hierarchy traversal, pose conversion, or physics scripting. Most physics regressions are transform-contract regressions rather than Box3D API mistakes. - -## Workflow - -1. State the coordinate space for every pose, offset, rotation, and dimension involved: authored local, composed world, rigid-body local, or Box3D world. -2. Add a focused regression in `EppoEngineTesting/Source/Physics/PhysicsWorld.cpp`; cover hierarchy, rotation, non-uniform or mirrored scale, and degenerate dimensions when relevant. -3. Keep `PhysicsWorld` responsible for Box3D handles and operations. Keep scene traversal, collider aggregation, and ECS synchronization in `Scene`. -4. Update editor component controls, serialization, debug rendering, and C# APIs when changing a physics component. -5. Preserve safe no-op/default behavior for missing bodies, expired worlds, invalid entities, and absent runtime contexts. -6. Run `Physics`; also run `Scene`, `Scripting`, or the graphical `Renderer` suite when their boundary changes. - -## Guardrails - -- Build one Box3D body per `RigidBodyComponent`; gather descendant colliders until another rigid-body boundary. -- Exclude entity scale from the body pose and apply composed scale to collider geometry and offsets. -- Step physics before scripts so scripts observe the current simulated pose. -- Synchronize parent bodies before children and convert world poses back to authored local transforms. -- Keep bodies without colliders valid and report them without suppressing simulation. diff --git a/.claude/skills/eppo-physics-integration/references/architecture.md b/.claude/skills/eppo-physics-integration/references/architecture.md deleted file mode 100644 index 42347adb..00000000 --- a/.claude/skills/eppo-physics-integration/references/architecture.md +++ /dev/null @@ -1,101 +0,0 @@ -# Physics integration architecture - -## Responsibility split - -`PhysicsWorld` wraps Box3D and owns the Box3D world plus the entity-UUID-to-body map. It creates bodies, attaches already-described colliders, steps simulation, and exposes safe body operations. - -`Scene` owns ECS interpretation: - -- compose hierarchy transforms; -- find rigid-body roots; -- gather descendant collider components; -- convert authored transforms into rigid-body-local collider data; -- create bodies at runtime start; -- synchronize Box3D world poses back to ECS local transforms after each step. - -Keep this split so collider derivation remains reusable outside the editor and Box3D details do not leak across scene code. - -## Data model - -`RigidBodyComponent` defines static, kinematic, or dynamic body type plus gravity scale and damping. Collider components define authored dimensions, local offset, density, friction, and restitution: - -- box: half-size; -- sphere: radius; -- capsule: radius and cylindrical height; -- cylinder: radius and height. - -`ColliderData` is the scene-to-physics normalized description. It includes shape type, transformed dimensions/offset, rotation, and material properties. `PhysicsWorld::AttachCollider` maps it to the appropriate Box3D shape definition. - -One `RigidBodyComponent` produces one Box3D body, even when it has no colliders. - -## Collider gathering - -At runtime start, for each rigid-body entity: - -1. Compose the entity's world transform and decompose translation, rotation, and scale. -2. Build the Box3D body pose from translation and rotation only. -3. Traverse the rigid-body entity and descendants. -4. Stop traversal when reaching a descendant with its own `RigidBodyComponent`; that node starts a new body boundary. -5. For each collider, compute its pose relative to the root body and apply the composed hierarchy scale to shape dimensions and offsets. -6. Track visited UUIDs to prevent malformed hierarchy cycles from recursing forever. -7. Attach every gathered shape to the root body. - -Scale is authored ECS geometry, not part of the Box3D body pose. Mirrored scale must mirror offsets while dimensions remain physically valid magnitudes. Nested rotations rotate collider offsets and local axes into body space for shear-free transforms. Current decomposition approximates rotation when non-uniform scale and rotation compose into shear; do not claim exact collider poses for that case without redesigning the transform representation and adding focused tests. - -## Simulation synchronization - -`Scene::OnUpdateRuntime` steps physics before scripts. It collects bodies with hierarchy depth, sorts parents before children, and reads each Box3D world pose. - -For a root entity, write simulated translation/rotation directly while preserving authored scale. For a parented body, multiply the world pose by the inverse parent world transform, decompose it, and write the resulting local translation/rotation. Parent-first order ensures the inverse uses the current frame's parent pose. - -Scripts then observe current transforms and can query or mutate body velocity/impulses through the active physics world. - -## Degenerate and missing data - -- A body with no collider still simulates; the scene records a warning name for editor display. -- Missing entity/body operations return safe defaults or no-op. -- An expired scripting physics-world weak reference makes managed callbacks safe no-ops. -- Zero/near-zero collider dimensions are clamped or converted according to existing shape behavior; preserve tests such as zero-height capsule and zero-extent box. -- A collider without any rigid-body ancestor creates no body. - -## Collider fitting - -`Scene::FitColliderToMesh` reads reusable mesh primitive bounds through the active project asset manager and derives authored collider dimensions: - -- box from bounds half-extents; -- sphere from the largest relevant extent; -- capsule/cylinder from vertical extent plus radial horizontal extent. - -Keep fitting in `Scene`, not `PropertyPanel`, so a runtime or future standalone tool can reuse it. The property panel only triggers the operation. - -## Cross-system checklist - -When adding or changing a physics property or shape, inspect: - -1. `Scene/Components.h` schema/defaults. -2. `Physics/PhysicsTypes.h` and `PhysicsWorld` Box3D mapping. -3. Scene collider gathering, scale, offsets, and runtime sync. -4. Scene copy/duplicate and JSON serialization. -5. `PropertyPanel` editing and fit controls. -6. `SceneRenderer` collider debug meshes/wireframes. -7. C# component API, `Physics` API, internal-call delegates, native callbacks, and registration. -8. Physics and scripting tests. - -## Regression matrix - -`EppoEngineTesting/Source/Physics/PhysicsWorld.cpp` is intentionally broad. Choose cases from the matrix that match the risk: - -| Risk | Cases | -| --- | --- | -| Basic dynamics | gravity, impulse, damping, kinematic velocity, static body | -| Shape mapping | sphere, capsule, cylinder, rotated box, material/dimension behavior | -| Degenerate geometry | zero capsule height, zero box extent | -| Authored scale | scaled root, nested scale, mirrored scale | -| Hierarchy | child colliders, multiple children, nested rotation, nested rigid-body boundary | -| Pose sync | parented dynamic root, child body under moving parent | -| Persistence/copy | serialized physics components, copied scene collider gathering | -| Asset-derived shape | fit every collider type to primitive mesh bounds | - -Also run `Scripting` when a managed property or physics call changes, and the graphical `Renderer` suite when debug rendering or frame-level behavior changes. - -Use `PhysicsWorld::HasBody`, `GetShapeCount`, and `GetPosition` to observe body boundaries, gathered shapes, and authored-to-world pose mapping without reaching into Box3D internals. diff --git a/.claude/skills/eppo-rendering-pipeline/SKILL.md b/.claude/skills/eppo-rendering-pipeline/SKILL.md deleted file mode 100644 index 36acf5a4..00000000 --- a/.claude/skills/eppo-rendering-pipeline/SKILL.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: eppo-rendering-pipeline -description: Develop and diagnose Eppo's Vulkan and NVRHI renderer, including device and swapchain lifecycle, shader compilation and reflection, binding layouts, bindless descriptors, GPU resources, pipelines, render passes, command buffers, SceneRenderer passes, swapchain compositing, ImGui rendering, and graphical tests. Use for changes under EppoEngine/Source/Renderer, EppoEngine/Source/Platform/Vulkan, renderer-facing ImGui code, editor shaders, or the Renderer test suite. ---- - -# Eppo Rendering Pipeline - -Read [references/architecture.md](references/architecture.md) before changing renderer initialization, bindings, pass construction, or frame submission. Follow a resource from creation through ownership, descriptor registration, binding, command recording, submission, and release. - -## Workflow - -1. Identify the layer that owns the change: Vulkan platform setup, NVRHI abstraction, reusable GPU resource, shader/reflection contract, render pass, scene submission, or editor presentation. -2. Trace initialization and frame order before editing. Respect the publication order between `DeviceManager`, `Renderer`, the descriptor manager, shader loading, swapchain images, and ImGui. -3. For shader changes, update source, reflected resource expectations, C++ set/binding declarations, pipeline layouts, pass inputs, and tests together. -4. For resources, define lifetime and resize behavior. Preserve bindless handle move-only ownership and avoid retaining stale framebuffer or descriptor handles. -5. Add the smallest renderer regression. Use non-graphical construction tests only where no live device is required; otherwise use the `Renderer` graphical suite, whose `SceneRendering` tests drive end-to-end `Scene -> SceneRenderer` behavior through `TestContext`/`ScenarioLayer`. -6. Build before running graphical tests. Run from `EppoEditor/` or through CTest so shaders resolve directly from the source resources. - -## Guardrails - -- Treat descriptor set ordering as an ABI: NVRHI legacy mode maps set numbers to layout-vector indices. -- Keep resource and sampler bindless heaps independent. -- Do not assume swapchain image count equals frames in flight. -- Skip zero-sized viewport resize work and tolerate minimized windows. -- Keep Vulkan-specific code below the renderer abstraction unless the API genuinely cannot express it. diff --git a/.claude/skills/eppo-rendering-pipeline/references/architecture.md b/.claude/skills/eppo-rendering-pipeline/references/architecture.md deleted file mode 100644 index bc2082df..00000000 --- a/.claude/skills/eppo-rendering-pipeline/references/architecture.md +++ /dev/null @@ -1,113 +0,0 @@ -# Rendering architecture - -## Layer map - -| Layer | Primary files | Responsibility | -| --- | --- | --- | -| Application ownership | `Core/Application.*`, `Core/Window.*` | Create the window, device manager, renderer, ImGui layer, and drive frames. | -| API-neutral device | `Renderer/DeviceManager.*` | Select the renderer API, expose NVRHI device/swapchain state, and own `Renderer`. | -| Vulkan backend | `Platform/Vulkan/DeviceManagerVK.*`, `PhysicalDevice.*`, `LogicalDevice.*`, `Swapchain.*`, `Vulkan.h` | Create Vulkan instance/device/surface/swapchain and wrap them with NVRHI. | -| Shader backend | `Renderer/Shader.*`, `ShaderLibrary.*`, `Platform/Vulkan/VulkanShader.*` | Load sources, compile/cache SPIR-V through DXC, reflect resources, and create NVRHI shader/layout handles. | -| Resources | `Image`, `Sampler`, vertex/index/uniform/storage buffers, `Framebuffer` | Own NVRHI resources, upload data, resize, and participate in descriptors. | -| Binding | `DescriptorManager.*`, `RenderPass.*` | Own global bindless tables and pass-local binding sets/push constants. | -| Execution | `Pipeline.*`, `RenderCommandBuffer.*`, `Renderer.*` | Create graphics pipelines, record commands/timers, begin/end passes, and composite a final image to the swapchain. | -| Scene orchestration | `SceneRenderer.*`, editor shader resources | Batch scene submissions and execute geometry, sky, and wireframe passes. | -| UI | `ImGui/ImGuiRenderer.*`, `ImGuiLayer.*` | Render ImGui draw data through the same NVRHI device and bindless infrastructure. | - -## Initialization order - -1. `Application` creates a GLFW window. -2. `DeviceManager::Create` chooses `DeviceManagerVK`; its constructor gathers GLFW's required instance extensions, creates the Vulkan instance and physical/logical devices, and creates the NVRHI device. -3. `DeviceManagerVK::Init` creates the window surface and swapchain resources. -4. `DeviceManager::InitRenderer` publishes a `Renderer` owned by the device manager. The `Renderer` **constructor** creates the descriptor manager, so its global binding layouts exist before anything else runs; `Renderer::Init` then creates the swapchain composite sampler and composite command buffer. -5. `Application` calls `Renderer::LoadShaders(packedShaders, packedIncludes)` — a separate, explicit step, not part of `Renderer::Init`. It iterates the fixed `s_EngineShaderNames` set (`composite`, `geometry`, `imgui`, `skybox`, `wireframe`). Empty arguments mean compile from `Resources/Shaders`; a non-empty packed set that is missing a name, or has an entry with no sources, is an error rather than a silent disk fallback. -6. ImGui attaches **after** shader loading, because `ImGuiRenderer` grabs `GetShader("imgui")` in its constructor during `ImGuiLayer::OnAttach`. Editor layers may then create images and `SceneRenderer` resources. - -Do not move shader/resource construction earlier without rechecking calls to `DeviceManager::Get()` and `GetRenderer()->GetDescriptorManager()`, and do not fold `LoadShaders` back into `Renderer::Init` — the runtime needs to supply packed sources between the two. - -## Frame flow - -`Application::StepFrame` pumps events and, when not minimized, updates layers and submits UI around the device frame: - -1. Acquire/begin the current swapchain frame. -2. Update application layers; the editor asks `Scene` to submit to `SceneRenderer`. -3. Begin ImGui, render layer UIs, and end/record ImGui. -4. Submit recorded command lists. -5. Present the acquired swapchain image. - -Keep acquisition failure and zero-size/minimized paths safe. Swapchain resize recreates image/framebuffer state; anything caching those handles must be refreshed. - -## SceneRenderer flow - -`Scene::RenderScene` visits mesh and point-light components and submits composed world transforms plus environment data. `SceneRenderer` separates collection from execution: - -- `BeginScene` selects editor or scene camera data and resets per-frame submission state. -- `SubmitMesh` batches instances by mesh/draw key. -- `SubmitPointLight` and `SubmitEnvironment` fill scene buffers. -- `EndScene` flattens and uploads instance transforms before command recording; `PrepareRender` then uploads camera, light, and environment buffers. -- `GeometryPass` renders material geometry to the main framebuffer. -- `SkyPass` draws the environment. -- `WireframePass` draws debug colliders, selected-entity highlights, and mesh wireframes when enabled. -- `EndScene` records/submits the command buffer and exposes the final image to the editor viewport. - -`EditorLayer` calls `SetScene` every frame because edit/play transitions replace the active scene while the renderer object survives. - -## Presenting the final image - -The editor displays `SceneRenderer`'s final image as an ImGui viewport texture. A deployed runtime has no such panel, so `RuntimeLayer` calls `Renderer::CompositeToSwapchain(image)` instead: a full-screen three-vertex draw through the `composite` shader straight into the current swapchain framebuffer. - -Its pass state is **per back buffer and lazily built** — `m_CompositePasses` / `m_CompositeFramebuffers` are sized to the back-buffer count, and an entry is rebuilt when it is empty or when the swapchain handed back a different `nvrhi::FramebufferHandle` (which is what a resize looks like from here). Anything caching a framebuffer handle must follow the same compare-and-rebuild rule. - -## Shader and binding contract - -Shader sources live in `EppoEditor/Resources/Shaders`, with includes under `Resources/Shaders/Includes`. The editor and graphical tests read them directly from the `EppoEditor/` working directory. Compiled SPIR-V is cached in `FS::GetShaderCacheDirectory()` — `Resources/Shaders/Cache` when no writable directory is configured, otherwise `/ShaderCache`. - -A `ShaderSpecification` carrying `Sources` is packed: it compiles those, and resolves `#include`s only from its `Includes` map through a handler that never touches the filesystem. A deployed runtime ships no shader files, so an include missing from the pack fails the compile rather than finding a stray file on disk. Without `Sources` the shader is compiled from `Resources/Shaders` with DXC's default (disk-reading) include handler. `Renderer::LoadShaders` takes the packed set or nothing, and treats a packed entry that has no sources as missing rather than letting it degrade into a disk compile. A failed compile logs and asserts in the constructor: it means a broken editor build, and `EP_ASSERT` throws under `EP_DIST`, so a deployed game surfaces it through the runtime error dialog. - -`VulkanShader` compiles and reflects each stage. Reflection populates: - -- vertex input attributes and stride; -- resource bindings grouped by descriptor set; -- push-constant range; -- NVRHI binding layouts ordered by ascending set. - -NVRHI legacy Vulkan binding mode treats the layout vector index as the Vulkan descriptor-set number. A missing set in the middle shifts every later set. `Shader::GetBindingLayouts` therefore returns an ordered map, and `RenderPass::Bake` merges static pass bindings with global bindless layouts without gaps. - -When changing a shader binding: - -1. Update the shader declaration and stage usage. -2. Confirm reflection recognizes its NVRHI resource type and array size. -3. Update pass `SetInput(set, binding, resource)` or bindless registration. -4. Update push-constant declaration if applicable. -5. Confirm pipeline layout order and pass baking. -6. Editing a file under `Resources/Shaders/Includes` invalidates the cache on its own: the cache hash covers the top-level `.vert`/`.frag` source plus every include's contents. -7. Add or update `Shader`, `Pipeline`, or `RenderPass` tests. - -## Bindless ownership - -`DescriptorManager` owns separate resource and sampler heaps. Each heap has a binding layout, descriptor table, capacity, next sequential slot, free list, and mutex. - -`BindlessHandle` is move-only RAII. Destruction or move-assignment releases an owned slot to the originating manager through a weak reference. Preserve these invariants: - -- invalid index is `uint32_t` max; -- released slots are preferred before heap growth; -- resource and sampler indices are independent; -- growth cannot exceed the declared maximum table capacity; -- moving a handle transfers ownership exactly once; -- a resource must not outlive the descriptor data it points at unless the descriptor is rewritten or released. - -Images, uniform buffers, storage buffers, and samplers register through the appropriate heap. Materials store bindless indices rather than owning the global tables. - -## Resource and resize rules - -- `Framebuffer` owns its color/depth images and rebuilds them on resize. -- `Pipeline` derives current size from its framebuffer; resize through the owning pass/pipeline path. -- Buffer resize must preserve intended usage flags and rewrite descriptors when the underlying NVRHI handle changes. -- `RenderCommandBuffer` allocates timing data per back buffer, not merely per frame in flight. -- Use NVRHI handles for lifetime management; use raw native Vulkan handles only inside the backend and swapchain bridge. - -## Tests - -Renderer tests are registered as graphical because most require a live Vulkan/NVRHI device. The suite covers device availability, descriptor allocation/lifetime/growth, pipeline layout order, pass binding-set baking, shader layouts, framebuffer creation, command submission/timers, sampler ownership, and mesh material indices. - -Behavior that must traverse `Scene -> SceneRenderer` or editor-camera input belongs in the same suite's `SceneRendering` tests, which drive multiple frames through `TestContext`/`ScenarioLayer`. Run graphical suites only with a real display and GPU. Headless CI excludes the `graphical` label, so report any unexecuted graphical coverage explicitly. diff --git a/.claude/skills/eppo-scene-ecs-lifecycle/SKILL.md b/.claude/skills/eppo-scene-ecs-lifecycle/SKILL.md deleted file mode 100644 index 94f21d1d..00000000 --- a/.claude/skills/eppo-scene-ecs-lifecycle/SKILL.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -name: eppo-scene-ecs-lifecycle -description: Develop and diagnose Eppo scenes and ECS behavior across EnTT entities, component ownership, UUID identity, parent-child relationships, world transforms, duplication and copying, scene serialization and repair, runtime start/update/stop, render submission, physics and scripting coordination, and scene tests. Use for changes under EppoEngine/Source/Scene or any feature that adds or changes scene components. ---- - -# Eppo Scene and ECS Lifecycle - -Read [references/architecture.md](references/architecture.md) before adding components or changing hierarchy, copy, serialization, or runtime behavior. Treat component definition, copying, persistence, editor exposure, scripting exposure, and tests as one feature surface. - -## Workflow - -1. Establish the identity and ownership effects: transient EnTT handle, stable UUID, scene pointer, asset handle, relationship UUID, or runtime-side object. -2. Add a regression first in `EppoEngineTesting/Source/Scene/`, or in Physics/Scripting when the behavior crosses those runtime systems. -3. Update every component touchpoint: `Components.h`, scene creation/copy/duplicate logic, serializer read/write, editor property UI, and managed wrappers/internal calls when exposed to scripts. -4. Preserve hierarchy consistency and world transforms through reparenting, repair, duplication, deletion, scene copy, and physics synchronization. -5. Keep runtime start/update/stop symmetric. Create runtime-only state on start and release it on stop without leaking values into the authored scene. -6. Run `Scene` plus every affected integration suite. - -## Guardrails - -- Use UUIDs across scene copies and serialization; never persist EnTT handles or `Entity` wrappers. -- Keep `m_EntityMap` synchronized with the registry. -- Treat `RelationshipComponent` as sparse: roots need not carry it. -- Iterate the whole scene through `ForEachEntity`; sort by UUID before deterministic serialization. -- Make malformed relationship data recoverable without discarding otherwise valid entities. diff --git a/.claude/skills/eppo-scene-ecs-lifecycle/references/architecture.md b/.claude/skills/eppo-scene-ecs-lifecycle/references/architecture.md deleted file mode 100644 index 257c0d78..00000000 --- a/.claude/skills/eppo-scene-ecs-lifecycle/references/architecture.md +++ /dev/null @@ -1,130 +0,0 @@ -# Scene and ECS architecture - -## Core model - -`Scene` is both an `Asset` and the owner of an `entt::registry`. It maintains an `m_EntityMap` from stable `UUID` to transient EnTT handle. `Entity` is a lightweight pair of handle and raw `Scene*`; it does not own either. - -Every entity receives: - -- `IDComponent` with stable UUID; -- `TagComponent` with display name; -- `TransformComponent` with authored local translation, Euler rotation, and scale. - -Other components are optional. `RelationshipComponent` is intentionally sparse and stores parent/children as UUIDs, not registry handles. - -## File map - -| File | Responsibility | -| --- | --- | -| `Scene/Components.h` | Native component schemas and defaults. | -| `Scene/Entity.*` | Type-safe component access over an EnTT handle. | -| `Scene/Scene.*` | Entity lifecycle, hierarchy, transforms, runtime coordination, copying, and render submission. | -| `Scene/SceneSerializer.*` | JSON persistence, field persistence, deterministic ordering, and relationship repair. | -| `Renderer/Camera/SceneCamera.*` | Projection state stored by `CameraComponent`. | -| `EppoEngineTesting/Source/Scene` | ECS, hierarchy, copy/duplicate, and malformed-serialization regressions. | - -## Identity rules - -- EnTT handles are valid only within one registry lifetime. -- `Entity` equality includes both handle and scene pointer. -- UUIDs survive serialization and `Scene::Copy` and are the only supported cross-scene identity. -- `m_EntityMap` must be updated during create, deserialize, copy, and destroy. -- Asset handles identify referenced assets such as meshes or skyboxes; they are separate from entity UUIDs. - -Capture UUID values before releasing a scene. Never dereference or inspect an `Entity` after its scene is destroyed. - -## Hierarchy and transforms - -`RelationshipComponent` stores `Parent` and `Children`. Roots normally have no relationship component. `Scene::SetParent`: - -1. Rejects self-parenting and cycles. -2. Captures the child's current world transform. -3. Removes the child from its previous parent's child list. -4. Adds/removes sparse relationship components as needed. -5. Recomputes the child's local transform beneath the new parent so the world pose stays fixed. - -`GetWorldTransform` composes local transforms up the UUID parent chain. Protect new traversal code against missing parents and cycles; malformed serialized relationships are repaired, but runtime code should not hang if invariants are temporarily broken. - -Deletion of a subtree must detach its root from the external parent, recursively destroy descendants, remove entity-map entries and script field maps, and tolerate deletion during full-scene enumeration. - -## Serialization model - -`SceneSerializer` writes scene environment and entities. Entities are sorted by UUID to produce deterministic output. Component data is stored explicitly rather than by raw memory layout. - -Deserialization creates entities by serialized UUID, populates components, restores script field values when scripting metadata is available, and then repairs relationships. Repair handles: - -- a parent that does not list the child; -- a child list that names an entity with a different parent; -- missing parent UUIDs; -- invalid parent while valid children remain; -- duplicate children; -- collider nodes detached by missing ancestry. - -Repair preserves world transforms when detaching. Notices are collected for the editor to display after load. - -When adding a component, update serialization and deserialization together. Use optional-key handling for backward compatibility when older scenes legitimately lack new properties. Defaults should produce sensible behavior. - -## Copy and duplication - -`Scene::Copy` creates a new registry and maps each source UUID to a new EnTT handle with the same UUID. Component-copy helpers copy supported component types and environment state. Script field storage remains in `ScriptEngine` under the preserved UUID, so the runtime copy reuses the authored values without copying the side table. Entity handles must never be copied directly. - -`DuplicateEntity` creates new UUIDs for the source subtree, copies copyable components and independent script field maps, recreates relationships among the duplicate nodes, and attaches the new subtree consistently. `ScriptFieldType::Entity` values are currently copied as raw UUIDs, so references still point at the original entity even when the target is inside the duplicated subtree. Decide and test whether a feature should preserve or remap those references before changing duplication semantics. - -Current recursive duplication has no visited set. It assumes a valid acyclic relationship tree; harden it before relying on duplication of malformed runtime data. Add new component types to both full-scene copy and duplicate paths. - -## Runtime lifecycle - -Runtime state belongs to the copied play scene: - -### Start - -- Warn if there is no primary camera. -- Create a `PhysicsWorld` and bodies/colliders from authored components. -- Publish the active physics world to scripting. -- Create and invoke scripts for entities with `ScriptComponent`. - -### Update - -- Step physics. -- Synchronize body poses into transforms, parents before children. -- Invoke script updates after physics. - -### Stop - -- Release physics and warnings. -- Invoke script destruction and clear instance state/context through the editor/script lifecycle. - -Keep start and stop symmetric when introducing runtime-only systems. - -## Rendering boundary - -`OnRenderEditor` uses `EditorCamera`. `OnRenderRuntime` resolves the primary `CameraComponent`; it does nothing without one. Both call `RenderScene`, which: - -- submits mesh instances using composed world transforms; -- submits point lights in world space; -- submits environment settings; -- lets `SceneRenderer` own GPU details. - -Do not place NVRHI/Vulkan command logic in `Scene`. - -## Adding a component - -Check every applicable surface: - -1. Native schema/default/copy semantics in `Components.h`. -2. Scene copy and subtree duplication. -3. JSON serialize/deserialize and backward-compatible defaults. -4. Property panel authoring and add/remove UI. -5. Runtime initialization/update/cleanup. -6. Render submission or physics integration. -7. C# wrapper, internal calls, and native registration. -8. Core umbrella header if it is a public engine type. -9. Focused suite tests plus serialization round-trip coverage. - -## Test routing - -- `Scene`: entity APIs, sparse relationships, repair, deletion during iteration, and duplication. -- `Physics`: runtime bodies, hierarchy/scale transforms, collider serialization, and copied-scene behavior. -- `Scripting`: component wrappers and script field storage, including duplicate field-map independence under the suite's shared CoreCLR harness. -- graphical `Renderer` (its `SceneRendering` tests): camera movement, loaded scene behavior, and scene-to-renderer integration. -- `Project`: scene persistence as a packed asset, when a change affects what `Game.eppak` carries. diff --git a/.claude/skills/eppo-scripting-integration/SKILL.md b/.claude/skills/eppo-scripting-integration/SKILL.md deleted file mode 100644 index f72b8a4f..00000000 --- a/.claude/skills/eppo-scripting-integration/SKILL.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -name: eppo-scripting-integration -description: Develop and diagnose Eppo's C++/C# scripting integration across CoreCLR hosting, managed assembly discovery, native internal calls, field and method marshalling, entity script lifecycle, script hot reload and the managed build, deployment, and scripting tests. Use for changes under EppoEngine/Source/Scripting, EppoScriptCore, script-aware scene/editor code, Utility/FileWatcher or Process when driving script rebuilds, EppoScriptCore/premake5.lua, or the Scripting and ScriptMarshalling suites. ---- - -# Eppo Scripting Integration - -Read [references/architecture.md](references/architecture.md) before changing the scripting boundary. Treat the native declarations, managed exports, internal-call registration, serialized field storage, and managed test harness as one contract. - -## Workflow - -1. Trace the request through every affected layer: C# public API, managed `ScriptGlue`, native `ManagedFunctions` or `ScriptGlue`, `Assembly`, `ScriptEngine`, scene/editor lifecycle, and deployment. -2. Define the ABI before editing. Keep type widths, enum ordinals, calling conventions, entry-point names, argument order, ownership, and string allocation/freeing identical on both sides. -3. Add or update the smallest regression in `EppoEngineTesting/Source/Scripting/`. Extend `EppoEngineTesting/TestData/Scripts/Source/HarnessScript.cs` when managed user code is required. -4. Implement both sides of a cross-boundary change in the same change set. Preserve guarded behavior when the runtime, scene context, entity, component, or physics world is unavailable. -5. Rebuild `EppoEngineTesting` after any C# edit so the generated build (VS or Ninja) rebuilds and deploys both managed assemblies. -6. Run `Scripting` and `ScriptMarshalling`; run the broader headless set when lifecycle, scene, physics, or build wiring changes. - -## Guardrails - -- Initialize CoreCLR once per process; do not design tests around repeated runtime initialization. -- Keep editor field storage authoritative. Push it into new managed instances at runtime start; do not serialize transient managed values. -- Keep `ScriptEngine`'s entity-instance registry authoritative for live script existence. -- Clear scene and physics contexts before their native objects can expire. -- Never reload the user assembly while a scene context is published; defer to a later frame instead of unloading under live managed instances. -- A project with no `.csproj` is a valid, script-free project — do not turn its absence into an error that blocks play. -- Route reusable runtime APIs through `EppoEngine` and `EppoScriptCore`, not the editor. diff --git a/.claude/skills/eppo-scripting-integration/references/architecture.md b/.claude/skills/eppo-scripting-integration/references/architecture.md deleted file mode 100644 index 0de01d7e..00000000 --- a/.claude/skills/eppo-scripting-integration/references/architecture.md +++ /dev/null @@ -1,116 +0,0 @@ -# Scripting architecture - -## File map - -| Area | Primary files | Responsibility | -| --- | --- | --- | -| Runtime host | `EppoEngine/Source/Scripting/RuntimeHost.*`, `Platform.h`, vendored `hostfxr.h` and `coreclr_delegates.h` | Locate hostfxr, load it, initialize from `runtimeconfig.json`, and resolve unmanaged entry points. | -| Managed assembly facade | `Assembly.*`, `ManagedFunctions.h` | Resolve the exported managed function table, register native internal calls, load/unload the user assembly, and cache reflected metadata. | -| Engine lifecycle | `ScriptEngine.*`, `ScriptInstance.*`, `ScriptClass.*`, `ScriptField.h` | Own the core assembly facade, live entity instances, editor field storage, and active scene/physics contexts. | -| Native callbacks | `ScriptGlue.*` | Implement callbacks invoked by managed public APIs and publish the name/function table consumed by `Assembly::RegisterInternalCalls`. | -| Managed bridge | `EppoScriptCore/Source/Core/ScriptGlue.cs`, `InternalCalls.cs` | Export unmanaged entry points, discover user types, own managed instances, marshal calls, and store registered native pointers. | -| Public C# API | `EppoScriptCore/Source/Scene`, `Physics`, `Core`, `Math` | Present user-facing entities, components, input, logging, physics, key codes, and blittable vector types. | -| Build/deploy | `EppoScriptCore/premake5.lua`, `EppoEngineTesting/TestData/Scripts/premake5.lua`, target `premake5.lua` files, `EppoEditor/runtimeconfig.json` | Build `EppoScriptCore.dll`, build the test user assembly, and copy managed outputs beside native executables. | -| Tests | `EppoEngineTesting/Source/Scripting`, `TestData/Scripts` | Exercise reflection, lifecycle, fields, method invocation, internal calls, exceptions, and layout. | - -## Boot and assembly flow - -1. `EditorLayer::OpenProject` builds the project's C# assembly before opening its start scene. -2. `ScriptEngine::Init(runtimeConfigPath)` constructs the singleton and its `Assembly`. -3. `Assembly` constructs `RuntimeHost`, resolves `EppoScriptCore.ScriptGlue` exports, bootstraps managed state, and registers every native internal call by name. -4. `LoadUserAssembly` enters a collectible managed load context, discovers non-core subclasses of `Eppo.Scene.Entity`, and rebuilds native `ScriptClass` metadata. -5. Scene deserialization can then restore editor-time field storage against available field metadata. It does not reject unknown class names; the property panel and runtime instance creation report class validity later. - -CoreCLR is process-global in practice. `ScriptEngine::Shutdown` ends engine ownership, but tests must share one initialization rather than repeatedly booting CoreCLR. - -## Build and hot reload - -`ReloadProjectAssembly` is the single path that turns C# sources into a loaded assembly, used both for the initial project open and for reloads. It: - -1. Clears `m_UserAssemblyValid` up front, so a failure anywhere below leaves scripting explicitly invalid rather than stale-but-apparently-fine. -2. Treats a project with no `.csproj` as a **valid** state — logs, marks valid, returns true. Absence of scripts must not block play. -3. Installs the `FileWatcher` on `Project::GetScriptsDirectory()` *before* building, so a project that opens with broken sources still reloads once the user fixes them. -4. Runs `dotnet build` through `Utility/Process::RunProcess` into `Project::GetCacheDirectory() / "Scripts"`, passing `-p:CoreManagedDll=` pointed at this build's `EppoScriptCore.dll` rather than a baked-in path. -5. Verifies the assembly exists, then `UnloadUserAssembly` (which also clears `m_EntityInstances`) followed by `LoadUserAssembly`. - -`ScriptEngine::VerifyRuntime`, called per frame, is the reload trigger and deliberately does nothing eagerly: - -- A change reported by `FileWatcher::ConsumeChange` only sets `m_ReloadPending` and returns, so a burst of editor saves collapses into a single build one frame later. -- A pending reload is skipped entirely while `GetSceneContext()` is non-null — that means play mode, and swapping assemblies under live managed instances is not supported. - -Editor field storage (`m_FieldStorage`) survives a reload because it is keyed by entity UUID and lives outside the assembly; live `ScriptInstance`s do not. Reflected `ScriptClass` metadata is rebuilt from scratch, so any cached class index is invalid after a reload. - -## Runtime entity flow - -`EditorLayer::OnScenePlay` copies the authored scene. `Scene::OnRuntimeStart` creates physics, then calls `ScriptEngine::OnCreateEntity` for each `ScriptComponent`. Creation resolves the class index, creates a managed instance keyed by entity UUID, copies serialized editor fields into it, and invokes `OnCreate`. - -`Scene` owns publishing both scripting contexts. `Scene::OnRuntimeStart` sets the active physics world and the scene context (via `shared_from_this`) *before* the `OnCreateEntity` loop, so entity, component and physics internal calls all resolve from managed `OnCreate`. Hosts must not publish the scene context themselves; `EditorLayer` deliberately does not. - -Each runtime update steps physics first and then invokes `OnUpdate`. On stop, `Scene::OnRuntimeStop` invokes `OnDestroy` and destroys live instances *first* — while both the scene context and the physics world are still published — then clears the scene context and releases physics. Managed `OnDestroy` can therefore still resolve its entity, other entities, components and the running simulation. Treat any reordering of these four steps as a lifecycle change requiring explicit tests; the `Scene_OnRuntimeStart_*` / `Scene_OnRuntimeStop_*` tests in the Scripting suite cover it. - -The ownership split is deliberate: - -- `ScriptEngine` owns one native `ScriptInstance` per running entity. -- Managed `ScriptGlue` owns the actual C# object in an entity-ID keyed registry. -- The native handle stores the assembly pointer, raw 64-bit entity ID, and class index. -- The scene owns `ScriptComponent`; editor field values live in `ScriptEngine::m_FieldStorage`, keyed by stable UUID. - -## ABI contracts - -Keep these synchronized: - -- Export names in `[UnmanagedCallersOnly(EntryPoint = ...)]`, the function-pointer lookup strings in `Assembly`, typedefs in `ManagedFunctions.h`, and call sites. -- `ScriptFieldType` member order and underlying byte width in C++ and C#. -- Blittable layouts for `Vector2`, `Vector3`, `Vector4`, primitive field types, entity IDs, and method argument/return buffers. Managed vectors use sequential layout; preserve the native assumptions such as a 12-byte `glm::vec3`/managed `Vector3` contract. -- C# internal-call delegate signatures, registration names, and C++ callback signatures. -- Boolean and character widths; do not assume C++ `bool` or `char` matches an arbitrary managed declaration without an explicit existing contract. -- Native strings returned by managed exports: managed allocation must be released through the exported `FreeString` path. - -`ScriptFieldValue` stores up to 16 bytes with 8-byte alignment. `ScriptFieldTypeSize` is the shared native width authority. `ScriptMarshalling` must cover any new field type. - -## Adding a managed component API - -1. Add or confirm the native scene component. -2. Add the managed wrapper property or method in `Components.cs`. -3. Add the internal-call delegate and invocation in `InternalCalls.cs`. -4. Add the C++ callback in `ScriptGlue.h/.cpp` and publish it under the exact managed name from `ScriptGlue::GetInternalCalls`. -5. Leave `Assembly::RegisterInternalCalls` as the generic table consumer unless the registration mechanism itself changes. -6. Validate scene context, entity lookup, and component presence in the callback. Follow existing safe defaults for reads and no-op writes. -7. Add C# harness behavior only when the call must originate from user code; otherwise direct method invocation through reflected harness methods may suffice. -8. Exercise the public C# wrapper in the managed harness rather than testing only a raw `InternalCalls` method. Test getters and setters independently, plus missing-context behavior when meaningful. - -`TransformComponent::Rotation` is authored as XYZ Euler radians and passed to `glm::quat`; do not silently expose degrees or a quaternion in C#. Direct transform setters mutate ECS state only. They do not teleport an active physics body, whose simulated pose can overwrite the component on a later runtime update. - -## Adding an exported managed operation - -1. Define the managed `[UnmanagedCallersOnly]` method and keep exceptions behind the managed guard. -2. Add the matching typedef and field to `ManagedFunctions`. -3. Resolve it in `Assembly::ResolveManagedFunctions`; scripting is unavailable when required functions cannot bind. -4. Add the guarded `Assembly` facade operation and then expose it through `ScriptEngine`, `ScriptClass`, or `ScriptInstance` as appropriate. -5. Add an ABI/lifecycle regression. - -## Field persistence - -- Reflected `ScriptField` metadata describes a class field; it does not store an entity value. -- `ScriptEngine`'s `ScriptFieldMap` is the serialized editor value side table. -- Play-scene copy preserves UUIDs, so it intentionally resolves the same stored editor field map without cloning it. -- Entity duplication must copy the source field map to the new UUID. -- Entity destruction must remove its field storage. -- Runtime edits affect the live instance; replay starts again from stored editor values. -- Scene serialization should only write values compatible with the currently reflected field type. - -## Build and test details - -Use the required order: - -```text -Scripts\Setup.bat --action vs2026 # generate (sh Scripts/setup.sh on Linux) -# build EppoEngineTesting: in the generated VS solution, or `ninja EppoEngineTesting_Debug_x64` -ctest --test-dir build/bin/Debug-windows-x86_64 -R "Scripting|ScriptMarshalling" --output-on-failure -``` - -Each target's `premake5.lua` post-build commands place `EppoScriptCore.dll`, PDB/deps files, and `runtimeconfig.json` beside the executable, and the harness project (`EppoEngineTesting/TestData/Scripts/premake5.lua`) builds and deploys `EppoTesting.Scripts.dll`. Those managed files are resolved through `FS::GetExecutableDirectory()`, independent of cwd. Run tests through CTest so their editor resources resolve from the configured `EppoEditor/` working directory. - -Use `Scripting` for discovery, invocation, lifecycle, field values, internal calls, managed exceptions, and C# API behavior. Use `ScriptMarshalling` for enum widths and buffer layout. Also run `Scene` for serialization/copy changes and `Physics` for managed physics changes. - -Field-map tests that need reflected field metadata require the suite's shared initialized CoreCLR harness; place them in `Scripting`, not a standalone headless Scene test that initializes and tears down the runtime independently. diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index c430d861..00000000 --- a/CLAUDE.md +++ /dev/null @@ -1,151 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project - -EppoEngine — a C++20 cross-platform (Windows/Linux) game engine + editor with C# scripting via CoreCLR (.NET 10) and Vulkan rendering through NVRHI. Built with Premake 5.0.0-beta8 + vcpkg (manifest mode). AGENTS.md holds the same core guidance for other agents; keep the two in sync when editing either. - -## Prerequisites (validated by `Scripts/Setup.py`) - -- **Vulkan SDK** with `dxc` (`VULKAN_SDK` set). -- **vcpkg** is discovered through `VCPKG_ROOT`, then `PATH`, or provisioned locally with permission. Manifest mode; overlay ports are in `Dependencies/Ports` (imguifiledialog, nvrhi, tinygltf). -- **.NET SDK 10** with `DOTNET_ROOT` set (managed core targets `net10.0`). -- **compiler**: Windows defaults to MSVC and optionally supports Clang; Linux uses `clang`/`clang++`. -- **Ninja 1.6+** on Linux. -- **CTest 3.21+** for running the generated standalone test manifests. -- Linux also needs `uuid-dev` to build Premake and X11/GL dev libs: `libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev pkg-config`. Ubuntu's `cmake` package supplies the standalone CTest executable; Eppo does not use CMake to generate or build. - -## Commands - -Run `Scripts\Setup.bat` on Windows and choose VS2022 or VS2026. Run `sh Scripts/setup.sh` on Linux for Ninja + Clang. Windows generates one `EppoEngine` solution; its real C# projects are grouped under EppoScriptCore and map solution Dist to managed Release. Generated solutions/build files are used directly; there is no build wrapper. Binary dirs are `build/bin/--x86_64/`. -Run `Scripts\GenerateBuildFiles.bat` on Windows or `sh Scripts/generatebuildfiles.sh` on Linux to only rerun Premake (`Setup.py --generate-only`) after the first setup: it reuses the action, compiler, Premake and vcpkg root recorded in `.eppo/build.json`, skips tool provisioning and `vcpkg install`, and never prompts. `--action`/`--compiler` still override. -Run `Scripts\Clean.bat` on Windows or `sh Scripts/clean.sh` on Linux to remove all setup and build outputs, including locally provisioned tools under `.eppo`. - -```bash -Scripts\Setup.bat --action vs2026 # generate Visual Studio 2026 on Windows -Scripts\GenerateBuildFiles.bat # regenerate only, reusing the recorded setup -# Build EppoEngineTesting in the generated solution -ctest --test-dir build/bin/Debug-windows-x86_64 --output-on-failure -ctest --test-dir build/bin/Debug-windows-x86_64 --label-exclude graphical -ctest --test-dir build/bin/Debug-windows-x86_64 -R Scripting -``` - -Run a suite directly from `EppoEditor/` so source resources resolve correctly: `../build/bin/Debug-windows-x86_64/EppoEngineTesting/EppoEngineTesting --gtest_filter=Scripting.*` (Google Test; suite = the first `TEST(Suite, Name)` argument). CTest passes exactly this filter per suite. -Suites and labels (registered in `Scripts/Premake/Testing.lua`): `Core`, `Physics`, `Scene` (`core`); `Project` (`unit`); `Scripting`, `ScriptMarshalling` (`scripting`); `App`, `ProjectExport`, `Renderer` (`graphical`). -Visual Studio's built-in Test Adapter for Google Test discovers the suite in Test Explorer with no per-developer setup. The runner's `main.cpp` `chdir`s to `EppoEditor` on startup (via the premake-baked `EP_TEST_WORKING_DIR`), so `Resources/`/`Projects/`/`TestData/` resolve for graphical and data-driven suites regardless of how the exe is launched (Test Explorer runs it from the output dir; CTest also sets `WORKING_DIRECTORY`). - -Required order: **generate → build → test**. After editing C# only, rebuild the `EppoEngineTesting` (or `EppoEditor`) target so the dotnet custom commands re-run and DLLs are re-copied. - -## Architecture - -### Targets - -- `EppoEngine/` — static library, the engine. `Source/` modules: `Asset`, `Core`, `Event`, `ImGui`, `Physics`, `Platform`, `Project`, `Renderer`, `Scene`, `Scripting`, `Utility`. Public umbrella header `Source/EppoEngine.h`; PCH `Source/pch.h`. `Core/Buffer/` is the binary serialization substrate: abstract `StreamWriter`/`StreamReader` with `Buffer*` (in-memory) and `FileStream*` (on-disk) implementations, plus paired `StreamSerializable`/`StreamDeserializable` concepts backing `WriteObject`/`ReadObject`. `GameData` is built on it. -- `EppoEditor/` — editor executable (`EppoEditor.cpp` → `EditorLayer`). Depends on `EppoEngine` + `EppoScriptCore`. Owns `Resources/` and `runtimeconfig.json`. -- `EppoScriptCore/` — C# class library (net10.0). Visual Studio exposes the real `.csproj` in the EppoScriptCore solution group and maps solution Dist to managed Release. Ninja invokes `dotnet` through the project Premake definition. Namespaces mirror the folder path minus `Source/`. -- `EppoEngineTesting/` — Google Test runner (custom `main.cpp` wraps `RUN_ALL_TESTS` with logging + `AppHarness::Shutdown`). `Source/` suites mirror engine modules; `Source/Support/` has `AppHarness` (boots a real `Application` for graphical suites), `TestContext` + `ScenarioLayer` (multi-frame scene/camera scenarios, used by the `Renderer` suite), and the `EppoTest.h` / `GlmCheck.h` / `TempDir.h` helpers. `EppoTest.h` provides `EP_REQUIRE`/`EP_REQUIRE_EQ` (a fatal check usable in value-returning helpers where `ASSERT_*` cannot) and `EP_EXPECT_ARRAY_EQ`; `GlmCheck.h` keeps `CHECK_VEC*/MAT4_CLOSE` on `EXPECT_NEAR`. `TestData/Scripts/` builds the `EppoTesting.Scripts.dll` harness the Scripting suite loads. Suites are registered in `Scripts/Premake/Testing.lua`. -- `EppoRuntime/` — standalone player. Reads `Game.eppak` before creating the application, since its engine shaders come from there. It stages **no** `Resources/`: shader sources and their includes travel in the pack, and it never reads them from disk. Logs and its shader cache are written beside the executable. -- `Scripts/Premake/` — shared dependency names and standalone CTest manifest generation. Each native target owns a `premake5.lua`; vcpkg overlays live under `Dependencies/Ports`. - -Key libraries: entt (ECS), NVRHI (Vulkan RHI), GLFW + ImGui (docking), glm, box3d (physics), spdlog, tinygltf, Tracy, Google Test. - -### Packaging and the deployed runtime (spans Project, Asset, Renderer, EppoRuntime) - -- `ProjectExporter::Export` stages the runtime executable and writes `Game.eppak`. `GameData` owns the byte layout — it is documented in full at the top of `Project/GameData.h`, and every section is read/written through the `Core/Buffer/` streams. -- `Asset/PackFormat.h` holds the four-character magics and versions (`EPAK` package, `ESHD` shaders, `EMSH` mesh, `ESCN` scene). Bump the version whenever a layout changes. -- Shaders are packed as **text**, keyed by name, alongside their `#include` sources keyed by path relative to `Resources/Shaders`. A packed `ShaderSpecification` carries both and resolves includes through a handler that never touches the filesystem, so a deployed game cannot silently fall back to a disk compile. -- `AssetManager` has a packed mode (constructed with owned `PackedAssetData` payloads) that loads lazily from memory instead of disk. There is no `PackedAssetManager` class — the test file of that name exercises `AssetManager`. -- `ApplicationParams::PackedShaders` / `PackedShaderIncludes` carry the shader text from the pack into `Renderer::LoadShaders`, which the `Application` constructor calls after `InitRenderer()` and **before** ImGui attaches (`ImGuiRenderer` grabs `GetShader("imgui")` during `ImGuiLayer::OnAttach`). Empty means "compile from `Resources/Shaders`", which is what the editor and tests do. - -### C#↔C++ scripting bridge (spans both languages — read as one system) - -- `Scripting/RuntimeHost` boots CoreCLR via hostfxr using the `runtimeconfig.json` next to the exe; **CoreCLR initializes once per process** and cannot be re-initialized. -- `ScriptEngine` (singleton, `Init`/`Shutdown`) loads `EppoScriptCore.dll` (core assembly) plus a user assembly, holds per-entity `ScriptInstance`s keyed by entity UUID, and owns the editor-time field side table (`ScriptFieldMap`) — the authoritative, serialized copy of script fields, pushed into the managed instance on create. -- `ScriptGlue.cpp` registers the native functions; on the C# side `EppoScriptCore/Source/Core/InternalCalls.cs` is the **sole unsafe hub** — all `[UnmanagedCallersOnly]`/extern glue lives there, wrapped by friendly APIs (`Entity`, `Components`, `Input`, `Log`, `Physics`). -- Internal-call conventions: structs passed by pointer, entity UUID is the first argument, the live scene is resolved through `ScriptEngine`'s scene context (set on play, cleared on stop/unload). The active `PhysicsWorld` is held weakly so callbacks no-op after scene stop. -- The scene drives per-entity script lifecycle (`OnCreateEntity`/`OnUpdateEntity`/`OnDestroyEntity`). -- **Hot reload:** `ScriptEngine` owns a `FileWatcher` over the project's `Scripts` directory, polled from `VerifyRuntime`. A detected change only sets `m_ReloadPending` and returns — the rebuild happens on a later frame, so a burst of saves collapses into one build, and it is skipped entirely while a scene context is set (i.e. during play). `ReloadProjectAssembly` shells out to `dotnet build` via `Utility/Process`, then unloads and reloads the collectible user assembly. Editor field storage survives; live managed instances do not. - -## Gotchas - -- **Run the editor from `EppoEditor/`; run tests through CTest.** The editor and graphical tests resolve `Resources/` and `Projects/` from the working directory, while `runtimeconfig.json`, `EppoScriptCore.dll`, and test assemblies resolve beside their executable. CTest sets the source working directory automatically. -- **Managed projects follow the generated build system.** Visual Studio builds the real `.csproj` projects; Ninja invokes `dotnet` custom rules. Post-build steps copy managed outputs beside the native executable. -- **Graphical suites (`App`, `ProjectExport`, `Renderer`) need a real display + GPU.** They early-return if `AppHarness` can't boot; on headless/CI use `--label-exclude graphical`. -- **"SPIR-V CodeGen not available"** at runtime means the Microsoft `dxcompiler.dll` is shadowing the Vulkan SDK one; copy the Vulkan SDK's `dxcompiler.dll` next to the exe. -- **`EppoRuntime` owns its entry point.** It defines `EP_CUSTOM_ENTRY_POINT` (suppressing the `main` in `Core/EntryPoint.h`) and calls `Eppo::RunApplication` from its own `WinMain`/`main`, so it can wrap startup in a try/catch that reports through `ErrorDialog`. It reads `Game.eppak` inside `CreateApplication` — before the `Application` exists — because the shaders it hands to `ApplicationParams` are needed during construction. -- **Where files get written is configured, not assumed.** `FS::ConfigureWritableDirectory` sets the root that `FS::GetWritableDirectory` and `FS::GetShaderCacheDirectory` resolve against; the runtime points it at its own executable directory so logs and the shader cache land beside the game. Unconfigured, the shader cache falls back to `Resources/Shaders/Cache`. -- **Platform/config macros:** `EP_PLATFORM_WINDOWS`/`EP_PLATFORM_LINUX`; `EP_DEBUG`/`EP_RELEASE`/`EP_DIST`; `TRACY_ENABLE` in Debug and RelWithDebInfo. Linux defines `__EMULATE_UUID`. -- **`UUID::operator bool` is explicit.** Use `static_cast(uuid)` to get the raw id; implicit numeric conversion is a compile error by design. -- **`RelationshipComponent` is optional.** An entity with no parent or children has no relationship component. Readers guard its absence with `HasComponent`; parenting adds it lazily and unparenting removes it when empty. -- **Scene graph has two walk directions that can disagree.** The hierarchy panel and `Scene::GatherColliders` walk **down** via `Children`; `GetWorldTransform` and the collider wireframe pass walk **up** via `Parent` / iterate the whole registry (`ForEachEntity`). A one-directional link (child names a parent that doesn't list it back, e.g. a scene stored with only the child's `Parent`) is invisible to the down-walkers but still rendered — an entity you can't select/delete whose collider keeps drawing, and whose collider never joins the compound body. `SceneSerializer::Deserialize` must reconcile both directions on load. -- **Launch the editor and capture its startup log to observe runtime state.** This is *not* headless — it spins up the full GUI app (real window, Vulkan swapchain, ImGui, file dialogs); there is no headless editor run (it needs a display + GPU, same as the graphical test harness). From `EppoEditor/`, run `../build/bin/Debug-windows-x86_64/EppoEditor/EppoEditor.exe > out.txt 2>&1 &`, wait a few seconds, `taskkill //IM EppoEditor.exe //F`. It loads the project default scene and logs to `latest.log` + stdout; useful for confirming startup or a fix in the real app rather than trusting tests alone. Describe such runs as "launched the editor and checked its log," never as "headless." -- **Drive the real editor for visual feature verification.** On Windows, launch the built `EppoEditor.exe` with `EppoEditor/` as its working directory, focus its window, and use OS input automation (`user32` cursor/mouse calls plus `SendKeys`) to exercise ImGui. Select the hierarchy entity before coordinate-based property edits; double-click numeric drag fields to enter text. Keep verification edits unsaved, capture the editor window with `GetWindowRect` + `Graphics.CopyFromScreen`, and select a different entity when you need to distinguish persistent scene visualization from ImGuizmo. Stop only the editor process you launched and keep captures outside the repository. - -## Style - -Conventions below are near-universal in `Core`, `Platform/Vulkan` and `Renderer` — treat a deviation as a mistake, not a choice. - -**Formatting** - -- `.clang-format`: 4-space indent, 140-col limit, Allman braces, pointer left (`int* p`), `SortIncludes: Never`, namespaces indented. -- **Indentation is 4 spaces.** Remaining tabs in the older engine files are legacy, not a convention, and are pending a one-time repo-wide conversion. Write new and edited code with spaces; don't copy a tab-indented neighbour's whitespace. -- `.clang-tidy`: `bugprone-*`, `clang-diagnostic-*`, `clang-analyzer-*`, `cppcoreguidelines-*`, `modernize-*`, `misc-use-anonymous-namespace`. - -**Declarations** - -- **Trailing return types, everywhere**: `auto Name(args) -> T`, including `-> void`. This covers members, free functions, lambdas (`[this](Event& e) -> void`), `main`, `WinMain`, and friend declarations. There is not one classic `bool Foo()` declaration in the engine. -- `[[nodiscard]]` on const getters and anything returning a computed value; not on mutating `-> void`. Trivial getters are `constexpr` and defined inline in the header. -- `const` on by-value params in definitions (`auto WriteData(const char* data, const size_t size)`); `const auto` for locals by default. -- Concepts over SFINAE — `StreamSerializable`, `ResourceType`, `requires(std::derived_from)`. - -**Naming and layout** - -- `m_` members, `s_` statics and file-scope constants, `g_` globals. PascalCase for methods and public struct fields; camelCase for params and locals. Getters are `Get*` or `Is*`. -- Headers use `#pragma once`, never include guards. Class body order is `public:` → `private:` methods → a **second** `private:` for data members. -- Configurable types take one `XSpecification`/`XParams` struct with PascalCase fields and in-class defaults, constructed at the call site with designated initializers (`WindowSpecification{ .Title = ..., .Width = ... }`). -- File-local helpers go in an anonymous namespace nested inside `namespace Eppo` — never `static` free functions. - -**Includes** - -- `.cpp` files open with `#include "pch.h"`, then the file's own header, blank line, then project headers (quoted, module-relative from `Source/`), then third-party `<...>`, then std `<...>`. `SortIncludes: Never`, so this order is hand-maintained. -- `pch.h` already supplies the common std headers plus `Core/Base.h`, `Core/Buffer/Buffer.h`, `Core/Hash.h`, `Core/UUID.h`, `Utility/Filesystem.h`, `Utility/Random.h`. Engine headers rely on it (`Renderer/Image.h` names `Buffer` and `std::filesystem::path` with no include of its own) — don't add redundant includes for these. -- Forward-declare only to break include cycles; otherwise `#include`. - -**Engine vocabulary** (`Core/Base.h`) - -- `Ref`/`CreateRef` (shared), `ScopedPtr`/`CreateScopedPtr` (unique), `WeakRef`. Use `static auto Create(...)` factories where the constructor is private or construction can fail (`Sampler`, `Shader`, `DeviceManager`). -- **`EP_ASSERT` is a `constexpr` function, not a macro**: `EP_ASSERT(cond, "message")`. Do not copy the older `EP_ASSERT(false && "msg")` form still present in a few files — the `&&` collapses to a plain `false` and the message is silently discarded. -- `EP_PROFILE_FN("Scope::Name")` as the first statement of a hot function, no trailing semicolon. - -**Prose and error handling** - -- Comments: zero is the default. Add one only for a non-obvious "why", max 1 line. Never restate what the code does. `///` doc comments are rare — reserved for public serialization/lifecycle APIs. -- On error paths, log via `Log::` rather than silently returning. Guard clauses with early return; no braces around single-statement bodies. -- Don't add synonym APIs — if equivalent functionality exists, point the caller at it. - -## Domain skills - -Seven domain skills live in `.claude/skills/` (each `SKILL.md` + `references/architecture.md`). Read the matching skill before investigating or changing a major subsystem; use every applicable skill for cross-system work. They are full copies of the Codex skills in `.agents/skills/` — when editing a skill, apply the same change to both trees. - -- `eppo-scripting-integration` — CoreCLR hosting, native/managed ABI, assemblies, ScriptGlue, fields, lifecycle, deployment, and scripting tests. -- `eppo-rendering-pipeline` — Vulkan/NVRHI devices, shaders, descriptors, GPU resources, render passes, SceneRenderer, and graphical tests. -- `eppo-editor-development` — EditorLayer state, edit/play transitions, panels, viewport input, gizmos, projects, scenes, and content browsing. -- `eppo-scene-ecs-lifecycle` — EnTT entities, UUIDs, relationships, transforms, copy/duplication, serialization, runtime systems, and scene tests. -- `eppo-physics-integration` — Box3D bodies, hierarchy-aware colliders, transform conversion, runtime synchronization, scripting, and physics tests. -- `eppo-assets-and-projects` — asset handles, registry persistence, paths, loading/import/export, project lifecycle, `Game.eppak` packaging, and content-browser coordination. -- `eppo-application-framework` — application/frame lifecycle, layers, windows, events, input, ImGui, startup order, the deployed runtime, and application harnesses. - -## Workflow rules (required) - -- **Discover worktrees first.** Before inspecting, editing, building, or testing, run `git worktree list` from the repository and identify the worktree that contains the task. Never assume the primary checkout is the target; use the selected worktree consistently for every command. -- **Plan before code.** For anything beyond a trivial change, write a plan first and confirm key decisions (including naming/layout choices) with the user before implementing. -- **Test-driven development.** Write the test first as `TEST(Suite, Name)` (Google Test) in the matching `EppoEngineTesting/Source//` file; a suite is just the shared first argument, so a new suite must also be registered in `Scripts/Premake/Testing.lua`. Use `EXPECT_*`/`ASSERT_*`, `EP_REQUIRE` for fatal checks inside value-returning helpers, and the `CHECK_VEC*_CLOSE` glm helpers. Name suites/tests after the class/behaviour under test, not the goal ("Smoke"/"Sanity" are banned). Critical bug fixes get a regression test. -- **Systematic debugging.** Root cause before fix; no patching symptoms. -- **Code review via subagent** after substantial changes — do not review your own work. -- **No formatting changes to existing code.** Don't reindent or reflow lines you aren't otherwise editing, and never run clang-format across a file you didn't create. New and edited lines use 4 spaces (see Style); the tab-to-space conversion of legacy files is a deliberate, separately-run pass, not something to do as a drive-by. -- **Verify before claiming done.** Run the relevant build + `ctest` and confirm it passes. A green build alone does not verify editor/GUI behaviour — state what was actually verified. - -## CI - -GitLab CI (`.gitlab-ci.yml`): runs on MRs, `master`, `develop`, `feature/*`, `test/*`. On Linux it generates Ninja with Premake beta8, builds only `EppoEngineTesting_Debug_x64`, runs `ctest --label-exclude graphical`, and publishes JUnit. The toolchain is baked into `.gitlab/ci/Dockerfile`; the vcpkg binary cache is keyed on `vcpkg.json` + `vcpkg-configuration.json`. Use `glab` CLI for MR operations. diff --git a/Dependencies/Ports/tracy/build-tools.patch b/Dependencies/Ports/tracy/build-tools.patch index c9adb638..1cf84360 100644 --- a/Dependencies/Ports/tracy/build-tools.patch +++ b/Dependencies/Ports/tracy/build-tools.patch @@ -1 +1,93 @@ -diff --git a/CMakeLists.txt b/CMakeLists.txt index 3bb2572..f9ccc8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -291,3 +291,16 @@ endif() if(PROJECT_IS_TOP_LEVEL) set(CMAKE_COLOR_DIAGNOSTICS ON) endif() + +option(VCPKG_CLI_TOOLS "library" OFF) +option(VCPKG_GUI_TOOLS "library" OFF) +if(VCPKG_CLI_TOOLS) + add_subdirectory(csvexport) + add_subdirectory(capture) + add_subdirectory(import) + add_subdirectory(update) + add_subdirectory(merge) +endif() +if(VCPKG_GUI_TOOLS) + add_subdirectory(profiler) +endif() diff --git a/cmake/server.cmake b/cmake/server.cmake index 8298d21..ca5e9f3 100644 --- a/cmake/server.cmake +++ b/cmake/server.cmake @@ -1,3 +1,4 @@ +include_guard(GLOBAL) set(TRACY_COMMON_DIR ${CMAKE_CURRENT_LIST_DIR}/../public/common) set(TRACY_COMMON_SOURCES diff --git a/cmake/vendor.cmake b/cmake/vendor.cmake index 90d1680..1add52c 100644 --- a/cmake/vendor.cmake +++ b/cmake/vendor.cmake @@ -1,3 +1,4 @@ +include_guard(GLOBAL) # Vendor Specific CMake # The Tracy project keeps most vendor source locally diff --git a/cmake/GitRef.cmake b/cmake/GitRef.cmake --- a/cmake/GitRef.cmake +++ b/cmake/GitRef.cmake @@ -1,37 +1,24 @@ function(add_git_ref target) - if(NOT DEFINED GIT_REV) - set(GIT_REV "HEAD") - endif() - get_property(_git_ref_created GLOBAL PROPERTY _GIT_REF_CREATED) if(NOT _git_ref_created) set_property(GLOBAL PROPERTY _GIT_REF_CREATED TRUE) + set(_git_ref_file "${CMAKE_BINARY_DIR}/GitRef.hpp") find_package(Git) - set_property(GLOBAL PROPERTY _GIT_FOUND "${Git_FOUND}") if(Git_FOUND) - add_custom_target(git-ref - COMMAND ${CMAKE_COMMAND} -E echo "#pragma once" > GitRef.hpp.tmp - COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} log -1 "--format=namespace tracy { static inline const char* GitRef = %x22%h%x22; }" ${GIT_REV} >> GitRef.hpp.tmp || echo "namespace tracy { static inline const char* GitRef = \"unknown\"; }" >> GitRef.hpp.tmp - COMMAND ${CMAKE_COMMAND} -E copy_if_different GitRef.hpp.tmp GitRef.hpp - BYPRODUCTS GitRef.hpp GitRef.hpp.tmp - VERBATIM + execute_process( + COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} log -1 "--format=%h" + OUTPUT_VARIABLE _git_rev + OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE _git_result ) + if(NOT _git_result EQUAL 0) + set(_git_rev "unknown") + endif() else() - message(WARNING "git not found, using 'unknown' as git ref.") - add_custom_command( - OUTPUT GitRef.hpp - COMMAND ${CMAKE_COMMAND} -E echo "#pragma once" > GitRef.hpp - COMMAND ${CMAKE_COMMAND} -E echo "namespace tracy { static inline const char* GitRef = \"unknown\"; }" >> GitRef.hpp - VERBATIM - ) + set(_git_rev "unknown") endif() + file(WRITE "${_git_ref_file}" "#pragma once\nnamespace tracy { static inline const char* GitRef = \"${_git_rev}\"; }\n") endif() - target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - get_property(_git_found GLOBAL PROPERTY _GIT_FOUND) - if(_git_found) - add_dependencies(${target} git-ref) - else() - target_sources(${target} PUBLIC GitRef.hpp) - endif() + target_include_directories(${target} PRIVATE ${CMAKE_BINARY_DIR}) endfunction() \ No newline at end of file +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 3bb2572..f9ccc8b 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -291,3 +291,16 @@ endif() + if(PROJECT_IS_TOP_LEVEL) + set(CMAKE_COLOR_DIAGNOSTICS ON) + endif() ++ ++option(VCPKG_CLI_TOOLS "library" OFF) ++option(VCPKG_GUI_TOOLS "library" OFF) ++if(VCPKG_CLI_TOOLS) ++ add_subdirectory(csvexport) ++ add_subdirectory(capture) ++ add_subdirectory(import) ++ add_subdirectory(update) ++ add_subdirectory(merge) ++endif() ++if(VCPKG_GUI_TOOLS) ++ add_subdirectory(profiler) ++endif() +diff --git a/cmake/server.cmake b/cmake/server.cmake +index 8298d21..ca5e9f3 100644 +--- a/cmake/server.cmake ++++ b/cmake/server.cmake +@@ -1,3 +1,4 @@ ++include_guard(GLOBAL) + set(TRACY_COMMON_DIR ${CMAKE_CURRENT_LIST_DIR}/../public/common) + + set(TRACY_COMMON_SOURCES +diff --git a/cmake/vendor.cmake b/cmake/vendor.cmake +index 90d1680..1add52c 100644 +--- a/cmake/vendor.cmake ++++ b/cmake/vendor.cmake +@@ -1,3 +1,4 @@ ++include_guard(GLOBAL) + # Vendor Specific CMake + # The Tracy project keeps most vendor source locally + + +diff --git a/cmake/GitRef.cmake b/cmake/GitRef.cmake +--- a/cmake/GitRef.cmake ++++ b/cmake/GitRef.cmake +@@ -1,37 +1,24 @@ + function(add_git_ref target) +- if(NOT DEFINED GIT_REV) +- set(GIT_REV "HEAD") +- endif() +- + get_property(_git_ref_created GLOBAL PROPERTY _GIT_REF_CREATED) + if(NOT _git_ref_created) + set_property(GLOBAL PROPERTY _GIT_REF_CREATED TRUE) ++ set(_git_ref_file "${CMAKE_BINARY_DIR}/GitRef.hpp") + find_package(Git) +- set_property(GLOBAL PROPERTY _GIT_FOUND "${Git_FOUND}") + if(Git_FOUND) +- add_custom_target(git-ref +- COMMAND ${CMAKE_COMMAND} -E echo "#pragma once" > GitRef.hpp.tmp +- COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} log -1 "--format=namespace tracy { static inline const char* GitRef = %x22%h%x22; }" ${GIT_REV} >> GitRef.hpp.tmp || echo "namespace tracy { static inline const char* GitRef = \"unknown\"; }" >> GitRef.hpp.tmp +- COMMAND ${CMAKE_COMMAND} -E copy_if_different GitRef.hpp.tmp GitRef.hpp +- BYPRODUCTS GitRef.hpp GitRef.hpp.tmp +- VERBATIM ++ execute_process( ++ COMMAND ${GIT_EXECUTABLE} -C ${CMAKE_CURRENT_SOURCE_DIR} log -1 "--format=%h" ++ OUTPUT_VARIABLE _git_rev ++ OUTPUT_STRIP_TRAILING_WHITESPACE ++ RESULT_VARIABLE _git_result + ) ++ if(NOT _git_result EQUAL 0) ++ set(_git_rev "unknown") ++ endif() + else() +- message(WARNING "git not found, using 'unknown' as git ref.") +- add_custom_command( +- OUTPUT GitRef.hpp +- COMMAND ${CMAKE_COMMAND} -E echo "#pragma once" > GitRef.hpp +- COMMAND ${CMAKE_COMMAND} -E echo "namespace tracy { static inline const char* GitRef = \"unknown\"; }" >> GitRef.hpp +- VERBATIM +- ) ++ set(_git_rev "unknown") + endif() ++ file(WRITE "${_git_ref_file}" "#pragma once\nnamespace tracy { static inline const char* GitRef = \"${_git_rev}\"; }\n") + endif() + +- target_include_directories(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +- get_property(_git_found GLOBAL PROPERTY _GIT_FOUND) +- if(_git_found) +- add_dependencies(${target} git-ref) +- else() +- target_sources(${target} PUBLIC GitRef.hpp) +- endif() ++ target_include_directories(${target} PRIVATE ${CMAKE_BINARY_DIR}) + endfunction() diff --git a/Dependencies/Ports/tracy/downgrade-capstone-5.patch b/Dependencies/Ports/tracy/downgrade-capstone-5.patch index d159b2f1..800c8025 100644 --- a/Dependencies/Ports/tracy/downgrade-capstone-5.patch +++ b/Dependencies/Ports/tracy/downgrade-capstone-5.patch @@ -1 +1,87 @@ -diff --git a/profiler/src/profiler/TracyDisassembly.cpp b/profiler/src/profiler/TracyDisassembly.cpp --- a/profiler/src/profiler/TracyDisassembly.cpp +++ b/profiler/src/profiler/TracyDisassembly.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include "TracyDisassembly.hpp" @@ -249,7 +249,7 @@ DisasmData Disassemble( uint64_t symAddr, const Worker& worker ) rval = cs_open( CS_ARCH_ARM, CS_MODE_ARM, &handle ); break; case CpuArchArm64: - rval = cs_open( CS_ARCH_AARCH64, CS_MODE_ARM, &handle ); + rval = cs_open( CS_ARCH_ARM64, CS_MODE_ARM, &handle ); break; default: assert( false ); @@ -317,9 +317,9 @@ DisasmData Disassemble( uint64_t symAddr, const Worker& worker ) } break; case CpuArchArm64: - if( detail.aarch64.op_count == 1 && detail.aarch64.operands[0].type == AARCH64_OP_IMM ) + if( detail.arm64.op_count == 1 && detail.arm64.operands[0].type == ARM64_OP_IMM ) { - jumpAddr = (uint64_t)detail.aarch64.operands[0].imm; + jumpAddr = (uint64_t)detail.arm64.operands[0].imm; } break; default: @@ -404,18 +404,18 @@ DisasmData Disassemble( uint64_t symAddr, const Worker& worker ) } break; case CpuArchArm64: - for( uint8_t i=0; i #include -#include +#include #define ZDICT_STATIC_LINKING_ONLY #include @@ -4010,7 +4010,7 @@ void Worker::AddSymbolCode( uint64_t ptr, const char* data, size_t sz ) rval = cs_open( CS_ARCH_ARM, CS_MODE_ARM, &handle ); break; case CpuArchArm64: - rval = cs_open( CS_ARCH_AARCH64, CS_MODE_ARM, &handle ); + rval = cs_open( CS_ARCH_ARM64, CS_MODE_ARM, &handle ); break; default: assert( false ); @@ -4050,9 +4050,9 @@ void Worker::AddSymbolCode( uint64_t ptr, const char* data, size_t sz ) } break; case CpuArchArm64: - if( detail.aarch64.op_count == 1 && detail.aarch64.operands[0].type == AARCH64_OP_IMM ) + if( detail.arm64.op_count == 1 && detail.arm64.operands[0].type == ARM64_OP_IMM ) { - callAddr = (uint64_t)detail.aarch64.operands[0].imm; + callAddr = (uint64_t)detail.arm64.operands[0].imm; } break; default: \ No newline at end of file +diff --git a/profiler/src/profiler/TracyDisassembly.cpp b/profiler/src/profiler/TracyDisassembly.cpp +--- a/profiler/src/profiler/TracyDisassembly.cpp ++++ b/profiler/src/profiler/TracyDisassembly.cpp +@@ -1,4 +1,4 @@ +-#include ++#include + #include + + #include "TracyDisassembly.hpp" +@@ -249,7 +249,7 @@ DisasmData Disassemble( uint64_t symAddr, const Worker& worker ) + rval = cs_open( CS_ARCH_ARM, CS_MODE_ARM, &handle ); + break; + case CpuArchArm64: +- rval = cs_open( CS_ARCH_AARCH64, CS_MODE_ARM, &handle ); ++ rval = cs_open( CS_ARCH_ARM64, CS_MODE_ARM, &handle ); + break; + default: + assert( false ); +@@ -317,9 +317,9 @@ DisasmData Disassemble( uint64_t symAddr, const Worker& worker ) + } + break; + case CpuArchArm64: +- if( detail.aarch64.op_count == 1 && detail.aarch64.operands[0].type == AARCH64_OP_IMM ) ++ if( detail.arm64.op_count == 1 && detail.arm64.operands[0].type == ARM64_OP_IMM ) + { +- jumpAddr = (uint64_t)detail.aarch64.operands[0].imm; ++ jumpAddr = (uint64_t)detail.arm64.operands[0].imm; + } + break; + default: +@@ -404,18 +404,18 @@ DisasmData Disassemble( uint64_t symAddr, const Worker& worker ) + } + break; + case CpuArchArm64: +- for( uint8_t i=0; i + #include + +-#include ++#include + + #define ZDICT_STATIC_LINKING_ONLY + #include +@@ -4010,7 +4010,7 @@ void Worker::AddSymbolCode( uint64_t ptr, const char* data, size_t sz ) + rval = cs_open( CS_ARCH_ARM, CS_MODE_ARM, &handle ); + break; + case CpuArchArm64: +- rval = cs_open( CS_ARCH_AARCH64, CS_MODE_ARM, &handle ); ++ rval = cs_open( CS_ARCH_ARM64, CS_MODE_ARM, &handle ); + break; + default: + assert( false ); +@@ -4050,9 +4050,9 @@ void Worker::AddSymbolCode( uint64_t ptr, const char* data, size_t sz ) + } + break; + case CpuArchArm64: +- if( detail.aarch64.op_count == 1 && detail.aarch64.operands[0].type == AARCH64_OP_IMM ) ++ if( detail.arm64.op_count == 1 && detail.arm64.operands[0].type == ARM64_OP_IMM ) + { +- callAddr = (uint64_t)detail.aarch64.operands[0].imm; ++ callAddr = (uint64_t)detail.arm64.operands[0].imm; + } + break; + default: diff --git a/Dependencies/Ports/tracy/fix-imgui-patch.patch b/Dependencies/Ports/tracy/fix-imgui-patch.patch index d404abbd..1791cf5b 100644 --- a/Dependencies/Ports/tracy/fix-imgui-patch.patch +++ b/Dependencies/Ports/tracy/fix-imgui-patch.patch @@ -1 +1,68 @@ -diff --git a/cmake/imgui-loader.patch b/cmake/imgui-loader.patch --- a/cmake/imgui-loader.patch +++ b/cmake/imgui-loader.patch @@ -1,16 +1,16 @@ -diff --git i/backends/imgui_impl_opengl3_loader.h w/backends/imgui_impl_opengl3_loader.h -index 4ca0536..a1ff572 100644 ---- i/backends/imgui_impl_opengl3_loader.h -+++ w/backends/imgui_impl_opengl3_loader.h -@@ -180,6 +180,7 @@ typedef khronos_uint8_t GLubyte; - #define GL_VERSION 0x1F02 +diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h +index 2c80cc598..1177da586 100644 +--- a/backends/imgui_impl_opengl3_loader.h ++++ b/backends/imgui_impl_opengl3_loader.h +@@ -182,6 +183,7 @@ typedef khronos_uint8_t GLubyte; #define GL_EXTENSIONS 0x1F03 + #define GL_NEAREST 0x2600 #define GL_LINEAR 0x2601 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 #define GL_TEXTURE_MAG_FILTER 0x2800 #define GL_TEXTURE_MIN_FILTER 0x2801 #define GL_TEXTURE_WRAP_S 0x2802 -@@ -244,8 +245,10 @@ GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); +@@ -246,8 +247,10 @@ GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); #define GL_TEXTURE0 0x84C0 #define GL_ACTIVE_TEXTURE 0x84E0 typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); @@ -21,7 +21,7 @@ index 4ca0536..a1ff572 100644 #endif #endif /* GL_VERSION_1_3 */ #ifndef GL_VERSION_1_4 -@@ -481,7 +484,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); +@@ -489,7 +492,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); /* gl3w internal state */ union ImGL3WProcs { @@ -30,7 +30,7 @@ index 4ca0536..a1ff572 100644 struct { PFNGLACTIVETEXTUREPROC ActiveTexture; PFNGLATTACHSHADERPROC AttachShader; -@@ -497,6 +500,7 @@ union ImGL3WProcs { +@@ -506,6 +510,7 @@ union ImGL3WProcs { PFNGLCLEARPROC Clear; PFNGLCLEARCOLORPROC ClearColor; PFNGLCOMPILESHADERPROC CompileShader; @@ -38,7 +38,7 @@ index 4ca0536..a1ff572 100644 PFNGLCREATEPROGRAMPROC CreateProgram; PFNGLCREATESHADERPROC CreateShader; PFNGLDELETEBUFFERSPROC DeleteBuffers; -@@ -563,6 +567,7 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs; +@@ -575,6 +580,7 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs; #define glClear imgl3wProcs.gl.Clear #define glClearColor imgl3wProcs.gl.ClearColor #define glCompileShader imgl3wProcs.gl.CompileShader @@ -46,11 +46,11 @@ index 4ca0536..a1ff572 100644 #define glCreateProgram imgl3wProcs.gl.CreateProgram #define glCreateShader imgl3wProcs.gl.CreateShader #define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers -@@ -859,6 +864,7 @@ static const char *proc_names[] = { +@@ -873,6 +879,7 @@ static const char *proc_names[] = { "glClear", "glClearColor", "glCompileShader", + "glCompressedTexImage2D", "glCreateProgram", "glCreateShader", - "glDeleteBuffers", + "glDeleteBuffers", \ No newline at end of file +diff --git a/cmake/imgui-loader.patch b/cmake/imgui-loader.patch +--- a/cmake/imgui-loader.patch ++++ b/cmake/imgui-loader.patch +@@ -1,16 +1,16 @@ +-diff --git i/backends/imgui_impl_opengl3_loader.h w/backends/imgui_impl_opengl3_loader.h +-index 4ca0536..a1ff572 100644 +---- i/backends/imgui_impl_opengl3_loader.h +-+++ w/backends/imgui_impl_opengl3_loader.h +-@@ -180,6 +180,7 @@ typedef khronos_uint8_t GLubyte; +- #define GL_VERSION 0x1F02 ++diff --git a/backends/imgui_impl_opengl3_loader.h b/backends/imgui_impl_opengl3_loader.h ++index 2c80cc598..1177da586 100644 ++--- a/backends/imgui_impl_opengl3_loader.h +++++ b/backends/imgui_impl_opengl3_loader.h ++@@ -182,6 +183,7 @@ typedef khronos_uint8_t GLubyte; + #define GL_EXTENSIONS 0x1F03 ++ #define GL_NEAREST 0x2600 + #define GL_LINEAR 0x2601 + +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 + #define GL_TEXTURE_MAG_FILTER 0x2800 + #define GL_TEXTURE_MIN_FILTER 0x2801 + #define GL_TEXTURE_WRAP_S 0x2802 +-@@ -244,8 +245,10 @@ GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); ++@@ -246,8 +247,10 @@ GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); + #define GL_TEXTURE0 0x84C0 + #define GL_ACTIVE_TEXTURE 0x84E0 + typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +@@ -21,7 +21,7 @@ index 4ca0536..a1ff572 100644 + #endif + #endif /* GL_VERSION_1_3 */ + #ifndef GL_VERSION_1_4 +-@@ -481,7 +484,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); ++@@ -489,7 +492,7 @@ GL3W_API GL3WglProc imgl3wGetProcAddress(const char *proc); + + /* gl3w internal state */ + union ImGL3WProcs { +@@ -30,7 +30,7 @@ index 4ca0536..a1ff572 100644 + struct { + PFNGLACTIVETEXTUREPROC ActiveTexture; + PFNGLATTACHSHADERPROC AttachShader; +-@@ -497,6 +500,7 @@ union ImGL3WProcs { ++@@ -506,6 +510,7 @@ union ImGL3WProcs { + PFNGLCLEARPROC Clear; + PFNGLCLEARCOLORPROC ClearColor; + PFNGLCOMPILESHADERPROC CompileShader; +@@ -38,7 +38,7 @@ index 4ca0536..a1ff572 100644 + PFNGLCREATEPROGRAMPROC CreateProgram; + PFNGLCREATESHADERPROC CreateShader; + PFNGLDELETEBUFFERSPROC DeleteBuffers; +-@@ -563,6 +567,7 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs; ++@@ -575,6 +580,7 @@ GL3W_API extern union ImGL3WProcs imgl3wProcs; + #define glClear imgl3wProcs.gl.Clear + #define glClearColor imgl3wProcs.gl.ClearColor + #define glCompileShader imgl3wProcs.gl.CompileShader +@@ -46,11 +46,11 @@ index 4ca0536..a1ff572 100644 + #define glCreateProgram imgl3wProcs.gl.CreateProgram + #define glCreateShader imgl3wProcs.gl.CreateShader + #define glDeleteBuffers imgl3wProcs.gl.DeleteBuffers +-@@ -859,6 +864,7 @@ static const char *proc_names[] = { ++@@ -873,6 +879,7 @@ static const char *proc_names[] = { + "glClear", + "glClearColor", + "glCompileShader", + + "glCompressedTexImage2D", + "glCreateProgram", + "glCreateShader", +- "glDeleteBuffers", ++ "glDeleteBuffers", diff --git a/Dependencies/Ports/tracy/fix-vendor-versions.patch b/Dependencies/Ports/tracy/fix-vendor-versions.patch index e41ef79b..95c30739 100644 --- a/Dependencies/Ports/tracy/fix-vendor-versions.patch +++ b/Dependencies/Ports/tracy/fix-vendor-versions.patch @@ -1 +1,404 @@ -diff --git a/cmake/server.cmake b/cmake/server.cmake --- a/cmake/server.cmake +++ b/cmake/server.cmake @@ -31,7 +31,7 @@ list(TRANSFORM TRACY_SERVER_SOURCES PREPEND "${TRACY_SERVER_DIR}/") add_library(TracyServer STATIC EXCLUDE_FROM_ALL ${TRACY_COMMON_SOURCES} ${TRACY_SERVER_SOURCES}) target_include_directories(TracyServer PUBLIC ${TRACY_COMMON_DIR} ${TRACY_SERVER_DIR}) -target_link_libraries(TracyServer PUBLIC TracyCapstone libzstd PPQSort::PPQSort) +target_link_libraries(TracyServer PUBLIC capstone::capstone zstd::libzstd PPQSort::PPQSort) if(NO_STATISTICS) target_compile_definitions(TracyServer PUBLIC TRACY_NO_STATISTICS) endif() diff --git a/cmake/vendor.cmake b/cmake/vendor.cmake --- a/cmake/vendor.cmake +++ b/cmake/vendor.cmake @@ -7,7 +7,6 @@ set (ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../") # Dependencies are taken from the system first and if not found, they are pulled with CPM and built from source include(FindPkgConfig) -include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake) if(APPLE AND BUNDLE) set(DLOPT ON) @@ -26,59 +25,11 @@ endif() # capstone -pkg_check_modules(CAPSTONE capstone) -if(CAPSTONE_FOUND AND NOT DOWNLOAD_CAPSTONE) - message(STATUS "Capstone found: ${CAPSTONE}") - add_library(TracyCapstone INTERFACE) - target_include_directories(TracyCapstone INTERFACE ${CAPSTONE_INCLUDE_DIRS}) - target_link_libraries(TracyCapstone INTERFACE ${CAPSTONE_LINK_LIBRARIES}) -else() - CPMAddPackage( - NAME capstone - GITHUB_REPOSITORY capstone-engine/capstone - GIT_TAG 6.0.0-Alpha10 - OPTIONS - "CAPSTONE_X86_ATT_DISABLE ON" - "CAPSTONE_ALPHA_SUPPORT OFF" - "CAPSTONE_ARC_SUPPORT OFF" - "CAPSTONE_HPPA_SUPPORT OFF" - "CAPSTONE_LOONGARCH_SUPPORT OFF" - "CAPSTONE_M680X_SUPPORT OFF" - "CAPSTONE_M68K_SUPPORT OFF" - "CAPSTONE_MIPS_SUPPORT OFF" - "CAPSTONE_MOS65XX_SUPPORT OFF" - "CAPSTONE_PPC_SUPPORT OFF" - "CAPSTONE_SPARC_SUPPORT OFF" - "CAPSTONE_SYSTEMZ_SUPPORT OFF" - "CAPSTONE_XCORE_SUPPORT OFF" - "CAPSTONE_TRICORE_SUPPORT OFF" - "CAPSTONE_TMS320C64X_SUPPORT OFF" - "CAPSTONE_M680X_SUPPORT OFF" - "CAPSTONE_EVM_SUPPORT OFF" - "CAPSTONE_WASM_SUPPORT OFF" - "CAPSTONE_BPF_SUPPORT OFF" - "CAPSTONE_RISCV_SUPPORT OFF" - "CAPSTONE_SH_SUPPORT OFF" - "CAPSTONE_XTENSA_SUPPORT OFF" - "CAPSTONE_BUILD_MACOS_THIN ON" - EXCLUDE_FROM_ALL TRUE - ) - add_library(TracyCapstone INTERFACE) - target_include_directories(TracyCapstone INTERFACE ${capstone_SOURCE_DIR}/include/capstone) - target_link_libraries(TracyCapstone INTERFACE capstone_static) -endif() +find_package(capstone CONFIG) # Zstd -CPMAddPackage( - NAME zstd - GITHUB_REPOSITORY facebook/zstd - GIT_TAG v1.5.7 - OPTIONS - "ZSTD_BUILD_SHARED OFF" - EXCLUDE_FROM_ALL TRUE - SOURCE_SUBDIR build/cmake -) +find_package(zstd CONFIG) # Diff Template Library @@ -98,238 +49,89 @@ target_include_directories(TracyGetOpt PUBLIC ${GETOPT_DIR}) # PPQSort -CPMAddPackage( - NAME PPQSort - GITHUB_REPOSITORY GabTux/PPQSort - VERSION 1.0.6 - PATCHES - "${CMAKE_CURRENT_LIST_DIR}/ppqsort-nodebug.patch" - "${CMAKE_CURRENT_LIST_DIR}/ppqsort-semaphore.patch" - EXCLUDE_FROM_ALL TRUE -) +find_package(PPQSort CONFIG) # json -CPMAddPackage( - NAME json - GITHUB_REPOSITORY nlohmann/json - GIT_TAG v3.12.0 - EXCLUDE_FROM_ALL TRUE -) +find_package(nlohmann_json CONFIG) if(VENDOR_GUI) # GLFW if(NOT USE_WAYLAND AND NOT EMSCRIPTEN) - pkg_check_modules(GLFW glfw3) - if (GLFW_FOUND AND NOT DOWNLOAD_GLFW) - add_library(TracyGlfw3 INTERFACE) - target_include_directories(TracyGlfw3 INTERFACE ${GLFW_INCLUDE_DIRS}) - target_link_libraries(TracyGlfw3 INTERFACE ${GLFW_LINK_LIBRARIES}) - else() - CPMAddPackage( - NAME glfw - GITHUB_REPOSITORY glfw/glfw - GIT_TAG 3.4 - OPTIONS - "GLFW_BUILD_EXAMPLES OFF" - "GLFW_BUILD_TESTS OFF" - "GLFW_BUILD_DOCS OFF" - "GLFW_INSTALL OFF" - EXCLUDE_FROM_ALL TRUE - ) - add_library(TracyGlfw3 INTERFACE) - target_link_libraries(TracyGlfw3 INTERFACE glfw) - endif() + find_package(glfw3 CONFIG) endif() # freetype - pkg_check_modules(FREETYPE freetype2) - if (FREETYPE_FOUND AND NOT DOWNLOAD_FREETYPE) - add_library(TracyFreetype INTERFACE) - target_include_directories(TracyFreetype INTERFACE ${FREETYPE_INCLUDE_DIRS}) - target_link_libraries(TracyFreetype INTERFACE ${FREETYPE_LINK_LIBRARIES}) - else() - CPMAddPackage( - NAME freetype - GITHUB_REPOSITORY freetype/freetype - GIT_TAG VER-2-14-3 - OPTIONS - "FT_DISABLE_HARFBUZZ ON" - "FT_WITH_HARFBUZZ OFF" - "FT_DISABLE_ZLIB ON" - "FT_DISABLE_BZIP2 ON" - "FT_DISABLE_PNG ON" - "FT_DISABLE_BROTLI ON" - EXCLUDE_FROM_ALL TRUE - ) - add_library(TracyFreetype INTERFACE) - target_link_libraries(TracyFreetype INTERFACE freetype) - endif() + find_package(Freetype) # ImGui - CPMAddPackage( - NAME ImGui - GITHUB_REPOSITORY ocornut/imgui - GIT_TAG v1.92.9b-docking - DOWNLOAD_ONLY TRUE - PATCHES - "${CMAKE_CURRENT_LIST_DIR}/imgui-emscripten.patch" - "${CMAKE_CURRENT_LIST_DIR}/imgui-loader.patch" - "${CMAKE_CURRENT_LIST_DIR}/imgui-no-samplers.patch" - "${CMAKE_CURRENT_LIST_DIR}/imgui-no-default-font.patch" - "${CMAKE_CURRENT_LIST_DIR}/imgui-macos-clipboard.patch" - ) - - set(IMGUI_SOURCES - imgui_widgets.cpp - imgui_draw.cpp - imgui_demo.cpp - imgui.cpp - imgui_tables.cpp - misc/freetype/imgui_freetype.cpp - backends/imgui_impl_opengl3.cpp - ) - - list(TRANSFORM IMGUI_SOURCES PREPEND "${ImGui_SOURCE_DIR}/") - - add_library(TracyImGui STATIC EXCLUDE_FROM_ALL ${IMGUI_SOURCES}) - target_include_directories(TracyImGui PUBLIC ${ImGui_SOURCE_DIR}) - target_link_libraries(TracyImGui PUBLIC TracyFreetype) - target_compile_definitions(TracyImGui PRIVATE "IMGUI_ENABLE_FREETYPE") - target_compile_definitions(TracyImGui PUBLIC "IMGUI_USE_WCHAR32") - #target_compile_definitions(TracyImGui PUBLIC "IMGUI_DISABLE_OBSOLETE_FUNCTIONS") - - if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND LEGACY) - find_package(X11 REQUIRED) - target_link_libraries(TracyImGui PUBLIC ${X11_LIBRARIES}) - endif() + if (ImGui_SOURCE_DIR) + set(IMGUI_SOURCES + imgui_widgets.cpp + imgui_draw.cpp + imgui_demo.cpp + imgui.cpp + imgui_tables.cpp + misc/freetype/imgui_freetype.cpp + backends/imgui_impl_opengl3.cpp + ) - if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - target_compile_definitions(TracyImGui PRIVATE "IMGUI_DISABLE_DEBUG_TOOLS" "IMGUI_DISABLE_DEMO_WINDOWS") - endif() + list(TRANSFORM IMGUI_SOURCES PREPEND "${ImGui_SOURCE_DIR}/") - if(APPLE) - target_link_libraries(TracyImGui PUBLIC "-framework ApplicationServices") - endif() + add_library(TracyImGui STATIC EXCLUDE_FROM_ALL ${IMGUI_SOURCES}) + target_include_directories(TracyImGui PUBLIC ${ImGui_SOURCE_DIR}) + target_link_libraries(TracyImGui PUBLIC Freetype::Freetype) + target_compile_definitions(TracyImGui PRIVATE "IMGUI_ENABLE_FREETYPE") + target_compile_definitions(TracyImGui PUBLIC "IMGUI_USE_WCHAR32") + #target_compile_definitions(TracyImGui PUBLIC "IMGUI_DISABLE_OBSOLETE_FUNCTIONS") - # NFD + if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND LEGACY) + find_package(X11 REQUIRED) + target_link_libraries(TracyImGui PUBLIC ${X11_LIBRARIES}) + endif() - if(NOT NO_FILESELECTOR AND NOT EMSCRIPTEN) - if(GTK_FILESELECTOR) - set(NFD_PORTAL OFF) - else() - set(NFD_PORTAL ON) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_definitions(TracyImGui PRIVATE "IMGUI_DISABLE_DEBUG_TOOLS" "IMGUI_DISABLE_DEMO_WINDOWS") endif() - CPMAddPackage( - NAME nfd - GITHUB_REPOSITORY btzy/nativefiledialog-extended - GIT_TAG 3cd252a8f7ca32419b1ca235c2990ba6a0ecba7c - EXCLUDE_FROM_ALL TRUE - OPTIONS - "BUILD_SHARED_LIBS OFF" - "NFD_PORTAL ${NFD_PORTAL}" - ) + if(APPLE) + target_link_libraries(TracyImGui PUBLIC "-framework ApplicationServices") + endif() endif() + # NFD + + find_package(nfd CONFIG) + # md4c - CPMAddPackage( - NAME md4c - GITHUB_REPOSITORY mity/md4c - GIT_TAG 65c6c9d72cebd9a731aaa5597414ce04d9ea5de3 - OPTIONS - "BUILD_SHARED_LIBS OFF" - EXCLUDE_FROM_ALL TRUE - ) + find_package(md4c CONFIG) if(NOT EMSCRIPTEN) # base64 - set(BUILD_SHARED_LIBS_SAVE ${BUILD_SHARED_LIBS}) - set(BUILD_SHARED_LIBS OFF) - CPMAddPackage( - NAME base64 - GITHUB_REPOSITORY aklomp/base64 - GIT_TAG v0.5.2 - OPTIONS - "BASE64_BUILD_CLI OFF" - "BASE64_WITH_OpenMP OFF" - EXCLUDE_FROM_ALL TRUE - ) - set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVE}) + find_package(base64 CONFIG) # tidy - CPMAddPackage( - NAME tidy - GITHUB_REPOSITORY htacg/tidy-html5 - GIT_TAG 5.8.0 - PATCHES - "${CMAKE_CURRENT_LIST_DIR}/tidy-cmake.patch" - EXCLUDE_FROM_ALL TRUE - ) + find_package(unofficial-tidy-html5 CONFIG) # usearch - CPMAddPackage( - NAME usearch - GITHUB_REPOSITORY unum-cloud/usearch - GIT_TAG v2.26.0 - EXCLUDE_FROM_ALL TRUE - ) + find_package(usearch CONFIG) # pugixml - pkg_check_modules(PUGIXML pugixml) - if (PUGIXML_FOUND AND NOT DOWNLOAD_PUGIXML) - add_library(TracyPugixml INTERFACE) - target_include_directories(TracyPugixml INTERFACE ${PUGIXML_INCLUDE_DIRS}) - target_link_libraries(TracyPugixml INTERFACE ${PUGIXML_LINK_LIBRARIES}) - else() - CPMAddPackage( - NAME pugixml - GITHUB_REPOSITORY zeux/pugixml - GIT_TAG v1.16 - EXCLUDE_FROM_ALL TRUE - ) - add_library(TracyPugixml INTERFACE) - target_link_libraries(TracyPugixml INTERFACE pugixml) - endif() + find_package(pugixml CONFIG) # libcurl - pkg_check_modules(LIBCURL libcurl>=7.87.0) - if (LIBCURL_FOUND AND NOT DOWNLOAD_LIBCURL) - add_library(TracyLibcurl INTERFACE) - target_include_directories(TracyLibcurl INTERFACE ${LIBCURL_INCLUDE_DIRS}) - target_link_libraries(TracyLibcurl INTERFACE ${LIBCURL_LINK_LIBRARIES}) - else() - CPMAddPackage( - NAME libcurl - GITHUB_REPOSITORY curl/curl - GIT_TAG curl-8_21_0 - OPTIONS - "BUILD_STATIC_LIBS ON" - "BUILD_SHARED_LIBS OFF" - "HTTP_ONLY ON" - "CURL_ZSTD OFF" - "CURL_USE_LIBPSL OFF" - "CURL_USE_LIBSSH2 OFF" - "CURL_BROTLI OFF" - "USE_NGHTTP2 OFF" - "USE_LIBIDN2 OFF" - EXCLUDE_FROM_ALL TRUE - ) - add_library(TracyLibcurl INTERFACE) - target_link_libraries(TracyLibcurl INTERFACE libcurl_static) - target_include_directories(TracyLibcurl INTERFACE ${libcurl_SOURCE_DIR}/include) - endif() + find_package(CURL) endif() diff --git a/profiler/CMakeLists.txt b/profiler/CMakeLists.txt --- a/profiler/CMakeLists.txt +++ b/profiler/CMakeLists.txt @@ -301,7 +301,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE TracyImGui Threads::Threads nlohmann_json::nlohmann_json - md4c + md4c::md4c ) target_include_directories(${PROJECT_NAME} PRIVATE ${tidy_SOURCE_DIR}/include @@ -312,11 +312,11 @@ target_include_directories(${PROJECT_NAME} PRIVATE if(NOT EMSCRIPTEN) target_link_libraries(${PROJECT_NAME} PRIVATE - TracyLibcurl - base64 - tidy-static - TracyPugixml - usearch + CURL::libcurl + aklomp::base64 + unofficial::tidy-html5::tidy + pugixml::pugixml + usearch::usearch ) endif() @@ -327,7 +327,7 @@ if(NOT EMSCRIPTEN) target_link_libraries(${PROJECT_NAME} PRIVATE nfd::nfd) endif() if(NOT USE_WAYLAND) - target_link_libraries(${PROJECT_NAME} PRIVATE TracyGlfw3) + target_link_libraries(${PROJECT_NAME} PRIVATE glfw) endif() endif() \ No newline at end of file +diff --git a/cmake/server.cmake b/cmake/server.cmake +--- a/cmake/server.cmake ++++ b/cmake/server.cmake +@@ -31,7 +31,7 @@ list(TRANSFORM TRACY_SERVER_SOURCES PREPEND "${TRACY_SERVER_DIR}/") + + add_library(TracyServer STATIC EXCLUDE_FROM_ALL ${TRACY_COMMON_SOURCES} ${TRACY_SERVER_SOURCES}) + target_include_directories(TracyServer PUBLIC ${TRACY_COMMON_DIR} ${TRACY_SERVER_DIR}) +-target_link_libraries(TracyServer PUBLIC TracyCapstone libzstd PPQSort::PPQSort) ++target_link_libraries(TracyServer PUBLIC capstone::capstone zstd::libzstd PPQSort::PPQSort) + if(NO_STATISTICS) + target_compile_definitions(TracyServer PUBLIC TRACY_NO_STATISTICS) + endif() + +diff --git a/cmake/vendor.cmake b/cmake/vendor.cmake +--- a/cmake/vendor.cmake ++++ b/cmake/vendor.cmake +@@ -7,7 +7,6 @@ set (ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../") + # Dependencies are taken from the system first and if not found, they are pulled with CPM and built from source + + include(FindPkgConfig) +-include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake) + + if(APPLE AND BUNDLE) + set(DLOPT ON) +@@ -26,59 +25,11 @@ endif() + + # capstone + +-pkg_check_modules(CAPSTONE capstone) +-if(CAPSTONE_FOUND AND NOT DOWNLOAD_CAPSTONE) +- message(STATUS "Capstone found: ${CAPSTONE}") +- add_library(TracyCapstone INTERFACE) +- target_include_directories(TracyCapstone INTERFACE ${CAPSTONE_INCLUDE_DIRS}) +- target_link_libraries(TracyCapstone INTERFACE ${CAPSTONE_LINK_LIBRARIES}) +-else() +- CPMAddPackage( +- NAME capstone +- GITHUB_REPOSITORY capstone-engine/capstone +- GIT_TAG 6.0.0-Alpha10 +- OPTIONS +- "CAPSTONE_X86_ATT_DISABLE ON" +- "CAPSTONE_ALPHA_SUPPORT OFF" +- "CAPSTONE_ARC_SUPPORT OFF" +- "CAPSTONE_HPPA_SUPPORT OFF" +- "CAPSTONE_LOONGARCH_SUPPORT OFF" +- "CAPSTONE_M680X_SUPPORT OFF" +- "CAPSTONE_M68K_SUPPORT OFF" +- "CAPSTONE_MIPS_SUPPORT OFF" +- "CAPSTONE_MOS65XX_SUPPORT OFF" +- "CAPSTONE_PPC_SUPPORT OFF" +- "CAPSTONE_SPARC_SUPPORT OFF" +- "CAPSTONE_SYSTEMZ_SUPPORT OFF" +- "CAPSTONE_XCORE_SUPPORT OFF" +- "CAPSTONE_TRICORE_SUPPORT OFF" +- "CAPSTONE_TMS320C64X_SUPPORT OFF" +- "CAPSTONE_M680X_SUPPORT OFF" +- "CAPSTONE_EVM_SUPPORT OFF" +- "CAPSTONE_WASM_SUPPORT OFF" +- "CAPSTONE_BPF_SUPPORT OFF" +- "CAPSTONE_RISCV_SUPPORT OFF" +- "CAPSTONE_SH_SUPPORT OFF" +- "CAPSTONE_XTENSA_SUPPORT OFF" +- "CAPSTONE_BUILD_MACOS_THIN ON" +- EXCLUDE_FROM_ALL TRUE +- ) +- add_library(TracyCapstone INTERFACE) +- target_include_directories(TracyCapstone INTERFACE ${capstone_SOURCE_DIR}/include/capstone) +- target_link_libraries(TracyCapstone INTERFACE capstone_static) +-endif() ++find_package(capstone CONFIG) + + # Zstd + +-CPMAddPackage( +- NAME zstd +- GITHUB_REPOSITORY facebook/zstd +- GIT_TAG v1.5.7 +- OPTIONS +- "ZSTD_BUILD_SHARED OFF" +- EXCLUDE_FROM_ALL TRUE +- SOURCE_SUBDIR build/cmake +-) ++find_package(zstd CONFIG) + + # Diff Template Library + +@@ -98,238 +49,89 @@ target_include_directories(TracyGetOpt PUBLIC ${GETOPT_DIR}) + + # PPQSort + +-CPMAddPackage( +- NAME PPQSort +- GITHUB_REPOSITORY GabTux/PPQSort +- VERSION 1.0.6 +- PATCHES +- "${CMAKE_CURRENT_LIST_DIR}/ppqsort-nodebug.patch" +- "${CMAKE_CURRENT_LIST_DIR}/ppqsort-semaphore.patch" +- EXCLUDE_FROM_ALL TRUE +-) ++find_package(PPQSort CONFIG) + + # json + +-CPMAddPackage( +- NAME json +- GITHUB_REPOSITORY nlohmann/json +- GIT_TAG v3.12.0 +- EXCLUDE_FROM_ALL TRUE +-) ++find_package(nlohmann_json CONFIG) + + if(VENDOR_GUI) + + # GLFW + + if(NOT USE_WAYLAND AND NOT EMSCRIPTEN) +- pkg_check_modules(GLFW glfw3) +- if (GLFW_FOUND AND NOT DOWNLOAD_GLFW) +- add_library(TracyGlfw3 INTERFACE) +- target_include_directories(TracyGlfw3 INTERFACE ${GLFW_INCLUDE_DIRS}) +- target_link_libraries(TracyGlfw3 INTERFACE ${GLFW_LINK_LIBRARIES}) +- else() +- CPMAddPackage( +- NAME glfw +- GITHUB_REPOSITORY glfw/glfw +- GIT_TAG 3.4 +- OPTIONS +- "GLFW_BUILD_EXAMPLES OFF" +- "GLFW_BUILD_TESTS OFF" +- "GLFW_BUILD_DOCS OFF" +- "GLFW_INSTALL OFF" +- EXCLUDE_FROM_ALL TRUE +- ) +- add_library(TracyGlfw3 INTERFACE) +- target_link_libraries(TracyGlfw3 INTERFACE glfw) +- endif() ++ find_package(glfw3 CONFIG) + endif() + + # freetype + +- pkg_check_modules(FREETYPE freetype2) +- if (FREETYPE_FOUND AND NOT DOWNLOAD_FREETYPE) +- add_library(TracyFreetype INTERFACE) +- target_include_directories(TracyFreetype INTERFACE ${FREETYPE_INCLUDE_DIRS}) +- target_link_libraries(TracyFreetype INTERFACE ${FREETYPE_LINK_LIBRARIES}) +- else() +- CPMAddPackage( +- NAME freetype +- GITHUB_REPOSITORY freetype/freetype +- GIT_TAG VER-2-14-3 +- OPTIONS +- "FT_DISABLE_HARFBUZZ ON" +- "FT_WITH_HARFBUZZ OFF" +- "FT_DISABLE_ZLIB ON" +- "FT_DISABLE_BZIP2 ON" +- "FT_DISABLE_PNG ON" +- "FT_DISABLE_BROTLI ON" +- EXCLUDE_FROM_ALL TRUE +- ) +- add_library(TracyFreetype INTERFACE) +- target_link_libraries(TracyFreetype INTERFACE freetype) +- endif() ++ find_package(Freetype) + + # ImGui + +- CPMAddPackage( +- NAME ImGui +- GITHUB_REPOSITORY ocornut/imgui +- GIT_TAG v1.92.9b-docking +- DOWNLOAD_ONLY TRUE +- PATCHES +- "${CMAKE_CURRENT_LIST_DIR}/imgui-emscripten.patch" +- "${CMAKE_CURRENT_LIST_DIR}/imgui-loader.patch" +- "${CMAKE_CURRENT_LIST_DIR}/imgui-no-samplers.patch" +- "${CMAKE_CURRENT_LIST_DIR}/imgui-no-default-font.patch" +- "${CMAKE_CURRENT_LIST_DIR}/imgui-macos-clipboard.patch" +- ) +- +- set(IMGUI_SOURCES +- imgui_widgets.cpp +- imgui_draw.cpp +- imgui_demo.cpp +- imgui.cpp +- imgui_tables.cpp +- misc/freetype/imgui_freetype.cpp +- backends/imgui_impl_opengl3.cpp +- ) +- +- list(TRANSFORM IMGUI_SOURCES PREPEND "${ImGui_SOURCE_DIR}/") +- +- add_library(TracyImGui STATIC EXCLUDE_FROM_ALL ${IMGUI_SOURCES}) +- target_include_directories(TracyImGui PUBLIC ${ImGui_SOURCE_DIR}) +- target_link_libraries(TracyImGui PUBLIC TracyFreetype) +- target_compile_definitions(TracyImGui PRIVATE "IMGUI_ENABLE_FREETYPE") +- target_compile_definitions(TracyImGui PUBLIC "IMGUI_USE_WCHAR32") +- #target_compile_definitions(TracyImGui PUBLIC "IMGUI_DISABLE_OBSOLETE_FUNCTIONS") +- +- if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND LEGACY) +- find_package(X11 REQUIRED) +- target_link_libraries(TracyImGui PUBLIC ${X11_LIBRARIES}) +- endif() ++ if (ImGui_SOURCE_DIR) ++ set(IMGUI_SOURCES ++ imgui_widgets.cpp ++ imgui_draw.cpp ++ imgui_demo.cpp ++ imgui.cpp ++ imgui_tables.cpp ++ misc/freetype/imgui_freetype.cpp ++ backends/imgui_impl_opengl3.cpp ++ ) + +- if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") +- target_compile_definitions(TracyImGui PRIVATE "IMGUI_DISABLE_DEBUG_TOOLS" "IMGUI_DISABLE_DEMO_WINDOWS") +- endif() ++ list(TRANSFORM IMGUI_SOURCES PREPEND "${ImGui_SOURCE_DIR}/") + +- if(APPLE) +- target_link_libraries(TracyImGui PUBLIC "-framework ApplicationServices") +- endif() ++ add_library(TracyImGui STATIC EXCLUDE_FROM_ALL ${IMGUI_SOURCES}) ++ target_include_directories(TracyImGui PUBLIC ${ImGui_SOURCE_DIR}) ++ target_link_libraries(TracyImGui PUBLIC Freetype::Freetype) ++ target_compile_definitions(TracyImGui PRIVATE "IMGUI_ENABLE_FREETYPE") ++ target_compile_definitions(TracyImGui PUBLIC "IMGUI_USE_WCHAR32") ++ #target_compile_definitions(TracyImGui PUBLIC "IMGUI_DISABLE_OBSOLETE_FUNCTIONS") + +- # NFD ++ if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND LEGACY) ++ find_package(X11 REQUIRED) ++ target_link_libraries(TracyImGui PUBLIC ${X11_LIBRARIES}) ++ endif() + +- if(NOT NO_FILESELECTOR AND NOT EMSCRIPTEN) +- if(GTK_FILESELECTOR) +- set(NFD_PORTAL OFF) +- else() +- set(NFD_PORTAL ON) ++ if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") ++ target_compile_definitions(TracyImGui PRIVATE "IMGUI_DISABLE_DEBUG_TOOLS" "IMGUI_DISABLE_DEMO_WINDOWS") + endif() + +- CPMAddPackage( +- NAME nfd +- GITHUB_REPOSITORY btzy/nativefiledialog-extended +- GIT_TAG 3cd252a8f7ca32419b1ca235c2990ba6a0ecba7c +- EXCLUDE_FROM_ALL TRUE +- OPTIONS +- "BUILD_SHARED_LIBS OFF" +- "NFD_PORTAL ${NFD_PORTAL}" +- ) ++ if(APPLE) ++ target_link_libraries(TracyImGui PUBLIC "-framework ApplicationServices") ++ endif() + endif() + ++ # NFD ++ ++ find_package(nfd CONFIG) ++ + # md4c + +- CPMAddPackage( +- NAME md4c +- GITHUB_REPOSITORY mity/md4c +- GIT_TAG 65c6c9d72cebd9a731aaa5597414ce04d9ea5de3 +- OPTIONS +- "BUILD_SHARED_LIBS OFF" +- EXCLUDE_FROM_ALL TRUE +- ) ++ find_package(md4c CONFIG) + + if(NOT EMSCRIPTEN) + + # base64 + +- set(BUILD_SHARED_LIBS_SAVE ${BUILD_SHARED_LIBS}) +- set(BUILD_SHARED_LIBS OFF) +- CPMAddPackage( +- NAME base64 +- GITHUB_REPOSITORY aklomp/base64 +- GIT_TAG v0.5.2 +- OPTIONS +- "BASE64_BUILD_CLI OFF" +- "BASE64_WITH_OpenMP OFF" +- EXCLUDE_FROM_ALL TRUE +- ) +- set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVE}) ++ find_package(base64 CONFIG) + + # tidy + +- CPMAddPackage( +- NAME tidy +- GITHUB_REPOSITORY htacg/tidy-html5 +- GIT_TAG 5.8.0 +- PATCHES +- "${CMAKE_CURRENT_LIST_DIR}/tidy-cmake.patch" +- EXCLUDE_FROM_ALL TRUE +- ) ++ find_package(unofficial-tidy-html5 CONFIG) + + # usearch + +- CPMAddPackage( +- NAME usearch +- GITHUB_REPOSITORY unum-cloud/usearch +- GIT_TAG v2.26.0 +- EXCLUDE_FROM_ALL TRUE +- ) ++ find_package(usearch CONFIG) + + # pugixml + +- pkg_check_modules(PUGIXML pugixml) +- if (PUGIXML_FOUND AND NOT DOWNLOAD_PUGIXML) +- add_library(TracyPugixml INTERFACE) +- target_include_directories(TracyPugixml INTERFACE ${PUGIXML_INCLUDE_DIRS}) +- target_link_libraries(TracyPugixml INTERFACE ${PUGIXML_LINK_LIBRARIES}) +- else() +- CPMAddPackage( +- NAME pugixml +- GITHUB_REPOSITORY zeux/pugixml +- GIT_TAG v1.16 +- EXCLUDE_FROM_ALL TRUE +- ) +- add_library(TracyPugixml INTERFACE) +- target_link_libraries(TracyPugixml INTERFACE pugixml) +- endif() ++ find_package(pugixml CONFIG) + + # libcurl + +- pkg_check_modules(LIBCURL libcurl>=7.87.0) +- if (LIBCURL_FOUND AND NOT DOWNLOAD_LIBCURL) +- add_library(TracyLibcurl INTERFACE) +- target_include_directories(TracyLibcurl INTERFACE ${LIBCURL_INCLUDE_DIRS}) +- target_link_libraries(TracyLibcurl INTERFACE ${LIBCURL_LINK_LIBRARIES}) +- else() +- CPMAddPackage( +- NAME libcurl +- GITHUB_REPOSITORY curl/curl +- GIT_TAG curl-8_21_0 +- OPTIONS +- "BUILD_STATIC_LIBS ON" +- "BUILD_SHARED_LIBS OFF" +- "HTTP_ONLY ON" +- "CURL_ZSTD OFF" +- "CURL_USE_LIBPSL OFF" +- "CURL_USE_LIBSSH2 OFF" +- "CURL_BROTLI OFF" +- "USE_NGHTTP2 OFF" +- "USE_LIBIDN2 OFF" +- EXCLUDE_FROM_ALL TRUE +- ) +- add_library(TracyLibcurl INTERFACE) +- target_link_libraries(TracyLibcurl INTERFACE libcurl_static) +- target_include_directories(TracyLibcurl INTERFACE ${libcurl_SOURCE_DIR}/include) +- endif() ++ find_package(CURL) + + endif() + + +diff --git a/profiler/CMakeLists.txt b/profiler/CMakeLists.txt +--- a/profiler/CMakeLists.txt ++++ b/profiler/CMakeLists.txt +@@ -301,7 +301,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE + TracyImGui + Threads::Threads + nlohmann_json::nlohmann_json +- md4c ++ md4c::md4c + ) + target_include_directories(${PROJECT_NAME} PRIVATE + ${tidy_SOURCE_DIR}/include +@@ -312,11 +312,11 @@ target_include_directories(${PROJECT_NAME} PRIVATE + + if(NOT EMSCRIPTEN) + target_link_libraries(${PROJECT_NAME} PRIVATE +- TracyLibcurl +- base64 +- tidy-static +- TracyPugixml +- usearch ++ CURL::libcurl ++ aklomp::base64 ++ unofficial::tidy-html5::tidy ++ pugixml::pugixml ++ usearch::usearch + ) + endif() + +@@ -327,7 +327,7 @@ if(NOT EMSCRIPTEN) + target_link_libraries(${PROJECT_NAME} PRIVATE nfd::nfd) + endif() + if(NOT USE_WAYLAND) +- target_link_libraries(${PROJECT_NAME} PRIVATE TracyGlfw3) ++ target_link_libraries(${PROJECT_NAME} PRIVATE glfw) + endif() + endif() + diff --git a/Dependencies/Ports/tracy/portfile.cmake b/Dependencies/Ports/tracy/portfile.cmake index 74d9652b..5c89a4c0 100644 --- a/Dependencies/Ports/tracy/portfile.cmake +++ b/Dependencies/Ports/tracy/portfile.cmake @@ -1 +1,91 @@ -vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO wolfpld/tracy REF "v${VERSION}" SHA512 d6d07db668e62e2c4fb476b549243c240434613554e99bd68b6446b56b92e6cec606246186a02964ed9a223b5ccdac55546185510cd9f6fda05d458314fb05c1 HEAD_REF master PATCHES build-tools.patch fix-vendor-versions.patch fix-imgui-patch.patch downgrade-capstone-5.patch # tracy wants capstone-6-alpha but vcpkg ships the most recent production capstone, 5.0.6 as of 2026-02-04 ) vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS FEATURES on-demand TRACY_ON_DEMAND fibers TRACY_FIBERS verbose TRACY_VERBOSE INVERTED_FEATURES crash-handler TRACY_NO_CRASH_HANDLER ) vcpkg_check_features(OUT_FEATURE_OPTIONS TOOLS_OPTIONS FEATURES cli-tools VCPKG_CLI_TOOLS gui-tools VCPKG_GUI_TOOLS ) if ("gui-tools" IN_LIST FEATURES) vcpkg_from_github( OUT_SOURCE_PATH tracy_imgui_path REPO ocornut/imgui REF "v1.92.9b-docking" SHA512 7eddcdb475f1db1fc8242d918533b955c964d2267abe713bdf23f8e2444770946d3c79c7855e360bab6168e36231b95bd05a84106c08f876dcd53daac9caccac PATCHES "${SOURCE_PATH}/cmake/imgui-emscripten.patch" "${SOURCE_PATH}/cmake/imgui-loader.patch" ) list(APPEND TOOLS_OPTIONS "-DImGui_SOURCE_DIR=${tracy_imgui_path}") endif() if("cli-tools" IN_LIST FEATURES OR "gui-tools" IN_LIST FEATURES) vcpkg_find_acquire_program(PKGCONFIG) list(APPEND TOOLS_OPTIONS "-DPKG_CONFIG_EXECUTABLE=${PKGCONFIG}") endif() vcpkg_cmake_configure( SOURCE_PATH ${SOURCE_PATH} OPTIONS -DDOWNLOAD_CAPSTONE=OFF -DLEGACY=ON -DCMAKE_FIND_PACKAGE_TARGETS_GLOBAL=ON -DCMAKE_DISABLE_FIND_PACKAGE_Git=ON ${FEATURE_OPTIONS} OPTIONS_RELEASE ${TOOLS_OPTIONS} MAYBE_UNUSED_VARIABLES DOWNLOAD_CAPSTONE LEGACY CMAKE_DISABLE_FIND_PACKAGE_Git ImGui_SOURCE_DIR ) vcpkg_cmake_install() vcpkg_copy_pdbs() vcpkg_cmake_config_fixup(PACKAGE_NAME Tracy CONFIG_PATH "lib/cmake/Tracy") function(tracy_copy_tool tool_name tool_dir) vcpkg_copy_tools( TOOL_NAMES "${tool_name}" SEARCH_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/${tool_dir}" ) endfunction() set(TOOLS) if("cli-tools" IN_LIST FEATURES) list(APPEND TOOLS tracy-capture tracy-capture-daemon tracy-csvexport tracy-merge) tracy_copy_tool(tracy-import-chrome import) tracy_copy_tool(tracy-import-fuchsia import) tracy_copy_tool(tracy-update update) endif() if("gui-tools" IN_LIST FEATURES) list(APPEND TOOLS tracy-profiler) endif() if(TOOLS) vcpkg_copy_tools(TOOL_NAMES ${TOOLS} AUTO_CLEAN) endif() vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") \ No newline at end of file +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO wolfpld/tracy + REF "v${VERSION}" + SHA512 d6d07db668e62e2c4fb476b549243c240434613554e99bd68b6446b56b92e6cec606246186a02964ed9a223b5ccdac55546185510cd9f6fda05d458314fb05c1 + HEAD_REF master + PATCHES + build-tools.patch + fix-vendor-versions.patch + fix-imgui-patch.patch + downgrade-capstone-5.patch # tracy wants capstone-6-alpha but vcpkg ships the most recent production capstone, 5.0.6 as of 2026-02-04 +) + +vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS + FEATURES + on-demand TRACY_ON_DEMAND + fibers TRACY_FIBERS + verbose TRACY_VERBOSE + INVERTED_FEATURES + crash-handler TRACY_NO_CRASH_HANDLER +) + +vcpkg_check_features(OUT_FEATURE_OPTIONS TOOLS_OPTIONS + FEATURES + cli-tools VCPKG_CLI_TOOLS + gui-tools VCPKG_GUI_TOOLS +) + +if ("gui-tools" IN_LIST FEATURES) + vcpkg_from_github( + OUT_SOURCE_PATH tracy_imgui_path + REPO ocornut/imgui + REF "v1.92.9b-docking" + SHA512 7eddcdb475f1db1fc8242d918533b955c964d2267abe713bdf23f8e2444770946d3c79c7855e360bab6168e36231b95bd05a84106c08f876dcd53daac9caccac + PATCHES + "${SOURCE_PATH}/cmake/imgui-emscripten.patch" + "${SOURCE_PATH}/cmake/imgui-loader.patch" + ) + list(APPEND TOOLS_OPTIONS "-DImGui_SOURCE_DIR=${tracy_imgui_path}") +endif() + +if("cli-tools" IN_LIST FEATURES OR "gui-tools" IN_LIST FEATURES) + vcpkg_find_acquire_program(PKGCONFIG) + list(APPEND TOOLS_OPTIONS "-DPKG_CONFIG_EXECUTABLE=${PKGCONFIG}") +endif() + +vcpkg_cmake_configure( + SOURCE_PATH ${SOURCE_PATH} + OPTIONS + -DDOWNLOAD_CAPSTONE=OFF + -DLEGACY=ON + -DCMAKE_FIND_PACKAGE_TARGETS_GLOBAL=ON + -DCMAKE_DISABLE_FIND_PACKAGE_Git=ON + -DTRACY_ENABLE=ON + ${FEATURE_OPTIONS} + OPTIONS_RELEASE + ${TOOLS_OPTIONS} + MAYBE_UNUSED_VARIABLES + DOWNLOAD_CAPSTONE + LEGACY + CMAKE_DISABLE_FIND_PACKAGE_Git + ImGui_SOURCE_DIR + TRACY_ENABLE +) +vcpkg_cmake_install() +vcpkg_copy_pdbs() +vcpkg_cmake_config_fixup(PACKAGE_NAME Tracy CONFIG_PATH "lib/cmake/Tracy") + +function(tracy_copy_tool tool_name tool_dir) + vcpkg_copy_tools( + TOOL_NAMES "${tool_name}" + SEARCH_DIR "${CURRENT_BUILDTREES_DIR}/${TARGET_TRIPLET}-rel/${tool_dir}" + ) +endfunction() + +set(TOOLS) +if("cli-tools" IN_LIST FEATURES) + list(APPEND TOOLS tracy-capture tracy-capture-daemon tracy-csvexport tracy-merge) + tracy_copy_tool(tracy-import-chrome import) + tracy_copy_tool(tracy-import-fuchsia import) + tracy_copy_tool(tracy-update update) +endif() +if("gui-tools" IN_LIST FEATURES) + list(APPEND TOOLS tracy-profiler) +endif() + +if(TOOLS) + vcpkg_copy_tools(TOOL_NAMES ${TOOLS} AUTO_CLEAN) +endif() +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/LICENSE") +file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include") diff --git a/Dependencies/Ports/tracy/vcpkg.json b/Dependencies/Ports/tracy/vcpkg.json index 2f964ec7..f7eb680a 100644 --- a/Dependencies/Ports/tracy/vcpkg.json +++ b/Dependencies/Ports/tracy/vcpkg.json @@ -1 +1,93 @@ -{ "name": "tracy", "version": "0.14.0", "description": "A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications.", "homepage": "https://github.com/wolfpld/tracy", "license": "BSD-3-Clause", "supports": "!(windows & (arm | uwp))", "dependencies": [ { "name": "pthreads", "platform": "!windows" }, { "name": "vcpkg-cmake", "host": true }, { "name": "vcpkg-cmake-config", "host": true } ], "default-features": [ "crash-handler" ], "features": { "cli-tools": { "description": "Build Tracy command-line tools: `capture`, `capture-daemon`, `csvexport`, `import-chrome`, `import-fuchsia`, `merge` and `update`", "supports": "!(windows & x86)", "dependencies": [ { "name": "capstone", "features": [ "arm", "arm64", "x86" ] }, { "name": "dbus", "default-features": false, "platform": "!windows" }, "nlohmann-json", "ppqsort", "zstd" ] }, "crash-handler": { "description": "Enable crash handler" }, "fibers": { "description": "Enable fibers support" }, "gui-tools": { "description": "Build Tracy GUI tool: `profiler` (aka `Tracy` executable)", "supports": "!(windows & x86)", "dependencies": [ "aklomp-base64", { "name": "capstone", "features": [ "arm", "arm64", "x86" ] }, "curl", { "name": "dbus", "default-features": false, "platform": "!windows" }, "freetype", "glfw3", "md4c", "nativefiledialog-extended", "nlohmann-json", "ppqsort", "pugixml", "tidy-html5", "usearch", "zstd" ] }, "on-demand": { "description": "Enable on-demand profiling" }, "verbose": { "description": "Enables verbose logging", "supports": "!android" } } } \ No newline at end of file +{ + "name": "tracy", + "version": "0.14.0", + "description": "A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling profiler for games and other applications.", + "homepage": "https://github.com/wolfpld/tracy", + "license": "BSD-3-Clause", + "supports": "!(windows & (arm | uwp))", + "dependencies": [ + { + "name": "pthreads", + "platform": "!windows" + }, + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ], + "default-features": [ + "crash-handler" + ], + "features": { + "cli-tools": { + "description": "Build Tracy command-line tools: `capture`, `capture-daemon`, `csvexport`, `import-chrome`, `import-fuchsia`, `merge` and `update`", + "supports": "!(windows & x86)", + "dependencies": [ + { + "name": "capstone", + "features": [ + "arm", + "arm64", + "x86" + ] + }, + { + "name": "dbus", + "default-features": false, + "platform": "!windows" + }, + "nlohmann-json", + "ppqsort", + "zstd" + ] + }, + "crash-handler": { + "description": "Enable crash handler" + }, + "fibers": { + "description": "Enable fibers support" + }, + "gui-tools": { + "description": "Build Tracy GUI tool: `profiler` (aka `Tracy` executable)", + "supports": "!(windows & x86)", + "dependencies": [ + "aklomp-base64", + { + "name": "capstone", + "features": [ + "arm", + "arm64", + "x86" + ] + }, + "curl", + { + "name": "dbus", + "default-features": false, + "platform": "!windows" + }, + "freetype", + "glfw3", + "md4c", + "nativefiledialog-extended", + "nlohmann-json", + "ppqsort", + "pugixml", + "tidy-html5", + "usearch", + "zstd" + ] + }, + "on-demand": { + "description": "Enable on-demand profiling" + }, + "verbose": { + "description": "Enables verbose logging", + "supports": "!android" + } + } +} diff --git a/EppoEditor/imgui.ini b/EppoEditor/imgui.ini index d22da3bc..5afdae68 100644 --- a/EppoEditor/imgui.ini +++ b/EppoEditor/imgui.ini @@ -1,63 +1,63 @@ -[Window][DockSpace] -Pos=0,0 -Size=1600,900 -Collapsed=0 - -[Window][Debug##Default] -Pos=60,60 -Size=400,400 -Collapsed=0 - -[Window][Scene Hierarchy] -Pos=0,22 -Size=315,450 -Collapsed=0 -DockId=0x00000007,0 - -[Window][Viewport] -Pos=317,22 -Size=1071,586 -Collapsed=0 -DockId=0x00000001,0 - -[Window][Scene Renderer] -Pos=1390,22 -Size=210,878 -Collapsed=0 -DockId=0x00000004,0 - -[Window][Properties] -Pos=0,474 -Size=315,426 -Collapsed=0 -DockId=0x00000008,0 - -[Window][Content Browser] -Pos=317,610 -Size=1071,290 -Collapsed=0 -DockId=0x00000002,0 - -[Window][Scene Settings] -Pos=0,474 -Size=315,426 -Collapsed=0 -DockId=0x00000008,1 - -[Window][Log] -Pos=317,610 -Size=1071,290 -Collapsed=0 -DockId=0x00000002,1 - -[Docking][Data] -DockSpace ID=0x14621557 Window=0x3DA2F1DE Pos=114,94 Size=1600,878 Split=X - DockNode ID=0x00000005 Parent=0x14621557 SizeRef=315,880 Split=Y Selected=0xB8729153 - DockNode ID=0x00000007 Parent=0x00000005 SizeRef=278,451 Selected=0xB8729153 - DockNode ID=0x00000008 Parent=0x00000005 SizeRef=278,427 Selected=0x8C72BEA8 - DockNode ID=0x00000006 Parent=0x14621557 SizeRef=1283,880 Split=X - DockNode ID=0x00000003 Parent=0x00000006 SizeRef=1071,880 Split=Y - DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1600,588 CentralNode=1 Selected=0xC450F867 - DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1600,290 Selected=0x139FDA3F - DockNode ID=0x00000004 Parent=0x00000006 SizeRef=210,880 Selected=0x68D924E0 - +[Window][DockSpace] +Pos=0,0 +Size=1600,900 +Collapsed=0 + +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][Scene Hierarchy] +Pos=0,22 +Size=320,450 +Collapsed=0 +DockId=0x00000007,0 + +[Window][Viewport] +Pos=322,22 +Size=1066,586 +Collapsed=0 +DockId=0x00000001,0 + +[Window][Scene Renderer] +Pos=1390,22 +Size=210,878 +Collapsed=0 +DockId=0x00000004,0 + +[Window][Properties] +Pos=0,474 +Size=320,426 +Collapsed=0 +DockId=0x00000008,0 + +[Window][Content Browser] +Pos=322,610 +Size=1066,290 +Collapsed=0 +DockId=0x00000002,0 + +[Window][Scene Settings] +Pos=0,474 +Size=320,426 +Collapsed=0 +DockId=0x00000008,1 + +[Window][Log] +Pos=322,610 +Size=1066,290 +Collapsed=0 +DockId=0x00000002,1 + +[Docking][Data] +DockSpace ID=0x14621557 Window=0x3DA2F1DE Pos=60,105 Size=1600,878 Split=X + DockNode ID=0x00000005 Parent=0x14621557 SizeRef=320,880 Split=Y Selected=0xB8729153 + DockNode ID=0x00000007 Parent=0x00000005 SizeRef=278,451 Selected=0xB8729153 + DockNode ID=0x00000008 Parent=0x00000005 SizeRef=278,427 Selected=0x8C72BEA8 + DockNode ID=0x00000006 Parent=0x14621557 SizeRef=1278,880 Split=X + DockNode ID=0x00000003 Parent=0x00000006 SizeRef=1066,880 Split=Y + DockNode ID=0x00000001 Parent=0x00000003 SizeRef=1600,588 CentralNode=1 Selected=0xC450F867 + DockNode ID=0x00000002 Parent=0x00000003 SizeRef=1600,290 Selected=0x139FDA3F + DockNode ID=0x00000004 Parent=0x00000006 SizeRef=210,880 Selected=0x68D924E0 + diff --git a/EppoEngine/Source/Core/Base.cpp b/EppoEngine/Source/Core/Base.cpp index 283f2662..d73e30d1 100644 --- a/EppoEngine/Source/Core/Base.cpp +++ b/EppoEngine/Source/Core/Base.cpp @@ -1,30 +1,30 @@ -#include "pch.h" -#include "Core/Base.h" - -#if defined(EP_TRACK_MEMORY) -void* operator new(size_t size) -{ - void* block = malloc(size); - TracySecureAllocS(block, size, 32); - return block; -} - -void* operator new[](size_t size) -{ - void* block = malloc(size); - TracySecureAllocS(block, size, 32); - return block; -} - -void operator delete(void* block) noexcept -{ - TracySecureFreeS(block, 32); - free(block); -} - -void operator delete[](void* block) noexcept -{ - TracySecureFreeS(block, 32); - free(block); -} -#endif \ No newline at end of file +#include "pch.h" +#include "Core/Base.h" + +#if defined(EP_TRACK_MEMORY) +void* operator new(size_t size) +{ + void* block = malloc(size); + TracyAllocS(block, size, 32); + return block; +} + +void* operator new[](size_t size) +{ + void* block = malloc(size); + TracyAllocS(block, size, 32); + return block; +} + +void operator delete(void* block) noexcept +{ + TracyFreeS(block, 32); + free(block); +} + +void operator delete[](void* block) noexcept +{ + TracyFreeS(block, 32); + free(block); +} +#endif From d4226236ac1f567ec305155b181f573e6d4cc94b Mon Sep 17 00:00:00 2001 From: Niels Eppenhof Date: Sat, 15 Aug 2026 21:04:18 +0200 Subject: [PATCH 2/2] ci: migrate builds to GitHub Actions (#31) * ci: migrate builds to GitHub Actions * ci: create Linux vcpkg downloads directory * fix: repair cross-platform CI failures --- .github/workflows/ci.yml | 282 ++++++++++++++++++ .gitlab-ci.yml | 73 ----- .gitlab/ci/Dockerfile | 57 ---- AGENTS.md | 2 +- EppoEngine/Source/Core/Base.h | 5 + EppoEngine/Source/Scripting/ScriptGlue.cpp | 52 ++-- EppoEngineTesting/Source/Core/Timer.cpp | 1 + .../Source/Scripting/Scripting.cpp | 28 ++ EppoScriptCore/Source/Core/InternalCalls.cs | 28 +- 9 files changed, 357 insertions(+), 171 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .gitlab-ci.yml delete mode 100644 .gitlab/ci/Dockerfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..873886d3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,282 @@ +name: CI + +on: + pull_request: + branches: [master, develop] + push: + branches: [master, develop] + workflow_dispatch: + +concurrency: + group: ci-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + VULKAN_SDK_VERSION: 1.4.309.0 + VCPKG_COMMIT: 8779f47d2db0785ee034b47fdfdcdb0e88b7010e + VCPKG_ROOT: ${{ github.workspace }}/.eppo/tools/vcpkg/8779f47d2db0785ee034b47fdfdcdb0e88b7010e + VCPKG_DISABLE_METRICS: "1" + VCPKG_DOWNLOADS: ${{ github.workspace }}/build/vcpkg-downloads + VCPKG_NUGET_REPOSITORY: ${{ github.server_url }}/${{ github.repository }} + VCPKG_BINARY_SOURCES: clear;nuget,GitHubPackages,${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && 'readwrite' || 'read' }} + DOTNET_CLI_TELEMETRY_OPTOUT: "1" + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "1" + DOTNET_NOLOGO: "1" + +jobs: + build-test: + name: ${{ matrix.system }} / ${{ matrix.configuration }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-24.04 + system: linux + configuration: Debug + - runner: ubuntu-24.04 + system: linux + configuration: Release + - runner: ubuntu-24.04 + system: linux + configuration: Dist + - runner: windows-2025-vs2026 + system: windows + configuration: Debug + - runner: windows-2025-vs2026 + system: windows + configuration: Release + - runner: windows-2025-vs2026 + system: windows + configuration: Dist + + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Restore build tools + id: build-tools + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: .eppo/tools + key: eppo-tools-${{ runner.os }}-vulkan-1.4.309.0-premake-5.0.0-beta8-vcpkg-8779f47d${{ runner.os == 'Windows' && '-debug-libraries' || '' }} + + - name: Install Linux prerequisites + if: runner.os == 'Linux' + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + autoconf automake autoconf-archive bison flex libtool libltdl-dev \ + libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev \ + libxext-dev libwayland-dev libxkbcommon-dev wayland-protocols \ + libgl1-mesa-dev libglu1-mesa-dev mono-complete pkg-config uuid-dev + + - name: Install Vulkan SDK on Linux + if: runner.os == 'Linux' && steps.build-tools.outputs.cache-hit != 'true' + shell: bash + run: | + archive="$RUNNER_TEMP/vulkansdk.tar.xz" + curl -fsSL "https://sdk.lunarg.com/sdk/download/$VULKAN_SDK_VERSION/linux/vulkansdk-linux-x86_64-$VULKAN_SDK_VERSION.tar.xz" -o "$archive" + echo "616a25a9d8b33336e83957f97ca273b8e95461649723354300d02c26ae52a1f6 $archive" | sha256sum --check + mkdir -p .eppo/tools/vulkan + tar -xf "$archive" -C .eppo/tools/vulkan + + - name: Install Vulkan SDK on Windows + if: runner.os == 'Windows' && steps.build-tools.outputs.cache-hit != 'true' + shell: pwsh + run: | + $installer = Join-Path $env:RUNNER_TEMP "VulkanSDK-Installer.exe" + $destination = Join-Path $env:GITHUB_WORKSPACE ".eppo/tools/vulkan/$env:VULKAN_SDK_VERSION" + curl.exe --fail --location "https://sdk.lunarg.com/sdk/download/$env:VULKAN_SDK_VERSION/windows/VulkanSDK-$env:VULKAN_SDK_VERSION-Installer.exe" --output $installer + if ((Get-FileHash -Algorithm SHA256 $installer).Hash -ne "48B132169B64FE65CDB0F20970195335A65354E73F1EA5373032C2A8BBAD4297") { + throw "The Vulkan SDK installer checksum does not match." + } + $process = Start-Process -FilePath $installer -ArgumentList @( + "--root", $destination, + "--accept-licenses", "--default-answer", "--confirm-command", "install", + "com.lunarg.vulkan", "com.lunarg.vulkan.debug", "copy_only=1" + ) -Wait -PassThru + if ($process.ExitCode -ne 0) { + throw "The Vulkan SDK installer failed with exit code $($process.ExitCode)." + } + + - name: Configure Vulkan SDK on Linux + if: runner.os == 'Linux' + shell: bash + run: | + vulkan_root="$GITHUB_WORKSPACE/.eppo/tools/vulkan/$VULKAN_SDK_VERSION/x86_64" + test -x "$vulkan_root/bin/dxc" + test -f "$vulkan_root/lib/libdxcompiler.so" + echo "VULKAN_SDK=$vulkan_root" >> "$GITHUB_ENV" + echo "$vulkan_root/bin" >> "$GITHUB_PATH" + echo "LD_LIBRARY_PATH=$vulkan_root/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" >> "$GITHUB_ENV" + + - name: Configure Vulkan SDK on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + $vulkanRoot = Join-Path $env:GITHUB_WORKSPACE ".eppo/tools/vulkan/$env:VULKAN_SDK_VERSION" + if (!(Test-Path (Join-Path $vulkanRoot "Bin/dxc.exe")) -or !(Test-Path (Join-Path $vulkanRoot "Bin/dxcompiler.dll"))) { + throw "The cached Vulkan SDK is incomplete." + } + "VULKAN_SDK=$vulkanRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + (Join-Path $vulkanRoot "Bin") | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Set up .NET 10 + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + dotnet-version: 10.0.x + + - name: Set up MSBuild + if: runner.os == 'Windows' + uses: microsoft/setup-msbuild@30375c66a4eea26614e0d39710365f22f8b0af57 # v3 + with: + vs-version: "[18.0,19.0)" + + - name: Provision pinned vcpkg on Linux + if: runner.os == 'Linux' + shell: bash + run: | + mkdir -p "$VCPKG_DOWNLOADS" + if [[ ! -x "$VCPKG_ROOT/vcpkg" ]]; then + mkdir -p "$(dirname "$VCPKG_ROOT")" + git init "$VCPKG_ROOT" + git -C "$VCPKG_ROOT" remote add origin https://github.com/microsoft/vcpkg.git + git -C "$VCPKG_ROOT" fetch --depth 1 origin "$VCPKG_COMMIT" + git -C "$VCPKG_ROOT" checkout --detach FETCH_HEAD + "$VCPKG_ROOT/bootstrap-vcpkg.sh" -disableMetrics + fi + test "$(git -C "$VCPKG_ROOT" rev-parse HEAD)" = "$VCPKG_COMMIT" + + - name: Provision pinned vcpkg on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + if (!(Test-Path (Join-Path $env:VCPKG_ROOT "vcpkg.exe"))) { + New-Item -ItemType Directory -Force (Split-Path $env:VCPKG_ROOT) | Out-Null + git init $env:VCPKG_ROOT + git -C $env:VCPKG_ROOT remote add origin https://github.com/microsoft/vcpkg.git + git -C $env:VCPKG_ROOT fetch --depth 1 origin $env:VCPKG_COMMIT + git -C $env:VCPKG_ROOT checkout --detach FETCH_HEAD + & (Join-Path $env:VCPKG_ROOT "bootstrap-vcpkg.bat") -disableMetrics + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + } + if ((git -C $env:VCPKG_ROOT rev-parse HEAD) -ne $env:VCPKG_COMMIT) { + throw "The cached vcpkg checkout is not the pinned commit." + } + + - name: Provision pinned build tools on Linux + if: runner.os == 'Linux' + shell: bash + run: python3 Scripts/Setup.py --action ninja --compiler clang --yes --skip-dependencies --no-generate + + - name: Provision pinned build tools on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + & .\Scripts\Setup.bat --action vs2026 --compiler msc --yes --skip-dependencies --no-generate + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + + - name: Configure GitHub Packages for vcpkg on Linux + if: runner.os == 'Linux' + shell: bash + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + nuget=$("$VCPKG_ROOT/vcpkg" fetch nuget | tail -n 1) + mono "$nuget" sources add \ + -Source "https://nuget.pkg.github.com/$GITHUB_REPOSITORY_OWNER/index.json" \ + -StorePasswordInClearText \ + -Name GitHubPackages \ + -UserName "$GITHUB_REPOSITORY_OWNER" \ + -Password "$GITHUB_TOKEN" \ + -NonInteractive + mono "$nuget" setapikey "$GITHUB_TOKEN" \ + -Source "https://nuget.pkg.github.com/$GITHUB_REPOSITORY_OWNER/index.json" \ + -NonInteractive + + - name: Configure GitHub Packages for vcpkg on Windows + if: runner.os == 'Windows' + shell: pwsh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + $vcpkg = Join-Path $env:VCPKG_ROOT "vcpkg.exe" + $nuget = (& $vcpkg fetch nuget | Select-Object -Last 1).Trim() + & $nuget sources add ` + -Source "https://nuget.pkg.github.com/$env:GITHUB_REPOSITORY_OWNER/index.json" ` + -StorePasswordInClearText ` + -Name GitHubPackages ` + -UserName $env:GITHUB_REPOSITORY_OWNER ` + -Password $env:GITHUB_TOKEN ` + -NonInteractive + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + & $nuget setapikey $env:GITHUB_TOKEN ` + -Source "https://nuget.pkg.github.com/$env:GITHUB_REPOSITORY_OWNER/index.json" ` + -NonInteractive + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + + - name: Generate build files on Linux + if: runner.os == 'Linux' + shell: bash + run: python3 Scripts/Setup.py --action ninja --compiler clang --yes + + - name: Generate build files on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + & .\Scripts\Setup.bat --action vs2026 --compiler msc --yes + if ($LASTEXITCODE -ne 0) { + exit $LASTEXITCODE + } + + - name: Build tests on Linux + if: runner.os == 'Linux' + shell: bash + run: ninja EppoEngineTesting_${{ matrix.configuration }}_x64 + + - name: Build tests on Windows + if: runner.os == 'Windows' + shell: pwsh + run: msbuild EppoEngine.slnx /restore /t:EppoEngineTesting /p:Configuration=${{ matrix.configuration }} /p:Platform=x64 /v:minimal + + - name: Run tests + run: >- + ctest + --test-dir build/bin/${{ matrix.configuration }}-${{ matrix.system }}-x86_64 + --label-exclude graphical + --output-on-failure + --output-junit build/bin/${{ matrix.configuration }}-${{ matrix.system }}-x86_64/report.xml + + - name: Upload test report + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: test-report-${{ matrix.system }}-${{ matrix.configuration }} + path: build/bin/${{ matrix.configuration }}-${{ matrix.system }}-x86_64/report.xml + if-no-files-found: ignore + retention-days: 30 + + - name: Remove transient vcpkg files from the build-tools cache + if: always() + shell: pwsh + run: | + $vcpkgRoot = $env:VCPKG_ROOT + foreach ($directory in @("buildtrees", "downloads", "packages")) { + $path = Join-Path $vcpkgRoot $directory + if (Test-Path $path) { + Remove-Item -LiteralPath $path -Recurse -Force + } + } diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index a88f1a94..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,73 +0,0 @@ -stages: - - build-image - - build-test - -workflow: - rules: - - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - - if: '$CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "develop"' - - if: '$CI_COMMIT_BRANCH =~ /^(feature|test)\//' - - when: never - -variables: - GIT_DEPTH: "1" - GIT_SUBMODULE_STRATEGY: none - VCPKG_BINARY_SOURCES: "clear;files,$CI_PROJECT_DIR/.vcpkg-cache,readwrite" - VCPKG_DISABLE_METRICS: "1" - DOCKER_IMAGE: "$CI_REGISTRY_IMAGE/ci:latest" - -build-ci-image: - stage: build-image - image: - name: gcr.io/kaniko-project/executor:debug - entrypoint: [""] - interruptible: true - rules: - - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - changes: [.gitlab/ci/Dockerfile] - - if: '$CI_COMMIT_BRANCH' - changes: [.gitlab/ci/Dockerfile] - - when: manual - allow_failure: true - script: - - mkdir -p /kaniko/.docker - - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(printf '%s:%s' "$CI_REGISTRY_USER" "$CI_REGISTRY_PASSWORD" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json - - >- - /kaniko/executor - --context "$CI_PROJECT_DIR" - --dockerfile "$CI_PROJECT_DIR/.gitlab/ci/Dockerfile" - --destination "$DOCKER_IMAGE" - --cache=true - -build-and-test: - stage: build-test - image: "$DOCKER_IMAGE" - interruptible: true - needs: - - job: build-ci-image - optional: true - cache: - - key: - files: - - vcpkg.json - - vcpkg-configuration.json - paths: - - .vcpkg-cache/ - policy: pull-push - before_script: - - mkdir -p .vcpkg-cache - - 'test -n "$VCPKG_ROOT" || { echo "VCPKG_ROOT missing from image"; exit 1; }' - - 'test -n "$VULKAN_SDK" || { echo "VULKAN_SDK missing from image"; exit 1; }' - - 'test -n "$DOTNET_ROOT" || { echo "DOTNET_ROOT missing from image"; exit 1; }' - - premake5 --version | grep '5.0.0-beta8' - script: - - python3 Scripts/Setup.py --action ninja --compiler clang --yes - - ninja EppoEngineTesting_Debug_x64 - - ctest --test-dir build/bin/Debug-linux-x86_64 --label-exclude graphical --output-on-failure --output-junit "$CI_PROJECT_DIR/build/bin/Debug-linux-x86_64/report.xml" - artifacts: - when: always - reports: - junit: build/bin/Debug-linux-x86_64/report.xml - paths: - - build/bin/Debug-linux-x86_64/report.xml - expire_in: 1 week diff --git a/.gitlab/ci/Dockerfile b/.gitlab/ci/Dockerfile deleted file mode 100644 index 0eb53b0e..00000000 --- a/.gitlab/ci/Dockerfile +++ /dev/null @@ -1,57 +0,0 @@ -FROM ubuntu:24.04 - -ENV DEBIAN_FRONTEND=noninteractive - -# Ubuntu distributes CTest in the cmake package; Eppo never invokes CMake to generate or build. -RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates curl git wget \ - clang lld llvm \ - cmake ninja-build make pkg-config uuid-dev \ - zip unzip tar xz-utils \ - python3 \ - autoconf automake autoconf-archive libtool libltdl-dev bison flex \ - libx11-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev \ - libxext-dev libwayland-dev libxkbcommon-dev wayland-protocols \ - libgl1-mesa-dev \ - && rm -rf /var/lib/apt/lists/* - -ARG PREMAKE_VERSION=5.0.0-beta8 -ARG PREMAKE_COMMIT=2ca338a25ed5f6e62d36c9cd70e5f313953c630d -RUN git clone --branch "v${PREMAKE_VERSION}" --depth 1 https://github.com/premake/premake-core.git /tmp/premake \ - && test "$(git -C /tmp/premake rev-parse HEAD)" = "${PREMAKE_COMMIT}" \ - && cd /tmp/premake \ - && CC=clang CXX=clang++ PREMAKE_OPTS=--cc=clang sh Bootstrap.sh \ - && install -m 0755 bin/release/premake5 /usr/local/bin/premake5 \ - && rm -rf /tmp/premake \ - && premake5 --version | grep "${PREMAKE_VERSION}" - -ARG VULKAN_SDK_VERSION=1.4.309.0 -RUN curl -fsSL "https://sdk.lunarg.com/sdk/download/${VULKAN_SDK_VERSION}/linux/vulkansdk-linux-x86_64-${VULKAN_SDK_VERSION}.tar.xz" \ - -o /tmp/vulkansdk.tar.xz \ - && mkdir -p /opt/vulkan \ - && tar -xf /tmp/vulkansdk.tar.xz -C /opt/vulkan \ - && rm /tmp/vulkansdk.tar.xz -ENV VULKAN_SDK=/opt/vulkan/${VULKAN_SDK_VERSION}/x86_64 -ENV PATH="${VULKAN_SDK}/bin:${PATH}" -ENV LD_LIBRARY_PATH="${VULKAN_SDK}/lib" - -ENV DOTNET_ROOT=/usr/share/dotnet -RUN curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh \ - && bash /tmp/dotnet-install.sh --channel 10.0 --install-dir "${DOTNET_ROOT}" \ - && rm /tmp/dotnet-install.sh -ENV PATH="${DOTNET_ROOT}:${PATH}" -ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 \ - DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 \ - DOTNET_NOLOGO=1 - -ENV VCPKG_ROOT=/opt/vcpkg -ARG VCPKG_COMMIT=8779f47d2db0785ee034b47fdfdcdb0e88b7010e -RUN git init "${VCPKG_ROOT}" \ - && git -C "${VCPKG_ROOT}" remote add origin https://github.com/microsoft/vcpkg.git \ - && git -C "${VCPKG_ROOT}" fetch --depth 1 origin "${VCPKG_COMMIT}" \ - && git -C "${VCPKG_ROOT}" checkout --detach FETCH_HEAD \ - && "${VCPKG_ROOT}/bootstrap-vcpkg.sh" -disableMetrics -ENV PATH="${VCPKG_ROOT}:${PATH}" \ - VCPKG_DISABLE_METRICS=1 \ - CC=clang \ - CXX=clang++ diff --git a/AGENTS.md b/AGENTS.md index f829b167..7d0a9122 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -148,4 +148,4 @@ Seven domain skills live in `.agents/skills/` (each `SKILL.md` + `references/arc ## CI -GitLab CI (`.gitlab-ci.yml`): runs on MRs, `master`, `develop`, `feature/*`, `test/*`. On Linux it generates Ninja with Premake beta8, builds only `EppoEngineTesting_Debug_x64`, runs `ctest --label-exclude graphical`, and publishes JUnit. The toolchain is baked into `.gitlab/ci/Dockerfile`; the vcpkg binary cache is keyed on `vcpkg.json` + `vcpkg-configuration.json`. Use `glab` CLI for MR operations. +GitHub Actions (`.github/workflows/ci.yml`) runs the `EppoEngineTesting` Debug, Release and Dist configurations on Linux and Windows for pull requests and pushes to `master` or `develop`, plus manual dispatches. Every job runs `ctest --label-exclude graphical` and retains its JUnit report for 30 days. The pinned Vulkan SDK, Premake and vcpkg tools are cached per host under `.eppo/tools`; vcpkg binary packages are restored from GitHub Packages through its NuGet provider. Pull requests read the package cache, while trusted pushes and manual runs can update it. diff --git a/EppoEngine/Source/Core/Base.h b/EppoEngine/Source/Core/Base.h index 309a7053..6505f2ba 100644 --- a/EppoEngine/Source/Core/Base.h +++ b/EppoEngine/Source/Core/Base.h @@ -4,6 +4,11 @@ #include +#if defined(EP_DIST) + #undef TracyFree + #define TracyFree(ptr) ((void)0) +#endif + #include #include diff --git a/EppoEngine/Source/Scripting/ScriptGlue.cpp b/EppoEngine/Source/Scripting/ScriptGlue.cpp index a87c5efb..cb2a7c5c 100644 --- a/EppoEngine/Source/Scripting/ScriptGlue.cpp +++ b/EppoEngine/Source/Scripting/ScriptGlue.cpp @@ -82,12 +82,12 @@ namespace Eppo } #pragma region Core - auto Input_IsKeyPressed(const uint16_t keyCode) -> bool + auto Input_IsKeyPressed(const uint16_t keyCode) -> uint8_t { return Input::IsKeyPressed(keyCode); } - auto Input_IsMouseButtonPressed(const uint16_t button) -> bool + auto Input_IsMouseButtonPressed(const uint16_t button) -> uint8_t { return Input::IsMouseButtonPressed(button); } @@ -175,7 +175,7 @@ namespace Eppo outHit->EntityId = static_cast(hit.EntityId); } - auto Physics_OverlapsSphere(const uint64_t id, const glm::vec3* center, const float radius) -> bool + auto Physics_OverlapsSphere(const uint64_t id, const glm::vec3* center, const float radius) -> uint8_t { const auto world = ScriptEngine::Get().GetActivePhysicsWorld(); if (!world) @@ -186,7 +186,7 @@ namespace Eppo #pragma endregion #pragma region Scene - auto Entity_HasComponent(const uint64_t id, const char* typeName) -> bool + auto Entity_HasComponent(const uint64_t id, const char* typeName) -> uint8_t { const Entity entity = GetEntity(id); if (!entity) @@ -254,7 +254,7 @@ namespace Eppo entity.TryAddComponent(); } - auto Entity_RemoveComponent(const uint64_t id, const char* typeName) -> bool + auto Entity_RemoveComponent(const uint64_t id, const char* typeName) -> uint8_t { Entity entity = GetEntity(id); if (!entity) @@ -446,7 +446,7 @@ namespace Eppo entity.GetComponent().MeshHandle = handle; } - auto CameraComponent_GetPrimary(const uint64_t id) -> bool + auto CameraComponent_GetPrimary(const uint64_t id) -> uint8_t { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) @@ -455,13 +455,13 @@ namespace Eppo return entity.GetComponent().Primary; } - auto CameraComponent_SetPrimary(const uint64_t id, const bool primary) -> void + auto CameraComponent_SetPrimary(const uint64_t id, const uint8_t primary) -> void { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) return; - entity.GetComponent().Primary = primary; + entity.GetComponent().Primary = primary != 0; } auto CameraComponent_GetVerticalFov(const uint64_t id) -> float @@ -706,7 +706,7 @@ namespace Eppo entity.GetComponent().AngularDamping = angularDamping; } - auto RigidBodyComponent_GetLockLinearX(const uint64_t id) -> bool + auto RigidBodyComponent_GetLockLinearX(const uint64_t id) -> uint8_t { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) @@ -715,16 +715,16 @@ namespace Eppo return entity.GetComponent().LockLinearX; } - auto RigidBodyComponent_SetLockLinearX(const uint64_t id, const bool locked) -> void + auto RigidBodyComponent_SetLockLinearX(const uint64_t id, const uint8_t locked) -> void { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) return; - entity.GetComponent().LockLinearX = locked; + entity.GetComponent().LockLinearX = locked != 0; } - auto RigidBodyComponent_GetLockLinearY(const uint64_t id) -> bool + auto RigidBodyComponent_GetLockLinearY(const uint64_t id) -> uint8_t { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) @@ -733,16 +733,16 @@ namespace Eppo return entity.GetComponent().LockLinearY; } - auto RigidBodyComponent_SetLockLinearY(const uint64_t id, const bool locked) -> void + auto RigidBodyComponent_SetLockLinearY(const uint64_t id, const uint8_t locked) -> void { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) return; - entity.GetComponent().LockLinearY = locked; + entity.GetComponent().LockLinearY = locked != 0; } - auto RigidBodyComponent_GetLockLinearZ(const uint64_t id) -> bool + auto RigidBodyComponent_GetLockLinearZ(const uint64_t id) -> uint8_t { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) @@ -751,16 +751,16 @@ namespace Eppo return entity.GetComponent().LockLinearZ; } - auto RigidBodyComponent_SetLockLinearZ(const uint64_t id, const bool locked) -> void + auto RigidBodyComponent_SetLockLinearZ(const uint64_t id, const uint8_t locked) -> void { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) return; - entity.GetComponent().LockLinearZ = locked; + entity.GetComponent().LockLinearZ = locked != 0; } - auto RigidBodyComponent_GetLockAngularX(const uint64_t id) -> bool + auto RigidBodyComponent_GetLockAngularX(const uint64_t id) -> uint8_t { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) @@ -769,16 +769,16 @@ namespace Eppo return entity.GetComponent().LockAngularX; } - auto RigidBodyComponent_SetLockAngularX(const uint64_t id, const bool locked) -> void + auto RigidBodyComponent_SetLockAngularX(const uint64_t id, const uint8_t locked) -> void { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) return; - entity.GetComponent().LockAngularX = locked; + entity.GetComponent().LockAngularX = locked != 0; } - auto RigidBodyComponent_GetLockAngularY(const uint64_t id) -> bool + auto RigidBodyComponent_GetLockAngularY(const uint64_t id) -> uint8_t { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) @@ -787,16 +787,16 @@ namespace Eppo return entity.GetComponent().LockAngularY; } - auto RigidBodyComponent_SetLockAngularY(const uint64_t id, const bool locked) -> void + auto RigidBodyComponent_SetLockAngularY(const uint64_t id, const uint8_t locked) -> void { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) return; - entity.GetComponent().LockAngularY = locked; + entity.GetComponent().LockAngularY = locked != 0; } - auto RigidBodyComponent_GetLockAngularZ(const uint64_t id) -> bool + auto RigidBodyComponent_GetLockAngularZ(const uint64_t id) -> uint8_t { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) @@ -805,13 +805,13 @@ namespace Eppo return entity.GetComponent().LockAngularZ; } - auto RigidBodyComponent_SetLockAngularZ(const uint64_t id, const bool locked) -> void + auto RigidBodyComponent_SetLockAngularZ(const uint64_t id, const uint8_t locked) -> void { const Entity entity = GetEntity(id); if (!entity || !entity.HasComponent()) return; - entity.GetComponent().LockAngularZ = locked; + entity.GetComponent().LockAngularZ = locked != 0; } auto BoxColliderComponent_GetHalfSize(const uint64_t id, glm::vec3* outHalfSize) -> void diff --git a/EppoEngineTesting/Source/Core/Timer.cpp b/EppoEngineTesting/Source/Core/Timer.cpp index 5ec218cf..89292620 100644 --- a/EppoEngineTesting/Source/Core/Timer.cpp +++ b/EppoEngineTesting/Source/Core/Timer.cpp @@ -8,6 +8,7 @@ TEST(Core, Timer_StartsOnConstruction) using namespace std::chrono; Timer timer; + std::this_thread::sleep_for(2ms); EXPECT_LT(0, timer.GetElapsedMilliseconds()); auto elapsed = timer.GetElapsedMilliseconds(); diff --git a/EppoEngineTesting/Source/Scripting/Scripting.cpp b/EppoEngineTesting/Source/Scripting/Scripting.cpp index 13f11829..f2b538e6 100644 --- a/EppoEngineTesting/Source/Scripting/Scripting.cpp +++ b/EppoEngineTesting/Source/Scripting/Scripting.cpp @@ -1831,6 +1831,34 @@ TEST(Scripting, RigidBodyComponent_AngularDamping_RoundTripsSceneValue) engine.OnDestroyEntity(entity); } +TEST(Scripting, RigidBodyComponent_MotionLockGetters_FalseValuesMarshalAcrossNativeBoundary) +{ + EP_REQUIRE(EnsureRuntime()); + + const Ref scene = CreateRef(); + auto& engine = ScriptEngine::Get(); + Entity entity = MakeContextEntity(scene); + entity.AddComponent(); + + const ScriptClass* c = FindClass(kUserClass); + EP_REQUIRE(c != nullptr); + + const char* getters[] = { + "RigidBodyComponent_GetLockLinearX", "RigidBodyComponent_GetLockLinearY", "RigidBodyComponent_GetLockLinearZ", + "RigidBodyComponent_GetLockAngularX", "RigidBodyComponent_GetLockAngularY", "RigidBodyComponent_GetLockAngularZ", + }; + for (const char* name : getters) + { + const ScriptMethod* getter = c->GetMethod(name); + EP_REQUIRE(getter != nullptr); + bool got = true; + c->InvokeMethod(entity, *getter, nullptr, &got); + EXPECT_FALSE(got); + } + + engine.OnDestroyEntity(entity); +} + TEST(Scripting, RigidBodyComponent_MotionLocks_RoundTripSceneValues) { EP_REQUIRE(EnsureRuntime()); diff --git a/EppoScriptCore/Source/Core/InternalCalls.cs b/EppoScriptCore/Source/Core/InternalCalls.cs index 1520e8f0..f46eb37f 100644 --- a/EppoScriptCore/Source/Core/InternalCalls.cs +++ b/EppoScriptCore/Source/Core/InternalCalls.cs @@ -245,12 +245,12 @@ internal static void MeshComponent_SetMeshHandle(ulong id, ulong meshHandle) internal static bool CameraComponent_GetPrimary(ulong id) { - return ((delegate* unmanaged[Cdecl])Get("CameraComponent_GetPrimary"))(id); + return ((delegate* unmanaged[Cdecl])Get("CameraComponent_GetPrimary"))(id) != 0; } internal static void CameraComponent_SetPrimary(ulong id, bool primary) { - ((delegate* unmanaged[Cdecl])Get("CameraComponent_SetPrimary"))(id, primary); + ((delegate* unmanaged[Cdecl])Get("CameraComponent_SetPrimary"))(id, primary ? (byte)1 : (byte)0); } internal static float CameraComponent_GetVerticalFov(ulong id) @@ -390,62 +390,62 @@ internal static void RigidBodyComponent_SetAngularDamping(ulong id, float angula internal static bool RigidBodyComponent_GetLockLinearX(ulong id) { - return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockLinearX"))(id); + return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockLinearX"))(id) != 0; } internal static void RigidBodyComponent_SetLockLinearX(ulong id, bool locked) { - ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockLinearX"))(id, locked); + ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockLinearX"))(id, locked ? (byte)1 : (byte)0); } internal static bool RigidBodyComponent_GetLockLinearY(ulong id) { - return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockLinearY"))(id); + return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockLinearY"))(id) != 0; } internal static void RigidBodyComponent_SetLockLinearY(ulong id, bool locked) { - ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockLinearY"))(id, locked); + ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockLinearY"))(id, locked ? (byte)1 : (byte)0); } internal static bool RigidBodyComponent_GetLockLinearZ(ulong id) { - return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockLinearZ"))(id); + return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockLinearZ"))(id) != 0; } internal static void RigidBodyComponent_SetLockLinearZ(ulong id, bool locked) { - ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockLinearZ"))(id, locked); + ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockLinearZ"))(id, locked ? (byte)1 : (byte)0); } internal static bool RigidBodyComponent_GetLockAngularX(ulong id) { - return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockAngularX"))(id); + return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockAngularX"))(id) != 0; } internal static void RigidBodyComponent_SetLockAngularX(ulong id, bool locked) { - ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockAngularX"))(id, locked); + ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockAngularX"))(id, locked ? (byte)1 : (byte)0); } internal static bool RigidBodyComponent_GetLockAngularY(ulong id) { - return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockAngularY"))(id); + return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockAngularY"))(id) != 0; } internal static void RigidBodyComponent_SetLockAngularY(ulong id, bool locked) { - ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockAngularY"))(id, locked); + ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockAngularY"))(id, locked ? (byte)1 : (byte)0); } internal static bool RigidBodyComponent_GetLockAngularZ(ulong id) { - return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockAngularZ"))(id); + return ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_GetLockAngularZ"))(id) != 0; } internal static void RigidBodyComponent_SetLockAngularZ(ulong id, bool locked) { - ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockAngularZ"))(id, locked); + ((delegate* unmanaged[Cdecl])Get("RigidBodyComponent_SetLockAngularZ"))(id, locked ? (byte)1 : (byte)0); } internal static Vector3 BoxColliderComponent_GetHalfSize(ulong id)