diff --git a/.github/adr_template.md b/.github/adr_template.md new file mode 100644 index 00000000..5f0a5f01 --- /dev/null +++ b/.github/adr_template.md @@ -0,0 +1,81 @@ +--- +id: ADR-XXXX +title: Short, descriptive title of the decision +date: YYYY-MM-DD +status: proposed # proposed | accepted | rejected | deprecated | superseded +authors: + - Author Name +deciders: + - Decider Name +--- + + +# ADR-XXXX: [Short, descriptive title of the decision] + +![Proposed][badge-proposed] + + +*(If superseded, link to the new ADR: "Superseded by [ADR-YYYY](path/to/adr-yyyy.md)")* + +## Context + +Describe the problem space, the current technical constraints, and the forces at play. + +- What is the issue we are trying to solve? +- Why does it need to be solved now? +- Are there performance, architectural, or organizational constraints? + +## Decision + +State the clear, actionable architectural decision. + +- What are we choosing to do? +- Be precise and concrete. If this introduces a new pattern, tool, or folder structure, describe it + exactly. + +## Alternatives Considered + +Briefly document other approaches that were evaluated and why they were ultimately rejected. + +### Option A: [Short description of the alternative] + +Explain the alternative. + +**Why rejected:** Describe the reasons this option was not chosen. Focus on trade-offs, not just +personal preference. + +### Option B: [Short description of the alternative] + +Explain the alternative. + +**Why rejected:** Describe the reasons this option was not chosen. Focus on trade-offs, not just +personal preference. + +## Consequences + +List the direct outcomes of applying this decision. Focus on trade-offs. + +### Positive + +- (e.g., Reduces cross-module coupling) +- (e.g., Improves rendering thread performance by 15%) + +### Negative + +- (e.g., Increases initial boilerplate for creating new modules) +- (e.g., Requires migrating X legacy systems to the new API) + +## Rationale and Cross-References + +- [ADR-YYYY](./ADR-YYYY.md): Link to related ADRs that influenced this decision. + +**Canonical references:** + +- Other projects, patterns, or literature that influenced this decision. + + +[badge-proposed]: https://img.shields.io/badge/Status-Proposed-yellow.svg +[badge-accepted]: https://img.shields.io/badge/Status-Accepted-brightgreen.svg +[badge-rejected]: https://img.shields.io/badge/Status-Rejected-red.svg +[badge-deprecated]: https://img.shields.io/badge/Status-Deprecated-lightgrey.svg +[badge-superseded]: https://img.shields.io/badge/Status-Superseded-lightgrey.svg diff --git a/.github/assets/code-of-conduct.svg b/.github/assets/code-of-conduct.svg deleted file mode 100644 index 88f1dc33..00000000 --- a/.github/assets/code-of-conduct.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/assets/contribution-guidelines.svg b/.github/assets/contribution-guidelines.svg deleted file mode 100644 index 492b61ba..00000000 --- a/.github/assets/contribution-guidelines.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/assets/donors.svg b/.github/assets/donors.svg deleted file mode 100644 index 59e82aa2..00000000 --- a/.github/assets/donors.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/assets/repository-title.svg b/.github/assets/repository-title.svg deleted file mode 100644 index 20f5a365..00000000 --- a/.github/assets/repository-title.svg +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/assets/security-guidelines.svg b/.github/assets/security-guidelines.svg deleted file mode 100644 index c82a3d0a..00000000 --- a/.github/assets/security-guidelines.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml index fb5dbc34..5f6bfeb7 100644 --- a/.github/workflows/sync-docs.yml +++ b/.github/workflows/sync-docs.yml @@ -43,11 +43,15 @@ jobs: rm -rf gp-docs/docs/gp-engine mkdir -p gp-docs/docs/gp-engine cd gp-engine + + # copy docs from gp-build-tool if it exists mkdir -p 'docs/Programming With C++/GP Build Tool' if [ -d "cmake/gp-build-tool/docs" ]; then cp -r cmake/gp-build-tool/docs/* 'docs/Programming With C++/GP Build Tool/' fi - find . -type f -path "*/docs/*" -not -path "./thirdparty/*" -not -path "./cmake/*" -print0 | while IFS= read -r -d '' file; do + + # copy docs from gp-engine to gp-docs (excluding thirdparty, cmake and adrs) + find . -type f -path "*/docs/*" -not -path "./thirdparty/*" -not -path "./cmake/*" -not -path "*/docs/adr/*" -print0 | while IFS= read -r -d '' file; do rel_path="${file#./}" dest_path=$(echo "$rel_path" | sed -e 's|^docs/||' -e 's|/docs/|/|g') target_file="../gp-docs/docs/gp-engine/$dest_path" diff --git a/.markdownlint.jsonc b/.markdownlint.jsonc new file mode 100644 index 00000000..771d8c03 --- /dev/null +++ b/.markdownlint.jsonc @@ -0,0 +1,7 @@ +{ + "default": true, + "MD013": { + "line_length": 100 + }, + "no-inline-html": false +} diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 00000000..888a6498 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1,3 @@ +binaries/ +build/ +thirdparty/ diff --git a/.sanitizers/asan.supp b/.sanitizers/asan.supp new file mode 100644 index 00000000..3e135dc2 --- /dev/null +++ b/.sanitizers/asan.supp @@ -0,0 +1 @@ +# AddressSanitizer suppressions for known issues in external libraries. diff --git a/.sanitizers/lsan.supp b/.sanitizers/lsan.supp new file mode 100644 index 00000000..1bf71404 --- /dev/null +++ b/.sanitizers/lsan.supp @@ -0,0 +1 @@ +# LeakSanitizer suppressions for known memory leaks in external libraries. diff --git a/.sanitizers/sanitizer.ignorelist b/.sanitizers/sanitizer.ignorelist new file mode 100644 index 00000000..e2f07276 --- /dev/null +++ b/.sanitizers/sanitizer.ignorelist @@ -0,0 +1 @@ +# Sanitizer instrumentation exclusions for code that should not be instrumented. diff --git a/.sanitizers/tsan.supp b/.sanitizers/tsan.supp new file mode 100644 index 00000000..cbccc53c --- /dev/null +++ b/.sanitizers/tsan.supp @@ -0,0 +1 @@ +# ThreadSanitizer suppressions for known issues in external libraries. diff --git a/.sanitizers/ubsan.supp b/.sanitizers/ubsan.supp new file mode 100644 index 00000000..bf45eaef --- /dev/null +++ b/.sanitizers/ubsan.supp @@ -0,0 +1 @@ +# UndefinedBehaviorSanitizer suppressions for known issues in external libraries. diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 3f6f7591..9b2ddeb6 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -9,6 +9,7 @@ "matepek.vscode-catch2-test-adapter", "Gruntfuggly.bettercomment", "ms-vscode.cmake-tools", + "KylinIdeTeam.cmake-intellisence", "cschlosser.doxdocgen", /* Shaders */ @@ -23,7 +24,10 @@ /* Github */ "GitHub.vscode-pull-request-github", "github.vscode-github-actions", - "me-dutour-mathieu.vscode-github-actions" + "me-dutour-mathieu.vscode-github-actions", + + /* Other */ + "FanaticPythoner.better-todo-tree" ], "unwantedRecommendations": [ /* Conflicts with Clangd */ diff --git a/.vscode/gp-engine.code-snippets b/.vscode/gp-engine.code-snippets index d5b0901d..7edb6460 100644 --- a/.vscode/gp-engine.code-snippets +++ b/.vscode/gp-engine.code-snippets @@ -5,7 +5,7 @@ "include": ["**/*.hpp", "**/*.hxx", "**/*.hh", "**/*.h", "**/*.inl", "**/*.inc"], "body": [ "// Copyright (c) - Graphical Playground. All rights reserved.", - "// For more information, see https://graphical-playground/legal", + "// For more information, see https://graphical-playground.com/legal", "// mailto:support AT graphical-playground DOT com", "", "#pragma once", @@ -19,7 +19,7 @@ "prefix": "gpl", "body": [ "// Copyright (c) - Graphical Playground. All rights reserved.", - "// For more information, see https://graphical-playground/legal", + "// For more information, see https://graphical-playground.com/legal", "// mailto:support AT graphical-playground DOT com", "" ], @@ -31,7 +31,7 @@ "prefix": "gpl", "body": [ "# Copyright (c) - Graphical Playground. All rights reserved.", - "# For more information, see https://graphical-playground/legal", + "# For more information, see https://graphical-playground.com/legal", "# mailto:support AT graphical-playground DOT com", "", "include(gp-build-tool)", @@ -45,7 +45,7 @@ "prefix": "gpl", "body": [ "// Copyright (c) - Graphical Playground. All rights reserved.", - "// For more information, see https://graphical-playground/legal", + "// For more information, see https://graphical-playground.com/legal", "// mailto:support AT graphical-playground DOT com", "" ], @@ -58,7 +58,7 @@ "body": [ "#!/bin/bash", "# Copyright (c) - Graphical Playground. All rights reserved.", - "# For more information, see https://graphical-playground/legal", + "# For more information, see https://graphical-playground.com/legal", "# mailto:support AT graphical-playground DOT com", "" ], diff --git a/CMakeLists.txt b/CMakeLists.txt index 2cbe048d..4324e308 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ project(GraphicalPlayground VERSION 0.1.0 LANGUAGES CXX C DESCRIPTION "Graphical Playground Engine, learning and prototyping platform for graphics programming." + HOMEPAGE_URL "https://graphical-playground.com" ) # Add an option to allow users to skip the forced update (e.g., if they are developing the build tool locally) @@ -39,6 +40,9 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/gp-build-tool/s include(gp-build-tool) # Apply the default policies and configurations for Graphical Playground targets. +# This sets all the necessary CMAKE_CXX flags (C++23, Standard required, etc.) +# This also configures the default binary output directories for all targets. +# It enforces PIC for all targets, set the default testing framework to GoogleTest and the LIBCXX for all linux targets. gpApplyGraphicalPlaygroundDefaultPolicy() # Start the GPBT build tool and auto-scan the source directory for targets to build. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d04720b1..560d113a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -124,10 +124,10 @@ extension will automatically build a ready-to-use C++23/Clang 22 environment. The build process is orchestrated by our custom `gp-build-tool`, which is integrated as a Git -submodule ([GraphicalPlayground/gp-build-tool](https://github.com/GraphicalPlayground/gp-build-tool)). +submodule ([GraphicalPlayground/gp-build-tool][gpbt]).
-

Windows

+

Windows Windows

#### 0. Prerequisites @@ -229,7 +229,7 @@ the [GP Build Tool Configuration Guide][gpbt-config-guide].
-

Linux

+

Linux Linux

#### 1. Cloning the Repository @@ -302,7 +302,7 @@ the [GP Build Tool Configuration Guide][gpbt-config-guide].
-

MacOS

+

Apple MacOS

#### 1. Cloning the Repository @@ -477,7 +477,56 @@ _wip..._ ### Directory Structure -_wip..._ +As a pedagogical engine meant to help you learn AAA game engine design, the `gp-engine` repository +is organized to clearly reflect a modern, scalable engine architecture. Our directory layout +separates core engine code, platform-specific layers, tools, and build configurations to keep the +learning curve manageable while exposing you to industry-standard project organization. + +Below is an overview of the main directories and their roles: + +**Principal Directories:** + +- `/source/`: The heart of the engine. This is where all C++ source code, headers, and core + logic live. + - `/runtime/`: Contains the core engine systems used at runtime (e.g., `/core/`, `/rhi/`, + `/renderer/`, `/physics/`, `/audio/`). Exploring this directory gives you a deep dive into how + an engine ticks frame-by-frame. + - `/launch/`: Contains the entry points for the engine applications. It is split into targets + like `/editor/` (the authoring tool) and `/standalone/` (the packaged game executable). This + demonstrates how engines separate their development tools from the final shipped product. + - `/shaders/`: Houses all HLSL/GLSL shader code. We separate `/public/` + (shared interfaces/includes) from `/private/` (actual shader implementations) to teach proper + shader resource management and encapsulation. + - `/plugins/`: An ecosystem for extensible engine modules. We use this to demonstrate how to build + a modular architecture where features can be loaded or unloaded without modifying the core + `/runtime/`. +- `/examples/`: Practical, stripped-down examples and sample projects. These are designed to isolate + specific engine features (like a rendering pass or an input system) so you can study them without + being overwhelmed by the entire engine context. +- `/cmake/`: Contains the [`gp-build-tool`][gpbt] git submodule, orchestrating our robust build + process. This exposes you to advanced, modular CMake practices used in large-scale C++ projects. +- `/toolchain/`: Scripts and configuration files for setting up the development environment + across platforms (Windows, Linux, MacOS). This includes CMake presets to guarantee a unified + build experience. +- `/thirdparty/`: Contains CMake scripts and licenses for external dependencies (e.g., SDL3, + Vulkan headers). The actual source code is fetched automatically by the GPBT, teaching you modern + dependency management that avoids repository bloat. +- `/docs/`: High-level guides, architectural overviews, and tutorials. Scattered `docs/` folders + throughout the repository dive into specific modules. All these are aggregated into our + [documentation website](https://docs.graphical-playground.com/docs/gp-engine/Introduction). +- `/.devcontainer/`: Configuration for a Docker-based Devcontainer. It provides a standardized, + pre-configured development environment, ensuring every contributor has the same toolchain + instantly. +- `/.github/`: GitHub Actions workflows for our CI/CD pipeline, PR templates, and issue tracking. +- `/translations/`: Multilingual support for our documentation, making the pedagogical resources + accessible to a broader global audience. +- `/.vscode/`: Recommended workspace settings, tasks, and extensions for Visual Studio Code to + enforce coding styles and streamline debugging. + +> [!TIP] +> You can read the article ["Engine Architecture and Directory Layout: A Principal Engineer's Guide"](https://docs.graphical-playground.com/blog/engine-architecture-layout) +> for a deeper dive into the reasoning behind our directory structure and how it reflects modern AAA +> engine design principles. ## Development Workflow @@ -485,11 +534,59 @@ _wip..._ ### Branching Strategy -_wip..._ +Our repository follows a structured branching model to ensure stability and smooth collaboration. + +**Main Branches:** + +- `main`: This is the current stable development branch. +- `dev`: This is the current experimental branch where active integration happens. + +**Release Branches:** + +- `release-`: We use specific branches for major releases (e.g., `release-1.0.0`), + isolating them for final polishing and bug fixes. + +**Working Branches:** + +- **Personal Prefixes:** Everyone works on their own branches, which must be prefixed with the + author's initials. For example: + - `ms/...` (Mallory Scotton) + - `hc/...` (Hugo Cathelain) + - `nf/...` (Nathan Fievet) +- **One Feature Per Branch:** Keep your work focused. Each branch should encompass a single feature + or fix. +- **Automated Workflows:** Before a pull request can be merged, all CI/CD workflows (formatting, + build, tags, etc.) must pass successfully. +- **Cleanup:** Once merged, working branches are deleted automatically. ### Commit Message Guidelines -_wip..._ +We follow a structured convention for commit messages to ensure a clear and well-documented project +history. + +**General Rules:** + +- **Prefer using lower case** for the commit message subject. +- **Use a prefix** to indicate the type of commit (e.g., `add`, `update`, `chore`, `fix`, `hotfix`, + `bug`, `docs`, etc.). +- **Sub-categories (Optional):** You can add a specific sub-category or scope in parentheses to + provide more context. For example: `chore(format): ...`. +- **Be Explicit:** The commit message should explicitly state what the commit is actually + implementing. +- **Small, Atomic Commits:** Prefer doing multiple focused commits instead of one giant one. +- **Co-authors:** If someone helps you with a commit or code, think about adding them as a + co-author if it's relevant (e.g., `Co-authored-by: Name ` at the end of the + commit message body). + +**Classic Commit Guidelines:** + +- Separate the subject line from the body with a blank line. +- Limit the subject line to 50-72 characters. +- Do not end the subject line with a period. +- Use the imperative mood in the subject line (e.g., `add core rendering module`, not `added` or + `adds`). +- Wrap the body text at 72 characters. +- Use the body to explain _what_ you did and _why_, rather than _how_ you did it. ### Pull Request Process @@ -578,7 +675,9 @@ You can sponsor the Graphical Playground project through the following links: - [**Buy Me A Coffee**](https://www.buymeacoffee.com/GraphicalPlayground) - [**GitHub Sponsors**](https://github.com/sponsors/GraphicalPlayground) -- [**Direct Donation**](https://graphical-playground.com/donate) +- [**Open Collective**](https://opencollective.com/graphical-playground) +- [**Thanks Dev**](https://thanks.dev/u/gh/GraphicalPlayground) +- [**Direct donation**](https://graphical-playground.com/donate) --- @@ -588,3 +687,5 @@ _Thank you for being a part of the Graphical Playground. We can't wait to see wh © 2026 Graphical Playground. Built for the next generation of graphics engineers. ![Graphical Playground](https://github.com/GraphicalPlayground/.github/blob/main/assets/misc/gplayd-footer.svg) + +[gpbt]: https://github.com/GraphicalPlayground/gp-build-tool diff --git a/REFERENCES.md b/REFERENCES.md new file mode 100644 index 00000000..250d0d12 --- /dev/null +++ b/REFERENCES.md @@ -0,0 +1,186 @@ + + +![Graphical Playground - References & Acknowledgments](https://github.com/GraphicalPlayground/.github/blob/main/assets/banners/gplayd-references.svg) + +**Table of content** +[Overview](#overview) +[Books & Literature](#books--literature) +[Conferences & Presentations](#conferences--presentations) +[Open Source Projects & Engines](#open-source-projects--engines) +[Tutorials & Articles](#tutorials--articles) +[Official Documentation](#official-documentation) +[People & Inspirations](#people--inspirations) + +## Overview + +Welcome to the `gp-engine` references and acknowledgments page. Building a pedagogical, AAA-oriented +game engine from scratch is a monumental task, and we stand on the shoulders of giants. This +document exists to acknowledge the invaluable resources, brilliant engineers, and open-source +projects that have inspired our design decisions, architecture, and educational approach. + +If you are a student or a contributor, we highly recommend exploring these resources to deepen +your understanding of graphics engineering and game engine architecture. + +## Books & Literature + +_Foundational textbooks, architectural bibles, and advanced rendering literature._ + + + + + + + + + + + + + + + + + + +
+ Game Engine Architecture, Fourth Edition + +

Game Engine Architecture, Fourth Edition

+

by Jason Gregory

+

+ Jason Gregory's deep dive into engine internals is unmatched. It's the most comprehensive + resource we've found for understanding the plumbing of a modern game engine. +

+ + Buy on Amazon + +
+ Foundations of Game Engine Development, Volume 1: Mathematics + +

Foundations of Game Engine Development, Volume 1: Mathematics

+

by Eric Lengyel

+

+ A very focused look at the math and rendering theory needed for engine development. Volume + 1 is all about the linear algebra and geometry, while Volume 2 applies it to the GPU + pipeline. +

+ + Buy on Amazon + +
+ Foundations of Game Engine Development, Volume 2: Rendering + +

Foundations of Game Engine Development, Volume 2: Rendering

+

by Eric Lengyel

+

+ A very focused look at the math and rendering theory needed for engine development. Volume + 1 is all about the linear algebra and geometry, while Volume 2 applies it to the GPU + pipeline. +

+ + Buy on Amazon + +
+ Real-Time Rendering, Fourth Edition + +

Real-Time Rendering, Fourth Edition

+

by + Tomas Akenine-Möller, + Eric Haines, + Naty Hoffman, + Angelo Pesce, + Michal Iwanicki, + Sébastien Hillaire +

+

+ The definitive guide for any graphics programmer. It covers the entire pipeline with + incredible detail, and we still find ourselves reaching for it constantly. If you only pick + up one book, this should be it. +

+ + Buy on Amazon + +
+ +## Conferences & Presentations + +_GDC, SIGGRAPH, CppCon, and other industry talks that shaped our technical decisions._ + +- **[Talk Title]** ([Year]) by [Speaker] at [Conference] + - _Focus:_ [e.g., "Render Graph architecture and frame graph optimizations."] + - _Link:_ [YouTube/PDF Link] + +## Open Source Projects & Engines + +_Codebases we’ve studied, integrated, or taken architectural inspiration from._ + +- **[Project/Engine Name]** + - _Inspiration:_ [e.g., "Inspired our modular plugin system and Entity Component System + (ECS) design."] + - _Link:_ [GitHub/Website] + +## Tutorials & Articles + +_Blogs, series, and independent articles that break down complex topics._ + +- **[Article/Series Name]** by [Author] + - _Topic:_ [e.g., "Vulkan synchronization and barrier management."] + - _Link:_ [URL] + +## Official Documentation + +_The specification documents and API references we rely on daily._ + +- **[API/Tool Name] Specification** + - _Usage:_ [e.g., "The core graphics API specification used for our RHI (Render Hardware + Interface)."] + - _Link:_ [URL] + +## People & Inspirations + +_Individuals whose work, mentorship, or public sharing of knowledge have significantly impacted +this project._ + +- **[Name]** + - _Contribution/Influence:_ [e.g., "For their continuous work on democratizing graphics + programming education."] + - _Links:_ [Twitter/GitHub/Blog] + +--- + +> [!TIP] +> If you notice a missing reference or want to suggest a resource that aligns with our pedagogical +> goals, please open a pull request! + +--- +© 2026 Graphical Playground. Built for the next generation of graphics engineers. + +![Graphical Playground](https://github.com/GraphicalPlayground/.github/blob/main/assets/misc/gplayd-footer.svg) diff --git a/VERSION b/VERSION index afaf360d..be0aef56 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 \ No newline at end of file +1.0.0-alpha \ No newline at end of file diff --git a/cmake/gp-build-tool b/cmake/gp-build-tool index 3d5e6477..27bb37de 160000 --- a/cmake/gp-build-tool +++ b/cmake/gp-build-tool @@ -1 +1 @@ -Subproject commit 3d5e6477933b99fe781a9c1e25080c8672b549a9 +Subproject commit 27bb37de33eb3edff562b657fc350423bb6b2239 diff --git a/docs/adr/ADR-0001.md b/docs/adr/ADR-0001.md new file mode 100644 index 00000000..f3a82d79 --- /dev/null +++ b/docs/adr/ADR-0001.md @@ -0,0 +1,140 @@ +--- +id: ADR-0001 +title: Adoption of Public/Private/Internal Directory Discipline for Modules +date: 2026-05-08 +status: accepted +deciders: + - mallory-scotton +authors: + - mallory-scotton +--- + + +# ADR-0001: Adoption of Public/Private/Internal Directory Discipline for Modules + +![Accepted][badge-accepted] + +## Context + +As a C++ game engine scales, managing header dependencies becomes increasingly difficult. Without +strict physical boundaries, engine subsystems become tightly coupled, a "big ball of mud", leading +to exponential increases in compile times, circular dependencies, and making isolated refactors +nearly impossible. We needed a way to make **architectural rules hard constraints** within the +source tree so that developers intuitively understand what is safe to depend on, and so that +violating those rules requires deliberate effort rather than being the path of least resistance. + +A naïve single-directory-per-module layout (all headers flat at the module root) gives no signal +about intended exposure. The compiler sees everything; a developer reaching across a private boundary +has no indication they are doing something architecturally wrong. + +The guiding principle: **a folder named `private/` is a contract**. Nothing outside the module may +include from there. + +## Decision + +Every module in the GP Engine enforces a strict three-tier visibility discipline, directly inspired +by the `Public/Private/Internal` layout pioneered by Unreal Engine 5, and mechanically enforced by +our build tool ([ADR-0003](./ADR-0003.md)): + + +| Tier | Directory | Include scope | Propagated to consumers? | +| --- | --- | --- | --- | +| **Public API** | `public/` | Any module that declares a dependency | Yes, auto-added to consumer's include path | +| **Private implementation** | `private/` | This module only | No | +| **Internal contract** | `internal/` *(optional)* | An explicit allowlist of "friend" modules | No, must be explicitly granted | + + +Concretely, when GPBT configures a module `M`, the compiler sees: + +```cmake +# For M itself during compilation: +-I .../M/public (PUBLIC: propagated) +-I .../M/private (PRIVATE: this build only) +-I .../M/internal (PRIVATE: this build only, but shared with explicit friends) + +# For any module that declares a dependency on M: +-I .../M/public (only: never private or internal) +``` + +The `internal/` tier exists to resolve the awkward middle case: headers that must be shared between +two closely related modules (e.g., the binary contract between `core` and `engine` for memory-budget +reporting) without being part of the public SDK surface. A consumer that did not declare itself as an +explicit internal friend cannot include from `internal/`. + +## Alternatives Considered + +### Option A: Flat header directory at the module root (no `public/private` split) + +All headers sit at the module root; the compiler and linker do not distinguish public from private. +This is the dominant pattern in Strategy-A (flat + prefix) and Strategy-B (subsystem dirs) engines, +Quake, Doom 3, Cocos2d-x, and Godot all use this approach. + +**Why rejected:** Provides no mechanical enforcement. A developer who includes +`../../audio/FooInternal.hpp` from a renderer module gets no compile error; the violation is +invisible until a code review catches it (or does not). Over hundreds of modules and years of +development, architectural debt accumulates faster than code review can stop it. + +### Option B: Two-tier split (`include/` + `src/`), standard C++ project convention + +The conventional C++ layout uses `include//` for public headers and `src/` for everything +else. Used by most CMake-based OSS projects. + +**Why rejected:** No native support for the `internal/` tier. Encoding the internal/public +distinction in a single flat `include/` tree requires naming conventions (e.g., `_internal.hpp` +suffix), which have the same enforcement problem as Option A. Also breaks the symmetry with our +module anatomy (every module must be a self-contained unit with its own `CMakeLists.txt`; a shared +root `include/` folder breaks that invariant). + +### Option C: Unreal Engine's exact `Public/Private/Internal` (PascalCase) + +Adopt UE's exact convention, including PascalCase directory names. + +**Why rejected:** Inconsistent with our choice to use `lower-kebab-case` for all directories (see +[ADR-0005](./ADR-0005.md)). We adopt the semantics of UE's three-tier split, not the capitalization +convention. + +## Consequences + +### Positive + +- **Enforces encapsulation at the file-system level.** A build failure is the signal for a boundary + violation, not a code review finding weeks later. +- **Reduces header-inclusion cascades.** A non-public change inside `private/` cannot trigger + recompilation of any consumer. This directly translates to faster incremental builds. +- **Makes dependency intent explicit.** Reading a module's `CMakeLists.txt` reveals exactly which + headers it exposes and to whom, in under 30 lines. +- **Educational clarity.** A student who opens any module immediately understands the three zones and + knows where to look for the API vs. the implementation. +- **Enables `internal/` for trusted sub-graphs.** Without the third tier, closely related modules + either over-expose (polluting `public/`) or under-expose (duplicating code). + +### Negative + +- **Navigation overhead.** Developers must descend one more level to reach source files. +- **Discipline required.** The build system enforces visibility mechanically; choosing *what belongs + in `public/`* still requires human judgment. A poorly disciplined `public/` that leaks + implementation types is not prevented by the tooling. +- **`internal/` allowlist must be maintained.** Granting internal access to a new module is a + deliberate step; this is a feature, but it means one more thing to update when restructuring. + +## Rationale and Cross-References + +This decision is the **foundational layout primitive** of the GP Engine. Every subsequent layout +decision either references it or follows from it: + +- [ADR-0003](./ADR-0003.md): GPBT enforces this at build time via + `gpAddDependency(PUBLIC|PRIVATE|INTERNAL ...)`. +- [ADR-0004](./ADR-0004.md): Lifecycle directories apply the same separation at the top-level + `source/` tree. +- [ADR-0006](./ADR-0006.md): Co-located `tests/` and `benchmarks/` directories are siblings of + `public/` and `private/`, not mixed in. + +**Canonical reference:** Unreal Engine 5 Source Tree, +`Engine/Source/Runtime/Core/{Public,Private,Internal}/`. + + +[badge-proposed]: https://img.shields.io/badge/Status-Proposed-yellow.svg +[badge-accepted]: https://img.shields.io/badge/Status-Accepted-brightgreen.svg +[badge-rejected]: https://img.shields.io/badge/Status-Rejected-red.svg +[badge-deprecated]: https://img.shields.io/badge/Status-Deprecated-lightgrey.svg +[badge-superseded]: https://img.shields.io/badge/Status-Superseded-lightgrey.svg diff --git a/docs/adr/ADR-0002.md b/docs/adr/ADR-0002.md new file mode 100644 index 00000000..bf3aa9ca --- /dev/null +++ b/docs/adr/ADR-0002.md @@ -0,0 +1,188 @@ +--- +id: ADR-0002 +title: Abstracted Rendering Hardware Interface (RHI) with Runtime-Loaded Backends +date: 2026-05-08 +status: accepted +deciders: + - mallory-scotton +authors: + - mallory-scotton +--- + + +# ADR-0002: Abstracted Rendering Hardware Interface (RHI) with Runtime-Loaded Backends + +![Accepted][badge-accepted] + +## Context + +The GP Engine targets multiple operating systems (Windows, Linux, macOS) and the major modern +graphics APIs: Direct3D 11, Direct3D 12, Vulkan, OpenGL, and Metal. No single API is available on +all target platforms, and the best-performing API varies by platform and GPU vendor. + +Tying the core rendering logic to any one API would make porting prohibitively expensive and would +violate the engine's multi-backend pluggability goal. Conversely, a "lowest-common-denominator" +abstraction that wraps every API uniformly introduces overhead and constrains feature parity. + +Three forms of coupling between the engine and a graphics API must be considered independently: + +1. **Compile-time coupling**: the engine's headers `#include` API-specific types. +2. **Link-time coupling**: the engine binary statically references API-specific symbols. +3. **Runtime coupling**: the engine loads an API-specific shared library at startup via + `dlopen`/`LoadLibrary`. + +The goal is to eliminate compile-time and link-time coupling entirely. The engine binary should +contain zero direct references to Vulkan, D3D, or Metal symbols. Only runtime coupling (plugin +loading) is acceptable. + +## Decision + +We adopt a **`base/` + N-backends** decomposition for the RHI layer: + +```text +source/runtime/rhi/ + base/ <-- abstract interfaces + factory (IRHIDevice, IRHITexture, ...) + d3d11/ <-- Direct3D 11 concrete implementation + d3d12/ <-- Direct3D 12 concrete implementation + vulkan/ <-- Vulkan concrete implementation + opengl/ <-- OpenGL concrete implementation (compatibility) + metal/ <-- Metal concrete implementation (Apple platforms) + null/ <-- Null/headless implementation for servers and tests +``` + +### Architectural rules + +1. **`rhi/base` is the only RHI-tier dependency of any consumer.** The `renderer` module, the engine + module, and all other consumers link against `rhi/base` only. They never link against a concrete + backend. + +2. **Concrete backends are runtime (dynamic) dependencies, not link-time dependencies.** Each backend + compiles to a shared library. At engine startup, the RHI factory loads the appropriate library + via the platform's dynamic-loading API and queries a registered factory function. The engine + binary itself contains no backend symbols. + +3. **`rhi/base` never link-depends on any backend.** The dependency DAG is strictly acyclic: + `consumer -> rhi/base <- rhi/vulkan` (backends depend on `base`, never the reverse). + +4. **Adding a new backend is a purely additive change.** A new backend is a new sibling directory + under `rhi/`. The only change to existing files is one `gpAddDependency(DYNAMIC rhi/)` line + in `rhi/base/CMakeLists.txt`. + +### GPBT expression + +```cmake +# In rhi/base/CMakeLists.txt +gpStartModule(rhi) + gpAddDependency(PUBLIC core) + + gpAddDependency(DYNAMIC rhi/null) # always present as safe fallback + + if(WIN32) + gpAddDependency(DYNAMIC rhi/d3d11) + gpAddDependency(DYNAMIC rhi/d3d12) + endif() + if(UNIX) + gpAddDependency(DYNAMIC rhi/vulkan) + gpAddDependency(DYNAMIC rhi/opengl) + endif() + if(APPLE) + gpAddDependency(DYNAMIC rhi/metal) + endif() +gpEndModule() +``` + +`gpAddDependency(DYNAMIC ...)` creates a build-order edge (so the backend is compiled before the +engine) but no link-time edge. The `null` backend is unconditionally present on all platforms as a +safe headless fallback. + +### This pattern is general + +This `base/` + N-backends decomposition is **not specific to the RHI**. It is applied uniformly +to every pluralistic subsystem in the engine. See [ADR-0007](./ADR-0007.md) for the general +decision covering `audio/`, `physics/`, and `parser/`. + +## Alternatives Considered + +### Option A: Single graphics API (Vulkan-only or D3D12-only) + +Commit to one modern API and do not build an abstraction. + +**Why rejected:** Eliminates Windows-exclusive D3D12 path (important for performance on +Windows-dominant gaming hardware), Apple platform support (Metal-only), and headless server builds +(OpenGL/null backend). The engine's cross-platform mandate makes this non-viable. + +### Option B: Compile-time backend selection (one API per build) + +Build the engine against one API selected at CMake configure time (`-DGPE_RHI=Vulkan`). No runtime +switching. + +**Why rejected:** Forces users to maintain separate build artifacts per API. CI must replicate N +builds for N APIs. A developer on Windows who wants to test the Vulkan path cannot switch without +a full rebuild. The null backend for headless unit testing becomes an artificial special case. + +### Option C: Godot-style three-layer indirection (server + driver separation) + +Godot separates `servers/rendering/` (the headless abstract server) from `drivers/vulkan/` (the +concrete driver). The scene layer talks to the server; the server talks to the driver. + +**Why rejected:** Adds a third layer of indirection on top of our renderer -> RHI -> backend chain +without substantive benefit at our current scale. The Godot pattern is elegant for its scene/server +coupling but introduces cognitive overhead when reading call stacks. Our two-layer model +(`renderer -> rhi/base -> rhi/vulkan`) is sufficient and easier to trace. + +### Option D: Unreal Engine's RHI inside a single Runtime module + +Unreal co-locates all RHI backends inside `Engine/Source/Runtime/` as sibling modules at the same +level as `Core`, `RenderCore`, etc. They are treated as regular static-link modules. + +**Why rejected:** UE uses static linking for RHI backends, which means the shipping binary contains +symbols for every API the build was configured for. Our dynamic-loading approach removes D3D12 code +from a Vulkan-only shipping build entirely. + +## Consequences + +### Positive + +- **The engine binary is backend-agnostic.** A shipping build for a Vulkan-only platform does not + contain D3D12 code. Binary size and attack surface are minimized. +- **Backends are independently versioned and replaceable.** A new Vulkan driver can be deployed + by replacing `rhi_vulkan.so` without relinking the engine. +- **Adding a backend is purely additive.** No existing file is modified except for the one-line + `gpAddDependency(DYNAMIC ...)` registration. +- **The directory tree encodes the architecture.** A student who opens `source/runtime/rhi/` sees + every available backend immediately. +- **The null backend enables headless testing.** Unit tests for systems above the RHI run without + a GPU or display, using the null backend as a stub. + +### Negative + +- **Designing the `rhi/base` abstraction is the hardest engineering task in the RHI layer.** An + interface that covers D3D11's immediate-mode model, D3D12/Vulkan's explicit resource management, + and Metal's pipeline model without leaking abstractions is genuinely complex. +- **Runtime loading introduces startup latency.** `dlopen` + symbol lookup adds initialization time. + Mitigated by the fact that this happens once at engine startup, not per frame. +- **Dynamic linking complicates packaging.** All backend shared libraries must be distributed + alongside the engine binary. + +## Rationale and Cross-References + +- [ADR-0001](./ADR-0001.md): `rhi/base` uses `public/private` discipline; backends expose minimal + `public/` surfaces (only the factory entry point). +- [ADR-0003](./ADR-0003.md): `gpAddDependency(DYNAMIC ...)` is the GPBT primitive that separates + build-order from link-order. +- [ADR-0007](./ADR-0007.md): The same `base/` + N-backends pattern is applied to `audio/`, + `physics/`, and `parser/`. + +**Canonical references:** + +- Godot Engine: `servers/rendering/` + `drivers/vulkan/` three-layer model. +- Unreal Engine 5: `Engine/Source/Runtime/RHI/` module. +- Frostbite FrameGraph (GDC 2017): treating rendering passes as modules with declarative I/O, + the same "make dependencies visible" principle applied at the pass level. + + +[badge-proposed]: https://img.shields.io/badge/Status-Proposed-yellow.svg +[badge-accepted]: https://img.shields.io/badge/Status-Accepted-brightgreen.svg +[badge-rejected]: https://img.shields.io/badge/Status-Rejected-red.svg +[badge-deprecated]: https://img.shields.io/badge/Status-Deprecated-lightgrey.svg +[badge-superseded]: https://img.shields.io/badge/Status-Superseded-lightgrey.svg diff --git a/docs/adr/ADR-0003.md b/docs/adr/ADR-0003.md new file mode 100644 index 00000000..99b1f60b --- /dev/null +++ b/docs/adr/ADR-0003.md @@ -0,0 +1,202 @@ +--- +id: ADR-0003 +title: CMake as Build Generator with GPBT as Declarative Orchestration Layer +date: 2026-05-08 +status: accepted +deciders: + - mallory-scotton +authors: + - mallory-scotton +--- + + +# ADR-0003: CMake as Build Generator with GPBT as Declarative Orchestration Layer + +![Accepted][badge-accepted] + +## Context + +The GP Engine is a multi-platform C++ monorepo spanning dozens of modules with strict +visibility rules ([ADR-0001](./ADR-0001.md)), runtime-loaded backends ([ADR-0002](./ADR-0002.md)), +and lifecycle-separated source directories ([ADR-0004](./ADR-0004.md)). Any build system we adopt +must satisfy several non-negotiable requirements: + +1. **Declarative module descriptions.** A module's build description must say *what* the module is + (its sources, its dependencies, its visibility) without encoding *how* CMake should configure it. +2. **Mechanical enforcement of `public/private` boundaries.** The build system must set include paths + such that a consumer of module `M` cannot access `M/private/` or `M/internal/`, even if the + paths exist on disk. +3. **Dependency-order-independent discovery.** In a large tree, the order in which CMake discovers + modules must not matter. A module declared before its dependency must still build correctly. +4. **C++-ecosystem standard.** The build system must be familiar to a senior C++ engineer arriving + from any studio background, without requiring knowledge of a proprietary scripting language. +5. **Educational transparency.** A student reading a module's build description must understand the + full dependency story of that module in under 30 lines. + +Raw CMake fails requirements 1-3 without significant scaffolding. Proprietary alternatives +(UnrealBuildTool, Premake, Bazel) each fail at least one of 1-5. + +## Decision + +We adopt **CMake** as the underlying build generator (satisfying requirement 4) and build a thin +orchestration layer on top of it, the **Graphical Playground Build Tool (GPBT)**, to satisfy +requirements 1-3. + +GPBT is a pure CMake include library: a set of `.cmake` files that define macros. No external +language runtime (C#, Python, Ruby) is required. A developer who can read CMake can read GPBT. + +### The Three-Phase Build + +Standard CMake processes `CMakeLists.txt` files in discovery order. If module B depends on module A +but B is discovered first, the build is undefined or broken. GPBT solves this with a two-pass +system (analogous to UnrealBuildTool's Rules compilation, but implemented entirely in CMake): + +**Phase 1: Registration.** GPBT recursively scans the source tree and executes each +`CMakeLists.txt` in a lightweight registration mode. Each target records its name, type, source +glob roots, and dependency list. No real CMake targets are created. + +**Phase 2: Configuration.** GPBT performs a topological sort over all registered targets and +re-processes each `CMakeLists.txt` in dependency order, this time creating the real CMake targets +with correct include paths, compile definitions, and link libraries. + +**Phase 3: Generation.** Standard CMake generation to the chosen backend (Ninja, Makefile, +MSBuild, Xcode). + +### The Four Dependency Visibility Levels + +GPBT introduces four dependency kinds that map to the three coupling levels described in +[ADR-0002](./ADR-0002.md): + +| GPBT macro | Compile | Link | Runtime | Propagated to consumer? | +| --- | --- | --- | --- | --- | +| `gpAddDependency(PUBLIC B)` | Yes (B's `public/`) | Yes | - | Yes | +| `gpAddDependency(PRIVATE B)` | Yes (B's `public/`) | Yes | - | No | +| `gpAddDependency(INTERNAL B)` | Yes (B's `public/`) | Yes | - | No (semantically internal) | +| `gpAddDependency(DYNAMIC B)` | - | - | Yes (build-order only) | - | + +`DYNAMIC` creates a build-order edge so the shared library is built before the engine binary, but +creates **no** link-time dependency. + +### GPBT Macro Surface + +A complete module declaration looks like: + +```cmake +include(gp-build-tool) + +gpStartModule(audio) + gpAddDependency(PUBLIC core) + gpAddDependency(PRIVATE gp::thirdparty::sdl3) + gpTargetSetTestsEnabled(TRUE) + gpTargetSetBenchmarksEnabled(TRUE) +gpEndModule() +``` + +GPBT automatically globs `private/**/*.cpp` as private sources, adds `public/` to the PUBLIC +include path (propagated to consumers), adds `private/` and `internal/` to the PRIVATE include +path, and creates test and benchmark executables when opted in. + +### Filesystem Analogy + + +| GPBT macro | Filesystem analogue | +| --- | --- | +| `gpStartModule(name) ... gpEndModule()` | A folder with a `CMakeLists.txt` | +| Auto-glob `private/**/*.cpp` | The `private/` folder | +| Auto-PUBLIC `public/` include path | The `public/` folder | +| Auto-PRIVATE `internal/` include path | The `internal/` folder | +| `gpAddDependency(PUBLIC B)` | "B's `public/` is part of my API surface" | +| `gpAddDependency(PRIVATE B)` | "B is an implementation detail; my consumers don't see it" | +| `gpAddDependency(DYNAMIC B)` | "B is a runtime plugin; no link edge" | +| `gpStartPlugin(name) ... gpEndPlugin()` | A folder under `source/plugins/` | +| `gpStartExecutable(name) ... gpEndExecutable()` | A folder under `source/launch/` or `source/programs/` | + + +## Alternatives Considered + +### Option A: Raw CMake (no orchestration layer) + +Write standard `CMakeLists.txt` files using `add_library`, `target_include_directories`, +`target_link_libraries` directly. + +**Why rejected:** Raw CMake does not enforce discovery-order independence. Public/private path +discipline must be repeated in every module. No mechanical enforcement of the `public/`-only +exposure rule. Macros would be copy-pasted and drift within a release cycle. + +### Option B: UnrealBuildTool (C# rules files) + +Adopt UE's module descriptor format (`*.Build.cs` + `*.Target.cs`) and write a C# tool to +process them. + +**Why rejected:** Introduces a C# runtime and a substantial out-of-band tool into a pure C++ +project. Engineers onboarding from Godot, O3DE, or any CMake-based studio background must learn a +bespoke language used nowhere else. We take the **semantics** of UBT's +`PublicDependencyModuleNames`/`PrivateDependencyModuleNames` and express them in CMake instead. + +### Option C: Bazel / Buck2 + +Use a hermetic, content-addressed build system. + +**Why rejected:** Excellent for reproducibility at Google/Meta scale. Requires non-trivial `BUILD` +file authorship and a learning curve not shared with the broader C++ CMake ecosystem. Our +educational mandate requires CMake. + +### Option D: Premake + +Use Premake's Lua-based build description language. + +**Why rejected:** Premake generates project files but does not manage dependency propagation or +include-path visibility. We would still need a custom layer on top. Lua is less familiar to C++ +engineers than CMake. + +### Option E: Meson + +Use the Meson build system. + +**Why rejected:** Strong cross-platform story, but has no native mechanism for runtime-plugin +dependency edges (`DYNAMIC` in GPBT). Would require custom wrappers similar in spirit to GPBT but +in a less familiar ecosystem. + +## Consequences + +### Positive + +- **CMake familiarity is broadly available.** Any C++ engineer who has contributed to an open-source + project can read GPBT after a short ramp-up. +- **GPBT is transparent.** It is implemented as plain `.cmake` files under `cmake/`. Engineers who + need to debug the build can read the implementation directly. +- **The `public/private` boundary is mechanically enforced.** A developer cannot accidentally expose + `private/` headers to consumers. +- **Discovery-order independence removes a whole class of build bugs.** +- **`DYNAMIC` dependencies correctly model runtime plug-ins.** The build system expresses a + distinction that raw `target_link_libraries` cannot. + +### Negative + +- **GPBT is a custom tool that must be learned.** Despite being written in CMake, its macro API is + GP-specific. +- **The two-pass system adds configure-time overhead.** The double traversal adds seconds to + `cmake ..` for very large source trees. This is a one-time cost at configure time, not build time. +- **GPBT diverges from vanilla CMake.** A developer who adds a module with raw CMake rather than + GPBT macros will produce a target that does not follow the visibility conventions. + +## Related Decisions + +- [ADR-0001](./ADR-0001.md): Defines the `public/private/internal` convention that GPBT enforces. +- [ADR-0002](./ADR-0002.md): Defines `DYNAMIC` dependency semantics used for RHI backends. +- [ADR-0007](./ADR-0007.md): Extends `DYNAMIC` dependencies to audio, physics, and parser backends. +- [ADR-0008](./ADR-0008.md): GPBT has been extracted to its own versioned repository; the engine + consumes it as a Git submodule under `cmake/`. + +> **Note:** GPBT was later extracted to its own repository at +> [`GraphicalPlayground/gp-build-tool`](https://github.com/GraphicalPlayground/gp-build-tool) and is +> no longer part of the GP Engine monorepo. The `cmake/` directory in the engine contains a pinned +> version of GPBT as a Git submodule. See [ADR-0008](./ADR-0008.md) for the decision record +> covering that extraction. + + +[badge-proposed]: https://img.shields.io/badge/Status-Proposed-yellow.svg +[badge-accepted]: https://img.shields.io/badge/Status-Accepted-brightgreen.svg +[badge-rejected]: https://img.shields.io/badge/Status-Rejected-red.svg +[badge-deprecated]: https://img.shields.io/badge/Status-Deprecated-lightgrey.svg +[badge-superseded]: https://img.shields.io/badge/Status-Superseded-lightgrey.svg diff --git a/docs/adr/ADR-0004.md b/docs/adr/ADR-0004.md new file mode 100644 index 00000000..9a42e8a8 --- /dev/null +++ b/docs/adr/ADR-0004.md @@ -0,0 +1,153 @@ +--- +id: ADR-0004 +title: Lifecycle Separation via Top-Level Source Directories +date: 2026-05-08 +status: accepted +deciders: + - mallory-scotton +authors: + - mallory-scotton +--- + + +# ADR-0004: Lifecycle Separation via Top-Level Source Directories + +![Accepted][badge-accepted] + +## Context + +A game engine's source code spans multiple **build-time lifecycles**: code that ships in a packaged +game, code used only during development, standalone tooling, entry-point executables, shader source, +and optional plugins. Without an explicit lifecycle separation in the directory tree, the following +failure modes arise: + +- **Shipping builds include editor code.** Tools needed only during development accidentally end up + in retail builds because nothing enforces the boundary. +- **Editor rebuilds trigger game-runtime rebuilds.** If editor-only code is co-located with runtime + code, a change to the editor causes unnecessary recompilation of runtime modules. +- **Launchers are confused with libraries.** An executable that bootstraps the editor (linking + runtime + editor modules) is architecturally distinct from the runtime modules it links. +- **Tools have unclear ownership.** A standalone shader-compile worker sitting next to runtime + subsystems looks like a subsystem; it is not. + +The core insight from the Unreal Engine 5 layout is that the **top-level source directory structure +encodes build-lifecycle rules**, not just organizational convention. + +## Decision + +The `source/` directory is divided into six top-level lifecycle buckets. Each bucket maps to a +distinct CMake include-dependency scope enforced by GPBT ([ADR-0003](./ADR-0003.md)): + +```text +source/ + runtime/ <-- code that ships in a packaged game binary + launch/ <-- executable entry points (link runtime modules, never imported by other modules) + developer/ <-- development/editor-only code (excluded from Shipping builds) + programs/ <-- standalone tool executables (asset bakers, shader workers, ...) + plugins/ <-- optional, runtime-loadable engine extensions + shaders/ <-- shader source (HLSL/GLSL/MSL, built by the shader pipeline) +``` + +### Semantics of each bucket + + +| Directory | Included in shipping? | Can import `runtime/`? | Can import `developer/`? | Produces a library? | +| --- | --- | --- | --- | --- | +| `runtime/` | Yes | Yes (siblings) | No | Yes (static/shared) | +| `launch/` | Yes (as executable) | Yes | Yes (editor launcher only) | No (executable only) | +| `developer/` | No | Yes | Yes (siblings) | Yes | +| `programs/` | No (separate binary) | Yes | Yes | No (executable) | +| `plugins/` | Optional | Yes | No | Yes (shared lib) | +| `shaders/` | Yes (compiled artifacts) | - | - | No (compiled to SPIR-V/DXIL/MSL) | + + +### `launch/` in detail + +The `launch/` directory contains **executable entry points only**. Each subdirectory is a thin +launcher that links runtime (and optionally developer) modules: + +```text +source/launch/ + editor/ <-- GP Editor (links runtime + developer) + standalone/ <-- packaged-game launcher (links runtime only) + client/ <-- (future) network client + server/ <-- (future) headless dedicated server +``` + +A launcher is not importable. No module in `runtime/` or `developer/` may link against a launcher. +Launchers are sinks in the dependency DAG. + +### `shaders/` in detail + +Shader source is not a C++ module. It is processed by a separate shader pipeline configured by +`shaders/Shaders.build.cmake`, which invokes the appropriate shader compiler (DXC, glslc, Metal +shader compiler) per backend. This separation prevents shader compilation rules from polluting +module CMakeLists files. + +## Alternatives Considered + +### Option A: Flat `source/` with lifecycle implied by naming conventions + +All modules sit at the same level under `source/`; runtime vs. editor is distinguished by a name +prefix (e.g., `editor_*`, `tool_*`). + +**Why rejected:** Naming conventions are aspirational, not mechanical. Nothing prevents a module +prefixed `editor_` from being linked into a shipping build. Unreal Engine learned this lesson early +and added `Runtime/Editor/Developer/Programs/` buckets specifically to make lifecycle boundaries +compile-time enforceable. + +### Option B: Unreal Engine's exact five-bucket layout + +Adopt UE's exact names (`Runtime/Editor/Developer/Programs/ThirdParty/`) and capitalize them. + +**Why rejected:** We use `lower-kebab-case` directories (see [ADR-0005](./ADR-0005.md)). We also +rename `Editor/` to `developer/` to avoid implying the bucket is only for the GUI editor. We place +`ThirdParty/` at the repo root rather than inside `source/`, because third-party dependencies are +external libraries managed by GPBT's `gp-thirdparty.cmake`, not modules in our system. + +### Option C: O3DE / Bevy, everything is a Gem/crate, no lifecycle buckets + +Treat all modules uniformly; lifecycle is controlled by manifest flags rather than directory +placement. + +**Why rejected:** Manifest-driven lifecycle is less immediately visible than directory-driven +lifecycle. A student reading the source tree must open every module's manifest to learn its lifecycle +role. Directory placement communicates the role in zero clicks. + +## Consequences + +### Positive + +- **Shipping builds cannot accidentally include editor code.** Build targets under `developer/` and + `programs/` are never linked by `launch/standalone/` or `launch/server/`. The directory boundary + is a hard CMake constraint. +- **Incremental compilation is better scoped.** A change to `developer/assetcooker` does not trigger + recompilation of any `runtime/` module. +- **The dependency DAG direction is readable from the tree.** `launch/` at the top; `runtime/` + below it. Any developer new to the codebase understands the flow without reading documentation. +- **Lifecycle is self-documenting.** A module's directory tells you whether it ships. + +### Negative + +- **More directories to navigate.** A developer looking for `audio/` must know to look in + `source/runtime/audio/`, not at the repo root. +- **Occasional ambiguity at the boundary.** Some modules straddle lifecycle boundaries (e.g., a + profiler overlay optionally included in debug-release builds). These are resolved by placing the + module in the more restrictive bucket (`developer/`) and providing a compile-time opt-in flag. + +## Rationale and Cross-References + +- [ADR-0001](./ADR-0001.md): `public/private/internal` discipline applies within every module in + every lifecycle bucket. +- [ADR-0003](./ADR-0003.md): GPBT enforces lifecycle rules via the `gpStartModule` / + `gpStartPlugin` / `gpStartExecutable` macro family. +- [ADR-0005](./ADR-0005.md): Naming conventions for directories. + +**Canonical reference:** Unreal Engine 5, `Engine/Source/{Runtime,Editor,Developer,Programs}/`. + + +[badge-proposed]: https://img.shields.io/badge/Status-Proposed-yellow.svg +[badge-accepted]: https://img.shields.io/badge/Status-Accepted-brightgreen.svg +[badge-rejected]: https://img.shields.io/badge/Status-Rejected-red.svg +[badge-deprecated]: https://img.shields.io/badge/Status-Deprecated-lightgrey.svg +[badge-superseded]: https://img.shields.io/badge/Status-Superseded-lightgrey.svg diff --git a/docs/adr/ADR-0005.md b/docs/adr/ADR-0005.md new file mode 100644 index 00000000..1f360190 --- /dev/null +++ b/docs/adr/ADR-0005.md @@ -0,0 +1,190 @@ +--- +id: ADR-0005 +title: Monorepo Layout and Repository Root Conventions +date: 2026-05-08 +status: accepted +deciders: + - mallory-scotton +authors: + - mallory-scotton +--- + + +# ADR-0005: Monorepo Layout and Repository Root Conventions + +![Accepted][badge-accepted] + +## Context + +The GP Engine is a **monorepo**: engine source, example projects, build tooling, and CI +configuration all live in a single repository. This requires answering two questions: + +1. **What are the top-level directories**, and what is the rule for which files live at the repo + root vs. inside a subdirectory? +2. **What naming convention do directories and files follow?** + +Without explicit rules, repository roots accumulate incidental files over time. Contributors add +configuration files, scripts, and auxiliary assets wherever is convenient, producing a cluttered +root that signals to newcomers: "this project is not carefully maintained." An unstructured root +also makes it harder to know where to look for a file without searching, and harder to distinguish +build artifacts from source-controlled assets. + +## Decision + +### Top-Level Structure + +The repository root is divided into **five source directories** and a set of **root-level files**. +Every root-level file must belong to exactly one of three categories: **community contract**, +**build entry point**, or **tooling contract**. + +```text +gp-engine/ + source/ <-- all engine C++/C/shader source (see ADR-0004) + thirdparty/ <-- external dependencies (isolation barrier) + examples/ <-- SDK example projects (depend on engine, never imported by engine) + toolchain/ <-- per-platform CMakePresets and toolchain scripts + cmake/ <-- GPBT build tool (see ADR-0003, ADR-0008) + + [Community contracts] + README.md + CONTRIBUTING.md + CODE_OF_CONDUCT.md + SECURITY.md + CONTRIBUTORS.md + DONORS.md + CHANGELOG.md + LICENSE.md + LICENSE_HEADER <-- canonical comment block stamped on every source file by pre-commit + + [Build entry points] + CMakeLists.txt + CMakePresets.json + VERSION <-- single-line semver; sourced by CMake and CI + + [Tooling contracts] + .editorconfig + .clang-format / .clang-format-ignore + .clang-tidy + .clangd + .gitignore / .gitattributes + .git-blame-ignore-revs + .mailmap + .pre-commit-config.yaml +``` + +**Rule:** If a file is not a community contract, a build entry point, or a tooling contract, it does +not belong at the repo root. It belongs inside one of the five source directories. + +### The Five Source Directories + +| Directory | Reads | Depends on | Purpose | +| --- | --- | --- | --- | +| `source/` | Engine engineers | `thirdparty/` | All engine source (modules, shaders) | +| `thirdparty/` | - | - | External dependency isolation barrier | +| `examples/` | Game developers | `source/` (public API only) | SDK usage examples | +| `toolchain/` | Build system | - | Per-platform CMake toolchain files and presets | +| `cmake/` | Build system | - | GPBT and its dependencies | + +The **reading order** is intentional: `source/` is the engine; `thirdparty/` is what the engine +depends on; `examples/` is what depends on the engine; `toolchain/` and `cmake/` describe how all +of it is built. + +### Naming Convention + +All directories in the repository follow **`lower-kebab-case`** (hyphens for multi-word names, all +lowercase). This applies to top-level directories, module directories, and subdirectory names within +modules (`public/`, `private/`, `internal/`, `tests/`, `benchmarks/`, platform subdirectories, etc.) + +The only exceptions are files that must follow an ecosystem-standard name: + +- `CMakeLists.txt`: CMake requires exact casing. +- `README.md`, `CHANGELOG.md`, `LICENSE.md`, etc.: Conventional all-caps for community files. +- `Dockerfile`: Docker convention. + +C++ source files and headers follow **`PascalCase`** for class/type files, the standard C++ +community convention, distinct from directory naming. + +### The `.github/` and `.devcontainer/` Directories + +These two hidden directories are tooling contracts that warrant separate mention: + +- **`.github/`**: GitHub-platform conventions: CI workflows, issue templates, PR template, + CODEOWNERS, labeler config, and FUNDING. The structure mirrors GitHub's documented conventions + exactly. +- **`.devcontainer/`**: Development container configuration (Dockerfile + `devcontainer.json`). + This is the **highest-ROI file in the repository for new contributors**: it reduces "git clone to + working build" time from hours (toolchain installation) to minutes (one-click container launch in + VS Code or GitHub Codespaces). + +## Alternatives Considered + +### Option A: Polyrepo (engine, examples, toolchain as separate repositories) + +Each major area lives in its own Git repository. Examples import the engine as a submodule or +package. + +**Why rejected:** Cross-cutting changes (a new engine API that requires updating examples and CI +simultaneously) require multi-repo PRs with coordination overhead. Atomic commits are impossible. +At AAA scale (Anvil, Snowdrop), even multi-team internal engines consolidate into shared monorepos +because cross-team module reuse outweighs the autonomy of separate codebases. + +### Option B: Source at repo root (no `source/` subdirectory) + +Module directories sit directly at the repo root: `runtime/`, `launch/`, `developer/`, etc. + +**Why rejected:** Mixes source directories with tooling, CI, and documentation at the same level. +Adds cognitive noise when scanning the root. The `source/` subdirectory is a single-purpose +container that groups everything a C++ engineer would call "the code." + +### Option C: PascalCase directories (Unreal Engine convention) + +Adopt UE's `Source/`, `Runtime/`, `Editor/`, `ThirdParty/` casing. + +**Why rejected:** Inconsistent with the modern C++ open-source ecosystem standard. Lower-kebab-case +is more portable across case-sensitive file systems (Linux) and less likely to produce `#include` +casing bugs. + +### Option D: Minimal root (README + CMakeLists only) + +Ship only the bare minimum at the root; documentation and community files live inside `docs/`. + +**Why rejected:** GitHub, GitLab, and other hosting platforms render `README.md`, `CONTRIBUTING.md`, +`CODE_OF_CONDUCT.md`, `SECURITY.md`, and `FUNDING.yml` from the repo root automatically. Hiding +them inside `docs/` breaks the platform's community health features and degrades the first-contact +experience for contributors and students. + +## Consequences + +### Positive + +- **The root has a legible structure.** A newcomer can orient in under 30 seconds: five directories, + three kinds of root files, and no surprises. +- **Community health files are discoverable.** GitHub's community health checklist is satisfied. +- **`.devcontainer/` eliminates onboarding friction.** The most common reason a student abandons an + open-source C++ project is toolchain setup failure. The dev container removes this barrier. +- **`VERSION` as a single source of truth.** CMake, CI badges, and release pipelines all read from + one file; version drift across config files is impossible. +- **Naming consistency reduces grep false positives.** All lowercase directory names mean + `find source/ -name "vulkan"` produces no casing ambiguities on any platform. + +### Negative + +- **Monorepo CI is more complex than polyrepo CI.** Path filters must be configured to trigger only + the relevant workflows for a given change. +- **`lower-kebab-case` module dirs are a departure from UE.** Engineers arriving from an Unreal + background will find the casing surprising at first. + +## Rationale and Cross-References + +- [ADR-0004](./ADR-0004.md): Defines the `source/` subdirectory structure. +- [ADR-0003](./ADR-0003.md): `cmake/` houses GPBT; its internal layout mirrors the same + `public/internal` discipline as engine modules. +- [ADR-0008](./ADR-0008.md): GPBT was extracted to its own repo; `cmake/` contains a pinned + submodule. + + +[badge-proposed]: https://img.shields.io/badge/Status-Proposed-yellow.svg +[badge-accepted]: https://img.shields.io/badge/Status-Accepted-brightgreen.svg +[badge-rejected]: https://img.shields.io/badge/Status-Rejected-red.svg +[badge-deprecated]: https://img.shields.io/badge/Status-Deprecated-lightgrey.svg +[badge-superseded]: https://img.shields.io/badge/Status-Superseded-lightgrey.svg diff --git a/docs/adr/ADR-0006.md b/docs/adr/ADR-0006.md new file mode 100644 index 00000000..82587753 --- /dev/null +++ b/docs/adr/ADR-0006.md @@ -0,0 +1,183 @@ +--- +id: ADR-0006 +title: Co-Located Documentation, Tests, and Benchmarks per Module +date: 2026-05-08 +status: accepted +deciders: + - mallory-scotton +authors: + - mallory-scotton +--- + + +# ADR-0006: Co-Located Documentation, Tests, and Benchmarks per Module + +![Accepted][badge-accepted] + +## Context + +In most large C++ projects, tests, benchmarks, and documentation are **satellite directories** +separated from the code they describe: + +```text +project/ + src/ <-- code + tests/ <-- tests for all of src/ + docs/ <-- documentation for all of src/ + benchmarks/ <-- benchmarks for all of src/ +``` + +This layout has a systemic failure mode: **documentation and tests drift from the code they +describe**. When a module is refactored, its co-located tests must move; when a module is deleted, +its satellite tests must be hunted down separately. In practice they often are not, leaving orphan +test files that reference non-existent code. + +The GP Engine has an explicit educational mandate: a student who navigates to any module should find +everything they need to understand, test, and benchmark that module **in the same place as the +code**. + +A module is not just code; it is a **unit of ownership**. The complete description of a module +requires: code, build description, documentation, change history, tests, and benchmarks. + +## Decision + +Every module in the GP Engine ships its own documentation, tests, and benchmarks **co-located within +the module directory**. The canonical module anatomy is: + +```text +/ + public/ <-- exported headers (PUBLIC include path) + private/ <-- .cpp sources + internal headers (PRIVATE) + internal/ <-- restricted-share headers (PRIVATE-but-friend, optional) + tests/ <-- test sources, opt-in via gpTargetSetTestsEnabled(TRUE) + benchmarks/ <-- benchmark sources, opt-in via gpTargetSetBenchmarksEnabled(TRUE) + docs/ <-- longer-form documentation, design notes, module-level ADRs + README.md <-- module overview (what, why, dependencies, API surface) + CHANGELOG.md <-- per-module change history (Keep a Changelog format) + CMakeLists.txt <-- GPBT module declaration + .gitignore <-- module-specific gitignore +``` + +### README.md contract + +Every module's `README.md` answers exactly four questions, in order: + +1. **What** does this module do? +2. **Why** does it exist as a separate module (rather than being merged into `core` or `engine`)? +3. **What** are its declared dependencies? +4. **What** does it expose to consumers? + +This template is enforced in code review. A README that skips question 2 is flagged; that is the +most important question for architectural understanding. + +### CHANGELOG.md contract + +Every module ships a `CHANGELOG.md` following the [Keep a Changelog](https://keepachangelog.com) +format with a **per-module version stream independent of the engine's master `CHANGELOG.md`**. This +allows SDK consumers tracking ABI stability for a single module to subscribe to that module's +changelog without wading through engine-wide release notes. + +### Tests and benchmarks: opt-in, not default + +Tests and benchmarks are not built by default. The module's `CMakeLists.txt` opts in: + +```cmake +gpStartModule(audio) + gpAddDependency(PUBLIC core) + gpTargetSetTestsEnabled(TRUE) + gpTargetSetBenchmarksEnabled(TRUE) +gpEndModule() +``` + +When enabled, GPBT creates `_tests` and `_benchmarks` CMake targets that link +against the module under test plus the engine's chosen test framework and benchmarking harness. +They are excluded from the default all-modules build; they are opt-in via a CMake preset or flag. + +The opt-in design prevents CI from building every module's tests on every change. CI selects which +modules' tests to run based on which files changed (path filtering in GPBT). + +## Alternatives Considered + +### Option A: Single top-level `tests/` directory (satellite pattern) + +All test files live in a flat `tests/` at the repository root, mirroring the module structure. + +**Why rejected:** Module refactoring breaks test paths. Module deletion requires manual cleanup of +the satellite directory. The educational value is lost: a student reading `source/runtime/audio/` +must remember to look in `tests/runtime/audio/` for the tests. + +**Reference:** Godot uses this pattern (`tests/` at root); it is a known pain point when modules +are reorganized. + +### Option B: In-source tests (test code mixed into `private/`) + +Place test source files alongside implementation files in `private/`. Guard with +`#ifdef GP_BUILD_TESTS`. + +**Why rejected:** Pollutes the module's implementation namespace with test symbols. Conditional +compilation for tests is a known source of subtle bugs (test-only code paths never compiled by +production builds and vice versa). + +### Option C: Separate `tests/` module per module (O3DE / Gem pattern) + +O3DE places tests in a separate `Tests/` subdirectory within a Gem, treated as its own GPBT target. + +**Why adopted partially:** We adopt the same co-location principle. Our difference is that GPBT +handles the test/benchmark target creation automatically via the `gpTargetSetTestsEnabled` flag; +there is no separate `CMakeLists.txt` inside `tests/`. The test sources are discovered by a glob; +GPBT creates the target. This reduces boilerplate compared to a full separate module. + +### Option D: Central `docs/` repository (documentation-as-a-repo) + +Move all documentation to a separate repository, linked as a Git submodule or managed by a docs +platform. + +**Why rejected:** Decoupling docs from code guarantees docs rot. A PR that renames a public API +must also update the docs; if docs live in a separate repo, the requirement is invisible to +reviewers. Co-location makes docs maintenance a first-class part of every PR. + +## Consequences + +### Positive + +- **Refactoring is atomic.** Moving a module moves its tests, benchmarks, and documentation with + it. No satellite cleanup. +- **Deletion is clean.** `git rm -r source/runtime/audio/` removes the module and all of its + associated resources. +- **Per-module changelogs enable targeted ABI tracking.** SDK consumers can subscribe to + `audio/CHANGELOG.md` and ignore the rest. +- **Educational clarity.** A student navigating to any module finds the README, the API (`public/`), + the implementation (`private/`), the tests, and the benchmarks in one place. +- **Benchmark proximity incentivizes profiling-driven development.** Running a benchmark for + `core/memory/backends/Malloc` is one CMake target away; the benchmark lives next to the source. + +### Negative + +- **Module directories are more complex.** A newcomer sees eight items in a module directory + instead of two. The overhead is front-loaded (one-time orientation) rather than recurring. +- **Per-module CHANGELOG maintenance requires discipline.** Developers must remember to update the + module-level `CHANGELOG.md` when making API-visible changes. Enforced in code review, not tooling. +- **`docs/` inside each module can become redundant with the module README.** Convention: + `README.md` for quick orientation; `docs/` for deep-dives (design documents, decision records, + diagrams). + +## Rationale and Cross-References + +- [ADR-0001](./ADR-0001.md): `public/private/internal` are the first three items in module anatomy; + `tests/`, `benchmarks/`, `docs/` are the next three. +- [ADR-0003](./ADR-0003.md): `gpTargetSetTestsEnabled` / `gpTargetSetBenchmarksEnabled` are the + GPBT primitives that wire up co-located test/benchmark directories. +- [ADR-0004](./ADR-0004.md): Co-location applies to every lifecycle bucket: runtime, developer, + and plugin modules all follow the same anatomy. + +**Canonical references:** + +- O3DE Gems: each Gem is a self-contained unit with code, assets, tests, and a manifest. +- Bevy crates: each crate has its own `Cargo.toml`, `README.md`, and test module. + + +[badge-proposed]: https://img.shields.io/badge/Status-Proposed-yellow.svg +[badge-accepted]: https://img.shields.io/badge/Status-Accepted-brightgreen.svg +[badge-rejected]: https://img.shields.io/badge/Status-Rejected-red.svg +[badge-deprecated]: https://img.shields.io/badge/Status-Deprecated-lightgrey.svg +[badge-superseded]: https://img.shields.io/badge/Status-Superseded-lightgrey.svg diff --git a/docs/adr/ADR-0007.md b/docs/adr/ADR-0007.md new file mode 100644 index 00000000..20a62de8 --- /dev/null +++ b/docs/adr/ADR-0007.md @@ -0,0 +1,214 @@ +--- +id: ADR-0007 +title: Uniform Base+N-Backends Pattern for All Pluralistic Subsystems +date: 2026-05-08 +status: accepted +deciders: + - mallory-scotton +authors: + - mallory-scotton +--- + + +# ADR-0007: Uniform Base+N-Backends Pattern for All Pluralistic Subsystems + +![Accepted][badge-accepted] + +## Context + +A modern cross-platform engine cannot commit to a single implementation for several subsystems: + +| Subsystem | Platform/vendor variations | +| --- | --- | +| Graphics (RHI) | Direct3D 11, Direct3D 12, Vulkan, OpenGL, Metal, null | +| Audio | OpenAL, XAudio2 (Windows), CoreAudio (macOS/iOS), FMOD | +| Physics | Jolt Physics, NVIDIA PhysX | +| Asset parsers | OBJ, FBX, glTF, JSON, INI, XML, YAML | + +For each of these subsystems, the engine must be able to: + +1. **Ship with only the backends required for the target platform.** A Linux Vulkan build should not + contain XAudio2 code. +2. **Allow a backend to be replaced or added without modifying existing engine code.** +3. **Test in isolation** using a minimal (null/stub) backend when a real API is unavailable (e.g., + CI servers without GPUs, audio hardware, or physics libraries). + +[ADR-0002](./ADR-0002.md) established the `base/` + N-backends decomposition for the RHI layer +specifically. The question this ADR answers: **should the `base/` + N-backends pattern be codified +as a general engine-wide rule, or remain an ad-hoc convention?** + +## Decision + +The **`base/` + N-backends decomposition is the mandatory pattern for every pluralistic subsystem**. +If a subsystem has more than one concrete implementation, or if a concrete implementation is +platform-conditional, the subsystem **must** decompose into a `base/` abstraction module plus one +sibling module per concrete backend. + +The concrete application across all current subsystems: + +```text +source/runtime/ + rhi/ + base/ <-- IRHIDevice, IRHITexture, IRHIBuffer, factory + d3d11/ <-- Direct3D 11 + d3d12/ <-- Direct3D 12 + vulkan/ <-- Vulkan + opengl/ <-- OpenGL (compatibility) + metal/ <-- Metal (Apple platforms) + null/ <-- headless / testing + + audio/ + base/ <-- IAudioDevice, IAudioBuffer, IAudioSource, factory + openal/ <-- OpenAL (cross-platform) + xaudio2/ <-- XAudio2 (Windows) + coreaudio/ <-- CoreAudio (macOS / iOS) + fmod/ <-- FMOD (commercial, optional) + + physics/ + base/ <-- IPhysicsWorld, IRigidBody, ICollider, factory + jolt/ <-- Jolt Physics (open-source, default) + physx/ <-- NVIDIA PhysX (commercial, optional) + + parser/ + obj/ <-- Wavefront OBJ + fbx/ <-- Autodesk FBX + gltf/ <-- glTF 2.0 + json/ <-- Generic JSON + ini/ <-- INI file + xml/ <-- XML + yaml/ <-- YAML +``` + +> **Note on `parser/`.** Each parser is a backend for a specific file *format*, not a +> platform-specific *API*. All parsers can be built on all platforms. The `base/` tier provides a +> common deserialization interface; each format module provides a concrete implementation. The +> pattern is structurally identical even if the motivation is format-pluralism rather than +> platform-pluralism. + +### Invariants + +These invariants hold for every subsystem following this pattern: + +1. **The `base/` module never link-depends on any backend.** The dependency DAG is: + `consumer -> base <- backend_A <- backend_B <- backend_N` + Never: `consumer -> base -> backend_A` (FORBIDDEN). + +2. **Backends are runtime (dynamic) dependencies, not link-time dependencies.** Backends compile to + shared libraries and are loaded via `dlopen`/`LoadLibrary` at engine startup. + +3. **The `base/` module provides a factory entry point.** The factory queries registered backends by + name (or by platform capability) and returns a concrete instance behind the abstract interface. + +4. **Adding a new backend is a purely additive change.** No existing file in `base/` or in any + other module is modified. The only changes are a new sibling directory and one + `gpAddDependency(DYNAMIC /)` line. + +### GPBT expression (audio example) + +```cmake +# source/runtime/audio/base/CMakeLists.txt +gpStartModule(audio) + gpAddDependency(PUBLIC core) + + gpAddDependency(DYNAMIC audio/openal) # cross-platform + + if(WIN32) + gpAddDependency(DYNAMIC audio/xaudio2) + endif() + if(APPLE) + gpAddDependency(DYNAMIC audio/coreaudio) + endif() + if(GP_AUDIO_FMOD) + gpAddDependency(DYNAMIC audio/fmod) # commercial, opt-in + endif() +gpEndModule() +``` + +### The directory tree as architecture documentation + +The directory layout itself communicates the architecture. A student who opens +`source/runtime/audio/` sees every available implementation immediately. No further documentation +is needed to understand that audio is pluralistic and what implementations exist. +**The directory tree is the architecture made visible.** + +## Alternatives Considered + +### Option A: Subsystem-specific pattern (ad hoc per subsystem) + +Apply the base+backends pattern only where a subsystem author judges it necessary. + +**Why rejected:** Without a codified rule, pressure toward the "simpler" unified implementation +grows over time. A subsystem that starts with a single implementation tends to accumulate +platform-specific `#ifdef` blocks instead of factoring out a second backend. The pattern must be +mandatory to be effective. + +### Option B: Godot's servers + drivers two-tier split + +Each subsystem is split into a server (headless abstract API) at `servers/` and drivers +(concrete implementations) at `drivers/`. All drivers live in a single flat `drivers/` +directory regardless of which subsystem they implement. + +**Why rejected:** A flat `drivers/` directory makes it harder to find which drivers belong to which +subsystem. Our layout, placing backends as siblings of `base/` within the subsystem folder, keeps +the association visible without an indirection. + +### Option C: O3DE Gems, each backend is a first-class Gem + +In O3DE, each rendering backend is a separate Gem. Gems are universal units with no structural +distinction between a backend Gem and a feature Gem. + +**Why rejected:** Flattens the `base/` + backends hierarchy into a large sibling list at the Gem +level, losing the visual grouping that tells a student "these are all RHI backends." Our layout +preserves the grouping by making backends siblings *within* the subsystem directory. + +### Option D: Compile-time backend selection (one per build, no runtime loading) + +Select a single backend at configure time. No abstract factory; no `dlopen`. + +**Why rejected:** Forces separate build artifacts per backend combination. Prevents runtime backend +switching. Makes the null/stub backend an artificial special case. See [ADR-0002](./ADR-0002.md) for +the full argument against compile-time-only selection. + +## Consequences + +### Positive + +- **Uniform cognitive model.** Any engineer who understands how `rhi/` works can immediately + understand how `audio/`, `physics/`, and `parser/` work. The mental model transfers exactly. +- **Platform-specific backends are excluded from irrelevant builds.** XAudio2 is not compiled on + Linux. CoreAudio is not compiled on Windows. +- **Adding a backend is O(1) complexity relative to existing code.** No existing file is modified. +- **The directory tree is self-documenting.** Opening any pluralistic subsystem folder shows all + available backends. +- **Null/stub backends enable headless CI.** Physics and audio tests run on servers without GPU, + sound card, or physics hardware by loading null backends. + +### Negative + +- **Abstract factory design is harder than direct instantiation.** Subsystem authors must design the + `base/` interface carefully; a leaky abstraction that forces backend-specific knowledge into + `base/` defeats the purpose. +- **More directories per subsystem.** A subsystem with six backends has seven directories. +- **Dynamic loading adds startup latency.** Each backend `dlopen` call happens at engine startup. + Mitigated by lazy loading where possible. + +## Rationale and Cross-References + +- [ADR-0002](./ADR-0002.md): Establishes the `base/` + N-backends pattern for the RHI + specifically; this ADR generalizes it. +- [ADR-0003](./ADR-0003.md): `gpAddDependency(DYNAMIC ...)` is the GPBT primitive for runtime + dependency edges. +- [ADR-0004](./ADR-0004.md): All pluralistic subsystem directories live under `source/runtime/`. + +**Canonical references:** + +- Godot Engine: `servers/rendering/` + `drivers/vulkan/`, the conceptual ancestor of this pattern. +- Source 2 (Valve): incremental subsystem replacement (Rubikon, Panorama), the same invariant that + each subsystem must be replaceable in isolation. + + +[badge-proposed]: https://img.shields.io/badge/Status-Proposed-yellow.svg +[badge-accepted]: https://img.shields.io/badge/Status-Accepted-brightgreen.svg +[badge-rejected]: https://img.shields.io/badge/Status-Rejected-red.svg +[badge-deprecated]: https://img.shields.io/badge/Status-Deprecated-lightgrey.svg +[badge-superseded]: https://img.shields.io/badge/Status-Superseded-lightgrey.svg diff --git a/docs/adr/ADR-0008.md b/docs/adr/ADR-0008.md new file mode 100644 index 00000000..11a0bf41 --- /dev/null +++ b/docs/adr/ADR-0008.md @@ -0,0 +1,163 @@ +--- +id: ADR-0008 +title: GPBT Extracted to an Independent, Versioned Repository +date: 2026-05-08 +status: accepted +deciders: + - mallory-scotton +authors: + - mallory-scotton +--- + + +# ADR-0008: GPBT Extracted to an Independent, Versioned Repository + +![Accepted][badge-accepted] + +## Context + +[ADR-0003](./ADR-0003.md) established the **Graphical Playground Build Tool (GPBT)** as a CMake +orchestration layer that lives inside the engine's `cmake/` directory. This was the correct decision +during early development: keeping the build tool in the same repository simplified iteration and +removed version-coordination overhead. + +As the engine matured, two problems emerged: + +1. **GPBT has value beyond the GP Engine.** Its declarative module system, two-pass dependency + resolution, and `public/private/internal` enforcement are general-purpose mechanisms applicable + to any C++ monorepo. Embedding it inside the engine repository makes it unusable by other + projects without forking the entire engine. + +2. **GPBT's release cadence diverges from the engine's.** A bug fix in GPBT's scanner should not + require an engine release to propagate. Conversely, an engine API change should not force a GPBT + version bump. Entangling the two causes unnecessary coupling at the release level. + +## Decision + +GPBT is extracted to its own repository at +[`GraphicalPlayground/gp-build-tool`](https://github.com/GraphicalPlayground/gp-build-tool) and is +no longer committed directly into the GP Engine source tree. + +The GP Engine's `cmake/` directory contains a **pinned Git submodule** pointing to a specific tagged +release of GPBT. The engine's `CMakeLists.txt` includes GPBT via: + +```cmake +include(cmake/gp-build-tool/gp-build-tool.cmake) +``` + +The submodule pin ensures **reproducible builds**: every contributor, every CI run, and every +release uses the exact same version of GPBT. The pin is updated deliberately (via a PR) when the +engine adopts a new GPBT release. + +### What changes + + +| Before | After | +| --- | --- | +| `cmake/gp-build-tool/`, GPBT source files committed to the engine repo | `cmake/gp-build-tool/`, Git submodule pointing to a tagged GPBT release | +| GPBT version = engine version | GPBT has its own independent semver | +| GPBT bug fixes require an engine commit | GPBT bug fixes are released in the GPBT repo; the engine opts in by updating the submodule pin | +| No external users of GPBT | Any CMake-based C++ project can adopt GPBT independently | + + +### What does not change + +- The `cmake/` directory structure is identical from the engine's perspective. +- All GPBT macros (`gpStartModule`, `gpAddDependency`, `gpStartPlugin`, etc.) retain identical + semantics. +- The same `public/internal/` discipline that the engine applies to its own modules is applied to + the GPBT repository itself: `gp-build-tool.cmake` is the single public entry point; internal + helpers live under `internals/`; tests live under `tests/`. + +### Submodule initialization + +New contributors initialize the submodule with: + +```sh +git clone --recurse-submodules https://github.com/GraphicalPlayground/gp-engine.git +``` + +Or, for an already-cloned repository: + +```sh +git submodule update --init --recursive +``` + +The `.devcontainer/` setup (see [ADR-0005](./ADR-0005.md)) runs this automatically, so the +onboarding experience is unaffected. + +## Alternatives Considered + +### Option A: Keep GPBT embedded in the engine repository (status quo) + +No extraction; GPBT continues to live in `cmake/` as committed source files. + +**Why rejected:** Prevents other projects from adopting GPBT without forking the engine. Couples +GPBT's release cadence to the engine's. Makes GPBT contributions require understanding the entire +engine repository context. + +### Option B: Publish GPBT as a CMake package (find_package) + +Extract GPBT and publish it to a CMake package registry (vcpkg, Conan, or the CMake Package +Registry). Consumers find it via `find_package(GPBT)`. + +**Why rejected:** CMake package management for build-system tooling (as opposed to runtime +libraries) is fragmented and not universally supported across platforms. The Git submodule approach +is more transparent, the exact commit is visible in `.gitmodules` and in the engine's Git history, +and does not require external package infrastructure. + +### Option C: Copy GPBT into consuming projects (vendoring without submodule) + +Distribute GPBT as a tarball or zip that consuming projects copy into their `cmake/` directories. + +**Why rejected:** No automatic update mechanism. Bug fixes require manually re-copying files. +Git history of GPBT is lost inside the consuming project. The submodule approach provides all the +benefits of vendoring (reproducible, offline builds) plus a clear update mechanism and retained +history. + +### Option D: Extract to a separate directory in the same monorepo + +Keep GPBT in the engine's monorepo but in a separate directory at the root level (e.g., +`gp-build-tool/`) treated as a logically distinct project. + +**Why rejected:** Solves neither the release-cadence decoupling nor the external-reuse problem. +GPBT is still only visible to those who have cloned the engine repository. The version is still +implicitly tied to the engine version. + +## Consequences + +### Positive + +- **GPBT is reusable by other C++ projects.** Any project that wants declarative CMake module + management with `public/private/internal` enforcement can adopt GPBT independently. +- **Release cadence is decoupled.** A GPBT patch release does not require a GP Engine release. +- **The engine's build is reproducible.** The submodule pin guarantees that a checked-out commit of + the engine always builds with the exact same GPBT version. +- **GPBT contributions are scoped.** A contributor fixing a GPBT bug can do so in the GPBT + repository without touching the engine repository. PR reviews are smaller and focused. +- **GPBT has its own changelog, issues, and release notes.** + +### Negative + +- **Submodule UX is non-trivial for newcomers.** Forgetting `--recurse-submodules` on clone, or + forgetting to run `git submodule update` after a `git pull` that bumps the GPBT pin, leads to + broken builds. The `.devcontainer/` setup and `CONTRIBUTING.md` document this explicitly. +- **Two repositories to maintain.** GPBT bug reports, PRs, and releases now live in a separate + GitHub repository. Small teams may find this overhead disproportionate to the benefit. +- **Submodule pin updates require a deliberate PR.** Adopting a GPBT bug fix requires opening a PR + in the engine repository to bump the submodule pin. This is a feature (explicit, reviewable + adoption) but adds latency. + +## Rationale and Cross-References + +- [ADR-0003](./ADR-0003.md): Original decision to build GPBT as a CMake orchestration layer; this + ADR supersedes the "GPBT lives in `cmake/`" premise with a submodule approach. +- [ADR-0005](./ADR-0005.md): `.devcontainer/` handles submodule initialization automatically for + new contributors; the onboarding story is preserved. + + +[badge-proposed]: https://img.shields.io/badge/Status-Proposed-yellow.svg +[badge-accepted]: https://img.shields.io/badge/Status-Accepted-brightgreen.svg +[badge-rejected]: https://img.shields.io/badge/Status-Rejected-red.svg +[badge-deprecated]: https://img.shields.io/badge/Status-Deprecated-lightgrey.svg +[badge-superseded]: https://img.shields.io/badge/Status-Superseded-lightgrey.svg diff --git a/gp-engine.code-workspace b/gp-engine.code-workspace index 09b9d518..6dfc7869 100644 --- a/gp-engine.code-workspace +++ b/gp-engine.code-workspace @@ -3,59 +3,7 @@ { "path": ".", "name": "Engine Root" - }, - { - "path": "./source/shaders", - "name": "Shaders" - }, - { - "path": "./source/runtime/core", - "name": "Runtime / Core" - }, - { - "path": "./source/runtime/rhi", - "name": "Runtime / RHI" - }, - { - "path": "./source/runtime/hal", - "name": "Runtime / HAL" - }, - { - "path": "./source/runtime/engine", - "name": "Runtime / Engine" - }, - { - "path": "./source/runtime/renderer", - "name": "Runtime / Renderer" - }, - { - "path": "./source/runtime/application", - "name": "Runtime / Application" - }, - { - "path": "./source/runtime/parser", - "name": "Runtime / Parsers" - }, - { - "path": "./source/runtime/audio", - "name": "Runtime / Audio" - }, - { - "path": "./source/runtime/network", - "name": "Runtime / Network" - }, - { - "path": "./source/runtime/physics", - "name": "Runtime / Physics" - }, - { - "path": "./source/launch/editor", - "name": "Launch / Editor" - }, - { - "path": "./source/launch/standalone", - "name": "Launch / Standalone" - }, + } ], "settings": { /* C++ and ClangD settings */ @@ -77,7 +25,11 @@ "*.tests.cpp": "test-ts", "*.benchmark.cpp": "test-jsx", "*.benchmarks.cpp": "test-jsx", - "_category_.json": "tree" + "_category_.json": "tree", + "REFERENCES.md": "citation", + "*.supp": "tune", + "sanitizer.ignorelist": "lintstaged", + "*.natvis": "subtitles" }, "material-icon-theme.folders.customClones": [ { @@ -240,7 +192,16 @@ "bindings": "attachment", "state": "tasks", "system": "desktop", - "raytracing": "claude" + "raytracing": "claude", + "allocators": "bloc", + "zstd": "archive", + ".sanitizers": "review", + "window": "desktop", + "hardware": "core", + "targets": "target", + "linkers": "contract", + "frameworks": "lib", + "adr": "skills" }, /* File Associations */ "files.associations": { @@ -248,7 +209,10 @@ ".clang-format": "yaml", "Doxyfile": "ini", ".mailmap": "git-mailmap", - "VERSION": "plaintext" + "VERSION": "plaintext", + "*.supp": "ignore", + "sanitizer.ignorelist": "ignore", + "*.natvis": "xml" }, /* Test Execution */ "testMate.cpp.test.executables": "{build,Build,BUILD,out,Out,OUT,binaries,Binaries}/**/*{test,Test,TEST}*", @@ -339,6 +303,12 @@ "[yaml]": { "editor.tabSize": 2 }, + "[xml]": { + "editor.tabSize": 2 + }, + "[xaml]": { + "editor.tabSize": 2 + }, "[shellscript]": { "editor.tabSize": 2 }, @@ -366,6 +336,7 @@ "matepek.vscode-catch2-test-adapter", "Gruntfuggly.bettercomment", "ms-vscode.cmake-tools", + "KylinIdeTeam.cmake-intellisence", "cschlosser.doxdocgen", /* Shaders */ "TimGJones.hlsltools", @@ -377,7 +348,9 @@ /* Github */ "GitHub.vscode-pull-request-github", "github.vscode-github-actions", - "me-dutour-mathieu.vscode-github-actions" + "me-dutour-mathieu.vscode-github-actions", + /* Other */ + "FanaticPythoner.better-todo-tree" ], "unwantedRecommendations": [ /* Conflicts with Clangd */ diff --git a/gp-engine.natvis b/gp-engine.natvis new file mode 100644 index 00000000..7f2ef05f --- /dev/null +++ b/gp-engine.natvis @@ -0,0 +1,68 @@ + + + + + [{x}, {y}] + + x + y + + + + + [{x}, {y}, {z}] + + x + y + z + + + + + [{x}, {y}, {z}, {w}] + + x + y + z + w + + + + + [Pitch={pitch} Yaw={yaw} Roll={roll}] + + + + [Angle={angle}] + + + + [{x}, {y}, {z}, {w}] + + + + Real={real} Dual={dual} + + + + Origin={origin} Dir={direction} + + + + Center={center} Radius={radius} + + + + Normal={normal} Distance={distance} + + + + T={translation} R={rotation} S={scale} + + translation + rotation + scale + + + + diff --git a/source/runtime/core/private/memory/backends/Malloc.cpp b/source/runtime/core/private/memory/backends/Malloc.cpp index acc45d50..eb10b36b 100644 --- a/source/runtime/core/private/memory/backends/Malloc.cpp +++ b/source/runtime/core/private/memory/backends/Malloc.cpp @@ -48,4 +48,9 @@ bool Malloc::canGetAllocationSize() return false; } +USize Malloc::getActualAllocationSize(USize requestedSize, UInt32 /* alignment */) +{ + return requestedSize; // Default implementation has no way of determining this. +} + } // namespace gp::memory diff --git a/source/runtime/core/private/memory/backends/MallocAnsi.cpp b/source/runtime/core/private/memory/backends/MallocAnsi.cpp index e28a81b7..b09a7120 100644 --- a/source/runtime/core/private/memory/backends/MallocAnsi.cpp +++ b/source/runtime/core/private/memory/backends/MallocAnsi.cpp @@ -4,7 +4,7 @@ #include "memory/backends/MallocAnsi.hpp" #include "maths/base/Scalar.hpp" -#include "memory/Memory.hpp" +#include "memory/Memory.hpp" // IWYU pragma: keep #include "profiling/Profiler.hpp" #if GP_PLATFORM_USE_ANSI_POSIX_MALLOC #include diff --git a/source/runtime/core/private/platforms/generic/system/SharedLibrary.cpp b/source/runtime/core/private/platforms/generic/system/SharedLibrary.cpp new file mode 100644 index 00000000..bbcccb36 --- /dev/null +++ b/source/runtime/core/private/platforms/generic/system/SharedLibrary.cpp @@ -0,0 +1,27 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#include "platforms/generic/system/SharedLibrary.hpp" + +namespace gp::platform::generic +{ + +void* SharedLibrary::getHandle([[maybe_unused]] gp::StringView filename) noexcept +{ + // TODO: Add a fatal log: SharedLibrary::getHandle is not implemented for this platform. + return nullptr; +} + +void* SharedLibrary::getExport([[maybe_unused]] void* handle, [[maybe_unused]] gp::StringView procName) noexcept +{ + // TODO: Add a fatal log: SharedLibrary::getExport is not implemented for this platform. + return nullptr; +} + +void SharedLibrary::freeHandle([[maybe_unused]] void* handle) noexcept +{ + // TODO: Add a fatal log: SharedLibrary::freeHandle is not implemented for this platform. +} + +} // namespace gp::platform::generic diff --git a/source/runtime/core/private/platforms/windows/system/SharedLibrary.cpp b/source/runtime/core/private/platforms/windows/system/SharedLibrary.cpp new file mode 100644 index 00000000..5af44cef --- /dev/null +++ b/source/runtime/core/private/platforms/windows/system/SharedLibrary.cpp @@ -0,0 +1,114 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#include "platforms/windows/system/SharedLibrary.hpp" +#include + +namespace gp::platform::windows +{ + +void SharedLibrary::freeHandle(void* handle) noexcept +{ + if (handle != nullptr) + { + ::FreeLibrary(static_cast(handle)); + } +} + +void* SharedLibrary::getExport(void* handle, gp::StringView procName) noexcept +{ + if (handle == nullptr || procName.isEmpty()) + { + return nullptr; + } + + const FARPROC proc = ::GetProcAddress(static_cast(handle), procName.data()); + + return reinterpret_cast(proc); +} + +void* SharedLibrary::getHandle(gp::StringView filename) noexcept +{ + (void)filename; + + // TODO: Add logic to combine the search paths with the contents of the directory. + + DWORD errorMode = 0; + if (/* dllerrors */ false) + { + errorMode |= SEM_NOOPENFILEERRORBOX; + if (/* unattended */ false) + { + errorMode |= SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX; + } + } + + DWORD previousErrorMode = 0; + const BOOL havePreviousErrorMode = ::SetThreadErrorMode(errorMode, &previousErrorMode); + + void* handle = nullptr; // TODO: Add logic to load the library with search paths. + + if (havePreviousErrorMode) + { + ::SetThreadErrorMode(previousErrorMode, nullptr); + } + + return handle; +} + +void SharedLibrary::addDirectory(gp::StringView directory) noexcept +{ + // TODO: Implement directory addition logic, including normalization and caching of the dlls. + + (void)directory; + // gp::String normalizedDirectory = gp::Path::resolve(directory); + // gp::Path::normalizeDirectory(normalizedDirectory); + // gp::Path::makePlatformFilename(normalizedDirectory); + + if (/* !s_searchPaths.contains(directory) */ false) + { + // s_searchPaths.pushBack(normalizedDirectory); + + // Enumerate the shared libraries in the directory and cache them + { + // TODO: Implement directory enumeration for .dll files and cache them to s_searchPathsCache normalized. + } + } +} + +void SharedLibrary::pushDirectory(gp::StringView directory) noexcept +{ + // Set the DLL search directory to the specified directory + ::SetDllDirectory(directory.data()); + + // TODO: Add the directory to the search paths. + // Save the directory to the stack for later restoration + // s_searchPathsStack.pushBack(directory); +} + +void SharedLibrary::popDirectory(gp::StringView directory) noexcept +{ + // TODO: Implement logic to pop the directory from the stack and restore the previous search path. + (void)directory; + // Check for an empty stack before popping, indicating a potential error in the code flow + // ensure(!s_searchPathsStack.isEmpty(), "Attempted to pop from an empty DLL directory stack"); + + // Verify that the directory being popped matches the top of the stack + // check(s_searchPathsStack.top() == directory, "Mismatch in Push/Pop DLL directory operations"); + + // Pop the directory from the stack + // s_searchPathsStack.popBack(); + + // Restore the previous DLL search directory or reset to the default if the stack is empty + if (/* !s_searchPathsStack.isEmpty() */ false) + { + // ::SetDllDirectory(s_searchPathsStack.top().data()); + } + else + { + ::SetDllDirectory(""); + } +} + +} // namespace gp::platform::windows diff --git a/source/runtime/core/public/compilers/clang/ClangCompiler.hpp b/source/runtime/core/public/compilers/clang/ClangCompiler.hpp index a29f0250..25873b20 100644 --- a/source/runtime/core/public/compilers/clang/ClangCompiler.hpp +++ b/source/runtime/core/public/compilers/clang/ClangCompiler.hpp @@ -23,7 +23,11 @@ /// @brief Suppresses debug-info generation for a function in debug builds. /// Useful for thin wrappers that would otherwise pollute stepping in the debugger. -#define GP_NODEBUG [[clang::nodebug]] +#if defined(_MSC_VER) + #define GP_NODEBUG __declspec(noinline) +#else + #define GP_NODEBUG [[clang::nodebug]] +#endif /// @section Allocation attributes. diff --git a/source/runtime/core/public/compilers/intel/IntelCompiler.hpp b/source/runtime/core/public/compilers/intel/IntelCompiler.hpp index 0df69b1f..2e9fd6c9 100644 --- a/source/runtime/core/public/compilers/intel/IntelCompiler.hpp +++ b/source/runtime/core/public/compilers/intel/IntelCompiler.hpp @@ -12,8 +12,8 @@ #if defined(__INTEL_LLVM_COMPILER) - /// @section ICX (Intel oneAPI DPC++/C++, LLVM front-end). - /// Largely identical to Clang; reuse its attribute vocabulary. +/// @section ICX (Intel oneAPI DPC++/C++, LLVM front-end). +/// Largely identical to Clang; reuse its attribute vocabulary. #define GP_COMPILER_VERSION_MAJOR (__INTEL_LLVM_COMPILER / 10000) #define GP_COMPILER_VERSION_MINOR (__INTEL_LLVM_COMPILER / 100 % 100) @@ -41,7 +41,7 @@ #else // Classic ICC - /// @section ICC (classic Intel C++ compiler, GCC ABI). +/// @section ICC (classic Intel C++ compiler, GCC ABI). #define GP_COMPILER_VERSION_MAJOR (__INTEL_COMPILER / 100) #define GP_COMPILER_VERSION_MINOR (__INTEL_COMPILER % 100 / 10) @@ -70,4 +70,4 @@ // ICC supports __attribute__((flatten)) since version 19.1. #define GP_FLATTEN __attribute__((flatten)) -#endif // __INTEL_LLVM_COMPILER +#endif // __INTEL_LLVM_COMPILER diff --git a/source/runtime/core/public/containers/arrays/Vector.hpp b/source/runtime/core/public/containers/arrays/Vector.hpp index 88b93605..a29eb6df 100644 --- a/source/runtime/core/public/containers/arrays/Vector.hpp +++ b/source/runtime/core/public/containers/arrays/Vector.hpp @@ -3,3 +3,60 @@ // mailto:support AT graphical-playground DOT com #pragma once + +#include "containers/ContainerForward.hpp" // IWYU pragma: keep +#include "CoreMinimal.hpp" +#include + +namespace gp +{ + +/// @brief A dynamic array that can grow and shrink in size. +/// @tparam T The type of elements stored in the vector. +/// @tparam Allocator The allocator type used for memory management. +template +class Vector +{ +private: + template + friend class Vector; + +public: + using ValueType = T; + using AllocatorType = Allocator; + using SizeType = gp::USize; + using DifferenceType = gp::ISize; + using Reference = ValueType&; + using ConstReference = const ValueType&; + using Pointer = ValueType*; + using ConstPointer = const ValueType*; + using Iterator = Pointer; + using ConstIterator = ConstPointer; + using ReverseIterator = std::reverse_iterator; + using ConstReverseIterator = std::reverse_iterator; +}; + +} // namespace gp + +namespace gp::concepts +{ + +namespace detail +{ + +/// @brief Helper variable template to determine if a type is a gp::Vector. +template +constexpr bool IsVectorV = false; + +/// @brief Specialization for gp::Vector types. +template +constexpr bool IsVectorV> = true; + +} // namespace detail + +/// @brief Concept to check if a type is a gp::Vector. +/// @tparam T The type to check. +template +concept IsVector = detail::IsVectorV>; + +} // namespace gp::concepts diff --git a/source/runtime/core/public/containers/strings/FixedString.hpp b/source/runtime/core/public/containers/strings/FixedString.hpp new file mode 100644 index 00000000..74904248 --- /dev/null +++ b/source/runtime/core/public/containers/strings/FixedString.hpp @@ -0,0 +1,446 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "concepts/Concepts.hpp" +#include "containers/ContainerForward.hpp" +#include "containers/views/StringView.hpp" +#include "CoreMinimal.hpp" +#include "platforms/base/Platform.hpp" +#include + +namespace gp::container +{ + +/// @brief A fixed-size string that is allocated on the stack. +/// @tparam CharT The character type of the string. +/// @tparam N The maximum number of characters the string can hold, excluding the null terminator. +/// @note This class is designed to be a lightweight, stack-allocated string with a fixed capacity. +/// It does not perform any dynamic memory allocation and is suitable for scenarios where the maximum string size is +/// known at compile time. +template +class BasicFixedString +{ +public: + using SmallSizeType = std::conditional_t; + static_assert(N < std::numeric_limits::max(), "BasicFixedString: N must be less than 65535."); + + using ValueType = CharT; + using SizeType = SmallSizeType; + using DifferenceType = gp::ISize; + using Reference = ValueType&; + using ConstReference = const ValueType&; + using Pointer = ValueType*; + using ConstPointer = const ValueType*; + using Iterator = Pointer; + using ConstIterator = ConstPointer; + using ReverseIterator = std::reverse_iterator; + using ConstReverseIterator = std::reverse_iterator; + +private: + ValueType m_data[N + 1]{}; + SizeType m_size{ 0u }; + +public: + /// @brief Default constructor. Initializes an empty fixed string. + constexpr BasicFixedString() noexcept = default; + + /// @brief Constructs a fixed string from a string view. + /// @param[in] view The string view to construct from. + constexpr BasicFixedString(BasicStringView view) noexcept + { + assign(view); + } + + /// @brief Constructs a fixed string from a null-terminated string. + /// @param[in] str The null-terminated string to construct from. + constexpr BasicFixedString(const CharT* str) noexcept + { + assign(BasicStringView(str)); + } + +public: + /// @brief Gets a reference to the character at the specified position. + /// @param[in] pos The position of the character to retrieve. + /// @return A reference to the character at the specified position. + [[nodiscard]] constexpr Reference operator[](SizeType pos) noexcept + { + GP_ASSERT(pos < m_size); + return m_data[pos]; + } + + /// @brief Gets a const reference to the character at the specified position. + /// @param[in] pos The position of the character to retrieve. + /// @return A const reference to the character at the specified position. + [[nodiscard]] constexpr ConstReference operator[](SizeType pos) const noexcept + { + GP_ASSERT(pos < m_size); + return m_data[pos]; + } + + /// @brief Gets a pointer to the underlying character array. + /// @return A pointer to the underlying character array. + [[nodiscard]] constexpr Pointer operator*() noexcept + { + return m_data; + } + + /// @brief Gets a const pointer to the underlying character array. + /// @return A const pointer to the underlying character array. + [[nodiscard]] constexpr ConstPointer operator*() const noexcept + { + return m_data; + } + + /// @brief Compares this string with another string view for equality. + /// @param[in] other The string view to compare with. + /// @return true if the strings are equal, false otherwise. + [[nodiscard]] constexpr bool operator==(const BasicStringView& other) const noexcept + { + return static_cast>(*this) == other; + } + + /// @brief Compares this string with another string view for ordering. + /// @param[in] other The string view to compare with. + /// @return A value indicating the relative ordering of the strings. + [[nodiscard]] constexpr auto operator<=>(const BasicStringView& other) const noexcept + { + return static_cast>(*this) <=> other; + } + + /// @brief Compares this string with another fixed string for equality. + /// @param[in] other The fixed string to compare with. + /// @return true if the strings are equal, false otherwise. + template + [[nodiscard]] constexpr bool operator==(const BasicFixedString& other) const noexcept + { + return static_cast>(*this) == static_cast>(other); + } + + /// @brief Compares this string with another fixed string for ordering. + /// @param[in] other The fixed string to compare with. + /// @return A value indicating the relative ordering of the strings. + template + [[nodiscard]] constexpr auto operator<=>(const BasicFixedString& other) const noexcept + { + return static_cast>(*this) <=> static_cast>(other); + } + + /// @brief Converts this fixed string to a string view. + /// @return A string view representing the contents of this fixed string. + [[nodiscard]] constexpr operator BasicStringView() const noexcept + { + return BasicStringView(m_data, m_size); + } + + /// @brief Appends a string view to this fixed string. + /// @param[in] view The string view to append. + /// @return A reference to this fixed string. + constexpr BasicFixedString& operator+=(BasicStringView view) noexcept + { + append(view); + return *this; + } + + /// @brief Appends a character to this fixed string. + /// @param[in] ch The character to append. + /// @return A reference to this fixed string. + BasicFixedString& operator+=(CharT ch) noexcept + { + pushBack(ch); + return *this; + } + +public: + /// @brief Gets a reference to the character at the specified position with bounds checking. + /// @param[in] pos The position of the character to retrieve. + /// @return A reference to the character at the specified position. + [[nodiscard]] constexpr Reference at(SizeType pos) + { + GP_ASSERT(pos < m_size); + return m_data[pos]; + } + + /// @brief Gets a const reference to the character at the specified position with bounds checking. + /// @param[in] pos The position of the character to retrieve. + /// @return A const reference to the character at the specified position. + [[nodiscard]] constexpr ConstReference at(SizeType pos) const + { + GP_ASSERT(pos < m_size); + return m_data[pos]; + } + + /// @brief Gets a reference to the first character in the string. + /// @return A reference to the first character in the string. + [[nodiscard]] constexpr Reference front() noexcept + { + GP_ASSERT(!isEmpty()); + return m_data[0]; + } + + /// @brief Gets a const reference to the first character in the string. + /// @return A const reference to the first character in the string. + [[nodiscard]] constexpr ConstReference front() const noexcept + { + GP_ASSERT(!isEmpty()); + return m_data[0]; + } + + /// @brief Gets a reference to the last character in the string. + /// @return A reference to the last character in the string. + [[nodiscard]] constexpr Reference back() noexcept + { + GP_ASSERT(!isEmpty()); + return m_data[m_size - 1]; + } + + /// @brief Gets a const reference to the last character in the string. + /// @return A const reference to the last character in the string. + [[nodiscard]] constexpr ConstReference back() const noexcept + { + GP_ASSERT(!isEmpty()); + return m_data[m_size - 1]; + } + + /// @brief Checks if the string is empty. + /// @return true if the string is empty, false otherwise. + [[nodiscard]] constexpr bool isEmpty() const noexcept + { + return m_size == 0u; + } + + /// @brief Gets the number of characters in the string. + /// @return The number of characters in the string. + [[nodiscard]] constexpr SizeType size() const noexcept + { + return m_size; + } + + /// @brief Gets the number of characters in the string. + /// @return The number of characters in the string. + [[nodiscard]] constexpr SizeType length() const noexcept + { + return m_size; + } + + /// @brief Gets the maximum number of characters the string can hold, excluding the null terminator. + /// @return The maximum number of characters the string can hold, excluding the null terminator. + [[nodiscard]] constexpr SizeType capacity() const noexcept + { + return N; + } + + /// @brief Gets the maximum number of characters the string can hold, excluding the null terminator. + /// @return The maximum number of characters the string can hold, excluding the null terminator. + [[nodiscard]] constexpr SizeType maxSize() const noexcept + { + return N; + } + + /// @brief Gets a pointer to the underlying character array. + /// @return A pointer to the underlying character array. + [[nodiscard]] constexpr ConstPointer data() const noexcept + { + return m_data; + } + + /// @brief Gets a pointer to the underlying character array. + /// @return A pointer to the underlying character array. + [[nodiscard]] constexpr Pointer data() noexcept + { + return m_data; + } + + /// @brief Converts this fixed string to a string view. + /// @return A string view representing the contents of this fixed string. + [[nodiscard]] constexpr BasicStringView view() const noexcept + { + return BasicStringView(m_data, m_size); + } + + /// @brief Gets a const pointer to the underlying null-terminated character array. + /// @return A const pointer to the underlying null-terminated character array. + [[nodiscard]] constexpr ConstPointer cString() const noexcept + { + return m_data; + } + + /// @brief Gets a pointer to the underlying null-terminated character array. + /// @return A pointer to the underlying null-terminated character array. + [[nodiscard]] constexpr Pointer cString() noexcept + { + return m_data; + } + + /// @brief Gets a const iterator to the beginning of the string. + /// @return A const iterator to the beginning of the string. + [[nodiscard]] constexpr ConstIterator begin() const noexcept + { + return m_data; + } + + /// @brief Gets an iterator to the beginning of the string. + /// @return An iterator to the beginning of the string. + [[nodiscard]] constexpr Iterator begin() noexcept + { + return m_data; + } + + /// @brief Gets a const iterator to the end of the string. + /// @return A const iterator to the end of the string. + [[nodiscard]] constexpr ConstIterator end() const noexcept + { + return m_data + m_size; + } + + /// @brief Gets an iterator to the end of the string. + /// @return An iterator to the end of the string. + [[nodiscard]] constexpr Iterator end() noexcept + { + return m_data + m_size; + } + + /// @brief Gets a const iterator to the beginning of the string. + /// @return A const iterator to the beginning of the string. + [[nodiscard]] constexpr ConstIterator cbegin() const noexcept + { + return m_data; + } + + /// @brief Gets a const iterator to the end of the string. + /// @return A const iterator to the end of the string. + [[nodiscard]] constexpr ConstIterator cend() const noexcept + { + return m_data + m_size; + } + + /// @brief Gets a const reverse iterator to the beginning of the string. + /// @return A const reverse iterator to the beginning of the string. + [[nodiscard]] constexpr ConstReverseIterator rbegin() const noexcept + { + return ConstReverseIterator(end()); + } + + /// @brief Gets a reverse iterator to the beginning of the string. + /// @return A reverse iterator to the beginning of the string. + [[nodiscard]] constexpr ReverseIterator rbegin() noexcept + { + return ReverseIterator(end()); + } + + /// @brief Gets a const reverse iterator to the end of the string. + /// @return A const reverse iterator to the end of the string. + [[nodiscard]] constexpr ConstReverseIterator rend() const noexcept + { + return ConstReverseIterator(begin()); + } + + /// @brief Gets a reverse iterator to the end of the string. + /// @return A reverse iterator to the end of the string. + [[nodiscard]] constexpr ReverseIterator rend() noexcept + { + return ReverseIterator(begin()); + } + + /// @brief Gets a const reverse iterator to the beginning of the string. + /// @return A const reverse iterator to the beginning of the string. + [[nodiscard]] constexpr ConstReverseIterator crbegin() const noexcept + { + return ConstReverseIterator(end()); + } + + /// @brief Gets a const reverse iterator to the end of the string. + /// @return A const reverse iterator to the end of the string. + [[nodiscard]] constexpr ConstReverseIterator crend() const noexcept + { + return ConstReverseIterator(begin()); + } + + /// @brief Clears the string, setting its size to zero and null-terminating it. + /// @note This does not deallocate any memory, as the string is stack-allocated and has a fixed capacity. + constexpr void clear() noexcept + { + m_size = 0u; + m_data[0] = CharT{ 0 }; + } + + /// @brief Assigns the contents of a string view to this fixed string. + /// @param[in] view The string view to assign from. + constexpr void assign(BasicStringView view) noexcept + { + GP_ASSERT(view.size() <= N && "FixedString capacity exceeded"); + m_size = static_cast(view.size()); + + if (m_size > 0) + { + std::copy_n(view.data(), m_size, m_data); + } + m_data[m_size] = CharT{ 0 }; + } + + /// @brief Appends the contents of a string view to this fixed string. + /// @param[in] view The string view to append. + constexpr void append(BasicStringView view) noexcept + { + GP_ASSERT(m_size + view.size() <= N && "FixedString capacity exceeded"); + + if (!view.isEmpty()) + { + std::copy_n(view.data(), view.size(), m_data + m_size); + m_size += static_cast(view.size()); + m_data[m_size] = CharT{ 0 }; + } + } + + /// @brief Appends a character to this fixed string. + /// @param[in] ch The character to append. + constexpr void pushBack(CharT ch) noexcept + { + GP_ASSERT(m_size < N && "FixedString capacity exceeded"); + m_data[m_size++] = ch; + m_data[m_size] = CharT{ 0 }; + } + + /// @brief Removes the last character from this fixed string. + /// @note This does not deallocate any memory, as the string is stack-allocated and has a fixed capacity. + constexpr void popBack() noexcept + { + GP_ASSERT(m_size > 0 && "Cannot pop from an empty string"); + --m_size; + m_data[m_size] = CharT{ 0 }; + } +}; + +} // namespace gp::container + +namespace gp +{ + +/// @brief Owning, stack-allocated string with a fixed capacity. +/// @tparam N The maximum number of characters the string can hold, excluding the null terminator. +template +using FixedString = container::BasicFixedString; + +/// @brief Owning, stack-allocated string with a fixed capacity. +/// @tparam N The maximum number of characters the string can hold, excluding the null terminator. +template +using FixedWString = container::BasicFixedString; + +/// @brief Owning, stack-allocated string with a fixed capacity. +/// @tparam N The maximum number of characters the string can hold, excluding the null terminator. +template +using FixedU8String = container::BasicFixedString; + +/// @brief Owning, stack-allocated string with a fixed capacity. +/// @tparam N The maximum number of characters the string can hold, excluding the null terminator. +template +using FixedU16String = container::BasicFixedString; + +/// @brief Owning, stack-allocated string with a fixed capacity. +/// @tparam N The maximum number of characters the string can hold, excluding the null terminator. +template +using FixedU32String = container::BasicFixedString; + +} // namespace gp diff --git a/source/runtime/core/public/containers/strings/String.hpp b/source/runtime/core/public/containers/strings/String.hpp new file mode 100644 index 00000000..dd6528d3 --- /dev/null +++ b/source/runtime/core/public/containers/strings/String.hpp @@ -0,0 +1,96 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "concepts/Concepts.hpp" +#include "containers/ContainerForward.hpp" +#include "CoreMinimal.hpp" + +namespace gp::container +{ + +template +class BasicString +{ +public: + using ValueType = CharT; + using SizeType = gp::USize; + using DifferenceType = gp::ISize; + using Reference = ValueType&; + using ConstReference = const ValueType&; + using Pointer = ValueType*; + using ConstPointer = const ValueType*; + using Iterator = Pointer; + using ConstIterator = ConstPointer; + using ReverseIterator = std::reverse_iterator; + using ConstReverseIterator = std::reverse_iterator; + +public: + [[nodiscard]] constexpr Reference operator[](SizeType pos) noexcept; + [[nodiscard]] constexpr ConstReference operator[](SizeType pos) const noexcept; + + [[nodiscard]] constexpr bool operator==(const BasicString& other) const noexcept; + [[nodiscard]] constexpr auto operator<=>(const BasicString& other) const noexcept; + + [[nodiscard]] constexpr bool operator==(const BasicStringView& other) const noexcept; + [[nodiscard]] constexpr auto operator<=>(const BasicStringView& other) const noexcept; + + [[nodiscard]] constexpr bool operator==(const CharT* other) const noexcept; + [[nodiscard]] constexpr auto operator<=>(const CharT* other) const noexcept; + + [[nodiscard]] constexpr Pointer operator*() noexcept; + [[nodiscard]] constexpr ConstPointer operator*() const noexcept; + +public: + [[nodiscard]] constexpr Reference at(SizeType pos); + [[nodiscard]] constexpr ConstReference at(SizeType pos) const; + + [[nodiscard]] constexpr bool isEmpty() const noexcept; + [[nodiscard]] constexpr SizeType size() const noexcept; + [[nodiscard]] constexpr SizeType length() const noexcept; + [[nodiscard]] constexpr SizeType capacity() const noexcept; + [[nodiscard]] constexpr SizeType maxSize() const noexcept; + + [[nodiscard]] constexpr ConstPointer data() const noexcept; + [[nodiscard]] constexpr Pointer data() noexcept; + + [[nodiscard]] constexpr ConstIterator begin() const noexcept; + [[nodiscard]] constexpr Iterator begin() noexcept; + [[nodiscard]] constexpr ConstIterator end() const noexcept; + [[nodiscard]] constexpr Iterator end() noexcept; + + [[nodiscard]] constexpr ConstIterator cbegin() const noexcept; + [[nodiscard]] constexpr ConstIterator cend() const noexcept; + + [[nodiscard]] constexpr ConstReverseIterator rbegin() const noexcept; + [[nodiscard]] constexpr ReverseIterator rbegin() noexcept; + [[nodiscard]] constexpr ConstReverseIterator rend() const noexcept; + [[nodiscard]] constexpr ReverseIterator rend() noexcept; + + [[nodiscard]] constexpr ConstReverseIterator crbegin() const noexcept; + [[nodiscard]] constexpr ConstReverseIterator crend() const noexcept; +}; + +} // namespace gp::container + +namespace gp +{ + +/// @brief Owning, read/write `char` string. +using String = container::BasicString; + +/// @brief Owning, read/write `wchar_t` string. +using WString = container::BasicString; + +/// @brief Owning, read/write `char8_t` string. +using U8String = container::BasicString; + +/// @brief Owning, read/write `char16_t` string. +using U16String = container::BasicString; + +/// @brief Owning, read/write `char32_t` string. +using U32String = container::BasicString; + +} // namespace gp diff --git a/source/runtime/core/public/containers/views/StringView.hpp b/source/runtime/core/public/containers/views/StringView.hpp index 3f7e7059..4b43726a 100644 --- a/source/runtime/core/public/containers/views/StringView.hpp +++ b/source/runtime/core/public/containers/views/StringView.hpp @@ -769,7 +769,9 @@ template std::basic_ostream& operator<<(std::basic_ostream& os, const gp::container::BasicStringView& sv) { if (sv.data()) + { os.write(sv.data(), static_cast(sv.size())); + } return os; } diff --git a/source/runtime/core/public/memory/GlobalMemory.hpp b/source/runtime/core/public/memory/GlobalMemory.hpp index 6659abfc..175fdf0a 100644 --- a/source/runtime/core/public/memory/GlobalMemory.hpp +++ b/source/runtime/core/public/memory/GlobalMemory.hpp @@ -16,10 +16,33 @@ namespace detail /// @brief Pointer to the global memory allocator instance. extern GP_CORE_API Malloc* g_malloc; +#if !GP_IS_MONOLITHIC +/// @brief Pointer to the local shadow memory allocator instance, used to bypass DLL Import overhead. +extern Malloc* g_localShadowMalloc; +#endif + } // namespace detail /// @brief Retrieves the global memory allocator instance. +/// @details This function is garanted to be thread-safe from being called in the bootstrapping phase of the engine, and +/// is safe to call from any thread after the engine has been initialized. /// @return A pointer to the global memory allocator instance. [[nodiscard]] GP_CORE_API Malloc* getGlobalMalloc(); +/// @brief Gets the memory allocator while completely bypassing DLL Import (IAT) overhead. +/// @note This function is intended for use in performance-critical code where the overhead of DLL Import Table (IAT) +/// lookups can be avoided. It provides a direct access to the memory allocator without the usual DLL import overhead, +/// which can be beneficial in scenarios where memory allocation is a frequent operation and performance is paramount. +/// @details The module system is responsible for ensuring that the global memory allocator is initialized before this +/// function is called. It is +/// @return A pointer to the memory allocator, bypassing DLL Import overhead. +[[nodiscard]] GP_FORCEINLINE_HINT Malloc* getInlineMalloc() +{ +#if !GP_IS_MONOLITHIC + return detail::g_localShadowMalloc; +#else + return detail::g_malloc; +#endif +} + } // namespace gp::memory diff --git a/source/runtime/core/public/memory/Memory.hpp b/source/runtime/core/public/memory/Memory.hpp index c267867c..4dd9f39e 100644 --- a/source/runtime/core/public/memory/Memory.hpp +++ b/source/runtime/core/public/memory/Memory.hpp @@ -6,6 +6,9 @@ #include "concepts/Concepts.hpp" #include "CoreMinimal.hpp" +#include "memory/GlobalMemory.hpp" +#include "memory/MemoryBase.hpp" +#include "platforms/base/Platform.hpp" #include "platforms/base/PlatformMemory.hpp" #include #include @@ -204,4 +207,20 @@ inline void systemDeallocate(void* ptr) ::free(ptr); } +/// @brief Get the actual size of an allocation, which may be larger than the requested size. +/// @param[in] requestedSize The requested size of the memory block, in bytes. +/// @param[in] alignment The alignment requirement for the allocated memory block, in bytes. +/// @return The actual size of the allocated memory block, in bytes. +/// @details For some allocators this will return the actual size that should be requested to eliminate +/// internal fragmentation. The return value will always be >= requestedSize. This can be used to grow +/// and shrink containers to optimal sizes. +GP_FORCEINLINE gp::USize getActualAllocationSize(gp::USize requestedSize, gp::UInt32 alignment = kDefaultAlignment) +{ + if (!gp::memory::getInlineMalloc()) [[unlikely]] + { + return requestedSize; + } + return gp::memory::getInlineMalloc()->getActualAllocationSize(requestedSize, alignment); +} + } // namespace gp::memory diff --git a/source/runtime/core/public/memory/MemoryForward.hpp b/source/runtime/core/public/memory/MemoryForward.hpp index b36db1a8..79077313 100644 --- a/source/runtime/core/public/memory/MemoryForward.hpp +++ b/source/runtime/core/public/memory/MemoryForward.hpp @@ -14,6 +14,14 @@ namespace gp::memory class Malloc; class MallocAnsi; +/// @section Allocator forward declarations + +template +class SizedAllocatorBase; + +template +class SizedHeapAllocator; + } // namespace gp::memory namespace gp diff --git a/source/runtime/core/public/memory/allocators/AllocatorUtilities.hpp b/source/runtime/core/public/memory/allocators/AllocatorUtilities.hpp new file mode 100644 index 00000000..1fa8df10 --- /dev/null +++ b/source/runtime/core/public/memory/allocators/AllocatorUtilities.hpp @@ -0,0 +1,206 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground.com/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "CoreMinimal.hpp" +#include "memory/Memory.hpp" +#include "memory/MemoryBase.hpp" +#include "platforms/base/Platform.hpp" +#include + +namespace gp +{ + +/// @brief Opaque type used to represent container elements whose type is not known at compile time. +struct UntypedContainerElement +{}; + +namespace memory::detail +{ + +/// @brief Computes the new capacity when shrinking a container, minimizing excessive reallocation. +/// @tparam SizeType Container size type. +/// @param[in] newSize Requested new size in elements. +/// @param[in] currentSize Current capacity in elements. +/// @param[in] bytesPerElement Size of a single element in bytes. +/// @param[in] allowQuantize If true, snaps the resulting capacity to the underlying allocator block size. +/// @param[in] alignment Memory alignment requirement. +/// @return The computed capacity in elements. +template +[[nodiscard]] GP_FORCEINLINE_HINT constexpr SizeType defaultCalculateSlackShrink( + SizeType newSize, + SizeType currentSize, + gp::USize bytesPerElement, + bool allowQuantize, + gp::UInt32 alignment = gp::memory::kDefaultAlignment +) +{ + SizeType result{ 0 }; + // TODO: Assert that newSize < currentSize + + const SizeType currentSlackElements = currentSize - newSize; + const gp::USize currentSlackBytes = currentSlackElements * bytesPerElement; + const bool hasTooManySlackBytes = currentSlackBytes >= 16'384; + const bool hasTooManySlackElements = 3 * newSize < 2 * currentSize; + + if ((hasTooManySlackBytes || hasTooManySlackElements) && (currentSlackElements > 64 || !newSize)) + { + result = newSize; + if (result > 0) + { + if (allowQuantize) + { + result = static_cast( + gp::memory::getActualAllocationSize(result * bytesPerElement, alignment) / bytesPerElement + ); + } + } + } + else + { + result = currentSize; + } + + return result; +} + +/// @brief Computes the optimal capacity for a strict container reservation. +/// @tparam SizeType Container size type. +/// @param[in] newSize Minimum required capacity in elements. +/// @param[in] bytesPerElement Size of a single element in bytes. +/// @param[in] allowQuantize If true, snaps the resulting capacity to the underlying allocator block size. +/// @param[in] alignment Memory alignment requirement. +/// @return The computed capacity in elements. +template +[[nodiscard]] GP_FORCEINLINE_HINT constexpr SizeType defaultCalculateSlackReserve( + SizeType newSize, gp::USize bytesPerElement, bool allowQuantize, gp::UInt32 alignment = kDefaultAlignment +) noexcept +{ + SizeType result = newSize; + + if (allowQuantize) + { + result = static_cast( + gp::memory::getActualAllocationSize(result * bytesPerElement, alignment) / bytesPerElement + ); + if (newSize > result) + { + result = std::numeric_limits::max(); + } + } + + return result; +} + +#ifndef GP_CONTAINER_INITIAL_ALLOC_ZERO_SLACK + #define GP_CONTAINER_INITIAL_ALLOC_ZERO_SLACK GP_TRUE +#endif + +#if defined(GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR) && !defined(GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR) + #error If GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR is defined you must also define GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR +#endif +#if defined(GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR) && !defined(GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR) + #error If GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR is defined you must also define GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR +#endif + +#ifndef GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR + #if GP_AGGRESSIVE_MEMORY_SAVING + #define GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR 1 + #else + #define GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR 3 + #endif +#endif + +#ifndef GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR + #if GP_AGGRESSIVE_MEMORY_SAVING + #define GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR 4 + #else + #define GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR 8 + #endif +#endif + +static_assert( + GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR > 0, + "GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR must be greater than 0" +); +static_assert( + GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR > GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR, + "GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR must be greater than GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR" +); + +/// @brief Computes the next capacity when a container exhausts its current allocation. +/// @tparam SizeType Container size type. +/// @param[in] newSize Minimum capacity required to fulfill the current operation. +/// @param[in] currentSize Current capacity in elements. +/// @param[in] bytesPerElement Size of a single element in bytes. +/// @param[in] allowQuantize If true, snaps the resulting capacity to the underlying allocator block size. +/// @param[in] alignment Memory alignment requirement. +/// @return The computed capacity in elements. +template +[[nodiscard]] GP_FORCEINLINE_HINT constexpr SizeType defaultCalculateSlackGrow( + SizeType newSize, + SizeType currentSize, + gp::USize bytesPerElement, + bool allowQuantize, + gp::UInt32 alignment = kDefaultAlignment +) noexcept +{ +#if GP_AGGRESSIVE_MEMORY_SAVING + const gp::USize firstGrow = 1; + const gp::USize constantGrow = 0; +#else + const gp::USize firstGrow = 4; + const gp::USize constantGrow = 16; +#endif + + SizeType result; + // TODO: Assert that newSize > currentSize and newSize > 0 + + gp::USize grow = firstGrow; + +#if GP_CONTAINER_INITIAL_ALLOC_ZERO_SLACK + if (currentSize) + { + grow = static_cast(newSize) + + GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR * static_cast(newSize) / + GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR + + constantGrow; + } + else if (static_cast(newSize) > grow) + { + grow = static_cast(newSize); + } +#else + if (currentSize || static_cast(newSize) > grow) + { + grow = static_cast(newSize) + + GP_CONTAINER_SLACK_GROWTH_FACTOR_NUMERATOR * static_cast(newSize) / + GP_CONTAINER_SLACK_GROWTH_FACTOR_DENOMINATOR + + constantGrow; + } +#endif + + if (allowQuantize) + { + result = static_cast( + gp::memory::getActualAllocationSize(grow * bytesPerElement, alignment) / bytesPerElement + ); + } + else + { + result = static_cast(grow); + } + + if (newSize > result) + { + result = std::numeric_limits::max(); + } + + return result; +} + +} // namespace memory::detail + +} // namespace gp diff --git a/source/runtime/core/public/memory/allocators/SizedAllocatorBase.hpp b/source/runtime/core/public/memory/allocators/SizedAllocatorBase.hpp new file mode 100644 index 00000000..d69b0bc9 --- /dev/null +++ b/source/runtime/core/public/memory/allocators/SizedAllocatorBase.hpp @@ -0,0 +1,333 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground.com/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "compilers/clang/ClangCompiler.hpp" +#include "concepts/Concepts.hpp" // IWYU pragma: keep +#include "CoreMinimal.hpp" // IWYU pragma: keep +#include "memory/allocators/AllocatorUtilities.hpp" +#include "memory/MemoryBase.hpp" +#include "platforms/base/Platform.hpp" +#include +#include + +namespace gp::memory +{ + +namespace detail +{ + +/// @brief A base class for sized allocators that provides common functionality and type definitions. +template +struct BitsToSizeType +{ + /// @details Compile-time failure for unsupported allocator index sizes. + static_assert(Bits == Bits + 1, "Unsupported allocator index size."); +}; + +// clang-format off + +/// @details We use signed integer types for size types to allow for negative values to be used safely. +template<> struct BitsToSizeType<8> { using Type = gp::Int8; }; +template<> struct BitsToSizeType<16> { using Type = gp::Int16; }; +template<> struct BitsToSizeType<32> { using Type = gp::Int32; }; +template<> struct BitsToSizeType<64> { using Type = gp::Int64; }; + +// clang-format on + +} // namespace detail + +/// @brief A base class for sized allocators that provides common functionality and type definitions. +/// @tparam IndexSize The size of the index type used by the allocator (in bits). +/// @tparam SubClass The derived class that inherits from this base class. +template +class SizedAllocatorBase +{ +public: + using SizeType = detail::BitsToSizeType::Type; + using USizeType = std::make_unsigned_t; + +public: + static constexpr bool kNeedsElementType = false; + static constexpr bool kRequireRangeCheck = true; + +public: + /// @brief Untyped allocation interface for containers that manage their own element types. + class ForAnyElementType + { + private: + template + friend class SizedAllocatorBase; + + private: + gp::UntypedContainerElement* m_data{ nullptr }; + + public: + /// @brief Default constructor, initializes with no allocation. + GP_NODEBUG constexpr ForAnyElementType() noexcept + : m_data(nullptr) + {} + + /// @brief Copying allocators is explicitly disabled. + ForAnyElementType(const ForAnyElementType&) = delete; + ForAnyElementType& operator=(const ForAnyElementType&) = delete; + + /// @brief Destructor, frees any held memory via the derived allocator class. + GP_NODEBUG GP_FORCEINLINE_HINT constexpr ~ForAnyElementType() noexcept + { + if (m_data) + { + // C++23 deducing `this` cannot be applied to destructors, so the CRTP cast remains necessary here. + static_cast(this)->deallocate(m_data); + } + } + + public: + /// @brief Transfers memory ownership from a different allocator type. + /// @tparam OtherAllocator Source allocator type. + /// @tparam Self Deducing this type. + /// @param[in] self The target allocator instance. + /// @param[in,out] other The source allocator instance to steal memory from. + template + GP_NODEBUG GP_FORCEINLINE_HINT constexpr void + takeOwnershipFromOther(this Self&& self, typename OtherAllocator::ForAnyElementType& other) + { + // TODO: Add a real check for allocator compatibility here. + [[assume(static_cast(&self) != static_cast(&other))]]; + + if (self.m_data) + { + self.deallocate(self.m_data); + } + + // Optimized move, generates zero-cost register swaps. + self.m_data = std::exchange(other.m_data, nullptr); + } + + /// @brief Transfers memory ownership from another allocator of the same derived type. + /// @tparam Self Deducing this type. + /// @param[in] self The target allocator instance. + /// @param[in,out] other The source allocator instance to steal memory from. + template + GP_NODEBUG GP_FORCEINLINE_HINT constexpr void + takeOwnership(this Self&& self, SubClass::ForAnyElementType& other) + { + self.template takeOwnershipFromOther(other); + } + + /// @brief Retrieves the raw pointer to the managed memory block. + /// @return Pointer to the untyped allocation. + [[nodiscard]] GP_NODEBUG GP_FORCEINLINE_HINT constexpr gp::UntypedContainerElement* + getAllocation() const noexcept + { + return m_data; + } + + /// @brief Checks if the allocator currently holds an active memory allocation. + /// @return True if memory is allocated, false otherwise. + [[nodiscard]] GP_NODEBUG constexpr bool hasAllocation() const noexcept + { + return m_data != nullptr; + } + + /// @brief Modifies the size of the current memory allocation. + /// @tparam Self Deducing this type. + /// @param[in] self The allocator instance. + /// @param[in] currentSize Current capacity in elements. + /// @param[in] newSize Requested capacity in elements. + /// @param[in] bytesPerElement Size of a single element in bytes. + template + GP_NODEBUG void resizeAllocation( + this Self&& self, [[maybe_unused]] SizeType currentSize, SizeType newSize, gp::USize bytesPerElement + ) + { + if (!self.m_data && newSize == 0) + { + return; + } + + static_assert(sizeof(SizeType) <= sizeof(gp::USize), "gp::USize is expected to handle all possible sizes."); + + bool isInvalidResize = newSize < 0 || bytesPerElement < 1 || + bytesPerElement > static_cast(std::numeric_limits::max()); + if constexpr (sizeof(SizeType) == sizeof(gp::USize)) + { + isInvalidResize = + isInvalidResize || (static_cast(static_cast(newSize)) > + static_cast(std::numeric_limits::max()) / bytesPerElement); + } + + if (isInvalidResize) [[unlikely]] + { + // TODO: Implement a proper error handling mechanism for invalid resize operations. + // OnInvalidSizedHeapAllocatorNum(indexSize, newSize, bytesPerElement); + } + + self.m_data = + static_cast(self.reallocate(self.m_data, newSize, bytesPerElement)); + } + + /// @brief Modifies the size of the current memory allocation, respecting strict alignment requirements. + /// @tparam Self Deducing this type. + /// @param[in] self The allocator instance. + /// @param[in] currentSize Current capacity in elements. + /// @param[in] newSize Requested capacity in elements. + /// @param[in] bytesPerElement Size of a single element in bytes. + /// @param[in] alignment Required memory alignment. + template + GP_NODEBUG void resizeAllocation( + this Self&& self, + [[maybe_unused]] SizeType currentSize, + SizeType newSize, + gp::USize bytesPerElement, + gp::UInt32 alignment + ) + { + if (!self.m_data && newSize == 0) + { + return; + } + + static_assert(sizeof(SizeType) <= sizeof(gp::USize), "gp::USize is expected to handle all possible sizes."); + + bool isInvalidResize = newSize < 0 || bytesPerElement < 1 || + bytesPerElement > static_cast(std::numeric_limits::max()); + if constexpr (sizeof(SizeType) == sizeof(gp::USize)) + { + isInvalidResize = + isInvalidResize || (static_cast(static_cast(newSize)) > + static_cast(std::numeric_limits::max()) / bytesPerElement); + } + + if (isInvalidResize) [[unlikely]] + { + // TODO: Implement a proper error handling mechanism for invalid resize operations. + // OnInvalidSizedHeapAllocatorNum(indexSize, newSize, bytesPerElement); + } + + self.m_data = static_cast( + self.reallocate(self.m_data, newSize, bytesPerElement, alignment) + ); + } + + [[nodiscard]] GP_NODEBUG constexpr gp::USize + getAllocatedSize(SizeType currentSize, gp::USize bytesPerElement) const + { + return currentSize * bytesPerElement; + } + + [[nodiscard]] GP_NODEBUG constexpr SizeType getInitialCapacity() const noexcept + { + return 0; + } + + /// @brief Platform-specific hook to reallocate a memory block. Must be implemented by the derived class. + /// @param[in] data Existing memory block to resize, or null to allocate new. + /// @param[in] newSize Requested new size in elements. + /// @param[in] bytesPerElement Size of a single element in bytes. + /// @param[in] alignment Required memory alignment. + /// @return Pointer to the newly allocated or resized memory block. + [[nodiscard]] void* reallocate( + void* data, + SizeType newSize, + gp::USize bytesPerElement, + gp::UInt32 alignment = gp::memory::kDefaultAlignment + ) = delete; + + /// @brief Platform-specific hook to free a memory block. Must be implemented by the derived class. + /// @param[in] data Memory block to deallocate. + void deallocate(void* data) = delete; + + /// @brief Computes the optimal capacity for a reservation request. + /// @param[in] newSize Minimum required capacity in elements. + /// @param[in] bytesPerElement Size of a single element in bytes. + /// @return The computed capacity in elements. + [[nodiscard]] GP_NODEBUG GP_FORCEINLINE_HINT constexpr SizeType + calculateSlackReserve(SizeType newSize, gp::USize bytesPerElement) const noexcept + { + return detail::defaultCalculateSlackReserve(newSize, bytesPerElement, true); + } + + /// @brief Computes the optimal capacity for a reservation request, respecting alignment. + /// @param[in] newSize Minimum required capacity in elements. + /// @param[in] bytesPerElement Size of a single element in bytes. + /// @param[in] alignment Required memory alignment. + /// @return The computed capacity in elements. + [[nodiscard]] GP_NODEBUG GP_FORCEINLINE_HINT constexpr SizeType + calculateSlackReserve(SizeType newSize, gp::USize bytesPerElement, gp::UInt32 alignment) const noexcept + { + return detail::defaultCalculateSlackReserve(newSize, bytesPerElement, true, alignment); + } + + /// @brief Computes the new capacity when shrinking a container, minimizing excessive reallocation. + /// @param[in] newSize Requested new size in elements. + /// @param[in] currentSize Current capacity in elements. + /// @param[in] bytesPerElement Size of a single element in bytes. + /// @return The computed capacity in elements. + [[nodiscard]] GP_NODEBUG GP_FORCEINLINE_HINT constexpr SizeType + calculateSlackShrink(SizeType newSize, SizeType currentSize, gp::USize bytesPerElement) const noexcept + { + return detail::defaultCalculateSlackShrink(newSize, currentSize, bytesPerElement, true); + } + + /// @brief Computes the new capacity when shrinking a container, minimizing excessive reallocation, respecting + /// alignment. + /// @param[in] newSize Requested new size in elements. + /// @param[in] currentSize Current capacity in elements. + /// @param[in] bytesPerElement Size of a single element in bytes. + /// @param[in] alignment Required memory alignment. + /// @return The computed capacity in elements. + [[nodiscard]] GP_NODEBUG GP_FORCEINLINE_HINT constexpr SizeType calculateSlackShrink( + SizeType newSize, SizeType currentSize, gp::USize bytesPerElement, gp::UInt32 alignment + ) const noexcept + { + return detail::defaultCalculateSlackShrink(newSize, currentSize, bytesPerElement, true, alignment); + } + + /// @brief Computes the next capacity when a container exhausts its current allocation. + /// @param[in] newSize Minimum capacity required to fulfill the current operation. + /// @param[in] currentSize Current capacity in elements. + /// @param[in] bytesPerElement Size of a single element in bytes. + /// @return The computed capacity in elements. + [[nodiscard]] GP_NODEBUG GP_FORCEINLINE_HINT constexpr SizeType + calculateSlackGrow(SizeType newSize, SizeType currentSize, gp::USize bytesPerElement) const noexcept + { + return detail::defaultCalculateSlackGrow(newSize, currentSize, bytesPerElement, true); + } + + /// @brief Computes the next capacity when a container exhausts its current allocation, respecting alignment. + /// @param[in] newSize Minimum capacity required to fulfill the current operation. + /// @param[in] currentSize Current capacity in elements. + /// @param[in] bytesPerElement Size of a single element in bytes. + /// @param[in] alignment Required memory alignment. + /// @return The computed capacity in elements. + [[nodiscard]] GP_NODEBUG GP_FORCEINLINE_HINT constexpr SizeType calculateSlackGrow( + SizeType newSize, SizeType currentSize, gp::USize bytesPerElement, gp::UInt32 alignment + ) const noexcept + { + return detail::defaultCalculateSlackGrow(newSize, currentSize, bytesPerElement, true, alignment); + } + }; + + /// @brief Typed allocation interface for containers with known element types. + /// @tparam T The element type being allocated. + template + class ForElementType : public SubClass::ForAnyElementType + { + public: + /// @brief Default constructor. + ForElementType() = default; + + public: + /// @brief Retrieves the strongly-typed pointer to the managed memory block. + /// @return Typed pointer to the allocation. + [[nodiscard]] GP_NODEBUG GP_FORCEINLINE_HINT constexpr T* getAllocation() const noexcept + { + return static_cast(ForAnyElementType::getAllocation()); + } + }; +}; + +} // namespace gp::memory diff --git a/source/runtime/core/public/memory/allocators/SizedHeapAllocator.hpp b/source/runtime/core/public/memory/allocators/SizedHeapAllocator.hpp new file mode 100644 index 00000000..8808a7e4 --- /dev/null +++ b/source/runtime/core/public/memory/allocators/SizedHeapAllocator.hpp @@ -0,0 +1,24 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground.com/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "CoreMinimal.hpp" // IWYU pragma: keep +#include "memory/allocators/SizedAllocatorBase.hpp" + +namespace gp::memory +{ + +/// @todo Implement the SizeHeapAllocator class, which is a sized allocator that uses a heap-based memory allocation. +template +class SizedHeapAllocator : public SizedAllocatorBase> +{ +public: + using MyClass = SizedHeapAllocator; + using SuperClass = SizedAllocatorBase; + using BaseMalloc = BaseMallocType; + using SizeType = SuperClass::SizeType; +}; + +} // namespace gp::memory diff --git a/source/runtime/core/public/memory/backends/Malloc.hpp b/source/runtime/core/public/memory/backends/Malloc.hpp index 84526121..fb415250 100644 --- a/source/runtime/core/public/memory/backends/Malloc.hpp +++ b/source/runtime/core/public/memory/backends/Malloc.hpp @@ -74,6 +74,15 @@ class GP_CORE_API Malloc : public UseSystemMallocForNew /// @brief Check if the allocator can get the size of an allocated memory block. /// @return true if the allocator can get the size, false otherwise. virtual bool canGetAllocationSize(); + + /// @brief Get the actual size of an allocation, which may be larger than the requested size. + /// @param[in] requestedSize The requested size of the memory block, in bytes. + /// @param[in] alignment The alignment requirement for the allocated memory block, in bytes. + /// @return The actual size of the allocated memory block, in bytes. + /// @details For some allocators this will return the actual size that should be requested to eliminate + /// internal fragmentation. The return value will always be >= requestedSize. This can be used to grow + /// and shrink containers to optimal sizes. + virtual USize getActualAllocationSize(USize requestedSize, UInt32 alignment = kDefaultAlignment); }; } // namespace gp::memory diff --git a/source/runtime/core/public/platforms/android/AndroidPlatform.hpp b/source/runtime/core/public/platforms/android/AndroidPlatform.hpp deleted file mode 100644 index 91b89966..00000000 --- a/source/runtime/core/public/platforms/android/AndroidPlatform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "AndroidPlatform.hpp is not yet implemented. Please implement the Android platform support in this file." diff --git a/source/runtime/core/public/platforms/apple/system/SharedLibrary.hpp b/source/runtime/core/public/platforms/apple/system/SharedLibrary.hpp new file mode 100644 index 00000000..ec29a0f4 --- /dev/null +++ b/source/runtime/core/public/platforms/apple/system/SharedLibrary.hpp @@ -0,0 +1,16 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "CoreMinimal.hpp" // IWYU pragma: keep +#include "platforms/generic/system/SharedLibrary.hpp" + +namespace gp::platform::apple +{ + +struct SharedLibrary : public gp::platform::generic::SharedLibrary +{}; + +} // namespace gp::platform::apple diff --git a/source/runtime/core/public/platforms/base/system/SharedLibrary.hpp b/source/runtime/core/public/platforms/base/system/SharedLibrary.hpp new file mode 100644 index 00000000..9be9e571 --- /dev/null +++ b/source/runtime/core/public/platforms/base/system/SharedLibrary.hpp @@ -0,0 +1,31 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "platforms/base/Platform.hpp" // IWYU pragma: keep +#if GP_PLATFORM_WINDOWS + #include "platforms/windows/system/SharedLibrary.hpp" +#elif GP_PLATFORM_LINUX + #include "platforms/linux/system/SharedLibrary.hpp" +#elif GP_PLATFORM_MACOS + #include "platforms/macos/system/SharedLibrary.hpp" +#else + #include "platforms/generic/system/SharedLibrary.hpp" +#endif + +namespace gp::platform +{ + +#if GP_PLATFORM_WINDOWS +using SharedLibrary = gp::platform::windows::SharedLibrary; +#elif GP_PLATFORM_LINUX +using SharedLibrary = gp::platform::linux::SharedLibrary; +#elif GP_PLATFORM_MACOS +using SharedLibrary = gp::platform::macos::SharedLibrary; +#else +using SharedLibrary = gp::platform::generic::SharedLibrary; +#endif + +} // namespace gp::platform diff --git a/source/runtime/core/public/platforms/generic/system/SharedLibrary.hpp b/source/runtime/core/public/platforms/generic/system/SharedLibrary.hpp new file mode 100644 index 00000000..939733d1 --- /dev/null +++ b/source/runtime/core/public/platforms/generic/system/SharedLibrary.hpp @@ -0,0 +1,49 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "containers/views/StringView.hpp" +#include "CoreMinimal.hpp" + +namespace gp::platform::generic +{ + +/// @brief A class for managing shared libraries. +struct SharedLibrary +{ +public: + /// @brief Get the Handle object + /// @param[in] filename + /// @return + [[nodiscard]] static GP_CORE_API void* getHandle(gp::StringView filename) noexcept; + + /// @brief Get the Export object + /// @param[in] handle + /// @param[in] procName + /// @return + [[nodiscard]] static GP_CORE_API void* getExport(void* handle, gp::StringView procName) noexcept; + + /// @brief + /// @param[in] handle + /// @return + static GP_CORE_API void freeHandle(void* handle) noexcept; + + /// @brief + /// @param[in] directory + GP_FORCEINLINE_HINT static void addDirectory([[maybe_unused]] gp::StringView directory) noexcept + {} + + /// @brief + /// @param[in] directory + GP_FORCEINLINE_HINT static void pushDirectory([[maybe_unused]] gp::StringView directory) noexcept + {} + + /// @brief + /// @param[in] directory + GP_FORCEINLINE_HINT static void popDirectory([[maybe_unused]] gp::StringView directory) noexcept + {} +}; + +} // namespace gp::platform::generic diff --git a/source/runtime/core/public/platforms/ios/IOsPlatform.hpp b/source/runtime/core/public/platforms/ios/IOsPlatform.hpp deleted file mode 100644 index ac5c587f..00000000 --- a/source/runtime/core/public/platforms/ios/IOsPlatform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "IOsPlatform.hpp is not yet implemented. Please implement the iOS platform support in this file." diff --git a/source/runtime/core/public/platforms/linux/system/SharedLibrary.hpp b/source/runtime/core/public/platforms/linux/system/SharedLibrary.hpp new file mode 100644 index 00000000..5248442d --- /dev/null +++ b/source/runtime/core/public/platforms/linux/system/SharedLibrary.hpp @@ -0,0 +1,16 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "CoreMinimal.hpp" // IWYU pragma: keep +#include "platforms/unix/system/SharedLibrary.hpp" + +namespace gp::platform::linux +{ + +struct SharedLibrary final : public gp::platform::unix::SharedLibrary +{}; + +} // namespace gp::platform::linux diff --git a/source/runtime/core/public/platforms/macos/system/SharedLibrary.hpp b/source/runtime/core/public/platforms/macos/system/SharedLibrary.hpp new file mode 100644 index 00000000..f5a57e01 --- /dev/null +++ b/source/runtime/core/public/platforms/macos/system/SharedLibrary.hpp @@ -0,0 +1,16 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "CoreMinimal.hpp" // IWYU pragma: keep +#include "platforms/apple/system/SharedLibrary.hpp" + +namespace gp::platform::windows +{ + +struct SharedLibrary final : public gp::platform::apple::SharedLibrary +{}; + +} // namespace gp::platform::windows diff --git a/source/runtime/core/public/platforms/ps4/PS4Platform.hpp b/source/runtime/core/public/platforms/ps4/PS4Platform.hpp deleted file mode 100644 index 2cc52d44..00000000 --- a/source/runtime/core/public/platforms/ps4/PS4Platform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "PS4Platform.hpp is not yet implemented. Please implement the PS4 platform support in this file." diff --git a/source/runtime/core/public/platforms/ps5/PS5Platform.hpp b/source/runtime/core/public/platforms/ps5/PS5Platform.hpp deleted file mode 100644 index dbf2bb8a..00000000 --- a/source/runtime/core/public/platforms/ps5/PS5Platform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "PS5Platform.hpp is not yet implemented. Please implement the PS5 platform support in this file." diff --git a/source/runtime/core/public/platforms/switch/SwitchPlatform.hpp b/source/runtime/core/public/platforms/switch/SwitchPlatform.hpp deleted file mode 100644 index fa5920ad..00000000 --- a/source/runtime/core/public/platforms/switch/SwitchPlatform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "SwitchPlatform.hpp is not yet implemented. Please implement the Switch platform support in this file." diff --git a/source/runtime/core/public/platforms/switch2/Switch2Platform.hpp b/source/runtime/core/public/platforms/switch2/Switch2Platform.hpp deleted file mode 100644 index 3a7fc340..00000000 --- a/source/runtime/core/public/platforms/switch2/Switch2Platform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "Switch2Platform.hpp is not yet implemented. Please implement the Switch2 platform support in this file." diff --git a/source/runtime/core/public/platforms/tvos/TvOsPlatform.hpp b/source/runtime/core/public/platforms/tvos/TvOsPlatform.hpp deleted file mode 100644 index 02ec7462..00000000 --- a/source/runtime/core/public/platforms/tvos/TvOsPlatform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "TvOsPlatform.hpp is not yet implemented. Please implement the TvOS platform support in this file." diff --git a/source/runtime/core/public/platforms/unix/system/SharedLibrary.hpp b/source/runtime/core/public/platforms/unix/system/SharedLibrary.hpp new file mode 100644 index 00000000..a812e420 --- /dev/null +++ b/source/runtime/core/public/platforms/unix/system/SharedLibrary.hpp @@ -0,0 +1,16 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "CoreMinimal.hpp" // IWYU pragma: keep +#include "platforms/generic/system/SharedLibrary.hpp" + +namespace gp::platform::unix +{ + +struct SharedLibrary : public gp::platform::generic::SharedLibrary +{}; + +} // namespace gp::platform::unix diff --git a/source/runtime/core/public/platforms/visionos/VisionOsPlatform.hpp b/source/runtime/core/public/platforms/visionos/VisionOsPlatform.hpp deleted file mode 100644 index 6fb0aa22..00000000 --- a/source/runtime/core/public/platforms/visionos/VisionOsPlatform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "VisionOsPlatform.hpp is not yet implemented. Please implement the VisionOS platform support in this file." diff --git a/source/runtime/core/public/platforms/windows/system/SharedLibrary.hpp b/source/runtime/core/public/platforms/windows/system/SharedLibrary.hpp new file mode 100644 index 00000000..90dc076e --- /dev/null +++ b/source/runtime/core/public/platforms/windows/system/SharedLibrary.hpp @@ -0,0 +1,57 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground/legal +// mailto:support AT graphical-playground DOT com + +#pragma once + +#include "containers/arrays/Vector.hpp" +#include "containers/ContainerForward.hpp" +#include "CoreMinimal.hpp" // IWYU pragma: keep +#include "platforms/generic/system/SharedLibrary.hpp" + +namespace gp::platform::windows +{ + +struct SharedLibrary final : public gp::platform::generic::SharedLibrary +{ +private: + /// @brief Since Windows can only have one directory at a time, this stack is used to reset the previous directory. + // static gp::Vector s_searchPathsStack; + + /// @brief All the directories we want to load shared libraries from. + // static gp::Vector s_searchPaths; + + /// @brief A cache of the shared libraries found in each directory in @p s_searchPaths. + // static gp::Map> s_searchPathsCache; + +public: + /// @brief Get the Handle object + /// @param[in] filename + /// @return + [[nodiscard]] static GP_CORE_API void* getHandle(gp::StringView filename) noexcept; + + /// @brief Get the Export object + /// @param[in] handle + /// @param[in] procName + /// @return + [[nodiscard]] static GP_CORE_API void* getExport(void* handle, gp::StringView procName) noexcept; + + /// @brief + /// @param[in] handle + /// @return + static GP_CORE_API void freeHandle(void* handle) noexcept; + + /// @brief + /// @param[in] directory + static void addDirectory(gp::StringView directory) noexcept; + + /// @brief + /// @param[in] directory + static void pushDirectory(gp::StringView directory) noexcept; + + /// @brief + /// @param[in] directory + static void popDirectory(gp::StringView directory) noexcept; +}; + +} // namespace gp::platform::windows diff --git a/source/runtime/core/public/platforms/xboxone/XboxOnePlatform.hpp b/source/runtime/core/public/platforms/xboxone/XboxOnePlatform.hpp deleted file mode 100644 index 497d80fe..00000000 --- a/source/runtime/core/public/platforms/xboxone/XboxOnePlatform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "XboxOnePlatform.hpp is not yet implemented. Please implement the Xbox One platform support in this file." diff --git a/source/runtime/core/public/platforms/xboxseries/XboxSeriesPlatform.hpp b/source/runtime/core/public/platforms/xboxseries/XboxSeriesPlatform.hpp deleted file mode 100644 index fa7c98a5..00000000 --- a/source/runtime/core/public/platforms/xboxseries/XboxSeriesPlatform.hpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once - -#error "XboxSeriesPlatform.hpp is not yet implemented. Please implement the Xbox Series platform support in this file." diff --git a/source/runtime/core/public/profiling/TracyProfiler.hpp b/source/runtime/core/public/profiling/TracyProfiler.hpp index 81f51a30..011b8c0c 100644 --- a/source/runtime/core/public/profiling/TracyProfiler.hpp +++ b/source/runtime/core/public/profiling/TracyProfiler.hpp @@ -66,19 +66,19 @@ /// @section GPU Zones #if GP_TRACY_HAS_GPU -#define GP_GPU_SCOPE(name) TracyGpuZone(name) -#define GP_GPU_SCOPE_C(name, color) TracyGpuZoneC(name, color) -#define GP_GPU_SCOPE_CTX(ctx, name) TracyGpuZoneTransient(ctx, ___gpuZone, name, true) -#define GP_GPU_SCOPE_CTX_C(ctx, name, color) TracyGpuZoneTransient(ctx, ___gpuZone, name, true) -#define GP_GPU_MARKER(ctx, color, name) (void)(ctx) -#define GP_GPU_COLLECT() TracyGpuCollect + #define GP_GPU_SCOPE(name) TracyGpuZone(name) + #define GP_GPU_SCOPE_C(name, color) TracyGpuZoneC(name, color) + #define GP_GPU_SCOPE_CTX(ctx, name) TracyGpuZoneTransient(ctx, ___gpuZone, name, true) + #define GP_GPU_SCOPE_CTX_C(ctx, name, color) TracyGpuZoneTransient(ctx, ___gpuZone, name, true) + #define GP_GPU_MARKER(ctx, color, name) (void)(ctx) + #define GP_GPU_COLLECT() TracyGpuCollect #else -#define GP_GPU_SCOPE(name) -#define GP_GPU_SCOPE_C(name, color) -#define GP_GPU_SCOPE_CTX(ctx, name) -#define GP_GPU_SCOPE_CTX_C(ctx, name, color) -#define GP_GPU_MARKER(ctx, color, name) -#define GP_GPU_COLLECT() + #define GP_GPU_SCOPE(name) + #define GP_GPU_SCOPE_C(name, color) + #define GP_GPU_SCOPE_CTX(ctx, name) + #define GP_GPU_SCOPE_CTX_C(ctx, name, color) + #define GP_GPU_MARKER(ctx, color, name) + #define GP_GPU_COLLECT() #endif /// @section Locks / Mutexes diff --git a/source/runtime/core/public/templates/Expected.hpp b/source/runtime/core/public/templates/Expected.hpp index 688aad89..350c50e2 100644 --- a/source/runtime/core/public/templates/Expected.hpp +++ b/source/runtime/core/public/templates/Expected.hpp @@ -117,8 +117,8 @@ Unexpected(E) -> Unexpected>; /// @param[in] error Error value to wrap. /// @return Unexpected> holding the provided error. template -[[nodiscard]] constexpr Unexpected> makeUnexpected(E&& error -) noexcept(noexcept(Unexpected>(std::forward(error)))) +[[nodiscard]] constexpr Unexpected> + makeUnexpected(E&& error) noexcept(noexcept(Unexpected>(std::forward(error)))) { return Unexpected>(std::forward(error)); } @@ -240,7 +240,8 @@ requires concepts::IsDestructible && (!concepts::IsReference) && concepts: /// @brief Move-constructs from another Expected. /// @param[in] other Expected to move from. - constexpr Expected(Expected&& other + constexpr Expected( + Expected&& other ) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) : m_hasValue(other.m_hasValue) { @@ -299,8 +300,10 @@ requires concepts::IsDestructible && (!concepts::IsReference) && concepts: /// @brief Move-assigns from another Expected. /// @param[in] other Expected to move from. /// @return Reference to this Expected. - Expected& operator=(Expected&& other - ) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v && std::is_nothrow_move_assignable_v) + Expected& operator=(Expected&& other) noexcept( + std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v && + std::is_nothrow_move_assignable_v && std::is_nothrow_move_assignable_v + ) { if (this == &other) { @@ -596,7 +599,8 @@ requires concepts::IsDestructible && (!concepts::IsReference) && concepts: /// constructs from the temporary. Three moves, zero heap allocations. On same-track swaps /// we delegate to std::swap which is free to use optimised intrinsics for trivial types. /// @param[in] other Expected to swap with. - void swap(Expected& other + void swap( + Expected& other ) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) { if (m_hasValue && other.m_hasValue) diff --git a/source/runtime/core/public/templates/Pair.hpp b/source/runtime/core/public/templates/Pair.hpp index 534288d4..081b5e46 100644 --- a/source/runtime/core/public/templates/Pair.hpp +++ b/source/runtime/core/public/templates/Pair.hpp @@ -6,7 +6,7 @@ #include "concepts/Concepts.hpp" #include "concepts/Construction.hpp" -#include "CoreMinimal.hpp" +#include "CoreMinimal.hpp" // IWYU pragma: keep #include namespace gp @@ -31,7 +31,8 @@ struct Pair public: /// @brief Default constructor, value-initializes both elements. - [[nodiscard]] constexpr Pair() requires(concepts::IsDefaultConstructible && concepts::IsDefaultConstructible) + [[nodiscard]] constexpr Pair() + requires(concepts::IsDefaultConstructible && concepts::IsDefaultConstructible) : first() , second() {} diff --git a/source/runtime/core/tests/containers/strings/FixedString.tests.cpp b/source/runtime/core/tests/containers/strings/FixedString.tests.cpp new file mode 100644 index 00000000..923d2358 --- /dev/null +++ b/source/runtime/core/tests/containers/strings/FixedString.tests.cpp @@ -0,0 +1,152 @@ +// Copyright (c) - Graphical Playground. All rights reserved. +// For more information, see https://graphical-playground.com/legal +// mailto:support AT graphical-playground DOT com + +#include "containers/strings/FixedString.hpp" +#include + +namespace gp::tests +{ + +TEST(FixedStringTests, DefaultConstructor) +{ + FixedString<32> str; + EXPECT_TRUE(str.isEmpty()); + EXPECT_EQ(str.size(), 0u); + EXPECT_EQ(str.length(), 0u); + EXPECT_EQ(str.capacity(), 32u); + EXPECT_EQ(str.maxSize(), 32u); + EXPECT_STREQ(str.cString(), ""); +} + +TEST(FixedStringTests, ConstructFromCString) +{ + FixedString<32> str("Hello"); + EXPECT_FALSE(str.isEmpty()); + EXPECT_EQ(str.size(), 5u); + EXPECT_STREQ(str.cString(), "Hello"); +} + +TEST(FixedStringTests, ConstructFromStringView) +{ + gp::StringView view("World"); + FixedString<32> str(view); + EXPECT_EQ(str.size(), 5u); + EXPECT_STREQ(str.cString(), "World"); +} + +TEST(FixedStringTests, ElementAccess) +{ + FixedString<32> str("Hello"); + EXPECT_EQ(str[0], 'H'); + EXPECT_EQ(str[4], 'o'); + EXPECT_EQ(str.at(1), 'e'); + EXPECT_EQ(str.front(), 'H'); + EXPECT_EQ(str.back(), 'o'); +} + +TEST(FixedStringTests, Iterators) +{ + FixedString<32> str("abc"); + + // Normal iterators + auto it = str.begin(); + EXPECT_EQ(*it, 'a'); + ++it; + EXPECT_EQ(*it, 'b'); + ++it; + EXPECT_EQ(*it, 'c'); + ++it; + EXPECT_EQ(it, str.end()); + + // Reverse iterators + auto rit = str.rbegin(); + EXPECT_EQ(*rit, 'c'); + ++rit; + EXPECT_EQ(*rit, 'b'); + ++rit; + EXPECT_EQ(*rit, 'a'); + ++rit; + EXPECT_EQ(rit, str.rend()); +} + +TEST(FixedStringTests, Modification) +{ + FixedString<32> str; + str.pushBack('H'); + EXPECT_EQ(str.size(), 1u); + EXPECT_STREQ(str.cString(), "H"); + + str += 'i'; + EXPECT_EQ(str.size(), 2u); + EXPECT_STREQ(str.cString(), "Hi"); + + str.append(" there"); + EXPECT_EQ(str.size(), 8u); + EXPECT_STREQ(str.cString(), "Hi there"); + + str += "!"; + EXPECT_EQ(str.size(), 9u); + EXPECT_STREQ(str.cString(), "Hi there!"); + + str.popBack(); + EXPECT_EQ(str.size(), 8u); + EXPECT_STREQ(str.cString(), "Hi there"); + + str.clear(); + EXPECT_TRUE(str.isEmpty()); + EXPECT_EQ(str.size(), 0u); + EXPECT_STREQ(str.cString(), ""); +} + +TEST(FixedStringTests, Assign) +{ + FixedString<32> str("Initial"); + str.assign("New Value"); + EXPECT_EQ(str.size(), 9u); + EXPECT_STREQ(str.cString(), "New Value"); +} + +TEST(FixedStringTests, Comparison) +{ + FixedString<32> str1("abc"); + FixedString<32> str2("abc"); + FixedString<32> str3("abd"); + FixedString<16> str4("abc"); + + EXPECT_TRUE(str1 == str2); + EXPECT_FALSE(str1 == str3); + EXPECT_TRUE(str1 == str4); + + EXPECT_TRUE(str1 <=> str3 < 0); + EXPECT_TRUE(str3 <=> str1 > 0); + EXPECT_TRUE(str1 <=> str2 == 0); + + gp::StringView view("abc"); + EXPECT_TRUE(str1 == view); +} + +TEST(FixedStringTests, ConversionToStringView) +{ + FixedString<32> str("Hello"); + gp::StringView view = str; + EXPECT_EQ(view.size(), 5u); + EXPECT_EQ(view, "Hello"); +} + +TEST(FixedStringTests, TypeAliases) +{ + FixedWString<32> wstr(L"Wide"); + EXPECT_EQ(wstr.size(), 4u); + + FixedU8String<32> u8str(u8"UTF-8"); + EXPECT_EQ(u8str.size(), 5u); + + FixedU16String<32> u16str(u"UTF-16"); + EXPECT_EQ(u16str.size(), 6u); + + FixedU32String<32> u32str(U"UTF-32"); + EXPECT_EQ(u32str.size(), 6u); +} + +} // namespace gp::tests diff --git a/source/runtime/core/tests/templates/Enums.tests.cpp b/source/runtime/core/tests/templates/Enums.tests.cpp index efc05063..f2f6b718 100644 --- a/source/runtime/core/tests/templates/Enums.tests.cpp +++ b/source/runtime/core/tests/templates/Enums.tests.cpp @@ -147,7 +147,7 @@ TEST(EnumUtilsFunctionsTest, SetFlags) auto flags = TestBitwiseFlags::FlagA; auto result = enums::setFlags(flags, TestBitwiseFlags::FlagB); - EXPECT_EQ(result, (TestBitwiseFlags::FlagA | TestBitwiseFlags::FlagB)); + EXPECT_EQ(result, TestBitwiseFlags::FlagA | TestBitwiseFlags::FlagB); EXPECT_TRUE(enums::hasAllFlags(result, TestBitwiseFlags::FlagA | TestBitwiseFlags::FlagB)); } @@ -169,7 +169,7 @@ TEST(EnumUtilsFunctionsTest, ToggleFlags) // Toggle on flags = enums::toggleFlags(flags, TestBitwiseFlags::FlagB); - EXPECT_EQ(flags, (TestBitwiseFlags::FlagA | TestBitwiseFlags::FlagB)); + EXPECT_EQ(flags, TestBitwiseFlags::FlagA | TestBitwiseFlags::FlagB); // Toggle off flags = enums::toggleFlags(flags, TestBitwiseFlags::FlagA); diff --git a/source/runtime/core/tests/templates/Expected.tests.cpp b/source/runtime/core/tests/templates/Expected.tests.cpp index ffde661a..67c60374 100644 --- a/source/runtime/core/tests/templates/Expected.tests.cpp +++ b/source/runtime/core/tests/templates/Expected.tests.cpp @@ -424,9 +424,12 @@ TEST(ExpectedMonadicTest, AndThen) LifecycleTracker::reset(); Expected exLT(LifecycleTracker{ 42 }); LifecycleTracker::reset(); - auto res = std::move(exLT).andThen([](LifecycleTracker lt) -> Expected { + auto res = std::move(exLT).andThen( + [](LifecycleTracker lt) -> Expected + { return lt.value; - }); + } + ); EXPECT_EQ(res.value(), 42); EXPECT_EQ(LifecycleTracker::moves, 1); @@ -434,9 +437,12 @@ TEST(ExpectedMonadicTest, AndThen) LifecycleTracker::reset(); Expected errLT(makeUnexpected(LifecycleTracker{ 404 })); LifecycleTracker::reset(); - auto resErr = std::move(errLT).andThen([](int x) -> Expected { + auto resErr = std::move(errLT).andThen( + [](int x) -> Expected + { return x; - }); + } + ); EXPECT_FALSE(resErr.hasValue()); EXPECT_EQ(resErr.error().value, 404); // 1 move from storage to Unexpected temp, 1 move from Unexpected temp to new Expected storage @@ -468,9 +474,12 @@ TEST(ExpectedMonadicTest, OrElse) LifecycleTracker::reset(); Expected errLT(makeUnexpected(LifecycleTracker{ 404 })); LifecycleTracker::reset(); - auto res = std::move(errLT).orElse([](LifecycleTracker lt) -> Expected { + auto res = std::move(errLT).orElse( + [](LifecycleTracker lt) -> Expected + { return lt.value; - }); + } + ); EXPECT_EQ(res.value(), 404); EXPECT_EQ(LifecycleTracker::moves, 1); @@ -478,9 +487,12 @@ TEST(ExpectedMonadicTest, OrElse) LifecycleTracker::reset(); Expected exLT(LifecycleTracker{ 42 }); LifecycleTracker::reset(); - auto resEx = std::move(exLT).orElse([](int e) -> Expected { + auto resEx = std::move(exLT).orElse( + [](int e) -> Expected + { return makeUnexpected(e); - }); + } + ); EXPECT_TRUE(resEx.hasValue()); EXPECT_EQ(resEx.value().value, 42); EXPECT_EQ(LifecycleTracker::moves, 1); @@ -504,7 +516,8 @@ TEST(ExpectedMonadicTest, Transform) LifecycleTracker::reset(); Expected exLT(LifecycleTracker{ 42 }); LifecycleTracker::reset(); - auto res = std::move(exLT).transform([](LifecycleTracker lt) { + auto res = std::move(exLT).transform([](LifecycleTracker lt) + { return lt.value; }); EXPECT_EQ(res.value(), 42); @@ -514,7 +527,8 @@ TEST(ExpectedMonadicTest, Transform) LifecycleTracker::reset(); Expected errLT(makeUnexpected(LifecycleTracker{ 404 })); LifecycleTracker::reset(); - auto resErr = std::move(errLT).transform([](int x) { + auto resErr = std::move(errLT).transform([](int x) + { return x; }); EXPECT_FALSE(resErr.hasValue()); @@ -540,7 +554,8 @@ TEST(ExpectedMonadicTest, TransformError) LifecycleTracker::reset(); Expected errLT(makeUnexpected(LifecycleTracker{ 404 })); LifecycleTracker::reset(); - auto res = std::move(errLT).transformError([](LifecycleTracker lt) { + auto res = std::move(errLT).transformError([](LifecycleTracker lt) + { return lt.value; }); EXPECT_FALSE(res.hasValue()); @@ -551,7 +566,8 @@ TEST(ExpectedMonadicTest, TransformError) LifecycleTracker::reset(); Expected exLT(LifecycleTracker{ 42 }); LifecycleTracker::reset(); - auto resEx = std::move(exLT).transformError([](int e) { + auto resEx = std::move(exLT).transformError([](int e) + { return e; }); EXPECT_TRUE(resEx.hasValue()); diff --git a/source/runtime/rhi/agc/.gitignore b/source/runtime/rhi/agc/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/agc/CHANGELOG.md b/source/runtime/rhi/agc/CHANGELOG.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/agc/CMakeLists.txt b/source/runtime/rhi/agc/CMakeLists.txt deleted file mode 100644 index 552b344a..00000000 --- a/source/runtime/rhi/agc/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) - Graphical Playground. All rights reserved. -# For more information, see https://graphical-playground/legal -# mailto:support AT graphical-playground DOT com - -include(gp-build-tool) - -gpStartModule(rhi/agc) - gpAddDependency(PUBLIC core) - gpAddDependency(PUBLIC rhi/base) -gpEndModule() diff --git a/source/runtime/rhi/agc/README.md b/source/runtime/rhi/agc/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/agc/docs/README.md b/source/runtime/rhi/agc/docs/README.md deleted file mode 100644 index ad3d0c82..00000000 --- a/source/runtime/rhi/agc/docs/README.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -sidebar_position: 0 -title: AGC -description: PlayStation 5 low-level graphics API backend. NDA-protected, work in progress. -tags: - - rhi - - ps5 - - playstation - - nda - - wip ---- - -# AGC - -

PlayStation 5 low-level graphics API backend.

- -AGC is Sony's low-level graphics library for the PlayStation 5. It is the successor to the -[GNM](../gnm/README.md) and [GNMX](../gnmx/README.md) libraries used on PlayStation 4, following -the same close-to-the-hardware approach: -the engine builds command buffers and manages GPU state directly, rather than going through a -higher-level abstraction layer. - -:::danger NDA-protected platform -The PlayStation 5 SDK, including the AGC headers, samples, and reference documentation, is -covered by Sony's non-disclosure agreement with registered PlayStation Partners. Nothing from -that material can be reproduced, described, or linked to on this page, and the same restriction -applies to every page under this section of the documentation. -::: - -## Current status - -The `agc` backend in `gp-engine` is internal work in progress. The module currently holds build -scaffolding and an empty CMake target; no PlayStation NDA material has been added, because the -team does not yet have PlayStation NDA clearance. - -Once that clearance is in place, this page will be filled in for the contributors on the project -who are also registered PlayStation Partners. It will not be published outside that group, and -public builds of `gp-engine` will not ship with this backend enabled. - -## Requesting access - -Access to the PS5 SDK, dev kits, and AGC documentation is managed entirely by Sony. If you need -to review or contribute to this backend, you or your studio have to register directly through -Sony's own program. - -- [PlayStation Partners](https://partners.playstation.net/): the registration portal for the - PlayStation developer program. - -We have no ability to grant, share, or forward PlayStation NDA material on request. Approval is -at Sony's sole discretion. - -## Curriculum - -Graphical Playground plans to cover console-specific rendering paths, AGC included, as part of -its learning material. That curriculum sits behind the same NDA and will only be released to -contributors and learners who hold active PlayStation Partners clearance, once it exists. diff --git a/source/runtime/rhi/agc/docs/_category_.json b/source/runtime/rhi/agc/docs/_category_.json deleted file mode 100644 index 9cc88d9e..00000000 --- a/source/runtime/rhi/agc/docs/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "AGC" -} diff --git a/source/runtime/rhi/agc/private/RHIAGC.cpp b/source/runtime/rhi/agc/private/RHIAGC.cpp deleted file mode 100644 index 3113e461..00000000 --- a/source/runtime/rhi/agc/private/RHIAGC.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com diff --git a/source/runtime/rhi/agc/public/RHIAGC.hpp b/source/runtime/rhi/agc/public/RHIAGC.hpp deleted file mode 100644 index 88b93605..00000000 --- a/source/runtime/rhi/agc/public/RHIAGC.hpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once diff --git a/source/runtime/rhi/d3d12x/.gitignore b/source/runtime/rhi/d3d12x/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/d3d12x/CHANGELOG.md b/source/runtime/rhi/d3d12x/CHANGELOG.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/d3d12x/CMakeLists.txt b/source/runtime/rhi/d3d12x/CMakeLists.txt deleted file mode 100644 index ac80ad5b..00000000 --- a/source/runtime/rhi/d3d12x/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) - Graphical Playground. All rights reserved. -# For more information, see https://graphical-playground/legal -# mailto:support AT graphical-playground DOT com - -include(gp-build-tool) - -gpStartModule(rhi/d3d12x) - gpAddDependency(PUBLIC core) - gpAddDependency(PUBLIC rhi/base) -gpEndModule() diff --git a/source/runtime/rhi/d3d12x/README.md b/source/runtime/rhi/d3d12x/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/d3d12x/docs/README.md b/source/runtime/rhi/d3d12x/docs/README.md deleted file mode 100644 index ad64306e..00000000 --- a/source/runtime/rhi/d3d12x/docs/README.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -sidebar_position: 0 -title: DirectX 12 Xbox -description: Xbox low-level graphics API backend. NDA-protected, work in progress. -tags: - - rhi - - xbox - - directx - - nda - - wip ---- - -# DirectX 12 Xbox - -

Xbox low-level graphics API backend.

- -D3D12X is Microsoft's Xbox-specific extension of [Direct3D 12](../d3d12/README.md), distributed -through the Xbox Game Development Kit (GDK). It builds on the retail D3D12 API and adds -console-specific extensions and optimizations that are not exposed on desktop Windows. - -:::danger NDA-protected platform -The Xbox GDK, including the D3D12X headers, samples, and reference documentation, is covered by -Microsoft's non-disclosure agreement with registered Xbox developers. Nothing from that material -can be reproduced, described, or linked to on this page, and the same restriction applies to every -page under this section of the documentation. -::: - -## Current status - -The `d3d12x` backend in `gp-engine` is internal work in progress. The module currently holds build -scaffolding and an empty CMake target; no Xbox NDA material has been added, because the team does -not yet have Xbox NDA clearance. - -Once that clearance is in place, this page will be filled in for the contributors on the project -who are also registered Xbox developers. It will not be published outside that group, and public -builds of `gp-engine` will not ship with this backend enabled. - -## Requesting access - -Access to the Xbox GDK, dev kits, and D3D12X documentation is managed entirely by Microsoft. If you -need to review or contribute to this backend, you or your studio have to register directly through -Microsoft's own program. - -- [ID@Xbox](https://www.xbox.com/en-us/developers/id): the registration portal for independent and - studio developers seeking Xbox development access. - -We have no ability to grant, share, or forward Xbox NDA material on request. Approval is at -Microsoft's sole discretion. - -## Curriculum - -Graphical Playground plans to cover console-specific rendering paths, D3D12X included, as part of -its learning material. That curriculum sits behind the same NDA and will only be released to -contributors and learners who hold active Xbox developer clearance, once it exists. diff --git a/source/runtime/rhi/d3d12x/docs/_category_.json b/source/runtime/rhi/d3d12x/docs/_category_.json deleted file mode 100644 index 802ab78f..00000000 --- a/source/runtime/rhi/d3d12x/docs/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "DirectX 12 Xbox" -} diff --git a/source/runtime/rhi/d3d12x/private/RHID3D12x.cpp b/source/runtime/rhi/d3d12x/private/RHID3D12x.cpp deleted file mode 100644 index 3113e461..00000000 --- a/source/runtime/rhi/d3d12x/private/RHID3D12x.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com diff --git a/source/runtime/rhi/d3d12x/public/RHID3D12x.hpp b/source/runtime/rhi/d3d12x/public/RHID3D12x.hpp deleted file mode 100644 index 88b93605..00000000 --- a/source/runtime/rhi/d3d12x/public/RHID3D12x.hpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once diff --git a/source/runtime/rhi/gnm/.gitignore b/source/runtime/rhi/gnm/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/gnm/CHANGELOG.md b/source/runtime/rhi/gnm/CHANGELOG.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/gnm/CMakeLists.txt b/source/runtime/rhi/gnm/CMakeLists.txt deleted file mode 100644 index 076b4c8d..00000000 --- a/source/runtime/rhi/gnm/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) - Graphical Playground. All rights reserved. -# For more information, see https://graphical-playground/legal -# mailto:support AT graphical-playground DOT com - -include(gp-build-tool) - -gpStartModule(rhi/gnm) - gpAddDependency(PUBLIC core) - gpAddDependency(PUBLIC rhi/base) -gpEndModule() diff --git a/source/runtime/rhi/gnm/README.md b/source/runtime/rhi/gnm/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/gnm/docs/README.md b/source/runtime/rhi/gnm/docs/README.md deleted file mode 100644 index 39705142..00000000 --- a/source/runtime/rhi/gnm/docs/README.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -sidebar_position: 0 -title: GNM -description: PlayStation 4 low-level graphics API backend. NDA-protected, work in progress. -tags: - - rhi - - ps4 - - playstation - - nda - - wip ---- - -# GNM - -

PlayStation 4 low-level graphics API backend.

- -GNM is Sony's low-level graphics library for the PlayStation 4. It exposes direct control over -command buffer generation and GPU state, with very little driver overhead between engine code and -the hardware. It is typically paired with [GNMX](../gnmx/README.md), a thinner convenience layer -built on top of it. On PlayStation 5, GNM is superseded by [AGC](../agc/README.md). - -:::danger NDA-protected platform -The PlayStation 4 SDK, including the GNM headers, samples, and reference documentation, is covered -by Sony's non-disclosure agreement with registered PlayStation Partners. Nothing from that -material can be reproduced, described, or linked to on this page, and the same restriction applies -to every page under this section of the documentation. -::: - -## Current status - -The `gnm` backend in `gp-engine` is internal work in progress. The module currently holds build -scaffolding and an empty CMake target; no PlayStation NDA material has been added, because the -team does not yet have PlayStation NDA clearance. - -Once that clearance is in place, this page will be filled in for the contributors on the project -who are also registered PlayStation Partners. It will not be published outside that group, and -public builds of `gp-engine` will not ship with this backend enabled. - -## Requesting access - -Access to the PS4 SDK, dev kits, and GNM documentation is managed entirely by Sony. If you need to -review or contribute to this backend, you or your studio have to register directly through Sony's -own program. - -- [PlayStation Partners](https://partners.playstation.net/): the registration portal for the - PlayStation developer program. - -We have no ability to grant, share, or forward PlayStation NDA material on request. Approval is at -Sony's sole discretion. - -## Curriculum - -Graphical Playground plans to cover console-specific rendering paths, GNM included, as part of its -learning material. That curriculum sits behind the same NDA and will only be released to -contributors and learners who hold active PlayStation Partners clearance, once it exists. diff --git a/source/runtime/rhi/gnm/docs/_category_.json b/source/runtime/rhi/gnm/docs/_category_.json deleted file mode 100644 index fa05c954..00000000 --- a/source/runtime/rhi/gnm/docs/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "GNM" -} diff --git a/source/runtime/rhi/gnm/private/RHIGNM.cpp b/source/runtime/rhi/gnm/private/RHIGNM.cpp deleted file mode 100644 index 3113e461..00000000 --- a/source/runtime/rhi/gnm/private/RHIGNM.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com diff --git a/source/runtime/rhi/gnm/public/RHIGNM.hpp b/source/runtime/rhi/gnm/public/RHIGNM.hpp deleted file mode 100644 index 88b93605..00000000 --- a/source/runtime/rhi/gnm/public/RHIGNM.hpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once diff --git a/source/runtime/rhi/gnmx/.gitignore b/source/runtime/rhi/gnmx/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/gnmx/CHANGELOG.md b/source/runtime/rhi/gnmx/CHANGELOG.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/gnmx/CMakeLists.txt b/source/runtime/rhi/gnmx/CMakeLists.txt deleted file mode 100644 index 7470d18b..00000000 --- a/source/runtime/rhi/gnmx/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) - Graphical Playground. All rights reserved. -# For more information, see https://graphical-playground/legal -# mailto:support AT graphical-playground DOT com - -include(gp-build-tool) - -gpStartModule(rhi/gnmx) - gpAddDependency(PUBLIC core) - gpAddDependency(PUBLIC rhi/base) -gpEndModule() diff --git a/source/runtime/rhi/gnmx/README.md b/source/runtime/rhi/gnmx/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/gnmx/docs/README.md b/source/runtime/rhi/gnmx/docs/README.md deleted file mode 100644 index 5a37d206..00000000 --- a/source/runtime/rhi/gnmx/docs/README.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -sidebar_position: 0 -title: GNMX -description: PlayStation 4 mid-level graphics API backend. NDA-protected, work in progress. -tags: - - rhi - - ps4 - - playstation - - nda - - wip ---- - -# GNMX - -

PlayStation 4 mid-level graphics API backend.

- -GNMX is Sony's convenience layer on top of [GNM](../gnm/README.md) for the PlayStation 4. It wraps -the raw, low-overhead GNM command buffer API with state-tracking and helper functions closer to a -traditional graphics API, while still targeting the same hardware directly. - -:::danger NDA-protected platform -The PlayStation 4 SDK, including the GNMX headers, samples, and reference documentation, is -covered by Sony's non-disclosure agreement with registered PlayStation Partners. Nothing from that -material can be reproduced, described, or linked to on this page, and the same restriction applies -to every page under this section of the documentation. -::: - -## Current status - -The `gnmx` backend in `gp-engine` is internal work in progress. The module currently holds build -scaffolding and an empty CMake target; no PlayStation NDA material has been added, because the -team does not yet have PlayStation NDA clearance. - -Once that clearance is in place, this page will be filled in for the contributors on the project -who are also registered PlayStation Partners. It will not be published outside that group, and -public builds of `gp-engine` will not ship with this backend enabled. - -## Requesting access - -Access to the PS4 SDK, dev kits, and GNMX documentation is managed entirely by Sony. If you need -to review or contribute to this backend, you or your studio have to register directly through -Sony's own program. - -- [PlayStation Partners](https://partners.playstation.net/): the registration portal for the - PlayStation developer program. - -We have no ability to grant, share, or forward PlayStation NDA material on request. Approval is at -Sony's sole discretion. - -## Curriculum - -Graphical Playground plans to cover console-specific rendering paths, GNMX included, as part of -its learning material. That curriculum sits behind the same NDA and will only be released to -contributors and learners who hold active PlayStation Partners clearance, once it exists. diff --git a/source/runtime/rhi/gnmx/docs/_category_.json b/source/runtime/rhi/gnmx/docs/_category_.json deleted file mode 100644 index f2867999..00000000 --- a/source/runtime/rhi/gnmx/docs/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "GNMX" -} diff --git a/source/runtime/rhi/gnmx/private/RHIGNMX.cpp b/source/runtime/rhi/gnmx/private/RHIGNMX.cpp deleted file mode 100644 index 3113e461..00000000 --- a/source/runtime/rhi/gnmx/private/RHIGNMX.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com diff --git a/source/runtime/rhi/gnmx/public/RHIGNMX.hpp b/source/runtime/rhi/gnmx/public/RHIGNMX.hpp deleted file mode 100644 index 88b93605..00000000 --- a/source/runtime/rhi/gnmx/public/RHIGNMX.hpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once diff --git a/source/runtime/rhi/nvn/.gitignore b/source/runtime/rhi/nvn/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/nvn/CHANGELOG.md b/source/runtime/rhi/nvn/CHANGELOG.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/nvn/CMakeLists.txt b/source/runtime/rhi/nvn/CMakeLists.txt deleted file mode 100644 index 9be62c67..00000000 --- a/source/runtime/rhi/nvn/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) - Graphical Playground. All rights reserved. -# For more information, see https://graphical-playground/legal -# mailto:support AT graphical-playground DOT com - -include(gp-build-tool) - -gpStartModule(rhi/nvn) - gpAddDependency(PUBLIC core) - gpAddDependency(PUBLIC rhi/base) -gpEndModule() diff --git a/source/runtime/rhi/nvn/README.md b/source/runtime/rhi/nvn/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/nvn/docs/README.md b/source/runtime/rhi/nvn/docs/README.md deleted file mode 100644 index 6f6ecf3a..00000000 --- a/source/runtime/rhi/nvn/docs/README.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -sidebar_position: 0 -title: NVN -description: Nintendo Switch low-level graphics API backend. NDA-protected, work in progress. -tags: - - rhi - - switch - - nintendo - - nda - - wip ---- - -# NVN - -

Nintendo Switch low-level graphics API backend.

- -NVN is Nintendo's low-level graphics API for the Switch family of consoles, distributed as part of -the Nintendo SDK. Like [GNM](../gnm/README.md) on PlayStation 4 and [Direct3D 12](../d3d12/README.md) -on desktop, it favors explicit command buffer and resource management over driver-managed state. - -:::danger NDA-protected platform -The Nintendo SDK, including the NVN headers, samples, and reference documentation, is covered by -Nintendo's non-disclosure agreement with licensed developers. Nothing from that material can be -reproduced, described, or linked to on this page, and the same restriction applies to every page -under this section of the documentation. -::: - -## Current status - -The `nvn` backend in `gp-engine` is internal work in progress. The module currently holds build -scaffolding and an empty CMake target; no Nintendo NDA material has been added, because the team -does not yet have Nintendo NDA clearance. - -Once that clearance is in place, this page will be filled in for the contributors on the project -who are also licensed Nintendo developers. It will not be published outside that group, and public -builds of `gp-engine` will not ship with this backend enabled. - -## Requesting access - -Access to the Nintendo SDK, dev kits, and NVN documentation is managed entirely by Nintendo. If you -need to review or contribute to this backend, you or your studio have to register directly through -Nintendo's own program. - -- [Nintendo Developer Portal](https://developer.nintendo.com/): the registration portal for the - Nintendo developer program. - -We have no ability to grant, share, or forward Nintendo NDA material on request. Approval is at -Nintendo's sole discretion. - -## Curriculum - -Graphical Playground plans to cover console-specific rendering paths, NVN included, as part of its -learning material. That curriculum sits behind the same NDA and will only be released to -contributors and learners who hold active Nintendo developer clearance, once it exists. diff --git a/source/runtime/rhi/nvn/docs/_category_.json b/source/runtime/rhi/nvn/docs/_category_.json deleted file mode 100644 index a613d595..00000000 --- a/source/runtime/rhi/nvn/docs/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "NVN" -} diff --git a/source/runtime/rhi/nvn/private/RHINVN.cpp b/source/runtime/rhi/nvn/private/RHINVN.cpp deleted file mode 100644 index 3113e461..00000000 --- a/source/runtime/rhi/nvn/private/RHINVN.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com diff --git a/source/runtime/rhi/nvn/public/RHINVN.hpp b/source/runtime/rhi/nvn/public/RHINVN.hpp deleted file mode 100644 index 88b93605..00000000 --- a/source/runtime/rhi/nvn/public/RHINVN.hpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once diff --git a/source/runtime/rhi/opengles/.gitignore b/source/runtime/rhi/opengles/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/opengles/CHANGELOG.md b/source/runtime/rhi/opengles/CHANGELOG.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/opengles/CMakeLists.txt b/source/runtime/rhi/opengles/CMakeLists.txt deleted file mode 100644 index ec0010b7..00000000 --- a/source/runtime/rhi/opengles/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) - Graphical Playground. All rights reserved. -# For more information, see https://graphical-playground/legal -# mailto:support AT graphical-playground DOT com - -include(gp-build-tool) - -gpStartModule(rhi/opengles) - gpAddDependency(PUBLIC core) - gpAddDependency(PUBLIC rhi/base) -gpEndModule() diff --git a/source/runtime/rhi/opengles/README.md b/source/runtime/rhi/opengles/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/opengles/docs/README.md b/source/runtime/rhi/opengles/docs/README.md deleted file mode 100644 index abff5f72..00000000 --- a/source/runtime/rhi/opengles/docs/README.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -sidebar_position: 0 -title: OpenGLES ---- diff --git a/source/runtime/rhi/opengles/docs/_category_.json b/source/runtime/rhi/opengles/docs/_category_.json deleted file mode 100644 index b5561e6c..00000000 --- a/source/runtime/rhi/opengles/docs/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "OpenGLES" -} diff --git a/source/runtime/rhi/opengles/private/RHIOpenGLES.cpp b/source/runtime/rhi/opengles/private/RHIOpenGLES.cpp deleted file mode 100644 index 3113e461..00000000 --- a/source/runtime/rhi/opengles/private/RHIOpenGLES.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com diff --git a/source/runtime/rhi/opengles/public/RHIOpenGLES.hpp b/source/runtime/rhi/opengles/public/RHIOpenGLES.hpp deleted file mode 100644 index 88b93605..00000000 --- a/source/runtime/rhi/opengles/public/RHIOpenGLES.hpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once diff --git a/source/runtime/rhi/webgl/.gitignore b/source/runtime/rhi/webgl/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/webgl/CHANGELOG.md b/source/runtime/rhi/webgl/CHANGELOG.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/webgl/CMakeLists.txt b/source/runtime/rhi/webgl/CMakeLists.txt deleted file mode 100644 index ba0b88e1..00000000 --- a/source/runtime/rhi/webgl/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) - Graphical Playground. All rights reserved. -# For more information, see https://graphical-playground/legal -# mailto:support AT graphical-playground DOT com - -include(gp-build-tool) - -gpStartModule(rhi/webgl) - gpAddDependency(PUBLIC core) - gpAddDependency(PUBLIC rhi/base) -gpEndModule() diff --git a/source/runtime/rhi/webgl/README.md b/source/runtime/rhi/webgl/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/webgl/docs/README.md b/source/runtime/rhi/webgl/docs/README.md deleted file mode 100644 index 9f5ae889..00000000 --- a/source/runtime/rhi/webgl/docs/README.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -sidebar_position: 0 -title: WebGL ---- diff --git a/source/runtime/rhi/webgl/docs/_category_.json b/source/runtime/rhi/webgl/docs/_category_.json deleted file mode 100644 index 6a83c941..00000000 --- a/source/runtime/rhi/webgl/docs/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "WebGL" -} diff --git a/source/runtime/rhi/webgl/private/RHIWebGL.cpp b/source/runtime/rhi/webgl/private/RHIWebGL.cpp deleted file mode 100644 index 3113e461..00000000 --- a/source/runtime/rhi/webgl/private/RHIWebGL.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com diff --git a/source/runtime/rhi/webgl/public/RHIWebGL.hpp b/source/runtime/rhi/webgl/public/RHIWebGL.hpp deleted file mode 100644 index 88b93605..00000000 --- a/source/runtime/rhi/webgl/public/RHIWebGL.hpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once diff --git a/source/runtime/rhi/webgpu/.gitignore b/source/runtime/rhi/webgpu/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/webgpu/CHANGELOG.md b/source/runtime/rhi/webgpu/CHANGELOG.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/webgpu/CMakeLists.txt b/source/runtime/rhi/webgpu/CMakeLists.txt deleted file mode 100644 index 5d5ae83e..00000000 --- a/source/runtime/rhi/webgpu/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) - Graphical Playground. All rights reserved. -# For more information, see https://graphical-playground/legal -# mailto:support AT graphical-playground DOT com - -include(gp-build-tool) - -gpStartModule(rhi/webgpu) - gpAddDependency(PUBLIC core) - gpAddDependency(PUBLIC rhi/base) -gpEndModule() diff --git a/source/runtime/rhi/webgpu/README.md b/source/runtime/rhi/webgpu/README.md deleted file mode 100644 index e69de29b..00000000 diff --git a/source/runtime/rhi/webgpu/docs/README.md b/source/runtime/rhi/webgpu/docs/README.md deleted file mode 100644 index 310ca4e4..00000000 --- a/source/runtime/rhi/webgpu/docs/README.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -sidebar_position: 0 -title: WebGPU ---- diff --git a/source/runtime/rhi/webgpu/docs/_category_.json b/source/runtime/rhi/webgpu/docs/_category_.json deleted file mode 100644 index c9dc26ee..00000000 --- a/source/runtime/rhi/webgpu/docs/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "WebGPU" -} diff --git a/source/runtime/rhi/webgpu/private/RHIWebGPU.cpp b/source/runtime/rhi/webgpu/private/RHIWebGPU.cpp deleted file mode 100644 index 3113e461..00000000 --- a/source/runtime/rhi/webgpu/private/RHIWebGPU.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com diff --git a/source/runtime/rhi/webgpu/public/RHIWebGPU.hpp b/source/runtime/rhi/webgpu/public/RHIWebGPU.hpp deleted file mode 100644 index 88b93605..00000000 --- a/source/runtime/rhi/webgpu/public/RHIWebGPU.hpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) - Graphical Playground. All rights reserved. -// For more information, see https://graphical-playground/legal -// mailto:support AT graphical-playground DOT com - -#pragma once diff --git a/thirdparty/zstd/CMakeLists.txt b/thirdparty/zstd/CMakeLists.txt new file mode 100644 index 00000000..83cd2b73 --- /dev/null +++ b/thirdparty/zstd/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright (c) - Graphical Playground. All rights reserved. +# For more information, see https://graphical-playground/legal +# mailto:support AT graphical-playground DOT com + +include(gp-build-tool) + +gpStartThirdparty("zstd" VERSION "1.5.7") + gpThirdpartyRequiresPlatforms(Windows Linux macOS iOS Android) + + gpThirdpartySource( + URL "https://github.com/facebook/zstd/archive/refs/tags/v1.5.7.tar.gz" + HASH "SHA256=37d7284556b20954e56e1ca85b80226768902e2edabd3b649e9e72c0c9012ee3" + TARGET "zstd::libzstd_static" + ) + + gpThirdpartySetCMakeArgs( + ZSTD_BUILD_PROGRAMS=OFF + ZSTD_BUILD_SHARED=OFF + ZSTD_BUILD_STATIC=ON + ZSTD_BUILD_TESTS=OFF + ZSTD_MULTITHREAD_SUPPORT=ON + ) +gpEndThirdparty() diff --git a/thirdparty/zstd/LICENSE.md b/thirdparty/zstd/LICENSE.md new file mode 100644 index 00000000..75800288 --- /dev/null +++ b/thirdparty/zstd/LICENSE.md @@ -0,0 +1,30 @@ +BSD License + +For Zstandard software + +Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook, nor Meta, nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.