Skip to content

Commit

Permalink
Hey, so i took a stab at my humble attempt of trying to improve the d…
Browse files Browse the repository at this point in the history
…ocumentation. I expect this to be an on-going effort, I hope this helps!

Let me know if you think this is helpful, I want to help this project succeed! I'm hoping if I pair my "making a game" with "learning to use this library", others will have an *easier* time integrating ecst with their game.

Anywho, love to talk this over with you.
  • Loading branch information
bjadamson committed Sep 4, 2016
1 parent ba7f53d commit a081778
Show file tree
Hide file tree
Showing 3 changed files with 350 additions and 259 deletions.
14 changes: 14 additions & 0 deletions BUILD_INSTRUCTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Build instructions

1. Acquire and install [`boost::hana`](http://www.boost.org/doc/libs/1_61_0/libs/hana/doc/html/index.html).
2. Clone the repository.
3. Execute the `./init-repository.sh` script.
4. Create a build directory and execute CMake:

```bash
mkdir ./build
cd ./build
cmake ..
```

*(Some examples may require [SFML](https://sfml-dev.org) to be installed.)*
261 changes: 261 additions & 0 deletions CODE_SAMPLE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
```cpp
#include <ecst.hpp>

// Define some components.
namespace c
{
// Components are simple classes, usually POD structs. There is no need
// for components to derive from a "base component" class or to satisfy
// a particular interface.

struct position
{
vec2f _v;
};

struct velocity
{
vec2f _v;
};

struct acceleration
{
vec2f _v;
};
}

// Define component tags.
namespace ct
{
namespace sc = ecst::signature::component;

constexpr auto position = ecst::tag::component::v<c::position>;
constexpr auto velocity = ecst::tag::component::v<c::velocity>;
constexpr auto acceleration = ecst::tag::component::v<c::acceleration>;
}

// Define some systems.
namespace s
{
// Systems are simple classes as well, that do not need to satisfy any
// particular interface. They can store data and have any method the
// user desires.

// This system accelerates the subscribed particles.
struct acceleration
{
// The `process` method is not hardcoded or specially recognized by
// ECST in any way. Using a lambda in the execution code, we can
// tell an ECST context to execute a particular method (also
// forwarding extra arguments to it).

// The `data` parameter is a proxy object generated by the system
// execution strategy that abstracts away the eventual underlying
// parallelism.

template <typename TData>
void process(ft dt, TData& data)
{
// Notice that the code below does not know anything about the
// multithreading strategy employed by the system: the same
// syntax works with any kind (or lack) of parallel execution.
data.for_entities([&](auto eid)
{
auto& v = data.get(ct::velocity, eid)._v;
const auto& a = data.get(ct::acceleration, eid)._v;

v += a * dt;
});
}
};

// This system moves the subscribed particles.
struct velocity
{
template <typename TData>
void process(ft dt, TData& data)
{
data.for_entities([&](auto eid)
{
auto& p = data.get(ct::position, eid)._v;
const auto& v = data.get(ct::velocity, eid)._v;

p += v * dt;
});
}
};
}

// Setup compile-time settings.
namespace ecst_setup
{
// Builds and returns a "component signature list".
constexpr auto make_csl()
{
namespace sc = ecst::signature::component;
namespace slc = ecst::signature_list::component;

// Store `c::acceleration`, `c::velocity` and `c::position` in
// three separate contiguous buffers (SoA).
constexpr auto cs_acceleration =
sc::make(ct::acceleration).contiguous_buffer();

constexpr auto cs_velocity =
sc::make(ct::velocity).contiguous_buffer();

constexpr auto cs_position =
sc::make(ct::position).contiguous_buffer();

// Build and return the "component signature list".
return slc::make(cs_acceleration, cs_velocity, cs_position);

// Components can be stored in multiple ways, and users
// can define their complex storage types. Here's an example
// of "AoS" storage:
/*
constexpr auto cs_aos_physics =
sc::make(ct::acceleration, ct::velocity, ct::position)
.contiguous_buffer();
*/
}

// Builds and returns a "system signature list".
constexpr auto make_ssl()
{
// Signature namespace aliases.
namespace ss = ecst::signature::system;
namespace sls = ecst::signature_list::system;

// Inner parallelism aliases and definitions.
namespace ips = ecst::inner_parallelism::strategy;
namespace ipc = ecst::inner_parallelism::composer;

// "Split processing evenly between cores."
constexpr auto split_evenly_per_core =
ips::split_evenly_fn::v_cores();

// Acceleration system.
// * Multithreaded.
// * No dependencies.
constexpr auto ssig_acceleration =
ss::make(st::acceleration)
.parallelism(split_evenly_per_core)
.read(ct::acceleration)
.write(ct::velocity);

// Velocity system.
// * Multithreaded.
constexpr auto ssig_velocity =
ss::make(st::velocity)
.parallelism(split_evenly_per_core)
.dependencies(st::acceleration)
.read(ct::velocity)
.write(ct::position);

// Build and return the "system signature list".
return sls::make(ssig_acceleration, ssig_velocity);
}
}

// Create a particle and return its unique ID.
template <typename TProxy>
auto mk_particle(TProxy& proxy, const vec2f& position)
{
auto eid = proxy.create_entity();

auto& ca = proxy.add_component(ct::acceleration, eid);
ca._v.y = 1;

auto& cv = proxy.add_component(ct::velocity, eid);
cv._v = rndvec2f(-3, 3);

return eid;
}


int main()
{
// Namespace aliases.
using namespace ecst_setup;
namespace cs = ecst::settings;
namespace ss = ecst::scheduler;
namespace sea = ecst::system_execution_adapter;

// Define ECST context settings.
constexpr auto s =
ecst::settings::make()
.allow_inner_parallelism()
.fixed_entity_limit(ecst::sz_v<10000>)
.component_signatures(make_csl())
.system_signatures(make_ssl())
.scheduler(cs::scheduler<ss::s_atomic_counter>);

// Create an ECST context.
auto ctx = ecst::context::make_uptr(s);

// Initialize context with some entities.
ctx->step([&](auto& proxy)
{
for(sz_t i = 0; i < 1000; ++i)
{
mk_particle(proxy, random_position());
}
});

// "Game loop."
while(true)
{
auto dt = delta_time();

ctx->step([dt](auto& proxy)
{
// Start executing a chain of systems from `st::acceleration`:
proxy.execute_systems_from(st::acceleration)(
// Match systems in the chain by tag...
sea::t(st::acceleration, st::velocity, st::position)
// ...and execute the same logic for every parallel subtask:
.for_subtasks([dt](auto& s, auto& data)
{
s.process(dt, data);
})
);

// Need more control? Here's an additional example:
proxy.execute_systems_from(st::acceleration)(
sea::t(st::acceleration, st::velocity)
.for_subtasks([dt](auto& s, auto& data)
{
s.process(dt, data);
}),
sea::t(st::position).detailed_instance(
[&proxy, dt](auto& instance, auto& executor)
{
// Access system `instance` details:
std::cout << instance.subscribed().size() << "\n";
auto& s(instance.system());

do_something_before();

executor.for_subtasks([&s, dt](auto& data)
{
s.process(dt, data);
});

do_something_after();
})
);
},
// Refresh events can be caught and handled sequentially.
ecst::refresh_event::on_subscribe(st::acceleration,
[](auto& system, auto eid)
{
log() << "Entity #" << eid
<< " subscribed to acceleration system.\n".
}),
ecst::refresh_event::on_reclaim([](auto eid)
{
log() << "Entity #" << eid << " reclaimed.\n".
}));
}
}
```
Loading

0 comments on commit a081778

Please sign in to comment.