Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • Sep 14 22:52

    turanszkij on master

    updated third person controller… (compare)

  • Sep 14 18:20

    turanszkij on master

    camera constant buffer refactor (compare)

  • Sep 14 16:22

    turanszkij on master

    added chromatic aberration post… (compare)

  • Sep 14 14:53

    turanszkij on master

    sky + readme update (compare)

  • Sep 11 22:53

    turanszkij on master

    new-sky dynamic sky atmosphere updates (compare)

  • Sep 11 22:48

    turanszkij on new-sky

    dynamic sky atmosphere updates (compare)

  • Sep 08 21:55

    turanszkij on new-sky

    new-sky (compare)

  • Sep 04 20:42

    turanszkij on master

    cloud update (compare)

  • Sep 04 19:08

    turanszkij on master

    updated clouds and shadercompil… (compare)

  • Sep 03 20:02

    turanszkij on master

    small refactor (compare)

  • Sep 02 10:59
    turanszkij commented #28
  • Aug 31 19:29

    turanszkij on appveyor

    removed uwp (compare)

  • Aug 31 19:00

    turanszkij on appveyor

    block rdp (compare)

  • Aug 31 18:51

    turanszkij on appveyor

    rdp no password (compare)

  • Aug 31 18:46

    turanszkij on appveyor

    rdp (compare)

  • Aug 31 18:39

    turanszkij on appveyor

    try (compare)

  • Aug 31 18:28

    turanszkij on appveyor

    updated to vs 2019 (compare)

  • Aug 31 18:26

    turanszkij on appveyor

    updated to vs 2019 (compare)

  • Aug 29 19:20

    turanszkij on master

    lightmap fix (compare)

  • Aug 28 21:46

    turanszkij on master

    updated hello world sample (compare)

Matthias Moulin
@matt77hias
Thanks for the answer, János! I think it is also cleaner since you can treat components as values in comparisons and copies without having to handle the entity member variable.
I also assume that the transformation is retrieved the most some_component --> entity --> transform_component (e.g., for populating some model buffer).
I also have some slight incoherent questions about different aspects, if you don't mind me asking all of them :-)
1) If you kill an entity, you need to access all component managers (engine-defined and user-defined), right?
Matthias Moulin
@matt77hias
2) One aspect which keeps me puzzling is enabling/disabling an entity/component. You don't want to destroy and re-create these, but still want to keep them around, but that also means that each entity and component can be possibly disabled during some update cycle. A separate component for such state creates too many indirections since every component operation needs to check. Any thoughts on enabling/disabling?
3) Is it still beneficial to do skinning only once per frame? As far as I understand, skinning only differs with regard to the VS. Pre-computing the skinning once per frame consumes memory while still requiring some basic VS for shadowing, lighting, etc. (memory consumption vs memory accesses)?
Turánszki János
@turanszkij
Yes, for example if you see the serialization, you have to do special consideration for entities for several reasons, so it is also better to keep them separated, so if you would have custom constructors for components, they wouldn't interfere and less error prone.
Transform component can be retrieved as you said, however it is generally better to avoid the map lookup when querying a component manager by entity. Usually I aim to do it once per frame, so mesh instances would receive an index for their transform component, or just copy the transform component into them in some other cases. Then I can do several render passes, and no need for map lookups any more, only direct indexing.
1) Yes, you have to do it, but you can make a system that takes care of that. A system in my opinion is just a function that takes the ComponentManagers that it operates on as arguments.
2) What about having a boolean in the component indicating if it is enabled/disabled? It seems like it is just a specialized behaviour, not needed for every type of component. Better yet, to avoid booleans in structs (because of alignment issues), and have bitfields with packed booleans in them. It even makes it easier to serialize and keep backwards and forward compatibility
Turánszki János
@turanszkij
3) I still prefer skinning in compute shader. Sure you have increased memory requirements, but several benefits too:
  • vertex shader permutations vastly simplified
  • Also use skinned meshes to spawn particles from
  • reuse skinned meshes across several render passes or even frames
  • use async compute
Matthias Moulin
@matt77hias
So with regard to the meshes, you have some extra intermediate set of components which more directly map to GPU buffers? E.g., transform, mesh, material are separate components which are merged in some fourth component per frame that can be directly mapped to a constant buffer (or just populate the buffers directly without storing these intermediates)?
You're right about the boolean, it should indeed be some elementary operation that should be part of specific component interfaces.
Matthias Moulin
@matt77hias
For the skinning, you'll indeed have some extra VS but are the permutations that large for VSs (not sure, though, how many different vertex layouts you handle)? You can indeed reuse the data, but these data is passed to the regular VS which still requires a certain number of cycles for each render pass? The particle benefit is a nice feature, but I guess SO can handle that as well? Anyway thanks for the feedback, animation stuff is not for very soon yet :D
Turánszki János
@turanszkij
Right now, I have the GPUBuffers as part of the MeshComponent and MaterialComponent which are used when rendering the scene. Then the renderer assembles a render queue which is just direct indices to ObjectComponents (which are the mesh instances, kind of a stupid name, but I got used to it) and MeshComponents. Then the renderqueue gets sorted by meshIndex and distance to camera, and the nice thing is that it only sorts indices which directly index into ComponentManagers (these not change after the Scene::Update has been called, so indexing directly is safe for one frame).
For skinning, permutations might not be huge, but still very much easier to manage without a complicated shader build system and one person only. Kind of why I don't have tangent vertex buffers too. The skinning data is not passed to any vertex shader, they just swap the unskinned vertex buffers with the animated ones. Streamout can handle skinning, but the API is clunky and inefficient as it is a geometry shader extension (even if you have it as part of vertex shader, you still compile it as GS as far as I can remember). Then StreamOut stage has ordering requirements which make it less efficient than Compute Shaders. Also, you can use groupshared memory in CS which I also use for skinning.
Matthias Moulin
@matt77hias
So a MeshComponent contains things like VertexBuffer and IndexBuffer. MaterialComponent a material buffer. But what about transform data? I presume you could combine transform and material data (all model data) in one buffer for each instance?
Turánszki János
@turanszkij
You can take a look at wiRenderer::RenderMeshes() in wiRenderer.cpp if you are interested. This function is doing instanced draw calls. The renderqueue is a list of (meshindex, instanceindex) sorted by meshindex. It maps a big gpu buffer, then copies the instance matrices (by indexing into transform component manager) into it. After that it loops through the meshes and calls DrawIndexedInstanced for each and offsetting into the instance buffer.
Matthias Moulin
@matt77hias
What are the invalid entities for?
Turánszki János
@turanszkij
Not sure which particular one you mean, but usually it just means "nullptr" in the ecs world. It is the 0 value. It is a value that can never be a valid entity, and the ComponentManager is unable to contain it. There is a nice feature to this: there is a hashmap optimization that can be done, if there is a designated special value that can never be a key (the hashmap can be flattened into a simple array, and this specific key will be responsible of separating hash buckets). Although I am still using std::unordered_map, hopefully replacing it with yield a big performance increase with minimal code change. I am still contemplating whether to start writing my own, or include a third party fast hash map.
Matthias Moulin
@matt77hias
But without the optimization you mention, you do not use invalid entities?
Isn't the flattening of the hashmap prone to lots of overhead if the the first buckets are too small?
Alternatively, you can assume that all hashed keys are equally likely so that all buckets can reserve the same number of elements.
Then you can use a special value to tag empty slots, or just use a separate array on the stack containing the size (!= capacity) of all the buckets.
Matthias Moulin
@matt77hias
And the capacity of a single bucket is just allocated (size / (sizeof key-value pair) * (number of buckets))
Correction: no size array, since the number of buckets may grow (or shrink) dynamically.
Turánszki János
@turanszkij
I still use invalid entities. When declaring an entity somewhere, I always initialize to invalid entity. The ComponentManager always asserts if it finds an invalid entity. It is generally used when entity is a member of something, it can be checked for invalid entity and I know that it references nothing if it is invalid. Like MeshComponent for example has an armatureID entity as its member. If that is INVALID_ENTITY, that means the mesh is not deformed by armature.
For the hashmap, I haven't tried writing it yet, so I can't really comment. But in this video the guy speaks of the expected speedups of a fast hashmap implementation and comparing it to std::unordered_map: https://youtu.be/M2fKMP47slQ
Turánszki János
@turanszkij
I said that I use invalid entities for specifying unused entities, so that would also mean I am actually not using invalid entities for anything. So clarification: INVALID_ENTITY means that it should not be used because it doesn't point to any components
nemesis567
@nemesis567
Hello
Turánszki János
@turanszkij
@nemesis567 Hi, if you have a question, ask away or just chat. :)
nemesis567
@nemesis567
Thanks
What's the current workflow for creating a new compute shader?
Turánszki János
@turanszkij

@nemesis567 You can just create a new compute shader in the WickedEngine_SHADERS project. When it's compiled, it will be output to WickedEngine/shaders folder by default. Then the simplest way is to load it via the resource manager:

ComputeShader* mycomputeshader = (ComputeShader*)wiResourceManager::GetShaderManager().add("mycomputeshader.cso", wiResourceManager::COMPUTESHADER);

(The wiResourceManager::GetShaderManager() is not necessary, you can use any resource manager object, but this one will be cleared when wiRenderer::ReloadShaders() is called)

You don't have to create the shader in that project though, it can be anywhere, you just have to load the compiled shader object (.cso) file
Furthermore, there are lots of examples how to create shaders in wiRenderer.cpp LoadShaders() function, good luck!
nemesis567
@nemesis567
Thanks
Is this WickedEngine's main communication channel?
Turánszki János
@turanszkij
For now, yes. I'm evaluating Discord and maybe move over as more people will be familiar with it. Do you have an opinion on this?
nemesis567
@nemesis567
Yes, discord is a lot better as everyone doing game dev uses it. Discord channels for other engines are also very active.
And many people stroll from one channel to the other to see what's new on each, all within the same page.
Turánszki János
@turanszkij
The Discord channel has been created: https://discord.gg/CFjRYmE