LWJGLRenderer Bug

LWJGLRenderer has bug. Lines :





prevTextureNumber = ts.getNumberOfSetTextures() < TextureState.getNumberOfFixedUnits() ?

ts.getNumberOfSetTextures() : TextureState.getNumberOfFixedUnits();



should be replaced by :



prevTextureNumber = ts.getNumberOfSetTextures() < TextureState.getNumberOfFragmentTexCoordUnits() ?

                ts.getNumberOfSetTextures() : TextureState.getNumberOfFragmentTexCoordUnits();



Bug occurs (probably only in nvidia cards since they have only 4 fixed texture units) when we render geometry using vbo with more then 4 texture coords and after that we render other geometry not using vbo and less then 4 

tex coords… 


It's me again here is example which causes jvm crash (on nvidia cards):





package jmetest.renderer.state;



import jmetest.terrain.TestTerrainSplatting;



import com.jme.app.SimpleGame;

import com.jme.bounding.BoundingBox;

import com.jme.image.Texture;

import com.jme.math.Vector3f;

import com.jme.scene.VBOInfo;

import com.jme.scene.shape.Sphere;

import com.jme.scene.state.TextureState;

import com.jme.util.TextureManager;

import com.jmex.terrain.TerrainPage;

import com.jmex.terrain.util.RawHeightMap;



public class TestShaderTexturing extends SimpleGame

{



public static void main(String[] args)

{

TestShaderTexturing app = new TestShaderTexturing();

app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);

app.start();

}







@Override

protected void simpleInitGame()

{



RawHeightMap heightMap = new RawHeightMap(TestTerrainSplatting.class

                .getClassLoader().getResource(

                        "jmetest/data/texture/terrain/heights.raw").getFile(),

                129, RawHeightMap.FORMAT_16BITLE, false);



        Vector3f terrainScale = new Vector3f(17, 0.003f, 6);

        heightMap.setHeightScale(0.001f);

        TerrainPage page = new TerrainPage("Terrain",129, heightMap.getSize(),

                terrainScale, heightMap.getHeightMap(), false);

        page.setDetailTexture(0, 1);

        page.setDetailTexture(1, 1);

        page.setDetailTexture(2, 1);

        page.setDetailTexture(3, 1);

        page.setDetailTexture(4, 1);   

       

TextureState ts = display.getRenderer().createTextureState();

Texture t0 = TextureManager.loadTexture(ClassLoader

.getSystemResource("jmetest/data/texture/Decal.PNG"),

Texture.MM_LINEAR_LINEAR, Texture.FM_NEAREST);

ts.setTexture(t0, 0);

Texture t1 = TextureManager.loadTexture(ClassLoader

.getSystemResource("jmetest/data/texture/highest.jpg"),

Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);

ts.setTexture(t1, 1);

Texture t2 = TextureManager.loadTexture(ClassLoader

.getSystemResource("jmetest/data/cursor/test.PNG"),

Texture.MM_LINEAR_LINEAR, Texture.FM_NEAREST, 0, true);

t2.setWrap(Texture.WM_ECLAMP_S_ECLAMP_T);

ts.setTexture(t2, 2);

Texture t3 = TextureManager.loadTexture(ClassLoader

.getSystemResource("jmetest/data/texture/highest.jpg"),

Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);

ts.setTexture(t3, 3);

Texture t4 = TextureManager.loadTexture(ClassLoader

.getSystemResource("jmetest/data/cursor/test.PNG"),

Texture.MM_LINEAR_LINEAR, Texture.FM_NEAREST, 0, false);

t4.setWrap(Texture.WM_ECLAMP_S_ECLAMP_T);

ts.setTexture(t4, 4);

page.setModelBound(new BoundingBox());     

     

        page.setRenderState(ts);

        page.updateModelBound();

        page.setLocalTranslation(0, 0, -20);

rootNode.attachChild(page);



TextureState ts2 = display.getRenderer().createTextureState();

Texture t01 = TextureManager.loadTexture(ClassLoader

.getSystemResource("jmetest/data/texture/Decal.PNG"),

Texture.MM_LINEAR_LINEAR, Texture.FM_NEAREST);

ts2.setTexture(t01, 0);



Sphere newMesh = new Sphere("", 500, 500, 5);

newMesh.setRenderState(ts2);

newMesh.setModelBound(new BoundingBox());

newMesh.updateModelBound();

newMesh.setLocalTranslation(0, 0, -30);

rootNode.attachChild(newMesh);

rootNode.updateRenderState();

lightState.setEnabled(false);

}



}

I agree with this, this is a bug. Making the distinction between the number texture units and number of texture coordinate attributes is sometimes shady. Maybe the getNumberOfFragmentTexCoordUnits() should be called getNumberOfFragmentTexCoordAttributes(), to better make the distinction between texture units and texture coordinate vertex attributes.

When i think about this some more, the reasoning for using getNumberOfFixedUnits() was probably that old cards without shader support probably have getNumberOfFragmentTexCoordUnits()==0.



So this may be the solution:



int numUnits = TextureState.getNumberOfFragmentTexCoordUnits();
if(numUnits<TextureState.getNumberOfFixedUnits())
  numUnits = TextureState.getNumberOfFixedUnits();
prevTextureNumber = ts.getNumberOfSetTextures() < numUnits  ?
      ts.getNumberOfSetTextures() : numUnits;



ATI: 8 fixed 8 shader = 8 units
nVidia: 4 fixed 8 shader = 8 units
old card without shader support: 2 fixed 0 shader = 2 units

Hello



I found the same error with my NVidia 8800 on jme2, but I don't see any correction in latest versions…

I would be sure it's not my responsability (my code) before continue my project.



Thank's for your answers (and sorry for my english).

Bye




protected void simpleInitGame()
   {

      RawHeightMap heightMap = new RawHeightMap(TestTerrainSplatting.class
                .getClassLoader().getResource(
                        "jmetest/data/texture/terrain/heights.raw").getFile(),
                129, RawHeightMap.FORMAT_16BITLE, false);

      Vector3f terrainScale = new Vector3f(0, 0, 0);
      TerrainPage page = new TerrainPage("Terrain",129, heightMap.getSize(),
                terrainScale, heightMap.getHeightMap(), false);
        page.setDetailTexture(0, 1);
        page.setDetailTexture(1, 1);
        page.setDetailTexture(2, 1);
        page.setDetailTexture(3, 1);
        page.setDetailTexture(4, 1);   
       
      TextureState ts = display.getRenderer().createTextureState();
      Texture t0 = TextureManager.loadTexture(ClassLoader
            .getSystemResource("jmetest/data/texture/Decal.PNG"));
      ts.setTexture(t0, 0);
     
      Texture t1 = TextureManager.loadTexture(ClassLoader
            .getSystemResource("jmetest/data/texture/highest.jpg"));
      ts.setTexture(t1, 1);
     
      Texture t2 = TextureManager.loadTexture(ClassLoader
            .getSystemResource("jmetest/data/cursor/test.PNG"));
      ts.setTexture(t2, 2);
     
      Texture t3 = TextureManager.loadTexture(ClassLoader
            .getSystemResource("jmetest/data/texture/highest.jpg"));
      ts.setTexture(t3, 3);
     
      Texture t4 = TextureManager.loadTexture(ClassLoader
            .getSystemResource("jmetest/data/cursor/test.PNG"));
      ts.setTexture(t4, 4);

      page.setRenderState(ts);
      rootNode.attachChild(page);
      rootNode.updateWorldBound();

/*      Sphere newMesh = new Sphere("", 500, 500, 5);
      rootNode.attachChild(newMesh);
      /**/
   }



PS : Error appear when i attach the new Sphere on rootNode (javaw.exe crash) or when i press 'B' to debug bounds, F4 to show FPS, etc.  with this error message :

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x696b7437, pid=2680, tid=4748
#
# JRE version: 6.0_19-b04
# Java VM: Java HotSpot(TM) Client VM (16.2-b04 mixed mode windows-x86 )
# Problematic frame:
# C  [nvoglnt.dll+0x1b7437]
#


I can send the log file if you want...

i dont think your error is reacted to the problem here.



Try to create a sphere with fewer samples:

new Sphere("", 50, 50, 5);



500 is too detailed.

I can't try your idea now, but i don't see the link with F4 option for example.

This source code shows the bug i have in my part of a little integration on a big project that I can't totally change (the big project). So, I would understand where is the real error and how miss this error.



In my real code, I have just a problem with the 5th texture coordinate layer. If i test my integration without send the 5th layer, there is no bug…



I have no error in normal output (on Eclipse) when i have just the sphere, but i have a Windows dialog that said "javaw.exe have an error / show stack trace / quit application" [ this is a very bad traduction, but you see i have any help with this message ].



Thank's for your attention.

Bye

@Core-Dump : 500 is effectively too detailled, with 50 in this code => no error.

So I tried only one sphere with 500 => no error.

I tried again the sphere with the previous code but with a new comment :

//        page.setDetailTexture(4, 1);

And here, no error… But when I uncomment this line, error appear again.

I really would understand the problem because I think is not just because I use 500 samples…







#######################################################################



EDIT :

Remember this old links :

http://www.jmonkeyengine.com/forum/index.php?topic=3197.0  (May 2006)

http://www.jmonkeyengine.com/forum/index.php?topic=12678.0 (November 2009)



And I have a new simple version for you :


    protected void simpleInitGame() {
        int MAX = 5;
        Quad q = new Quad("quad", 20, 20);
        rootNode.attachChild(q);
        
        
        Texture tex = TextureManager.loadTexture(ClassLoader.getSystemResource("jmetest/data/texture/cube_face_negx.png"));
        TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        
        for(int i=0; i<MAX; i++) {
            ts.setTexture(tex, i);
        }
        
        q.setRenderState(ts);
        q.updateRenderState();
        for(int i=0; i<5; i++) {
            FloatBuffer coords = ByteBuffer.allocateDirect(4*2*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
            coords.clear();
            coords.rewind();
            coords.put(0).put(1);
            coords.put(0).put(0);
            coords.put(1).put(0);
            coords.put(1).put(1);
            
            q.setTextureCoords(new TexCoords(coords), i);
        }
        Sphere sphere = new Sphere("sphere", 200, 400, 5);
        rootNode.attachChild(sphere);
    }



Yes, the sphere is always here, because this code have no bug without this. It's probably a memory error I think...
And if you change the last "for" loop with i<MAX-1, the bug isn't here ...
And when I change in LWJGLRenderer in predraw, it's perfect with this lines :

            int numUnits = TextureState.getNumberOfFragmentTexCoordUnits();
            if(numUnits<TextureState.getNumberOfFixedUnits())
              numUnits = TextureState.getNumberOfFixedUnits();
            prevTextureNumber = ts.getNumberOfSetTextures() < numUnits  ?
                   ts.getNumberOfSetTextures() : numUnits;





#######################################################################

EDIT :

Ardor3D, LwjglContextCapabilities class :

/ Go on to check number of texture units supported for vertex and
        // fragment shaders
        if (caps.GL_ARB_shader_objects && caps.GL_ARB_vertex_shader && caps.GL_ARB_fragment_shader) {
            GL11.glGetInteger(ARBVertexShader.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB, buf);
            _numVertexTexUnits = buf.get(0);
            GL11.glGetInteger(ARBFragmentShader.GL_MAX_TEXTURE_IMAGE_UNITS_ARB, buf);
            _numFragmentTexUnits = buf.get(0);
            GL11.glGetInteger(ARBFragmentShader.GL_MAX_TEXTURE_COORDS_ARB, buf);
            _numFragmentTexCoordUnits = buf.get(0);
        } else {
            // based on nvidia dev doc:
            // http://developer.nvidia.com/object/General_FAQ.html#t6
            // "For GPUs that do not support GL_ARB_fragment_program and
            // GL_NV_fragment_program, those two limits are set equal to
            // GL_MAX_TEXTURE_UNITS."
            _numFragmentTexCoordUnits = _numFixedTexUnits;
            _numFragmentTexUnits = _numFixedTexUnits;

            // We'll set this to 0 for now since we do not know:
            _numVertexTexUnits = 0;
        }

        // Now determine the maximum number of supported texture units
        _numTotalTexUnits = Math.max(_numFragmentTexCoordUnits, Math.max(_numFixedTexUnits, Math.max(
                _numFragmentTexUnits, _numVertexTexUnits)));



We can see at the end, the max function ...

up

So you suggest this patch right?



Can anyone else have a look at this? I don't know this area of jme … :slight_smile:


Index: src/com/jme/renderer/lwjgl/LWJGLRenderer.java
===================================================================
--- src/com/jme/renderer/lwjgl/LWJGLRenderer.java   (revision 4953)
+++ src/com/jme/renderer/lwjgl/LWJGLRenderer.java   (working copy)
@@ -1519,9 +1519,13 @@
                 }
             }
 
-            prevTextureNumber = ts.getNumberOfSetTextures() < TextureState
-                    .getNumberOfFixedUnits() ? ts.getNumberOfSetTextures()
-                    : TextureState.getNumberOfFixedUnits();
+            int numUnits = TextureState.getNumberOfFragmentTexCoordUnits();
+            if (numUnits < TextureState.getNumberOfFixedUnits()) {
+               numUnits = TextureState.getNumberOfFixedUnits();
+            }
+           
+            prevTextureNumber = ts.getNumberOfSetTextures() < numUnits ?
+                  ts.getNumberOfSetTextures() : numUnits;
         }
         
         return indicesVBO;

I work on this a little more, and I see JOGL have the same error, and my first patch it's not the best solution.

I create a new topic with hope a quick reply :

http://www.jmonkeyengine.com/forum/index.php?topic=13715.0

Thank's for your time, and if a man which know this part of jme can look this new topic, I'll appreciate a lot.



(sorry again for my english :S)