Menu

CreatureResource.CreateCreature Slow

EugeneLoza
2017-05-29
2017-06-06
  • EugeneLoza

    EugeneLoza - 2017-05-29

    Hi, Michalis!
    I'm working with adding creatures, but CreatureResource.CreateCreature "interrupts" the world loading. As far as I've understood, the first time I call CreatureResource.CreateCreature it makes some initialization routines (~2 seconds for a knight creature).

    04:52:55> Resources: Resource "Knight" becomes used, preparing
    04:52:57> Resources: Loading resources time: 2.48 seconds
    

    Is there a way to pre-initialize the creature without actually adding it? Before it "became used"?
    I.e. I have an animated load screen, while loading the game data. I'd like to initialize all the creatures during this stage. So there would be no "a few seconds stop" after stopping the load screen and before entering the world (adding the world objects to Window.SceneManager).
    Looks like it should be something like T3DResource.Prepare(const BaseLights: TAbstractLightInstancesList; const GravityUp: TVector3Single); however, I have no idea how to call it correctly :) Namely, where can I get BaseLights for the current world (while the actual world doesn't exist yet). ConfigAlwaysPrepared := true; also doesn't seem to do this (when assigned runtime). Best if this could be made in a thread (without rendering), while the loadscreen is still being rendered animated correctly.
    Or, maybe, I can create a loose SceneManager, construct it properly in a thread (with complete world and creatures) and then add it as / copy it to / use it to replace Window.SceneManager? What is the correct way to do so?

     
    • Michalis Kamburelis

      • The T3DResource.Prepare is indeed the correct routine. You should use the BaseLights and GravityUp from your SceneManager instance, it has functions to get them. If you don't have the SceneManager created yet, a hacky solution right now is to just create a temporary TCastleSceneManager instance only to access it's BaseLights and GravityUp. There is no need to add this scene manager to Window.Controls or set it as Window.SceneManager.

        Hm, I admit I don't like this design myself. It would be simpler if T3DResource.Prepare did not require any parameters... But the current routines to prepare shaders require the BaseLights information (this way they can be prepared for the current state of the headlight). And GravityUp is required to calculate the best radius early.

        Both of these could be probably eliminated, I'll see what I can do. As said, I don't like these parameters myself, but it was easier to implement it initially to require these parameters.

        The "standard" way of preparing creatures/items is when loading a level using TGameSceneManager.LoadLevel, and then everything is perfect and simple. But I know that TGameSceneManager.LoadLevel approach may too simple sometimes, not for every usecase.

      • The ConfigAlwaysPrepared is not for this -- it is only an initial value of AlwaysPrepared, which in turn determines whether to prepare some resources (creatures/items) before any level is loading, regardless if this level declares that this creature/item is used.

        So it does not make the preparations happen earlier. It only means that T3DResourceList.Prepare, and so also TGameSceneManager.LoadLevel, prepare more resources than they would otherwise.

      • Threads are not used --- preparing (among other things) also loads stuff to GPU using OpenGL. So it is an OpenGL-related operation, just like rendering.

        You should also not create your own temporary scene manager in a separate thread, it will cause problems because of the same OpenGL limitation (and because the engine code is really not tested and not coded to be thread-safe). I really advice to use everything from the Castle Game Engine only from the single thread -- this is the only guaranteed way to work now :)

       
  • Michalis Kamburelis

    You can now call just T3DResource.Prepare, without any parameters :)

    The GravityUp parameter was removed, it's not necessary anymore.

    The BaseLights parameter is actually still present, but it can be safely nil now, and it has a default value of nil. On desktop, there's no need to pass it.

    On OpenGLES, passing correct BaseLights (taken from SceneManager.BaseLights) still makes a small optimization, so this parameter still has some use. It allows to "prepare" perfect shaders for OpenGLES rendering, so it makes the start of the level 100% smooth on OpenGES, as everything is prepared. Without it, PrepareResources on OpenGLES can prepare like 99% of the work, but the shaders will still have to be adjusted to the actual headlight parameters at the 1st frame.

     

    Last edit: Michalis Kamburelis 2017-05-30
  • EugeneLoza

    EugeneLoza - 2017-06-06

    Thanks a lot! It's working perfectly! :)

     
    • EugeneLoza

      EugeneLoza - 2017-06-08

      UPD: Yes, "prepare" isn't working in a thread :) Hmm... I should think of something.
      I wonder if its caused by using OpenGl features in TCastleScene.PrepareResources? Theoretically it seems that loading the data (slow and thread-safe) and pre-rendering (quick and not thread-safe) may be separated.

       
      • Michalis Kamburelis

        When preparing, we load data to OpenGL VBO and load texture data to OpenGL, not only pre-render. This is not (always) a quick operation.

        Even before doing OpenGL stuff, we still may load things using some global caches. They are not coded to be thread-safe, for speed (and simplicity of implementation).

        So, as I mentioned above, you really have to use everything from the Castle Game Engine only from the single thread. There is no exception to this rule. I understand that you want to often use CGE code from multiple threads, but it's just really not ready for this (sometimes because I wanted to keep the code simple, sometimes because we use libraries that force this, like OpenGL).

         
  • EugeneLoza

    EugeneLoza - 2017-08-08

    Is it possible to publish T3DResourceAnimation.FSceneForAnimation (e.g. to Protected level)? Or make a procedure to load it manually?
    Looks like preforming FSceneForAnimation := TCastleScene.Create(nil); FSceneForAnimation.Load(URL); (which looks thread-safe to me) may improve loading speed by 30-40% (still can't move everything to a thread, but at least the delay will be a bit shorter).
    (I'm still struggling with choosing the best way to load and display animated 3D models :))

     
    • Michalis Kamburelis

      About threads: Please see my previous comment above in this thread: "you really have to use everything from the Castle Game Engine only from the single thread. There is no exception to this rule.". Things are really, really not ready to be performed in the threads. Sometimes they may happen to work, but that's really a pure accident, it may change from OS to OS, CPU to CPU, or from CGE version to another CGE version. It may also depend on luck, or your particular input (features used by your 3D model).

      In this case, creating and loading TCastleScene can access global AnyNodeDestructionNotifications and X3DCache (as far as I remember). Depending on your models, sometimes the loading may not touch them, but I really prefer not to make guarantees about it.

      When you do loading, you really have to load things in the same thread where you display, for now. It means that the loading cannot show a smooth animation -- I'm sorry. I realize that you have good reasons for using threads (I'm guessing that having a smooth animation under loading is one of these reasons), but I'm sorry -- it's really not supported now.

      The only thing I have in my defense is that many other game engines, like Unity3d, also have this limitation ( http://answers.unity3d.com/questions/180243/threading-in-unity.html ). Also some other libraries, like Lazarus LCL or Delphi VCL, have this limitation. It's just hard to implement (it's hard to make the multi-threading access always safe, and it's even harder to make it safe and not loose speed on useless synchronization when it's not necessary).

      You can of course perform other (unrelated to CGE) calculations in a separate thread. But all access to CGE must be done from a single thread.

      As for making SceneForAnimation public: SceneForAnimation is freed in T3DResourceAnimation.Release, which is supposed to free only things allocated in Prepare. So, I would not want to make SceneForAnimation public, but... I could make another scene, like SceneForAnimationOverride, public. This would be used by T3DResourceAnimation.Scene() implementatation, if non-nil. It's not trivial but possible:

      • one then has to also change Duration into a function that can use SceneForAnimationOverride
      • and it should have a better name than SceneForAnimationOverride :), I don't know yet what.

      In any case, this SceneForAnimationOverride would not be a gate to enable loading this scene in a thread, this is really not supported, I'm sorry...

       
  • EugeneLoza

    EugeneLoza - 2017-08-08

    Thanks a lot!
    Yes, that's exactly the reason I'm trying to dive into threads, and yes, I'm issuing as much locks as possible to prevent any global data (except for animated loadscren) simultaneous access. And the issue is that it works for me (e.g. loading TScenes for tiles in a thread works fine, but not calling TScene.Prepare which fails badly :)). So my logic was to move at least HDD access on TSCene.Load into a thread.
    That said, when I'll return to Overworld I'll have to load and unload models dynamically to/from memory, which would also be extremely welcome if done at least partially in a thread. It looks like sequences that don't require GL features may be relatively safe if ran strictly sequentially (and nothing else is changed in parallel).
    And of course there is absolutely no problem, no need to be sorry. I understand the delay is inavoidable (and actually many games, which had dozens of developers working on them, have the same issue). I just want to make it as small as possible, including by taking some risks :)
    I've came up with a new idea on how to manage animation (I've been playing with resource_animations example for two days and I believe I should make the animations as lightweight as possible (I need only animated model, all the logic is preformed game-side), so maybe publishing will not be ever required - I'll make a special animation container similar to T3DResourceAnimation). Still working on it.
    I hope I'll come up with some results soon :)

     
    • Michalis Kamburelis

      It looks like sequences that don't require GL features may be relatively safe if ran strictly sequentially (and nothing else is changed in parallel).

      The phrase "they may be relatively safe" makes me uneasy :)

      Some usage scenarios (from multiple-threads) may indeed happen to be safe. But they have not been designed to be thread-safe, and I fear that judging "what is OK" by a trial-and-error is a risky approach.

      It is especially risky because errors from incorrect thread usage may manifest only sometimes (only on some CPU or OS, or just randomly, or depending on your data). And when these errors happen -- they will have completely undefined results (usually a crash, but sometimes a random corruption of random variables).

      And, what happens to be thread-safe now may not be thread-safe in a future CGE version. It is really not guaranteed what happens when you use CGE from multiple threads, I don't secure for threads when writing new code, and I don't test usage from multiple threads.

      One day, one of us may spend time to make a new, thread-safe version of the engine, and then (at least a subset of the engine) will be thread-safe. But it will require analyzing the engine code:

      • not only all OpenGL initialization will need to be separated, which is more-or-less possible already,
      • but also access to all global / static variables needs to be secured or just redesigned. All the current "caches" and "notification mechanisms" will need be to checked.

      And from that day the engine will be thread-safe. But not sooner.

      Before this happens, I really advice to "bite the bullet" and just use CGE only from a single thread. I know that it will have disadvantages, e.g. smooth loading screen animation is just not possible. But it's the only way to get a really stable game code, in my eyes, where you will not stumble on random crashes. Risking to use CGE from multiple threads, right now, is something I really not advice.

       
      • EugeneLoza

        EugeneLoza - 2017-08-09

        Yes, I understand that. As I've written somewhere, I've already had some critical crash back some days with damaging of a HDD filesystem :)

         

Anonymous
Anonymous

Add attachments
Cancel