Skip to content

Instantly share code, notes, and snippets.

@alicanalbayrak
Created June 10, 2015 11:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alicanalbayrak/ace2d7f426a4e75ca008 to your computer and use it in GitHub Desktop.
Save alicanalbayrak/ace2d7f426a4e75ca008 to your computer and use it in GitHub Desktop.
Java Implementation of streaming textures using PBO (tutorial source: http://www.songho.ca/opengl/gl_pbo.html)
package pbo;
import static com.jogamp.opengl.GL.GL_COLOR_BUFFER_BIT;
import static com.jogamp.opengl.GL.GL_DEPTH_BUFFER_BIT;
import static com.jogamp.opengl.GL.GL_DEPTH_TEST;
import static com.jogamp.opengl.GL.GL_LEQUAL;
import static com.jogamp.opengl.GL.GL_NICEST;
import static com.jogamp.opengl.GL2ES1.GL_PERSPECTIVE_CORRECTION_HINT;
import static com.jogamp.opengl.fixedfunc.GLLightingFunc.GL_SMOOTH;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.IntBuffer;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GLAutoDrawable;
import com.jogamp.opengl.GLEventListener;
import com.jogamp.opengl.awt.GLCanvas;
import com.jogamp.opengl.glu.GLU;
import com.jogamp.opengl.util.FPSAnimator;
public class PBO2Texture extends GLCanvas implements GLEventListener {
private static final long serialVersionUID = 896825927734135562L;
private GLU glu;
// constants
private static int IMAGE_WIDTH = 1024;
private static int IMAGE_HEIGHT = 1024;
private static int CHANNEL_COUNT = 4;
private static int DATA_SIZE = IMAGE_WIDTH * IMAGE_HEIGHT * CHANNEL_COUNT;
private static int PIXEL_FORMAT = GL2.GL_BGRA;
// vars
private int[] pboIds = new int[2];
private int[] textureId = new int[1];
int screenWidth;
int screenHeight;
boolean pboSupported;
int pboMode;
private static int index = 0;
private int nextIndex = 0;
static int color = 0;
public PBO2Texture() {
this.addGLEventListener(this);
}
// ===========================================================================
// OpenGL Callback Methods
// ===========================================================================
@Override
public void init(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
glu = new GLU(); // get GL Utilities
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // set background (clear) color
gl.glClearDepth(1.0f); // set clear depth value to farthest
gl.glEnable(GL_DEPTH_TEST); // enables depth testing
gl.glDepthFunc(GL_LEQUAL); // the type of depth test to do
gl.glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // best
// perspective
// correction
gl.glShadeModel(GL_SMOOTH); // blends colors nicely, and smoothes out
// lighting
generateTexture(gl);
pboSupported = checkForPBOSupport(gl);
if (pboSupported) {
generatePBOs(gl);
}
}
@Override
public void dispose(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
deletePBORelatedBuffers(gl);
}
@Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
renderPBO2Texture(gl);
gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
drawTexture2Screen(gl);
}
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
GL2 gl = drawable.getGL().getGL2();
this.screenWidth = width;
this.screenHeight = height;
toPerspective(gl);
}
// ===========================================================================
// PBO Related Methods & Variables
// ===========================================================================
private boolean checkForPBOSupport(GL2 gl) {
boolean functionAvailability = gl.isFunctionAvailable("glGenBuffersARB")
&& gl.isFunctionAvailable("glBindBufferARB") && gl.isFunctionAvailable("glBufferDataARB")
&& gl.isFunctionAvailable("glBufferSubDataARB") && gl.isFunctionAvailable("glDeleteBuffersARB")
&& gl.isFunctionAvailable("glGetBufferParameterivARB") && gl.isFunctionAvailable("glMapBufferARB")
&& gl.isFunctionAvailable("glUnmapBufferARB");
boolean wglExtSwapAvailability = gl.isExtensionAvailable("WGL_EXT_swap_control");
boolean glArbPBOAvailability = gl.isExtensionAvailable("GL_ARB_pixel_buffer_object");
if (functionAvailability && wglExtSwapAvailability && glArbPBOAvailability) {
System.out.println("Video card seems ok!");
pboMode = 2;
return true;
} else {
pboMode = 0;
}
return false;
}
private void generateTexture(GL2 gl) {
gl.glGenTextures(1, textureId, 0);
gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId[0]);
gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_NEAREST);
gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_NEAREST);
gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP);
gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP);
gl.glTexImage2D(GL2.GL_TEXTURE_2D, 0, GL2.GL_RGBA8, IMAGE_WIDTH, IMAGE_HEIGHT, 0, PIXEL_FORMAT,
GL2.GL_UNSIGNED_BYTE, null);
gl.glBindTexture(GL2.GL_TEXTURE_2D, 0);
}
private void generatePBOs(GL2 gl) {
gl.glGenBuffers(2, pboIds, 0);
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, pboIds[0]);
gl.glBufferData(GL2.GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, null, GL2.GL_STREAM_DRAW);
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, pboIds[1]);
gl.glBufferData(GL2.GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, null, GL2.GL_STREAM_DRAW);
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, 0);
}
private void renderPBO2Texture(GL2 gl) {
if (pboMode == 1) {
index = nextIndex = 0;
} else if (pboMode == 2) {
index = (index + 1) % 2;
nextIndex = (index + 1) % 2;
}
// start to copy from PBO to texture object
gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId[0]);
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, pboIds[index]);
// copy pixels from PBO to texture object
// Use offset instead of ponter.
gl.glTexSubImage2D(GL2.GL_TEXTURE_2D, 0, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, PIXEL_FORMAT, GL2.GL_UNSIGNED_BYTE, 0);
// bind PBO to update pixel values
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, pboIds[nextIndex]);
// map the buffer object into client's memory
// Note that glMapBufferARB() causes sync issue.
// If GPU is working with this buffer, glMapBufferARB() will wait(stall)
// for GPU to finish its job. To avoid waiting (stall), you can call
// first glBufferDataARB() with NULL pointer before glMapBufferARB().
// If you do that, the previous data in PBO will be discarded and
// glMapBufferARB() returns a new allocated pointer immediately
// even if GPU is still working with the previous data.
gl.glBufferData(GL2.GL_PIXEL_UNPACK_BUFFER, DATA_SIZE, null, GL2.GL_STREAM_DRAW);
IntBuffer intBuffer = gl.glMapBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, GL2.GL_WRITE_ONLY).asIntBuffer();
if (intBuffer != null) {
updatePixels(intBuffer);
gl.glUnmapBuffer(GL2.GL_PIXEL_UNPACK_BUFFER);
}
// it is good idea to release PBOs with ID 0 after use.
// Once bound with 0, all pixel operations behave normal ways.
gl.glBindBuffer(GL2.GL_PIXEL_UNPACK_BUFFER, 0);
}
private void updatePixels(IntBuffer intBuffer) {
// copy 4 bytes at once
for (int i = 0; i < IMAGE_HEIGHT; ++i) {
for (int j = 0; j < IMAGE_WIDTH; ++j) {
intBuffer.put(color);
}
color += 257; // add an arbitary number (no meaning)
}
++color; // scroll down
}
private void drawTexture2Screen(GL2 gl) {
gl.glTranslatef(0.0f, 0.0f, -3.0f);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glBindTexture(GL2.GL_TEXTURE_2D, textureId[0]);
gl.glColor4f(1, 1, 1, 1);
gl.glBegin(GL2.GL_QUADS);
gl.glTexCoord2f(0.0f, 0.0f);
gl.glVertex3f(-1.0f, -1.0f, 0.0f);
gl.glTexCoord2f(1.0f, 0.0f);
gl.glVertex3f(1f, -1.0f, 0.0f);
gl.glTexCoord2f(1.0f, 1.0f);
gl.glVertex3f(1.0f, 1.0f, 0.0f);
gl.glTexCoord2f(0.0f, 1.0f);
gl.glVertex3f(-1.0f, 1.0f, 0.0f);
gl.glEnd();
gl.glBindTexture(GL2.GL_TEXTURE_2D, 0);
gl.glDisable(GL.GL_TEXTURE_2D);
}
private void deletePBORelatedBuffers(GL2 gl) {
// Delete generated texture
gl.glDeleteBuffers(1, textureId, 0);
// Delete pixel buffer objects
gl.glDeleteBuffers(2, pboIds, 0);
}
// ===========================================================================
// Helper Methods
// ===========================================================================
private void toPerspective(GL2 gl) {
// set viewport to be the entire window
gl.glViewport(0, 0, screenWidth, screenHeight);
// set perspective viewing frustum
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(45.0f, (float) (screenWidth) / screenHeight, 1.0f, 100.0f);
// switch to modelview matrix in order to set scene
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadIdentity();
}
private void toOrtho(GL2 gl) {
// set viewport to be the entire window
gl.glViewport(0, 0, screenWidth, screenHeight);
// set orthographic viewing frustum
gl.glMatrixMode(GL2.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrtho(0, screenWidth, 0, screenHeight, -1, 1);
// switch to modelview matrix in order to set scene
gl.glMatrixMode(GL2.GL_MODELVIEW);
gl.glLoadIdentity();
}
// ===========================================================================
// Main Method & Variables
// ===========================================================================
private static int FPS = 60;
public static void main(String[] args) {
// Run the GUI codes in the event-dispatching thread for thread safety
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
GLCanvas canvas = new PBO2Texture();
canvas.setPreferredSize(new Dimension(400, 300));
final FPSAnimator animator = new FPSAnimator(canvas, FPS, false);
final JFrame frame = new JFrame();
frame.getContentPane().add(canvas);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
new Thread() {
@Override
public void run() {
if (animator.isStarted())
animator.stop();
System.exit(0);
}
}.start();
}
});
frame.setTitle("PBO to Texture (Texture Streaming)");
frame.pack();
frame.setVisible(true);
animator.start(); // start the animation loop
}
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment