Found a bug!

hi

I think, i found a bug in the TextureLoader class. When you use the TextureLoader.tf.getTexture(String) method on the same Resource in two Universes it starts taking more and more memory. and finally crashes.

Is this bug already known?

My workaround is to use two instances of the TextureLoader class. But this is of course somewhat slower if you load several Textures twice.

Qudus

That was reported by someone some time ago. Please try with TextureLoader2 and tell if you still have the same problem. (Probably you will).

[quote="<MagicSpark.org [ BlueSky ]>,post:2,topic:27443"]
That was reported by someone some time ago. Please try with TextureLoader2 and tell if you still have the same problem. (Probably you will).
[/quote]
TextureLoader2 doesn’t seem to work the same way as TextureLoader does. I couldn’t find a method to register a texture path. And what is a mipmap. There’s a boolean parameter at the getTexture signature specifying if it is a mipmap or not (I think). I don’t know what to pass.

What is the difference between these two classes except TextureLoader surely is the next evolutionary step I suppose? Should I use TL2 if possible or does TL1 still do things in a somewhat modern way?

By the way: What is the fastest way to load Textures (whithout loosing quality) with making use of the caching mechanism? I use the getTexture(String) method.

when mipmap is activated, it will automatically load your texture also in smaller sizes than your original image, so rendering speed can be improved, if for example your textured quad is far away (so you won’t see the difference).

TextureLoader2 is better than TextureLoader. To load a texture with TextureLoader2 do this:


TextureStreamLocatorFile tslf = new TextureStreamLocatorFile(mytexturedirectory);
TextureLoader2 loader = TextureLoader2.getInstance();
loader.addTextureStreamLocator(tslf);
Texture tex = loader.getTexture("textureName",true); //if you want to have mipmap activated

Thank you. But as you presumed the problem with the memory consumption is the same. Is it hard to fix?

And there’s another problem using the TextureLoader2 class. When I use the Skybox class it always uses the TextureLoader.tf instance. S I will always have to feed texture paths into this one too.

Can you describe what you mean by 2 univeres ? Do you create 2 RenderPeers ? and use the same texture in both ?

This is nolt supported - A texture object can only store the id for one RenderPeer. So if you use it in both it will always recreate it or do other strange thing. Then you really need 2 texture loaders - sorry.

PS: The fastest way to load textures is by using a DDS file and register the TextureStreamLoaderDDS:

TextureLoader2 tl = TextureLoader2.getInstance();
tl.addTextureStreamLoader(new TextureStreamLoaderDDS());
tl.getTexture("brick.dds", true);

Note: this can also load compressed textures

Is it hard to implement shared textures ?

Yes! 2 VirtualUniverses, 2 Locales, 2 Views, 2 CanvasPeers and 2 Canvas3Ds. Did I miss anything? Well, two or more of everything.

I support the idea of somewhat shared textures, too. By using the TextureLoader2 class, I have no chance to make two or more instances. And I would really like to use TL2.

How can I create Compressed Textures to load them again (from a jpeg or png)?

Ok, then we must either change it to one TextureLoader2 instance per CanvasPeer or make the constructor public. The only reasion for a singleton was easier setup (one time registartion of all pathes, plugins, etc…). and to only create one cache (which is the problem here).

As for DDS file creations - there is a tool from nVidia: http://developer.nvidia.com/object/dds_utilities.html which can be used. I also have the DirectX SDK installed which contains a tool to do the conversation.

Must be very useful when you feel lonely… ;D ;D

Then I suggest to put the cache and paths in an object that you could transfer to another TL2 instance if you want (as a copy or a reference by choice). There should be a constructor to pass this object to.

I have an ATI card and work on Linux. Is it usable for me?

[quote="<MagicSpark.org [ BlueSky ]>,post:10,topic:27443"]
Must be very useful when you feel lonely… ;D ;D
[/quote]
This is a very useful contribution - thank you.

The DirectX stuff works only for Windows.
The nVidia stuff is only allowed to be used if you have an nVidia card (they say that in there license).

But you could take a look at o3tc: http://www.ozone3d.net

I have the code for this lying around (but not yet ported to Xith3D):

public class TextureStreamLoaderO3TC implements TextureStreamLoader {
    
    private static final int HEADER_ID = 0x4354334F;
    private static final int O3_TC_RGB_S3TC_DXT1 = 1;
    private static final int O3_TC_RGBA_S3TC_DXT5 = 4;
    private static final int O3_TC_ATI3DC_ATI2N = 16;
    
    /** Creates a new instance of TextureStreamLoaderO3TC */
    public TextureStreamLoaderO3TC() {
    }

    public Texture loadTexture(InputStream in) throws IOException {
        int dataSize = readLE32(in);
        int header = readLE32(in);
        int headerSize = readLE32(in);
        int version = readLE32(in);
        
        if(header != HEADER_ID || headerSize != 12 || version != 1 || dataSize < 12) {
            return null;
        }
        dataSize -= 12;
        
        int chunkHeaderSize = readLE32(in);
        int reserved1 = readLE32(in);
        int chunkDataSize = readLE32(in);
        int reserved2 = readLE32(in);
        int format = readLE32(in);
        int width = readLE32(in);
        int height = readLE32(in);
        int depth = readLE32(in);
        int numMipMaps = readLE32(in);
        byte[] textureName = new byte[128];
        readFully(in, textureName, 0, 128);
        int textureId = readLE32(in);
        
        if(chunkHeaderSize != 168 || width < 1 || height < 1 ||
                width > 4096 || height > 4096 || depth < 0 ||
                depth > 256 || numMipMaps < 1 || numMipMaps > 12 ||
                dataSize < 168) {
            return null;
        }
        
        dataSize -= 168;
        if(chunkDataSize < dataSize) {
            return null;
        }
        
        TextureShader.TextureFormat texFormat;
        TextureShader.TextureFormatHint texFormatHint;
        int blockSize;
        
        switch (format) {
            case O3_TC_RGB_S3TC_DXT1:
                texFormat = TextureShader.TextureFormat.DXT1;
                texFormatHint = TextureShader.TextureFormatHint.DXT1;
                blockSize = 8;
                break;
            case O3_TC_RGBA_S3TC_DXT5:
                texFormat = TextureShader.TextureFormat.DXT5;
                texFormatHint = TextureShader.TextureFormatHint.DXT5;
                blockSize = 16;
                break;
            default:
                return null;
        }
        
        if(depth == 0) {
            Texture2D tex = new Texture2D(texFormat, width, height);
            ByteBuffer[] data = new ByteBuffer[numMipMaps];

            for(int i=0 ; i<numMipMaps ; ++i) {
                int size = ((width+3)/4) * ((height+3)/4) * blockSize;
                if(size > chunkDataSize) {
                    return null;
                }
                chunkDataSize -= size;
                
                data[i] = BufferUtils.createByteBuffer(size);
                readFully(in, data[i]);
                data[i].flip();

                width = Math.max(1, width >> 1);
                height = Math.max(1, height >> 1);
            }
            
            if(chunkDataSize > 0) {
                return null;
            }
            
            tex.setFormatHint(texFormatHint);
            tex.setData(data);
            return tex;
        } else {
            throw new UnsupportedOperationException();
        }
    }
    
    private void readFully(InputStream in, byte[] b, int off, int count) throws IOException {
        while(count > 0) {
            int read = in.read(b, off, count);
            if(read <= 0) {
                throw new EOFException();
            }
            off += read;
            count -= read;
        }
    }
    
    private int readLE32(InputStream in) throws IOException {
        byte[] tmp = new byte[4];
        readFully(in, tmp, 0, 4);
        return ((tmp[3] & 255) << 24) |
                ((tmp[2] & 255) << 16) |
                ((tmp[1] & 255) << 8) |
                ((tmp[0] & 255));
    }

    private void readFully(InputStream in, ByteBuffer buf) throws IOException {
        byte[] tmp = new byte[4096];
        while(buf.remaining() > 0) {
            int read = in.read(tmp, 0, Math.min(tmp.length, buf.remaining()));
            if(read <= 0) {
                throw new EOFException();
            }
            buf.put(tmp, 0, read);
        }
    }
}