Shaders PS2/3

I know the GLSLShaderObjectState has the isSupported method to ensure that GLSL shaders are supported by the users graphics card.

But is there any way to determine whether the users card is PS2 or PS3 compliant? So that alternative shaders may be loaded?

If there's currently not an ability to do this I wonder how hard it would be to add?  Perhaps LWJGL already supports this and we just need to wrap it?

Back a year ago, i posted a similar question on OpenGL forums. The best i got was this answer:



For nVidia, you can check if the NV extensions are present and you'll know what the hw can do.
For ATI, you can hardcode according to what GL_RENDERER returns. You can extract the GPU name from GL_RENDERER.


So the answer is no, there is no easy way. No wonder some games run a benchmark after installation to determine what the given hardware can do. Guess they then throw out every shader which didn't compile, or which resulted in an exception, or was too slow. Some features may be silently ignored, so they may require testing with specially written shaders, and checking the results.

I noticed that which ever developer implemented the Bloom or MotionBlur shader, first created an additional GLSLShaderObjectState and loaded their shaders (via .load() ) and applied the shader (via .apply() ) within an try statement. If this succeeded, then then loaded the same shaders into their global GLSLShaderObjectState, applied all necessary uniforms and applied again.



I guess if the shader was using incompatable code for the users graphics card then an exception would have been thrown, but im not sure?



Would a developer kindly confirm.

Thanks

I did that cause I wanted to be able to reload shaders at runtime without screwing up the running program…so I'll change the shader source while running, and hit reload to try a new thing, if it doesnt work it just throws an exception, if it works it reloads the shader…



i do not know of any solid way of testing for shader model version either unfortunately…

When loading shaders are they immediately evaluated? I.e will it throw an error if there is a semantic error within the code, possibly due to incompatibilities or undeclared functions. Or will we only find that out during the rendering phase?



If the shaders are evaluated during loading then a few well placed try statements should be sufficient for what we need.

that's why i call the apply method directly after loading…it will detect code errors, but i don't think it fails on the things you want to discover until the actual rendering…i'll be interested to find out for sure though, so keep me posted :slight_smile:

I will do, the only problem is I dont think any of us have old enough graphics cards to test the shaders on.  :?



If anyone has an old graphics card which they know doesn't support shaders please report here and Ill put together a simple test script and we'll see what happens!



Thanks

My laptop might be a good candidate for such a test.



I probably also have a machine hanging around I could test on.

Ok, Ill edit Mr Coders bloom/motion blur test and get back to you.

Ok, here we go. This was quickly thrown together from Rikards code:

Put everything into the 'test' package.



The shader will do lots of stuff such as applying the phong lighting model and a texture, and then just set everything to red so you can clearly see if its loaded.

The only problem is I dont know what the difference is between PS2/3.



Try removing the isSupported() line before loading the shader and see what happens on your old graphics card.



package test;

import com.jme.app.SimplePassGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.pass.RenderPass;
import com.jme.scene.Node;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Torus;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TestShader extends SimplePassGame {
   private GLSLShaderObjectsState shader;

   public static void main(String[] args) {
      Logger.getLogger("").setLevel(Level.WARNING);

      TestShader app = new TestShader();
      app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
      app.start();
   }

   @Override
   protected void simpleInitGame() {
      cam.setFrustumPerspective(55.0f, (float) display.getWidth() / (float) display.getHeight(), 1, 1000);

      PointLight light = new PointLight();
      light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
      light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
      light.setLocation(new Vector3f(0, 30, 0));
      light.setEnabled(true);
      lightState.attach(light);

      rootNode.attachChild(createObjects());

      GLSLShaderObjectsState shader = DisplaySystem.getDisplaySystem().getRenderer().createGLSLShaderObjectsState();

      if(!shader.isSupported()) {
           Logger.getLogger("TestShader").log(Level.WARNING, "GLSL not supported");
      } else {
         TextureState ts = display.getRenderer().createTextureState();
         Texture t0 = TextureManager.loadTexture(TestShader.class.getClassLoader().getResource("jmetest/data/texture/wall.jpg"),Texture.MM_LINEAR_LINEAR,Texture.FM_LINEAR);
         t0.setWrap(Texture.WM_WRAP_S_WRAP_T);
         ts.setTexture(t0);

         try {
            System.out.println("SHADER: LOADING");
            shader.load(TestShader.class.getClassLoader().getResource("test/test.vert"), TestShader.class.getClassLoader().getResource("test/test.frag"));
            shader.setUniform("Texture", 0);
            System.out.println("APPLYING");
            shader.apply();
         } catch(Exception e) {
            System.out.println("ERROR LOADING AND APPLYING SHADER");

            e.printStackTrace();
            System.exit(0);
         }

         rootNode.setRenderState(shader);
         rootNode.setRenderState(ts);
      }

      RenderPass rootPass = new RenderPass();
      rootPass.add(rootNode);
      pManager.add(rootPass);

      RenderPass fpsPass = new RenderPass();
      fpsPass.add(fpsNode);
      pManager.add(fpsPass);
   }

   @Override
   protected void simpleUpdate() {
      if(shader.isSupported()) {

         //   shader.apply();

      }
   }

   private Node createObjects() {
      Node objects = new Node("objects");

      Torus torus = new Torus("Torus", 50, 50, 10, 20);
      torus.setLocalTranslation(new Vector3f(50, -5, 20));
      TextureState ts = display.getRenderer().createTextureState();
      Texture t0 = TextureManager.loadTexture(TestShader.class.getClassLoader().getResource("jmetest/data/images/Monkey.jpg"),Texture.MM_LINEAR_LINEAR,Texture.FM_LINEAR);
      Texture t1 = TextureManager.loadTexture(TestShader.class.getClassLoader().getResource("jmetest/data/texture/north.jpg"),Texture.MM_LINEAR_LINEAR,Texture.FM_LINEAR);
      t1.setEnvironmentalMapMode(Texture.EM_SPHERE);
      ts.setTexture(t0, 0);
      ts.setTexture(t1, 1);
      ts.setEnabled(true);
      torus.setRenderState(ts);
      objects.attachChild(torus);

      ts = display.getRenderer().createTextureState();
      t0 = TextureManager.loadTexture(TestShader.class.getClassLoader().getResource("jmetest/data/texture/wall.jpg"),Texture.MM_LINEAR_LINEAR,Texture.FM_LINEAR);
      t0.setWrap(Texture.WM_WRAP_S_WRAP_T);
      ts.setTexture(t0);

      Box box = new Box("box1", new Vector3f(-10, -10, -10), new Vector3f(10, 10, 10));
      box.setLocalTranslation(new Vector3f(0, -7, 0));
      box.setRenderState(ts);
      box.setModelBound(new BoundingBox());
      box.updateModelBound();
      objects.attachChild(box);

      box = new Box("box2", new Vector3f(-5, -5, -5), new Vector3f(5, 5, 5));
      box.setLocalTranslation(new Vector3f(15, 10, 0));
      box.setModelBound(new BoundingBox());
      box.updateModelBound();
      box.setRenderState(ts);
      objects.attachChild(box);

      box = new Box("box4", new Vector3f(-5, -5, -5), new Vector3f(5, 5, 5));
      box.setLocalTranslation(new Vector3f(20, 0, 0));
      box.setModelBound(new BoundingBox());
      box.updateModelBound();
      box.setRenderState(ts);
      objects.attachChild(box);

      box = new Box("box5", new Vector3f(-50, -2, -50), new Vector3f(50, 2, 50));
      box.setLocalTranslation(new Vector3f(0, -15, 0));
      box.setModelBound(new BoundingBox());
      box.updateModelBound();
      box.setRenderState(ts);
      objects.attachChild(box);

      ts = display.getRenderer().createTextureState();
      t0 = TextureManager.loadTexture(TestShader.class.getClassLoader().getResource("jmetest/data/texture/cloud_land.jpg"),Texture.MM_LINEAR_LINEAR,Texture.FM_LINEAR);
      t0.setWrap(Texture.WM_WRAP_S_WRAP_T);
      ts.setTexture(t0);

      box = new Box("floor", new Vector3f(-1000, -10, -1000), new Vector3f(1000, 10, 1000));
      box.setLocalTranslation(new Vector3f(0, -100, 0));
      box.setModelBound(new BoundingBox());
      box.updateModelBound();
      box.setRenderState(ts);
      objects.attachChild(box);

      return objects;
   }
}




varying vec4 diffuse,ambientGlobal, ambient;
varying vec3 normal,lightDir,halfVector;
varying float dist;

uniform sampler2D Texture;

void main()
{
   vec3 n,halfV,viewV,ldir;
   float NdotL,NdotHV;
   vec4 color = ambientGlobal;
   float att;
   
   vec4 fragment = texture2D(Texture, gl_TexCoord[0].st);

   n = normalize(normal);
   NdotL = max(dot(n,normalize(lightDir)),0.0);

   if (NdotL > 0.0) {
   
      att = 1.0 / (gl_LightSource[0].constantAttenuation + gl_LightSource[0].linearAttenuation * dist + gl_LightSource[0].quadraticAttenuation * dist * dist);
      color += att * (diffuse * NdotL + ambient);
   
      halfV = normalize(halfVector);
      NdotHV = max(dot(n,halfV),0.0);
      color += att * gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess);
   }

   gl_FragColor = vec4(color.rgb * fragment.rgb, 1.0);
   
   // set everything red
   gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}




varying vec4 diffuse,ambientGlobal,ambient;
varying vec3 normal,lightDir,halfVector;
varying float dist;

void main()
{   
   vec4 ecPos;
   vec3 aux;
   
   normal = normalize(gl_NormalMatrix * gl_Normal);
   ecPos = gl_ModelViewMatrix * gl_Vertex;
   aux = vec3(gl_LightSource[0].position-ecPos);
   lightDir = normalize(aux);
   dist = length(aux);

   halfVector = normalize(gl_LightSource[0].halfVector.xyz);
   
   // diffuse
   diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
   
   // ambient attenuation
   ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
   ambientGlobal = gl_LightModel.ambient * gl_FrontMaterial.ambient;
   
   gl_TexCoord[0] = gl_MultiTexCoord0;
   gl_Position = ftransform();
}

I found this wikipedia page on the subject. The most obvious and most easy to test difference is “dynamic flow control”. The “if” statement in fragment shader code should work on PS3.0 but not on PS2.0 . The “if” condition may be ignored on some cards, so your test may still work on those cards, but without ever testing the condition.

Thanks for finding that vear!



Frog! Dust off your old machine and prep it for PS2 testing.  :stuck_out_tongue:

That requires me to set up Eclipse and everything on it…wanna just give me a ZIP containing the build? :wink:

:expressionless: Not really. This wouldn't be a one off test. Given what Vear says is true (and I believe it is - read it somewhere myself) - we will need to do lots of testing.

We dont have to do that right away as we dont even have PS3 compliant shaders yet (my book hasnt arrived  :x ) But if we cant disable shaders for only PS2 compatible cards then we may have a problem.



This is on the wrong forum really.