jogl update, attempt #2. using NEWT by default, stencil buffer disabled for the time being.

This commit is contained in:
codeanticode
2012-11-11 19:36:15 +00:00
parent 7f6546067c
commit 8a38bc06b0
2 changed files with 166 additions and 247 deletions

View File

@@ -25,6 +25,7 @@ package processing.opengl;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.nio.Buffer;
import java.nio.ByteBuffer;
@@ -45,6 +46,7 @@ import javax.media.opengl.GLContext;
import javax.media.opengl.GLDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLException;
import javax.media.opengl.GLFBODrawable;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.glu.GLU;
@@ -56,6 +58,7 @@ import processing.core.PConstants;
import com.jogamp.newt.awt.NewtCanvasAWT;
import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.opengl.FBObject;
import com.jogamp.opengl.util.AnimatorBase;
/**
@@ -351,9 +354,9 @@ public class PGL {
protected boolean initialized;
/** Windowing toolkit */
protected static int toolkit = AWT;
protected static int toolkit = NEWT;
protected static boolean enable_screen_FBO_macosx = true;
protected static boolean enable_screen_FBO_macosx = false;
protected static boolean enable_screen_FBO_windows = false;
protected static boolean enable_screen_FBO_linux = false;
protected static boolean enable_screen_FBO_other = false;
@@ -396,6 +399,14 @@ public class PGL {
// FBO for anti-aliased rendering
protected int drawTexName;
protected int drawTexWidth, drawTexHeight;
protected FBObject drawFBO;
/*
protected static final boolean ENABLE_OSX_SCREEN_FBO = false;
protected static final int MIN_OSX_VER_FOR_SCREEN_FBO = 6;
protected static final int MIN_SAMPLES_FOR_SCREEN_FBO = 1;
protected boolean needScreenFBO = false;
protected int fboWidth, fboHeight;
protected int numSamples;
@@ -410,6 +421,7 @@ public class PGL {
protected int[] glDepthBuffer = { 0 };
protected int[] glStencilBuffer = { 0 };
protected int contextHashCode;
*/
///////////////////////////////////////////////////////////
@@ -510,16 +522,6 @@ public class PGL {
protected void initPrimarySurface(int antialias) {
if (PApplet.platform == PConstants.MACOSX) {
needScreenFBO = enable_screen_FBO_macosx;
} else if (PApplet.platform == PConstants.WINDOWS) {
needScreenFBO = enable_screen_FBO_windows;
} else if (PApplet.platform == PConstants.LINUX) {
needScreenFBO = enable_screen_FBO_linux;
} else {
needScreenFBO = enable_screen_FBO_other;
}
if (profile == null) {
profile = GLProfile.getDefault();
} else {
@@ -540,15 +542,25 @@ public class PGL {
// Setting up the desired GL capabilities;
GLCapabilities caps = new GLCapabilities(profile);
if (1 < antialias && !needScreenFBO) {
if (1 < antialias) {
caps.setSampleBuffers(true);
caps.setNumSamples(antialias);
} else {
caps.setSampleBuffers(false);
}
if (PApplet.platform == PConstants.MACOSX) {
caps.setFBO(enable_screen_FBO_macosx);
} else if (PApplet.platform == PConstants.WINDOWS) {
caps.setFBO(enable_screen_FBO_windows);
} else if (PApplet.platform == PConstants.LINUX) {
caps.setFBO(enable_screen_FBO_linux);
} else {
caps.setFBO(enable_screen_FBO_other);
}
caps.setDepthBits(24);
caps.setStencilBits(8);
//caps.setStencilBits(8);
caps.setAlphaBits(8);
caps.setBackgroundOpaque(true);
caps.setOnscreen(true);
@@ -571,6 +583,7 @@ public class PGL {
window = GLWindow.create(caps);
canvasNEWT = new NewtCanvasAWT(window);
canvasNEWT.setBounds(0, 0, pg.width, pg.height);
canvasNEWT.setBackground(Color.GRAY);
pg.parent.setLayout(new BorderLayout());
pg.parent.add(canvasNEWT, BorderLayout.CENTER);
@@ -585,6 +598,8 @@ public class PGL {
capabilities = window.getChosenGLCapabilities();
canvas = canvasNEWT;
canvasAWT = null;
System.out.println(capabilities);
}
initialized = true;
@@ -603,183 +618,8 @@ public class PGL {
if (!setFramerate) {
setFrameRate(targetFramerate);
}
if (needScreenFBO && glColorFbo[0] == 0) {
String ext = gl.glGetString(GL.GL_EXTENSIONS);
if (-1 < ext.indexOf("texture_non_power_of_two")) {
fboWidth = pg.width;
fboHeight = pg.height;
} else {
fboWidth = PGL.nextPowerOfTwo(pg.width);
fboHeight = PGL.nextPowerOfTwo(pg.height);
}
if (-1 < ext.indexOf("_framebuffer_multisample")) {
numSamples = qualityToSamples(pg.quality);
} else {
numSamples = 1;
}
multisample = 1 < numSamples;
if (multisample && gl2x == null) {
throw new RuntimeException("Doesn't have the OpenGL extensions " +
"necessary for multisampling.");
}
packedDepthStencil = ext.indexOf("packed_depth_stencil") != -1;
contextHashCode = context.hashCode();
// Create the color texture...
gl.glGenTextures(2, glColorTex, 0);
// for (int i = 0; i < 2; i++) { // Don't create back-buffer for now.
for (int i = 0; i < 1; i++) {
gl.glBindTexture(GL.GL_TEXTURE_2D, glColorTex[i]);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
GL.GL_NEAREST);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
GL.GL_NEAREST);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S,
GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T,
GL.GL_CLAMP_TO_EDGE);
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, fboWidth, fboHeight,
0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, null);
initTexture(GL.GL_TEXTURE_2D, PGL.RGBA, fboWidth, fboHeight);
}
gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
// ...and attach to the color framebuffer.
gl.glGenFramebuffers(1, glColorFbo, 0);
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, glColorFbo[0]);
gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0,
GL.GL_TEXTURE_2D, glColorTex[0], 0);
if (multisample) {
// Now, creating mutisampled FBO with packed depth and stencil buffers.
gl.glGenFramebuffers(1, glMultiFbo, 0);
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, glMultiFbo[0]);
// color render buffer...
gl.glGenRenderbuffers(1, glColorRenderBuffer, 0);
gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, glColorRenderBuffer[0]);
gl2x.glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, numSamples,
GL.GL_RGBA8, fboWidth, fboHeight);
gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER, GL.GL_COLOR_ATTACHMENT0,
GL.GL_RENDERBUFFER, glColorRenderBuffer[0]);
if (packedDepthStencil) {
// packed depth+stencil buffer...
gl.glGenRenderbuffers(1, glPackedDepthStencil, 0);
gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, glPackedDepthStencil[0]);
gl2x.glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, numSamples,
GL.GL_DEPTH24_STENCIL8,
fboWidth, fboHeight);
gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
GL.GL_DEPTH_ATTACHMENT,
GL.GL_RENDERBUFFER,
glPackedDepthStencil[0]);
gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
GL.GL_STENCIL_ATTACHMENT,
GL.GL_RENDERBUFFER,
glPackedDepthStencil[0]);
} else {
// Separate depth and stencil buffers...
gl.glGenRenderbuffers(1, glDepthBuffer, 0);
gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, glDepthBuffer[0]);
gl2x.glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, numSamples,
GL.GL_DEPTH_COMPONENT24,
fboWidth, fboHeight);
gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
GL.GL_DEPTH_ATTACHMENT,
GL.GL_RENDERBUFFER,
glDepthBuffer[0]);
// Some hardware doesn't support distinct depth and stencil buffers:
// http://lists.apple.com/archives/mac-opengl/2008/Aug/msg00089.html
// which just results in an unsupported framebuffer error.
gl.glGenRenderbuffers(1, glStencilBuffer, 0);
gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, glStencilBuffer[0]);
gl2x.glRenderbufferStorageMultisample(GL.GL_RENDERBUFFER, numSamples,
GL.GL_STENCIL_INDEX8,
fboWidth, fboHeight);
gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
GL.GL_STENCIL_ATTACHMENT,
GL.GL_RENDERBUFFER,
glStencilBuffer[0]);
}
validateFramebuffer();
// Clear all the buffers in the multisample FBO
gl.glClearDepth(1);
gl.glClearStencil(0);
gl.glClearColor(0, 0, 0, 0);
gl.glClear(GL.GL_DEPTH_BUFFER_BIT |
GL.GL_STENCIL_BUFFER_BIT |
GL.GL_COLOR_BUFFER_BIT);
// All set with multisampled FBO!
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, glColorFbo[0]);
} else {
if (packedDepthStencil) {
// packed depth+stencil buffer...
gl.glGenRenderbuffers(1, glPackedDepthStencil, 0);
gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, glPackedDepthStencil[0]);
gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL.GL_DEPTH24_STENCIL8,
fboWidth, fboHeight);
gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
GL.GL_DEPTH_ATTACHMENT,
GL.GL_RENDERBUFFER,
glPackedDepthStencil[0]);
gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
GL.GL_STENCIL_ATTACHMENT,
GL.GL_RENDERBUFFER,
glPackedDepthStencil[0]);
} else {
// Separate depth and stencil buffers...
gl.glGenRenderbuffers(1, glDepthBuffer, 0);
gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, glDepthBuffer[0]);
gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL.GL_DEPTH_COMPONENT24,
fboWidth, fboHeight);
gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
GL.GL_DEPTH_ATTACHMENT,
GL.GL_RENDERBUFFER,
glDepthBuffer[0]);
gl.glGenRenderbuffers(1, glStencilBuffer, 0);
gl.glBindRenderbuffer(GL.GL_RENDERBUFFER, glStencilBuffer[0]);
gl.glRenderbufferStorage(GL.GL_RENDERBUFFER, GL.GL_STENCIL_INDEX8,
fboWidth, fboHeight);
gl.glFramebufferRenderbuffer(GL.GL_FRAMEBUFFER,
GL.GL_STENCIL_ATTACHMENT,
GL.GL_RENDERBUFFER, glStencilBuffer[0]);
}
validateFramebuffer();
// Clear the depth and stencil buffers in the color FBO. There is no
// need to clear the color buffers because the textures attached were
// properly initialized blank.
gl.glClearDepth(1);
gl.glClearStencil(0);
gl.glClear(GL.GL_DEPTH_BUFFER_BIT | GL.GL_STENCIL_BUFFER_BIT);
}
// The screen framebuffer is the color FBO just created. We need
// to update the screenFramebuffer object so when the framebuffer
// is popped back to the screen, the correct id is set.
PGraphicsOpenGL.screenFramebuffer.glFbo = glColorFbo[0];
backTex = 1;
frontTex = 0;
} else {
// To make sure that the default screen buffer is used, specially after
// doing screen rendering on an FBO.
PGraphicsOpenGL.screenFramebuffer.glFbo = 0;
}
}
protected void updateOffscreen(PGL primary) {
gl = primary.gl;
gl2 = primary.gl2;
@@ -787,49 +627,59 @@ public class PGL {
}
protected int primaryDrawBuffer() {
if (glColorFbo[0] == 0) {
return GL.GL_BACK;
protected int primaryReadFramebuffer() {
if (capabilities.isFBO()) {
return context.getDefaultReadFramebuffer();
} else {
return GL.GL_COLOR_ATTACHMENT0;
return 0;
}
}
/*
protected boolean primaryIsDoubleBuffered() {
// When using the multisampled FBO, the color
// FBO is single buffered as it has only one
// texture bound to it.
//return glColorFbo[0] == 0;
return true;
protected int primaryDrawFramebuffer() {
if (capabilities.isFBO()) {
return context.getDefaultDrawFramebuffer();
} else {
return 0;
}
}
protected int primaryDrawBuffer() {
if (capabilities.isFBO()) {
return GL.GL_COLOR_ATTACHMENT0;
} else {
return GL.GL_BACK;
}
}
protected int primaryReadBuffer() {
if (capabilities.isFBO()) {
return GL.GL_COLOR_ATTACHMENT0;
} else {
return GL.GL_BACK;
}
}
*/
protected boolean primaryIsFboBacked() {
return glColorFbo[0] != 0;
return capabilities.isFBO();
}
protected int getFboTexTarget() {
return GL.GL_TEXTURE_2D;
}
protected int getFboTexName() {
return glColorTex[0];
return drawTexName;
}
protected int getFboWidth() {
return fboWidth;
return drawTexWidth;
}
protected int getFboHeight() {
return fboHeight;
return drawTexHeight;
}
/*
protected void bindPrimaryColorFBO() {
if (multisample) {
// Blit the contents of the multisampled FBO into the color FBO,
@@ -875,7 +725,7 @@ public class PGL {
gl.glDeleteRenderbuffers(1, glColorRenderBuffer, 0);
}
}
*/
protected int qualityToSamples(int quality) {
if (quality <= 1) {
@@ -887,15 +737,26 @@ public class PGL {
}
}
protected void forceUpdate() {
if (0 < capabilities.getNumSamples()) {
drawFBO.syncSamplingSink(gl);
drawFBO.bind(gl);
}
}
protected void bindBackBufferTex() {
/*
if (!texturingIsEnabled(GL.GL_TEXTURE_2D)) {
enableTexturing(GL.GL_TEXTURE_2D);
}
gl.glBindTexture(GL.GL_TEXTURE_2D, glColorTex[backTex]);
*/
}
protected void unbindBackBufferTex() {
/*
if (textureIsBound(GL.GL_TEXTURE_2D, glColorTex[backTex])) {
// We don't want to unbind another texture
// that might be bound instead of this one.
@@ -907,6 +768,7 @@ public class PGL {
gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
}
}
*/
}
@@ -916,6 +778,7 @@ public class PGL {
protected void beginOnscreenDraw(boolean clear) {
/*
if (glColorFbo[0] != 0) {
gl.glBindFramebuffer(GL.GL_FRAMEBUFFER, glColorFbo[0]);
gl.glFramebufferTexture2D(GL.GL_FRAMEBUFFER,
@@ -936,10 +799,12 @@ public class PGL {
PGraphicsOpenGL.screenFramebuffer.glFbo = glColorFbo[0];
}
}
*/
}
protected void endOnscreenDraw(boolean clear0) {
/*
if (glColorFbo[0] != 0) {
if (multisample) {
// Blit the contents of the multisampled FBO into the color FBO:
@@ -988,6 +853,7 @@ public class PGL {
// frontTex = backTex;
// backTex = temp;
}
*/
}
@@ -1635,7 +1501,11 @@ public class PGL {
public void readPixels(int x, int y, int width, int height, int format,
int type, Buffer buffer) {
gl.glReadPixels(x, y, width, height, format, type, buffer);
}
@@ -2578,12 +2448,51 @@ public class PGL {
// Java specific stuff
protected class PGLListener implements GLEventListener {
@Override
// http://www.opengl.org/wiki/Default_Framebuffer
public void display(GLAutoDrawable adrawable) {
drawable = adrawable;
context = adrawable.getContext();
if (capabilities.isFBO()) {
GLFBODrawable fboDrawable = null;
if (toolkit == AWT) {
GLCanvas drCanvas = (GLCanvas)adrawable;
fboDrawable = (GLFBODrawable)drCanvas.getDelegatedDrawable();
// FBObject fboFront = dr.getFBObject(GL.GL_FRONT);
// FBObject.Colorbuffer colorBuf = fboFront.getColorbuffer(0);
// FBObject.TextureAttachment texFront = (FBObject.TextureAttachment) colorBuf;
// System.out.println("front texture: " + texFront.getName());
} else {
GLWindow drWindow = (GLWindow)adrawable;
fboDrawable = (GLFBODrawable)drWindow.getDelegatedDrawable();
}
FBObject.TextureAttachment texAttach = null;
if (fboDrawable != null) {
//fboBack = fboDrawable.getFBObject(GL.GL_BACK);
//fboFront = fboDrawable.getFBObject(GL.GL_FRONT);
//FBObject.Colorbuffer colorBuf = fboFront.getSamplingSinkFBO().getColorbuffer(0);
//texAttach = (FBObject.TextureAttachment) colorBuf;
//texAttach = fboBack.getSamplingSink();
drawFBO = fboDrawable.getFBObject(GL.GL_BACK);
if (0 < capabilities.getNumSamples()) {
// When using multisampled FBO,the back buffer is the MSAA
// surface so it cannot read from, the one to use is the front.
texAttach = fboDrawable.getTextureBuffer(GL.GL_FRONT);
} else {
// W/out multisampling, rendering is done on the back buffer.
texAttach = fboDrawable.getTextureBuffer(GL.GL_BACK);
}
}
if (texAttach != null) {
drawTexName = texAttach.getName();
drawTexWidth = texAttach.getWidth();
drawTexHeight = texAttach.getHeight();
}
}
gl = context.getGL();
gl2 = gl.getGL2ES2();
try {
@@ -2625,11 +2534,13 @@ public class PGL {
drawable = adrawable;
context = adrawable.getContext();
/*
if (glColorFbo[0] != 0) {
// The screen FBO hack needs the FBO to be recreated when starting
// and after resizing.
glColorFbo[0] = 0;
}
*/
}
}

View File

@@ -363,7 +363,8 @@ public class PGraphicsOpenGL extends PGraphics {
static protected int fbStackDepth;
static protected FrameBuffer[] fbStack = new FrameBuffer[FB_STACK_DEPTH];
static protected FrameBuffer screenFramebuffer;
static protected FrameBuffer drawFramebuffer;
static protected FrameBuffer readFramebuffer;
static protected FrameBuffer currentFramebuffer;
// .......................................................
@@ -1552,12 +1553,17 @@ public class PGraphicsOpenGL extends PGraphics {
getGLParameters();
}
if (screenFramebuffer == null) {
screenFramebuffer = new FrameBuffer(parent, width, height, true);
setFramebuffer(screenFramebuffer);
}
if (primarySurface) {
if (drawFramebuffer == null) {
drawFramebuffer = new FrameBuffer(parent, width, height, true);
setFramebuffer(drawFramebuffer);
}
drawFramebuffer.setFBO(pgl.primaryDrawFramebuffer());
if (readFramebuffer == null) {
readFramebuffer = new FrameBuffer(parent, width, height, true);
}
readFramebuffer.setFBO(pgl.primaryReadFramebuffer());
pgl.updatePrimary();
pgl.drawBuffer(pgl.primaryDrawBuffer());
} else {
@@ -1868,30 +1874,24 @@ public class PGraphicsOpenGL extends PGraphics {
}
protected void beginPixelsOp(int op) {
if (primarySurface) {
if (pgl.primaryIsFboBacked()) {
if (op == OP_READ) {
// We read from the color FBO, but the multisample FBO is currently
// bound, so:
offscreenNotCurrent = true;
pgl.bindPrimaryColorFBO();
pgl.readBuffer(pgl.primaryDrawBuffer());
} else {
// We write directly to the multisample FBO.
offscreenNotCurrent = false;
pgl.drawBuffer(pgl.primaryDrawBuffer());
// We read or write from the back buffer, where all the
// drawing in the current frame is taking place.
pushFramebuffer();
if (op == OP_READ) {
setFramebuffer(readFramebuffer);
pgl.readBuffer(pgl.primaryReadBuffer());
if (pgl.primaryIsFboBacked()) {
pgl.forceUpdate();
}
} else {
// We read or write from the back buffer, where all the
// drawing in the current frame is taking place.
if (op == OP_READ) {
pgl.readBuffer(pgl.primaryDrawBuffer());
} else {
pgl.drawBuffer(pgl.primaryDrawBuffer());
}
offscreenNotCurrent = false;
setFramebuffer(drawFramebuffer);
pgl.drawBuffer(pgl.primaryDrawBuffer());
}
offscreenNotCurrent = true;
} else {
// Making sure that the offscreen FBO is current. This allows to do calls
// like loadPixels(), set() or get() without enclosing them between
@@ -1933,6 +1933,18 @@ public class PGraphicsOpenGL extends PGraphics {
protected void endPixelsOp() {
if (offscreenNotCurrent) {
if (!primarySurface && pixelsOp == OP_WRITE && offscreenMultisample) {
// We were writing to the multisample FBO, so we need
// to blit its contents to the color FBO.
offscreenFramebufferMultisample.copy(offscreenFramebuffer);
}
popFramebuffer();
}
pixelsOp = OP_NONE;
/*
if (offscreenNotCurrent) {
if (primarySurface) {
pgl.bindPrimaryMultiFBO();
@@ -1946,6 +1958,7 @@ public class PGraphicsOpenGL extends PGraphics {
}
}
pixelsOp = OP_NONE;
*/
}
@@ -5307,7 +5320,6 @@ public class PGraphicsOpenGL extends PGraphics {
pgl.readPixels(0, 0, width, height, PGL.RGBA, PGL.UNSIGNED_BYTE,
pixelBuffer);
endPixelsOp();
PGL.nativeToJavaARGB(pixels, width, height);
}
@@ -5403,13 +5415,9 @@ public class PGraphicsOpenGL extends PGraphics {
loadTextureImpl(Texture.POINT, false);
if (pgl.primaryIsFboBacked()) {
pgl.bindPrimaryColorFBO();
// Copy the contents of the FBO used by the primary surface into
// texture, this copy operation is very fast because it is resolved
// in the GPU.
pgl.forceUpdate();
texture.set(pgl.getFboTexTarget(), pgl.getFboTexName(),
pgl.getFboWidth(), pgl.getFboHeight(), width, height);
pgl.bindPrimaryMultiFBO();
} else {
// Here we go the slow route: we first copy the contents of the color
// buffer into a pixels array (but we keep it in native format) and
@@ -5926,7 +5934,7 @@ public class PGraphicsOpenGL extends PGraphics {
protected void bindBackTexture() {
if (primarySurface) {
pgl.bindBackBufferTex();
//pgl.bindBackBufferTex();
} else {
}
@@ -5935,7 +5943,7 @@ public class PGraphicsOpenGL extends PGraphics {
protected void unbindBackTexture() {
if (primarySurface) {
pgl.unbindBackBufferTex();
//pgl.unbindBackBufferTex();
} else {
}
@@ -6101,8 +6109,8 @@ public class PGraphicsOpenGL extends PGraphics {
OPENGL_EXTENSIONS.indexOf("_shader_objects") == -1 ||
OPENGL_EXTENSIONS.indexOf("_shading_language") == -1) {
// GLSL extensions are not present, we cannot do anything else here.
throw new RuntimeException("GLSL shaders are not supported by this " +
"video card");
throw new RuntimeException("Processing cannot run because GLSL shaders" +
" are not available.");
}
}