diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLCapabilities.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLCapabilities.java new file mode 100755 index 00000000000..f047cb19b38 --- /dev/null +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLCapabilities.java @@ -0,0 +1,411 @@ +package com.jme3.renderer.opengl; + +import com.jme3.renderer.Caps; +import com.jme3.renderer.Limits; +import com.jme3.renderer.RenderContext; +import com.jme3.util.BufferUtils; + +import java.nio.IntBuffer; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static java.util.Arrays.asList; + +public class GLCapabilities { + + private static final Logger logger = Logger.getLogger(GLCapabilities.class.getName()); + private final EnumSet caps = EnumSet.noneOf(Caps.class); + private static final Pattern GLVERSION_PATTERN = Pattern.compile(".*?(\\d+)\\.(\\d+).*"); + private final EnumMap limits = new EnumMap<>(Limits.class); + + private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16); + private GL gl; + private GL2 gl2; + private GL3 gl3; + private HashSet extensions; + + public void loadCapabilities(GL gl, GL2 gl2, GL3 gl3, RenderContext context) { + this.gl = gl; + this.gl2 = gl2; + this.gl3 = gl3; + if (gl2 != null) { + loadCapabilitiesGL2(gl.glGetString(GL.GL_VERSION), context); + loadCapabilitiesGLSL(gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION)); + loadCapabilitiesGLSL1Support(); + } else { + loadCapabilitiesES(); + } + loadCapabilitiesCommon(); + } + + /** + * Load Embedded System Capabilities. + * Important: Do not add OpenGL20 - that's the desktop capability! + */ + private void loadCapabilitiesES() { + caps.add(Caps.GLSL100); + caps.add(Caps.OpenGLES20); + } + + protected void loadCapabilitiesGL2(String glVersion, RenderContext context) { + int oglVer = extractVersion(glVersion); + Map> versionCapMap = getGLVersionCapabilityMapping(); + addCapabilitiesBasedOnVersion(versionCapMap, oglVer); + + + // Fix issue in TestRenderToMemory when GL.GL_FRONT is the main + // buffer being used. + context.initialDrawBuf = getInteger(GL2.GL_DRAW_BUFFER); + context.initialReadBuf = getInteger(GL2.GL_READ_BUFFER); + } + + /** + * Load GL Shading Language capabilities + */ + protected void loadCapabilitiesGLSL(String glslVersion) { + int glslVer = extractVersion(glslVersion); + Map> versionCapMap = getGSLVersionCapabilityMapping(); + addCapabilitiesBasedOnVersion(versionCapMap, glslVer); + } + + /** + * Workaround, always assume we support GLSL100 & GLSL110 + * Supporting OpenGL 2.0 means supporting GLSL 1.10. + */ + protected void loadCapabilitiesGLSL1Support() { + caps.add(Caps.GLSL110); + caps.add(Caps.GLSL100); + } + + protected void loadCapabilitiesCommon() { + extensions = loadExtensions(); + + limits.put(Limits.VertexTextureUnits, getInteger(GL.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS)); + if (limits.get(Limits.VertexTextureUnits) > 0) { + caps.add(Caps.VertexTextureFetch); + } + + limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS)); + + if (caps.contains(Caps.OpenGLES20)) { + limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_VECTORS)); + } else { + limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4); + } + limits.put(Limits.VertexAttributes, getInteger(GL.GL_MAX_VERTEX_ATTRIBS)); + limits.put(Limits.TextureSize, getInteger(GL.GL_MAX_TEXTURE_SIZE)); + limits.put(Limits.CubemapSize, getInteger(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE)); + + if (hasExtension("GL_ARB_draw_instanced") && + hasExtension("GL_ARB_instanced_arrays")) { + caps.add(Caps.MeshInstancing); + } + + if (hasExtension("GL_OES_element_index_uint") || gl2 != null) { + caps.add(Caps.IntegerIndexBuffer); + } + + if (hasExtension("GL_ARB_texture_buffer_object")) { + caps.add(Caps.TextureBuffer); + } + + loadTextureFormatExtensions(); + + if (hasExtension("GL_ARB_vertex_array_object") || caps.contains(Caps.OpenGL30)) { + caps.add(Caps.VertexBufferArray); + } + + if (hasExtension("GL_ARB_texture_non_power_of_two") || + hasExtension("GL_OES_texture_npot") || + caps.contains(Caps.OpenGL30)) { + caps.add(Caps.NonPowerOfTwoTextures); + } else { + logger.log(Level.WARNING, "Your graphics card does not " + + "support non-power-of-2 textures. " + + "Some features might not work."); + } + + if (caps.contains(Caps.OpenGLES20)) { + // OpenGL ES 2 has some limited support for NPOT textures + caps.add(Caps.PartialNonPowerOfTwoTextures); + } + + if (hasExtension("GL_EXT_texture_array") || caps.contains(Caps.OpenGL30)) { + caps.add(Caps.TextureArray); + } + + if (hasExtension("GL_EXT_texture_filter_anisotropic")) { + caps.add(Caps.TextureFilterAnisotropic); + limits.put(Limits.TextureAnisotropy, getInteger(GLExt.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT)); + } + + if (hasExtension("GL_EXT_framebuffer_object") + || caps.contains(Caps.OpenGL30) + || caps.contains(Caps.OpenGLES20)) { + caps.add(Caps.FrameBuffer); + + 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)) { + caps.add(Caps.FrameBufferBlit); + } + + if (hasExtension("GL_EXT_framebuffer_multisample")) { + caps.add(Caps.FrameBufferMultisample); + limits.put(Limits.FrameBufferSamples, getInteger(GLExt.GL_MAX_SAMPLES_EXT)); + } + + if (hasExtension("GL_ARB_texture_multisample")) { + 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)); + if (!limits.containsKey(Limits.FrameBufferSamples)) { + // In case they want to query samples on main FB ... + limits.put(Limits.FrameBufferSamples, limits.get(Limits.ColorTextureSamples)); + } + } + + if (hasExtension("GL_ARB_draw_buffers") || caps.contains(Caps.OpenGL30)) { + limits.put(Limits.FrameBufferMrtAttachments, getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB)); + if (limits.get(Limits.FrameBufferMrtAttachments) > 1) { + caps.add(Caps.FrameBufferMRT); + } + } else { + limits.put(Limits.FrameBufferMrtAttachments, 1); + } + } + + if (hasExtension("GL_ARB_multisample")) { + boolean available = getInteger(GLExt.GL_SAMPLE_BUFFERS_ARB) != 0; + int samples = getInteger(GLExt.GL_SAMPLES_ARB); + logger.log(Level.FINER, "Samples: {0}", samples); + boolean enabled = gl.glIsEnabled(GLExt.GL_MULTISAMPLE_ARB); + if (samples > 0 && available && !enabled) { + // Doesn't seem to be neccessary .. OGL spec says its always + // set by default? + gl.glEnable(GLExt.GL_MULTISAMPLE_ARB); + } + caps.add(Caps.Multisample); + } + + // Supports sRGB pipeline. + if ( (hasExtension("GL_ARB_framebuffer_sRGB") && hasExtension("GL_EXT_texture_sRGB")) + || caps.contains(Caps.OpenGL30) ) { + caps.add(Caps.Srgb); + } + + // Supports seamless cubemap + if (hasExtension("GL_ARB_seamless_cube_map") || caps.contains(Caps.OpenGL32)) { + caps.add(Caps.SeamlessCubemap); + } + + if (caps.contains(Caps.OpenGL32) && !hasExtension("GL_ARB_compatibility")) { + caps.add(Caps.CoreProfile); + } + + if (hasExtension("GL_ARB_get_program_binary")) { + int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS); + if (binaryFormats > 0) { + caps.add(Caps.BinaryShader); + } + } + + // Print context information + logger.log(Level.INFO, "OpenGL Renderer Information\n" + + " * Vendor: {0}\n" + + " * Renderer: {1}\n" + + " * OpenGL Version: {2}\n" + + " * GLSL Version: {3}\n" + + " * Profile: {4}", + new Object[]{ + gl.glGetString(GL.GL_VENDOR), + gl.glGetString(GL.GL_RENDERER), + gl.glGetString(GL.GL_VERSION), + gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION), + caps.contains(Caps.CoreProfile) ? "Core" : "Compatibility" + }); + + // Print capabilities (if fine logging is enabled) + if (logger.isLoggable(Level.FINE)) { + StringBuilder sb = new StringBuilder(); + sb.append("Supported capabilities: \n"); + for (Caps cap : caps) + { + sb.append("\t").append(cap.toString()).append("\n"); + } + logger.log(Level.FINE, sb.toString()); + } + } + + private void loadTextureFormatExtensions() { + boolean hasFloatTexture; + + hasFloatTexture = hasExtension("GL_OES_texture_half_float") && + hasExtension("GL_OES_texture_float"); + + if (!hasFloatTexture) { + hasFloatTexture = hasExtension("GL_ARB_texture_float") && + hasExtension("GL_ARB_half_float_pixel"); + + if (!hasFloatTexture) { + hasFloatTexture = caps.contains(Caps.OpenGL30); + } + } + + if (hasFloatTexture) { + caps.add(Caps.FloatTexture); + } + + if (hasExtension("GL_OES_depth_texture") || gl2 != null) { + caps.add(Caps.DepthTexture); + + // TODO: GL_OES_depth24 + } + + if (hasExtension("GL_OES_rgb8_rgba8") || + hasExtension("GL_ARM_rgba8") || + hasExtension("GL_EXT_texture_format_BGRA8888")) { + caps.add(Caps.Rgba8); + } + + if (caps.contains(Caps.OpenGL30) || hasExtension("GL_OES_packed_depth_stencil")) { + caps.add(Caps.PackedDepthStencilBuffer); + } + + if (hasExtension("GL_ARB_color_buffer_float") && + hasExtension("GL_ARB_half_float_pixel")) { + // XXX: Require both 16 and 32 bit float support for FloatColorBuffer. + caps.add(Caps.FloatColorBuffer); + } + + if (hasExtension("GL_ARB_depth_buffer_float")) { + caps.add(Caps.FloatDepthBuffer); + } + + if ((hasExtension("GL_EXT_packed_float") && hasFloatTexture) || + caps.contains(Caps.OpenGL30)) { + // Either OpenGL3 is available or both packed_float & half_float_pixel. + caps.add(Caps.PackedFloatColorBuffer); + caps.add(Caps.PackedFloatTexture); + } + + if (hasExtension("GL_EXT_texture_shared_exponent") || caps.contains(Caps.OpenGL30)) { + caps.add(Caps.SharedExponentTexture); + } + + if (hasExtension("GL_EXT_texture_compression_s3tc")) { + caps.add(Caps.TextureCompressionS3TC); + } + + if (hasExtension("GL_ARB_ES3_compatibility")) { + caps.add(Caps.TextureCompressionETC2); + caps.add(Caps.TextureCompressionETC1); + } else if (hasExtension("GL_OES_compressed_ETC1_RGB8_texture")) { + caps.add(Caps.TextureCompressionETC1); + } + } + + public int extractVersion(String version) { + Matcher m = GLVERSION_PATTERN.matcher(version); + if (m.matches()) { + int major = Integer.parseInt(m.group(1)); + int minor = Integer.parseInt(m.group(2)); + if (minor >= 10 && minor % 10 == 0) { + // some versions can look like "1.30" instead of "1.3". + // make sure to correct for this + minor /= 10; + } + return major * 100 + minor * 10; + } else { + return -1; + } + } + + protected HashSet loadExtensions() { + HashSet extensionSet = new HashSet(64); + if (getCaps().contains(Caps.OpenGL30)) { + // If OpenGL3+ is available, use the non-deprecated way + // of getting supported extensions. + gl3.glGetInteger(GL3.GL_NUM_EXTENSIONS, intBuf16); + int extensionCount = intBuf16.get(0); + for (int i = 0; i < extensionCount; i++) { + String extension = gl3.glGetString(GL.GL_EXTENSIONS, i); + extensionSet.add(extension); + } + } else { + extensionSet.addAll(asList(gl.glGetString(GL.GL_EXTENSIONS).split(" "))); + } + return extensionSet; + } + + public Map> getGLVersionCapabilityMapping() { + + Map> map = new TreeMap<>(); + + map.put(200, Collections.singletonList(Caps.OpenGL20)); + map.put(210, Collections.singletonList(Caps.OpenGL21)); + map.put(300, Collections.singletonList(Caps.OpenGL30)); + map.put(310, Collections.singletonList(Caps.OpenGL31)); + map.put(320, Collections.singletonList(Caps.OpenGL32)); + map.put(330, asList(Caps.OpenGL33, Caps.GeometryShader)); + map.put(400, asList(Caps.OpenGL40, Caps.TesselationShader)); + + return map; + } + + public Map> getGSLVersionCapabilityMapping() { + Map> map = new TreeMap<>(); + + map.put(100, Collections.singletonList(Caps.GLSL100)); + map.put(110, Collections.singletonList(Caps.GLSL110)); + map.put(120, Collections.singletonList(Caps.GLSL120)); + map.put(130, Collections.singletonList(Caps.GLSL130)); + map.put(140, Collections.singletonList(Caps.GLSL140)); + map.put(150, Collections.singletonList(Caps.GLSL150)); + map.put(330, Collections.singletonList(Caps.GLSL330)); + map.put(400, Collections.singletonList(Caps.GLSL400)); + + return map; + } + + /** + * Add capabilities for all versions below the given version. + * @param versionCapMap mapping of existing versions and there capabilities + * @param version version to add capabilities for + */ + private void addCapabilitiesBasedOnVersion(Map> versionCapMap, int version) { + for (Map.Entry> entry : versionCapMap.entrySet()) { + if (version >= entry.getKey()) { + for (Caps cap : entry.getValue()) { + caps.add(cap); + } + } + } + } + + protected boolean has(Caps cap) { + return caps.contains(cap); + } + + private boolean hasExtension(String extensionName) { + return extensions.contains(extensionName); + } + + private int getInteger(int en) { + intBuf16.clear(); + gl.glGetInteger(en, intBuf16); + return intBuf16.get(0); + } + + public EnumSet getCaps() { + return caps; + } + + public EnumMap getLimits() { + return limits; + } +} diff --git a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java index 0e74d879c50..1430d05fc2c 100755 --- a/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java +++ b/jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java @@ -62,34 +62,27 @@ import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import jme3tools.shader.ShaderDebug; -import static java.util.Arrays.asList; +import jme3tools.shader.ShaderDebug; public class GLRenderer implements Renderer { private static final Logger logger = Logger.getLogger(GLRenderer.class.getName()); private static final boolean VALIDATE_SHADER = false; - private static final Pattern GLVERSION_PATTERN = Pattern.compile(".*?(\\d+)\\.(\\d+).*"); private final ByteBuffer nameBuf = BufferUtils.createByteBuffer(250); private final StringBuilder stringBuf = new StringBuilder(250); private final IntBuffer intBuf1 = BufferUtils.createIntBuffer(1); private final IntBuffer intBuf16 = BufferUtils.createIntBuffer(16); - private final FloatBuffer floatBuf16 = BufferUtils.createFloatBuffer(16); protected final RenderContext context = new RenderContext(); private final NativeObjectManager objManager = new NativeObjectManager(); - protected final EnumSet caps = EnumSet.noneOf(Caps.class); - private final EnumMap limits = new EnumMap(Limits.class); + private final GLCapabilities glCaps = new GLCapabilities(); private FrameBuffer mainFbOverride = null; private final Statistics statistics = new Statistics(); private int vpX, vpY, vpW, vpH; private int clipX, clipY, clipW, clipH; private boolean linearizeSrgbImages; - private HashSet extensions; private final GL gl; private final GL2 gl2; @@ -116,351 +109,12 @@ public Statistics getStatistics() { @Override public EnumSet getCaps() { - return caps; + return glCaps.getCaps(); } // Not making public yet ... public EnumMap getLimits() { - return limits; - } - - protected HashSet loadExtensions() { - HashSet extensionSet = new HashSet(64); - if (caps.contains(Caps.OpenGL30)) { - // If OpenGL3+ is available, use the non-deprecated way - // of getting supported extensions. - gl3.glGetInteger(GL3.GL_NUM_EXTENSIONS, intBuf16); - int extensionCount = intBuf16.get(0); - for (int i = 0; i < extensionCount; i++) { - String extension = gl3.glGetString(GL.GL_EXTENSIONS, i); - extensionSet.add(extension); - } - } else { - extensionSet.addAll(asList(gl.glGetString(GL.GL_EXTENSIONS).split(" "))); - } - return extensionSet; - } - - public int extractVersion(String version) { - Matcher m = GLVERSION_PATTERN.matcher(version); - if (m.matches()) { - int major = Integer.parseInt(m.group(1)); - int minor = Integer.parseInt(m.group(2)); - if (minor >= 10 && minor % 10 == 0) { - // some versions can look like "1.30" instead of "1.3". - // make sure to correct for this - minor /= 10; - } - return major * 100 + minor * 10; - } else { - return -1; - } - } - - private boolean hasExtension(String extensionName) { - return extensions.contains(extensionName); - } - - /** - * Load Embedded System Capabilities. - * Important: Do not add OpenGL20 - that's the desktop capability! - */ - private void loadCapabilitiesES() { - caps.add(Caps.GLSL100); - caps.add(Caps.OpenGLES20); - } - - protected void loadCapabilitiesGL2() { - int oglVer = extractVersion(gl.glGetString(GL.GL_VERSION)); - Map> versionCapMap = getGLVersionCapabilityMapping(); - addCapabilitiesBasedOnVersion(versionCapMap, oglVer); - - - // Fix issue in TestRenderToMemory when GL.GL_FRONT is the main - // buffer being used. - context.initialDrawBuf = getInteger(GL2.GL_DRAW_BUFFER); - context.initialReadBuf = getInteger(GL2.GL_READ_BUFFER); - } - - /** - * Load GL Shading Language capabilities - */ - protected void loadCapabilitiesGLSL() { - int glslVer = extractVersion(gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION)); - Map> versionCapMap = getGSLVersionCapabilityMapping(); - addCapabilitiesBasedOnVersion(versionCapMap, glslVer); - } - - /** - * Add capabilities for all versions below the given version. - * @param versionCapMap mapping of existing versions and there capabilities - * @param version version to add capabilities for - */ - private void addCapabilitiesBasedOnVersion(Map> versionCapMap, int version) { - for (Map.Entry> entry : versionCapMap.entrySet()) { - if (version >= entry.getKey()) { - for (Caps cap : entry.getValue()) { - caps.add(cap); - } - } - } - } - - /** - * Workaround, always assume we support GLSL100 & GLSL110 - * Supporting OpenGL 2.0 means supporting GLSL 1.10. - */ - protected void loadCapabilitiesGLSL1Support() { - caps.add(Caps.GLSL110); - caps.add(Caps.GLSL100); - } - - protected void loadCapabilitiesCommon() { - extensions = loadExtensions(); - - limits.put(Limits.VertexTextureUnits, getInteger(GL.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS)); - if (limits.get(Limits.VertexTextureUnits) > 0) { - caps.add(Caps.VertexTextureFetch); - } - - limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS)); - - if (caps.contains(Caps.OpenGLES20)) { - limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_VECTORS)); - } else { - limits.put(Limits.VertexUniformVectors, getInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS) / 4); - } - limits.put(Limits.VertexAttributes, getInteger(GL.GL_MAX_VERTEX_ATTRIBS)); - limits.put(Limits.TextureSize, getInteger(GL.GL_MAX_TEXTURE_SIZE)); - limits.put(Limits.CubemapSize, getInteger(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE)); - - if (hasExtension("GL_ARB_draw_instanced") && - hasExtension("GL_ARB_instanced_arrays")) { - caps.add(Caps.MeshInstancing); - } - - if (hasExtension("GL_OES_element_index_uint") || gl2 != null) { - caps.add(Caps.IntegerIndexBuffer); - } - - if (hasExtension("GL_ARB_texture_buffer_object")) { - caps.add(Caps.TextureBuffer); - } - - // == texture format extensions == - - boolean hasFloatTexture; - - hasFloatTexture = hasExtension("GL_OES_texture_half_float") && - hasExtension("GL_OES_texture_float"); - - if (!hasFloatTexture) { - hasFloatTexture = hasExtension("GL_ARB_texture_float") && - hasExtension("GL_ARB_half_float_pixel"); - - if (!hasFloatTexture) { - hasFloatTexture = caps.contains(Caps.OpenGL30); - } - } - - if (hasFloatTexture) { - caps.add(Caps.FloatTexture); - } - - if (hasExtension("GL_OES_depth_texture") || gl2 != null) { - caps.add(Caps.DepthTexture); - - // TODO: GL_OES_depth24 - } - - if (hasExtension("GL_OES_rgb8_rgba8") || - hasExtension("GL_ARM_rgba8") || - hasExtension("GL_EXT_texture_format_BGRA8888")) { - caps.add(Caps.Rgba8); - } - - if (caps.contains(Caps.OpenGL30) || hasExtension("GL_OES_packed_depth_stencil")) { - caps.add(Caps.PackedDepthStencilBuffer); - } - - if (hasExtension("GL_ARB_color_buffer_float") && - hasExtension("GL_ARB_half_float_pixel")) { - // XXX: Require both 16 and 32 bit float support for FloatColorBuffer. - caps.add(Caps.FloatColorBuffer); - } - - if (hasExtension("GL_ARB_depth_buffer_float")) { - caps.add(Caps.FloatDepthBuffer); - } - - if ((hasExtension("GL_EXT_packed_float") && hasFloatTexture) || - caps.contains(Caps.OpenGL30)) { - // Either OpenGL3 is available or both packed_float & half_float_pixel. - caps.add(Caps.PackedFloatColorBuffer); - caps.add(Caps.PackedFloatTexture); - } - - if (hasExtension("GL_EXT_texture_shared_exponent") || caps.contains(Caps.OpenGL30)) { - caps.add(Caps.SharedExponentTexture); - } - - if (hasExtension("GL_EXT_texture_compression_s3tc")) { - caps.add(Caps.TextureCompressionS3TC); - } - - if (hasExtension("GL_ARB_ES3_compatibility")) { - caps.add(Caps.TextureCompressionETC2); - caps.add(Caps.TextureCompressionETC1); - } else if (hasExtension("GL_OES_compressed_ETC1_RGB8_texture")) { - caps.add(Caps.TextureCompressionETC1); - } - - // == end texture format extensions == - - if (hasExtension("GL_ARB_vertex_array_object") || caps.contains(Caps.OpenGL30)) { - caps.add(Caps.VertexBufferArray); - } - - if (hasExtension("GL_ARB_texture_non_power_of_two") || - hasExtension("GL_OES_texture_npot") || - caps.contains(Caps.OpenGL30)) { - caps.add(Caps.NonPowerOfTwoTextures); - } else { - logger.log(Level.WARNING, "Your graphics card does not " - + "support non-power-of-2 textures. " - + "Some features might not work."); - } - - if (caps.contains(Caps.OpenGLES20)) { - // OpenGL ES 2 has some limited support for NPOT textures - caps.add(Caps.PartialNonPowerOfTwoTextures); - } - - if (hasExtension("GL_EXT_texture_array") || caps.contains(Caps.OpenGL30)) { - caps.add(Caps.TextureArray); - } - - if (hasExtension("GL_EXT_texture_filter_anisotropic")) { - caps.add(Caps.TextureFilterAnisotropic); - limits.put(Limits.TextureAnisotropy, getInteger(GLExt.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT)); - } - - if (hasExtension("GL_EXT_framebuffer_object") - || caps.contains(Caps.OpenGL30) - || caps.contains(Caps.OpenGLES20)) { - caps.add(Caps.FrameBuffer); - - 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)) { - caps.add(Caps.FrameBufferBlit); - } - - if (hasExtension("GL_EXT_framebuffer_multisample")) { - caps.add(Caps.FrameBufferMultisample); - limits.put(Limits.FrameBufferSamples, getInteger(GLExt.GL_MAX_SAMPLES_EXT)); - } - - if (hasExtension("GL_ARB_texture_multisample")) { - 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)); - if (!limits.containsKey(Limits.FrameBufferSamples)) { - // In case they want to query samples on main FB ... - limits.put(Limits.FrameBufferSamples, limits.get(Limits.ColorTextureSamples)); - } - } - - if (hasExtension("GL_ARB_draw_buffers") || caps.contains(Caps.OpenGL30)) { - limits.put(Limits.FrameBufferMrtAttachments, getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB)); - if (limits.get(Limits.FrameBufferMrtAttachments) > 1) { - caps.add(Caps.FrameBufferMRT); - } - } else { - limits.put(Limits.FrameBufferMrtAttachments, 1); - } - } - - if (hasExtension("GL_ARB_multisample")) { - boolean available = getInteger(GLExt.GL_SAMPLE_BUFFERS_ARB) != 0; - int samples = getInteger(GLExt.GL_SAMPLES_ARB); - logger.log(Level.FINER, "Samples: {0}", samples); - boolean enabled = gl.glIsEnabled(GLExt.GL_MULTISAMPLE_ARB); - if (samples > 0 && available && !enabled) { - // Doesn't seem to be neccessary .. OGL spec says its always - // set by default? - gl.glEnable(GLExt.GL_MULTISAMPLE_ARB); - } - caps.add(Caps.Multisample); - } - - // Supports sRGB pipeline. - if ( (hasExtension("GL_ARB_framebuffer_sRGB") && hasExtension("GL_EXT_texture_sRGB")) - || caps.contains(Caps.OpenGL30) ) { - caps.add(Caps.Srgb); - } - - // Supports seamless cubemap - if (hasExtension("GL_ARB_seamless_cube_map") || caps.contains(Caps.OpenGL32)) { - caps.add(Caps.SeamlessCubemap); - } - - if (caps.contains(Caps.OpenGL32) && !hasExtension("GL_ARB_compatibility")) { - caps.add(Caps.CoreProfile); - } - - if (hasExtension("GL_ARB_get_program_binary")) { - int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS); - if (binaryFormats > 0) { - caps.add(Caps.BinaryShader); - } - } - - // Print context information - logger.log(Level.INFO, "OpenGL Renderer Information\n" + - " * Vendor: {0}\n" + - " * Renderer: {1}\n" + - " * OpenGL Version: {2}\n" + - " * GLSL Version: {3}\n" + - " * Profile: {4}", - new Object[]{ - gl.glGetString(GL.GL_VENDOR), - gl.glGetString(GL.GL_RENDERER), - gl.glGetString(GL.GL_VERSION), - gl.glGetString(GL.GL_SHADING_LANGUAGE_VERSION), - caps.contains(Caps.CoreProfile) ? "Core" : "Compatibility" - }); - - // Print capabilities (if fine logging is enabled) - if (logger.isLoggable(Level.FINE)) { - StringBuilder sb = new StringBuilder(); - sb.append("Supported capabilities: \n"); - for (Caps cap : caps) - { - sb.append("\t").append(cap.toString()).append("\n"); - } - logger.log(Level.FINE, sb.toString()); - } - - texUtil.initialize(caps); - } - - protected void loadCapabilities() { - if (gl2 != null) { - loadCapabilitiesGL2(); - loadCapabilitiesGLSL(); - loadCapabilitiesGLSL1Support(); - } else { - loadCapabilitiesES(); - } - loadCapabilitiesCommon(); - } - - private int getInteger(int en) { - intBuf16.clear(); - gl.glGetInteger(en, intBuf16); - return intBuf16.get(0); + return glCaps.getLimits(); } private boolean getBoolean(int en) { @@ -470,17 +124,19 @@ private boolean getBoolean(int en) { @SuppressWarnings("fallthrough") public void initialize() { - loadCapabilities(); + glCaps.loadCapabilities(gl, gl2, gl3, context); + + texUtil.initialize(glCaps.getCaps()); // Initialize default state.. gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1); - if (caps.contains(Caps.SeamlessCubemap)) { + if (glCaps.has(Caps.SeamlessCubemap)) { // Enable this globally. Should be OK. gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS); } - if (caps.contains(Caps.CoreProfile)) { + if (glCaps.has(Caps.CoreProfile)) { // Core Profile requires VAO to be bound. gl3.glGenVertexArrays(intBuf16); int vaoId = intBuf16.get(0); @@ -488,7 +144,7 @@ public void initialize() { } if (gl2 != null) { gl2.glEnable(GL2.GL_VERTEX_PROGRAM_POINT_SIZE); - if (!caps.contains(Caps.CoreProfile)) { + if (!glCaps.has(Caps.CoreProfile)) { gl2.glEnable(GL2.GL_POINT_SPRITE); context.pointSprite = true; } @@ -563,7 +219,7 @@ public void setBackgroundColor(ColorRGBA color) { } public void setAlphaToCoverage(boolean value) { - if (caps.contains(Caps.Multisample)) { + if (glCaps.has(Caps.Multisample)) { if (value) { gl.glEnable(GLExt.GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); } else { @@ -1019,7 +675,7 @@ public void updateShaderSourceData(ShaderSource source) { throw new RendererException("Cannot recompile shader source"); } - boolean gles2 = caps.contains(Caps.OpenGLES20); + boolean gles2 = glCaps.has(Caps.OpenGLES20); String language = source.getLanguage(); if (gles2 && !language.equals("GLSL100")) { @@ -1138,7 +794,7 @@ public void updateShaderData(Shader shader) { // Check if GLSL version is 1.5 for shader gl3.glBindFragDataLocation(id, 0, "outFragColor"); // For MRT - for (int i = 0; i < limits.get(Limits.FrameBufferMrtAttachments); i++) { + for (int i = 0; i < glCaps.getLimits().get(Limits.FrameBufferMrtAttachments); i++) { gl3.glBindFragDataLocation(id, i, "outFragData[" + i + "]"); } } @@ -1238,7 +894,7 @@ public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst) { } public void copyFrameBuffer(FrameBuffer src, FrameBuffer dst, boolean copyDepth) { - if (caps.contains(Caps.FrameBufferBlit)) { + if (glCaps.has(Caps.FrameBufferBlit)) { int srcX0 = 0; int srcY0 = 0; int srcX1; @@ -1349,7 +1005,7 @@ private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) { context.boundRB = id; } - int rbSize = limits.get(Limits.RenderBufferSize); + int rbSize = glCaps.getLimits().get(Limits.RenderBufferSize); if (fb.getWidth() > rbSize || fb.getHeight() > rbSize) { throw new RendererException("Resolution " + fb.getWidth() + ":" + fb.getHeight() + " is not supported."); @@ -1357,9 +1013,9 @@ private void updateRenderBuffer(FrameBuffer fb, RenderBuffer rb) { GLImageFormat glFmt = texUtil.getImageFormatWithError(rb.getFormat(), fb.isSrgb()); - if (fb.getSamples() > 1 && caps.contains(Caps.FrameBufferMultisample)) { + if (fb.getSamples() > 1 && glCaps.has(Caps.FrameBufferMultisample)) { int samples = fb.getSamples(); - int maxSamples = limits.get(Limits.FrameBufferSamples); + int maxSamples = glCaps.getLimits().get(Limits.FrameBufferSamples); if (maxSamples < samples) { samples = maxSamples; } @@ -1489,7 +1145,7 @@ public Vector2f[] getFrameBufferSamplePositions(FrameBuffer fb) { if (fb.getSamples() <= 1) { throw new IllegalArgumentException("Framebuffer must be multisampled"); } - if (!caps.contains(Caps.TextureMultisample)) { + if (!glCaps.has(Caps.TextureMultisample)) { throw new RendererException("Multisampled textures are not supported"); } @@ -1538,28 +1194,26 @@ public void setReadDrawBuffers(FrameBuffer fb) { if (fb.getNumColorBuffers() == 0) { // make sure to select NONE as draw buf // no color buffer attached. - if (gl2 != null) { - if (context.boundDrawBuf != NONE) { - gl2.glDrawBuffer(GL.GL_NONE); - context.boundDrawBuf = NONE; - } - if (context.boundReadBuf != NONE) { - gl2.glReadBuffer(GL.GL_NONE); - context.boundReadBuf = NONE; - } + if (context.boundDrawBuf != NONE) { + gl2.glDrawBuffer(GL.GL_NONE); + context.boundDrawBuf = NONE; + } + if (context.boundReadBuf != NONE) { + gl2.glReadBuffer(GL.GL_NONE); + context.boundReadBuf = NONE; } } else { - if (fb.getNumColorBuffers() > limits.get(Limits.FrameBufferAttachments)) { + if (fb.getNumColorBuffers() > glCaps.getLimits().get(Limits.FrameBufferAttachments)) { throw new RendererException("Framebuffer has more color " + "attachments than are supported" + " by the video hardware!"); } if (fb.isMultiTarget()) { - if (!caps.contains(Caps.FrameBufferMRT)) { + if (!glCaps.has(Caps.FrameBufferMRT)) { throw new RendererException("Multiple render targets " + " are not supported by the video hardware"); } - if (fb.getNumColorBuffers() > limits.get(Limits.FrameBufferMrtAttachments)) { + if (fb.getNumColorBuffers() > glCaps.getLimits().get(Limits.FrameBufferMrtAttachments)) { throw new RendererException("Framebuffer has more" + " multi targets than are supported" + " by the video hardware!"); @@ -1578,11 +1232,9 @@ public void setReadDrawBuffers(FrameBuffer fb) { } else { RenderBuffer rb = fb.getColorBuffer(fb.getTargetIndex()); // select this draw buffer - if (gl2 != null) { - if (context.boundDrawBuf != rb.getSlot()) { - gl2.glDrawBuffer(GLFbo.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot()); - context.boundDrawBuf = rb.getSlot(); - } + if (context.boundDrawBuf != rb.getSlot()) { + gl2.glDrawBuffer(GLFbo.GL_COLOR_ATTACHMENT0_EXT + rb.getSlot()); + context.boundDrawBuf = rb.getSlot(); } } } @@ -1601,7 +1253,7 @@ public void setFrameBuffer(FrameBuffer fb) { } } - if (!caps.contains(Caps.FrameBuffer)) { + if (!glCaps.has(Caps.FrameBuffer)) { throw new RendererException("Framebuffer objects are not supported" + " by the video hardware"); } @@ -1704,7 +1356,7 @@ public void deleteFrameBuffer(FrameBuffer fb) { |* Textures *| \*********************************************************************/ private int convertTextureType(Texture.Type type, int samples, int face) { - if (samples > 1 && !caps.contains(Caps.TextureMultisample)) { + if (samples > 1 && !glCaps.has(Caps.TextureMultisample)) { throw new RendererException("Multisample textures are not supported" + " by the video hardware."); } @@ -1717,7 +1369,7 @@ private int convertTextureType(Texture.Type type, int samples, int face) { return GL.GL_TEXTURE_2D; } case TwoDimensionalArray: - if (!caps.contains(Caps.TextureArray)) { + if (!glCaps.has(Caps.TextureArray)) { throw new RendererException("Array textures are not supported" + " by the video hardware."); } @@ -1727,7 +1379,7 @@ private int convertTextureType(Texture.Type type, int samples, int face) { return GLExt.GL_TEXTURE_2D_ARRAY_EXT; } case ThreeDimensional: - if (!caps.contains(Caps.OpenGL20)) { + if (!glCaps.has(Caps.OpenGL20)) { throw new RendererException("3D textures are not supported" + " by the video hardware."); } @@ -1828,7 +1480,7 @@ private void setupTextureParams(int unit, Texture tex) { gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, convertMinFilter(tex.getMinFilter(), haveMips)); curState.minFilter = tex.getMinFilter(); } - if (caps.contains(Caps.TextureFilterAnisotropic) + if (glCaps.has(Caps.TextureFilterAnisotropic) && curState.anisoFilter != tex.getAnisotropicFilter()) { bindTextureAndUnit(target, image, unit); gl.glTexParameterf(target, @@ -1899,13 +1551,13 @@ private void checkNonPowerOfTwo(Texture tex) { return; } - if (caps.contains(Caps.NonPowerOfTwoTextures)) { + if (glCaps.has(Caps.NonPowerOfTwoTextures)) { // Texture is NPOT but it is supported by video hardware. return; } // Maybe we have some / partial support for NPOT? - if (!caps.contains(Caps.PartialNonPowerOfTwoTextures)) { + if (!glCaps.has(Caps.PartialNonPowerOfTwoTextures)) { // Cannot use any type of NPOT texture (uncommon) throw new RendererException("non-power-of-2 textures are not " + "supported by the video hardware"); @@ -2011,7 +1663,7 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s // Image does not have mipmaps, but they are required. // Generate from base level. - if (!caps.contains(Caps.FrameBuffer) && gl2 != null) { + if (!glCaps.has(Caps.FrameBuffer) && gl2 != null) { gl2.glTexParameteri(target, GL2.GL_GENERATE_MIPMAP, GL.GL_TRUE); img.setMipmapsGenerated(true); } else { @@ -2031,27 +1683,27 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s int imageSamples = img.getMultiSamples(); if (imageSamples > 1) { if (img.getFormat().isDepthFormat()) { - img.setMultiSamples(Math.min(limits.get(Limits.DepthTextureSamples), imageSamples)); + img.setMultiSamples(Math.min(glCaps.getLimits().get(Limits.DepthTextureSamples), imageSamples)); } else { - img.setMultiSamples(Math.min(limits.get(Limits.ColorTextureSamples), imageSamples)); + img.setMultiSamples(Math.min(glCaps.getLimits().get(Limits.ColorTextureSamples), imageSamples)); } } // Check if graphics card doesn't support multisample textures - if (!caps.contains(Caps.TextureMultisample)) { + if (!glCaps.has(Caps.TextureMultisample)) { if (img.getMultiSamples() > 1) { throw new RendererException("Multisample textures are not supported by the video hardware"); } } // Check if graphics card doesn't support depth textures - if (img.getFormat().isDepthFormat() && !caps.contains(Caps.DepthTexture)) { + if (img.getFormat().isDepthFormat() && !glCaps.has(Caps.DepthTexture)) { throw new RendererException("Depth textures are not supported by the video hardware"); } if (target == GL.GL_TEXTURE_CUBE_MAP) { // Check max texture size before upload - int cubeSize = limits.get(Limits.CubemapSize); + int cubeSize = glCaps.getLimits().get(Limits.CubemapSize); if (img.getWidth() > cubeSize || img.getHeight() > cubeSize) { throw new RendererException("Cannot upload cubemap " + img + ". The maximum supported cubemap resolution is " + cubeSize); } @@ -2059,7 +1711,7 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s throw new RendererException("Cubemaps must have square dimensions"); } } else { - int texSize = limits.get(Limits.TextureSize); + int texSize = glCaps.getLimits().get(Limits.TextureSize); if (img.getWidth() > texSize || img.getHeight() > texSize) { throw new RendererException("Cannot upload texture " + img + ". The maximum supported texture resolution is " + texSize); } @@ -2082,7 +1734,7 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s texUtil.uploadTexture(imageForUpload, GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, i, linearizeSrgbImages); } } else if (target == GLExt.GL_TEXTURE_2D_ARRAY_EXT) { - if (!caps.contains(Caps.TextureArray)) { + if (!glCaps.has(Caps.TextureArray)) { throw new RendererException("Texture arrays not supported by graphics hardware"); } @@ -2104,7 +1756,7 @@ public void updateTexImageData(Image img, Texture.Type type, int unit, boolean s img.setMultiSamples(imageSamples); } - if (caps.contains(Caps.FrameBuffer) || gl2 == null) { + if (glCaps.has(Caps.FrameBuffer) || gl2 == null) { if (!img.hasMipmaps() && img.isGeneratedMipmapsRequired() && img.getData(0) != null) { glfbo.glGenerateMipmapEXT(target); img.setMipmapsGenerated(true); @@ -2319,7 +1971,7 @@ public void setVertexAttrib(VertexBuffer vb, VertexBuffer idb) { } if (vb.isInstanced()) { - if (!caps.contains(Caps.MeshInstancing)) { + if (!glCaps.has(Caps.MeshInstancing)) { throw new RendererException("Instancing is required, " + "but not supported by the " + "graphics hardware"); @@ -2401,7 +2053,7 @@ public void setVertexAttrib(VertexBuffer vb) { } public void drawTriangleArray(Mesh.Mode mode, int count, int vertCount) { - boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); + boolean useInstancing = count > 1 && glCaps.has(Caps.MeshInstancing); if (useInstancing) { glext.glDrawArraysInstancedARB(convertElementMode(mode), 0, vertCount, count); @@ -2421,7 +2073,7 @@ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { break; case UnsignedInt: // Requres extension on OpenGL ES 2. - if (!caps.contains(Caps.IntegerIndexBuffer)) { + if (!glCaps.has(Caps.IntegerIndexBuffer)) { throw new RendererException("32-bit index buffers are not supported by the video hardware"); } break; @@ -2446,7 +2098,7 @@ public void drawTriangleList(VertexBuffer indexBuf, Mesh mesh, int count) { } int vertCount = mesh.getVertexCount(); - boolean useInstancing = count > 1 && caps.contains(Caps.MeshInstancing); + boolean useInstancing = count > 1 && glCaps.has(Caps.MeshInstancing); if (mesh.getMode() == Mode.Hybrid) { int[] modeStart = mesh.getModeStart(); @@ -2668,7 +2320,7 @@ public void renderMesh(Mesh mesh, int lod, int count, VertexBuffer[] instanceDat public void setMainFrameBufferSrgb(boolean enableSrgb) { // Gamma correction - if (!caps.contains(Caps.Srgb) && enableSrgb) { + if (!glCaps.has(Caps.Srgb) && enableSrgb) { // Not supported, sorry. logger.warning("sRGB framebuffer is not supported " + "by video hardware, but was requested."); @@ -2693,38 +2345,14 @@ public void setMainFrameBufferSrgb(boolean enableSrgb) { } public void setLinearizeSrgbImages(boolean linearize) { - if (caps.contains(Caps.Srgb)) { + if (glCaps.has(Caps.Srgb)) { linearizeSrgbImages = linearize; } } - public Map> getGLVersionCapabilityMapping() { - - Map> map = new TreeMap<>(); - - map.put(200, Collections.singletonList(Caps.OpenGL20)); - map.put(210, Collections.singletonList(Caps.OpenGL21)); - map.put(300, Collections.singletonList(Caps.OpenGL30)); - map.put(310, Collections.singletonList(Caps.OpenGL31)); - map.put(320, Collections.singletonList(Caps.OpenGL32)); - map.put(330, asList(Caps.OpenGL33, Caps.GeometryShader)); - map.put(400, asList(Caps.OpenGL40, Caps.TesselationShader)); - - return map; - } - - public Map> getGSLVersionCapabilityMapping() { - Map> map = new TreeMap<>(); - - map.put(100, Collections.singletonList(Caps.GLSL100)); - map.put(110, Collections.singletonList(Caps.GLSL110)); - map.put(120, Collections.singletonList(Caps.GLSL120)); - map.put(130, Collections.singletonList(Caps.GLSL130)); - map.put(140, Collections.singletonList(Caps.GLSL140)); - map.put(150, Collections.singletonList(Caps.GLSL150)); - map.put(330, Collections.singletonList(Caps.GLSL330)); - map.put(400, Collections.singletonList(Caps.GLSL400)); - - return map; + private int getInteger(int en) { + intBuf16.clear(); + gl.glGetInteger(en, intBuf16); + return intBuf16.get(0); } } diff --git a/jme3-core/src/main/resources/com/jme3/system/version.properties b/jme3-core/src/main/resources/com/jme3/system/version.properties index ab697bc9f44..6348fa76baa 100644 --- a/jme3-core/src/main/resources/com/jme3/system/version.properties +++ b/jme3-core/src/main/resources/com/jme3/system/version.properties @@ -1,12 +1,12 @@ # THIS IS AN AUTO-GENERATED FILE.. # DO NOT MODIFY! build.date=2016-04-03 -git.revision=5501 -git.branch=imageraster-cyclic -git.hash=1118cdf84383374836192ced709d4c95ea3136ea -git.hash.short=1118cdf +git.revision=5508 +git.branch=gl-render-maintainable +git.hash=c7dba4b9daf900df2aa5f92202dc9cb9a8a8b885 +git.hash.short=c7dba4b git.tag=null -name.full=jMonkeyEngine 3.1-imageraster-cyclic-5501 -version.full=3.1-imageraster-cyclic-5501 +name.full=jMonkeyEngine 3.1-gl-render-maintainable-5508 +version.full=3.1-gl-render-maintainable-5508 version.number=3.1.0 version.tag=SNAPSHOT \ No newline at end of file