From 27bf2447299d1771bc17c1fa39a7d7b2e7ca583d Mon Sep 17 00:00:00 2001 From: "sha..RD" Date: Mon, 8 Apr 2013 01:01:24 +0000 Subject: [PATCH] * Added glGetError() checks after every GL call (its ugly, I know, but it helps with debugging). Added option to disable it with constant on RendererUtil.ENABLE_ERROR_CHECKING. * Set lastFb in OGLESShaderRenderer when context is reset, this is needed otherwise the state tracker would not work correctly. * Fix issue with Mesh.Mode.Hybrid that wasn't rendering triangle fans correctly (was rendering them as triangle strips instead). * Remove call to glPointSize in OGLESShaderRenderer (it would most likely crash anyway, since GLES10 calls can't be used in a GLES20 context) * TestCustomMesh now uses shorts instead of ints for the index buffer. 32-bit indices are not supported on Android and are slower on Desktop, don't use them if you can avoid it. git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10525 75d07b2b-3a1a-0410-a2c5-0572b91ccdca --- .../renderer/android/OGLESShaderRenderer.java | 177 +++++++++++++++--- .../jme3/renderer/android/RendererUtil.java | 53 ++++++ .../jme3/renderer/android/TextureUtil.java | 27 ++- .../jme3/renderer/lwjgl/LwjglRenderer.java | 2 +- .../jme3test/model/shape/TestCustomMesh.java | 4 +- 5 files changed, 219 insertions(+), 44 deletions(-) create mode 100644 engine/src/android/com/jme3/renderer/android/RendererUtil.java diff --git a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java index 6f6861130..a38b6dfa4 100644 --- a/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java +++ b/engine/src/android/com/jme3/renderer/android/OGLESShaderRenderer.java @@ -121,13 +121,6 @@ public class OGLESShaderRenderer implements Renderer { nameBuf.rewind(); } - private void checkGLError() { - int error; - while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { - throw new RendererException("OpenGL Error " + error); - } - } - public Statistics getStatistics() { return statistics; } @@ -330,6 +323,9 @@ public class OGLESShaderRenderer implements Renderer { // Allocate buffer for compressed formats. IntBuffer compressedFormats = BufferUtils.createIntBuffer(numCompressedFormats); GLES20.glGetIntegerv(GLES20.GL_COMPRESSED_TEXTURE_FORMATS, compressedFormats); + + // Check for errors after all glGet calls. + RendererUtil.checkGLError(); // Print compressed formats. for (int i = 0; i < numCompressedFormats; i++) { @@ -337,9 +333,10 @@ public class OGLESShaderRenderer implements Renderer { } TextureUtil.loadTextureFeatures(extensions); - + applyRenderState(RenderState.DEFAULT); GLES20.glDisable(GLES20.GL_DITHER); + RendererUtil.checkGLError(); useVBO = false; @@ -363,7 +360,7 @@ public class OGLESShaderRenderer implements Renderer { objManager.resetObjects(); statistics.clearMemory(); boundShader = null; -// lastFb = null; + lastFb = null; context.reset(); } @@ -383,6 +380,7 @@ public class OGLESShaderRenderer implements Renderer { \*********************************************************************/ public void setDepthRange(float start, float end) { GLES20.glDepthRangef(start, end); + RendererUtil.checkGLError(); } public void clearBuffers(boolean color, boolean depth, boolean stencil) { @@ -398,11 +396,13 @@ public class OGLESShaderRenderer implements Renderer { } if (bits != 0) { GLES20.glClear(bits); + RendererUtil.checkGLError(); } } public void setBackgroundColor(ColorRGBA color) { GLES20.glClearColor(color.r, color.g, color.b, color.a); + RendererUtil.checkGLError(); } public void applyRenderState(RenderState state) { @@ -418,24 +418,30 @@ public class OGLESShaderRenderer implements Renderer { if (state.isDepthTest() && !context.depthTestEnabled) { GLES20.glEnable(GLES20.GL_DEPTH_TEST); GLES20.glDepthFunc(GLES20.GL_LEQUAL); + RendererUtil.checkGLError(); context.depthTestEnabled = true; } else if (!state.isDepthTest() && context.depthTestEnabled) { GLES20.glDisable(GLES20.GL_DEPTH_TEST); + RendererUtil.checkGLError(); context.depthTestEnabled = false; } if (state.isDepthWrite() && !context.depthWriteEnabled) { GLES20.glDepthMask(true); + RendererUtil.checkGLError(); context.depthWriteEnabled = true; } else if (!state.isDepthWrite() && context.depthWriteEnabled) { GLES20.glDepthMask(false); + RendererUtil.checkGLError(); context.depthWriteEnabled = false; } if (state.isColorWrite() && !context.colorWriteEnabled) { GLES20.glColorMask(true, true, true, true); + RendererUtil.checkGLError(); context.colorWriteEnabled = true; } else if (!state.isColorWrite() && context.colorWriteEnabled) { GLES20.glColorMask(false, false, false, false); + RendererUtil.checkGLError(); context.colorWriteEnabled = false; } // if (state.isPointSprite() && !context.pointSprite) { @@ -452,6 +458,8 @@ public class OGLESShaderRenderer implements Renderer { GLES20.glEnable(GLES20.GL_POLYGON_OFFSET_FILL); GLES20.glPolygonOffset(state.getPolyOffsetFactor(), state.getPolyOffsetUnits()); + RendererUtil.checkGLError(); + context.polyOffsetEnabled = true; context.polyOffsetFactor = state.getPolyOffsetFactor(); context.polyOffsetUnits = state.getPolyOffsetUnits(); @@ -460,6 +468,8 @@ public class OGLESShaderRenderer implements Renderer { || state.getPolyOffsetUnits() != context.polyOffsetUnits) { GLES20.glPolygonOffset(state.getPolyOffsetFactor(), state.getPolyOffsetUnits()); + RendererUtil.checkGLError(); + context.polyOffsetFactor = state.getPolyOffsetFactor(); context.polyOffsetUnits = state.getPolyOffsetUnits(); } @@ -467,6 +477,8 @@ public class OGLESShaderRenderer implements Renderer { } else { if (context.polyOffsetEnabled) { GLES20.glDisable(GLES20.GL_POLYGON_OFFSET_FILL); + RendererUtil.checkGLError(); + context.polyOffsetEnabled = false; context.polyOffsetFactor = 0; context.polyOffsetUnits = 0; @@ -475,8 +487,10 @@ public class OGLESShaderRenderer implements Renderer { if (state.getFaceCullMode() != context.cullMode) { if (state.getFaceCullMode() == RenderState.FaceCullMode.Off) { GLES20.glDisable(GLES20.GL_CULL_FACE); + RendererUtil.checkGLError(); } else { GLES20.glEnable(GLES20.GL_CULL_FACE); + RendererUtil.checkGLError(); } switch (state.getFaceCullMode()) { @@ -484,12 +498,15 @@ public class OGLESShaderRenderer implements Renderer { break; case Back: GLES20.glCullFace(GLES20.GL_BACK); + RendererUtil.checkGLError(); break; case Front: GLES20.glCullFace(GLES20.GL_FRONT); + RendererUtil.checkGLError(); break; case FrontAndBack: GLES20.glCullFace(GLES20.GL_FRONT_AND_BACK); + RendererUtil.checkGLError(); break; default: throw new UnsupportedOperationException("Unrecognized face cull mode: " @@ -502,6 +519,7 @@ public class OGLESShaderRenderer implements Renderer { if (state.getBlendMode() != context.blendMode) { if (state.getBlendMode() == RenderState.BlendMode.Off) { GLES20.glDisable(GLES20.GL_BLEND); + RendererUtil.checkGLError(); } else { GLES20.glEnable(GLES20.GL_BLEND); switch (state.getBlendMode()) { @@ -532,6 +550,7 @@ public class OGLESShaderRenderer implements Renderer { throw new UnsupportedOperationException("Unrecognized blend mode: " + state.getBlendMode()); } + RendererUtil.checkGLError(); } context.blendMode = state.getBlendMode(); } @@ -543,6 +562,8 @@ public class OGLESShaderRenderer implements Renderer { public void setViewPort(int x, int y, int w, int h) { if (x != vpX || vpY != y || vpW != w || vpH != h) { GLES20.glViewport(x, y, w, h); + RendererUtil.checkGLError(); + vpX = x; vpY = y; vpW = w; @@ -553,10 +574,12 @@ public class OGLESShaderRenderer implements Renderer { public void setClipRect(int x, int y, int width, int height) { if (!context.clipRectEnabled) { GLES20.glEnable(GLES20.GL_SCISSOR_TEST); + RendererUtil.checkGLError(); context.clipRectEnabled = true; } if (clipX != x || clipY != y || clipW != width || clipH != height) { GLES20.glScissor(x, y, width, height); + RendererUtil.checkGLError(); clipX = x; clipY = y; clipW = width; @@ -567,6 +590,7 @@ public class OGLESShaderRenderer implements Renderer { public void clearClipRect() { if (context.clipRectEnabled) { GLES20.glDisable(GLES20.GL_SCISSOR_TEST); + RendererUtil.checkGLError(); context.clipRectEnabled = false; clipX = 0; @@ -577,10 +601,8 @@ public class OGLESShaderRenderer implements Renderer { } public void onFrame() { - int error = GLES20.glGetError(); - if (error != GLES20.GL_NO_ERROR){ - throw new RendererException("OpenGL Error " + error + ". Enable error checking for more info."); - } + RendererUtil.checkGLErrorForced(); + objManager.deleteUnused(this); } @@ -598,6 +620,8 @@ public class OGLESShaderRenderer implements Renderer { stringBuf.append(uniform.getName()).append('\0'); updateNameBuffer(); int loc = GLES20.glGetUniformLocation(shader.getId(), uniform.getName()); + RendererUtil.checkGLError(); + if (loc < 0) { uniform.setLocation(-1); // uniform is not declared in shader @@ -610,6 +634,8 @@ public class OGLESShaderRenderer implements Renderer { int shaderId = shader.getId(); if (context.boundShaderProgram != shaderId) { GLES20.glUseProgram(shaderId); + RendererUtil.checkGLError(); + statistics.onShaderUse(shader, true); boundShader = shader; context.boundShaderProgram = shaderId; @@ -626,6 +652,8 @@ public class OGLESShaderRenderer implements Renderer { if (context.boundShaderProgram != shaderId) { GLES20.glUseProgram(shaderId); + RendererUtil.checkGLError(); + statistics.onShaderUse(shader, true); boundShader = shader; context.boundShaderProgram = shaderId; @@ -730,6 +758,7 @@ public class OGLESShaderRenderer implements Renderer { default: throw new UnsupportedOperationException("Unsupported uniform type: " + uniform.getVarType()); } + RendererUtil.checkGLError(); } protected void updateShaderUniforms(Shader shader) { @@ -775,6 +804,8 @@ public class OGLESShaderRenderer implements Renderer { if (id == -1) { // Create id id = GLES20.glCreateShader(convertShaderType(source.getType())); + RendererUtil.checkGLError(); + if (id <= 0) { throw new RendererException("Invalid ID received when trying to create shader."); } @@ -821,7 +852,10 @@ public class OGLESShaderRenderer implements Renderer { // System.out.println("precision "+precision[0]); GLES20.glCompileShader(id); + RendererUtil.checkGLError(); + GLES20.glGetShaderiv(id, GLES20.GL_COMPILE_STATUS, intBuf1); + RendererUtil.checkGLError(); boolean compiledOK = intBuf1.get(0) == GLES20.GL_TRUE; String infoLog = null; @@ -830,7 +864,7 @@ public class OGLESShaderRenderer implements Renderer { // even if compile succeeded, check // log for warnings GLES20.glGetShaderiv(id, GLES20.GL_INFO_LOG_LENGTH, intBuf1); - checkGLError(); + RendererUtil.checkGLError(); infoLog = GLES20.glGetShaderInfoLog(id); } @@ -858,6 +892,7 @@ public class OGLESShaderRenderer implements Renderer { if (id == -1) { // create program id = GLES20.glCreateProgram(); + RendererUtil.checkGLError(); if (id <= 0) { throw new RendererException("Invalid ID received when trying to create shader program."); @@ -871,23 +906,30 @@ public class OGLESShaderRenderer implements Renderer { if (source.isUpdateNeeded()) { updateShaderSourceData(source); } + GLES20.glAttachShader(id, source.getId()); + RendererUtil.checkGLError(); } // link shaders to program GLES20.glLinkProgram(id); + RendererUtil.checkGLError(); + GLES20.glGetProgramiv(id, GLES20.GL_LINK_STATUS, intBuf1); + RendererUtil.checkGLError(); boolean linkOK = intBuf1.get(0) == GLES20.GL_TRUE; String infoLog = null; if (VALIDATE_SHADER || !linkOK) { GLES20.glGetProgramiv(id, GLES20.GL_INFO_LOG_LENGTH, intBuf1); + RendererUtil.checkGLError(); int length = intBuf1.get(0); if (length > 3) { // get infos infoLog = GLES20.glGetProgramInfoLog(id); + RendererUtil.checkGLError(); } } @@ -940,7 +982,10 @@ public class OGLESShaderRenderer implements Renderer { } source.clearUpdateNeeded(); + GLES20.glDeleteShader(source.getId()); + RendererUtil.checkGLError(); + source.resetObject(); } @@ -953,11 +998,15 @@ public class OGLESShaderRenderer implements Renderer { for (ShaderSource source : shader.getSources()) { if (source.getId() != -1) { GLES20.glDetachShader(shader.getId(), source.getId()); + RendererUtil.checkGLError(); + deleteShaderSource(source); } } GLES20.glDeleteProgram(shader.getId()); + RendererUtil.checkGLError(); + statistics.onDeleteShader(); shader.resetObject(); } @@ -1161,12 +1210,16 @@ public class OGLESShaderRenderer implements Renderer { int id = rb.getId(); if (id == -1) { GLES20.glGenRenderbuffers(1, intBuf1); + RendererUtil.checkGLError(); + id = intBuf1.get(0); rb.setId(id); } if (context.boundRB != id) { GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, id); + RendererUtil.checkGLError(); + context.boundRB = id; } @@ -1198,6 +1251,8 @@ public class OGLESShaderRenderer implements Renderer { imageFormat.renderBufferStorageFormat, fb.getWidth(), fb.getHeight()); + + RendererUtil.checkGLError(); } } @@ -1229,6 +1284,8 @@ public class OGLESShaderRenderer implements Renderer { convertTextureType(tex.getType()), image.getId(), 0); + + RendererUtil.checkGLError(); } public void updateFrameBufferAttachment(FrameBuffer fb, RenderBuffer rb) { @@ -1246,6 +1303,8 @@ public class OGLESShaderRenderer implements Renderer { convertAttachmentSlot(rb.getSlot()), GLES20.GL_RENDERBUFFER, rb.getId()); + + RendererUtil.checkGLError(); } } @@ -1255,6 +1314,8 @@ public class OGLESShaderRenderer implements Renderer { intBuf1.clear(); // create FBO GLES20.glGenFramebuffers(1, intBuf1); + RendererUtil.checkGLError(); + id = intBuf1.get(0); fb.setId(id); objManager.registerForCleanup(fb); @@ -1264,6 +1325,8 @@ public class OGLESShaderRenderer implements Renderer { if (context.boundFBO != id) { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, id); + RendererUtil.checkGLError(); + // binding an FBO automatically sets draw buf to GL_COLOR_ATTACHMENT0 context.boundDrawBuf = 0; context.boundFBO = id; @@ -1309,6 +1372,7 @@ public class OGLESShaderRenderer implements Renderer { // int textureType = convertTextureType(tex.getType(), tex.getImage().getMultiSamples(), rb.getFace()); int textureType = convertTextureType(tex.getType()); GLES20.glGenerateMipmap(textureType); + RendererUtil.checkGLError(); } } } @@ -1317,6 +1381,8 @@ public class OGLESShaderRenderer implements Renderer { // unbind any fbos if (context.boundFBO != 0) { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + RendererUtil.checkGLError(); + statistics.onFrameBufferUse(null, true); context.boundFBO = 0; @@ -1347,6 +1413,8 @@ public class OGLESShaderRenderer implements Renderer { if (context.boundFBO != fb.getId()) { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb.getId()); + RendererUtil.checkGLError(); + statistics.onFrameBufferUse(fb, true); // update viewport to reflect framebuffer's resolution @@ -1395,6 +1463,8 @@ public class OGLESShaderRenderer implements Renderer { // select this draw buffer if (context.boundDrawBuf != rb.getSlot()) { GLES20.glActiveTexture(convertAttachmentSlot(rb.getSlot())); + RendererUtil.checkGLError(); + context.boundDrawBuf = rb.getSlot(); } } @@ -1427,6 +1497,8 @@ public class OGLESShaderRenderer implements Renderer { setFrameBuffer(fb); if (context.boundReadBuf != rb.getSlot()) { GLES20.glActiveTexture(convertAttachmentSlot(rb.getSlot())); + RendererUtil.checkGLError(); + context.boundReadBuf = rb.getSlot(); } } else { @@ -1434,17 +1506,21 @@ public class OGLESShaderRenderer implements Renderer { } GLES20.glReadPixels(vpX, vpY, vpW, vpH, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuf); + RendererUtil.checkGLError(); } private void deleteRenderBuffer(FrameBuffer fb, RenderBuffer rb) { intBuf1.put(0, rb.getId()); GLES20.glDeleteRenderbuffers(1, intBuf1); + RendererUtil.checkGLError(); } public void deleteFrameBuffer(FrameBuffer fb) { if (fb.getId() != -1) { if (context.boundFBO == fb.getId()) { GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); + RendererUtil.checkGLError(); + context.boundFBO = 0; } @@ -1457,6 +1533,8 @@ public class OGLESShaderRenderer implements Renderer { intBuf1.put(0, fb.getId()); GLES20.glDeleteFramebuffers(1, intBuf1); + RendererUtil.checkGLError(); + fb.resetObject(); statistics.onDeleteFrameBuffer(); @@ -1539,6 +1617,7 @@ public class OGLESShaderRenderer implements Renderer { GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MIN_FILTER, minFilter); GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_MAG_FILTER, magFilter); + RendererUtil.checkGLError(); /* if (tex.getAnisotropicFilter() > 1){ @@ -1565,6 +1644,8 @@ public class OGLESShaderRenderer implements Renderer { // fall down here is intentional.. // case OneDimensional: GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, convertWrapMode(tex.getWrap(WrapAxis.S))); + + RendererUtil.checkGLError(); break; default: throw new UnsupportedOperationException("Unknown texture type: " + tex.getType()); @@ -1594,6 +1675,8 @@ public class OGLESShaderRenderer implements Renderer { if (texId == -1) { // create texture GLES20.glGenTextures(1, intBuf1); + RendererUtil.checkGLError(); + texId = intBuf1.get(0); img.setId(texId); objManager.registerForCleanup(img); @@ -1606,10 +1689,14 @@ public class OGLESShaderRenderer implements Renderer { if (context.boundTextures[0] != img) { if (context.boundTextureUnit != 0) { GLES20.glActiveTexture(GLES20.GL_TEXTURE0); + RendererUtil.checkGLError(); + context.boundTextureUnit = 0; } GLES20.glBindTexture(target, texId); + RendererUtil.checkGLError(); + context.boundTextures[0] = img; } @@ -1697,6 +1784,8 @@ public class OGLESShaderRenderer implements Renderer { } GLES20.glBindTexture(type, texId); + RendererUtil.checkGLError(); + textures[unit] = image; statistics.onTextureUse(tex.getImage(), true); @@ -1734,6 +1823,8 @@ public class OGLESShaderRenderer implements Renderer { intBuf1.position(0).limit(1); GLES20.glDeleteTextures(1, intBuf1); + RendererUtil.checkGLError(); + image.resetObject(); statistics.onDeleteTexture(); @@ -1791,6 +1882,8 @@ public class OGLESShaderRenderer implements Renderer { if (bufId == -1) { // create buffer GLES20.glGenBuffers(1, intBuf1); + RendererUtil.checkGLError(); + bufId = intBuf1.get(0); vb.setId(bufId); objManager.registerForCleanup(vb); @@ -1804,12 +1897,16 @@ public class OGLESShaderRenderer implements Renderer { target = GLES20.GL_ELEMENT_ARRAY_BUFFER; if (context.boundElementArrayVBO != bufId) { GLES20.glBindBuffer(target, bufId); + RendererUtil.checkGLError(); + context.boundElementArrayVBO = bufId; } } else { target = GLES20.GL_ARRAY_BUFFER; if (context.boundArrayVBO != bufId) { GLES20.glBindBuffer(target, bufId); + RendererUtil.checkGLError(); + context.boundArrayVBO = bufId; } } @@ -1825,21 +1922,21 @@ public class OGLESShaderRenderer implements Renderer { case Byte: case UnsignedByte: GLES20.glBufferData(target, size, (ByteBuffer) vb.getData(), usage); + RendererUtil.checkGLError(); break; - //case Half: case Short: case UnsignedShort: GLES20.glBufferData(target, size, (ShortBuffer) vb.getData(), usage); + RendererUtil.checkGLError(); break; case Int: case UnsignedInt: GLES20.glBufferData(target, size, (IntBuffer) vb.getData(), usage); + RendererUtil.checkGLError(); break; case Float: GLES20.glBufferData(target, size, (FloatBuffer) vb.getData(), usage); - break; - case Double: - GLES20.glBufferData(target, size, (DoubleBuffer) vb.getData(), usage); + RendererUtil.checkGLError(); break; default: throw new RuntimeException("Unknown buffer format."); @@ -1851,20 +1948,21 @@ public class OGLESShaderRenderer implements Renderer { case Byte: case UnsignedByte: GLES20.glBufferSubData(target, 0, size, (ByteBuffer) vb.getData()); + RendererUtil.checkGLError(); break; case Short: case UnsignedShort: GLES20.glBufferSubData(target, 0, size, (ShortBuffer) vb.getData()); + RendererUtil.checkGLError(); break; case Int: case UnsignedInt: GLES20.glBufferSubData(target, 0, size, (IntBuffer) vb.getData()); + RendererUtil.checkGLError(); break; case Float: GLES20.glBufferSubData(target, 0, size, (FloatBuffer) vb.getData()); - break; - case Double: - GLES20.glBufferSubData(target, 0, size, (DoubleBuffer) vb.getData()); + RendererUtil.checkGLError(); break; default: throw new RuntimeException("Unknown buffer format."); @@ -1881,6 +1979,8 @@ public class OGLESShaderRenderer implements Renderer { intBuf1.position(0).limit(1); GLES20.glDeleteBuffers(1, intBuf1); + RendererUtil.checkGLError(); + vb.resetObject(); } } @@ -1891,6 +1991,8 @@ public class OGLESShaderRenderer implements Renderer { int idx = attribList.oldList[i]; GLES20.glDisableVertexAttribArray(idx); + RendererUtil.checkGLError(); + context.boundAttribs[idx] = null; } context.attribIndexList.copyNewToOld(); @@ -1920,6 +2022,7 @@ public class OGLESShaderRenderer implements Renderer { String attributeName = "in" + vb.getBufferType().name(); loc = GLES20.glGetAttribLocation(programId, attributeName); + RendererUtil.checkGLError(); // not really the name of it in the shader (inPosition\0) but // the internal name of the enum (Position). @@ -1934,6 +2037,7 @@ public class OGLESShaderRenderer implements Renderer { VertexBuffer[] attribs = context.boundAttribs; if (!context.attribIndexList.moveToNew(loc)) { GLES20.glEnableVertexAttribArray(loc); + RendererUtil.checkGLError(); //System.out.println("Enabled ATTRIB IDX: "+loc); } if (attribs[loc] != vb) { @@ -1946,8 +2050,9 @@ public class OGLESShaderRenderer implements Renderer { } if (context.boundArrayVBO != bufId) { - GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufId); + RendererUtil.checkGLError(); + context.boundArrayVBO = bufId; } @@ -1959,6 +2064,8 @@ public class OGLESShaderRenderer implements Renderer { vb.isNormalized(), vb.getStride(), 0); + + RendererUtil.checkGLError(); attribs[loc] = vb; } @@ -1977,6 +2084,7 @@ public class OGLESShaderRenderer implements Renderer { vertCount, count); }else{*/ GLES20.glDrawArrays(convertElementMode(mode), 0, vertCount); + RendererUtil.checkGLError(); /* }*/ } @@ -1994,11 +2102,13 @@ public class OGLESShaderRenderer implements Renderer { assert bufId != -1; if (bufId == -1) { - logger.warning("invalid buffer id!"); + throw new RendererException("Invalid buffer ID"); } if (context.boundElementArrayVBO != bufId) { GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, bufId); + RendererUtil.checkGLError(); + context.boundElementArrayVBO = bufId; } @@ -2044,6 +2154,7 @@ public class OGLESShaderRenderer implements Renderer { } else { indexBuf.getData().position(curOffset); GLES20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData()); + RendererUtil.checkGLError(); /* glDrawRangeElements(elMode, 0, @@ -2074,6 +2185,7 @@ public class OGLESShaderRenderer implements Renderer { indexBuf.getData().limit(), convertVertexBufferFormat(indexBuf.getFormat()), 0); + RendererUtil.checkGLError(); } } } @@ -2180,6 +2292,7 @@ public class OGLESShaderRenderer implements Renderer { drawTriangleList_Array(indices, mesh, count); } else { GLES20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount()); + RendererUtil.checkGLError(); } clearVertexAttribs(); clearTextureUnits(); @@ -2219,18 +2332,23 @@ public class OGLESShaderRenderer implements Renderer { } else { // throw new UnsupportedOperationException("Cannot render without index buffer"); GLES20.glDrawArrays(convertElementMode(mesh.getMode()), 0, mesh.getVertexCount()); + RendererUtil.checkGLError(); } clearVertexAttribs(); clearTextureUnits(); } public void renderMesh(Mesh mesh, int lod, int count) { + /* + * NOTE: not supported in OpenGL ES 2.0. if (context.pointSize != mesh.getPointSize()) { GLES10.glPointSize(mesh.getPointSize()); context.pointSize = mesh.getPointSize(); } + */ if (context.lineWidth != mesh.getLineWidth()) { GLES20.glLineWidth(mesh.getLineWidth()); + RendererUtil.checkGLError(); context.lineWidth = mesh.getLineWidth(); } @@ -2281,12 +2399,13 @@ public class OGLESShaderRenderer implements Renderer { if (i == stripStart) { elMode = convertElementMode(Mode.TriangleStrip); } else if (i == fanStart) { - elMode = convertElementMode(Mode.TriangleStrip); + elMode = convertElementMode(Mode.TriangleFan); } int elementLength = elementLengths[i]; indexBuf.getData().position(curOffset); GLES20.glDrawElements(elMode, elementLength, fmt, indexBuf.getData()); + RendererUtil.checkGLError(); curOffset += elementLength * elSize; } @@ -2296,6 +2415,7 @@ public class OGLESShaderRenderer implements Renderer { indexBuf.getData().limit(), convertVertexBufferFormat(indexBuf.getFormat()), indexBuf.getData()); + RendererUtil.checkGLError(); } } @@ -2323,6 +2443,8 @@ public class OGLESShaderRenderer implements Renderer { String attributeName = "in" + vb.getBufferType().name(); loc = GLES20.glGetAttribLocation(programId, attributeName); + RendererUtil.checkGLError(); + if (loc < 0) { attrib.setLocation(-1); return; // not available in shader. @@ -2345,8 +2467,11 @@ public class OGLESShaderRenderer implements Renderer { vb.isNormalized(), vb.getStride(), avb.getData()); + + RendererUtil.checkGLError(); GLES20.glEnableVertexAttribArray(loc); + RendererUtil.checkGLError(); attribs[loc] = vb; } // if (attribs[loc] != vb) @@ -2366,8 +2491,10 @@ public class OGLESShaderRenderer implements Renderer { public void setAlphaToCoverage(boolean value) { if (value) { GLES20.glEnable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE); + RendererUtil.checkGLError(); } else { GLES20.glDisable(GLES20.GL_SAMPLE_ALPHA_TO_COVERAGE); + RendererUtil.checkGLError(); } } @@ -2375,6 +2502,6 @@ public class OGLESShaderRenderer implements Renderer { public void invalidateState() { context.reset(); boundShader = null; -// lastFb = null; + lastFb = null; } } diff --git a/engine/src/android/com/jme3/renderer/android/RendererUtil.java b/engine/src/android/com/jme3/renderer/android/RendererUtil.java new file mode 100644 index 000000000..a84ca1775 --- /dev/null +++ b/engine/src/android/com/jme3/renderer/android/RendererUtil.java @@ -0,0 +1,53 @@ +package com.jme3.renderer.android; + +import android.opengl.GLES20; +import android.opengl.GLU; +import com.jme3.renderer.RendererException; + +/** + * Utility class used by the {@link OGLESShaderRenderer renderer} and sister classes. + * + * @author Kirill Vainer + */ +public class RendererUtil { + + /** + * When set to true, every OpenGL call will check for errors and throw + * an exception if there is one, if false, no error checking is performed. + */ + public static boolean ENABLE_ERROR_CHECKING = true; + + /** + * Checks for an OpenGL error and throws a {@link RendererException} + * if there is one. Ignores the value of {@link RendererUtil#ENABLE_ERROR_CHECKING}. + */ + public static void checkGLErrorForced() { + int error = GLES20.glGetError(); + if (error != 0) { + String message = GLU.gluErrorString(error); + if (message == null) { + throw new RendererException("An unknown OpenGL error has occurred."); + } else { + throw new RendererException("An OpenGL error has occurred: " + message); + } + } + } + + /** + * Checks for an OpenGL error and throws a {@link RendererException} + * if there is one. Does nothing if {@link RendererUtil#ENABLE_ERROR_CHECKING} + * is set to false. + */ + public static void checkGLError() { + if (!ENABLE_ERROR_CHECKING) return; + int error = GLES20.glGetError(); + if (error != 0) { + String message = GLU.gluErrorString(error); + if (message == null) { + throw new RendererException("An unknown OpenGL error has occurred."); + } else { + throw new RendererException("An OpenGL error has occurred: " + message); + } + } + } +} diff --git a/engine/src/android/com/jme3/renderer/android/TextureUtil.java b/engine/src/android/com/jme3/renderer/android/TextureUtil.java index cd69c0895..aa55f30a6 100644 --- a/engine/src/android/com/jme3/renderer/android/TextureUtil.java +++ b/engine/src/android/com/jme3/renderer/android/TextureUtil.java @@ -103,10 +103,10 @@ public class TextureUtil { logger.log(Level.FINEST, " - Uploading bitmap directly. Cannot compress as alpha present."); if (subTexture) { GLUtils.texSubImage2D(target, level, x, y, bitmap); - checkGLError(); + RendererUtil.checkGLError(); } else { GLUtils.texImage2D(target, level, bitmap, 0); - checkGLError(); + RendererUtil.checkGLError(); } } else { // Convert to RGB565 @@ -150,7 +150,8 @@ public class TextureUtil { ETC1.ETC1_RGB8_OES, etc1tex.getData().capacity(), etc1tex.getData()); - checkGLError(); + + RendererUtil.checkGLError(); } else { GLES20.glCompressedTexImage2D(target, level, @@ -160,7 +161,8 @@ public class TextureUtil { 0, etc1tex.getData().capacity(), etc1tex.getData()); - checkGLError(); + + RendererUtil.checkGLError(); } // ETC1Util.loadTexture(target, level, 0, GLES20.GL_RGB, @@ -208,17 +210,17 @@ public class TextureUtil { if (subTexture) { System.err.println("x : " + x + " y :" + y + " , " + bitmap.getWidth() + "/" + bitmap.getHeight()); GLUtils.texSubImage2D(target, 0, x, y, bitmap); - checkGLError(); + RendererUtil.checkGLError(); } else { GLUtils.texImage2D(target, 0, bitmap, 0); - checkGLError(); + RendererUtil.checkGLError(); } if (needMips) { // No pregenerated mips available, // generate from base level if required GLES20.glGenerateMipmap(target); - checkGLError(); + RendererUtil.checkGLError(); } } } @@ -485,13 +487,6 @@ public class TextureUtil { } } - private static void checkGLError() { - int error; - while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { - throw new RendererException("OpenGL Error " + error); - } - } - /** * Update the texture currently bound to target at with data from the given * Image at position x and y. The parameter index is used as the zoffset in @@ -564,10 +559,10 @@ public class TextureUtil { if (imageFormat.compress && data != null) { GLES20.glCompressedTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, data.remaining(), data); - checkGLError(); + RendererUtil.checkGLError(); } else { GLES20.glTexSubImage2D(target, i, x, y, mipWidth, mipHeight, imageFormat.format, imageFormat.dataType, data); - checkGLError(); + RendererUtil.checkGLError(); } pos += mipSizes[i]; diff --git a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java index cbe78370f..59933ecc1 100644 --- a/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java +++ b/engine/src/lwjgl/com/jme3/renderer/lwjgl/LwjglRenderer.java @@ -2256,7 +2256,7 @@ public class LwjglRenderer implements Renderer { if (i == stripStart) { elMode = convertElementMode(Mode.TriangleStrip); } else if (i == fanStart) { - elMode = convertElementMode(Mode.TriangleStrip); + elMode = convertElementMode(Mode.TriangleFan); } int elementLength = elementLengths[i]; diff --git a/engine/src/test/jme3test/model/shape/TestCustomMesh.java b/engine/src/test/jme3test/model/shape/TestCustomMesh.java index 9a9e91f55..e11b5264d 100644 --- a/engine/src/test/jme3test/model/shape/TestCustomMesh.java +++ b/engine/src/test/jme3test/model/shape/TestCustomMesh.java @@ -75,12 +75,12 @@ public class TestCustomMesh extends SimpleApplication { texCoord[3] = new Vector2f(1,1); // Indexes. We define the order in which mesh should be constructed - int [] indexes = {2,0,1,1,3,2}; + short[] indexes = {2, 0, 1, 1, 3, 2}; // Setting buffers m.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices)); m.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord)); - m.setBuffer(Type.Index, 1, BufferUtils.createIntBuffer(indexes)); + m.setBuffer(Type.Index, 1, BufferUtils.createShortBuffer(indexes)); m.updateBound(); // *************************************************************************