Skip to content

Releases: StarworksDev/Astra

Astra 3.1.0

Choose a tag to compare

@T3mps T3mps released this 11 Jun 09:18

Astra ECS v3.1 - Hardening: Scheduler Seam, TypeContext, Container Fixes & CI

Release Highlights

Astra 3.1 is a hardening release focused on correctness, portability, and safe integration patterns. It ships a clean threading seam (no surprise thread spawning), process-shared type identity for multi-module and DLL scenarios, two container correctness fixes found by new fuzz suites, and a GitHub Actions CI matrix covering MSVC, gcc, and clang (distro-default on ubuntu-latest; verified locally with gcc 15.1 / clang 18). The public API is backward-compatible; two deprecated footguns (TypeID_v and MakeComponentMask constexpr) are removed.


Parallel Execution & The Scheduler Seam

The old View::ParallelForEach, Relations::ParallelForEachDescendant, and ParallelExecutor implementations spawned std::async tasks ad-hoc per call. Benchmarking at 1 M entities with a trivial store-only kernel showed this was actively harmful: the ad-hoc async path took 2.43 ms against 1.53 ms for the sequential path -- a 1.59x loss due to thread-pool contention and synchronisation overhead on a memory-bound kernel.

IWorkScheduler seam

A thin scheduler interface lives at Core/WorkScheduler.hpp:

class IWorkScheduler {
public:
    virtual ~IWorkScheduler() = default;
    virtual void ParallelFor(size_t count, size_t minBatch,
                             const std::function<void(size_t, size_t)>& fn) = 0;
    virtual size_t WorkerCount() const noexcept = 0;
};

Inject via Registry::Config::workScheduler (std::shared_ptr<IWorkScheduler>). When nullptr (the new default), all parallel APIs run sequentially inline -- safe in single-threaded contexts and faster on memory-bound workloads.

// Sequential (default -- fastest for memory-bound kernels)
Astra::Registry registry;

// Parallel with your own pool
auto scheduler = std::make_shared<MyEnkiTSAdapter>();
Astra::Registry::Config config;
config.workScheduler = scheduler;
Astra::Registry registry(config);

The reference fork-join pool is available in tests/Support/TestWorkerPool.hpp for test and benchmark use. With the injected test pool on a compute-heavy kernel at 1 M entities: 2.17 ms (injected pool) vs 2.43 ms (old ad-hoc async) -- an 11% improvement once thread overhead is no longer paid per-call. Sequential remains the right default; parallelism pays when the kernel is compute-bound rather than memory-bound. For the store-only (memory-bound) kernel, sequential at 1.53 ms still wins; the default is now sequential rather than the silently-losing parallel path.


Multi-Module & DLL Support

Static typeindex-based IDs break across DLL boundaries because each module gets its own static. TypeContext (Core/TypeContext.hpp) replaces the old scheme with process-shared type identity.

How it works

  • Stable XXHash64 type-name hashes are the cross-module identity key.
  • Dense sequential ComponentIDs are assigned from the shared context and are mask-compatible with the existing archetype bitmask system.
  • A pending static-registration queue is drained when SetTypeContext is called from each module, so components registered before the context is installed are not lost.
  • MetaRegistry now lives inside the context; the singleton API (MetaRegistry::Instance()) is preserved.

DLL hot-reload sequence

// 1. Serialize living entities (snapshot or raw binary)
// 2. Unload old DLL; load new DLL
// 3. Install shared context in the new module
SetTypeContext(sharedCtx);
// 4. Refresh descriptors for recompiled components
componentRegistry->ReRegisterComponent<MyComponent>();
// 5. Deserialize -- IDs are stable across the reload

TypeID_v (a static-init template variable that was a DLL footgun) is removed; use Astra::TypeID<T>::Value() instead (IDs resolve through the active TypeContext).


Container Fixes & Fuzzing

New differential fuzz suites (21 fuzz tests covering FlatMap/FlatSet churn with adversarial hashing, SmallVector spill-boundary, and Bitmap bulk ops) found two correctness bugs in the internal containers.

FlatMap / FlatSet duplicate-insertion bug

Emplace inserted a key at the first empty-or-deleted (tombstone) slot encountered during the probe chain before completing the duplicate scan. Because tombstones do not terminate the probe sequence, a key already present later in the chain could be inserted a second time, causing a silent size overcount and returning iterators to two distinct slots for the same logical key.

Fix: defer slot selection until an EMPTY slot terminates the scan. If a matching key is found during the scan, return the existing slot (no insert). Only after confirming no duplicate exists is the deferred slot used for insertion.

SwissTable H2 metadata-range hazard

SwissTable::H2 extracted the top 7 bits of the hash via & 0x7F, which already excluded the 0x80 sentinel. However, a degenerate hasher whose top 7 bits are all zero produces H2 == 0, which falls outside the range [1, 127] that Group::Match assumes for probe bytes, breaking the SIMD match logic.

Fix: H2 now folds 0 to 1 centrally (return h2 == 0 ? 1 : h2) so any hasher is guaranteed to produce a value in [1, 127].


Cross-Compiler & CI

Approximately 25 portability fixes were required to build cleanly under current gcc and clang (verified locally with gcc 15.1 / clang 18):

  • Dependent-name disambiguators (typename / template) in templated base-class member access.
  • Removed in-class partial specialisations (not permitted by the standard; MSVC accepted them as an extension).
  • Fixed member-initialiser order to match declaration order (gcc -Wreorder).
  • Corrected attribute placement ([[nodiscard]] before inline, not after).
  • Warning hygiene: signed/unsigned comparisons, unused parameters, shadow declarations.

A GitHub Actions CI matrix (.github/workflows/ci.yml) runs on every push to every branch and on every pull request:

Job Compiler Standard
windows-msvc MSVC on windows-latest C++20
linux-gcc current distro gcc on ubuntu-latest (unpinned) C++20
linux-clang current distro clang on ubuntu-latest (unpinned) C++20

Compilers are unpinned (distro-default on ubuntu-latest and MSVC on windows-latest). Verified locally with gcc 15.1 / clang 18. All three jobs build in Release, run the full 496-test suite, and must be green before merge.


Documentation

  • Removed the nonexistent threadSafe config key from the README (it was never implemented).
  • Corrected component requirements section (copyable/movable constraints clarified).
  • Fixed CreateEntityWith examples to match the actual API signature.
  • Added Threading Model section documenting the scheduler seam and default-sequential behaviour.
  • Added Multi-Module (DLL) section documenting TypeContext, SetTypeContext, and the hot-reload sequence.

Removals

Removed Replacement Reason
TypeID_v<T> (static template variable) Astra::TypeID<T>::Value() Static-init order fiasco across DLL boundaries
MakeComponentMask constexpr overload Runtime ComponentMask construction Bitmap is not a literal type under clang; constexpr was misleading

Requirements

  • C++20 compatible compiler (MSVC 2022+, GCC 11+, Clang 13+)
  • Premake5 for project generation
  • Optional: GoogleTest, Google Benchmark

License

MIT License - See LICENSE file for details


Full Changelog: v3.0.0...v3.1.0

Astra 3.0.0

Choose a tag to compare

@T3mps T3mps released this 27 Feb 23:17

Astra ECS v3.0 - Resources, Runtime Reflection & Editor Integration

Major Release Highlights

Astra 3.0 introduces a singleton resource system for global state and a comprehensive runtime reflection system providing EnTT meta-style introspection, field enumeration, attribute decoration, and full ECS integration. This release enables editor tooling, runtime component inspection, and JSON schema generation.

New Capabilities

  • Resource system for singleton/global components with SBO optimization
  • Runtime type introspection with zero overhead when unused
  • Field enumeration with get/set accessors and attribute metadata
  • Entity inspection API for editor and debugging tools

New Features

Resource System (NEW)

Singleton resources for global state that exists outside the entity-component model:

// Add global resources
registry.AddResource<Time>(Time{ 0.0f, 0.016f });
registry.AddResource<InputState>();
registry.AddResource<RenderSettings>(width, height, vsync);

// Access resources
Time* time = registry.GetResource<Time>();
time->deltaTime = 0.016f;

// Check existence
if (registry.HasResource<InputState>()) { ... }

// Remove resources
registry.RemoveResource<RenderSettings>();

Storage Architecture

  • Sparse-dense pattern for O(1) access by ComponentID
  • Small Buffer Optimization - Resources ≤64 bytes stored inline (no heap allocation)
  • Swap-and-pop removal maintains cache locality
  • Automatic registration with ComponentRegistry

Type-Erased Operations

For command buffers and deferred execution:

// Set resource by ID (for CommandBuffer)
resourceStorage.SetByID(componentId, dataPtr, dataSize);

// Remove by ID
resourceStorage.RemoveByID(componentId);

// Get by ID
void* data = resourceStorage.GetByID(componentId);

Memory Efficiency

struct ResourceSlot {
    union Storage {
        alignas(64) std::byte inlineData[64];  // SBO: no allocation
        void* heapPtr;                          // Large resources
    };
    // ... metadata
};

// Memory usage tracking
size_t bytes = resourceStorage.GetMemoryUsage();

Runtime Reflection System (NEW)

TypeMeta - Complete Type Metadata

const TypeMeta* meta = GetMeta<Transform>();

// Introspect fields
meta->ForEachField([](const FieldInfo& field) {
    std::cout << field.name << " : " << field.typeName << "\n";
});

// Dynamic field access
meta->SetFieldValue(&transform, "x", 100.0f);
float x = meta->GetFieldValue<float>(&transform, "x").value();

FieldInfo - Field Descriptors with Accessors

  • Type-erased Get<T>() and Set<T>() methods
  • Offset, size, alignment metadata
  • Compile-time type trait detection (isConst, isPointer, isTrivial)
  • Attribute query support

Attribute System

Decorator metadata for fields and types:

ASTRA_REFLECT_TYPE(Transform)
    ASTRA_REFLECT_FIELD(Transform, x)
        ASTRA_REFLECT_ATTR(Range, -1000.0, 1000.0)
        ASTRA_REFLECT_ATTR(DisplayName, "X Position")
        ASTRA_REFLECT_ATTR(Tooltip, "Horizontal position in world space")
    ASTRA_REFLECT_FIELD(Transform, y)
    ASTRA_REFLECT_FIELD(Transform, z);

Built-in attributes:

  • Range - Min/max/step constraints
  • DisplayName - UI label override
  • Tooltip - Help text
  • Category - Property grouping
  • Hidden - Exclude from inspection
  • ReadOnly - Prevent modification
  • Serializable - Control persistence

Enum Reflection

ASTRA_REFLECT_ENUM(DamageType)
    ASTRA_REFLECT_ENUM_VALUE(DamageType, Physical)
    ASTRA_REFLECT_ENUM_VALUE_NAMED(DamageType, Fire, "Fire Damage")
    ASTRA_REFLECT_ENUM_FLAGS();  // Mark as bitmask

// Runtime conversion
auto name = enumInfo->ToString(DamageType::Fire);  // "Fire Damage"
auto value = enumInfo->FromString("Physical");      // DamageType::Physical

Container Traits Detection

Automatic detection of container types:

  • std::vector, std::array, std::span
  • std::map, std::unordered_map
  • std::set, std::unordered_set
  • Custom containers via trait specialization

ECS-Reflection Integration (NEW)

ComponentDescriptor ↔ TypeMeta Linking

Components registered with the ECS are automatically linked to their reflection metadata:

// ComponentDescriptor now includes:
const TypeMeta* meta;  // Auto-populated on registration

// Bidirectional lookup
const TypeMeta* meta = GetMeta(componentId);
ComponentID id = MetaRegistry::Instance().GetComponentId(typeHash);

Entity Inspection API

Runtime inspection of entity components for editors:

std::vector<ComponentInfo> components = registry.InspectEntity(entity);

for (const auto& info : components) {
    std::cout << "Component: " << info.descriptor->name << "\n";

    if (info.meta) {
        info.meta->ForEachField([&](const FieldInfo& field) {
            // Render field in editor UI
            RenderFieldWidget(field, info.data);
        });
    }
}

Resource Inspection API

std::vector<ResourceInfo> resources = registry.InspectResources();

for (const auto& res : resources) {
    std::cout << "Resource: " << res.descriptor->name << "\n";
    // Access via res.meta and res.data
}

Hash-Based Runtime Access

// Get components by type hash (runtime lookup)
void* component = registry.GetComponentByHash(entity, typeHash);
bool has = registry.HasComponentByHash(entity, typeHash);

// Get components by name
void* component = registry.GetComponentByName(entity, "Transform");

// Resources
void* resource = registry.GetResourceByHash(typeHash);
bool has = registry.HasResourceByHash(typeHash);

JSON Schema Generation (NEW)

Automatic JSON Schema generation for editor integration:

JsonSchemaGenerator generator;
std::string schema = generator.Generate(*GetMeta<Transform>());

Generates:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Transform",
  "type": "object",
  "properties": {
    "x": { "type": "number", "minimum": -1000, "maximum": 1000 },
    "y": { "type": "number" },
    "z": { "type": "number" }
  },
  "required": ["x", "y", "z"]
}

Architectural Improvements

MetaRegistry

Global singleton for reflection metadata:

  • O(1) lookup by type hash via FlatMap
  • Bidirectional component ID ↔ type hash mapping
  • Thread-safe registration (single-threaded lookup)
  • Lazy initialization via static builders

Reflection File Structure

include/Astra/Reflection/
  Reflection.hpp       # Main include
  TypeMeta.hpp         # Type metadata container
  FieldInfo.hpp        # Field descriptor with get/set
  EnumInfo.hpp         # Enum reflection
  Attribute.hpp        # Attribute base + common attributes
  MetaRegistry.hpp     # Global registry
  ContainerTraits.hpp  # Container type detection
  JsonSchema.hpp       # JSON schema generation
  Macros.hpp           # Registration macros

Requirements

  • C++20 compatible compiler (GCC 11+, Clang 13+, MSVC 2022+)
  • Premake5 for project generation
  • Optional: GoogleTest, Google Benchmark

License

MIT License - See LICENSE file for details


Full Changelog: v2.0.0...v3.0.0

Astra 2.0.0

Choose a tag to compare

@T3mps T3mps released this 24 Aug 09:50
1bc8ffc

Astra ECS v2.0 - System Scheduling, Parallel Execution & Advanced Architecture

🎉 Major Release Highlights

Astra 2.0 is a massive update introducing system scheduling, parallel execution, command buffers, batch operations, and a complete architectural overhaul for production-ready performance.

New Parallel Performance

  • 17x speedup on 20 cores for single component iteration
  • ParallelForEach with automatic work distribution across cores
  • Thread-safe chunk-based parallelization

🚀 Major New Features

System Scheduling Architecture (NEW)

  • SystemScheduler class for organizing and executing ECS systems
  • Automatic parallelization based on component access patterns
  • Lambda system support with automatic Read/Write detection via const qualifiers
  • System traits for declaring component dependencies (Reads/Writes)
  • Custom executor interface for job system integration
  • Dependency analysis for safe parallel execution

Command Buffer Architecture (NEW)

  • Deferred operations for safe modifications during iteration
  • Thread-safe command recording from multiple sources
  • Batch command execution with minimal overhead
  • Entity remapping for cloning and prefab instantiation
  • Undo/redo support foundation

Parallel Execution in Views (NEW)

  • ParallelForEach method for automatic parallelization
  • Chunk-based work distribution for load balancing
  • Thread-safe iteration with per-chunk processing
  • Configurable executor support

Const Components in Views (NEW)

  • Type-safe const components - View<Position, const Velocity>
  • Compile-time enforcement preventing accidental modifications

Batch Component Operations (NEW)

  • AddComponentBatch for efficient bulk additions
  • RemoveComponentBatch for bulk removals

FlatSet Container (NEW)

  • Open-addressing hash set with cache-friendly layout
  • SIMD-accelerated lookups using metadata bytes
  • Swiss table algorithm for efficient probing

📦 Architectural Restructuring

Entity Management Overhaul

  • EntityManager with for entity orchestration/lifecycle
  • EntityTable for version lookups/state management
  • EntityIDStack managing free/recycled IDs

Archetype System Redesign

  • ArchetypeManager as central archetype controller
  • ArchetypeGraph for efficient archetype transitions
  • ArchetypeChunkPool for optimized chunk allocation/reuse

🛠️ Requirements

  • C++20 compatible compiler (GCC 11+, Clang 13+, MSVC 2022+)
  • Premake5 for project generation
  • Optional: GoogleTest, Google Benchmark

📝 License

MIT License - See LICENSE file for details


Full Changelog: v1.0.0...v2.0.0

Astra 1.0.0

Choose a tag to compare

@T3mps T3mps released this 17 Aug 02:08
6520ddf

Astra ECS v1.0 - High-Performance Entity Component System

🚀 Overview

Astra is a header-only Entity Component System (ECS) library for modern C++20, delivering high performance through archetype-based storage and aggressive optimization techniques.

⚡ Performance Highlights

  • ~1.04B entities/second single-component iteration (hot cache)
  • ~465M entities/second dual-component iteration
  • Sub-nanosecond per-entity overhead in tight loops
  • 2x faster batch entity creation vs individual creation

🎯 Key Features

Core Architecture

  • Archetype-based storage with automatic entity grouping by component composition
  • Structure-of-Arrays (SoA) layout for optimal cache utilization
  • 16KB chunk-based allocation aligned to modern CPU cache hierarchies
  • O(1) component access via direct ComponentID indexing

Advanced Capabilities

  • Relationship graphs - Hierarchical parent-child and bidirectional entity links
  • Query system with compile-time iteration optimizations
    • Optional components (Optional<T>)
    • Exclusion filters (Not<T>)
    • Any-of queries (Any<T...>)
    • One-of constraints (OneOf<T...>)
  • Memory defragmentation - Configurable chunk coalescing and archetype cleanup
  • Binary serialization with compression support (LZ4/ZSTD)
  • Cross-platform SIMD abstractions (SSE2/SSE4.2/AVX2/NEON)

🛠️ Requirements

  • C++20 compatible compiler (GCC 11+, Clang 13+, MSVC 2022+)
  • Premake5
  • Optional: GoogleTest for tests, Google Benchmark for benchmarks

📝 License

MIT License - See LICENSE file for details


Full Changelog: https://github.com/T3mps/Astra/commits/1.0.0