• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.tests;
18 
19 import com.badlogic.gdx.Gdx;
20 import com.badlogic.gdx.graphics.GL20;
21 import com.badlogic.gdx.graphics.GL30;
22 import com.badlogic.gdx.graphics.Mesh;
23 import com.badlogic.gdx.graphics.Texture;
24 import com.badlogic.gdx.graphics.VertexAttribute;
25 import com.badlogic.gdx.graphics.VertexAttributes;
26 import com.badlogic.gdx.graphics.g2d.BitmapFont;
27 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
28 import com.badlogic.gdx.graphics.glutils.IndexBufferObjectSubData;
29 import com.badlogic.gdx.graphics.glutils.ShaderProgram;
30 import com.badlogic.gdx.graphics.glutils.VertexBufferObjectWithVAO;
31 import com.badlogic.gdx.graphics.glutils.VertexData;
32 import com.badlogic.gdx.math.MathUtils;
33 import com.badlogic.gdx.math.Matrix4;
34 import com.badlogic.gdx.math.WindowedMean;
35 import com.badlogic.gdx.tests.utils.GdxTest;
36 import com.badlogic.gdx.utils.BufferUtils;
37 import com.badlogic.gdx.utils.GdxRuntimeException;
38 import com.badlogic.gdx.utils.StringBuilder;
39 
40 import java.nio.ByteBuffer;
41 import java.nio.FloatBuffer;
42 import java.nio.IntBuffer;
43 
44 public class VBOWithVAOPerformanceTest extends GdxTest {
45 
46 	ShaderProgram shader;
47 	Texture texture;
48 	Matrix4 matrix = new Matrix4();
49 
50 	Mesh oldVBOWithVAOMesh;
51 	Mesh newVBOWithVAOMesh;
52 
53 	SpriteBatch batch;
54 	BitmapFont bitmapFont;
55 	StringBuilder stringBuilder;
56 
57 	WindowedMean newCounter = new WindowedMean(100);
58 	WindowedMean oldCounter = new WindowedMean(100);
59 
60 	WindowedMean newCounterStress = new WindowedMean(100);
61 	WindowedMean oldCounterStress = new WindowedMean(100);
62 
63 	@Override
create()64 	public void create () {
65 		if (Gdx.gl30 == null) {
66 			throw new GdxRuntimeException("GLES 3.0 profile required for this test");
67 		}
68 		String vertexShader = "attribute vec4 a_position;    \n" + "attribute vec4 a_color;\n" + "attribute vec2 a_texCoord0;\n"
69 			+ "uniform mat4 u_worldView;\n" + "varying vec4 v_color;" + "varying vec2 v_texCoords;"
70 			+ "void main()                  \n" + "{                            \n" + "   v_color = a_color; \n"
71 			+ "   v_texCoords = a_texCoord0; \n" + "   gl_Position =  u_worldView * a_position;  \n"
72 			+ "}                            \n";
73 		String fragmentShader = "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n" + "varying vec4 v_color;\n"
74 			+ "varying vec2 v_texCoords;\n" + "uniform sampler2D u_texture;\n" + "void main()                                  \n"
75 			+ "{                                            \n" + "  gl_FragColor = v_color * texture2D(u_texture, v_texCoords);\n"
76 			+ "}";
77 
78 		shader = new ShaderProgram(vertexShader, fragmentShader);
79 		if (shader.isCompiled() == false) {
80 			Gdx.app.log("ShaderTest", shader.getLog());
81 			Gdx.app.exit();
82 		}
83 
84 		int numSprites = 1000;
85 		int maxIndices =  numSprites * 6;
86 		int maxVertices = numSprites * 6;
87 
88 		VertexAttribute[] vertexAttributes = new VertexAttribute[] {VertexAttribute.Position(), VertexAttribute.ColorUnpacked(), VertexAttribute.TexCoords(0)};
89 
90 		VertexBufferObjectWithVAO newVBOWithVAO =  new VertexBufferObjectWithVAO(false, maxVertices, vertexAttributes);
91 		OldVertexBufferObjectWithVAO oldVBOWithVAO =  new OldVertexBufferObjectWithVAO(false, maxVertices, vertexAttributes);
92 
93 		IndexBufferObjectSubData newIndices = new IndexBufferObjectSubData(false, maxIndices);
94 		IndexBufferObjectSubData oldIndices = new IndexBufferObjectSubData(false, maxIndices);
95 
96 		newVBOWithVAOMesh = new Mesh(newVBOWithVAO, newIndices, false) {};
97 		oldVBOWithVAOMesh = new Mesh(oldVBOWithVAO, oldIndices, false) {};
98 
99 		float[] vertexArray = new float[maxVertices * 9];
100 		int index = 0;
101 		int stride = 9 * 6;
102 		for (int i = 0; i < numSprites; i++) {
103 			addRandomSprite(vertexArray, index);
104 			index += stride;
105 		}
106 		short[] indexArray = new short[maxIndices];
107 		for (short i = 0; i < maxIndices; i++) {
108 			indexArray[i] = i;
109 		}
110 
111 		newVBOWithVAOMesh.setVertices(vertexArray);
112 		newVBOWithVAOMesh.setIndices(indexArray);
113 
114 		oldVBOWithVAOMesh.setVertices(vertexArray);
115 		oldVBOWithVAOMesh.setIndices(indexArray);
116 
117 		texture = new Texture(Gdx.files.internal("data/badlogic.jpg"));
118 
119 		batch = new SpriteBatch();
120 		bitmapFont = new BitmapFont();
121 		stringBuilder = new StringBuilder();
122 	}
123 
addRandomSprite(float[] vertArray, int currentIndex)124 	private void addRandomSprite (float[] vertArray, int currentIndex) {
125 		float width = MathUtils.random(0.05f, 0.2f);
126 		float height = MathUtils.random(0.05f, 0.2f);
127 		float x = MathUtils.random(-1f, 1f);
128 		float y = MathUtils.random(-1f, 1f);
129 		float r = MathUtils.random();
130 		float g = MathUtils.random();
131 		float b = MathUtils.random();
132 		float a = MathUtils.random();
133 
134 		vertArray[currentIndex++] = x;
135 		vertArray[currentIndex++] = y;
136 		vertArray[currentIndex++] = 0;
137 		vertArray[currentIndex++] = r;
138 		vertArray[currentIndex++] = g;
139 		vertArray[currentIndex++] = b;
140 		vertArray[currentIndex++] = a;
141 		vertArray[currentIndex++] = 0;
142 		vertArray[currentIndex++] = 1;
143 
144 		vertArray[currentIndex++] = x + width;
145 		vertArray[currentIndex++] = y;
146 		vertArray[currentIndex++] = 0;
147 		vertArray[currentIndex++] = r;
148 		vertArray[currentIndex++] = g;
149 		vertArray[currentIndex++] = b;
150 		vertArray[currentIndex++] = a;
151 		vertArray[currentIndex++] = 1;
152 		vertArray[currentIndex++] = 1;
153 
154 		vertArray[currentIndex++] = x + width;
155 		vertArray[currentIndex++] = y + height;
156 		vertArray[currentIndex++] = 0;
157 		vertArray[currentIndex++] = r;
158 		vertArray[currentIndex++] = g;
159 		vertArray[currentIndex++] = b;
160 		vertArray[currentIndex++] = a;
161 		vertArray[currentIndex++] = 1;
162 		vertArray[currentIndex++] = 0;
163 
164 		vertArray[currentIndex++] = x + width;
165 		vertArray[currentIndex++] = y + height;
166 		vertArray[currentIndex++] = 0;
167 		vertArray[currentIndex++] = r;
168 		vertArray[currentIndex++] = g;
169 		vertArray[currentIndex++] = b;
170 		vertArray[currentIndex++] = a;
171 		vertArray[currentIndex++] = 1;
172 		vertArray[currentIndex++] = 0;
173 
174 		vertArray[currentIndex++] = x;
175 		vertArray[currentIndex++] = y + height;
176 		vertArray[currentIndex++] = 0;
177 		vertArray[currentIndex++] = r;
178 		vertArray[currentIndex++] = g;
179 		vertArray[currentIndex++] = b;
180 		vertArray[currentIndex++] = a;
181 		vertArray[currentIndex++] = 0;
182 		vertArray[currentIndex++] = 0;
183 
184 		vertArray[currentIndex++] = x;
185 		vertArray[currentIndex++] = y;
186 		vertArray[currentIndex++] = 0;
187 		vertArray[currentIndex++] = r;
188 		vertArray[currentIndex++] = g;
189 		vertArray[currentIndex++] = b;
190 		vertArray[currentIndex++] = a;
191 		vertArray[currentIndex++] = 0;
192 		vertArray[currentIndex++] = 1;
193 	}
194 
195 	@Override
render()196 	public void render () {
197 		Gdx.gl20.glViewport(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight());
198 		Gdx.gl20.glClearColor(0.2f, 0.2f, 0.2f, 1);
199 		Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
200 		Gdx.gl20.glEnable(GL20.GL_TEXTURE_2D);
201 		Gdx.gl20.glEnable(GL20.GL_BLEND);
202 		Gdx.gl20.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
203 
204 
205 		texture.bind();
206 		shader.begin();
207 		shader.setUniformMatrix("u_worldView", matrix);
208 		shader.setUniformi("u_texture", 0);
209 
210 		long beforeOld = System.nanoTime();
211 		oldVBOWithVAOMesh.render(shader, GL20.GL_TRIANGLES);
212 		Gdx.gl.glFlush();
213 		oldCounter.addValue((System.nanoTime() - beforeOld));
214 		shader.end();
215 
216 		Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
217 
218 		texture.bind();
219 		shader.begin();
220 		shader.setUniformMatrix("u_worldView", matrix);
221 		shader.setUniformi("u_texture", 0);
222 
223 		long beforeNew = System.nanoTime();
224 		newVBOWithVAOMesh.render(shader, GL20.GL_TRIANGLES);
225 		Gdx.gl.glFlush();
226 		newCounter.addValue((System.nanoTime() - beforeNew));
227 		shader.end();
228 
229 
230 		Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
231 
232 		texture.bind();
233 		shader.begin();
234 		shader.setUniformMatrix("u_worldView", matrix);
235 		shader.setUniformi("u_texture", 0);
236 
237 		long beforeOldStress = System.nanoTime();
238 		for (int i = 0; i < 100; i++)
239 			oldVBOWithVAOMesh.render(shader, GL20.GL_TRIANGLES);
240 		Gdx.gl.glFlush();
241 		oldCounterStress.addValue((System.nanoTime() - beforeOldStress));
242 		shader.end();
243 
244 
245 		Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
246 
247 		texture.bind();
248 		shader.begin();
249 		shader.setUniformMatrix("u_worldView", matrix);
250 		shader.setUniformi("u_texture", 0);
251 
252 		long beforeNewStress = System.nanoTime();
253 		for (int i = 0; i < 100; i++)
254 			newVBOWithVAOMesh.render(shader, GL20.GL_TRIANGLES);
255 		Gdx.gl.glFlush();
256 		newCounterStress.addValue((System.nanoTime() - beforeNewStress));
257 		shader.end();
258 
259 
260 
261 		batch.begin();
262 		stringBuilder.setLength(0);
263 		stringBuilder.append("O Mean Time: ");
264 		stringBuilder.append(oldCounter.getMean());
265 		bitmapFont.draw(batch, stringBuilder, 0, 200);
266 		stringBuilder.setLength(0);
267 		stringBuilder.append("N Mean Time: ");
268 		stringBuilder.append(newCounter.getMean());
269 		bitmapFont.draw(batch, stringBuilder, 0, 200 - 20);
270 
271 		float oldMean = oldCounter.getMean();
272 		float newMean = newCounter.getMean();
273 
274 		float meanedAverage = newMean/oldMean;
275 		stringBuilder.setLength(0);
276 		stringBuilder.append("New VBO time as a percentage of Old Time: ");
277 		stringBuilder.append(meanedAverage);
278 		bitmapFont.draw(batch, stringBuilder, 0, 200 - 40);
279 
280 		stringBuilder.setLength(0);
281 		stringBuilder.append("Stress: O Mean Time: ");
282 		stringBuilder.append(oldCounterStress.getMean());
283 		bitmapFont.draw(batch, stringBuilder, 0, 200 - 80);
284 		stringBuilder.setLength(0);
285 		stringBuilder.append("Stress: N Mean Time: ");
286 		stringBuilder.append(newCounterStress.getMean());
287 		bitmapFont.draw(batch, stringBuilder, 0, 200 - 100);
288 
289 		float oldMeanStress = oldCounterStress.getMean();
290 		float newMeanStress = newCounterStress.getMean();
291 
292 		float meanedStressAverage = newMeanStress/oldMeanStress;
293 		stringBuilder.setLength(0);
294 		stringBuilder.append("Stress: New VBO time as a percentage of Old Time: ");
295 		stringBuilder.append(meanedStressAverage);
296 		bitmapFont.draw(batch, stringBuilder, 0, 200 - 120);
297 
298 
299 		batch.end();
300 	}
301 
302 	@Override
dispose()303 	public void dispose () {
304 		oldVBOWithVAOMesh.dispose();
305 		newVBOWithVAOMesh.dispose();
306 		texture.dispose();
307 		shader.dispose();
308 	}
309 
310 	private static class OldVertexBufferObjectWithVAO implements VertexData {
311 		final static IntBuffer tmpHandle = BufferUtils.newIntBuffer(1);
312 
313 		final VertexAttributes attributes;
314 		final FloatBuffer buffer;
315 		final ByteBuffer byteBuffer;
316 		int bufferHandle;
317 		final boolean isStatic;
318 		final int usage;
319 		boolean isDirty = false;
320 		boolean isBound = false;
321 		boolean vaoDirty = true;
322 		int vaoHandle = -1;
323 
OldVertexBufferObjectWithVAO(boolean isStatic, int numVertices, VertexAttribute... attributes)324 		public OldVertexBufferObjectWithVAO (boolean isStatic, int numVertices, VertexAttribute... attributes) {
325 			this(isStatic, numVertices, new VertexAttributes(attributes));
326 		}
327 
OldVertexBufferObjectWithVAO(boolean isStatic, int numVertices, VertexAttributes attributes)328 		public OldVertexBufferObjectWithVAO (boolean isStatic, int numVertices, VertexAttributes attributes) {
329 			this.isStatic = isStatic;
330 			this.attributes = attributes;
331 
332 			byteBuffer = BufferUtils.newUnsafeByteBuffer(this.attributes.vertexSize * numVertices);
333 			buffer = byteBuffer.asFloatBuffer();
334 			buffer.flip();
335 			byteBuffer.flip();
336 			bufferHandle = Gdx.gl20.glGenBuffer();
337 			usage = isStatic ? GL20.GL_STATIC_DRAW : GL20.GL_DYNAMIC_DRAW;
338 		}
339 
340 		@Override
getAttributes()341 		public VertexAttributes getAttributes() {
342 			return attributes;
343 		}
344 
345 		@Override
getNumVertices()346 		public int getNumVertices() {
347 			return buffer.limit() * 4 / attributes.vertexSize;
348 		}
349 
350 		@Override
getNumMaxVertices()351 		public int getNumMaxVertices() {
352 			return byteBuffer.capacity() / attributes.vertexSize;
353 		}
354 
355 		@Override
getBuffer()356 		public FloatBuffer getBuffer() {
357 			isDirty = true;
358 			return buffer;
359 		}
360 
bufferChanged()361 		private void bufferChanged() {
362 			if (isBound) {
363 				Gdx.gl20.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.limit(), byteBuffer, usage);
364 				isDirty = false;
365 			}
366 		}
367 
368 		@Override
setVertices(float[] vertices, int offset, int count)369 		public void setVertices(float[] vertices, int offset, int count) {
370 			isDirty = true;
371 			BufferUtils.copy(vertices, byteBuffer, count, offset);
372 			buffer.position(0);
373 			buffer.limit(count);
374 			bufferChanged();
375 		}
376 
377 		@Override
updateVertices(int targetOffset, float[] vertices, int sourceOffset, int count)378 		public void updateVertices(int targetOffset, float[] vertices, int sourceOffset, int count) {
379 			isDirty = true;
380 			final int pos = byteBuffer.position();
381 			byteBuffer.position(targetOffset * 4);
382 			BufferUtils.copy(vertices, sourceOffset, count, byteBuffer);
383 			byteBuffer.position(pos);
384 			buffer.position(0);
385 			bufferChanged();
386 		}
387 
388 		@Override
bind(ShaderProgram shader)389 		public void bind(ShaderProgram shader) {
390 			bind(shader, null);
391 		}
392 
393 		@Override
bind(ShaderProgram shader, int[] locations)394 		public void bind(ShaderProgram shader, int[] locations) {
395 			GL30 gl = Gdx.gl30;
396 			if (vaoDirty || !gl.glIsVertexArray(vaoHandle)) {
397 				//initialize the VAO with our vertex attributes and buffer:
398 				tmpHandle.clear();
399 				gl.glGenVertexArrays(1, tmpHandle);
400 				vaoHandle = tmpHandle.get(0);
401 				gl.glBindVertexArray(vaoHandle);
402 				vaoDirty = false;
403 
404 			} else {
405 				//else simply bind the VAO.
406 				gl.glBindVertexArray(vaoHandle);
407 			}
408 
409 			bindAttributes(shader, locations);
410 
411 			//if our data has changed upload it:
412 			bindData(gl);
413 
414 			isBound = true;
415 		}
416 
bindAttributes(ShaderProgram shader, int[] locations)417 		private void bindAttributes(ShaderProgram shader, int[] locations) {
418 			final GL20 gl = Gdx.gl20;
419 			gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, bufferHandle);
420 			final int numAttributes = attributes.size();
421 			if (locations == null) {
422 				for (int i = 0; i < numAttributes; i++) {
423 					final VertexAttribute attribute = attributes.get(i);
424 					final int location = shader.getAttributeLocation(attribute.alias);
425 					if (location < 0) continue;
426 					shader.enableVertexAttribute(location);
427 
428 					shader.setVertexAttribute(location, attribute.numComponents, attribute.type, attribute.normalized, attributes.vertexSize,
429 						attribute.offset);
430 				}
431 
432 			} else {
433 				for (int i = 0; i < numAttributes; i++) {
434 					final VertexAttribute attribute = attributes.get(i);
435 					final int location = locations[i];
436 					if (location < 0) continue;
437 					shader.enableVertexAttribute(location);
438 
439 					shader.setVertexAttribute(location, attribute.numComponents, attribute.type, attribute.normalized, attributes.vertexSize,
440 						attribute.offset);
441 				}
442 			}
443 		}
444 
bindData(GL20 gl)445 		private void bindData(GL20 gl) {
446 			if (isDirty) {
447 				gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, bufferHandle);
448 				byteBuffer.limit(buffer.limit() * 4);
449 				gl.glBufferData(GL20.GL_ARRAY_BUFFER, byteBuffer.limit(), byteBuffer, usage);
450 				isDirty = false;
451 			}
452 		}
453 
454 		@Override
unbind(final ShaderProgram shader)455 		public void unbind(final ShaderProgram shader) {
456 			unbind(shader, null);
457 		}
458 
459 		@Override
unbind(final ShaderProgram shader, final int[] locations)460 		public void unbind(final ShaderProgram shader, final int[] locations) {
461 			GL30 gl = Gdx.gl30;
462 			gl.glBindVertexArray(0);
463 			isBound = false;
464 		}
465 
466 		@Override
invalidate()467 		public void invalidate() {
468 			bufferHandle = Gdx.gl20.glGenBuffer();
469 			isDirty = true;
470 			vaoDirty = true;
471 		}
472 
473 		@Override
dispose()474 		public void dispose() {
475 			GL30 gl = Gdx.gl30;
476 
477 			gl.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
478 			gl.glDeleteBuffer(bufferHandle);
479 			bufferHandle = 0;
480 			BufferUtils.disposeUnsafeByteBuffer(byteBuffer);
481 
482 			if (gl.glIsVertexArray(vaoHandle)) {
483 				tmpHandle.clear();
484 				tmpHandle.put(vaoHandle);
485 				tmpHandle.flip();
486 				gl.glDeleteVertexArrays(1, tmpHandle);
487 			}
488 		}
489 	}
490 }
491