1
\$\begingroup\$

I'm writing a simple OpenGL application in Java that implements the Monte Carlo method for estimating the value of PI. The method is pretty easy. Simply, you draw a circle inside a unit square and then plot random points over the scene. Now, for each point that is inside the circle you increment the counter for in points. After determining for all the random points wither they are inside the circle or not you divide the number of in points over the total number of points you have plotted all multiplied by 4 to get an estimation of PI. It goes something like this PI = (inPoints / totalPoints) * 4. This is because mathematically the ratio of a circle's area to a square's area is PI/4, so when we multiply it by 4 we get PI.

My problem doesn't lie in the algorithm itself; however, I'm having problems trying to plot the points as they are being generated instead of just plotting everything at once when the program finishes executing. I want to give the application a sense of real-time display where the user would see the points as they are being plotted. I'm a beginner at OpenGL and I'm pretty sure there is a multi-threading feature built into it. Non the less, I tried to manually create my own thread. Each worker thread plots one point at a time. Following is the psudo-code:

/* this part of the code exists in display() method in MyCanvas.java
which extends GLCanvas and implements GLEventListener */

// main loop
for(int i = 0; i < number_of_points; i++){
    RandomGenerator random = new RandomGenerator();
    float x = random.nextFloat();
    float y = random.nextFloat();
    Thread pointThread = new Thread(new PointThread(x, y));
}

gl.glFlush();

/* this part of the code exists in run() method in 
PointThread.java which implements Runnable */
void run(){
    try{
        gl.glPushMatrix();
        gl.glBegin(GL2.GL_POINTS);
            if(pointIsIn)
                gl.glColor3f(1.0f, 0.0f, 0.0f);   // red point
            else
                gl.glColor3f(0.0f, 0.0f, 1.0f);   // blue point
            gl.glVertex3f(x, y, 0.0f);   // coordinates
        gl.glEnd();
        gl.glPopMatrix();
    }catch(Exception e){
    }
}

I'm not sure if my approach to solving this issue is correct. I hope you guys can help me out. Thanks.

EDIT:

OK! So I have followed what you have suggested Sven, and instead of using AWT components that would restrict multi-threading, I went on and used GLWindow which is a NEWT context component that wouldn't restrict me from doing so; however, I'm still facing the same issue while dispatching threads. The program seems to wait until all points are determined and then draws everything all at once. Please look into my real code below which is one class called "MonteCarlo" the issue is specifically in the display() method. Put in mind that I'm not really plotting the points right now I'm just printing out hello to the standard output just to test the program.

import javax.media.opengl.GL2;
import javax.media.opengl.glu.gl2.GLUgl2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLProfile;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import com.jogamp.newt.event.WindowAdapter;
import com.jogamp.newt.event.WindowEvent;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.util.FPSAnimator;

import java.util.Random;

public class MonteCarlo implements GLEventListener{
private static final String TITLE = "Monte Carlo Method";
private static final int WINDOW_WIDTH = 400;
private static final int WINDOW_HEIGHT = 400;
private static final int FPS = 60;

private static final float RADIUS = 0.5f;

public MonteCarlo(){
}

@Override
public void init(GLAutoDrawable drawable){
    GL2 gl = drawable.getGL().getGL2();

    gl.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    gl.glClearDepth(1.0f);
}

@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height){
    GL2 gl = drawable.getGL().getGL2();
    GLUgl2 glu = new GLUgl2();

    gl.glViewport(0, 0, width, height);
    gl.glMatrixMode(GL2.GL_PROJECTION);
    gl.glLoadIdentity();
    glu.gluPerspective(53.14f, 1.0f, 1.0f, 100.0f);
    gl.glMatrixMode(GL2.GL_MODELVIEW);
}

@Override
public void display(GLAutoDrawable drawable){
    GL2 gl = drawable.getGL().getGL2();
    GLUgl2 glu = new GLUgl2();

    gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
    gl.glLoadIdentity();
    glu.gluLookAt(0.5f, 0.5f, 1.0f, 0.5f, 0.5f, -1.0f, 0.0f, 1.0f, 0.0f);

    gl.glPushMatrix();
    gl.glBegin(GL2.GL_LINES);
        gl.glColor3f(0.0f, 0.0f, 0.0f);
        gl.glVertex3f(0.0f, 0.5f, 0.0f);
        gl.glVertex3f(1.0f, 0.5f, 0.0f);
        gl.glVertex3f(0.5f, 0.0f, 0.0f);
        gl.glVertex3f(0.5f, 1.0f, 0.0f);
    gl.glEnd();
    gl.glPopMatrix();

    int in = 0;
    int total = 1000000;
    Random floatRandomGenerator = new Random();
    for(int i = 0; i < total; i++){
        float x = floatRandomGenerator.nextFloat();
        float y = floatRandomGenerator.nextFloat();
        if((Math.pow(x - 0.5, 2) + Math.pow(y - 0.5, 2)) <= Math.pow(RADIUS, 2)){
            in++;
            new Thread(){
                @Override
                public void run(){
                    System.out.println("hello");
                }
            }.start();
        }
    }

    gl.glPushMatrix();
    gl.glPointSize(10.0f);
    gl.glBegin(GL2.GL_POINTS);
        gl.glColor3f(1.0f, 0.0f, 0.0f);
        gl.glVertex3f(0.5f, 0.5f, 0.0f);
        gl.glVertex3f(0.75f, 0.5f, 0.0f);
        gl.glVertex3f(1.0f, 0.5f, 0.0f);
    gl.glEnd();
    gl.glPopMatrix();

    gl.glFlush();
}

@Override
public void dispose(GLAutoDrawable drawable){
}

public static void main(String[] args){
    GLProfile glProfile = GLProfile.getDefault();
    GLCapabilities glCapabilities = new GLCapabilities(glProfile);
    GLWindow glWindow = GLWindow.create(glCapabilities);
    final FPSAnimator fpsAnimator = new FPSAnimator(glWindow, FPS, true);

    glWindow.addWindowListener(new WindowAdapter(){
        @Override
        public void windowDestroyNotify(WindowEvent e){
            new Thread(){
                @Override
                public void run(){
                    fpsAnimator.stop();
                    System.exit(0);
                }
            }.start();
        };
    });

    glWindow.addGLEventListener(new MonteCarlo());
    glWindow.setTitle(TITLE);
    glWindow.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
    glWindow.setVisible(true);

    fpsAnimator.start();
}
}

Thank you :)

\$\endgroup\$
1
  • \$\begingroup\$ Please be sure to also post your question in the JOGL forum. forum.jogamp.org/jogl-f782158.html There's no better people to ask than the JOGL maintainers themselves. \$\endgroup\$ Commented Oct 8, 2012 at 22:20

2 Answers 2

2
\$\begingroup\$

Multithreaded use of one OpenGL context is supported by the specs, but one OpenGL context can only be made current by one thread at a time.

To use your pseudo code w/ multiple threads and one drawable target, you want to use shared context, see my answer to a related OpenGL multithreading question.

The shared context would need to be bound to a common drawable, which is a quite different scenario than any use case we have covered so far.

Wade is right here, that it is quite possible to do so by the spec, at least not forbidden :)

However OpenGL driver implementations usually bind the context to one drawable (as it is true w/ Mesa DRI) and locking will be performed.

This is also true w/ JOGL, i.e. we claim the drawable lock when make the context current to ensure that no other thread is using it concurrently.

Using a drawable concurrently usually produce undefined results anyways, as described in the OpenGL spec in regards to FBO (framebuffer objects).

Another OpenGL implementation sideffect is the emulation of fixed function pipeline, i.e. constructs like - glBegin(..) .. glVertex(..) .. glEnd()

will only - create an VBO - send it to the shader and render it at some point in time when it needs to be done (swapBuffer() is the latest point here).

Impact of the above means that your multiple threads will fight at least for the GPU's shader engine, but the impl. also for the drawable lock.

Even if you use OpenCL, which is designed for such a cause, i.e. HPC, your workflow is not suitable, since the data to be processed should be readily available before starting the kernel. Hence OpenGL or OpenCL would make no difference here.

There is one little light at the end of the tunnel though :) If you would use a fragment shader using the noise function (random) and render the circle solely in it - you might get what you are heading at, a GPU accelerated 'estimation'.

\$\endgroup\$
1
  • \$\begingroup\$ I was hoping that you can show me some example code. I understand that I have to use GLWindow instead of GLCanvas as the latter is restricting me from actually threading the points. Thank you for your help :) \$\endgroup\$
    – moeabdol
    Commented Oct 10, 2012 at 3:29
1
\$\begingroup\$

You're right that OpenGL is (in theory) multithreadable, and you can use it this way from JOGL. But in practice, if you pick any random platform and driver version, there are sometimes bugs in the multithreading support. So if you want your program to be robust for other users, accessing OpenGL from multiple threads might be more trouble than it's worth.

I'd suggest creating a single rendering thread that owns the GLContext and does all the OpenGL operations. Then have your worker PointThreads push the completed points into a thread-safe collection (e.g. a java.util.concurrent.ConcurrentLinkedQueue) maintained by the rendering thread. The rendering thread can just draw the points as they show up in the queue, or it can transfer them into a vertex buffer for higher-performance rendering.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .