import com.badlogic.gdx.Gdx;\r
import com.badlogic.gdx.graphics.VertexAttributes.Usage;\r
import com.badlogic.gdx.graphics.glutils.IndexBufferObject;\r
+import com.badlogic.gdx.graphics.glutils.IndexBufferObjectSubData;\r
+import com.badlogic.gdx.graphics.glutils.IndexData;\r
import com.badlogic.gdx.graphics.glutils.ShaderProgram;\r
import com.badlogic.gdx.graphics.glutils.VertexArray;\r
import com.badlogic.gdx.graphics.glutils.VertexBufferObject;\r
+import com.badlogic.gdx.graphics.glutils.VertexBufferObjectSubData;\r
import com.badlogic.gdx.graphics.glutils.VertexData;\r
\r
/**\r
* \r
*/\r
public class Mesh {\r
+ public enum VertexDataType {\r
+ VertexArray, VertexBufferObject, VertexBufferObjectSubData,\r
+ }\r
+\r
/** list of all meshes **/\r
static final ArrayList<Mesh> meshes = new ArrayList<Mesh>();\r
\r
final VertexData vertices;\r
- final IndexBufferObject indices;\r
+ final IndexData indices;\r
boolean autoBind = true;\r
final boolean isVertexArray;\r
\r
}\r
\r
/**\r
+ * Creates a new Mesh with the given attributes. This is an expert method\r
+ * with no error checking. Use at your own risk.\r
+ * \r
+ * @param type\r
+ * the {@link VertexDataType} to be used, VBO or VA.\r
+ * @param isStatic\r
+ * whether this mesh is static or not. Allows for internal\r
+ * optimizations.\r
+ * @param maxVertices\r
+ * the maximum number of vertices this mesh can hold\r
+ * @param maxIndices\r
+ * the maximum number of indices this mesh can hold\r
+ * @param attributes\r
+ * the {@link VertexAttribute}s. Each vertex attribute defines\r
+ * one property of a vertex such as position, normal or texture\r
+ * coordinate\r
+ */\r
+ public Mesh(VertexDataType type, boolean isStatic, int maxVertices,\r
+ int maxIndices, VertexAttribute... attributes) {\r
+ if (type == VertexDataType.VertexBufferObject) {\r
+ vertices = new VertexBufferObject(isStatic, maxVertices, attributes);\r
+ indices = new IndexBufferObject(isStatic, maxIndices);\r
+ isVertexArray = false;\r
+ } else if (type == VertexDataType.VertexBufferObjectSubData) {\r
+ vertices = new VertexBufferObjectSubData(isStatic, maxVertices, attributes);\r
+ indices = new IndexBufferObjectSubData(isStatic, maxIndices);\r
+ isVertexArray = false;\r
+ } else {\r
+ vertices = new VertexArray(maxVertices, attributes);\r
+ indices = new IndexBufferObject(maxIndices);\r
+ isVertexArray = true;\r
+ }\r
+\r
+ meshes.add(this);\r
+ }\r
+\r
+ /**\r
* Sets the vertices of this Mesh. The attributes are assumed to be given in\r
* float format. If this mesh is configured to use fixed point an\r
* IllegalArgumentException will be thrown.\r
* Sets whether to bind the underlying {@link VertexArray} or\r
* {@link VertexBufferObject} automatically on a call to one of the\r
* {@link #render(int)} methods or not. Usually you want to use autobind.\r
- * Manual binding is an expert functionality. There is a driver bug\r
- * on the MSM720xa chips that will fuck up memory if you manipulate the\r
- * vertices and indices of a Mesh multiple times while it is bound. Keep \r
- * this in mind.\r
+ * Manual binding is an expert functionality. There is a driver bug on the\r
+ * MSM720xa chips that will fuck up memory if you manipulate the vertices\r
+ * and indices of a Mesh multiple times while it is bound. Keep this in\r
+ * mind.\r
* \r
- * @param autoBind whether to autobind meshes.\r
+ * @param autoBind\r
+ * whether to autobind meshes.\r
*/\r
public void setAutoBind(boolean autoBind) {\r
this.autoBind = autoBind;\r
package com.badlogic.gdx.graphics;\r
\r
import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.graphics.Mesh.VertexDataType;\r
import com.badlogic.gdx.graphics.VertexAttributes.Usage;\r
import com.badlogic.gdx.graphics.glutils.ShaderProgram;\r
import com.badlogic.gdx.math.Matrix4;\r
public SpriteBatch() {\r
this(1000);\r
}\r
-\r
+ \r
/**\r
* <p>\r
* Constructs a new SpriteBatch. Sets the projection matrix to an\r
createShader();\r
}\r
\r
+ /**\r
+ * <p>\r
+ * Constructs a new SpriteBatch. Sets the projection matrix to an\r
+ * orthographic projection with y-axis point upwards, x-axis point to the\r
+ * right and the origin being in the bottome left corner of the screen. The\r
+ * projection will be pixel perfect with respect to the screen resolution.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * The size parameter specifies the maximum size of a single batch in number\r
+ * of sprites\r
+ * </p>\r
+ * \r
+ * @param size\r
+ * the batch size in number of sprites\r
+ * @param type\r
+ * the {@link VertexDataType} of the mesh to be used. This is an expert function.\r
+ */\r
+ public SpriteBatch(int size, VertexDataType type) {\r
+ this.mesh = new Mesh(type, false, size * 4, size * 6, new VertexAttribute(\r
+ Usage.Position, 2, "a_position"), new VertexAttribute(\r
+ Usage.ColorPacked, 4, "a_color"), new VertexAttribute(\r
+ Usage.TextureCoordinates, 2, "a_texCoords"));\r
+\r
+ projectionMatrix.setToOrtho2D(0, 0, Gdx.graphics.getWidth(),\r
+ Gdx.graphics.getHeight());\r
+\r
+ vertices = new float[size * Sprite.SPRITE_SIZE];\r
+\r
+ int len = size * 6;\r
+ short[] indices = new short[len];\r
+ short j = 0;\r
+ for (int i = 0; i < len; i += 6, j += 4) {\r
+ indices[i + 0] = (short) (j + 0);\r
+ indices[i + 1] = (short) (j + 1);\r
+ indices[i + 2] = (short) (j + 2);\r
+ indices[i + 3] = (short) (j + 2);\r
+ indices[i + 4] = (short) (j + 3);\r
+ indices[i + 5] = (short) (j + 0);\r
+ }\r
+ mesh.setIndices(indices);\r
+\r
+ if (Gdx.graphics.isGL20Available())\r
+ createShader();\r
+ }\r
+\r
private void createShader() {\r
String vertexShader = "attribute vec4 a_position; \n"\r
+ "attribute vec4 a_color; \n"\r
if (Gdx.graphics.isGL20Available() == false) {\r
GL10 gl = Gdx.gl10;\r
gl.glDepthMask(true);\r
- gl.glDisable(GL10.GL_BLEND);\r
gl.glDisable(GL10.GL_TEXTURE_2D);\r
} else {\r
shader.end();\r
GL20 gl = Gdx.gl20;\r
gl.glDepthMask(true);\r
- gl.glDisable(GL20.GL_BLEND);\r
gl.glDisable(GL20.GL_TEXTURE_2D);\r
}\r
mesh.unbind();\r
* @author mzechner\r
* \r
*/\r
-public class IndexBufferObject {\r
+public class IndexBufferObject implements IndexData {\r
final static IntBuffer tmpHandle = BufferUtils.newIntBuffer(1);\r
\r
ShortBuffer buffer;\r
* @author mzechner\r
* \r
*/\r
-public class IndexBufferObjectSubData {\r
+public class IndexBufferObjectSubData implements IndexData {\r
final static IntBuffer tmpHandle = BufferUtils.newIntBuffer(1);\r
\r
ShortBuffer buffer;\r
--- /dev/null
+package com.badlogic.gdx.graphics.glutils;\r
+\r
+import java.nio.ShortBuffer;\r
+\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.graphics.GL11;\r
+import com.badlogic.gdx.graphics.GL20;\r
+import com.badlogic.gdx.utils.GdxRuntimeException;\r
+\r
+/**\r
+ * An IndexData instance holds index data. Can be either a plain short buffer or an\r
+ * OpenGL buffer object. \r
+ * @author mzechner\r
+ *\r
+ */\r
+public interface IndexData {\r
+ /**\r
+ * @return the number of indices currently stored in this buffer\r
+ */\r
+ public int getNumIndices();\r
+\r
+ /**\r
+ * @return the maximum number of indices this IndexBufferObject can store.\r
+ */\r
+ public int getNumMaxIndices();\r
+\r
+ /**\r
+ * <p>\r
+ * Sets the indices of this IndexBufferObject, discarding the old indices.\r
+ * The count must equal the number of indices to be copied to this\r
+ * IndexBufferObject.\r
+ * </p>\r
+ * \r
+ * <p>\r
+ * This can be called in between calls to {@link #bind()} and\r
+ * {@link #unbind()}. The index data will be updated instantly.\r
+ * </p>\r
+ * \r
+ * @param indices\r
+ * the vertex data\r
+ * @param offset\r
+ * the offset to start copying the data from\r
+ * @param count\r
+ * the number of floats to copy\r
+ */\r
+ public void setIndices(short[] indices, int offset, int count);\r
+ /**\r
+ * <p>\r
+ * Returns the underlying ShortBuffer. If you modify the buffer contents\r
+ * they wil be uploaded on the call to {@link #bind()}. If you need\r
+ * immediate uploading use {@link #setIndices(short[], int, int)}.\r
+ * </p>\r
+ * \r
+ * @return the underlying short buffer.\r
+ */\r
+ public ShortBuffer getBuffer();\r
+\r
+ /**\r
+ * Binds this IndexBufferObject for rendering with glDrawElements.\r
+ */\r
+ public void bind();\r
+\r
+ /**\r
+ * Unbinds this IndexBufferObject.\r
+ */\r
+ public void unbind();\r
+ /**\r
+ * Invalidates the IndexBufferObject so a new OpenGL buffer handle is\r
+ * created. Use this in case of a context loss.\r
+ */\r
+ public void invalidate();\r
+\r
+ /**\r
+ * Disposes this IndexDatat and all its associated OpenGL resources.\r
+ */\r
+ public void dispose();\r
+}\r
--- /dev/null
+package com.badlogic.gdx.tests;\r
+\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.graphics.GL10;\r
+import com.badlogic.gdx.graphics.Mesh.VertexDataType;\r
+import com.badlogic.gdx.graphics.Sprite;\r
+import com.badlogic.gdx.graphics.SpriteBatch;\r
+import com.badlogic.gdx.graphics.SpriteCache;\r
+import com.badlogic.gdx.graphics.Texture;\r
+import com.badlogic.gdx.graphics.Texture.TextureFilter;\r
+import com.badlogic.gdx.graphics.Texture.TextureWrap;\r
+import com.badlogic.gdx.tests.utils.GdxTest;\r
+\r
+public class SpritePerformanceTest extends GdxTest {\r
+ StringBuilder log = new StringBuilder();\r
+ static final int SPRITES = 500;\r
+ Sprite[] sprites;\r
+ Texture texture;\r
+ SpriteBatch vaBatch;\r
+ SpriteBatch vboBatch;\r
+ SpriteCache cache;\r
+ int spritesHandle;\r
+ float rotation = 0;\r
+ \r
+ long startTime;\r
+ int frames;\r
+ \r
+ String[] modes = { "SpriteBatch blended", "SpriteBatch not blended", "SpriteBatch animated blended", "SpriteBatch animated not blended", "SpriteBatch VBO blended", "SpriteBatch VBO not blended", "SpriteBatch VBO animated blended", "SpriteBatch VBO animated not blended", "SpriteCache blended", "SpriteCache not blended" };\r
+ int mode = 0;\r
+ \r
+ public void create() {\r
+ texture = Gdx.graphics.newTexture(Gdx.files.internal("data/badlogicsmall.jpg"), TextureFilter.Linear, TextureFilter.Linear, TextureWrap.ClampToEdge, TextureWrap.ClampToEdge);\r
+ vaBatch = new SpriteBatch(1000);\r
+ vboBatch = new SpriteBatch(1000);\r
+ cache = new SpriteCache();\r
+ \r
+ sprites = new Sprite[SPRITES];\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ int x = (int)(Math.random() * (Gdx.graphics.getWidth() - 32));\r
+ int y = (int)(Math.random() * (Gdx.graphics.getHeight() - 32));\r
+ \r
+ sprites[i] = new Sprite(texture);\r
+ sprites[i].setPosition(x, y);\r
+ }\r
+ \r
+ cache.beginCache();\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ cache.add(sprites[i]);\r
+ }\r
+ int spritesHandle = cache.endCache();\r
+ \r
+ startTime = System.nanoTime();\r
+ frames = 0;\r
+ }\r
+ \r
+ public void render() {\r
+ Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT); \r
+ \r
+ switch(mode) {\r
+ case 0:\r
+ renderSpriteBatch();\r
+ break;\r
+ case 1:\r
+ renderSpriteBatchBlendDisabled();\r
+ break;\r
+ case 2:\r
+ renderSpriteBatchAnimated();\r
+ break;\r
+ case 3:\r
+ renderSpriteBatchAnimatedBlendDisabled();\r
+ break;\r
+ case 4:\r
+ renderSpriteBatchVBO();\r
+ break;\r
+ case 5:\r
+ renderSpriteBatchBlendDisabledVBO();\r
+ break;\r
+ case 6:\r
+ renderSpriteBatchAnimatedVBO();\r
+ break;\r
+ case 7:\r
+ renderSpriteBatchAnimatedBlendDisabledVBO();\r
+ break;\r
+ case 8:\r
+ renderSpriteCache();\r
+ break;\r
+ case 9:\r
+ renderSpriteCacheBlendDisabled();\r
+ break;\r
+ }\r
+ \r
+ int error = Gdx.gl.glGetError();\r
+ if(error != GL10.GL_NO_ERROR) {\r
+ Gdx.app.log("SpritePerformanceTest", "gl error: " + error );\r
+ }\r
+\r
+ \r
+ frames++;\r
+ if(System.nanoTime() - startTime > 5000000000l) {\r
+ Gdx.app.log("SpritePerformanceTest", "mode: " + modes[mode] + ", fps: " + frames / 5.0f);\r
+ log.append("mode: " + modes[mode] + ", fps: " + frames / 5.0f + "\n");\r
+ frames = 0;\r
+ startTime = System.nanoTime();\r
+ mode++;\r
+ if(mode > 9) mode = 0;\r
+ }\r
+ }\r
+ \r
+ void renderSpriteBatch() {\r
+ vaBatch.enableBlending();\r
+ vaBatch.begin();\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ sprites[i].draw(vaBatch);\r
+ }\r
+ vaBatch.end();\r
+ }\r
+ \r
+ void renderSpriteBatchBlendDisabled() {\r
+ vaBatch.disableBlending();\r
+ vaBatch.begin();\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ sprites[i].draw(vaBatch);\r
+ }\r
+ vaBatch.end();\r
+ }\r
+ \r
+ void renderSpriteBatchAnimated() {\r
+ rotation += 25 * Gdx.graphics.getDeltaTime();\r
+ vaBatch.enableBlending();\r
+ vaBatch.begin();\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ sprites[i].setRotation(rotation);\r
+ sprites[i].draw(vaBatch);\r
+ }\r
+ vaBatch.end();\r
+ }\r
+ \r
+ void renderSpriteBatchAnimatedBlendDisabled() {\r
+ rotation += 25 * Gdx.graphics.getDeltaTime();\r
+ vaBatch.disableBlending();\r
+ vaBatch.begin();\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ sprites[i].setRotation(rotation);\r
+ sprites[i].draw(vaBatch);\r
+ }\r
+ vaBatch.end();\r
+ }\r
+ \r
+ void renderSpriteBatchVBO() {\r
+ vaBatch.enableBlending();\r
+ vaBatch.begin();\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ sprites[i].draw(vaBatch);\r
+ }\r
+ vaBatch.end();\r
+ }\r
+ \r
+ void renderSpriteBatchBlendDisabledVBO() {\r
+ vaBatch.disableBlending();\r
+ vaBatch.begin();\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ sprites[i].draw(vaBatch);\r
+ }\r
+ vaBatch.end();\r
+ }\r
+ \r
+ void renderSpriteBatchAnimatedVBO() {\r
+ rotation += 25 * Gdx.graphics.getDeltaTime();\r
+ vaBatch.enableBlending();\r
+ vaBatch.begin();\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ sprites[i].setRotation(rotation);\r
+ sprites[i].draw(vaBatch);\r
+ }\r
+ vaBatch.end();\r
+ }\r
+ \r
+ void renderSpriteBatchAnimatedBlendDisabledVBO() {\r
+ rotation += 25 * Gdx.graphics.getDeltaTime();\r
+ vaBatch.disableBlending();\r
+ vaBatch.begin();\r
+ for(int i = 0; i < SPRITES; i++) {\r
+ sprites[i].setRotation(rotation);\r
+ sprites[i].draw(vaBatch);\r
+ }\r
+ vaBatch.end();\r
+ }\r
+ \r
+ \r
+ void renderSpriteCache() {\r
+ Gdx.gl.glEnable(GL10.GL_BLEND);\r
+ cache.begin();\r
+ cache.draw(spritesHandle);\r
+ cache.end();\r
+ }\r
+ \r
+ void renderSpriteCacheBlendDisabled() { \r
+ Gdx.gl.glDisable(GL10.GL_BLEND);\r
+ cache.begin();\r
+ cache.draw(spritesHandle);\r
+ cache.end();\r
+ }\r
+ \r
+ @Override\r
+ public boolean needsGL20() {\r
+ return false;\r
+ }\r
+\r
+}\r
import com.badlogic.gdx.tests.MyFirstTriangle;\r
import com.badlogic.gdx.tests.ObjTest;\r
import com.badlogic.gdx.tests.PixmapBlendingTest;\r
+import com.badlogic.gdx.tests.SpritePerformanceTest;\r
import com.badlogic.gdx.tests.SpriteSheetTest;\r
import com.badlogic.gdx.tests.ParticleEmitterTest;\r
import com.badlogic.gdx.tests.PixelsPerInchTest;\r
MultitouchTest.class,\r
MyFirstTriangle.class,\r
ObjTest.class,\r
- SpriteSheetTest.class,\r
ParticleEmitterTest.class,\r
PixelsPerInchTest.class,\r
PixmapBlendingTest.class,\r
SpriteBatchRotationTest.class,\r
SpriteBatchShaderTest.class,\r
SpriteBatchTest.class,\r
+ SpritePerformanceTest.class,\r
+ SpriteSheetTest.class,\r
StageTest.class,\r
TerrainTest.class, \r
TextureRenderTest.class,\r