Jump to content
  • Advertisement
QuesterDesura

C++ Need some help with the Entity Component System

Recommended Posts

Hello,

I'm currently trying to make a Minecraft Clone using OpenGL and using the ECS pattern. I didn't quite understood yet why I need the "Systems" when I already have components. My current structure I'm planing to use is this:

 VBVcfIz.png

My main problem is that I don't really know how to store the Entitys. If I for example want to attack and check which Entity I did hit then I would need to iterate through all Entity stored in my vector<GameObject> in the World Class and check if it got the Health Component and then decrease the health. Which doesn't seem very clever to me as I will probably have multiple thousands Entitys alone by the Blocks in the chunks. I did read some stuff about Handlers but didn't understood how they work or should solve the problem of iterating through all Entitys who could possible need to be called to Receive Damage. Can anyone help me?

 

Thanks 

Share this post


Link to post
Share on other sites
Advertisement
1 hour ago, QuesterDesura said:

Which doesn't seem very clever to me

Yeah it isnt, but this has nothing to do with ECS.

In a "proper" engine you have spatial structures that have all physical entities inside it with their collision bodies (that can be as simple as a box, or a bunch of other primitives attached together). This is often handled by the physics engine, you can check out Bullet for instance (if you're doing a 3D game) or Box2D (if you're doing a 2D game) to see how they work and how they're used... Or you could go down the path of trying to implement your own spatial structures, in which case you'll never finish anything. Your call.

Anyway, these structures can do neat things like you pass it some primitive, and it tells you all the stuff that collides with it.

In this particular case, what you'd do is to issue a query into that spatial structure, our primitive being an infinite ray (which would require for us to check the distance later) or a line segment (from the player up to the bullet's range), pointing in the direction the player  is firing. The physics engine/spatial  structure will tell you what entities did you hit. 

With that information, you could check if those entities have a health component, and if any of they do, decrease it.

1 hour ago, QuesterDesura said:

I didn't quite understood yet why I need the "Systems" when I already have components. 

Because in "pure" ECS, components have no logic. You implement it in the systems. That player class with a "process input" method? That'd go into an InputSystem that processes entities with Player components (and **only** those).

I suggest you google for "ecs library" and see what it turns up. No sense in reinventing all of this when you can draw inspiration from tons of ECS libs that have been spawned.

 

Edited by TheChubu

Share this post


Link to post
Share on other sites

There are many different architectures people call "ECS", because it's very hyped and popular right now, few people know why they need it, and many implementations of it are terrible.

It's not one design pattern, but a buzzword that acts as an umbrella for one or two designs that I personally think are nice, and a dozen more designs that are just terrible. Some really intelligent developers go a step further and say all ECS architectures are terrible. =)

22 minutes ago, QuesterDesura said:

I didn't quite understood yet why I need the "Systems" when I already have components.

As TheChubu mentioned, in some ECS architectures, components *only* hold data, and Systems contain the logic that transforms that data. Other ECS systems, that I wouldn't even call "ECS", put the logic in the components and lose half the reason why ECS exists in the first place.

24 minutes ago, QuesterDesura said:

If I for example want to attack and check which Entity I did hit then I would need to iterate through all Entity stored in my vector<GameObject> in the World Class and check if it got the Health Component and then decrease the health.

Yes, that sounds terrible.

I don't even want components of different type put in an array together - it doesn't seem worth it to me without a compelling reason (here's me explaining why I dislike it). I wouldn't want actions like 'IAttack' to be components either, nor would I have 'Player' responsible for drawing the GUI (depending on what you mean by GUI - if it's a name or healthbar over his head, I would - but if it's a HUD or menu widgets, I wouldn't).

I probably wouldn't make Blocks into entities - the blocks need to be highly optimized since you'll have huge numbers of them, and it'd be better to implement something special just for them.

 

In Minecraft-like games, there's really only a few different types of objects:

  • Blocks - As mentioned, special care needs to be given to these because of how many there are.
     
  • Special "blocks":
  • Wall switches (activated manually)
  • Floor switches (activated on-touch)
  • Doors
  • Arrow trap, spike floor trap, spike floor, etc...
  • Moving platforms / elevators
     
  • Monsters
  • Players
  • Projectiles (arrows, fireballs, etc...)
  • Items in the world (separate concept from the items in your inventory).

Essentially, I'd make two different classes:

  • Block (for all regular blocks - it'd probably cover 99% of all your objects)
  • Entity

'Entity' would handle all the other cases: special blocks, in-world items, projectiles (unless there are alot of projectiles - inwhich case, handle it specially), and monsters and players. They all share more in common than they don't:

  • Solid: Monsters, players, most special blocks (any that aren't solid, just have a "bool solid = false" or a empty bounding box)
  • Does stuff on touch: Monsters, projectiles, floor switches, items in world.
  • Does stuff on hit: Monsters, projectiles, wall switches, special blocks, etc...
  • Visible: All (any that aren't, just have a "bool invisible" or an empty 3D model)
  • Has health: All? (indestructible entities, if any could just be given a bajillion health).

I don't think components are needed here. Instead I'd change their behavior mostly by passing in parameters, including functors. Or if really needed, derive from a base class - or just implement specific behavior as abstract classes, contained by Entity.

Sure, this will all work perfectly fine in a component system (Solid, Visible, Touchable, Hitable), but component systems cost alot more code complexity, so if I was to pay for that complexity, I'd want to be sure it is actually worth the cost of a full ECS architecture.

Share this post


Link to post
Share on other sites

So if you don't want to make all Systems derive from a base class how should they be called then? Writing them all out manually or loop through them when having them all in a vector doesn't seem to be much difference for me.

When I make 2 Classes Block and Entity would you put them in 2 vectors where are Blocks/Entitys are stored? I was thinking bout making vectors for the 4 Areas in a Cartesian coordinate system and then I can access blocks with their position as a index and when I want to check for Raycasts then I can just take the player position and directly access all blocks in a radius of x and check if they are hit.

Share this post


Link to post
Share on other sites

Please oblige me and forgett about "GameObject". This is an approach I hate Unity for bringing it into the game because you do not never ever need something as a GameObject in a specialized game. What you should have is some set of Subsystems like Rendering, Logic, and Physics each has a list of specialized classes (no! GameObjects) like a combination of Matrix, Mesh and Texture (Rendering), a combination of Position, Bounds, Gravity, Force (Physics) and a combination of Healt, logical Inventory (Logic). Evrything may be connected due to a connector class holding 3 pointers to each of its connections to keep a maintainable structure of an entity for the game but thats it.

Rendering does not need to work on the logic, moving players does the Input System, moving NPCs does the AI Systemoperating on coresponding classes. Use some kind of polling or event based update routine to keep your game running.

In Minecraft, the Voxel Engine has no information about a single block until it was created as part of a querry into the terrain system. You never have single blocks stored as objects but a data managing unit that maps chunk data into visual and logicall data. A querry is given into the system to check for possible actions performing when a player hits the input button and on the case of mining or placing a block, some data is written into that chunk and rendering is updating the mesh to remove the texture/vertices from that position and have the underlying block "become visible".

I have already worked on some games that made use of modifying there environment by building, or growing

Share this post


Link to post
Share on other sites
1 hour ago, Shaarigan said:

Please oblige me and forgett about "GameObject". This is an approach I hate Unity for bringing it into the game because you do not never ever need something as a GameObject in a specialized game.

Many, many games had some concept of a GameObject long before Unity came out. It's often called different names, of which Actor or Entity are perhaps the 2 most common.

This doesn't stop you having specialised objects which subsystems can access. For example, your Rendering system can render Meshes (like you said), but the GameObject/Entity/Actor can be responsible for registering its mesh with the Rendering system.

 

To the original poster - for a combat system you might normally need 2 components for things that can get hit - a Collision component to mark the bounds of the object, and a Damage component to track damage done to the object. You can slice and dice this process however you like, depending on which component-based approach is most appealing to you: get the damage component from the collision component via an entity, have a system that returns only matching pairs of collision/damage components, etc.

Ultimately though, you're not going to want your blocks to be component-based entities anyway, as you will have far too many blocks for this to be a efficient way to work. So you'll probably want to handle mining and digging as a special case. So you'll need to ask yourself how much benefit you're going to get from these components.

Share this post


Link to post
Share on other sites
On 9/10/2017 at 3:25 AM, QuesterDesura said:

So if you don't want to make all Systems derive from a base class how should they be called then? Writing them all out manually or loop through them when having them all in a vector doesn't seem to be much difference for me.

The end-generated code isn't different, but the code itself is significantly different.

As you know, in programming there are many different ways to get the same result. But different ways of coding can have different pros and cons they tradeoff (and it's not zero-sum either - some are genuinely better, others genuinely worse) - the code design affects their execution speed, memory usage, development time, compile time, convenience vs amount of boilerplate required, ease of debugging, ease of expanding, ease of learning/understanding, ease of reasoning about after having learned it, and so on, even when the final output is the same.

 

<opinion>

My issue isn't that the end-result is different (it's not), but rather that one method actually has more cons than I can justify to myself, without any real gains, and makes it a little harder to expand the architecture in certain ways, as development continues.

In the thread I linked, someone asked a similar question and I gave some concrete examples, but to summarize a two key points:

  • Forcing a consistent interface on unrelated classes puts unnecessary constraints on how the individual interfaces are designed and used.
    Hodgeman gave one example, and I gave the Uncharted 2 example. For me personally, when I am putting constraints on an interface via inheritance, I find it hard to notice when the class isn't a suitable fit, because I've already made the assumption that it should be inherited - so it's hard to catch myself at it, hence my pro-active defense against it - i.e. I better justify it to myself in advance, because if I assume it is justifiable without cause, I have trouble remembering to question the justifications afterward.
     
  • Manually calling the systems makes their execution order clear and explicit.
    It's slightly easier to debug and optimize (including easier to parallelize). And being more straightforward, it's much easier to comprehend and reason about, helping other coders - or yourself in the future - understand what's going on. It's also less code. You don't gain anything of real substance from making them inherit, but you do lose some real benefits.

Others absolutely disagree with me. I had a link to a thread somewhere giving an alternative view, though I can't place my hand on the link.

I should also clarify that there are absolutely use-cases for components and component systems where it *does* make sense for the components to inherit from a base class. But at the level of abstraction we are talking about here, where the ECS is containing things as unrelated as Graphics and Physics, I've never been convinced that the actual benefits sacrificed on that particular altar of abstraction gain any benefit worth the cost.  =)

</opinion>

Quote

When I make 2 Classes Block and Entity would you put them in 2 vectors where are Blocks/Entitys are stored?

Yeah, I'd put them in separate containers. Entities would likely be in a vector, possibly with an unordered_map to redirect EntityID's to the vector's elements, if required.

For blocks, I'd start off with a 3D array of chunks with each chunk being a 3D array of blocks, and only do something cleverer if needed.

For example, subdividing the world into 16x16x16 chunks of blocks, to cull or stream them quicker.

By "3D array", I actually mean a regular 1D array (std::vector or std::array) to you index into as if it were 3D:

arraySize = (width * height * depth);

arrayIndex = (x) + (y * width) + (z * height * width); //Usually tossed into a convience function.

 

 

Share this post


Link to post
Share on other sites
On 9/9/2017 at 7:33 PM, TheChubu said:

That player class with a "process input" method? That'd go into an InputSystem that processes entities with Player components (and **only** those).

Just wanted to point out you may want the InputSystem to operate on the entities which contains an InputComponent.  This way you can control things other than just the player (like a homing missile, or a remote control car, etc.).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
  • Advertisement
  • Popular Tags

  • Similar Content

    • By nilkun
      Hello everyone!
      First time posting in the forum.
      I've just completed my first game ever ( C++ / SDL ), and I am feeling utterly proud. It is a small game resembling Missile Command. The code is a mess, but it is my mess! In the process of making the game, I developed my own little game engine.
      My question is, where would be a good place to spread the news to at least get some people to try the game?
    • By owenjr
      Hi, I'm a Multimedia Engineering student. I am about to finish my dergree and I'm already thinking about what topic to cover in my final college project.
      I'm interested in the procedural animation with c++ and OpenGL of creatures, something like a spider for example. Can someone tell me what are the issues I should investigate to carry it out? I understand that it has some dependence on artificial intelligence but I do not know to what extent. Can someone help me to find information about it? Thank you very much.
       
      Examples: 
      - Procedural multi-legged walking animation
      - Procedural Locomotion of Multi-Legged Characters in Dynamic Environments
    • By Andrew Parkes
      I am a talented 2D/3D artist with 3 years animation working experience and a Degree in Illustration and Animation. I have won a world-wide art competition hosted by SFX magazine and am looking to develop a survival game. I have some knowledge of C sharp and have notes for a survival based game with flexible storyline and PVP. Looking for developers to team up with. I can create models, animations and artwork and I have beginner knowledge of C sharp with Unity. The idea is Inventory menu based gameplay and is inspired by games like DAYZ.
      Here is some early sci-fi concept art to give you an idea of the work level. Hope to work with like minded people and create something special. email me andrewparkesanim@gmail.com.
      Developers who share the same passion please contact me, or if you have a similar project and want me to join your team email me. 
      Many thanks, Andrew.

    • By mike44
      Hi
      saw in dependency walker that my app still needs msvcp140d.dll even after disabling debug.
      What did I forget in the VS2017 release settings? After setting to multithreaded dll I get linker errors.
      Thanks
       
    • By 3dmodelerguy
      So I have been playing around with yaml-cpp as I want to use YAML for most of my game data files however I am running into some pretty big performance issues and not sure if it is something I am doing or the library itself.
      I created this code in order to test a moderately sized file:
      Player newPlayer = Player(); newPlayer.name = "new player"; newPlayer.maximumHealth = 1000; newPlayer.currentHealth = 1; Inventory newInventory; newInventory.maximumWeight = 10.9f; for (int z = 0; z < 10000; z++) { InventoryItem* newItem = new InventoryItem(); newItem->name = "Stone"; newItem->baseValue = 1; newItem->weight = 0.1f; newInventory.items.push_back(newItem); } YAML::Node newSavedGame; newSavedGame["player"] = newPlayer; newSavedGame["inventory"] = newInventory; This is where I ran into my first issue, memory consumption.
      Before I added this code, the memory usage of my game was about 22MB. After I added everything expect the YAML::Node stuff, it went up to 23MB, so far nothing unexpected. Then when I added the YAML::Node and added data to it, the memory went up to 108MB. I am not sure why when I add the class instance it only adds like 1MB of memory but then copying that data to a YAML:Node instance, it take another 85MB of memory.
      So putting that issue aside, I want want to test the performance of writing out the files. the initial attempt looked like this:
      void YamlUtility::saveAsFile(YAML::Node node, std::string filePath) { std::ofstream myfile; myfile.open(filePath); myfile << node << std::endl; myfile.close(); } To write out the file (that ends up to be about 570KB), it took about 8 seconds to do that. That seems really slow to me.
      After read the documentation a little more I decide to try a different route using the YAML::Emitter, the implemntation looked like this:
      static void buildYamlManually(std::ofstream& file, YAML::Node node) { YAML::Emitter out; out << YAML::BeginMap << YAML::Key << "player" << YAML::Value << YAML::BeginMap << YAML::Key << "name" << YAML::Value << node["player"]["name"].as<std::string>() << YAML::Key << "maximumHealth" << YAML::Value << node["player"]["maximumHealth"].as<int>() << YAML::Key << "currentHealth" << YAML::Value << node["player"]["currentHealth"].as<int>() << YAML::EndMap; out << YAML::BeginSeq; std::vector<InventoryItem*> items = node["inventory"]["items"].as<std::vector<InventoryItem*>>(); for (InventoryItem* const value : items) { out << YAML::BeginMap << YAML::Key << "name" << YAML::Value << value->name << YAML::Key << "baseValue" << YAML::Value << value->baseValue << YAML::Key << "weight" << YAML::Value << value->weight << YAML::EndMap; } out << YAML::EndSeq; out << YAML::EndMap; file << out.c_str() << std::endl; } While this did seem to improve the speed, it was still take about 7 seconds instead of 8 seconds.
      Since it has been a while since I used C++ and was not sure if this was normal, I decided to for testing just write a simple method to manually generate the YAMLin this use case, that looked something like this:
      static void buildYamlManually(std::ofstream& file, SavedGame savedGame) { file << "player: \n" << " name: " << savedGame.player.name << "\n maximumHealth: " << savedGame.player.maximumHealth << "\n currentHealth: " << savedGame.player.currentHealth << "\ninventory:" << "\n maximumWeight: " << savedGame.inventory.maximumWeight << "\n items:"; for (InventoryItem* const value : savedGame.inventory.items) { file << "\n - name: " << value->name << "\n baseValue: " << value->baseValue << "\n weight: " << value->weight; } } This wrote the same file and it took about 0.15 seconds which seemed a lot more to what I was expecting.
      While I would expect some overhead in using yaml-cpp to manage and write out YAML files, it consuming 70X+ the amount of memory and it being 40X+ slower in writing files seems really bad.
      I am not sure if I am doing something wrong with how I am using yaml-cpp that would be causing this issue or maybe it was never design to handle large files but was just wondering if anyone has any insight on what might be happening here (or an alternative to dealing with YAMLin C++)?
    • By 3dmodelerguy
      So I am trying to using Yaml as my game data files (mainly because it support comments, is a bit easier to read than JSON, and I am going to be working in these files a lot) with C++ and yaml-cpp (https://github.com/jbeder/yaml-cpp) seems like the most popular library for dealing with it however I am running into an issue when using pointers.
      Here is my code:
      struct InventoryItem { std::string name; int baseValue; float weight; }; struct Inventory { float maximumWeight; std::vector<InventoryItem*> items; }; namespace YAML { template <> struct convert<InventoryItem*> { static Node encode(const InventoryItem* inventoryItem) { Node node; node["name"] = inventoryItem->name; node["baseValue"] = inventoryItem->baseValue; node["weight"] = inventoryItem->weight; return node; } static bool decode(const Node& node, InventoryItem* inventoryItem) { // @todo validation inventoryItem->name = node["name"].as<std::string>(); inventoryItem->baseValue = node["baseValue"].as<int>(); inventoryItem->weight = node["weight"].as<float>(); return true; } }; template <> struct convert<Inventory> { static Node encode(const Inventory& inventory) { Node node; node["maximumWeight"] = inventory.maximumWeight; node["items"] = inventory.items; return node; } static bool decode(const Node& node, Inventory& inventory) { // @todo validation inventory.maximumWeight = node["maximumWeight"].as<float>(); inventory.items = node["items"].as<std::vector<InventoryItem*>>(); return true; } }; } if I just did `std::vector<InventoryItem> items` and had the encode / decode use `InventoryItem& inventoryItem` everything works fine however when I use the code above that has it as a pointer, I get the following error from code that is part of the yaml-cpp library:
      impl.h(123): error C4700: uninitialized local variable 't' used The code with the error is:
      template <typename T> struct as_if<T, void> { explicit as_if(const Node& node_) : node(node_) {} const Node& node; T operator()() const { if (!node.m_pNode) throw TypedBadConversion<T>(node.Mark()); T t; if (convert<T>::decode(node, t)) // NOTE: THIS IS THE LINE THE COMPILER ERROR IS REFERENCING return t; throw TypedBadConversion<T>(node.Mark()); } }; With my relative lack of experience in C++ and not being able to find any documentation for yaml-cpp using pointers, I am not exactly sure what is wrong with my code.
      Anyone have any ideas what I need to change with my code? 
    • By Gnollrunner
      I already asked this question on stack overflow, and they got pissed at me, down-voted me and so forth, LOL .... so I'm pretty sure the answer is NO, but I'll try again here anyway just in case..... Is there any way to get the size of a polymorphic object at run-time? I know you can create a virtual function that returns size and overload it for each child class, but I'm trying to avoid that since (a) it takes a virtual function call and I want it to be fast and (b) it's a pain to have to include the size function for every subclass. I figure since each object has a v-table their should be some way since the information is there, but perhaps there is no portable way to do it.
    • By MarcusAseth
      This is the code I have:
       //Create Window     DWORD windowStyle = WS_VISIBLE;     DWORD windowExStyle = WS_EX_OVERLAPPEDWINDOW;     SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE);     RECT client{ 0, 0, 100, 40 };     UINT dpi = GetDpiForSystem();     AdjustWindowRectExForDpi(&client, windowStyle, false, windowExStyle, dpi);     UINT adjustedWidth = client.right - client.left;     UINT adjustedHeight = client.bottom - client.top;     m_hwnd = CreateWindowEx(windowExStyle,                             className.c_str(),                             windowName.c_str(),                             windowStyle,                             CW_USEDEFAULT,                             CW_USEDEFAULT,                             adjustedWidth,                             adjustedHeight,                             nullptr,                             nullptr,                             m_hInstance,                             m_emu     ); The generated window has a client area of 1 pixel in height, even though I'm asking for 40. so I'm always getting 39 pixel less than what I need...can someone help me with this? x_x
    • By SeraphLance
      I've spent quite a while (and probably far longer than I actually should) trying to design an allocator system.  I've bounced ideas around to various people in the past, but never really gotten something satisfactory.
      Basically, the requirements I'm trying to target are:
        Composability -- allocators that seamlessly allocate from memory allocated by other allocators.  This helps me to do things like, for example, write an allocator that pads allocations from its parent allocator with bit patterns to detect heap corruption.  It also allows me to easily create spillovers, or optionally assert on overflow with specialized fallbacks.   Handling the fact that some allocators have different interfaces than others in an elegant way.  For example, a regular allocator might have Allocate/Deallocate, but a linear allocator can't do itemized deallocation (but can deallocate everything at once).   I want to be able to tell how much I've allocated, and how much of that is actually being used.  I also want to be able to bucket that on subsystem, but as far as I can tell, that doesn't really impact the design outside of adding a new parameter to allocate calls. Note:  I'm avoiding implementation of allocation buckets and alignment from this, since it's largely orthogonal to what I'm asking and can be done with any of the designs.
       
      To meet those three requirements, I've come up with the following solutions, all of which have significant drawbacks.
      Static Policy-Based Allocators
      I originally built this off of this talk.
      Examples;
      struct AllocBlock { std::byte* ptr; size_t size; }; class Mallocator { size_t allocatedMemory; public: Mallocator(); AllocBlock Allocate(size_t size); void Deallocate(AllocBlock blk); }; template <typename BackingAllocator, size_t allocSize> class LinearAllocator : BackingAllocator { AllocBlock baseMemory; char* ptr; char* end; public: LinearAllocator() : baseMemory(BackingAllocator::Allocate(allocSize)) { /* stuff */ } AllocBlock Allocate(size_t size); }; template <typename BackingAllocator, size_t allocSize> class PoolAllocator : BackingAllocator { AllocBlock baseMemory; char* currentHead; public: PoolAllocator() : baseMemory(BackingAllocator::Allocate(allocSize)) { /* stuff */ } void* Allocate(); // note the different signature. void Deallocate(void*); }; // ex: auto allocator = PoolAllocator<Mallocator, size>; Advantages:
      SFINAE gives me a pseudo-duck-typing thing.  I don't need any kind of common interfaces, and I'll get a compile-time error if I try to do something like create a LinearAllocator backed by a PoolAllocator. It's composable. Disadvantages:
      Composability is type composability, meaning every allocator I create has an independent chain of compositions.  This makes tracking memory usage pretty hard, and presumably can cause me external fragmentation issues.  I might able to get around this with some kind of singleton kung-fu, but I'm unsure as I don't really have any experience with them. Owing to the above, all of my customization points have to be template parameters because the concept relies on empty constructors.  This isn't a huge issue, but it makes defining allocators cumbersome. Dynamic Allocator Dependency
      This is probably just the strategy pattern, but then again everything involving polymorphic type composition looks like the strategy pattern to me. 😃
      Examples:
      struct AllocBlock { std::byte* ptr; size_t size; }; class Allocator { virtual AllocBlock Allocate(size_t) = 0; virtual void Deallocate(AllocBlock) = 0; }; class Mallocator : Allocator { size_t allocatedMemory; public: Mallocator(); AllocBlock Allocate(size_t size); void Deallocate(AllocBlock blk); }; class LinearAllocator { Allocator* backingAllocator; AllocBlock baseMemory; char* ptr; char* end; public: LinearAllocator(Allocator* backingAllocator, size_t allocSize) : backingAllocator(backingAllocator) { baseMemory = backingAllocator->Allocate(allocSize); /* stuff */ } AllocBlock Allocate(size_t size); }; class PoolAllocator { Allocator* backingAllocator; AllocBlock baseMemory; char* currentHead; public: PoolAllocator(Allocator* backingAllocator, size_t allocSize) : backingAllocator(backingAllocator) { baseMemory = backingAllocator->Allocate(allocSize); /* stuff */ } void* Allocate(); // note the different signature. void Deallocate(void*); }; // ex: auto allocator = PoolAllocator(someGlobalMallocator, size); There's an obvious problem with the above:  Namely that PoolAllocator and LinearAllocator don't inherit from the generic Allocator interface.  They can't, because their interfaces provide different semantics.  There's to ways I can solve this:
        Inherit from Allocator anyway and assert on unsupported operations (delegates composition failure to runtime errors, which I'd rather avoid).   As above:  Don't inherit and just deal with the fact that some composability is lost (not ideal, because it means you can't do things like back a pool allocator with a linear allocator) As for the overall structure, I think it looks something like this:
      Advantages:
      Memory usage tracking is easy, since I can use the top-level mallocator(s) to keep track of total memory allocated, and all of the leaf allocators to track of used memory.  How to do that in particular is outside the scope of what I'm asking about, but I've got some ideas. I still have composability Disadvantages:
      The interface issues above.  There's no duck-typing-like mechanism to help here, and I'm strongly of the opinion that programmer errors in construction like that should fail at compile-time, not runtime. Composition on Allocated Memory instead of Allocators
      This is probably going to be somewhat buggy and poorly thought, since it's just an idea rather than something I've actually tried.
      Examples:
      struct AllocBlock { void* ptr; size_t size; std::function<void()> dealloc; } class Mallocator { size_t allocatedMemory; public: Mallocator(); AllocBlock Allocate(size_t size) { void* ptr = malloc(size); return {ptr, size, [ptr](){ free(ptr); }}; } }; class LinearAllocator { AllocBlock baseMemory; char* ptr; char* end; public: LinearAllocator(AllocBlock baseMemory) : baseMemory(baseMemory) {end = ptr = baseMemory.ptr;} AllocBlock Allocate(size_t); }; class PoolAllocator { AllocBlock baseMemory; char* head; public: PoolAllocator(AllocBlock baseMemory) : baseMemory(baseMemory) { /* stuff */ } void* Allocate(); }; // ex: auto allocator = PoolAllocator(someGlobalMallocator.Allocate(size)); I don't really like this design at first blush, but I haven't really tried it.

      Advantages:
      "Composable", since we've delegated most of what composition entails into the memory block rather than the allocator. Tracking memory is a bit more complex, but I *think* it's still doable. Disadvantages:
      Makes the interface more complex, since we have to allocate first and then pass that block into our "child" allocator. Can't do specialized deallocation (i.e. stack deallocation) since the memory blocks don't know anything about their parent allocation pool.  I might be able to get around this though.  
      I've done a lot of research against all of the source-available engines I can find, and it seems like most of them either have very small allocator systems or simply don't try to make them composable at all (CryEngine does this, for example).  That said, it seems like something that should have a lot of good examples, but I can't find a whole lot.  Does anyone have any good feedback/suggestions on this, or is composability in general just a pipe dream?
    • By RobMaddison
      Hi
      I’ve been working on a game engine for years and I’ve recently come back to it after a couple of years break.  Because my engine uses DirectX9.0c I thought maybe it would be a good idea to upgrade it to DX11. I then installed Windows 10 and starting tinkering around with the engine trying to refamiliarise myself with all the code.
      It all seems to work ok in the new OS but there’s something I’ve noticed that has caused a massive slowdown in frame rate. My engine has a relatively sophisticated terrain system which includes the ability to paint roads onto it, ala CryEngine. The roads are spline curves and built up with polygons matching the terrain surface. It used to work perfectly but I’ve noticed that when I’m dynamically adding the roads, which involves moving the spline curve control points around the surface of the terrain, the frame rate comes to a grinding halt.
      There’s some relatively complex processing going on each time the mouse moves - the road either side of the control point(s) being moved, is reconstructed in real time so you can position and bend the road precisely. On my previous OS, which was Win2k Pro, this worked really smoothly and in release mode there was barely any slow down in frame rate, but now it’s unusable. As part of the road reconstruction, I lock the vertex and index buffers and refill them with the new values so my question is, on windows 10 using DX9, is anyone aware of any locking issues? I’m aware that there can be contention when locking buffers dynamically but I’m locking with LOCK_DISCARD and this has never been an issue before.
      Any help would be greatly appreciated.
  • Advertisement
  • Popular Now

  • Forum Statistics

    • Total Topics
      631383
    • Total Posts
      2999687
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!