Nvidia Sli support with LWJGL + OpenGL + Java?

Started by crash0veride007, September 01, 2005, 03:53:27

Previous topic - Next topic

crash0veride007

The curiosity question of the day. So we know OpenGL supports Sli Rendering (at least in SFR mode) as stated here in the below presentation: http://download.nvidia.com/developer/presentations/2005/GDC/OpenGL_Day/OpenGL_SLI.pdf

The easiest way to see if an app is even utilizing SLi is to enable the Show SLi load balancing in the SLi CPL of the Nvidia Forceware CPL.
SO..... Take for instance Far Cry or Doom III both games which are known to use OpenGL for rendering, can clearly be seen to be utililizng SLi. So here comes the million dollar question do LWJGL or JOGL allow apps written with them to utilize Sli or can they? For grins I ran some of my code on 2 of my SLi enabled RIGS (DUAL 7800GTX + Athlon FX55) and a (DUAL Quadro FX3400 + Dual Opteron). Now obviously we know that the OpenGL app must be run in fullscreen mode to properly enable it. So the results.... It does not appear the app is utilizing SLi no load balancer shows plus dropping back to Single GPU/NON-SLi mode does not chage the framerate or the performance data. Since I am a Hardware/OS/Network Infrastructure guy and have just only recently gotten into OpenGL/Java programming my first guess at the high-level would be that something with Java/LWJGL/JOGL does not get set up right in fullscreen mode to allow SLi to work. Else my other likely guess would be that something in Java itself interferes.

Matzon

this should be somewhat easily fixable for lwjgl - but we need to know what the magic initializer for SLI is. Anyone ?

Matzon

Something is bothering me tho. Assuming you're right - that would mean a LOT of games cannot run SLI untill they're recompiled to enable it - somehow ?

spasi

I'm not at all familiar with SLI, but AFAIK it is currently enabled only with specific application profiles, right? Only the "hottest" games are supported, which include FarCry and Doom 3 of course. I also heard that this will be "fixed" in a certain driver release (80+?), since it's the main selling point of ATI Crossfire (works for any game).

Try using one of the latest beta/leaked drivers, it might be working already.

crash0veride007

QuoteOnly the "hottest" games are supported, which include FarCry and Doom 3 of course.

What about 3Dmark 01,03 (DIRECTX) or UT2k (DIRECTX/OpenGL) which came before the time of SLi, thats what mostly baffled me, how can the older games/bench utils work?

spasi


Evil-Devil

I wonder that they also provide SLI support for the original Unreal. Thats from '95! I mention the graphics won't look better in SLI mode. As Unreal looks even on mainstream cards and high resolution settings like an old one ;)

crash0veride007

Ok created an SLi profile for "java.eve" "SFR mode" the SLi load balancer shows up in windowed and fullscreen mode and what not. However instead of helping the performance it TANKS it big time! Further investigation is needed I will post some code and screenshots soon. Comments anyone?

spasi

I think it depends on your bottleneck. SFR mode does not help if you are geometry/vertex shader limited. Check out NV's GPU Programming Guide for a few tips.

crash0veride007

Ok here is a link to a screenshot: http://i18.photobucket.com/albums/b148/crash0veride007/scrnshot.jpg

So for someone smarter than I according to the OpenGL/SLi stuff in this guide: http://download.nvidia.com/developer/GPU_Programming_Guide/GPU_Programming_Guide.pdf

1) does LWJGL display.update work and play well with SLi? AKA: swapbuffers
2) How could our code be best written in LWJGL to take advantage of SLi ?

and also some code to help further this discussion:

package JavaGL;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.glu.GLU;

import JavaGL.tools.Object3D;
import JavaGL.tools.Texture;
import JavaGL.tools.TextureLoader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import org.lwjgl.input.Keyboard;

public class MeshPolys {
   public static boolean done = false;
   public static String windowTitle = "Crash0veride007 JavaGL";
   public static DisplayMode displayMode;
   public static Object3D mesh;
   public static TextureLoader textureloader;
   public static Texture texture;
   public static float light1Ambient[] = { 0.4f, 0.4f, 0.4f, 1.0f };  // Ambient Light1 Values
   public static float light1Diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };      // Diffuse Light1 Values
   public static float light1Specular[] = { 0.5f, 0.5f, 0.5f, 1.0f }; // Specular Light1 Values
   public static float light1Position[] = { 0.0f, 0.0f, 85.0f, 1.0f }; // Light1 Position
   public static float light2Ambient[] = { 0.4f, 0.4f, 0.4f, 1.0f };  // Ambient Light2 Values
   public static float light2Diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f };      // Diffuse Light2 Values
   public static float light2Specular[] = { 0.5f, 0.5f, 0.5f, 1.0f }; // Specular Light2 Values
   public static float light2Position[] = { 0.0f, 0.0f, -85.0f, 1.0f }; // Light2 Position
   public static float angle =0.0f;
   
   public static void main(String[] args) {
       MeshPolys runit = new MeshPolys();
       runit.run();
   }
   
   public void run() {
       long startTime = System.currentTimeMillis() + 5000;
       long fps = 0;
       try {
           init();
           while (!done) {
               MainLoop();
               Display.update();  
               if (startTime > System.currentTimeMillis()) {
                   fps++;
               } else {
                   long timeUsed = 5000 + (startTime - System.currentTimeMillis());
                   startTime = System.currentTimeMillis() + 5000;
                   String outdata = fps + " frames in " + (float) (timeUsed / 1000f) + " seconds = "+ (fps / (timeUsed / 1000f))+" FPS";
                   System.out.println( outdata );
                   Display.setTitle(windowTitle + " " + outdata);
                   fps = 0;
               }
           }
           cleanup();
           System.exit(0);
       } catch (Exception e) {
           e.printStackTrace();
           System.exit(0);
       }
   }
   
   public void MainLoop() {
       rendershit();
       pollMK();
   }
   
   public void createWindow() throws Exception {
       DisplayMode d[] = Display.getAvailableDisplayModes();
       for (int i = 0; i < d.length; i++) {
           if (d.getWidth() == 1024
                   && d.getHeight() == 768
                   && d.getBitsPerPixel() == 32) {
               displayMode = d;
               break;
           }
       }
       Display.setDisplayMode(displayMode);
       Display.setTitle(windowTitle);
       Display.setFullscreen(false);
       Display.create();
   }
   
   public void init() throws Exception {
       createWindow();
       try {
           Keyboard.create();
           }
       catch (Exception e) {}
       initGL();
       loadtexture();
       loadmesh();
       Display.setVSyncEnabled(false);
       //texture.bind();
       ByteBuffer temp = ByteBuffer.allocateDirect(16);
       temp.order(ByteOrder.nativeOrder());
       GL11.glLight(GL11.GL_LIGHT1, GL11.GL_AMBIENT, (FloatBuffer)temp.asFloatBuffer().put(light1Ambient).flip()); // Setup The Ambient Light
       GL11.glLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, (FloatBuffer)temp.asFloatBuffer().put(light1Diffuse).flip()); // Setup The Diffuse Light
       GL11.glLight(GL11.GL_LIGHT1, GL11.GL_SPECULAR, (FloatBuffer)temp.asFloatBuffer().put(light1Specular).flip()); // Setup The Specular Light
       GL11.glLight(GL11.GL_LIGHT1, GL11.GL_POSITION,(FloatBuffer)temp.asFloatBuffer().put(light1Position).flip()); // Position The Light
       GL11.glEnable(GL11.GL_LIGHT1); // Enable Light One
       
       GL11.glLight(GL11.GL_LIGHT2, GL11.GL_AMBIENT, (FloatBuffer)temp.asFloatBuffer().put(light2Ambient).flip()); // Setup The Ambient Light
       GL11.glLight(GL11.GL_LIGHT2, GL11.GL_DIFFUSE, (FloatBuffer)temp.asFloatBuffer().put(light2Diffuse).flip()); // Setup The Diffuse Light
       GL11.glLight(GL11.GL_LIGHT2, GL11.GL_SPECULAR, (FloatBuffer)temp.asFloatBuffer().put(light2Specular).flip()); // Setup The Specular Light
       GL11.glLight(GL11.GL_LIGHT2, GL11.GL_POSITION,(FloatBuffer)temp.asFloatBuffer().put(light2Position).flip()); // Position The Light
       GL11.glEnable(GL11.GL_LIGHT2); // Enable Light Two
       
   }
   
   public void initGL() {
       GL11.glEnable(GL11.GL_LIGHTING);
       GL11.glEnable(GL11.GL_TEXTURE_2D);
       GL11.glEnable(GL11.GL_CULL_FACE);
       GL11.glShadeModel(GL11.GL_SMOOTH);
       GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
       GL11.glClearDepth(1.0);
       GL11.glEnable(GL11.GL_DEPTH_TEST);
       GL11.glDepthFunc(GL11.GL_LEQUAL);
       GL11.glMatrixMode(GL11.GL_PROJECTION);
       GL11.glLoadIdentity();
       GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
       GLU.gluPerspective(80.0f,(float) displayMode.getWidth() / (float) displayMode.getHeight(),0.1f,1000.0f);
       GL11.glMatrixMode(GL11.GL_MODELVIEW);
       GL11.glLoadIdentity();
       GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
       //GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
       //GL11.glFrontFace(GL11.GL_CCW);
       GLU.gluLookAt(0.0f, 0.0f, 95.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
   }
   
   public void loadmesh() {
       //FileReader f_read;
       String path ="mesh/imp.obj";
       String matpath = "JavaGL/mesh/";
       try {
           InputStream r_path = getClass().getResourceAsStream(path);
           BufferedReader b_read = new BufferedReader(new InputStreamReader(r_path));
           //f_read = new FileReader(r_path);
           //BufferedReader b_read = new BufferedReader(f_read);
           mesh = new Object3D(b_read,true, matpath);
           r_path.close();
           b_read.close();
       } catch (Exception e) {
           System.out.println("Could not open file: " + path);
       }
   }
   
   public void loadtexture() {
       String texpath = "JavaGL/mesh/imp_d.png";
       try {
       textureloader = new TextureLoader(1.0f);
   texture = textureloader.getTexture(texpath);
       } catch (Exception e) {
      System.out.println("Could not open file: " + texpath);
      }
   }
   
   public void pollMK() {
           while ( Keyboard.next() )  {
           if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) {
               done = true;
           }
       }
       if(Display.isCloseRequested()) {
           done = true;
       }
   }
   
   public void rendershit() {
       
       GL11.glColor3f(1.0f, 1.0f, 1.0f);
       GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
       GL11.glPushMatrix();
       //GL11.glRotatef(angle, 1.0f, .0f, 0.0f);
       GL11.glRotatef(angle, 0.0f, 1.0f, 0.0f);
       //GL11.glRotatef(angle, 0.0f, 0.0f, 1.0f);
       GL11.glPushMatrix();
       if (mesh != null)
       mesh.opengldraw();
       GL11.glPopMatrix();
       angle  = (angle+0.1f)%360;
       GL11.glPopMatrix();
       //GL11.glFlush();
   }
   
   public void cleanup() {
       Display.destroy();
   }
}

spasi

Except for this:

QuoteRequest PDF_SWAP_EXCHANGE Pixel Formats

When creating an OpenGL context, make sure to ask for a pixel format with the PFD_SWAP_EXCHANGE flag set instead of PFD_SWAP_COPY. The swap exchange flag implies that the application does not rely on the back buffer content after a SwapBuffers() is performed.

This is essential for the AFR mode because the driver cycles through GPUs per frame so the back buffer may be stored on another GPU.

For the SFR mode PFD_SWAP_EXCHANGE is preferred over PFD_SWAP_COPY because the swap copy mode requires additional synchronization between the GPUs.

I don't think there's anything special about LWJGL that needs fixing for it to work properly with SLI. Just follow the guidelines and you'll be OK.

As for the particular scene you're testing, it's way too simple. You won't see any SLI advantages, unless you're doing something a LOT heavier.

crash0veride007

So AFR mode is the way to go for the particular scene I am doing, I just need to make the scene WAY more complicated?
SFR mode on that scene tanks its performance big time. AFR mode just appears to do nothing.

spasi

Quote from: "crash0veride007"I just need to make the scene WAY more complicated?

No matter which mode you choose, at 2000fps the GPU is practically doing nothing. SLI makes sense in scenes that a single GPU is having a hard time, so don't expect big improvements before falling to, say, below 50fps.

Quote from: "crash0veride007"SFR mode on that scene tanks its performance big time. AFR mode just appears to do nothing.

How much? Unless you saw something extreme, you may have just measured the driver overhead by enabling the SFR mode (it requires more work from the driver than the simpler AFR mode). At 2000fps you're CPU/driver limited.

Anyway, I'm personally more interested in the SFR mode. The AFR is better when you've got large polygon counts and you want to share that load. The two cards are just rendering each frame alternately (you may want to enable triple buffering for better results). But most modern games are fillrate/fragment shader limited, so SFR works better.

crash0veride007

where can i locate some good LWJGL shader tuts to try out the SDR theory?

spasi

Check out the org.lwjgl.test.opengl.shaders package.