Okay I wrote a set of texture renders that are easily replaceable so I can do some tests.
First test was done by rendering scene 1 (low complexity) at a resolution of 1768px * 992px * 32bit
ScreenshotImmediate Texture Render: 120fps
Displaylist Texture Render: 123fps
Array Pointer Texture Render: 121fps
VBO Texture Render: 60fps
Second test was done by rendering scene 1 (low complexity) at a resolution of 1024px * 768px * 32bit
ScreenshotImmediate Texture Render: 235fps
Displaylist Texture Render: 240fps
Array Pointer Texture Render: 245fps
VBO Texture Render: 91fps
Third test was done by rendering scene 2 (high complexity) at a resolution of 1768px * 992px * 32bit
ScreenshotImmediate Texture Render: 71fps
Displaylist Texture Render: 103fps
Array Pointer Texture Render: 105fps
VBO Texture Render: 55fps
Fourth test was done by rendering scene 2 (high complexity) at a resolution of 1024px * 768px * 32bit
ScreenshotImmediate Texture Render: 115fps
Displaylist Texture Render: 114fps
Array Pointer Texture Render: 116fps
VBO Texture Render: 50fps
Immediate Texture Render Source
package illarion.graphics.lwjgl.render;
import illarion.graphics.SpriteColor;
import illarion.graphics.lwjgl.DriverSettingsLWJGL;
import illarion.graphics.lwjgl.TextureLWJGL;
import org.lwjgl.opengl.GL11;
/**
* This texture render uses the immediate methods to render a texture.
*
* @author Martin Karing
* @since 1.22
*/
public final class TextureRenderImmediate extends AbstractTextureRender {
/**
* The singleton instance of this class.
*/
private static final TextureRenderImmediate INSTANCE =
new TextureRenderImmediate();
/**
* Get the singleton instance of this class.
*
* @return the singleton instance of the immediate texture render
*/
public static TextureRenderImmediate getInstance() {
return INSTANCE;
}
/**
* Private constructor to avoid anything creating a instance of this
* renderer but singleton instance.
*/
private TextureRenderImmediate() {
// nothing to do
}
/**
* Draw a texture at a specified location using the immediate mode.
*
* @param x the x coordinate of the texture
* @param y the y coordinate of the texture
* @param z the z coordinate (so the layer) of the texture
* @param width the width of the area the texture shall be rendered on
* @param height the height of the area the texture shall be rendered on
* @param texture the texture that shall be drawn
* @param color the color that is supposed to be used with that texture
* @param mirror mirror the texture horizontal
*/
@Override
public void drawTexture(final float x, final float y, final float z,
final float width, final float height, final TextureLWJGL texture,
final SpriteColor color, final boolean mirror) {
DriverSettingsLWJGL.getInstance().enableTexture(texture.getTextureID());
color.setActiveColor();
GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
if (mirror) {
GL11.glTexCoord2f(texture.getRelX2(), texture.getRelY2());
GL11.glVertex2f(x, y);
GL11.glTexCoord2f(texture.getRelX2(), texture.getRelY1());
GL11.glVertex2f(x, y + height);
GL11.glTexCoord2f(texture.getRelX1(), texture.getRelY2());
GL11.glVertex2f(x + width, y);
GL11.glTexCoord2f(texture.getRelX1(), texture.getRelY1());
GL11.glVertex2f(x + width, y + height);
} else {
GL11.glTexCoord2f(texture.getRelX1(), texture.getRelY2());
GL11.glVertex2f(x, y);
GL11.glTexCoord2f(texture.getRelX1(), texture.getRelY1());
GL11.glVertex2f(x, y + height);
GL11.glTexCoord2f(texture.getRelX2(), texture.getRelY2());
GL11.glVertex2f(x + width, y);
GL11.glTexCoord2f(texture.getRelX2(), texture.getRelY1());
GL11.glVertex2f(x + width, y + height);
}
GL11.glEnd();
}
}
Displaylist Texture Render Source:
package illarion.graphics.lwjgl.render;
import illarion.graphics.SpriteColor;
import illarion.graphics.lwjgl.DriverSettingsLWJGL;
import illarion.graphics.lwjgl.TextureLWJGL;
import gnu.trove.TIntIntHashMap;
import gnu.trove.TIntObjectHashMap;
import org.lwjgl.opengl.GL11;
/**
* This texture render is drawing the graphics by using display lists.
*
* @author Martin Karing
* @since 1.22
*/
public final class TextureRenderDisplaylist extends AbstractTextureRender {
/**
* Get a new instance of this class.
*
* @return get a new instance of the display list render
*/
public static TextureRenderDisplaylist getInstance() {
return new TextureRenderDisplaylist();
}
/**
* The IDs of display lists stored in this instance of the texture render.
*/
private TIntIntHashMap displayLists = new TIntIntHashMap();
/**
* The IDs of display lists for mirrored render events stored in this
* texture render.
*/
private TIntIntHashMap displayListsMirror = new TIntIntHashMap();
/**
* The last dimensions that were rendered to check if the display list is
* still valid.
*/
private TIntObjectHashMap<float[]> lastDimentions =
new TIntObjectHashMap<float[]>();
/**
* Private constructor so new instances are only fetched by the
* {@link #getInstance()} method.
*/
private TextureRenderDisplaylist() {
// nothing to do
}
/**
* Draw a texture using display lists at a specified location.
*
* @param x the x coordinate of the texture
* @param y the y coordinate of the texture
* @param z the z coordinate (so the layer) of the texture
* @param width the width of the area the texture shall be rendered on
* @param height the height of the area the texture shall be rendered on
* @param texture the texture that shall be drawn
* @param color the color that is supposed to be used with that texture
* @param mirror mirror the texture horizontal
*/
@Override
public void drawTexture(final float x, final float y, final float z,
final float width, final float height, final TextureLWJGL texture,
final SpriteColor color, final boolean mirror) {
GL11.glPushMatrix();
final int textureID = texture.getUID();
DriverSettingsLWJGL.getInstance().enableTexture(texture.getTextureID());
color.setActiveColor();
GL11.glTranslatef(x, y, z);
TIntIntHashMap usedDisplayList;
if (mirror) {
usedDisplayList = displayListsMirror;
} else {
usedDisplayList = displayLists;
}
float[] textureSize = lastDimentions.get(textureID);
if (textureSize == null) {
textureSize = new float[2];
lastDimentions.put(textureID, textureSize);
}
int displayList = usedDisplayList.get(textureID);
boolean newList = false;
if ((textureSize[0] != width) || (textureSize[1] != height)) {
newList = true;
}
if (displayList < 1) {
displayList = GL11.glGenLists(1);
usedDisplayList.put(textureID, displayList);
newList = true;
}
if (newList) {
textureSize[0] = width;
textureSize[1] = height;
GL11.glNewList(displayList, GL11.GL_COMPILE);
GL11.glBegin(GL11.GL_TRIANGLE_STRIP);
if (mirror) {
GL11.glTexCoord2f(texture.getRelX2(), texture.getRelY2());
GL11.glVertex2f(0, 0);
GL11.glTexCoord2f(texture.getRelX2(), texture.getRelY1());
GL11.glVertex2f(0, height);
GL11.glTexCoord2f(texture.getRelX1(), texture.getRelY2());
GL11.glVertex2f(width, 0);
GL11.glTexCoord2f(texture.getRelX1(), texture.getRelY1());
GL11.glVertex2f(width, height);
} else {
GL11.glTexCoord2f(texture.getRelX1(), texture.getRelY2());
GL11.glVertex2f(0, 0);
GL11.glTexCoord2f(texture.getRelX1(), texture.getRelY1());
GL11.glVertex2f(0, height);
GL11.glTexCoord2f(texture.getRelX2(), texture.getRelY2());
GL11.glVertex2f(width, 0);
GL11.glTexCoord2f(texture.getRelX2(), texture.getRelY1());
GL11.glVertex2f(width, height);
}
GL11.glEnd();
GL11.glEndList();
}
GL11.glCallList(displayList);
GL11.glPopMatrix();
}
}
Array Pointer Texture Render Source
package illarion.graphics.lwjgl.render;
import illarion.graphics.SpriteColor;
import illarion.graphics.lwjgl.DriverSettingsLWJGL;
import illarion.graphics.lwjgl.TextureLWJGL;
import java.nio.FloatBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
/**
* This texture render uses array pointers to render a texture.
*
* @author Martin Karing
* @since 1.22
*/
public final class TextureRenderPointer extends AbstractTextureRender {
/**
* The singleton instance of this class.
*/
private static final TextureRenderPointer INSTANCE =
new TextureRenderPointer();
/**
* Get the singleton instance of this class.
*
* @return the singleton instance of the texture pointer render
*/
public static TextureRenderPointer getInstance() {
return INSTANCE;
}
/**
* The buffer that is used to store the texture coordinate data.
*/
private final FloatBuffer textureBuffer = BufferUtils.createFloatBuffer(8);
/**
* The buffer that is used to store the vertex data.
*/
private final FloatBuffer vertexBuffer = BufferUtils.createFloatBuffer(12);
/**
* Private constructor so new instances are only fetched by the
* {@link #getInstance()} method.
*/
private TextureRenderPointer() {
// nothing to do
}
/**
* Draw a texture using buffer pointers and draw arrays.
*
* @param x the x coordinate of the texture
* @param y the y coordinate of the texture
* @param z the z coordinate (so the layer) of the texture
* @param width the width of the area the texture shall be rendered on
* @param height the height of the area the texture shall be rendered on
* @param texture the texture that shall be drawn
* @param color the color that is supposed to be used with that texture
* @param mirror mirror the texture horizontal
*/
@Override
public void drawTexture(final float x, final float y, final float z,
final float width, final float height, final TextureLWJGL texture,
final SpriteColor color, final boolean mirror) {
DriverSettingsLWJGL.getInstance().enableTexturePointer(
texture.getTextureID());
color.setActiveColor();
vertexBuffer.rewind();
textureBuffer.rewind();
if (mirror) {
vertexBuffer.put(x).put(y);
textureBuffer.put(texture.getRelX2()).put(texture.getRelY2());
vertexBuffer.put(x).put(y + height);
textureBuffer.put(texture.getRelX2()).put(texture.getRelY1());
vertexBuffer.put(x + width).put(y);
textureBuffer.put(texture.getRelX1()).put(texture.getRelY2());
vertexBuffer.put(x + width).put(y + height);
textureBuffer.put(texture.getRelX1()).put(texture.getRelY1());
} else {
vertexBuffer.put(x).put(y);
textureBuffer.put(texture.getRelX1()).put(texture.getRelY2());
vertexBuffer.put(x).put(y + height);
textureBuffer.put(texture.getRelX1()).put(texture.getRelY1());
vertexBuffer.put(x + width).put(y);
textureBuffer.put(texture.getRelX2()).put(texture.getRelY2());
vertexBuffer.put(x + width).put(y + height);
textureBuffer.put(texture.getRelX2()).put(texture.getRelY1());
}
vertexBuffer.flip();
textureBuffer.flip();
GL11.glTexCoordPointer(2, 0, textureBuffer);
GL11.glVertexPointer(2, 0, vertexBuffer);
GL11.glDrawArrays(GL11.GL_TRIANGLE_STRIP, 0, 4);
}
}
VBO Texture Render Source:
package illarion.graphics.lwjgl.render;
import illarion.graphics.SpriteColor;
import illarion.graphics.lwjgl.DriverSettingsLWJGL;
import illarion.graphics.lwjgl.TextureLWJGL;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import gnu.trove.TIntIntHashMap;
import gnu.trove.TIntObjectHashMap;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ARBBufferObject;
import org.lwjgl.opengl.ARBVertexBufferObject;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GLContext;
/**
* Texture render using vertex buffer objects in order to render the textures.
* This texture render require the ARB extension for VBO rendering.
*
* @author Martin Karing
* @since 1.22
*/
public final class TextureRenderVBO extends AbstractTextureRender {
/**
* The byte offset of the texture data within the stride.
*/
private static final int OFFSET_TEXTURE = 2 * Float.SIZE / 8;
/**
* The byte offset of the vertex data within the stride.
*/
private static final int OFFSET_VERTEX = 0;
/**
* The length of one strife that contains vertex and texture coordinates in
* byte.
*/
private static final int STRIDE = (2 + 2) * Float.SIZE / 8;
static {
final int indexID = genBufferID();
final IntBuffer indexBuffer = BufferUtils.createIntBuffer(4);
indexBuffer.put(0).put(1).put(2).put(3);
indexBuffer.flip();
ARBBufferObject.glBindBufferARB(
ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexID);
ARBBufferObject.glBufferDataARB(
ARBVertexBufferObject.GL_ELEMENT_ARRAY_BUFFER_ARB, indexBuffer,
ARBBufferObject.GL_STATIC_DRAW_ARB);
}
/**
* Generate a new ID for a VBO Buffer.
*
* @return the generated ID
*/
private static int genBufferID() {
final IntBuffer intBuf = BufferUtils.createIntBuffer(1);
ARBBufferObject.glGenBuffersARB(intBuf);
return intBuf.get(0);
}
/**
* Get a instance of this class.
*
* @return a instance of the texture VBO render
*/
public static TextureRenderVBO getInstance() {
return new TextureRenderVBO();
}
/**
* The buffer that is used to store texture and vertex data.
*/
private final FloatBuffer buffer =
BufferUtils.createFloatBuffer(STRIDE * 4);
/**
* The last dimensions that were rendered to check if the display list is
* still valid.
*/
private TIntObjectHashMap<float[]> lastDimentions =
new TIntObjectHashMap<float[]>();
/**
* The IDs of VBOs stored in this instance of the texture render.
*/
private TIntIntHashMap vboIDs = new TIntIntHashMap();
/**
* The IDs of VBOs for mirrored render events stored in this texture render.
*/
private TIntIntHashMap vboIDsMirror = new TIntIntHashMap();
/**
* Private constructor so new instances are only fetched by the
* {@link #getInstance()} method.
*/
@SuppressWarnings("nls")
private TextureRenderVBO() {
if (!GLContext.getCapabilities().GL_ARB_vertex_buffer_object) {
throw new IllegalStateException("VBO not supported");
}
}
@Override
public void drawTexture(final float x, final float y, final float z,
final float width, final float height, final TextureLWJGL texture,
final SpriteColor color, final boolean mirror) {
final int textureID = texture.getUID();
DriverSettingsLWJGL.getInstance().enableTexturePointer(
texture.getTextureID());
GL11.glPushMatrix();
color.setActiveColor();
GL11.glTranslatef(x, y, z);
TIntIntHashMap usedVBOs;
if (mirror) {
usedVBOs = vboIDsMirror;
} else {
usedVBOs = vboIDs;
}
float[] textureSize = lastDimentions.get(textureID);
if (textureSize == null) {
textureSize = new float[2];
lastDimentions.put(textureID, textureSize);
}
int vboID = usedVBOs.get(textureID);
boolean newVBO = false;
if ((textureSize[0] != width) || (textureSize[1] != height)) {
newVBO = true;
}
if (vboID < 1) {
vboID = genBufferID();
usedVBOs.put(textureID, vboID);
newVBO = true;
}
ARBBufferObject.glBindBufferARB(
ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, vboID);
if (newVBO) {
GL11.glVertexPointer(2, GL11.GL_FLOAT, STRIDE, OFFSET_VERTEX);
GL11.glTexCoordPointer(2, GL11.GL_FLOAT, STRIDE, OFFSET_TEXTURE);
buffer.rewind();
if (mirror) {
buffer.put(0).put(0);
buffer.put(texture.getRelX2()).put(texture.getRelY2());
buffer.put(0).put(height);
buffer.put(texture.getRelX2()).put(texture.getRelY1());
buffer.put(width).put(0);
buffer.put(texture.getRelX1()).put(texture.getRelY2());
buffer.put(width).put(height);
buffer.put(texture.getRelX1()).put(texture.getRelY1());
} else {
buffer.put(0).put(0);
buffer.put(texture.getRelX1()).put(texture.getRelY2());
buffer.put(0).put(height);
buffer.put(texture.getRelX1()).put(texture.getRelY1());
buffer.put(width).put(0);
buffer.put(texture.getRelX2()).put(texture.getRelY2());
buffer.put(width).put(height);
buffer.put(texture.getRelX2()).put(texture.getRelY1());
}
buffer.flip();
ARBBufferObject.glBufferDataARB(
ARBVertexBufferObject.GL_ARRAY_BUFFER_ARB, buffer,
ARBBufferObject.GL_STREAM_DRAW_ARB);
}
GL12.glDrawRangeElements(GL11.GL_TRIANGLE_STRIP, 0, 4, 4,
GL11.GL_UNSIGNED_INT, 0);
GL11.glPopMatrix();
}
}
So the VBO's are not really useable in this case. The other texture renders work pretty good, only the immediate mode goes at its limits in case the render screen is large and the scene contains alot of images.
I will try now sorting the images by the textures to see how much the render speed is improved by this.
Nitram