Skip to content

Stream Views ​

The magic goggles that turn your Queries into super awesome rainbow roads!

a fennec painting wall in rainbow with a single smooth brush stroke

Stream<C1> ​

Stream<C1,C2> ​

Stream<C1,C2,C3> ​

Stream<C1,C2,C3,C4> ​

Stream<C1,C2,C3,C4,C5> ​

Entities in Queries (including Worlds) and their Components can be read and modified using the extremely light-weight Stream Views. Dubbed Stream<> in fennecs code and loosely related to zip iterators, their inner workings are tailored specifically to high-performance Entity-Component System use cases.

Neofox: rainbow STREAM VIEWS WILL LET YOU ...

  1. ... call a set of Runners, passing Delegate Actions where you read and write the components in your own custom code. Your action will be run in heavily optimized, super-tight, unrolled loops; most which can even be run in parallel.

  2. ... enumerate each Entity in its underlying Query, together with each Component matching the View's Stream Types, as ValueTuple(Entity, C1, C2, ...). This is awesome for Unit tests and easy inspection of your data, and prototyping behaviours using LINQ.

  3. ... give you safe (and unsafe) access to all the Components of all the Entities matched by the Query - as contiguous blocks of Memory<C>, for you do anything with that you can imagine.

How to obtain a Stream View ​

You can get a Stream View from a Query, or from a World.

var query = stream.Query<Position, Velocity>().Not<Boring>().Stream();
// This is the tried-and-true way of getting a Stream View from a Query,
// similar to how it was in fennecs 0.4.x but modernized by splitting
// the resolution of the Query from the matching from the Stream's
// iteration and execution. Queries compile fast and are cached!
// Stream<>.Query provides you access to the underlying Query just in case.
var query = world.Query().Has<Position>().Has<Velocity>().Not<Boring>();
// Query with arbitrarily complexity of Expressions, and any number of Streams
var positions = query.Stream<Position>();
var velocities = query.Stream<Velocity>();
var both = query.Stream<Position, Velocity>();
var swap = query.Stream<Velocity, Position>();
var query = world.Stream<Position, Velocity>();
// The super-foxy minimal boilerplate shorthand version!
// Disadvantage: Only up to 5 simple Has<T> Query Expressions.
// Internally also compiles a (new) cached Query and returns
// a view to *that*, because even though a World *is* a Query, it 
// can get too large to filter + iterate compared to simple Queries.

Internals ​

Each Stream has an underlying Query, and any number of Streams can be created as views into the same Query. When it comes to processing data, Stream Views are practically always your first go-to solution in fennecs.

You can get so much work done with these bad bois! (slaps roof)

Stream Types ​

The Type parameters, C1, C2, C3, C4, C5 are also known as the Stream Types. These are the types of Components that a specific Stream's Runners (e.g. For, Job and Raw) can supply to your code.

Runners - Executing Workloads ​

Each Stream View offers a set of Runners to execute code in batches or parallelized on all Entities in its Query - For, Job, and Raw, or the ValueTuples in the Stream itself. Their order is determined from your declaration of the Stream<>.

You then pass a delegate (anonymous lambda/delegate or named method, static or instance) to one of three functions, and your code gets run for each Entity. Easy as that for starters, but with some cool twists later!


For / For<U> ​

One work item at a time. Call a ComponentAction or delegate for each Entity in a Query, providing the Components that match the Stream Types as ref to the code.
Neofox: waffleNeofox: waffleNeofox: waffleNeofox: waffleNeofox: waffleNeofox: waffleNeofox: waffleNeofox: nom_waffle


Job / Job<U> ​

One work item at a time, multi-threaded. Takes a ComponentAction delegate and instantly schedules and executes the workload split into chunks, calling it many times in parallel across CPU cores.
Neofox: waffleNeofox: nom_waffleNeofox: waffleNeofox: nom_waffleNeofox: waffleNeofox: nom_waffleNeofox: waffleNeofox: nom_waffle


Raw / Raw<U> ​

All work items at once, as contiguous memory. Using a MemoryAction, delivers the entire stream data of each Archetype directly into your fox delegate in one Memory<T> per Stream Type. Neofox: waffle_long_blurryNeofox: kirby_succ

fennecs is released under the MIT License. Neofox is released under the CC BY-NC-SA 4.0 License.