Files
GhostEngine/AGENTS.md
Misaki d71bdb3fc9 Refactor shader system: arrays, keywords, property syntax
Major refactor of shader compiler and related systems:
- Switch ShaderDescriptor/PassDescriptor to arrays; remove IPassDescriptor
- Rewrite keywords block parser/semantic analysis for flexible syntax
- Change property initializers to brace syntax `{ ... }`
- Simplify TokenStream API (remove ref index params)
- Make GetBindlessIndex return uint (~0u for not found)
- Update shader compilation and variant logic for new descriptors
- Update test shader syntax to match new property/keyword formats
- Add AGENTS.md agent development guide
- Add Antlr4 dependency to Ghost.DSL
- Miscellaneous code style and error handling improvements
2026-01-10 18:36:18 +09:00

8.0 KiB

GhostEngine - Agent Development Guide

This guide provides essential information for AI coding agents working on the GhostEngine codebase.

Project Overview

  • Type: Game Engine
  • Language: C#
  • Target Framework: .NET 10.0
  • Special Features: ECS architecture, D3D12 rendering, AOT compilation, WinUI 3 editor
  • Platform: Windows (net10.0-windows10.0.22621.0 for editor projects)

Build Commands

Build Entire Solution

dotnet build GhostEngine.slnx

Build Specific Project

dotnet build Ghost.Entities/Ghost.Entities.csproj
dotnet build Ghost.Editor/Ghost.Editor.csproj

Build with Configuration

dotnet build GhostEngine.slnx -c Release
dotnet build GhostEngine.slnx -c Debug

Clean Build

dotnet clean GhostEngine.slnx
dotnet build GhostEngine.slnx

Test Commands

Run All Tests (Custom Framework)

Tests use a custom test framework (not xUnit/NUnit/MSTest). Each test project is an executable.

# Run entity tests
dotnet run --project Ghost.Entities.Test/Ghost.Entities.Test.csproj

# Run shader tests
dotnet run --project Ghost.Shader.Test/Ghost.Shader.Test.csproj

Run Single Test

Tests implement ITest interface. To run a specific test, modify the test project's Program.cs:

// In Ghost.Entities.Test/Program.cs
TestRunner.Run<EntityQueryTest>();  // Run specific test
TestRunner.Run<EntityQueryTest>(10); // Run with 10 iterations

Visual Tests (Graphics)

Graphics tests use WinUI 3 and require running as packaged apps:

dotnet run --project Ghost.Graphics.Test/Ghost.Graphics.Test.csproj

Code Style Guidelines

Formatting (from .editorconfig)

  • Braces: Allman style - all opening braces on new lines
  • Line Length: Max 400 characters (very permissive)
  • Single-line statements: Preserved (allowed)
  • Single-line blocks: Preserved (allowed)
// Correct brace style
public void Method()
{
    if (condition)
    {
        DoSomething();
    }
}

Imports

  • System directives: NOT sorted first (dotnet_sort_system_directives_first = false)
  • No grouping: Import directives not separated by blank lines
  • Order: Organize by project convention, not alphabetically
using Ghost.Core;
using Ghost.Entities;
using Misaki.HighPerformance.Collections;
using System.Diagnostics;
using TerraFX.Interop.DirectX;

Types and Nullability

  • Nullable: Enabled for all projects
  • Implicit usings: Enabled
  • Unsafe code: Allowed in most projects (AllowUnsafeBlocks = True)
  • Primary constructors: NOT preferred (csharp_style_prefer_primary_constructors = false)

Naming Conventions

  • Classes/Interfaces: PascalCase (EntityManager, ICommandBuffer)
  • Methods: PascalCase (CreateEntity, GetComponent)
  • Properties: PascalCase (IsSuccess, Value)
  • Fields (private): Camel case with underscore prefix (_entityLocations, _world)
  • Fields (public/internal): Camel case, no prefix for struct fields (archetypeID, chunkIndex)
  • Type parameters: Single letter or PascalCase (T, TComponent)
  • Constants: PascalCase (no SCREAMING_SNAKE_CASE)
public class EntityManager
{
    private readonly World _world;              // Private field
    private UnsafeSlotMap<EntityLocation> _entityLocations;
    
    public World World => _world;               // Property
    
    public Entity CreateEntity() { }            // Method
}

internal struct EntityLocation                  // Struct
{
    public int archetypeID;                     // Public struct field
    public int chunkIndex;
}

Error Handling

Use Result Types - Railway-oriented programming pattern:

// Custom result types defined in Ghost.Core
public ErrorStatus DoOperation()
{
    return ErrorStatus.None; // or ErrorStatus.NotFound, etc.
}

public Result<T> GetValue()
{
    if (success)
        return Result<T>.Success(value);
    else
        return Result<T>.Failure("Error message");
}

public Result<T, ErrorStatus> GetValueWithStatus()
{
    if (success)
        return value;  // Implicit conversion
    else
        return ErrorStatus.NotFound;  // Implicit conversion
}

// Extension methods for checking results
result.ThrowIfFailed();
var value = result.GetValueOrThrow();
var value = result.GetValueOrDefault(defaultValue);
if (result.TryGetValue(out var value)) { }

Error Status Values: None, NotFound, InvalidArgument, InvalidState, InternalError, PermissionDenied, NotSupported, OutOfMemory, Timeout, Cancelled, UnknownError

Memory and Performance

  • Use unsafe code when needed for performance-critical paths
  • Span and stackalloc: Prefer for temporary allocations
  • ref returns: Use for zero-copy access to internal data
  • Allocator patterns: Use Allocator.Persistent for long-lived allocations
  • AllocationManager: Create stack scopes for temporary allocations
// Stack allocation pattern
var entities = (Span<Entity>)stackalloc Entity[1];

// Using allocation scope
using var scope = AllocationManager.CreateStackScope();
var batchDestroy = new UnsafeList<EntityLocation>(entities.Length, scope.AllocationHandle);

// Ref returns for zero-copy access
public ref T GetSingleton<T>() where T : unmanaged, IComponent
{
    var ptr = GetSingleton(ComponentTypeID<T>.Value);
    return ref *(T*)ptr;
}

Type Safety Patterns

Strongly-typed identifiers:

Identifier<IComponent> componentID;
Identifier<Archetype> archetypeID;
Handle<T> resourceHandle;

Generic constraints:

public void Method<T>() where T : unmanaged, IComponent
public void Method<T, E>() where E : struct, Enum

Documentation

  • XML comments: Required for public APIs
  • Summary tags: Describe what, not how
  • Remarks: Add for complex behavior, thread-safety warnings, structural changes
/// <summary>
/// Create an entity with specified components.
/// </summary>
/// <param name="set">A set of component space IDs to add to the entities.</param>
/// <returns>The created entity.</returns>
/// <remarks>
/// This method causes structural changes and is not thread-safe.
/// Use <see cref="EntityCommandBuffer"/> to defer changes.
/// </remarks>
public Entity CreateEntity(ComponentSet set) { }

Common Patterns

ECS Component Registration:

// Type-safe component ID
ComponentTypeID<Transform>.Value

// Component sets for archetypes
var set = new ComponentSet(ComponentTypeID<Transform>.Value, ComponentTypeID<Velocity>.Value);

Disposal Pattern:

private bool _disposed;

~MyClass()
{
    Dispose();
}

public void Dispose()
{
    if (_disposed) return;
    
    // Cleanup code
    _disposed = true;
    GC.SuppressFinalize(this);
}

Debug-only validation:

#if DEBUG || GHOST_EDITOR
    if (!_isSuccess)
    {
        throw new InvalidOperationException($"Error: {_message}");
    }
#endif

Architecture Notes

Entity Component System (ECS)

  • Archetype-based storage (similar to Unity DOTS)
  • Component data stored in chunks
  • Queries use bitset signatures for fast matching
  • Structural changes move entities between archetypes

Graphics (D3D12)

  • Hardware abstraction via ICommandBuffer
  • Resource lifetime managed via handles
  • Pipeline state objects (PSO) cached in library
  • Native interop via TerraFX.Interop

Custom Dependencies

  • Misaki.HighPerformance.*: High-performance collections and utilities
  • TerraFX.Interop.*: Native Windows/DirectX interop
  • Custom source generators in Ghost.Generator

Important Rules

  1. Never disable nullable warnings - fix the root cause
  2. Use Result types instead of throwing exceptions for expected failures
  3. Document thread-safety in XML comments for public APIs
  4. AllowUnsafeBlocks is enabled - use unsafe code when it improves performance
  5. Avoid collection expressions/initializers (disabled in .editorconfig)
  6. Prefer explicit over implicit - clarity over brevity
  7. Test changes by running the appropriate test project executable