Threading and loading Textures

Anything libgdx related goes here!

Threading and loading Textures

Postby zortness » Wed Jul 27, 2011 6:21 pm

I'm attempting to start loading Textures (and TextureRegions, Animations) in the background, then switch to my main game screen once completed.

The issue I'm seeing is that the Texture of the first item I load (especially if it's a larger file) ends up being used for all subsequent definitions, so I'll see the proper sized, oriented, and positioned TextureRegions drawn on the screen with the wrong Texture. It seems like this may be an issue with FileHandle not being thread-safe, but it might just be something stupid that I'm doing on my part. So this is my custom Texture loading method (our textures and regions are defined in a JSON file that I read with Jackson).

Code: Select all
public synchronized TextureMetaData2d loadTexture(String textureName) throws Exception
   {
      TextureMetaData2d data = new TextureMetaData2d();
      FileHandle fh = Gdx.files.internal(rootPath + textureName + ".png");
      Texture t = new Texture(fh);
      data.name = textureName;
      data.texture = t;
      
      // look for the JSON metadata definitions
      fh = Gdx.files.internal(rootPath + textureName + ".json");
      if (fh.exists())
      {
         ObjectMapper mapper = new ObjectMapper();
         JsonNode root = mapper.readTree(fh.read());
         if (root != null)
         {
            parseMetadata(root, data);
         }
      }
      return data;
   }


The process here is that this method gets called from 2 threads: the loading screen in the main thread with the loading background image, and another loader thread with several different file names in a row. The result is each subsequent Texture data appears to only contain the data from the first call (the loading screen background image). My first inclination is that there's something in FileHandle that is not thread-safe, or I need to lock on something that I'm not seeing.

Any help is appreciated, thanks.
zortness
 
Posts: 5
Joined: Wed Jul 27, 2011 6:05 pm

Re: Threading and loading Textures

Postby AndroidGuy » Wed Jul 27, 2011 10:00 pm

I recently ran into problems with touching textures on any thread other than the glthread. I spent hours trying to trace down the problem with glError and it basically came down to the issue that if you call gl calls, they must be on the glthread. When you create a Texture, the texture is uploaded to GL on the thread that you create the texture on and it cannot guarantee the gl context is available. My issues were specifically with the textureid's that opengl was creating were being reused and overwriting texture data, like you mention.
AndroidGuy
 
Posts: 10
Joined: Fri Jul 15, 2011 4:06 pm

Re: Threading and loading Textures

Postby zortness » Wed Jul 27, 2011 10:24 pm

Very interesting. I'm digging through the GDX code now to see if there's a way to synchronize on the GL thread.
zortness
 
Posts: 5
Joined: Wed Jul 27, 2011 6:05 pm

Re: Threading and loading Textures

Postby mzechner » Thu Jul 28, 2011 12:32 am

No, you can access OpenGL ES only from a single thread. To be exact: only from the thread to so called EGL context is current on. You can make that context current in a separate thread other than the render thread libgdx sets up for you.

However...

that is horribly broken on most devices for whatever reason and i'd strongly advice against it.

If you want to have asynchronous loading you can load the Pixmaps, then inform the rendering thread that they are ready to be used via new Texture(Pixmap pixmap). If you want those Textures to be managed you can wrap the Pixmap inside a TextureData implementation.

It's not really a limitation of libgdx, but of OpenGL ES and EGL on Android i'm afaid.
mzechner
Site Admin
 
Posts: 4879
Joined: Sat Jul 10, 2010 3:50 pm

Re: Threading and loading Textures

Postby zortness » Fri Jul 29, 2011 9:05 pm

Friggin Sweet.
Ok, using a Pixmap to load the png in the background thread, and then converting into a finalized Texture in the main thread works perfectly. Awesome!

Btw, I use the SVN repo to build everything, and there appears to be some issues with the latest Pixmap and Gdx2DPixmap... I keep getting "unable to load Pixmap" errors if I update to the latest v2440, had to switch those 2 files back to v1777.
zortness
 
Posts: 5
Joined: Wed Jul 27, 2011 6:05 pm

Re: Threading and loading Textures

Postby mzechner » Fri Jul 29, 2011 9:48 pm

Ah sorry, I have to update the binaries in SVN. I changed a few things in the pixmap C backend. Will do so in a bit.
mzechner
Site Admin
 
Posts: 4879
Joined: Sat Jul 10, 2010 3:50 pm

Re: Threading and loading Textures

Postby stdout » Fri Sep 02, 2011 3:08 am

Great!!
I have been experiencing the same problem, and spent whole day trying.

How about loading fonts?
can I load BitmapFont.BitmapFontData data in a thread first then call
BitmapFont(BitmapFont.BitmapFontData data, TextureRegion region, boolean integer)

but we have data and pic right.. so we make something like:

Code: Select all
   private ArrayList<BitmapFont.BitmapFontData> fontData;
   private ArrayList<TextureRegion> fontTextures;

   public synchronized void loadFontData() {
      fontData = new ArrayList<BitmapFont.BitmapFontData>(4);
      fontTextures = new ArrayList<TextureRegion>(4);

      fontData.add(new BitmapFont.BitmapFontData(Gdx.files.internal("fonts/papyrus.fnt"), false));
      fontData.add(new BitmapFont.BitmapFontData(Gdx.files.internal("fonts/recordFont.fnt"), false));
      fontData.add(new BitmapFont.BitmapFontData(Gdx.files.internal("fonts/currentHeight.fnt"), false));
      fontData.add(new BitmapFont.BitmapFontData(Gdx.files.internal("fonts/dollar.fnt"), false));

      fontTextures.add(new TextureRegion(new Texture(Gdx.files.internal("fonts/papyrus.png"))));
      fontTextures.add(new TextureRegion(new Texture(Gdx.files.internal("fonts/recordFont.png"))));
      fontTextures.add(new TextureRegion(new Texture(Gdx.files.internal("fonts/currentHeight.png"))));
      fontTextures.add(new TextureRegion(new Texture(Gdx.files.internal("fonts/dollar.png"))));

   }

   public void loadFont() {

      font_papyrus = new BitmapFont(fontData.get(0), fontTextures.get(0), false);
      font_record = new BitmapFont(fontData.get(1), fontTextures.get(1), false);
      font_currentHeight = new BitmapFont(fontData.get(2), fontTextures.get(2), false);
      font_dollar = new BitmapFont(fontData.get(3), fontTextures.get(3), false);
         }


but I am getting: Invalid memory access of location 0x318 rip=0x7fff8953d328

Code: Select all
Process:         java [11219]
Path:            /usr/bin/java
Identifier:      com.apple.javajdk16.cmd
Version:         1.0 (1.0)
Code Type:       X86-64 (Native)
Parent Process:  eclipse [9632]

PlugIn Path:       /var/folders/*/libjogl.jnilib
PlugIn Identifier: libjogl.jnilib
PlugIn Version:    ??? (???)

Date/Time:       2011-09-02 07:58:32.819 +0400
OS Version:      Mac OS X 10.7 (11A511)
Report Version:  9

Interval Since Last Report:          52693 sec
Crashes Since Last Report:           25
Per-App Interval Since Last Report:  151148 sec
Per-App Crashes Since Last Report:   25
Anonymous UUID:                      EC46525C-EC8F-4898-B88A-737AB94B243D

Crashed Thread:  24  Java: loader

Exception Type:  EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000318

VM Regions Near 0x318:
-->
    __TEXT                 000000010228d000-0000000102295000 [   32K] r-x/rwx SM=COW  /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/bin/java

Application Specific Information:
objc[11219]: garbage collection is OFF
 
Java information:
 Exception type: Bus Error (0xa) at pc=7fff8953d328
 
 Java VM: Java HotSpot(TM) 64-Bit Server VM (20.1-b02-383 mixed mode macosx-amd64)
 
Current thread (7f9da1801800):  JavaThread "loader" [_thread_in_native, id=294060032, stack(111770000,111870000)]
Stack: [111770000,111870000]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  com.sun.opengl.impl.GLImpl.glGenTextures0(ILjava/lang/Object;I)V+0
j  com.sun.opengl.impl.GLImpl.glGenTextures(ILjava/nio/IntBuffer;)V+21
j  com.badlogic.gdx.backends.jogl.JoglGL20.glGenTextures(ILjava/nio/IntBuffer;)V+6
j  com.badlogic.gdx.graphics.Texture.createGLHandle()I+28
j  com.badlogic.gdx.graphics.Texture.create(Lcom/badlogic/gdx/graphics/TextureData;)V+1
j  com.badlogic.gdx.graphics.Texture.<init>(Lcom/badlogic/gdx/files/FileHandle;Lcom/badlogic/gdx/graphics/Pixmap$Format;Z)V+72
j  com.badlogic.gdx.graphics.Texture.<init>(Lcom/badlogic/gdx/files/FileHandle;)V+4
j  com.khonji.domdom.Assets.loadFontData()V+155
j  com.khonji.domdom.Loader.load()V+36
j  com.khonji.domdom.Loader.run()V+1
j  java.lang.Thread.run()V+11
v  ~StubRoutines::call_stub
 
Java Threads: ( => current thread )
=>7f9da1801800 JavaThread "loader" [_thread_in_native, id=294060032, stack(111770000,111870000)]
  7f9da31c1800 JavaThread "DestroyJavaVM" [_thread_blocked, id=45481984, stack(102a60000,102b60000)]
  7f9da18f3000 JavaThread "Thread-4" [_thread_blocked, id=237879296, stack(10e1dc000,10e2dc000)]
  7f9da18f8800 JavaThread "Java2D Disposer" daemon [_thread_blocked, id=226230272, stack(10d6c0000,10d7c0000)]
  7f9da19a5800 JavaThread "AWT-EventQueue-0" [_thread_in_Java, id=222834688, stack(10d383000,10d483000)]
  7f9da3113000 JavaThread "AWT-Shutdown" [_thread_blocked, id=171122688, stack(10a232000,10a332000)]
  7f9da3112000 JavaThread "AWT-AppKit" daemon [_thread_in_native, id=2017073504, stack(7fff6168d000,7fff61e8d000)]
  7f9da18ab000 JavaThread "Low Memory Detector" daemon [_thread_blocked, id=164175872, stack(109b92000,109c92000)]
  7f9da18aa800 JavaThread "C2 CompilerThread1" daemon [_thread_blocked, id=163115008, stack(109a8f000,109b8f000)]
  7f9da18a9800 JavaThread "C2 CompilerThread0" daemon [_thread_blocked, id=162054144, stack(10998c000,109a8c000)]
  7f9da18a9000 JavaThread "Signal Dispatcher" daemon [_thread_blocked, id=160993280, stack(109889000,109989000)]
  7f9da18a8000 JavaThread "Surrogate Locker Thread (Concurrent GC)" daemon [_thread_blocked, id=159932416, stack(109786000,109886000)]
  7f9da1892800 JavaThread "Finalizer" daemon [_thread_blocked, id=157032448, stack(1094c2000,1095c2000)]
  7f9da1891800 JavaThread "Reference Handler" daemon [_thread_blocked, id=155971584, stack(1093bf000,1094bf000)]
Other Threads:
  7f9da188d000 VMThread [stack: 1092bc000,1093bc000] [id=154910720]
  7f9da300d800 WatcherThread [stack: 109c95000,109d95000] [id=165236736]
 
VM state:not at safepoint (normal execution)
VM Mutex/Monitor currently owned by a thread: None
 
Heap
 par new generation   total 19136K, used 13274K [7f3000000, 7f44c0000, 7f44c0000)
  eden space 17024K,  77% used [7f3000000, 7f3cf6868, 7f40a0000)
  from space 2112K,   0% used [7f40a0000, 7f40a0000, 7f42b0000)
  to   space 2112K,   0% used [7f42b0000, 7f42b0000, 7f44c0000)
 concurrent mark-sweep generation total 63872K, used 0K [7f44c0000, 7f8320000, 7fae00000)
 concurrent-mark-sweep perm gen total 21248K, used 15516K [7fae00000, 7fc2c0000, 800000000)
 
Code Cache  [102cd8000, 102f49000, 105cd8000)
 total_blobs=657 nmethods=49 adapters=570 free_code_cache=49581760 largest_free_block=14592
 
Virtual Machine Arguments:
JVM Args: -Dfile.encoding=MacRoman
Java Command: com.khonji.domdom.domdomDesktop
Launcher Type: SUN_STANDARD
Physical Memory: Page Size = 4k, Total = 1792M, Free = 13M
 
stdout
 
Posts: 8
Joined: Thu Jul 28, 2011 7:59 pm

Re: Threading and loading Textures

Postby chuckDiesel » Fri Sep 02, 2011 4:09 pm

stdout, i'm dealing with the same issues and find that it's next to impossible to touch anything associated with managed code or opengl in a different thread. It doesn't seem possible to do in a way that is consistent across desktop and mobile devices. having said that, i'm sure there are workarounds but as mario has said before, it's very touchy to try and coordinate work across different threads and behavior is different on different devices so it probably will not be supported here.

i personally find this a big obstacle in development as i prefer to offset graphics processing work like you mentioned (textures, fonts, etc) to another thread but im constantly running into memory access issues in dealing with touching box2d, gdx-natives, etc so if there's a solution, i would love to hear about it.

for example, when i load a new screen, i'm trying to build the physics scene and do some of the texture/audio loading in a new thread before the screen loads. once these assests are finished processing, i want to show the new screen without any delays in rendering/audio playing. if i try to push everything all at once on the main thread, the initial screen rendering and audio will stutter for a few moments (even on desktop) and then will settle down but it looks bad and isn't sufficient for me.

maybe mario has some other thoughts on this but i realize that opengl is state driven and the addition of native code probably doesn't help with synchronization issues but multithreading to me is necessary in terms of effective development. it just seems odd to use one thread for everything and again, i understand it's probably next to impossible to implement given the support for desktop and mobile devices but if i find a way around it, i will post my outcomes here. as well, i would always welcome feedback on the outcomes of others.
chuckDiesel
 
Posts: 29
Joined: Sun Aug 07, 2011 4:39 pm

Re: Threading and loading Textures

Postby NateS » Sat Sep 03, 2011 5:13 am

Look at the new AssetManager class, it does everything you need.
NateS
 
Posts: 1980
Joined: Fri Nov 12, 2010 11:08 am

Re: Threading and loading Textures

Postby stdout » Sat Sep 03, 2011 9:21 am

Nates: I wish if there would be some comments/documentations. Its a bit cryptic and more than I need. I am trying to dig in and learn a bit.
I can find a test file here: http://code.google.com/p/libgdx/source/ ... rTest.java

An Asset Manager requires a TextureLoader. TextureLoader Needs a FileHandleResolver, but which file? seems they are using some ResolutionFileResolver, but I don't see the link, why resolution! ok, then we load all files calling load() which is nice. After that they update the manager, which returns true if finished. Then we have diagnosed boolean variable which is always false (seems a mistake). Then we have invalidateTexture() method, which I don't understand.
stdout
 
Posts: 8
Joined: Thu Jul 28, 2011 7:59 pm

Next

Return to Libgdx

Who is online

Users browsing this forum: Google [Bot] and 1 guest

cron