Sigma icon indicating copy to clipboard operation
Sigma copied to clipboard

Graphics pipeline could be more efficient + more flexible

Open wrongu opened this issue 11 years ago • 14 comments

I wrote up my thoughts on it here

EDIT: turns out I pretty much just described a scene graph.

wrongu avatar Jan 03 '14 17:01 wrongu

I think that you described the principle of ECS without even naming it. #126 has the same purpose, so we agree on the current design issues and how to solve them.

catageek avatar Jan 03 '14 20:01 catageek

I've been working under the assumption that Sigma is already ECS, so your PR that makes it more like ECS will still be compatible :). I haven't had a chance to really dig through everything you have in that PR - it's a lot.

wrongu avatar Jan 03 '14 20:01 wrongu

Glmesh uses named variables for taking which index each buffer id at stored at each index. For example there's a vertex buffer index which says the vertex buffer id is stored in the buffer array at that index.

adam4813 avatar Jan 03 '14 23:01 adam4813

@adam4813 I understand how the current system works, I just don't like that it requires components to know about shader details like the names of inputs. I'm trying to abstract that away.

After talking with @catageek on IRC, it looks like my ideas would be best implemented on top of his in #126. Namely I would need to maintain lists of components that are "batched" together, which is impossible with the current unique_ptr design.

wrongu avatar Jan 05 '14 18:01 wrongu

I commented the doc. The current components must become global (i.e 1 instance for many entities). There will be a component for the views, and a component for each type of uniform. the shaders must iterate on components and render all entities in this component. This avoid to switch too often the component Uniforms and to keep the same render function across iterations over entities (for cache efficiency)

catageek avatar Jan 06 '14 13:01 catageek

Having a component being an accessor instead of a data source seems counter to the goal of a component that belongs to each entity.

I haven't had a chance to review your PR thoroughly yet, but it looks promising if not counter-intuitive.

On Mon, Jan 6, 2014 at 7:00 AM, catageek [email protected] wrote:

I commented the doc. The current components must become global (i.e 1 instance for many entities). There will be a component for the views, and a component for each type of uniform. the shaders must iterate on components and render all entities in this component. This avoid to switch too often the component Uniforms.

— Reply to this email directly or view it on GitHubhttps://github.com/adam4813/Sigma/issues/128#issuecomment-31646429 .

-Adam

adam4813 avatar Jan 06 '14 13:01 adam4813

It is as counter-intuitive as having a bank account: You don't have it, it is the bank that owns it. You just request your bank to access it.

The bank can industrialize the process of account balance, operations, reports, at little cost. It is far more efficient than if everyone manage his own banking system.

catageek avatar Jan 06 '14 14:01 catageek

Since I like my "bank account" example, let's develop it:

If a tax department wants to take money on each account, OOP paradigm says : "ask every people if it has a bank account, and use their own function to take money". If the tax depends on income, it will add "tell me your income in the format you want so I can compute tax".

ECS paradigm says: "Request the income of all employees in each company, send them to the bank in order to perform the operation for you, in distinct packets if the format is specific for each company".

So in ECS an entity will "have" a bank account, a job with an income and will be taxed. 3 components.

catageek avatar Jan 06 '14 15:01 catageek

I like and understand your example. On Jan 6, 2014 9:00 AM, "catageek" [email protected] wrote:

Since I like my "bank account" example, let's develop it:

If a tax department wants to take money on each account, OOP paradigm says : "ask every people if it has a bank account, and use their own function to take money". If the tax depends on income, it will add "tell me your income in the format you want so I can compute tax".

ECS paradigm says: "Request the income of all employees in each company, send them to the bank in order to perform the operation for you, in distinct packets if the format is specific for each company".

So in ECS an entity will "have" a bank account, a job with an income and will be taxed. 3 components.

— Reply to this email directly or view it on GitHubhttps://github.com/adam4813/Sigma/issues/128#issuecomment-31654212 .

adam4813 avatar Jan 06 '14 15:01 adam4813

I also like the bank account example. But this thread went a bit off-topic. I'd like a little more feedback from @adam4813 before continuing with my pipeline branch. Namely,

  1. what exactly is the plan to get around the unique_ptr problem?
  2. How little are you willing to let the IGLComponents be responsible for? For example I would like to turn IGLComponent::Render(glm::mat4 *view, glm::mat4 *proj) into IGLComponent::Render() such that View Projection and maybe even Model matrices are handled by the shader objects.

PS the wiki link in my first comment has been updated.

wrongu avatar Jan 13 '14 23:01 wrongu

Wrongu,

I read the discussion on IRC of this night and I would like to give indications on how to apply ECS paradigm.

We eventually agre on the terms "Composite" and "Component". Basically, a composite is defined as a part of the identity of an entity, while components store the state of the entity.

shared resource is another thing that is not part of ECS. If a resource must be set as component, it will be provided as a pointer on the external resource.

Definition : Composite is what you query and you attach to an entity for it to become this entity. Adding or removing a composite (and dependencies) turns the entity in another kind of entity that is fully functional, with more or less properties. So a composite is defined as a module from the game point of view.

As Adam told on IRC, we can define a composite "Renderable" that turns an entity into something that has a graphic representation. Note that "Renderable" matches exactly the definition given above.

Definition: Component is the description of the state of an entity. To be more specific, a set of components describe the state of the entity for the composite associated. If some states are strongly dependant on each others and share same lifecycle, then they should be stored in the same composite, otherwise a composite must be modified or created.

for instance, a simple component of the Renderable composite would be a "isVisible" bool telling if the entity must be rendered.

Technically speaking, a composite is a class that registers a factory method with its name, and takes an entity id and a vector of properties as arguments. It parses the properties and store them in the component containers (there is a VectorMap template in my branch that was made for the purpose of running batch on ordered values like in a vector, and there is a BitArray template to be used as a bool container). the factory function returns a vector of unique_ptr on objects that wrap weak_ptr pointing on the component stored (there is an additional wrapper currently). The only purpose of this return value is to fill the variables of the entity instance, if there is an entity class. Currently, in the physics system, it is perfectly legal to call the factory method and drop the return value since no entity class is needed. Composites host also the batch code to run on components; The batch are called from the system update() function.

A possible implementation: (choose the names you want, this is just an example)

  • a composite "Renderable" that will store the components "FBindex", "Texture", "isVisible", shader ids, etc. (I am not a specialist of OpenGL so I let you fill the blanks).
  • a composite "Light" that will store the shader id and an additional transformation matrix to apply to the entity transformation.
  • a composite "Render2D" with a bool and the position of the graphic object on the screen. (this one may be merged in Renderable)
  • etc.

Note that each composite add a function to the entity, and removing a composite does not invalidate the entity, but turn them into another entity.

For the shaders, if you can't turn them into a additional property of an entity, you must find a way to iterate on entities that share the same shaders. A good way to do that is to use bit arrays (the BitArray template), 1 bit array for each shader that you fill with entity ids, and use the iterator provided by the template to iterate efficiently on entity ids that have their bit set. BitArray has also some logical operations to combine several of them, it may be useful in some cases.

A note on BitArray template: iterators perform much better if only a few bits are set. If a BitArray should be filled almost entirely with 1s, and only few 0s, store the inversed bit and use nested loops that will stop on the next "true" value (i.e not set) and skip it. This is the case of shaders that are common to almost every entity.

catageek avatar Jan 14 '14 10:01 catageek

Good definitions and examples. I am glad we have come to that happy middle ground. On Jan 14, 2014 4:45 AM, "catageek" [email protected] wrote:

Wrongu,

I read the discussion on IRC of this night and I would like to give indications on how to apply ECS paradigm.

We eventually agre on the terms "Composite" and "Component". Basically, a composite is defined as a part of the identity of an entity, while components store the state of the entity.

shared resource is another thing that is not part of ECS. If a resource must be set as component, it will be provided as a pointer on the external resource.

Definition : Composite is what you query and you attach to an entity for it to become this entity. Adding or removing a composite (and dependencies) turns the entity in another kind of entity that is fully functional, with more or less properties. So a composite is defined as a module from the game point of view.

As Adam told on IRC, we can define a composite "Renderable" that turns an entity into something that has a graphic representation. Note that "Renderable" matches exactly the definition given above.

Definition: Component is the description of the state of an entity. To be more specific, a set of components describe the state of the entity for the composite associated. If some states are strongly dependant on each others and share same lifecycle, then they should be stored in the same composite, otherwise a composite must be modified or created.

for instance, a simple component of the Renderable composite would be a "isVisible" bool telling if the entity must be rendered.

Technically speaking, a composite is a class that registers a factory method with its name, and takes an entity id and a vector of properties as arguments. It parses the properties and store them in the component containers (there is a VectorMap template in my branch that was made for the purpose of running batch on ordered values like in a vector, and there is a BitArray template to be used as a bool container). the factory function returns a vector of unique_ptr on objects that wrap weak_ptr pointing on the component stored (there is an additional wrapper currently). The only purpose of this return value is to fill the variables of the entity instance, if there is an entity class. Currently, in the physics system, it is perfectly legal to call the factory method and drop the return value since no entity class is needed. Composites host also the batch code to run on components; The batch are called from the system update() function.

A possible implementation: (choose the names you want, this is just an example)

  • a composite "Renderable" that will store the components "FBindex", "Texture", "isVisible", shader ids, etc. (I am not a specialist of OpenGL so I let you fill the blanks).
  • a composite "Light" that will store the shader id and an additional transformation matrix to apply to the entity transformation.
  • a composite "Render2D" with a bool and the position of the graphic object on the screen. (this one may be merged in Renderable)
  • etc.

Note that each composite add a function to the entity, and removing a composite does not invalidate the entity, but turn them into another entity.

For the shaders, if you can't turn them into a additional property of an entity, you must find a way to iterate on entities that share the same shaders. A good way to do that is to use bit arrays (the BitArray template), 1 bit array for each shader that you fill with entity ids, and use the iterator provided by the template to iterate efficiently on entity ids that have their bit set. BitArray has also some logical operations to combine several of them, it may be useful in some cases.

A note on BitArray template: iterators perform much better if only a few bits are set. If a BitArray should be filled almost entirely with 1s, and only few 0s, store the inversed bit and use nested loops that will stop on the next "true" value (i.e not set) and skip it. This is the case of shaders that are common to almost every entity.

— Reply to this email directly or view it on GitHubhttps://github.com/adam4813/Sigma/issues/128#issuecomment-32254620 .

adam4813 avatar Jan 14 '14 10:01 adam4813

Catageek, thank you for taking the time to explain. You are very thorough! I have questions but I won't put them here yet until I've had more time to think through them.

wrongu avatar Jan 14 '14 19:01 wrongu

After chatting with Adam and Meisaka, it looks like what I have been describing all along is really just a Scene Graph with the possibility of fancy handling of shaders. So this issue should probably be "make a scene graph"

wrongu avatar Jan 16 '14 03:01 wrongu