Renderer Changes

* Add new "Limits" enum to hold maximums of various renderer capabilities such as texture size
 * Add support for seamless cubemap (enabled by default for mipmapped cubemaps)
 * Fix conflict when using GL tracing with GL debugging
experimental
shadowislord 10 years ago
parent c212fab37a
commit 9f3a145dd7
  1. 10
      jme3-core/src/main/java/com/jme3/renderer/Caps.java
  2. 78
      jme3-core/src/main/java/com/jme3/renderer/Limits.java
  3. 3
      jme3-core/src/main/java/com/jme3/renderer/RenderContext.java
  4. 0
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLDebugDesktop.java
  5. 2
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLExt.java
  6. 192
      jme3-core/src/main/java/com/jme3/renderer/opengl/GLRenderer.java
  7. 6
      jme3-lwjgl/src/main/java/com/jme3/system/lwjgl/LwjglContext.java

@ -316,7 +316,15 @@ public enum Caps {
* {@link Texture.MinFilter#NearestNoMipMaps}</li>
* </ul>
*/
PartialNonPowerOfTwoTextures;
PartialNonPowerOfTwoTextures,
/**
* When sampling cubemap edges, interpolate between the adjecent faces
* instead of just sampling one face.
* <p>
* Improves the quality of environment mapping.
*/
SeamlessCubemap;
/**
* Returns true if given the renderer capabilities, the texture

@ -0,0 +1,78 @@
/*
* Copyright (c) 2009-2015 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.renderer;
/**
* <code>Limits</code> allows querying the limits of certain features in
* {@link Renderer}.
* <p>
* For example, maximum texture sizes or number of samples.
*
* @author Kirill Vainer
*/
public enum Limits {
/**
* Maximum number of vertex texture units, or number of textures
* that can be used in the vertex shader.
*/
VertexTextureUnits,
/**
* Maximum number of fragment texture units, or number of textures
* that can be used in the fragment shader.
*/
FragmentTextureUnits,
FragmentUniforms,
VertexAttributes,
FrameBufferSamples,
FrameBufferAttachments,
FrameBufferMrtAttachments,
RenderBufferSize,
TextureSize,
CubemapSize,
VertexCount,
TriangleCount,
ColorTextureSamples,
DepthTextureSamples,
}

@ -265,6 +265,8 @@ public class RenderContext {
public ColorRGBA clearColor = new ColorRGBA(0,0,0,0);
public boolean seamlessCubemap = false;
/**
* Reset the RenderContext to default GL state
*/
@ -317,5 +319,6 @@ public class RenderContext {
depthFunc = RenderState.TestFunction.LessOrEqual;
alphaFunc = RenderState.TestFunction.Greater;
clearColor.set(0,0,0,0);
seamlessCubemap = false;
}
}

@ -67,6 +67,7 @@ public interface GLExt extends GLFbo {
public static final int GL_MAX_DRAW_BUFFERS_ARB = 0x8824;
public static final int GL_MAX_SAMPLES_EXT = 0x8D57;
public static final int GL_MULTISAMPLE_ARB = 0x809D;
public static final int GL_NUM_PROGRAM_BINARY_FORMATS = 0x87FE;
public static final int GL_R11F_G11F_B10F_EXT = 0x8C3A;
public static final int GL_RGBA8 = 0x8058;
public static final int GL_RGB16F_ARB = 0x881B;
@ -85,6 +86,7 @@ public interface GLExt extends GLFbo {
public static final int GL_TEXTURE_2D_ARRAY_EXT = 0x8C1A;
public static final int GL_TEXTURE_2D_MULTISAMPLE = 0x9100;
public static final int GL_TEXTURE_2D_MULTISAMPLE_ARRAY = 0x9102;
public static final int GL_TEXTURE_CUBE_MAP_SEAMLESS = 0x884F;
public static final int GL_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE;
public static final int GL_UNSIGNED_INT_10F_11F_11F_REV_EXT = 0x8C3B;
public static final int GL_UNSIGNED_INT_24_8_EXT = 0x84FA;

@ -56,6 +56,7 @@ import com.jme3.util.BufferUtils;
import com.jme3.util.ListMap;
import com.jme3.util.NativeObjectManager;
import java.nio.*;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
@ -79,22 +80,23 @@ public class GLRenderer implements Renderer {
private final RenderContext context = new RenderContext();
private final NativeObjectManager objManager = new NativeObjectManager();
private final EnumSet<Caps> caps = EnumSet.noneOf(Caps.class);
private int vertexTextureUnits;
private int fragTextureUnits;
private int vertexUniforms;
private int fragUniforms;
private int vertexAttribs;
private int maxFBOSamples;
private int maxFBOAttachs;
private int maxMRTFBOAttachs;
private int maxRBSize;
private int maxTexSize;
private int maxCubeTexSize;
private int maxVertCount;
private int maxTriCount;
private int maxColorTexSamples;
private int maxDepthTexSamples;
private final EnumMap<Limits, Integer> limits = new EnumMap<Limits, Integer>(Limits.class);
// private int vertexTextureUnits;
// private int fragTextureUnits;
// private int vertexUniforms;
// private int fragUniforms;
// private int vertexAttribs;
// private int maxFBOSamples;
// private int maxFBOAttachs;
// private int maxMRTFBOAttachs;
// private int maxRBSize;
// private int maxTexSize;
// private int maxCubeTexSize;
// private int maxVertCount;
// private int maxTriCount;
// private int maxColorTexSamples;
// private int maxDepthTexSamples;
private FrameBuffer mainFbOverride = null;
private final Statistics statistics = new Statistics();
private int vpX, vpY, vpW, vpH;
@ -228,14 +230,12 @@ public class GLRenderer implements Renderer {
private void loadCapabilitiesCommon() {
extensions = loadExtensions(gl.glGetString(GL.GL_EXTENSIONS));
vertexTextureUnits = getInteger(GL.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS);
logger.log(Level.FINER, "VTF Units: {0}", vertexTextureUnits);
if (vertexTextureUnits > 0) {
limits.put(Limits.VertexTextureUnits, getInteger(GL.GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS));
if (limits.get(Limits.VertexTextureUnits) > 0) {
caps.add(Caps.VertexTextureFetch);
}
fragTextureUnits = getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS);
logger.log(Level.FINER, "Texture Units: {0}", fragTextureUnits);
limits.put(Limits.FragmentTextureUnits, getInteger(GL.GL_MAX_TEXTURE_IMAGE_UNITS));
// gl.glGetInteger(GL.GL_MAX_VERTEX_UNIFORM_COMPONENTS, intBuf16);
// vertexUniforms = intBuf16.get(0);
@ -245,22 +245,9 @@ public class GLRenderer implements Renderer {
// fragUniforms = intBuf16.get(0);
// logger.log(Level.FINER, "Fragment Uniforms: {0}", fragUniforms);
vertexAttribs = getInteger(GL.GL_MAX_VERTEX_ATTRIBS);
logger.log(Level.FINER, "Vertex Attributes: {0}", vertexAttribs);
// gl.glGetInteger(GL.GL_MAX_ELEMENTS_VERTICES, intBuf16);
// maxVertCount = intBuf16.get(0);
// logger.log(Level.FINER, "Preferred Batch Vertex Count: {0}", maxVertCount);
//
// gl.glGetInteger(GL.GL_MAX_ELEMENTS_INDICES, intBuf16);
// maxTriCount = intBuf16.get(0);
// logger.log(Level.FINER, "Preferred Batch Index Count: {0}", maxTriCount);
maxTexSize = getInteger(GL.GL_MAX_TEXTURE_SIZE);
logger.log(Level.FINER, "Maximum Texture Resolution: {0}", maxTexSize);
maxCubeTexSize = getInteger(GL.GL_MAX_CUBE_MAP_TEXTURE_SIZE);
logger.log(Level.FINER, "Maximum CubeMap Resolution: {0}", maxCubeTexSize);
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")) {
@ -374,42 +361,36 @@ public class GLRenderer implements Renderer {
if (hasExtension("GL_EXT_framebuffer_object")) {
caps.add(Caps.FrameBuffer);
maxRBSize = getInteger(GLExt.GL_MAX_RENDERBUFFER_SIZE_EXT);
logger.log(Level.FINER, "FBO RB Max Size: {0}", maxRBSize);
maxFBOAttachs = getInteger(GLExt.GL_MAX_COLOR_ATTACHMENTS_EXT);
logger.log(Level.FINER, "FBO Max renderbuffers: {0}", maxFBOAttachs);
limits.put(Limits.RenderBufferSize, getInteger(GLExt.GL_MAX_RENDERBUFFER_SIZE_EXT));
limits.put(Limits.FrameBufferAttachments, getInteger(GLExt.GL_MAX_COLOR_ATTACHMENTS_EXT));
if (hasExtension("GL_EXT_framebuffer_blit")) {
caps.add(Caps.FrameBufferBlit);
}
if (hasExtension("GL_EXT_framebuffer_multisample")) {
caps.add(Caps.FrameBufferMultisample);
maxFBOSamples = getInteger(GLExt.GL_MAX_SAMPLES_EXT);
logger.log(Level.FINER, "FBO Max Samples: {0}", maxFBOSamples);
limits.put(Limits.FrameBufferSamples, getInteger(GLExt.GL_MAX_SAMPLES_EXT));
}
if (hasExtension("GL_ARB_texture_multisample")) {
caps.add(Caps.TextureMultisample);
maxColorTexSamples = getInteger(GLExt.GL_MAX_COLOR_TEXTURE_SAMPLES);
logger.log(Level.FINER, "Texture Multisample Color Samples: {0}", maxColorTexSamples);
maxDepthTexSamples = getInteger(GLExt.GL_MAX_DEPTH_TEXTURE_SAMPLES);
logger.log(Level.FINER, "Texture Multisample Depth Samples: {0}", maxDepthTexSamples);
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")) {
maxMRTFBOAttachs = getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB);
if (maxMRTFBOAttachs > 1) {
limits.put(Limits.FrameBufferMrtAttachments, getInteger(GLExt.GL_MAX_DRAW_BUFFERS_ARB));
if (limits.get(Limits.FrameBufferMrtAttachments) > 1) {
caps.add(Caps.FrameBufferMRT);
logger.log(Level.FINER, "FBO Max MRT renderbuffers: {0}", maxMRTFBOAttachs);
}
} else {
maxMRTFBOAttachs = 1;
limits.put(Limits.FrameBufferMrtAttachments, 1);
}
}
@ -419,6 +400,8 @@ public class GLRenderer implements Renderer {
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);
@ -430,6 +413,17 @@ public class GLRenderer implements Renderer {
caps.add(Caps.Srgb);
}
// Supports seamless cubemap
if (hasExtension("GL_ARB_seamless_cube_map") || caps.contains(Caps.OpenGL32)) {
caps.add(Caps.SeamlessCubemap);
}
if (hasExtension("GL_ARB_get_program_binary")) {
// OK ..
int binaryFormats = getInteger(GLExt.GL_NUM_PROGRAM_BINARY_FORMATS);
System.out.println("Binary Formats: " + binaryFormats);
}
logger.log(Level.FINE, "Caps: {0}", caps);
texUtil.initialize(caps);
@ -564,24 +558,24 @@ public class GLRenderer implements Renderer {
context.depthFunc = state.getDepthFunc();
}
if (gl2 != null) {
if (state.isAlphaTest() && !context.alphaTestEnabled) {
gl2.glEnable(GL2.GL_ALPHA_TEST);
gl2.glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff);
context.alphaTestEnabled = true;
} else if (!state.isAlphaTest() && context.alphaTestEnabled) {
gl2.glDisable(GL2.GL_ALPHA_TEST);
context.alphaTestEnabled = false;
}
if (state.getAlphaFallOff() != context.alphaTestFallOff) {
gl2.glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff);
context.alphaTestFallOff = state.getAlphaFallOff();
}
if (state.getAlphaFunc() != context.alphaFunc) {
gl2.glAlphaFunc(convertTestFunction(state.getAlphaFunc()), context.alphaTestFallOff);
context.alphaFunc = state.getAlphaFunc();
}
}
// if (gl2 != null) {
// if (state.isAlphaTest() && !context.alphaTestEnabled) {
// gl2.glEnable(GL2.GL_ALPHA_TEST);
// gl2.glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff);
// context.alphaTestEnabled = true;
// } else if (!state.isAlphaTest() && context.alphaTestEnabled) {
// gl2.glDisable(GL2.GL_ALPHA_TEST);
// context.alphaTestEnabled = false;
// }
// if (state.getAlphaFallOff() != context.alphaTestFallOff) {
// gl2.glAlphaFunc(convertTestFunction(context.alphaFunc), context.alphaTestFallOff);
// context.alphaTestFallOff = state.getAlphaFallOff();
// }
// if (state.getAlphaFunc() != context.alphaFunc) {
// gl2.glAlphaFunc(convertTestFunction(state.getAlphaFunc()), context.alphaTestFallOff);
// context.alphaFunc = state.getAlphaFunc();
// }
// }
if (state.isDepthWrite() && !context.depthWriteEnabled) {
gl.glDepthMask(true);
@ -1128,7 +1122,7 @@ public class GLRenderer implements Renderer {
// Check if GLSL version is 1.5 for shader
gl3.glBindFragDataLocation(id, 0, "outFragColor");
// For MRT
for (int i = 0; i < maxMRTFBOAttachs; i++) {
for (int i = 0; i < limits.get(Limits.FrameBufferMrtAttachments); i++) {
gl3.glBindFragDataLocation(id, i, "outFragData[" + i + "]");
}
}
@ -1346,7 +1340,8 @@ public class GLRenderer implements Renderer {
context.boundRB = id;
}
if (fb.getWidth() > maxRBSize || fb.getHeight() > maxRBSize) {
int rbSize = limits.get(Limits.RenderBufferSize);
if (fb.getWidth() > rbSize || fb.getHeight() > rbSize) {
throw new RendererException("Resolution " + fb.getWidth()
+ ":" + fb.getHeight() + " is not supported.");
}
@ -1355,8 +1350,9 @@ public class GLRenderer implements Renderer {
if (fb.getSamples() > 1 && caps.contains(Caps.FrameBufferMultisample)) {
int samples = fb.getSamples();
if (maxFBOSamples < samples) {
samples = maxFBOSamples;
int maxSamples = limits.get(Limits.FrameBufferSamples);
if (maxSamples < samples) {
samples = maxSamples;
}
glext.glRenderbufferStorageMultisampleEXT(GLExt.GL_RENDERBUFFER_EXT,
samples,
@ -1576,7 +1572,7 @@ public class GLRenderer implements Renderer {
}
}
} else {
if (fb.getNumColorBuffers() > maxFBOAttachs) {
if (fb.getNumColorBuffers() > limits.get(Limits.FrameBufferAttachments)) {
throw new RendererException("Framebuffer has more color "
+ "attachments than are supported"
+ " by the video hardware!");
@ -1586,7 +1582,7 @@ public class GLRenderer implements Renderer {
throw new RendererException("Multiple render targets "
+ " are not supported by the video hardware");
}
if (fb.getNumColorBuffers() > maxMRTFBOAttachs) {
if (fb.getNumColorBuffers() > limits.get(Limits.FrameBufferMrtAttachments)) {
throw new RendererException("Framebuffer has more"
+ " multi targets than are supported"
+ " by the video hardware!");
@ -1809,6 +1805,17 @@ public class GLRenderer implements Renderer {
gl.glTexParameteri(target, GL.GL_TEXTURE_MIN_FILTER, minFilter);
image.getLastTextureState().minFilter = tex.getMinFilter();
}
if (caps.contains(Caps.SeamlessCubemap) && tex.getType() == Texture.Type.CubeMap) {
if (haveMips && !context.seamlessCubemap) {
// We can enable seamless cubemap filtering.
gl.glEnable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
context.seamlessCubemap = true;
} else if (!haveMips && context.seamlessCubemap) {
// For skyboxes (no mipmaps), disable seamless cubemap filtering.
gl.glDisable(GLExt.GL_TEXTURE_CUBE_MAP_SEAMLESS);
context.seamlessCubemap = false;
}
}
if (tex.getAnisotropicFilter() > 1) {
if (caps.contains(Caps.TextureFilterAnisotropic)) {
@ -1940,11 +1947,12 @@ public class GLRenderer implements Renderer {
// bind texture
int target = convertTextureType(type, img.getMultiSamples(), -1);
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
if (context.boundTextures[unit] != img) {
if (context.boundTextureUnit != unit) {
gl.glActiveTexture(GL.GL_TEXTURE0 + unit);
context.boundTextureUnit = unit;
}
gl.glBindTexture(target, texId);
context.boundTextures[unit] = img;
@ -1975,9 +1983,9 @@ public class GLRenderer implements Renderer {
int imageSamples = img.getMultiSamples();
if (imageSamples > 1) {
if (img.getFormat().isDepthFormat()) {
img.setMultiSamples(Math.min(maxDepthTexSamples, imageSamples));
img.setMultiSamples(Math.min(limits.get(Limits.DepthTextureSamples), imageSamples));
} else {
img.setMultiSamples(Math.min(maxColorTexSamples, imageSamples));
img.setMultiSamples(Math.min(limits.get(Limits.ColorTextureSamples), imageSamples));
}
}
@ -1995,15 +2003,17 @@ public class GLRenderer implements Renderer {
if (target == GL.GL_TEXTURE_CUBE_MAP) {
// Check max texture size before upload
if (img.getWidth() > maxCubeTexSize || img.getHeight() > maxCubeTexSize) {
throw new RendererException("Cannot upload cubemap " + img + ". The maximum supported cubemap resolution is " + maxCubeTexSize);
int cubeSize = limits.get(Limits.CubemapSize);
if (img.getWidth() > cubeSize || img.getHeight() > cubeSize) {
throw new RendererException("Cannot upload cubemap " + img + ". The maximum supported cubemap resolution is " + cubeSize);
}
if (img.getWidth() != img.getHeight()) {
throw new RendererException("Cubemaps must have square dimensions");
}
} else {
if (img.getWidth() > maxTexSize || img.getHeight() > maxTexSize) {
throw new RendererException("Cannot upload texture " + img + ". The maximum supported texture resolution is " + maxTexSize);
int texSize = limits.get(Limits.TextureSize);
if (img.getWidth() > texSize || img.getHeight() > texSize) {
throw new RendererException("Cannot upload texture " + img + ". The maximum supported texture resolution is " + texSize);
}
}

@ -42,7 +42,7 @@ import com.jme3.renderer.lwjgl.LwjglGLExt;
import com.jme3.renderer.opengl.GL;
import com.jme3.renderer.opengl.GL2;
import com.jme3.renderer.opengl.GL3;
import com.jme3.renderer.opengl.GLDebugGL2;
import com.jme3.renderer.opengl.GLDebugDesktop;
import com.jme3.renderer.opengl.GLExt;
import com.jme3.renderer.opengl.GLFbo;
import com.jme3.renderer.opengl.GLRenderer;
@ -218,12 +218,12 @@ public abstract class LwjglContext implements JmeContext {
GLFbo glfbo = new LwjglGLExt();
if (settings.getBoolean("GraphicsDebug")) {
gl = new GLDebugGL2(gl, glfbo);
gl = new GLDebugDesktop(gl, glfbo);
glfbo = (GLFbo) gl;
}
if (settings.getBoolean("GraphicsTrace")) {
gl = (GL) GLTracer.createDesktopGlTracer(gl, GL2.class);
gl = (GL) GLTracer.createDesktopGlTracer(gl, GL.class, GL2.class, GL3.class);
glfbo = (GLFbo) GLTracer.createDesktopGlTracer(glfbo, GLExt.class);
}

Loading…
Cancel
Save