feat(graphics): improve rendering pipeline and docs

- Refactor D3D12 backend and RenderGraph module
- Update graphics RHI and core rendering components
- Add Random.hlsl shader include
- Regenerate API documentation and update user guides
This commit is contained in:
2026-03-27 22:23:44 +09:00
parent 0a2eb619eb
commit d8a7b07624
495 changed files with 51961 additions and 892 deletions

View File

@@ -0,0 +1,162 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Components Overview | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Components Overview | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="components-overview">Components Overview</h1>
<p>In GhostEngine, components are strict, unmanaged data structures. They must implement the empty interface <code>IComponent</code> (or its derivations). This ensures AOT compatibility and zero GC allocation overhead when iterating over millions of entities.</p>
<h2 id="defining-a-component">Defining a Component</h2>
<p>To define a standard component, create an unmanaged struct implementing <code>IComponent</code>:</p>
<pre><code class="lang-csharp">using Ghost.Entities;
using System.Numerics;
public struct TransformComponent : IComponent
{
public Vector3 Position;
public Quaternion Rotation;
public Vector3 Scale;
}
</code></pre>
<h2 id="enableable-components">Enableable Components</h2>
<p>Sometimes you need to toggle a component on or off without incurring the heavy structural cost of removing and re-adding the component from an entity. GhostEngine supports this via <code>IEnableableComponent</code>.</p>
<pre><code class="lang-csharp">public struct Renderable : IEnableableComponent
{
public int MeshID;
public int MaterialID;
}
</code></pre>
<p>When iterating over an archetype chunk, you can check the bitmask to see if an entity's enableable component is currently active:</p>
<pre><code class="lang-csharp">// During a query chunk iteration:
var renderables = chunkView.GetComponentData&lt;Renderable&gt;();
var enableBits = chunkView.GetEnableBits&lt;Renderable&gt;();
for (int i = 0; i &lt; chunkView.EntityCount; i++)
{
if (chunkView.IsComponentEnabled&lt;Renderable&gt;(i))
{
// Process active renderable
}
}
</code></pre>
<h2 id="component-type-ids">Component Type IDs</h2>
<p>The engine assigns a unique runtime ID to each unmanaged component type. You can retrieve this identifier using the <code>ComponentTypeID&lt;T&gt;.Value</code> generic cache:</p>
<pre><code class="lang-csharp">Identifier&lt;IComponent&gt; id = ComponentTypeID&lt;TransformComponent&gt;.Value;
</code></pre>
<p>This ID is heavily used under the hood for query masking, archetype matching, and memory offset calculations inside a chunk.</p>
<h2 id="requirecomponent-attribute">RequireComponent Attribute</h2>
<p>You can enforce dependencies between components using the <code>[RequireComponentAttribute&lt;T&gt;]</code>. While this is mostly semantic, it helps structure dependencies for complex systems.</p>
<pre><code class="lang-csharp">[RequireComponent&lt;TransformComponent&gt;]
public struct PhysicsCollider : IComponent
{
public float Radius;
}
</code></pre>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,213 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECS Architecture | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="ECS Architecture | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="ecs-architecture">ECS Architecture</h1>
<p>This page describes how <code>Ghost.Entities</code> is organized internally and how the main pieces interact.</p>
<h2 id="core-objects">Core Objects</h2>
<ul>
<li><code>World</code>
<ul>
<li>Root owner for ECS runtime state.</li>
<li>Holds <code>EntityManager</code>, <code>ComponentManager</code>, <code>SystemManager</code>, and command buffers.</li>
<li>Carries a monotonic <code>Version</code> used by change tracking.</li>
</ul>
</li>
<li><code>Entity</code>
<ul>
<li>Lightweight handle (<code>ID</code>, <code>Generation</code>) used to validate stale references.</li>
</ul>
</li>
<li><code>EntityManager</code>
<ul>
<li>Responsible for create/destroy and structural changes (add/remove component).</li>
<li>Tracks entity location <code>(archetype, chunk, row)</code> in a slot map.</li>
</ul>
</li>
<li><code>ComponentManager</code>
<ul>
<li>Owns archetypes and query cache.</li>
<li>Maps signature hashes to archetype IDs.</li>
<li>Maps query-mask hashes to query IDs.</li>
</ul>
</li>
<li><code>EntityQuery</code>
<ul>
<li>Stores a compiled query mask.</li>
<li>Maintains a list of matching archetypes.</li>
<li>Exposes iterators (<code>chunk</code>, <code>component</code>, <code>entity+component</code>) and scheduling APIs.</li>
</ul>
</li>
</ul>
<h2 id="archetype-and-chunk-model">Archetype and Chunk Model</h2>
<p>Entities are grouped by exact component signature into archetypes.</p>
<ul>
<li>Each archetype owns N chunks.</li>
<li>Each chunk stores:
<ul>
<li>packed entity IDs,</li>
<li>packed component arrays,</li>
<li>optional enable bitmasks for enableable components,</li>
<li>per-component change versions,</li>
<li>chunk structural version.</li>
</ul>
</li>
</ul>
<p>Effects:</p>
<ul>
<li>Data for the same component is contiguous per chunk.</li>
<li>Iteration can be cache-friendly and branch-light.</li>
<li>Structural operations move entities between archetypes.</li>
</ul>
<h2 id="structural-changes">Structural Changes</h2>
<p>Adding/removing components changes an entity signature, so the entity moves:</p>
<ol>
<li>Resolve current <code>EntityLocation</code>.</li>
<li>Resolve or create destination archetype.</li>
<li>Allocate destination row.</li>
<li>Copy shared component bytes from old to new archetype layout.</li>
<li>Write the new/removed component result.</li>
<li>Remove old row with swap-back compaction.</li>
<li>Update <code>EntityLocation</code> for moved entities.</li>
</ol>
<p>Archetypes cache transition edges (<code>Add</code> / <code>Remove</code>) to avoid recomputing destination lookups repeatedly.</p>
<h2 id="query-compilation-and-matching">Query Compilation and Matching</h2>
<p><code>QueryBuilder</code> compiles high-level filter methods into bitset masks.</p>
<p>Main mask dimensions:</p>
<ul>
<li>structural requirements (<code>All</code>, <code>Any</code>, <code>Absent</code>),</li>
<li>enabled/disabled semantic filters,</li>
<li>write access intent.</li>
</ul>
<p>A query first filters at archetype level (signature match), then performs per-entity checks for enablement semantics when needed.</p>
<h2 id="systems-and-update-flow">Systems and Update Flow</h2>
<ul>
<li><code>SystemManager</code> owns system instances.</li>
<li><code>SystemGroup</code> can sort systems using <code>[UpdateAfter]</code> / <code>[UpdateBefore]</code>.</li>
<li><code>SystemBase</code> supports <code>RequireQueryForUpdate</code> gating and lifecycle hooks:
<ul>
<li><code>OnInitialize</code>,</li>
<li><code>OnStartRunning</code> / <code>OnStopRunning</code>,</li>
<li><code>OnUpdate</code>,</li>
<li><code>OnCleanup</code>.</li>
</ul>
</li>
</ul>
<h2 id="deferred-structural-writes">Deferred Structural Writes</h2>
<p><code>EntityCommandBuffer</code> records structural commands into a compact byte buffer.</p>
<ul>
<li>Record in gameplay/jobs.</li>
<li>Playback at controlled sync points.</li>
<li>Avoid invalidating iterators while iterating live chunks.</li>
</ul>
<p>Use per-thread command buffers (<code>World.GetThreadLocalEntityCommandBuffer</code>) in multithreaded pipelines.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,134 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECS Concepts | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="ECS Concepts | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="ecs-concepts">ECS Concepts</h1>
<p>GhostEngine's ECS implementation relies on several key concepts rooted in Data-Oriented Design. Understanding these concepts is essential to writing performant engine code and gameplay logic.</p>
<h2 id="world">World</h2>
<p>A <code>World</code> encapsulates all data and logic for an independent ECS simulation. It owns three core managers:</p>
<ul>
<li><strong>EntityManager:</strong> Handles the creation, destruction, and structural modification of Entities.</li>
<li><strong>ComponentManager:</strong> Manages Archetypes and EntityQueries, keeping track of component memory layouts and chunk allocations.</li>
<li><strong>SystemManager:</strong> Manages the lifecycle and execution order of all Systems.</li>
</ul>
<p>Multiple Worlds can exist simultaneously (e.g., one for logic, one for rendering, or server/client splits in multiplayer).</p>
<h2 id="entity">Entity</h2>
<p>An <code>Entity</code> in Ghost.Entities is not an object or a class; it is merely an integer ID (often accompanied by a version number). It serves as a lookup key to find a specific set of component data.</p>
<h2 id="component">Component</h2>
<p>A <code>Component</code> is a pure, unmanaged data structure (<code>struct</code>). It implements <code>IComponent</code> or <code>IEnableableComponent</code>. Components contain no logic. By restricting components to unmanaged types, the engine ensures they can be packed tightly into continuous blocks of memory (Chunks), entirely avoiding Garbage Collection (GC) overhead and ensuring fast CPU cache lines.</p>
<h2 id="archetype-and-chunks">Archetype and Chunks</h2>
<p>When you create an entity with a specific set of components (e.g., <code>Position</code> and <code>Velocity</code>), the ECS groups it into an <strong>Archetype</strong>. An Archetype represents a unique signature of components.</p>
<p>Entities of the same Archetype have their component data stored in <strong>Chunks</strong>. A Chunk is a fixed-size block of unmanaged memory (e.g., 16KB).</p>
<p>Instead of storing arrays of Entities containing objects (Array of Structures), the Chunk stores independent arrays for each component type (Structure of Arrays). This means when a system queries for <code>Position</code>, it receives a contiguous unmanaged memory <code>Span&lt;Position&gt;</code>, allowing the CPU to aggressively prefetch data and vectorize operations.</p>
<h2 id="system">System</h2>
<p>A <code>System</code> provides the logic that transforms component data from one state to the next. Systems iterate over matching Archetypes using <code>EntityQuery</code> and process component data in bulk. Systems can be managed manually or grouped into a <code>SystemGroup</code> which topologically sorts execution order based on dependencies.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,181 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ECS Workflows | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="ECS Workflows | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="ecs-workflows">ECS Workflows</h1>
<p>This document outlines standard daily workflows for working within GhostEngine's ECS.</p>
<h2 id="creating-entity-queries">Creating Entity Queries</h2>
<p>To read or modify data, you first build an <code>EntityQuery</code>. You use the <code>QueryBuilder</code> struct to define inclusion and exclusion constraints.</p>
<pre><code class="lang-csharp">Identifier&lt;EntityQuery&gt; queryId = QueryBuilder.Create()
.WithAll&lt;Position&gt;() // Must have Position
.WithAny&lt;Player, Enemy&gt;() // Must have Player OR Enemy
.WithNone&lt;Dead&gt;() // Must NOT have Dead structurally
.WithDisabled&lt;Renderable&gt;() // Must have Renderable structurally, but it must be disabled
.Build(world);
</code></pre>
<p>Queries are hashed and cached internally by the <code>ComponentManager</code>. Building the same query mask twice will yield the same <code>Identifier&lt;EntityQuery&gt;</code>.</p>
<h2 id="iterating-chunk-views">Iterating Chunk Views</h2>
<p>Once you have a query, use <code>GetChunkIterator()</code> to traverse the unmanaged memory directly. This returns a <code>ChunkView</code> struct.</p>
<p>Because the data is unmanaged, you can extract <code>Span&lt;T&gt;</code> and modify it with extreme speed.</p>
<pre><code class="lang-csharp">ref var query = ref world.ComponentManager.GetEntityQueryReference(queryId);
foreach (var chunkView in query.GetChunkIterator())
{
// Get Read-Only Span
ReadOnlySpan&lt;Velocity&gt; vels = chunkView.GetComponentData&lt;Velocity&gt;();
// Get Read-Write Span
Span&lt;Position&gt; pos = chunkView.GetComponentDataRW&lt;Position&gt;();
// For loop for cache-aligned processing
for (int i = 0; i &lt; chunkView.EntityCount; i++)
{
pos[i].Value += vels[i].Value * dt;
}
}
</code></pre>
<blockquote>
<p><strong>Note:</strong> Accessing data using <code>GetComponentDataRW&lt;T&gt;()</code> will automatically bump the internal version numbers for that component type in that specific chunk, allowing other systems to detect changes efficiently.</p>
</blockquote>
<h2 id="entity-command-buffers-structural-changes">Entity Command Buffers (Structural Changes)</h2>
<p>In ECS, structural changes (adding/removing components, destroying entities) reorganize chunk memory. Doing this while iterating over chunks invalidates the memory layout.</p>
<p>To safely defer structural changes, use an <code>EntityCommandBuffer</code>.</p>
<pre><code class="lang-csharp">// Get the world's main ECB
var ecb = world.EntityCommandBuffer;
foreach (var chunk in query.GetChunkIterator())
{
var entities = chunk.GetEntities();
var healths = chunk.GetComponentData&lt;Health&gt;();
for (int i = 0; i &lt; chunk.EntityCount; i++)
{
if (healths[i].Value &lt;= 0)
{
// Defer the destruction until Playback() is called
ecb.DestroyEntity(entities[i]);
}
}
}
</code></pre>
<p>At the end of the frame (or the end of the system group update), you must call:</p>
<pre><code class="lang-csharp">world.PlaybackEntityCommandBuffers();
</code></pre>
<h3 id="multi-threading-ecbs">Multi-threading ECBs</h3>
<p>When using the <code>JobScheduler</code>, you can record deferred commands across multiple worker threads concurrently by requesting thread-local ECBs:</p>
<pre><code class="lang-csharp">int workerIndex = JobScheduler.CurrentThreadIndex;
var threadEcb = world.GetThreadLocalEntityCommandBuffer(workerIndex);
// Safely record structural changes from parallel jobs
threadEcb.AddComponent&lt;Dead&gt;(entity);
</code></pre>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,156 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Entity Command Buffer | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Entity Command Buffer | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="entity-command-buffer">Entity Command Buffer</h1>
<p><code>EntityCommandBuffer</code> (ECB) records structural operations and replays them later.</p>
<p>Use it when you cannot safely mutate structure immediately (for example, while iterating query results or inside parallel jobs).</p>
<h2 id="why-use-ecb">Why Use ECB</h2>
<ul>
<li>Avoid structural modifications during active iteration.</li>
<li>Batch operations into one playback phase.</li>
<li>Use thread-local command buffers in multithreaded jobs.</li>
</ul>
<h2 id="recorded-operations">Recorded Operations</h2>
<p>The ECB supports recording:</p>
<ul>
<li>create entity,</li>
<li>create entity with component set,</li>
<li>destroy entity,</li>
<li>add component,</li>
<li>remove component,</li>
<li>set component.</li>
</ul>
<h2 id="basic-usage">Basic Usage</h2>
<pre><code class="lang-csharp">var world = World.Create();
var ecb = world.EntityCommandBuffer;
ecb.CreateEntity(count: 100);
var entity = world.EntityManager.CreateEntity();
ecb.AddComponent(entity, new Velocity { X = 1, Y = 0, Z = 0 });
ecb.SetComponent(entity, new Velocity { X = 2, Y = 0, Z = 0 });
</code></pre>
<h2 id="thread-local-ecb">Thread-Local ECB</h2>
<p>When a world has a <code>JobScheduler</code>, obtain per-thread buffers:</p>
<pre><code class="lang-csharp">var threadLocal = world.GetThreadLocalEntityCommandBuffer(threadIndex);
threadLocal.DestroyEntity(entity);
</code></pre>
<p>Then playback all buffers at a sync point:</p>
<p>Playback is performed by the world/runtime update flow at synchronization points.</p>
<h2 id="notes">Notes</h2>
<ul>
<li>Playback executes in recording order per buffer.</li>
<li>Playback clears internal command data (<code>Reset</code>) after execution.</li>
<li>ECB does not replace dependency handling; you still need proper job synchronization before playback.</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,219 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Entity Query Usage | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Entity Query Usage | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="entity-query-usage">Entity Query Usage</h1>
<p>This page describes practical <code>EntityQuery</code> usage patterns from the runtime ECS API.
For full API signatures, see the generated API pages under <code>doc/api/</code>.</p>
<h2 id="overview">Overview</h2>
<p><code>EntityQuery</code> is the main data access surface for ECS iteration in <code>Ghost.Entities</code>.</p>
<ul>
<li>Build a query once using <code>QueryBuilder</code> and reuse the query handle (<code>Identifier&lt;EntityQuery&gt;</code>) across frames.</li>
<li>Access query data in three common ways:
<ul>
<li><code>ForEach</code> for simple lambda-style iteration.</li>
<li><code>GetEntityComponentIterator&lt;T...&gt;()</code> when you need entity IDs with component refs.</li>
<li><code>GetChunkIterator()</code> for cache-friendly chunk-level traversal.</li>
</ul>
</li>
<li>Schedule jobs (<code>IJobChunk</code> / <code>IJobEntity&lt;...&gt;</code>) for parallel work.</li>
</ul>
<h2 id="minimal-setup">Minimal Setup</h2>
<pre><code class="lang-csharp">using Misaki.HighPerformance.Jobs;
using Misaki.HighPerformance.LowLevel.Buffer;
var scheduler = new JobScheduler(4);
var world = World.Create(scheduler);
using var scope = AllocationManager.CreateStackScope();
using var set = new ComponentSet(scope.AllocationHandle, ComponentTypeID&lt;Transform&gt;.Value);
Span&lt;Entity&gt; entities = stackalloc Entity[1000];
world.EntityManager.CreateEntities(entities, set);
var queryID = new QueryBuilder().WithAllRW&lt;Transform&gt;().Build(world);
ref var query = ref world.ComponentManager.GetEntityQueryReference(queryID);
</code></pre>
<h2 id="pattern-1-foreach-for-simple-iteration">Pattern 1: <code>ForEach</code> for Simple Iteration</h2>
<p>Use <code>ForEach&lt;T...&gt;</code> when you want concise in-place updates or debug output.</p>
<pre><code class="lang-csharp">query.ForEach&lt;Transform&gt;((entity, ref Transform transform) =&gt;
{
transform.position += new float3(1, 0, 0);
});
</code></pre>
<p>Why this pattern:</p>
<ul>
<li>Fast to write and easy to read.</li>
<li>Good for gameplay logic and quick data transforms.</li>
</ul>
<h2 id="pattern-2-entity--component-iterator">Pattern 2: Entity + Component Iterator</h2>
<p>Use <code>GetEntityComponentIterator&lt;T...&gt;()</code> when you need both identity and mutable component data.</p>
<pre><code class="lang-csharp">foreach (var (entity, transform) in query.GetEntityComponentIterator&lt;Transform&gt;())
{
transform.Get().position += new float3(0, 1, 0);
}
</code></pre>
<p>Why this pattern:</p>
<ul>
<li>Explicit access to the entity in each iteration.</li>
<li>Works well when you need side lookups keyed by entity.</li>
</ul>
<h2 id="pattern-3-chunk-iterator-for-throughput">Pattern 3: Chunk Iterator for Throughput</h2>
<p>Use <code>GetChunkIterator()</code> for the most cache-friendly, low-overhead traversal.</p>
<pre><code class="lang-csharp">foreach (var chunk in query.GetChunkIterator())
{
var transforms = chunk.GetComponentDataRW&lt;Transform&gt;();
var entitiesInChunk = chunk.GetEntities();
for (var i = 0; i &lt; chunk.EntityCount; i++)
{
transforms[i].position += new float3(0, 0, 1);
}
}
</code></pre>
<p>Why this pattern:</p>
<ul>
<li>Minimizes per-entity overhead.</li>
<li>Best fit for heavy simulation and broad data transforms.</li>
</ul>
<h2 id="parallel-job-scheduling">Parallel Job Scheduling</h2>
<p><code>EntityQuery</code> supports chunk and entity parallel scheduling.</p>
<p><code>IJobChunk</code> example:</p>
<pre><code class="lang-csharp">internal struct MoveChunkJob : IJobChunk
{
public void Execute(ChunkView view, ref readonly JobExecutionContext ctx)
{
var transforms = view.GetComponentDataRW&lt;Transform&gt;();
for (var i = 0; i &lt; view.EntityCount; i++)
{
transforms[i].position += new float3(5, 5, 5);
}
}
}
var handle = query.ScheduleChunkParallel(new MoveChunkJob(), batchSize: 1, JobHandle.Invalid);
scheduler.Wait(handle);
</code></pre>
<p>Notes:</p>
<ul>
<li>Ensure all dependencies are represented in the input <code>JobHandle</code>.</li>
<li>Wait or chain handles before reading modified data on the main thread.</li>
<li>Prefer chunk jobs for contiguous memory access patterns.</li>
</ul>
<h2 id="lifecycle-and-cleanup">Lifecycle and Cleanup</h2>
<pre><code class="lang-csharp">world.EntityManager.DestroyEntities(entities);
world.Dispose();
scheduler.Dispose();
JobScheduler.ReleaseTempAllocator();
</code></pre>
<p>If your test or app creates worlds/schedulers manually, always clean them up in deterministic order.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,184 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Getting Started with Ghost.Entities | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Getting Started with Ghost.Entities | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="getting-started-with-ghostentities">Getting Started with Ghost.Entities</h1>
<p>GhostEngine's Entity Component System (ECS) is a high-performance, Data-Oriented architecture designed for C# .NET 10. The ECS runtime is strictly AOT-compatible and focuses on unmanaged data structures to maximize cache locality and performance.</p>
<h2 id="initialization">Initialization</h2>
<p>The core of the ECS is the <code>World</code>. A <code>World</code> contains an <code>EntityManager</code>, <code>ComponentManager</code>, and <code>SystemManager</code>.</p>
<p>To get started, you must create a <code>World</code>:</p>
<pre><code class="lang-csharp">using Ghost.Entities;
// Create a new World with a default entity capacity of 16
World world = World.Create();
</code></pre>
<h2 id="creating-entities-and-adding-components">Creating Entities and Adding Components</h2>
<p>Entities are simply IDs that point to a combination of components. You use the <code>EntityManager</code> to create and modify them.</p>
<p>First, define your unmanaged components:</p>
<pre><code class="lang-csharp">public struct Position : IComponent
{
public float X, Y, Z;
}
public struct Velocity : IComponent
{
public float X, Y, Z;
}
</code></pre>
<p>Then, create entities and attach components:</p>
<pre><code class="lang-csharp">using Ghost.Core;
using Misaki.HighPerformance.LowLevel.Buffer;
// Creating a ComponentSet for fast archetype initialization
using var scope = AllocationManager.CreateStackScope();
var componentSet = new ComponentSet(
scope.AllocationHandle,
ComponentTypeID&lt;Position&gt;.Value,
ComponentTypeID&lt;Velocity&gt;.Value
);
// Create 1000 entities with both Position and Velocity
world.EntityManager.CreateEntities(1000, componentSet);
</code></pre>
<h2 id="adding-systems-and-updating">Adding Systems and Updating</h2>
<p>Systems contain the logic that operates on the component data. You add systems to the <code>SystemManager</code> and run them inside a loop.</p>
<pre><code class="lang-csharp">// Get the DefaultSystemGroup to attach systems
var group = world.SystemManager.GetSystem&lt;DefaultSystemGroup&gt;();
// Add custom systems
group.AddSystem&lt;MovementSystem&gt;();
// Sort systems based on their dependencies
group.SortSystems();
// Initialize all systems
world.SystemManager.InitializeAll(new TimeData());
// Update loop
while (running)
{
world.SystemManager.UpdateAll(timeData);
world.PlaybackEntityCommandBuffers();
}
// Cleanup at the end
world.Dispose();
</code></pre>
<h2 id="summary">Summary</h2>
<p>In GhostEngine ECS:</p>
<ol>
<li><strong>World</strong> manages the lifecycle of your ECS data.</li>
<li><strong>Components</strong> (<code>IComponent</code>) are unmanaged data structures.</li>
<li><strong>Entities</strong> are logical containers combining those components into archetypes.</li>
<li><strong>Systems</strong> (<code>SystemBase</code>) execute logic over arrays of components using <code>EntityQuery</code>.</li>
</ol>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,132 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Introduction | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Introduction | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="introduction">Introduction</h1>
<p>Ghost.Entities is an archetype-based ECS runtime designed for high throughput and predictable memory access.</p>
<p>At a high level:</p>
<ul>
<li><code>World</code> owns all ECS state for an isolated simulation context.</li>
<li><code>EntityManager</code> performs structural/entity/component operations.</li>
<li><code>ComponentManager</code> owns archetypes and cached queries.</li>
<li><code>EntityQuery</code> provides data access and iteration over matching archetypes/chunks.</li>
<li><code>SystemManager</code> and <code>SystemGroup</code> drive update order and lifecycle.</li>
</ul>
<p>The runtime favors:</p>
<ul>
<li>contiguous data layouts,</li>
<li>low-level spans/pointers in hot paths,</li>
<li>explicit structural changes,</li>
<li>deferred writes through <code>EntityCommandBuffer</code> for safe mutation points.</li>
</ul>
<p>Use this section for conceptual docs, and <code>doc/api/</code> for generated API reference.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,199 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Performance Patterns | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Performance Patterns | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="performance-patterns">Performance Patterns</h1>
<p>This page captures the runtime performance techniques used in <code>Ghost.Entities</code> and how to apply them.</p>
<h2 id="data-layout">Data Layout</h2>
<ul>
<li>Archetype storage uses contiguous component arrays per chunk.</li>
<li>Chunk memory is fixed-size (<code>Chunk.CHUNK_BUFFER_SIZE</code>) to bound allocations and improve locality.</li>
<li>Component layouts are precomputed per archetype:
<ul>
<li>offset,</li>
<li>size,</li>
<li>enable-bit offset,</li>
<li>version index.</li>
</ul>
</li>
</ul>
<p>Why it matters:</p>
<ul>
<li>Sequential memory access in inner loops.</li>
<li>Fewer pointer indirections than object-per-entity layouts.</li>
</ul>
<h2 id="structural-cost-model">Structural Cost Model</h2>
<p>Structural changes are intentionally explicit and potentially expensive.</p>
<ul>
<li><code>AddComponent</code> / <code>RemoveComponent</code> move entities between archetypes.</li>
<li>Removal uses swap-back compaction to keep chunks dense.</li>
<li>Batch destroy path sorts locations and processes by chunk to reduce random work.</li>
</ul>
<p>Guidance:</p>
<ul>
<li>Prefer bulk creation APIs (<code>CreateEntities</code>) over repeated single inserts.</li>
<li>Minimize structural churn in hot update loops.</li>
<li>Use <code>EntityCommandBuffer</code> to defer structural edits.</li>
</ul>
<h2 id="query-efficiency">Query Efficiency</h2>
<ul>
<li>Queries are compiled into bitset masks.</li>
<li>Query instances are cached by mask hash in <code>ComponentManager</code>.</li>
<li>New archetypes are incrementally tested and added to existing query match lists.</li>
</ul>
<p>Guidance:</p>
<ul>
<li>Build query once, reuse query ID.</li>
<li>Avoid rebuilding identical queries every frame.</li>
</ul>
<h2 id="enableable-components">Enableable Components</h2>
<p>Enableable state is stored in bitmasks per chunk.</p>
<ul>
<li>Toggle with <code>EntityManager.SetEnabled&lt;T&gt;(...)</code>.</li>
<li>Query semantics (<code>WithAll</code>, <code>WithNone</code>, <code>WithDisabled</code>, <code>WithPresent</code>) combine structural and enablement checks.</li>
</ul>
<p>Guidance:</p>
<ul>
<li>Prefer enable/disable for state toggles over add/remove where possible.</li>
</ul>
<h2 id="change-versioning">Change Versioning</h2>
<p>The runtime tracks:</p>
<ul>
<li>world version (<code>World.Version</code>),</li>
<li>chunk structural version,</li>
<li>per-component version per chunk.</li>
</ul>
<p><code>ChunkView</code> APIs such as <code>HasChanged&lt;T&gt;(version)</code> allow change-filtered work.</p>
<p>Guidance:</p>
<ul>
<li>Cache last processed version per system/subsystem.</li>
<li>Skip unchanged chunks/components when possible.</li>
</ul>
<h2 id="jobs-and-parallel-chunk-processing">Jobs and Parallel Chunk Processing</h2>
<p><code>EntityQuery.ScheduleChunkParallel</code> transforms matched chunks into job batches and schedules <code>IJobParallelFor</code> work.</p>
<p>Guidance:</p>
<ul>
<li>Use chunk jobs for broad data transforms.</li>
<li>Keep <code>batchSize</code> tuned for your workload.</li>
<li>Chain dependencies correctly and wait only at necessary sync points.</li>
</ul>
<h2 id="allocation-and-lifetime-hygiene">Allocation and Lifetime Hygiene</h2>
<ul>
<li>Temporary data often uses stack/temporary allocators (<code>AllocationManager.CreateStackScope</code>).</li>
<li>Persistent containers are explicitly disposed.</li>
</ul>
<p>Guidance:</p>
<ul>
<li>Keep temporary allocations scoped and short-lived.</li>
<li>Always dispose worlds, schedulers, and persistent collections deterministically.</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,156 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Performance and Memory | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Performance and Memory | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="performance-and-memory">Performance and Memory</h1>
<p>GhostEngine's ECS is explicitly designed around maximum CPU efficiency and AOT compatibility.</p>
<h2 id="structure-of-arrays-soa">Structure of Arrays (SoA)</h2>
<p>Traditional OOP patterns use Arrays of Structures (AoS), which causes cache misses when looping through objects to read a single field.</p>
<p>GhostEngine ECS uses Structure of Arrays (SoA) at the chunk level. A 16KB unmanaged <code>Chunk</code> separates components into continuous blocks. When a <code>System</code> iterates a <code>ChunkView</code> and calls <code>GetComponentData&lt;Position&gt;()</code>, it receives a contiguous <code>Span&lt;Position&gt;</code>.</p>
<p>Because modern CPUs pre-fetch continuous memory aggressively, an ECS query can be up to orders of magnitude faster than naive OOP equivalents.</p>
<h2 id="benchmark-example">Benchmark Example</h2>
<p>The internal <code>Ghost.Entities.Test</code> benchmarks demonstrate the raw speed comparison:</p>
<pre><code class="lang-csharp">// OOP Approach (~4000ns)
for (var i = 0; i &lt; _gameObjects.Length; i++)
{
_gameObjects[i].Position += new Vector4(_dt, _dt, _dt, 0);
}
// ECS Approach (~620ns)
ref var query = ref _world.ComponentManager.GetEntityQueryReference(_queryIdentifier);
foreach (var chunkView in query.GetChunkIterator())
{
var positions = chunkView.GetComponentDataRW&lt;Position&gt;();
ref var address = ref MemoryMarshal.GetReference(positions);
for (var i = 0; i &lt; positions.Length; i++)
{
Unsafe.Add(ref address, i).value += new Vector4(_dt, _dt, _dt, 0);
}
}
</code></pre>
<p><strong>Results for 1,000,000 Entities:</strong></p>
<ul>
<li>Over <strong>98.5% cache hits</strong></li>
<li>Approximately <strong>4x to 6x faster</strong> execution times</li>
<li><strong>0 Bytes</strong> allocated per frame (Zero Garbage Collection)</li>
</ul>
<h2 id="versioning-and-change-tracking">Versioning and Change Tracking</h2>
<p>Chunks maintain atomic version counters. Whenever <code>GetComponentDataRW&lt;T&gt;()</code> is called, the chunk updates its component version to match the current <code>World.Version</code>.</p>
<p>Systems can rapidly bypass entire chunks without iterating them by checking <code>chunkView.HasChanged&lt;T&gt;(LastSystemVersion)</code>. This enables dirty-flag processing at near-zero cost.</p>
<h2 id="unmanaged-constraints">Unmanaged Constraints</h2>
<p>To guarantee this performance, the engine strictly enforces unmanaged types.</p>
<ul>
<li>You cannot put <code>string</code>, <code>class</code>, or managed arrays inside <code>IComponent</code>.</li>
<li>For bulk temporary allocations during system updates, you should use <code>VirtualStack.Scope</code> or <code>AllocationManager</code> alongside <code>UnsafeList&lt;T&gt;</code> to remain entirely off the .NET Garbage Collector heap.</li>
</ul>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,182 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Query Benchmark | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Query Benchmark | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="query-benchmark">Query Benchmark</h1>
<p>This page summarizes the query performance benchmark in <code>src/Test/Ghost.Entities.Test/QueryBenchmark.cs</code>.</p>
<h2 id="goal">Goal</h2>
<p>Compare update throughput between:</p>
<ul>
<li>Classic managed object array iteration (<code>GameObject[]</code>).</li>
<li>ECS chunk iteration through <code>EntityQuery</code>.</li>
</ul>
<p>The benchmark updates one million items in both paths.</p>
<h2 id="benchmark-setup">Benchmark Setup</h2>
<pre><code class="lang-csharp">[GlobalSetup]
public void Setup()
{
_world = World.Create(entityCapacity: 1_000_000);
_gameObjects = new GameObject[1_000_000];
using var scope = AllocationManager.CreateStackScope();
var componentSet = new ComponentSet(scope.AllocationHandle, ComponentTypeID&lt;Position&gt;.Value);
_world.EntityManager.CreateEntities(1_000_000, componentSet);
_queryIdentifier = new QueryBuilder().WithAllRW&lt;Position&gt;().Build(_world);
}
</code></pre>
<p>Two benchmark methods:</p>
<pre><code class="lang-csharp">[Benchmark]
public void QueryGameObjects()
{
for (var i = 0; i &lt; _gameObjects.Length; i++)
{
_gameObjects[i].Position += new Vector4(_dt, _dt, _dt, 0);
}
}
[Benchmark(Baseline = true)]
public void QueryEntities()
{
ref var query = ref _world.ComponentManager.GetEntityQueryReference(_queryIdentifier);
foreach (var chunkView in query.GetChunkIterator())
{
var positions = chunkView.GetComponentDataRW&lt;Position&gt;();
ref var address = ref MemoryMarshal.GetReference(positions);
for (var i = 0; i &lt; positions.Length; i++)
{
Unsafe.Add(ref address, i).value += new Vector4(_dt, _dt, _dt, 0);
}
}
}
</code></pre>
<h2 id="notes-on-measurement">Notes on Measurement</h2>
<ul>
<li>The benchmark enables hardware counters (<code>CacheMisses</code>, <code>LlcReference</code>, <code>InstructionRetired</code>) for deeper analysis.</li>
<li>ECS path uses chunk traversal and contiguous component storage.</li>
<li><code>Unsafe.Add</code> avoids bounds-check overhead inside the inner loop.</li>
</ul>
<h2 id="running-the-benchmark">Running the Benchmark</h2>
<p>From <code>src/</code>:</p>
<pre><code class="lang-shell">dotnet run --project Test/Ghost.Entities.Test/Ghost.Entities.Test.csproj -c Release
</code></pre>
<p>The current <code>Program.cs</code> already executes <code>BenchmarkRunner.Run&lt;QueryBenchmark&gt;()</code>.</p>
<h2 id="interpreting-results">Interpreting Results</h2>
<p>When comparing output, evaluate both runtime and counters:</p>
<ul>
<li>Lower execution time for target workload.</li>
<li>Lower cache miss rates under similar work.</li>
<li>Reasonable instruction count for equivalent behavior.</li>
</ul>
<p>Use this benchmark as a template for your own data-oriented workload comparisons.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,219 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Serialization Pattern | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Serialization Pattern | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="serialization-pattern">Serialization Pattern</h1>
<p>This page documents the manual world serialization pattern demonstrated in <code>src/Test/Ghost.Entities.Test/SerializationTest.cs</code>.</p>
<h2 id="scope">Scope</h2>
<p>The current test shows a low-level JSON snapshot workflow for ECS data:</p>
<ul>
<li>Enumerate archetypes and chunks.</li>
<li>Read entity IDs and raw component bytes.</li>
<li>Convert bytes to managed instances.</li>
<li>Serialize component payloads as JSON.</li>
<li>Parse JSON back into typed objects.</li>
</ul>
<p>This is useful for debugging, tooling, and prototype save/load workflows.</p>
<h2 id="write-path-world---json">Write Path (World -&gt; JSON)</h2>
<p>Core flow from the test:</p>
<pre><code class="lang-csharp">using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });
writer.WriteStartObject();
writer.WriteString(&quot;Name&quot;, &quot;world 1&quot;);
writer.WriteStartArray(&quot;Entities&quot;);
for (var i = 0; i &lt; world.ComponentManager.ArchetypeCount; i++)
{
ref var archetype = ref world.ComponentManager.GetArchetypeReference(i);
for (var j = 0; j &lt; archetype.ChunkCount; j++)
{
ref var chunk = ref archetype.GetChunkReference(j);
for (var k = 0; k &lt; chunk._count; k++)
{
writer.WriteStartObject();
writer.WriteNumber(&quot;ID&quot;, entityID);
writer.WriteStartArray(&quot;Components&quot;);
// For each component in archetype layout:
// - resolve runtime type
// - marshal bytes to managed object
// - JsonSerializer.Serialize(writer, instance, type, options)
writer.WriteEndArray();
writer.WriteEndObject();
}
}
}
writer.WriteEndArray();
writer.WriteEndObject();
writer.Flush();
</code></pre>
<p>Data shape used in the test:</p>
<pre><code class="lang-json">{
&quot;Name&quot;: &quot;world 1&quot;,
&quot;Entities&quot;: [
{
&quot;ID&quot;: 1,
&quot;Components&quot;: [
{
&quot;Type&quot;: &quot;Full.AssemblyQualified.TypeName&quot;,
&quot;Data&quot;: { }
}
]
}
]
}
</code></pre>
<h2 id="read-path-json---typed-data">Read Path (JSON -&gt; Typed Data)</h2>
<p>The test parses JSON and reconstructs typed component instances:</p>
<pre><code class="lang-csharp">var root = JsonDocument.ParseValue(ref reader).RootElement;
var entityData = new List&lt;(int EntityID, Type ComponentType, object Instance)&gt;();
foreach (var entityElement in root.GetProperty(&quot;Entities&quot;).EnumerateArray())
{
var id = entityElement.GetProperty(&quot;ID&quot;).GetInt32();
foreach (var componentElement in entityElement.GetProperty(&quot;Components&quot;).EnumerateArray())
{
var typeName = componentElement.GetProperty(&quot;Type&quot;).GetString();
var type = Type.GetType(typeName!);
if (type == null)
{
continue;
}
var instance = componentElement.GetProperty(&quot;Data&quot;).Deserialize(type, options);
if (instance != null)
{
entityData.Add((id, type, instance));
}
}
}
</code></pre>
<h2 id="caveats">Caveats</h2>
<ul>
<li>The example is intentionally low-level and may use internal layout details.</li>
<li><code>AssemblyQualifiedName</code> based type resolution is convenient but can be brittle across assembly/version changes.</li>
<li>For production save/load, prefer stable type identifiers and versioned schemas.</li>
<li>Reflection + marshaling has overhead; avoid in per-frame hot paths.</li>
</ul>
<h2 id="recommended-use-cases">Recommended Use Cases</h2>
<ul>
<li>Inspecting world state during development.</li>
<li>Exporting compact debugging snapshots.</li>
<li>Prototyping serializer architecture before introducing a dedicated format.</li>
</ul>
<p>For public runtime API details, see generated docs in <code>doc/api/</code>.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,167 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>System Ordering | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="System Ordering | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="system-ordering">System Ordering</h1>
<p>This page explains how to define system execution order using attributes and groups.</p>
<h2 id="overview">Overview</h2>
<p><code>Ghost.Entities</code> systems are managed through <code>SystemManager</code> and grouped by <code>SystemGroup</code>.</p>
<ul>
<li>Add systems to a group.</li>
<li>Declare relative ordering with attributes.</li>
<li>Call <code>SortSystems()</code> before initialization/update loops.</li>
</ul>
<h2 id="basic-example">Basic Example</h2>
<pre><code class="lang-csharp">internal class TestSystemA : SystemBase
{
protected override void OnInitialize(ref readonly SystemAPI systemAPI)
{
Console.WriteLine(&quot;TestSystemA Initialized&quot;);
}
}
[UpdateAfter(typeof(TestSystemA))]
internal class TestSystemB : SystemBase
{
protected override void OnInitialize(ref readonly SystemAPI systemAPI)
{
Console.WriteLine(&quot;TestSystemB Initialized&quot;);
}
}
</code></pre>
<p>Registration and sort:</p>
<pre><code class="lang-csharp">var world = World.Create();
var group = world.SystemManager.GetSystem&lt;DefaultSystemGroup&gt;();
group.AddSystem&lt;TestSystemB&gt;();
group.AddSystem&lt;TestSystemA&gt;();
group.SortSystems();
</code></pre>
<p>Even though <code>TestSystemB</code> is added first, <code>[UpdateAfter(typeof(TestSystemA))]</code> ensures <code>TestSystemA</code> runs earlier after sorting.</p>
<p>Initialization/update execution is driven by the runtime loop (via internal system manager entry points).</p>
<h2 id="when-to-use-updateafter-and-updatebefore">When to Use <code>UpdateAfter</code> and <code>UpdateBefore</code></h2>
<p>Use ordering attributes when one system depends on side effects from another system in the same update phase.</p>
<ul>
<li><code>UpdateAfter(typeof(X))</code>: this system must run after <code>X</code>.</li>
<li><code>UpdateBefore(typeof(X))</code>: this system must run before <code>X</code>.</li>
</ul>
<p>Prefer expressing only required dependencies. Over-constraining can reduce scheduler flexibility.</p>
<h2 id="practical-guidance">Practical Guidance</h2>
<ul>
<li>Keep systems small and focused on one responsibility.</li>
<li>Place related systems in the same <code>SystemGroup</code> for predictable ordering.</li>
<li>Sort once after registration changes (startup/bootstrap phase).</li>
<li>Use clear naming to make ordering intent obvious.</li>
</ul>
<p>For API details, see generated docs for <code>SystemBase</code>, <code>SystemGroup</code>, <code>SystemManager</code>, <code>UpdateAfterAttribute</code>, and <code>UpdateBeforeAttribute</code> in <code>doc/api/</code>.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

177
doc/_site/docs/systems.html Normal file
View File

@@ -0,0 +1,177 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Systems Overview | GhostEngine </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="title" content="Systems Overview | GhostEngine ">
<link rel="icon" href="../favicon.ico">
<link rel="stylesheet" href="../public/docfx.min.css">
<link rel="stylesheet" href="../public/main.css">
<meta name="docfx:navrel" content="../toc.html">
<meta name="docfx:tocrel" content="toc.html">
<meta name="docfx:rel" content="../">
<meta name="loc:inThisArticle" content="In this article">
<meta name="loc:searchResultsCount" content="{count} results for &quot;{query}&quot;">
<meta name="loc:searchNoResults" content="No results for &quot;{query}&quot;">
<meta name="loc:tocFilter" content="Filter by title">
<meta name="loc:nextArticle" content="Next">
<meta name="loc:prevArticle" content="Previous">
<meta name="loc:themeLight" content="Light">
<meta name="loc:themeDark" content="Dark">
<meta name="loc:themeAuto" content="Auto">
<meta name="loc:changeTheme" content="Change theme">
<meta name="loc:copy" content="Copy">
<meta name="loc:downloadPdf" content="Download PDF">
<script type="module" src="./../public/docfx.min.js"></script>
<script>
const theme = localStorage.getItem('theme') || 'auto'
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') : theme)
</script>
</head>
<body class="tex2jax_ignore" data-layout="" data-yaml-mime="">
<header class="bg-body border-bottom">
<nav id="autocollapse" class="navbar navbar-expand-md" role="navigation">
<div class="container-xxl flex-nowrap">
<a class="navbar-brand" href="../index.html">
<img id="logo" class="svg" src="../logo.svg" alt="GhostEngine">
GhostEngine
</a>
<button class="btn btn-lg d-md-none border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navpanel" aria-controls="navpanel" aria-expanded="false" aria-label="Toggle navigation">
<i class="bi bi-three-dots"></i>
</button>
<div class="collapse navbar-collapse" id="navpanel">
<div id="navbar">
<form class="search" role="search" id="search">
<i class="bi bi-search"></i>
<input class="form-control" id="search-query" type="search" disabled placeholder="Search" autocomplete="off" aria-label="Search">
</form>
</div>
</div>
</div>
</nav>
</header>
<main class="container-xxl">
<div class="toc-offcanvas">
<div class="offcanvas-md offcanvas-start" tabindex="-1" id="tocOffcanvas" aria-labelledby="tocOffcanvasLabel">
<div class="offcanvas-header">
<h5 class="offcanvas-title" id="tocOffcanvasLabel">Table of Contents</h5>
<button type="button" class="btn-close" data-bs-dismiss="offcanvas" data-bs-target="#tocOffcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body">
<nav class="toc" id="toc"></nav>
</div>
</div>
</div>
<div class="content">
<div class="actionbar">
<button class="btn btn-lg border-0 d-md-none" type="button" data-bs-toggle="offcanvas" data-bs-target="#tocOffcanvas" aria-controls="tocOffcanvas" aria-expanded="false" aria-label="Show table of contents">
<i class="bi bi-list"></i>
</button>
<nav id="breadcrumb"></nav>
</div>
<article data-uid="">
<h1 id="systems-overview">Systems Overview</h1>
<p>Systems are where all your ECS logic lives. They interact with data by iterating over entities that match an <code>EntityQuery</code>.</p>
<h2 id="the-isystem-interface">The <code>ISystem</code> Interface</h2>
<p>At its core, a system implements <code>ISystem</code>, which provides three lifecycle methods:</p>
<pre><code class="lang-csharp">public interface ISystem
{
void Initialize(ref readonly SystemAPI systemAPI);
void Update(ref readonly SystemAPI systemAPI);
void Cleanup(ref readonly SystemAPI systemAPI);
}
</code></pre>
<p>The <code>SystemAPI</code> struct provides access to the <code>World</code> and <code>TimeData</code> for the current frame.</p>
<h2 id="systembase"><code>SystemBase</code></h2>
<p>For most gameplay code, you should inherit from the abstract class <code>SystemBase</code>, which provides convenient wrappers and handles &quot;Should Update&quot; logic automatically.</p>
<pre><code class="lang-csharp">public class MovementSystem : SystemBase
{
private Identifier&lt;EntityQuery&gt; _query;
protected override void OnInitialize(ref readonly SystemAPI systemAPI)
{
// Build a query for entities that have both Position and Velocity
_query = new QueryBuilder()
.WithAllRW&lt;Position&gt;()
.WithAll&lt;Velocity&gt;()
.Build(World);
// Tell the system base to ONLY run OnUpdate if this query has at least 1 entity
RequireQueryForUpdate(_query);
}
protected override void OnUpdate(ref readonly SystemAPI systemAPI)
{
float dt = systemAPI.Time.DeltaTime;
ref var query = ref World.ComponentManager.GetEntityQueryReference(_query);
foreach (var chunk in query.GetChunkIterator())
{
var positions = chunk.GetComponentDataRW&lt;Position&gt;();
var velocities = chunk.GetComponentData&lt;Velocity&gt;();
for (int i = 0; i &lt; chunk.EntityCount; i++)
{
positions[i].Value += velocities[i].Value * dt;
}
}
}
}
</code></pre>
<h3 id="automatic-update-triggers">Automatic Update Triggers</h3>
<p>Notice the <code>RequireQueryForUpdate(_query)</code> call. <code>SystemBase</code> will skip calling <code>OnUpdate</code> if the query results are completely empty, saving processing time. <code>SystemBase</code> also invokes <code>OnStartRunning</code> and <code>OnStopRunning</code> when the system transitions between having matching entities and not.</p>
<h2 id="system-execution-order">System Execution Order</h2>
<p>By default, systems execute in the order they are added to a <code>SystemGroup</code>. To enforce execution order regardless of initialization sequence, use the <code>[UpdateAfter]</code> and <code>[UpdateBefore]</code> attributes.</p>
<pre><code class="lang-csharp">[UpdateAfter(typeof(PhysicsSystem))]
[UpdateBefore(typeof(RenderSystem))]
public class MovementSystem : SystemBase
{
// ...
}
</code></pre>
<p>When you call <code>group.SortSystems()</code>, the <code>SystemGroup</code> uses Kahn's algorithm to perform a topological sort and resolves these dependencies. If circular dependencies are detected, the engine will throw an exception.</p>
<h2 id="system-groups">System Groups</h2>
<p><code>SystemGroup</code>s implement <code>ISystem</code> themselves, meaning you can nest groups within groups to build complex hierarchical update loops (e.g. <code>FixedUpdateGroup</code>, <code>PresentationGroup</code>). GhostEngine provides a <code>DefaultSystemGroup</code> that is automatically instantiated by the <code>SystemManager</code>.</p>
</article>
<div class="contribution d-print-none">
</div>
<div class="next-article d-print-none border-top" id="nextArticle"></div>
</div>
<div class="affix">
<nav id="affix"></nav>
</div>
</main>
<div class="container-xxl search-results" id="search-results"></div>
<footer class="border-top text-secondary">
<div class="container-xxl">
<div class="flex-fill">
<span>Made with <a href="https://dotnet.github.io/docfx">docfx</a></span>
</div>
</div>
</footer>
</body>
</html>

37
doc/_site/docs/toc.html Normal file
View File

@@ -0,0 +1,37 @@
<div id="sidetoggle">
<div>
<div class="sidefilter">
<form class="toc-filter">
<span class="glyphicon glyphicon-filter filter-icon"></span>
<span class="glyphicon glyphicon-remove clear-icon" id="toc_filter_clear"></span>
<input type="text" id="toc_filter_input" placeholder="Filter by title" onkeypress="if(event.keyCode==13) {return false;}">
</form>
</div>
<div class="sidetoc">
<div class="toc" id="toc">
<ul class="nav level1">
<li>
<a href="getting-started.html" name="" title="Getting Started">Getting Started</a>
</li>
<li>
<a href="ecs-concepts.html" name="" title="ECS Concepts">ECS Concepts</a>
</li>
<li>
<a href="components.html" name="" title="Components">Components</a>
</li>
<li>
<a href="systems.html" name="" title="Systems">Systems</a>
</li>
<li>
<a href="ecs-workflows.html" name="" title="ECS Workflows">ECS Workflows</a>
</li>
<li>
<a href="performance.html" name="" title="Performance">Performance</a>
</li>
</ul>
</div>
</div>
</div>
</div>

2
doc/_site/docs/toc.json Normal file
View File

@@ -0,0 +1,2 @@
{"items":[{"name":"Getting Started","href":"getting-started.html","topicHref":"getting-started.html"},{"name":"ECS Concepts","href":"ecs-concepts.html","topicHref":"ecs-concepts.html"},{"name":"Components","href":"components.html","topicHref":"components.html"},{"name":"Systems","href":"systems.html","topicHref":"systems.html"},{"name":"ECS Workflows","href":"ecs-workflows.html","topicHref":"ecs-workflows.html"},{"name":"Performance","href":"performance.html","topicHref":"performance.html"}],"pdf":false}