Textures missing / white objects on 2nd start of DisplaySystem

Hi there! :slight_smile:



I’m pretty new to jME, and currently developing my first “simulation”. I have already written a few lines of code and most things are running as they should or at least as expected! :wink: But there is this annoying bug, and I think I wasted enough time stepping through the code again and again - just because I used some method/class in the wrong way. So: Please, enlighten me! :smiley:



Idea:

I want to have a Swing-Panel to control my simulation in an external window.



Problem:

The first time I start my program, everything is fine. I hit a button on the Swing-GUI, the jME-Window appears and the simulation starts. I can pause, resume and stop it.

But when I stop it (simulation stopped, Display closed), and then try to start a new simulation (new Display…), I got to wait half a minute while the new jME-Window doesn’t react, and then the simulation starts, but there are not textures, no colors (everything is white), and even the HUD (com.jme.scene.Text) is just white!



My thoughts:

The whole problem seemed to be a renderer error, so I focused on how to deinitialize the DisplaySystem and Renderer, but with no success. I do it exactly the same way it is done in the BaseSimpleGame#cleanup() (even copied some code :P).

My second thought was that there is a problem with the Textures or the TextureManager (although the colors are missing, too!!?), but I wasn’t able to found one. Every piece of the model got its Texture…



Some code:



How I’m closing the simulation:


   @Override
   protected void cleanup() {
      
      [...]
      
      TextureManager.doTextureCleanup();
      KeyInput.destroyIfInitalized();
      MouseInput.destroyIfInitalized();
      JoystickInput.destroyIfInitalized();
      
      if (AudioSystem.isCreated()) {
         AudioSystem.getSystem().cleanup();
      }
      
      // Visualization
      if (display != null) {
         
         if (display.getRenderer() != null) {
            try {
               display.getRenderer().cleanup();
            } catch (Exception exc) {
               log.error("Exception ocurred while cleaning up renderer!", exc);
            }
         };
         
         display.reset();
         
         display.close();
      }
   }



How I'm initializing the simulation:


   @Override
   protected void initSystem() {

      [...]
      
      // Create window!
      display = DisplaySystem.getDisplaySystem(getNewSettings().getRenderer());
      display.createWindow(width, height, depth, freq, fullscreen);

      display.setTitle("SimCore - PreAlpha");
//      display.setVSyncEnabled(true);      // WARNING: VERY expensive!
      
      display.getRenderer().setBackgroundColor(ColorRGBA.black);
   }




Version:
jME: rev 5001
jMEPhysics2: rev 214

Question:
What am I doing wrong? What are the possible reasons for that kind of behaviour?

I hope my idea is not that abstruse, and I'm really looking forward to your solutions/hints/comments/questions!!! :)

Are you calling jme functions directly in the methods that are created for your swing buttons? Then it cannot work, you are accessing data that is used on the OpenGL thread from the Swing Event Dispatcher Thread. This is certain to cause errors. Please see this post about using the GameTaskQueueManager to avoid threading problems.



Cheers,

Normen

Hi Normen, thanks for your fast answer!



…but I'm not sure I got it right. :wink:



1.: No, I have a layer that performs requests from the Swing-GUI in separate Threads, to avoid this kind of typical Swing-issues.



2.: I read the posts you linked, and as I understand it, cleanup should be done by the same Thread that does the "game" loop, with update(float) and render(float) and so on. Is that what you mean by "OpenGL"-Thread? :open_mouth: Or is that another, special Thread? In fact I start my simulation by simply calling something like


   @Override
   public void start() {
      
      changeState(ESimEngineState.RUNNING);

      if (jmeSimLoop != null) {
         jmeSimLoop.interrupt();
      }
      
      jmeSimLoop = null;
      jmeSimLoop = new Thread(new Runnable() {
         @Override public void run() {SimEngine.super.start();}      
      }, "jmeSimLoop");
      jmeSimLoop.setPriority(Thread.MAX_PRIORITY);
      jmeSimLoop.start();
   }



Correct/ok/bad/super-dump?^^

This "jmeSimLoop" actually does the cleanup() in my implementation, caused to quit the normal game-loop by a simple flag, and I'm getting no errors or anything...? (Yes, I am using my own implementation of AbstractGame )


Hope I got the point...

I mean that by "opengl thread", but creating even more threads will surely not bring relief to your problem.



You have to create callables and register them in the update queue of the gametaskmanager:



(unchecked pseudo-code:)


Callable call=new Callable(){
      public Object call(){
          //modify
      }
};
GameTaskQueueManager.enqueue(call);



This way the code inside the call method will run on the opengl thread.

Cheers,
Normen
I mean that by "opengl thread", but creating even more threads will surely not bring relief to your problem.


Okay, perfect! :)

This way the code inside the call method will run on the opengl thread.


Okay, but as I understand the GameTaskQueueManager, I have to use StandardGame or BaseSimpleGame, which actually execute its queues in their game-loop. I don't use it, but make my game-loop cleanup() itself.

AbstractGame implementation:

      try {
         // main loop
         while (!finished && !display.isClosing()) {
            
            startCycle();
            InputSystem.update();
            update(-1.0f);
            render(-1.0f);
            display.getRenderer().displayBackBuffer();
            endCycle();
            Thread.yield();
         }      
      } catch (Throwable t) {         
         log.error("Exception in game loop", t);
         handleError(t);
      } finally {
         lastCycleComplete = true;
         
         log.info("Closing Simulation...");
         
         display.reset();
         cleanup();
         
         log.info("Simulation closed.");
      }



Please tell me if I got you wrong or anything... but at this point I don't think that the Thread-thing is causing my problem... :oops:

Edit: don't think - typo

I do not know how your complete software looks and I still find the thread creating strange, but if you want to modify any object that is used in the scene (and is thus used by the update() loop) you will have to do so on the same thread that calls the update() method.

So if your "jmeSimLoop" is not the one that calls update() and render() for openGL, it should not modify any objects or call methods of the game like destroy() or anything. If you dont have a queue you will have to build one yourself (or switch to BaseGame etc.).



Cheers,

Normen



Edit: threading is surely a problem when you use swing buttons. From what you showed me until now I would not know how that call could possibly be executed on the opengl thread.

Thanks for your explanation! So I'll use GameTaskQueueManager to modify objects later on, which I'm actually not doing right now.



But isn't there any other possible reason for my problem with the second instantiation (at runtime) of the simulation? Because till now I'm following your advice that one Thread should call update(), render() and cleanup(), so it can't be the cause for my problem.





Any other ideas are greatly appreciated!

Yes, one thread has to call these methods but its also important that it is the only one modifying any object that is used in the scenegraph or loading textures or anything.



Imagine this: The opengl thread reads the location of an object and wants to display it. Now you press your button in the GUI and the Swing thread goes about to change that location at the same time. When the Swing thread has only set two of the three coordinates, the opengl thread reads them all and gets a wrong location. This is the most simple example and it wont produce any problems but a wrong location but when you start loading textures or adding stuff to your scene from another thread it will surely go wrong / crash.



So again I dont know what else you do in your software. If you do everything on one thread and the objects are all white when you have placed them the second time maybe your lights are not set up correctly the second time or you do not call updateRenderState() on your root node?



Cheers,

Normen

Hi Normen! :slight_smile:



After two weeks full of other bugs and features (your hint with the GameTaskQueue came handy, btw! ;)), I finally returned to this disturbing error. And after carefully re-reading your posts I have another (hopefully the last???) question:



Is there only one OpenGL-Thread per time (to avoid concurrency-problems)? Or is it unique during the whole runtime of the application?



Because until now I start a new ("OpenGL"-)Thread when trying to start the 2nd simulation… after I killed the one that served the first!

You cannot start two render threads in jme2. You can use GameStates but they will run on the same thread successively.

The thread is created when you call game.start() and destroyed when you call game.destroy().

Using two DisplaySystems simultaneously or destroying/recreating them will lead to white textures… There's not much you can do about this, except reset the IDs for the textures (there might be a method in TextureManager to do that).

Thanks for your help, first problem is solved!


There's not much you can do about this, except reset the IDs for the textures (there might be a method in TextureManager to do that).


This is exactlly what I thought TextureManager.doTextureCleanup() would do, but somehow that wasn't enough... TextureManager.clearCache() does it, though! :)


But Colors are still white (sometimes black, maybe it is the light...?)
Text had been the same. Since I'm using Text.resetDefaultFontTextureState() the text is readable on 2nd start, but letters are not clear... Please have a look the image, it shows both problems (goal posts should be blue)!

PS: To avoid misunderstandings (Sorry, my English is not the best, so maybe I made myself not clear enough :-o): I don't want to start render-threads simultaneously but successively.

Edit: Removed double image

Not sure what the problem is, it seems like text is being reset incorrectly so there's a bug somewhere. For the goal posts, how are you setting the color? Using spatial.setSolidColor or using the MaterialState?

It might be that you need to invalidate the render state:

for (RenderState rs: Renderer.defaultStateList){
    rs.apply();
}

Problem finally solved! 8)


It might be that you need to invalidate the render state


That exactly caused these errors! I solved it by the time you posted that, Momoko_Fan... by simply try-and-error calling nearly every method I found in the Renderer/RenderContext... ;) and RenderContext<?>.invalidateStates() seem to work for me!

Again, the whole code-snippet I use to cleanup my simulation, maybe someone else has similar problems:

TextureManager.doTextureCleanup();
TextureManager.clearCache();

Text.resetDefaultFontTextureState();

KeyInput.destroyIfInitalized();
MouseInput.destroyIfInitalized();
JoystickInput.destroyIfInitalized();

if (AudioSystem.isCreated()) {
   AudioSystem.getSystem().cleanup();
}

// Visualization
if (display != null) {
   
   Renderer renderer = display.getRenderer();
   if (renderer != null) {
      try {
         renderer.cleanup();
      } catch (Exception exc) {
         log.error("Exception ocurred while cleaning up renderer!", exc);
      }
   };
   
   RenderContext<?> context = display.getCurrentContext();
   if (context != null) {
      context.invalidateStates();
   }
   
   display.reset();
   display.close();
}



Thank you both very much for spending your time on my problem!!! I'll certainly come back with my next bugs... ;)

Hi there,



I'm having almost the exact same problem - on the second start of the system, all textures are white.



I followed the solution in the last post, using the RenderContext<?>.invalidateStates() .  It works for the text display, but the rest of my textures are still white.



So my question is, is there more than one RenderContext that I must invalidate?



Thanks

Ha… solved my own problem.  It's not jME's fault, it's mine.  I cached my own textures on top of jME (I rolled my own engine before, this is part of a large effort to re-implement my app with jME). 



So all I had to do is clear my own cache  :stuck_out_tongue:



Thanks Terraner for a great solution!