|
@@ -71,6 +71,7 @@ import java.util.logging.Logger;
|
|
|
import java.util.regex.Matcher;
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
+
|
|
|
public final class GLRenderer implements Renderer {
|
|
|
|
|
|
private static final Logger logger = Logger.getLogger(GLRenderer.class.getName());
|
|
@@ -166,9 +167,28 @@ public final class GLRenderer implements Renderer {
|
|
|
}
|
|
|
|
|
|
private void loadCapabilitiesES() {
|
|
|
+ int oglVer = extractVersion(gl.glGetString(GL.GL_VERSION));
|
|
|
caps.add(Caps.GLSL100);
|
|
|
caps.add(Caps.OpenGLES20);
|
|
|
|
|
|
+ caps.add(Caps.Multisample);
|
|
|
+
|
|
|
+ if (oglVer >= 300) {
|
|
|
+ caps.add(Caps.OpenGLES30);
|
|
|
+ caps.add(Caps.GLSL300);
|
|
|
+ // Instancing is core in GLES300
|
|
|
+ caps.add(Caps.MeshInstancing);
|
|
|
+ }
|
|
|
+ if (oglVer >= 310) {
|
|
|
+ caps.add(Caps.OpenGLES31);
|
|
|
+ caps.add(Caps.GLSL310);
|
|
|
+ }
|
|
|
+ if (oglVer >= 320) {
|
|
|
+ caps.add(Caps.OpenGLES32);
|
|
|
+ caps.add(Caps.GLSL320);
|
|
|
+ caps.add(Caps.GeometryShader);
|
|
|
+ caps.add(Caps.TesselationShader);
|
|
|
+ }
|
|
|
// Important: Do not add OpenGL20 - that's the desktop capability!
|
|
|
}
|
|
|
|
|
@@ -293,6 +313,7 @@ public final class GLRenderer implements Renderer {
|
|
|
|
|
|
if (hasExtension("GL_ARB_draw_instanced") &&
|
|
|
hasExtension("GL_ARB_instanced_arrays")) {
|
|
|
+ // TODO: If there were a way to call the EXT extension for GLES2, should check also (hasExtension("GL_EXT_draw_instanced") && hasExtension("GL_EXT_instanced_arrays"))
|
|
|
caps.add(Caps.MeshInstancing);
|
|
|
}
|
|
|
|
|
@@ -330,8 +351,10 @@ public final class GLRenderer implements Renderer {
|
|
|
|
|
|
if (hasExtension("GL_OES_depth_texture") || gl2 != null) {
|
|
|
caps.add(Caps.DepthTexture);
|
|
|
+ }
|
|
|
|
|
|
- // TODO: GL_OES_depth24
|
|
|
+ if (hasExtension("GL_OES_depth24")) {
|
|
|
+ caps.add(Caps.Depth24);
|
|
|
}
|
|
|
|
|
|
if (hasExtension("GL_OES_rgb8_rgba8") ||
|
|
@@ -340,7 +363,7 @@ public final class GLRenderer implements Renderer {
|
|
|
caps.add(Caps.Rgba8);
|
|
|
}
|
|
|
|
|
|
- if (caps.contains(Caps.OpenGL30) || hasExtension("GL_OES_packed_depth_stencil")) {
|
|
|
+ if (caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30) || hasExtension("GL_OES_packed_depth_stencil")) {
|
|
|
caps.add(Caps.PackedDepthStencilBuffer);
|
|
|
}
|
|
|
|
|
@@ -350,7 +373,7 @@ public final class GLRenderer implements Renderer {
|
|
|
caps.add(Caps.FloatColorBuffer);
|
|
|
}
|
|
|
|
|
|
- if (hasExtension("GL_ARB_depth_buffer_float")) {
|
|
|
+ if (caps.contains(Caps.OpenGLES30) || hasExtension("GL_ARB_depth_buffer_float")) {
|
|
|
caps.add(Caps.FloatDepthBuffer);
|
|
|
}
|
|
|
|
|
@@ -397,7 +420,7 @@ public final class GLRenderer implements Renderer {
|
|
|
caps.add(Caps.PartialNonPowerOfTwoTextures);
|
|
|
}
|
|
|
|
|
|
- if (hasExtension("GL_EXT_texture_array") || caps.contains(Caps.OpenGL30)) {
|
|
|
+ if (hasExtension("GL_EXT_texture_array") || caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30)) {
|
|
|
caps.add(Caps.TextureArray);
|
|
|
}
|
|
|
|
|
@@ -414,16 +437,16 @@ public final class GLRenderer implements Renderer {
|
|
|
limits.put(Limits.RenderBufferSize, getInteger(GLFbo.GL_MAX_RENDERBUFFER_SIZE_EXT));
|
|
|
limits.put(Limits.FrameBufferAttachments, getInteger(GLFbo.GL_MAX_COLOR_ATTACHMENTS_EXT));
|
|
|
|
|
|
- if (hasExtension("GL_EXT_framebuffer_blit") || caps.contains(Caps.OpenGL30)) {
|
|
|
+ if (hasExtension("GL_EXT_framebuffer_blit") || caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30)) {
|
|
|
caps.add(Caps.FrameBufferBlit);
|
|
|
}
|
|
|
|
|
|
- if (hasExtension("GL_EXT_framebuffer_multisample")) {
|
|
|
+ if (hasExtension("GL_EXT_framebuffer_multisample") || caps.contains(Caps.OpenGLES30)) {
|
|
|
caps.add(Caps.FrameBufferMultisample);
|
|
|
limits.put(Limits.FrameBufferSamples, getInteger(GLExt.GL_MAX_SAMPLES_EXT));
|
|
|
}
|
|
|
|
|
|
- if (hasExtension("GL_ARB_texture_multisample")) {
|
|
|
+ if (hasExtension("GL_ARB_texture_multisample") || caps.contains(Caps.OpenGLES31)) { // GLES31 does not fully support it
|
|
|
caps.add(Caps.TextureMultisample);
|
|
|
limits.put(Limits.ColorTextureSamples, getInteger(GLExt.GL_MAX_COLOR_TEXTURE_SAMPLES));
|
|
|
limits.put(Limits.DepthTextureSamples, getInteger(GLExt.GL_MAX_DEPTH_TEXTURE_SAMPLES));
|
|
@@ -432,8 +455,8 @@ public final class GLRenderer implements Renderer {
|
|
|
limits.put(Limits.FrameBufferSamples, limits.get(Limits.ColorTextureSamples));
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- if (hasExtension("GL_ARB_draw_buffers") || caps.contains(Caps.OpenGL30)) {
|
|
|
+
|
|
|
+ if (hasExtension("GL_ARB_draw_buffers") || caps.contains(Caps.OpenGL30) || caps.contains(Caps.OpenGLES30)) {
|
|
|
limits.put(Limits.FrameBufferMrtAttachments, getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB));
|
|
|
if (limits.get(Limits.FrameBufferMrtAttachments) > 1) {
|
|
|
caps.add(Caps.FrameBufferMRT);
|
|
@@ -443,7 +466,7 @@ public final class GLRenderer implements Renderer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (hasExtension("GL_ARB_multisample")) {
|
|
|
+ if (hasExtension("GL_ARB_multisample") /*|| caps.contains(Caps.OpenGLES20)*/) {
|
|
|
boolean available = getInteger(GLExt.GL_SAMPLE_BUFFERS_ARB) != 0;
|
|
|
int samples = getInteger(GLExt.GL_SAMPLES_ARB);
|
|
|
logger.log(Level.FINER, "Samples: {0}", samples);
|
|
@@ -498,6 +521,14 @@ public final class GLRenderer implements Renderer {
|
|
|
limits.put(Limits.UniformBufferObjectMaxVertexBlocks, getInteger(GL3.GL_MAX_VERTEX_UNIFORM_BLOCKS));
|
|
|
}
|
|
|
|
|
|
+ if (hasExtension("GL_OES_geometry_shader") || hasExtension("GL_EXT_geometry_shader")) {
|
|
|
+ caps.add(Caps.GeometryShader);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hasExtension("GL_OES_tessellation_shader") || hasExtension("GL_EXT_tessellation_shader")) {
|
|
|
+ caps.add(Caps.TesselationShader);
|
|
|
+ }
|
|
|
+
|
|
|
// Print context information
|
|
|
logger.log(Level.INFO, "OpenGL Renderer Information\n" +
|
|
|
" * Vendor: {0}\n" +
|
|
@@ -539,7 +570,7 @@ public final class GLRenderer implements Renderer {
|
|
|
}
|
|
|
|
|
|
private void loadCapabilities() {
|
|
|
- if (gl2 != null) {
|
|
|
+ if (gl2 != null && !(gl instanceof GLES_30)) {
|
|
|
loadCapabilitiesGL2();
|
|
|
} else {
|
|
|
loadCapabilitiesES();
|
|
@@ -1327,10 +1358,11 @@ public final class GLRenderer implements Renderer {
|
|
|
throw new RendererException("Cannot recompile shader source");
|
|
|
}
|
|
|
|
|
|
+ boolean gles3 = caps.contains(Caps.OpenGLES30);
|
|
|
boolean gles2 = caps.contains(Caps.OpenGLES20);
|
|
|
String language = source.getLanguage();
|
|
|
|
|
|
- if (gles2 && !language.equals("GLSL100")) {
|
|
|
+ if (!gles3 && gles2 && !language.equals("GLSL100")) { //avoid this check for gles3
|
|
|
throw new RendererException("This shader cannot run in OpenGL ES 2. "
|
|
|
+ "Only GLSL 1.00 shaders are supported.");
|
|
|
}
|
|
@@ -1339,24 +1371,25 @@ public final class GLRenderer implements Renderer {
|
|
|
// Upload shader source.
|
|
|
// Merge the defines and source code.
|
|
|
stringBuf.setLength(0);
|
|
|
+ int version = Integer.parseInt(language.substring(4));
|
|
|
if (language.startsWith("GLSL")) {
|
|
|
- int version = Integer.parseInt(language.substring(4));
|
|
|
if (version > 100) {
|
|
|
stringBuf.append("#version ");
|
|
|
stringBuf.append(language.substring(4));
|
|
|
if (version >= 150) {
|
|
|
- stringBuf.append(" core");
|
|
|
+ if(gles3) {
|
|
|
+ stringBuf.append(" es");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ stringBuf.append(" core");
|
|
|
+ }
|
|
|
}
|
|
|
stringBuf.append("\n");
|
|
|
} else {
|
|
|
- if (gles2) {
|
|
|
+ if (gles2 || gles3) {
|
|
|
// request GLSL ES (1.00) when compiling under GLES2.
|
|
|
stringBuf.append("#version 100\n");
|
|
|
|
|
|
- if (source.getType() == ShaderType.Fragment) {
|
|
|
- // GLES2 requires precision qualifier.
|
|
|
- insertPrecision = true;
|
|
|
- }
|
|
|
} else {
|
|
|
// version 100 does not exist in desktop GLSL.
|
|
|
// put version 110 in that case to enable strict checking
|
|
@@ -1364,6 +1397,15 @@ public final class GLRenderer implements Renderer {
|
|
|
stringBuf.append("#version 110\n");
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ if (gles2 || gles3) {
|
|
|
+ //Inserting precision only to fragment shaders creates some link failures because of different precision between shaders
|
|
|
+ //But adding the precision to all shaders generates rendering glitches in some devices if not set to highp
|
|
|
+ if (source.getType() == ShaderType.Fragment) {
|
|
|
+ // GLES requires precision qualifier.
|
|
|
+ insertPrecision = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (linearizeSrgbImages) {
|
|
@@ -1375,11 +1417,22 @@ public final class GLRenderer implements Renderer {
|
|
|
stringBuf.append(source.getSource());
|
|
|
|
|
|
if(insertPrecision){
|
|
|
+ // default precision could be defined in GLSLCompat.glsllib so final users can use custom defined precision instead
|
|
|
// precision token is not a preprocessor dirrective therefore it must be placed after #extension tokens to avoid
|
|
|
// Error P0001: Extension directive must occur before any non-preprocessor tokens
|
|
|
int idx = stringBuf.lastIndexOf("#extension");
|
|
|
idx = stringBuf.indexOf("\n", idx);
|
|
|
- stringBuf.insert(idx + 1, "precision mediump float;\n");
|
|
|
+
|
|
|
+ if(version>=310) {
|
|
|
+ stringBuf.insert(idx + 1, "precision highp sampler2DMS;\n");
|
|
|
+ }
|
|
|
+ if(version>=300) {
|
|
|
+ stringBuf.insert(idx + 1, "precision highp sampler2DArray;\n");
|
|
|
+ stringBuf.insert(idx + 1, "precision highp sampler2DShadow;\n");
|
|
|
+ stringBuf.insert(idx + 1, "precision highp sampler3D;\n");
|
|
|
+ stringBuf.insert(idx + 1, "precision highp sampler2D;\n");
|
|
|
+ }
|
|
|
+ stringBuf.insert(idx + 1, "precision highp float;\n");
|
|
|
}
|
|
|
|
|
|
intBuf1.clear();
|
|
@@ -1612,6 +1665,7 @@ public final class GLRenderer implements Renderer {
|
|
|
if (copyDepth) {
|
|
|
mask |= GL.GL_DEPTH_BUFFER_BIT;
|
|
|
}
|
|
|
+
|
|
|
glfbo.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1,
|
|
|
dstX0, dstY0, dstX1, dstY1, mask,
|
|
|
GL.GL_NEAREST);
|
|
@@ -1842,7 +1896,7 @@ public final class GLRenderer implements Renderer {
|
|
|
}
|
|
|
|
|
|
public void setReadDrawBuffers(FrameBuffer fb) {
|
|
|
- if (gl2 == null) {
|
|
|
+ if (gl2 == null || gl instanceof GLES_30) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -1982,6 +2036,7 @@ public final class GLRenderer implements Renderer {
|
|
|
context.boundReadBuf = rb.getSlot();
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
} else {
|
|
|
setFrameBuffer(null);
|
|
|
}
|
|
@@ -2048,7 +2103,7 @@ public final class GLRenderer implements Renderer {
|
|
|
return GLExt.GL_TEXTURE_2D_ARRAY_EXT;
|
|
|
}
|
|
|
case ThreeDimensional:
|
|
|
- if (!caps.contains(Caps.OpenGL20)) {
|
|
|
+ if (!caps.contains(Caps.OpenGL20) && !caps.contains(Caps.OpenGLES30)) {
|
|
|
throw new RendererException("3D textures are not supported" +
|
|
|
" by the video hardware.");
|
|
|
}
|
|
@@ -2172,9 +2227,9 @@ public final class GLRenderer implements Renderer {
|
|
|
switch (tex.getType()) {
|
|
|
case ThreeDimensional:
|
|
|
case CubeMap: // cubemaps use 3D coords
|
|
|
- if (gl2 != null && curState.rWrap != tex.getWrap(WrapAxis.R)) {
|
|
|
+ if (gl2 != null && (caps.contains(Caps.OpenGL20) || caps.contains(Caps.OpenGLES30)) && curState.rWrap != tex.getWrap(WrapAxis.R)) {
|
|
|
bindTextureAndUnit(target, image, unit);
|
|
|
- gl2.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
|
|
|
+ gl.glTexParameteri(target, GL2.GL_TEXTURE_WRAP_R, convertWrapMode(tex.getWrap(WrapAxis.R)));
|
|
|
curState.rWrap = tex.getWrap(WrapAxis.R);
|
|
|
}
|
|
|
//There is no break statement on purpose here
|
|
@@ -2196,17 +2251,17 @@ public final class GLRenderer implements Renderer {
|
|
|
}
|
|
|
|
|
|
ShadowCompareMode texCompareMode = tex.getShadowCompareMode();
|
|
|
- if (gl2 != null && curState.shadowCompareMode != texCompareMode) {
|
|
|
+ if ( (gl2 != null || caps.contains(Caps.OpenGLES30)) && curState.shadowCompareMode != texCompareMode) {
|
|
|
bindTextureAndUnit(target, image, unit);
|
|
|
if (texCompareMode != ShadowCompareMode.Off) {
|
|
|
- gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_REF_TO_TEXTURE);
|
|
|
+ gl.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL2.GL_COMPARE_REF_TO_TEXTURE);
|
|
|
if (texCompareMode == ShadowCompareMode.GreaterOrEqual) {
|
|
|
- gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL);
|
|
|
+ gl.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_GEQUAL);
|
|
|
} else {
|
|
|
- gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL);
|
|
|
+ gl.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_FUNC, GL.GL_LEQUAL);
|
|
|
}
|
|
|
} else {
|
|
|
- gl2.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE);
|
|
|
+ gl.glTexParameteri(target, GL2.GL_TEXTURE_COMPARE_MODE, GL.GL_NONE);
|
|
|
}
|
|
|
curState.shadowCompareMode = texCompareMode;
|
|
|
}
|
|
@@ -2340,7 +2395,6 @@ public final class GLRenderer implements Renderer {
|
|
|
bindTextureAndUnit(target, img, unit);
|
|
|
|
|
|
int imageSamples = img.getMultiSamples();
|
|
|
-
|
|
|
if (imageSamples <= 1) {
|
|
|
if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired()) {
|
|
|
// Image does not have mipmaps, but they are required.
|
|
@@ -2353,7 +2407,7 @@ public final class GLRenderer implements Renderer {
|
|
|
// For OpenGL3 and up.
|
|
|
// We'll generate mipmaps via glGenerateMipmapEXT (see below)
|
|
|
}
|
|
|
- } else if (caps.contains(Caps.OpenGL20)) {
|
|
|
+ } else if (caps.contains(Caps.OpenGL20) || caps.contains(Caps.OpenGLES30)) {
|
|
|
if (img.hasMipmaps()) {
|
|
|
// Image already has mipmaps, set the max level based on the
|
|
|
// number of mipmaps we have.
|
|
@@ -3136,6 +3190,7 @@ public final class GLRenderer implements Renderer {
|
|
|
public boolean isTaskResultAvailable(int taskId) {
|
|
|
return gl.glGetQueryObjectiv(taskId, GL.GL_QUERY_RESULT_AVAILABLE) == 1;
|
|
|
}
|
|
|
+
|
|
|
@Override
|
|
|
public boolean getAlphaToCoverage() {
|
|
|
if (caps.contains(Caps.Multisample)) {
|