TextureManager.loadTexture... a faster alternative?

I'm displaying a movie as a texture of a trimesh. I basically wrote some code that saved the movie's frames as jpegs and now I'm

trying to use those jpegs for my animation. I first load the first image through TextureManager.loadTexture in Basegame's init method and things go fine.

In the update method (BaseGame) however when I try to load the remaining images through TextureManager.loadTexture( … /jpegfile.jpeg) it takes

the program about 10 seconds to move to the next image… very very slow. In order to speed things up i decided to create an ArrayList of Textures

like this:  ArrayList<Texture> textures = new ArrayList<Texture>();  textures.add(TextureManager.loadTexture('next jpeg file'));

and  load the textures when the program starts … but boy, after loading the 9th texture the program crashes:



java.lang.OutOfMemoryError: Direct buffer memory

at java.nio.Bits.reserveMemory(Bits.java:633)

at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:95)

at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:288)

at org.lwjgl.BufferUtils.createByteBuffer(BufferUtils.java:61)

at com.jme.scene.state.lwjgl.LWJGLTextureState.apply(Unknown Source)

at com.jme.util.TextureManager.loadTexture(Unknown Source)

at com.jme.util.TextureManager.loadTexture(Unknown Source)

at PlasmaWorld.initSystem(PlasmaWorld.java:154)

at com.jme.app.BaseGame.start(Unknown Source)

at PlasmaWorld.main(PlasmaWorld.java:56)



Is there any way of doing this efficiently? What about using BufferedImages? Here's the troublemaker

(the last for loop)



protected void initSystem() {

//store the properties information

width = properties.getWidth();

height = properties.getHeight();

depth = properties.getDepth();

freq = properties.getFreq();

fullscreen = properties.getFullscreen();



try {

display = DisplaySystem.getDisplaySystem(properties.getRenderer());

display.createWindow(width, height, depth, freq, fullscreen);

cam = display.getRenderer().createCamera(width, height);

} catch (JmeException e) {

e.printStackTrace();

System.exit(1);

}



//set the background to black

display.getRenderer().setBackgroundColor(ColorRGBA.black);



//initialize the camera

cam.setFrustumPerspective(45.0f, (float)width / (float)height, 1, 1000);

Vector3f loc = new Vector3f(0.0f, 0.0f, 45.0f);

Vector3f left = new Vector3f(-1.0f, 0.0f, 0.0f);

Vector3f up = new Vector3f(0.0f, 1.0f, 0.0f);

Vector3f dir = new Vector3f(0.0f, 0f, -1.0f);

// Move our camera to a correct place and orientation.

cam.setFrame(loc, left, up, dir);

/** Signal that we've changed our camera's location/frustum. /

cam.update();



/
* Get a high resolution timer for FPS updates. */

    timer = Timer.getTimer(properties.getRenderer());



      display.getRenderer().setCamera(cam);



KeyBindingManager.getKeyBindingManager().set("exit",KeyInput.KEY_ESCAPE);

/*this stuff is out of ordered; not ordered numerically

jpegs = new ArrayList<String>();

File[] files_contents = (new File("/Users/miguelmenchu/Desktop/Source Code/Eclipse Workspace/PlasmaWorld")).listFiles();

for(int i=0; i < files_contents.length; i++){

File file = files_contents;

if (file.toString().contains(".jpg")){

System.out.println(file.toString());

jpegs.add(file.toString());}}             

*/

textures = new ArrayList<Texture>();

for(int i=0; i <= 35950; i+= 50){

System.out.println("Loading image: RSSVisualizer." + i + ".jpg");

Texture mytexture = TextureManager.loadTexture( PlasmaWorld.class.getClassLoader().getResource(new String("RSSVisualizer.
" + i + ".jpg").toString()),Texture.MM_LINEAR_LINEAR,Texture.FM_LINEAR);

textures.add(mytexture);

TextureManager.clearCache();

}



   

}



Thanks.

I forgot to mention I've also tried what user TORA suggested:



Texture t = TextureManager.loadTexture(bufferedImage, Texture.MM_LINEAR,Texture.FM_LINEAR, 1.0f, true);

t.getImage().setType(com.jme.image.Image.RGB888_DXT1);

TextureKey tk = new TextureKey(null,Texture.MM_LINEAR, Texture.FM_LINEAR,1.0f, true, com.jme.image.Image.RGB888_DXT1);

t.setTextureKey(tk);



using BufferedImage. Unfortunately setTexturekey method is not reconized in my IDE (Eclipse complains) and the Texturekey constructor

complains and suggests i get rid of the last parameter to match the constructor's definition. I'm sure I have the latest version… any clues?



thnkx.

Solved. Add the following when compiling:



-XX:MaxDirectMemorySize=1024m 



(In Eclipse "run" and edit the run profile's 'VM parameters' to include this).


Do you still need textures after they are used?  You might decrease memory usage by blowing them completely away after they are used. (Delete from card and manager cache, etc.)

Also, as a mental note for the devs…  for something like this, it would be helpful if we wrote a way to reuse a texture, giving it new image contents with glTexSubImage2D.  This would really save on both system and video memory.

yep, I get rid of textures (actually the Images) after i no longer need them… the thing is that I need at least a significant number of them to be in memory

(remember I'm playing a movie) and that number is big enough to cause outofmemory errors. Passing those arguments when running the app seems to

have saved me, for now.



cheers.

I realize you are playing a movie, however I recently wrote a movie player that sounds similar and I use only 1 texture for the whole process.  shrug

did you treat frames as subareas of the texture? Then you probably had a huge texture.



If not then it's certainly something that i'm doing wrong. Could you enlighten me as to how accomplish such thing? Thanks.

No I used one texture that was the size of one video frame.  (in my case, 512x256)  Each time I wanted to update the texture for the next frame of video, I used glTexSubImage2D to write the next frame into the existing texture.  My code looks like:


        readFrame(ri); // grabs the data from somewhere - in my case, compressed memory
        convertFrameToRGB(ri); // basically fills the ByteBuffer ri.frameBuffer with data

        GL11.glBindTexture(GL11.GL_TEXTURE_2D, t.getTextureId());
        GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0, 0,
                ri.width, ri.height, GL11.GL_RGB,
                GL11.GL_UNSIGNED_BYTE, ri.frameBuffer);



Edit: Like I mention above in an aside to the devs, this trick requires direct access to lwjgl...  it would be nice to add a jme only way to do it.

If I may ask, how did you load your data to memory? Did you load from a movie file?



Just in case you happen to know; is there a faster way to load a jpeg to a texture (jpeg -> jme Image ->texture) instead of

using textureManager… takes centuries to load :confused:



thanks for sharing your code.

I loaded from an RoQ movie file.  As for your JPEG question, sorry, no idea.

This is a related question I was going to ask anyway, but I think it fits in here:

Is it possible to take an Image's ByteBuffer, write it to disk, and re-load it into another newly created Texture? Would the ByteBuffer be compressed by default? My goal is to save image format conversion, and mipmap generation, at load time.

I tried the above, but don't seem to get texture compression (probably because I used a BufferedImage to create the texture, I think renanse was going to fix texture compression for that) and mipmaps. I can post my custom texture writing method, if required, it's about 20 lines of code, but all I am doing in there is storing Image size, type, mipmap sizes, and the ByteBuffer.

Or maybe, I can even utilize the new model serialization code from mojo, but I don't have a clue how to do that. Also, is there an option to turn off compression for the new serialization system? The built in java zip compression feels rather slow to me.

I've found the speed of gzipping the stream to be pretty minimal, especially compared to transfer time across a network of the uncompressed version, but I could see where you could use the unzipped version locally or something like that.  It shouldn't be too hard to add a flag for that, but just remember you'll then have to keep track of which files are compressed are which are not…

My updates allowing awt Image Textures to make use of texture compression and caching is now in cvs.



Also, as to your question about ByteBuffer, the texture is compressed on the card, not by jME, so your ByteBuffer will never hold the compressed version of the texture (or the mipmaps either.)  If you want to do that, make your own DDS file.