es2pDrawCallBatchingTests.cpp revision 3c827367444ee418f129b2c238299f49d3264554
1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Draw call batching performance tests
22 *//*--------------------------------------------------------------------*/
23
24#include "es2pDrawCallBatchingTests.hpp"
25
26#include "gluShaderProgram.hpp"
27#include "gluRenderContext.hpp"
28
29#include "glwDefs.hpp"
30#include "glwFunctions.hpp"
31#include "glwEnums.hpp"
32
33#include "tcuTestLog.hpp"
34
35#include "deRandom.hpp"
36#include "deStringUtil.hpp"
37
38#include "deFile.h"
39#include "deString.h"
40#include "deClock.h"
41#include "deThread.h"
42
43#include <cmath>
44#include <vector>
45#include <string>
46#include <sstream>
47
48using tcu::TestLog;
49
50using namespace glw;
51
52using std::vector;
53using std::string;
54
55namespace deqp
56{
57namespace gles2
58{
59namespace Performance
60{
61
62namespace
63{
64const int CALIBRATION_SAMPLE_COUNT = 34;
65
66class DrawCallBatchingTest : public tcu::TestCase
67{
68public:
69	struct TestSpec
70	{
71		bool	useStaticBuffer;
72		int		staticAttributeCount;
73
74		bool	useDynamicBuffer;
75		int		dynamicAttributeCount;
76
77		int 	triangleCount;
78		int		drawCallCount;
79
80		bool	useDrawElements;
81		bool	useIndexBuffer;
82		bool	dynamicIndices;
83	};
84
85					DrawCallBatchingTest	(Context& context, const char* name, const char* description, const TestSpec& spec);
86					~DrawCallBatchingTest	(void);
87
88	void			init					(void);
89	void			deinit					(void);
90	IterateResult	iterate					(void);
91
92private:
93	enum State
94	{
95		STATE_LOG_INFO = 0,
96		STATE_WARMUP_BATCHED,
97		STATE_WARMUP_UNBATCHED,
98		STATE_CALC_CALIBRATION,
99		STATE_SAMPLE
100	};
101
102	State						m_state;
103
104	glu::RenderContext&			m_renderCtx;
105	de::Random					m_rnd;
106	int							m_sampleIteration;
107
108	int							m_unbatchedSampleCount;
109	int							m_batchedSampleCount;
110
111	TestSpec					m_spec;
112
113	glu::ShaderProgram*			m_program;
114
115	vector<deUint8>				m_dynamicIndexData;
116	vector<deUint8>				m_staticIndexData;
117
118	vector<GLuint>				m_unbatchedDynamicIndexBuffers;
119	GLuint						m_batchedDynamicIndexBuffer;
120
121	GLuint						m_unbatchedStaticIndexBuffer;
122	GLuint						m_batchedStaticIndexBuffer;
123
124	vector<vector<deInt8> >		m_staticAttributeDatas;
125	vector<vector<deInt8> >		m_dynamicAttributeDatas;
126
127	vector<GLuint>				m_batchedStaticBuffers;
128	vector<GLuint>				m_unbatchedStaticBuffers;
129
130	vector<GLuint>				m_batchedDynamicBuffers;
131	vector<vector<GLuint> >		m_unbatchedDynamicBuffers;
132
133	vector<deUint64>			m_unbatchedSamplesUs;
134	vector<deUint64>			m_batchedSamplesUs;
135
136	void						logTestInfo				(void);
137
138	deUint64					renderUnbatched			(void);
139	deUint64					renderBatched			(void);
140
141	void						createIndexData			(void);
142	void						createIndexBuffer		(void);
143
144	void						createShader			(void);
145	void						createAttributeDatas	(void);
146	void						createArrayBuffers		(void);
147};
148
149DrawCallBatchingTest::DrawCallBatchingTest (Context& context, const char* name, const char* description, const TestSpec& spec)
150	: tcu::TestCase					(context.getTestContext(), tcu::NODETYPE_PERFORMANCE, name, description)
151	, m_state						(STATE_LOG_INFO)
152	, m_renderCtx					(context.getRenderContext())
153	, m_rnd							(deStringHash(name))
154	, m_sampleIteration				(0)
155	, m_unbatchedSampleCount		(CALIBRATION_SAMPLE_COUNT)
156	, m_batchedSampleCount			(CALIBRATION_SAMPLE_COUNT)
157	, m_spec						(spec)
158	, m_program						(NULL)
159	, m_batchedDynamicIndexBuffer	(0)
160	, m_unbatchedStaticIndexBuffer	(0)
161	, m_batchedStaticIndexBuffer	(0)
162{
163}
164
165DrawCallBatchingTest::~DrawCallBatchingTest (void)
166{
167	deinit();
168}
169
170void DrawCallBatchingTest::createIndexData (void)
171{
172	if (m_spec.dynamicIndices)
173	{
174		for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
175		{
176			for (int triangleNdx = 0; triangleNdx < m_spec.triangleCount; triangleNdx++)
177			{
178				m_dynamicIndexData.push_back(triangleNdx * 3);
179				m_dynamicIndexData.push_back(triangleNdx * 3 + 1);
180				m_dynamicIndexData.push_back(triangleNdx * 3 + 2);
181			}
182		}
183	}
184	else
185	{
186		for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
187		{
188			for (int triangleNdx = 0; triangleNdx < m_spec.triangleCount; triangleNdx++)
189			{
190				m_staticIndexData.push_back(triangleNdx * 3);
191				m_staticIndexData.push_back(triangleNdx * 3 + 1);
192				m_staticIndexData.push_back(triangleNdx * 3 + 2);
193			}
194		}
195	}
196}
197
198void DrawCallBatchingTest::createShader (void)
199{
200	std::ostringstream		vertexShader;
201	std::ostringstream		fragmentShader;
202
203	for (int attributeNdx = 0; attributeNdx < m_spec.staticAttributeCount; attributeNdx++)
204		vertexShader << "attribute mediump vec4 a_static" << attributeNdx << ";\n";
205
206	if (m_spec.staticAttributeCount > 0 && m_spec.dynamicAttributeCount > 0)
207		vertexShader << "\n";
208
209	for (int attributeNdx = 0; attributeNdx < m_spec.dynamicAttributeCount; attributeNdx++)
210		vertexShader << "attribute mediump vec4 a_dyn" << attributeNdx << ";\n";
211
212	vertexShader
213	<< "\n"
214	<< "varying mediump vec4 v_color;\n"
215	<< "\n"
216	<< "void main (void)\n"
217	<< "{\n";
218
219	vertexShader << "\tv_color = ";
220
221	bool first = true;
222
223	for (int attributeNdx = 0; attributeNdx < m_spec.staticAttributeCount; attributeNdx++)
224	{
225		if (!first)
226			vertexShader << " + ";
227		first = false;
228
229		vertexShader << "a_static" << attributeNdx;
230	}
231
232	for (int attributeNdx = 0; attributeNdx < m_spec.dynamicAttributeCount; attributeNdx++)
233	{
234		if (!first)
235			vertexShader << " + ";
236		first = false;
237
238		vertexShader << "a_dyn" << attributeNdx;
239	}
240
241	vertexShader << ";\n";
242
243	if (m_spec.dynamicAttributeCount > 0)
244		vertexShader << "\tgl_Position = a_dyn0;\n";
245	else
246		vertexShader << "\tgl_Position = a_static0;\n";
247
248	vertexShader
249	<< "}";
250
251	fragmentShader
252	<< "varying mediump vec4 v_color;\n"
253	<< "\n"
254	<< "void main(void)\n"
255	<< "{\n"
256	<< "\tgl_FragColor = v_color;\n"
257	<< "}\n";
258
259	m_program = new glu::ShaderProgram(m_renderCtx, glu::ProgramSources() << glu::VertexSource(vertexShader.str()) << glu::FragmentSource(fragmentShader.str()));
260
261	m_testCtx.getLog() << (*m_program);
262	TCU_CHECK(m_program->isOk());
263}
264
265void DrawCallBatchingTest::createAttributeDatas (void)
266{
267	// Generate data for static attributes
268	for (int attribute = 0; attribute < m_spec.staticAttributeCount; attribute++)
269	{
270		vector<deInt8> data;
271
272		if (m_spec.dynamicAttributeCount == 0 && attribute == 0)
273		{
274			data.reserve(4 * 3 * m_spec.triangleCount * m_spec.drawCallCount);
275
276			for (int i = 0; i < m_spec.triangleCount * m_spec.drawCallCount; i++)
277			{
278				int sign = (m_spec.triangleCount % 2 == 1 || i % 2 == 0 ? 1 : -1);
279
280				data.push_back(-127 * sign);
281				data.push_back(-127 * sign);
282				data.push_back(0);
283				data.push_back(127);
284
285				data.push_back(127 * sign);
286				data.push_back(-127 * sign);
287				data.push_back(0);
288				data.push_back(127);
289
290				data.push_back(127 * sign);
291				data.push_back(127 * sign);
292				data.push_back(0);
293				data.push_back(127);
294			}
295		}
296		else
297		{
298			data.reserve(4 * 3 * m_spec.triangleCount * m_spec.drawCallCount);
299
300			for (int i = 0; i < 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount; i++)
301				data.push_back(m_rnd.getUint32());
302		}
303
304		m_staticAttributeDatas.push_back(data);
305	}
306
307	// Generate data for dynamic attributes
308	for (int attribute = 0; attribute < m_spec.dynamicAttributeCount; attribute++)
309	{
310		vector<deInt8> data;
311
312		if (attribute == 0)
313		{
314			data.reserve(4 * 3 * m_spec.triangleCount * m_spec.drawCallCount);
315
316			for (int i = 0; i < m_spec.triangleCount * m_spec.drawCallCount; i++)
317			{
318				int sign = (i % 2 == 0 ? 1 : -1);
319
320				data.push_back(-127 * sign);
321				data.push_back(-127 * sign);
322				data.push_back(0);
323				data.push_back(127);
324
325				data.push_back(127 * sign);
326				data.push_back(-127 * sign);
327				data.push_back(0);
328				data.push_back(127);
329
330				data.push_back(127 * sign);
331				data.push_back(127 * sign);
332				data.push_back(0);
333				data.push_back(127);
334			}
335		}
336		else
337		{
338			data.reserve(4 * 3 * m_spec.triangleCount * m_spec.drawCallCount);
339
340			for (int i = 0; i < 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount; i++)
341				data.push_back(m_rnd.getUint32());
342		}
343
344		m_dynamicAttributeDatas.push_back(data);
345	}
346}
347
348void DrawCallBatchingTest::createArrayBuffers (void)
349{
350	const glw::Functions& gl = m_renderCtx.getFunctions();
351
352	if (m_spec.useStaticBuffer)
353	{
354		// Upload static attributes for batched
355		for (int attribute = 0; attribute < m_spec.staticAttributeCount; attribute++)
356		{
357			GLuint buffer;
358
359			gl.genBuffers(1, &buffer);
360			gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
361			gl.bufferData(GL_ARRAY_BUFFER, 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount, &(m_staticAttributeDatas[attribute][0]), GL_STATIC_DRAW);
362			gl.bindBuffer(GL_ARRAY_BUFFER, 0);
363			GLU_EXPECT_NO_ERROR(gl.getError(), "Creating static buffer failed");
364
365			m_batchedStaticBuffers.push_back(buffer);
366		}
367
368		// Upload static attributes for unbatched
369		for (int attribute = 0; attribute < m_spec.staticAttributeCount; attribute++)
370		{
371			GLuint buffer;
372
373			gl.genBuffers(1, &buffer);
374			gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
375			gl.bufferData(GL_ARRAY_BUFFER, 4 * 3 * m_spec.triangleCount, &(m_staticAttributeDatas[attribute][0]), GL_STATIC_DRAW);
376			gl.bindBuffer(GL_ARRAY_BUFFER, 0);
377			GLU_EXPECT_NO_ERROR(gl.getError(), "Creating static buffer failed");
378
379			m_unbatchedStaticBuffers.push_back(buffer);
380		}
381	}
382
383	if (m_spec.useDynamicBuffer)
384	{
385		// Upload dynamic attributes for batched
386		for (int attribute = 0; attribute < m_spec.dynamicAttributeCount; attribute++)
387		{
388			GLuint buffer;
389
390			gl.genBuffers(1, &buffer);
391			gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
392			gl.bufferData(GL_ARRAY_BUFFER, 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount, &(m_dynamicAttributeDatas[attribute][0]), GL_STATIC_DRAW);
393			gl.bindBuffer(GL_ARRAY_BUFFER, 0);
394			GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic buffer failed");
395
396			m_batchedDynamicBuffers.push_back(buffer);
397		}
398
399		// Upload dynamic attributes for unbatched
400		for (int attribute = 0; attribute < m_spec.dynamicAttributeCount; attribute++)
401		{
402			vector<GLuint> buffers;
403
404			for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
405			{
406				GLuint buffer;
407
408				gl.genBuffers(1, &buffer);
409				gl.bindBuffer(GL_ARRAY_BUFFER, buffer);
410				gl.bufferData(GL_ARRAY_BUFFER, 4 * 3 * m_spec.triangleCount * m_spec.drawCallCount, &(m_dynamicAttributeDatas[attribute][0]), GL_STATIC_DRAW);
411				gl.bindBuffer(GL_ARRAY_BUFFER, 0);
412				GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic buffer failed");
413
414				buffers.push_back(buffer);
415			}
416
417			m_unbatchedDynamicBuffers.push_back(buffers);
418		}
419	}
420}
421
422void DrawCallBatchingTest::createIndexBuffer (void)
423{
424	const glw::Functions& gl = m_renderCtx.getFunctions();
425
426	if (m_spec.dynamicIndices)
427	{
428		for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
429		{
430			GLuint buffer;
431
432			gl.genBuffers(1, &buffer);
433			gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
434			gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * m_spec.triangleCount, &(m_dynamicIndexData[drawNdx * m_spec.triangleCount * 3]), GL_STATIC_DRAW);
435			gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
436			GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic index buffer failed");
437
438			m_unbatchedDynamicIndexBuffers.push_back(buffer);
439		}
440
441		{
442			GLuint buffer;
443
444			gl.genBuffers(1, &buffer);
445			gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
446			gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * m_spec.triangleCount * m_spec.drawCallCount, &(m_dynamicIndexData[0]), GL_STATIC_DRAW);
447			gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
448			GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic index buffer failed");
449
450			m_batchedDynamicIndexBuffer = buffer;
451		}
452	}
453	else
454	{
455		{
456			GLuint buffer;
457
458			gl.genBuffers(1, &buffer);
459			gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
460			gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * m_spec.triangleCount * m_spec.drawCallCount, &(m_staticIndexData[0]), GL_STATIC_DRAW);
461			gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
462			GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic index buffer failed");
463
464			m_batchedStaticIndexBuffer = buffer;
465		}
466
467		{
468			GLuint buffer;
469
470			gl.genBuffers(1, &buffer);
471			gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
472			gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * m_spec.triangleCount, &(m_staticIndexData[0]), GL_STATIC_DRAW);
473			gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
474			GLU_EXPECT_NO_ERROR(gl.getError(), "Creating dynamic index buffer failed");
475
476			m_unbatchedStaticIndexBuffer = buffer;
477		}
478	}
479}
480
481void DrawCallBatchingTest::init (void)
482{
483	createShader();
484	createAttributeDatas();
485	createArrayBuffers();
486
487	if (m_spec.useDrawElements)
488	{
489		createIndexData();
490
491		if (m_spec.useIndexBuffer)
492			createIndexBuffer();
493	}
494}
495
496void DrawCallBatchingTest::deinit (void)
497{
498	const glw::Functions& gl = m_renderCtx.getFunctions();
499
500	delete m_program;
501	m_program = NULL;
502
503	m_dynamicIndexData	= vector<deUint8>();
504	m_staticIndexData	= vector<deUint8>();
505
506	if (!m_unbatchedDynamicIndexBuffers.empty())
507	{
508		gl.deleteBuffers((GLsizei)m_unbatchedDynamicIndexBuffers.size(), &(m_unbatchedDynamicIndexBuffers[0]));
509		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
510
511		m_unbatchedDynamicIndexBuffers = vector<GLuint>();
512	}
513
514	if (m_batchedDynamicIndexBuffer)
515	{
516		gl.deleteBuffers((GLsizei)1, &m_batchedDynamicIndexBuffer);
517		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
518
519		m_batchedDynamicIndexBuffer = 0;
520	}
521
522	if (m_unbatchedStaticIndexBuffer)
523	{
524		gl.deleteBuffers((GLsizei)1, &m_unbatchedStaticIndexBuffer);
525		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
526
527		m_unbatchedStaticIndexBuffer = 0;
528	}
529
530	if (m_batchedStaticIndexBuffer)
531	{
532		gl.deleteBuffers((GLsizei)1, &m_batchedStaticIndexBuffer);
533		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
534
535		m_batchedStaticIndexBuffer = 0;
536	}
537
538	m_staticAttributeDatas	= vector<vector<deInt8> >();
539	m_dynamicAttributeDatas	= vector<vector<deInt8> >();
540
541	if (!m_batchedStaticBuffers.empty())
542	{
543		gl.deleteBuffers((GLsizei)m_batchedStaticBuffers.size(), &(m_batchedStaticBuffers[0]));
544		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
545
546		m_batchedStaticBuffers = vector<GLuint>();
547	}
548
549	if (!m_unbatchedStaticBuffers.empty())
550	{
551		gl.deleteBuffers((GLsizei)m_unbatchedStaticBuffers.size(), &(m_unbatchedStaticBuffers[0]));
552		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
553
554		m_unbatchedStaticBuffers = vector<GLuint>();
555	}
556
557	if (!m_batchedDynamicBuffers.empty())
558	{
559		gl.deleteBuffers((GLsizei)m_batchedDynamicBuffers.size(), &(m_batchedDynamicBuffers[0]));
560		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
561
562		m_batchedDynamicBuffers = vector<GLuint>();
563	}
564
565	for (int i = 0; i < (int)m_unbatchedDynamicBuffers.size(); i++)
566	{
567		gl.deleteBuffers((GLsizei)m_unbatchedDynamicBuffers[i].size(), &(m_unbatchedDynamicBuffers[i][0]));
568		GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteBuffers()");
569	}
570
571	m_unbatchedDynamicBuffers = vector<vector<GLuint> >();
572
573	m_unbatchedSamplesUs	= vector<deUint64>();
574	m_batchedSamplesUs		= vector<deUint64>();
575}
576
577deUint64 DrawCallBatchingTest::renderUnbatched (void)
578{
579	const glw::Functions&	gl		= m_renderCtx.getFunctions();
580	deUint64				beginUs	= 0;
581	deUint64				endUs	= 0;
582	vector<GLint>			dynamicAttributeLocations;
583
584	gl.viewport(0, 0, 32, 32);
585	gl.useProgram(m_program->getProgram());
586
587	// Setup static buffers
588	for (int attribNdx = 0; attribNdx < m_spec.staticAttributeCount; attribNdx++)
589	{
590		GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_static" + de::toString(attribNdx)).c_str());
591
592		gl.enableVertexAttribArray(location);
593
594		if (m_spec.useStaticBuffer)
595		{
596			gl.bindBuffer(GL_ARRAY_BUFFER, m_unbatchedStaticBuffers[attribNdx]);
597			gl.vertexAttribPointer(location, 4, GL_BYTE, GL_TRUE, 0, NULL);
598			gl.bindBuffer(GL_ARRAY_BUFFER, 0);
599		}
600		else
601			gl.vertexAttribPointer(location, 4, GL_BYTE, GL_TRUE, 0, &(m_staticAttributeDatas[attribNdx][0]));
602	}
603
604	// Get locations of dynamic attributes
605	for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
606	{
607		GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_dyn" + de::toString(attribNdx)).c_str());
608
609		gl.enableVertexAttribArray(location);
610		dynamicAttributeLocations.push_back(location);
611	}
612
613	if (m_spec.useDrawElements && m_spec.useIndexBuffer && !m_spec.dynamicIndices)
614		gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_unbatchedStaticIndexBuffer);
615
616	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup initial state for rendering.");
617
618	gl.finish();
619
620	beginUs = deGetMicroseconds();
621
622	for (int drawNdx = 0; drawNdx < m_spec.drawCallCount; drawNdx++)
623	{
624		for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
625		{
626			if (m_spec.useDynamicBuffer)
627			{
628				gl.bindBuffer(GL_ARRAY_BUFFER, m_unbatchedDynamicBuffers[attribNdx][drawNdx]);
629				gl.vertexAttribPointer(dynamicAttributeLocations[attribNdx], 4, GL_BYTE, GL_TRUE, 0, NULL);
630				gl.bindBuffer(GL_ARRAY_BUFFER, 0);
631			}
632			else
633				gl.vertexAttribPointer(dynamicAttributeLocations[attribNdx], 4, GL_BYTE, GL_TRUE, 0, &(m_dynamicAttributeDatas[attribNdx][m_spec.triangleCount * 3 * drawNdx * 4]));
634		}
635
636		if (m_spec.useDrawElements)
637		{
638			if (m_spec.useIndexBuffer)
639			{
640				if (m_spec.dynamicIndices)
641				{
642					gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_unbatchedDynamicIndexBuffers[drawNdx]);
643					gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3, GL_UNSIGNED_BYTE, NULL);
644					gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
645				}
646				else
647					gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3, GL_UNSIGNED_BYTE, NULL);
648			}
649			else
650			{
651				if (m_spec.dynamicIndices)
652					gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3, GL_UNSIGNED_BYTE, &(m_dynamicIndexData[drawNdx * m_spec.triangleCount * 3]));
653				else
654					gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3, GL_UNSIGNED_BYTE, &(m_staticIndexData[0]));
655			}
656		}
657		else
658			gl.drawArrays(GL_TRIANGLES, 0, 3 * m_spec.triangleCount);
659	}
660
661	gl.finish();
662
663	endUs = deGetMicroseconds();
664
665	GLU_EXPECT_NO_ERROR(gl.getError(), "Unbatched rendering failed");
666
667	gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
668
669	for (int attribNdx = 0; attribNdx < m_spec.staticAttributeCount; attribNdx++)
670	{
671		GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_static" + de::toString(attribNdx)).c_str());
672		gl.disableVertexAttribArray(location);
673	}
674
675	for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
676		gl.disableVertexAttribArray(dynamicAttributeLocations[attribNdx]);
677
678	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to reset state after unbatched rendering");
679
680	return endUs - beginUs;
681}
682
683deUint64 DrawCallBatchingTest::renderBatched (void)
684{
685	const glw::Functions&	gl		= m_renderCtx.getFunctions();
686	deUint64				beginUs	= 0;
687	deUint64				endUs	= 0;
688	vector<GLint>			dynamicAttributeLocations;
689
690	gl.viewport(0, 0, 32, 32);
691	gl.useProgram(m_program->getProgram());
692
693	// Setup static buffers
694	for (int attribNdx = 0; attribNdx < m_spec.staticAttributeCount; attribNdx++)
695	{
696		GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_static" + de::toString(attribNdx)).c_str());
697
698		gl.enableVertexAttribArray(location);
699
700		if (m_spec.useStaticBuffer)
701		{
702			gl.bindBuffer(GL_ARRAY_BUFFER, m_batchedStaticBuffers[attribNdx]);
703			gl.vertexAttribPointer(location, 4, GL_BYTE, GL_TRUE, 0, NULL);
704			gl.bindBuffer(GL_ARRAY_BUFFER, 0);
705		}
706		else
707			gl.vertexAttribPointer(location, 4, GL_BYTE, GL_TRUE, 0, &(m_staticAttributeDatas[attribNdx][0]));
708	}
709
710	// Get locations of dynamic attributes
711	for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
712	{
713		GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_dyn" + de::toString(attribNdx)).c_str());
714
715		gl.enableVertexAttribArray(location);
716		dynamicAttributeLocations.push_back(location);
717	}
718
719	if (m_spec.useDrawElements && m_spec.useIndexBuffer && !m_spec.dynamicIndices)
720		gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_batchedStaticIndexBuffer);
721
722	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to setup initial state for rendering.");
723
724	gl.finish();
725
726	beginUs = deGetMicroseconds();
727
728	for (int attribute = 0; attribute < m_spec.dynamicAttributeCount; attribute++)
729	{
730		if (m_spec.useDynamicBuffer)
731		{
732			gl.bindBuffer(GL_ARRAY_BUFFER, m_batchedDynamicBuffers[attribute]);
733			gl.vertexAttribPointer(dynamicAttributeLocations[attribute], 4, GL_BYTE, GL_TRUE, 0, NULL);
734			gl.bindBuffer(GL_ARRAY_BUFFER, 0);
735		}
736		else
737			gl.vertexAttribPointer(dynamicAttributeLocations[attribute], 4, GL_BYTE, GL_TRUE, 0, &(m_dynamicAttributeDatas[attribute][0]));
738	}
739
740	if (m_spec.useDrawElements)
741	{
742		if (m_spec.useIndexBuffer)
743		{
744			if (m_spec.dynamicIndices)
745			{
746				gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_batchedDynamicIndexBuffer);
747				gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3 * m_spec.drawCallCount, GL_UNSIGNED_BYTE, NULL);
748				gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
749			}
750			else
751				gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3 * m_spec.drawCallCount, GL_UNSIGNED_BYTE, NULL);
752		}
753		else
754		{
755			if (m_spec.dynamicIndices)
756				gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3 * m_spec.drawCallCount, GL_UNSIGNED_BYTE, &(m_dynamicIndexData[0]));
757			else
758				gl.drawElements(GL_TRIANGLES, m_spec.triangleCount * 3 * m_spec.drawCallCount, GL_UNSIGNED_BYTE, &(m_staticIndexData[0]));
759		}
760	}
761	else
762		gl.drawArrays(GL_TRIANGLES, 0, 3 * m_spec.triangleCount * m_spec.drawCallCount);
763
764	gl.finish();
765
766	endUs = deGetMicroseconds();
767
768	GLU_EXPECT_NO_ERROR(gl.getError(), "Batched rendering failed");
769
770	gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
771
772	for (int attribNdx = 0; attribNdx < m_spec.staticAttributeCount; attribNdx++)
773	{
774		GLint location = gl.getAttribLocation(m_program->getProgram(), ("a_static" + de::toString(attribNdx)).c_str());
775		gl.disableVertexAttribArray(location);
776	}
777
778	for (int attribNdx = 0; attribNdx < m_spec.dynamicAttributeCount; attribNdx++)
779		gl.disableVertexAttribArray(dynamicAttributeLocations[attribNdx]);
780
781	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to reset state after batched rendering");
782
783	return endUs - beginUs;
784}
785
786struct Statistics
787{
788	double mean;
789	double standardDeviation;
790	double standardErrorOfMean;
791};
792
793Statistics calculateStats (const vector<deUint64>& samples)
794{
795	double mean = 0.0;
796
797	for (int i = 0; i < (int)samples.size(); i++)
798		mean += (double)samples[i];
799
800	mean /= (double)samples.size();
801
802	double standardDeviation = 0.0;
803
804	for (int i = 0; i < (int)samples.size(); i++)
805	{
806		double x = (double)samples[i];
807		standardDeviation += (x - mean) * (x - mean);
808	}
809
810	standardDeviation /= (double)samples.size();
811	standardDeviation = std::sqrt(standardDeviation);
812
813	double standardErrorOfMean = standardDeviation / std::sqrt((double)samples.size());
814
815	Statistics stats;
816
817	stats.mean					= mean;
818	stats.standardDeviation		= standardDeviation;
819	stats.standardErrorOfMean	= standardErrorOfMean;
820
821	return stats;
822}
823
824void DrawCallBatchingTest::logTestInfo (void)
825{
826	TestLog&				log		= m_testCtx.getLog();
827	tcu::ScopedLogSection	section	(log, "Test info", "Test info");
828
829	log << TestLog::Message << "Rendering using " << (m_spec.useDrawElements ? "glDrawElements()" : "glDrawArrays()") << "." << TestLog::EndMessage;
830
831	if (m_spec.useDrawElements)
832		log << TestLog::Message << "Using " << (m_spec.dynamicIndices ? "dynamic " : "") << "indices from " << (m_spec.useIndexBuffer ? "buffer" : "pointer") << "." << TestLog::EndMessage;
833
834	if (m_spec.staticAttributeCount > 0)
835		log << TestLog::Message << "Using " << m_spec.staticAttributeCount << " static attribute" << (m_spec.staticAttributeCount > 1 ? "s" : "") << " from " << (m_spec.useStaticBuffer ? "buffer" : "pointer") << "." << TestLog::EndMessage;
836
837	if (m_spec.dynamicAttributeCount > 0)
838		log << TestLog::Message << "Using " << m_spec.dynamicAttributeCount << " dynamic attribute" << (m_spec.dynamicAttributeCount > 1 ? "s" : "") << " from " << (m_spec.useDynamicBuffer ? "buffer" : "pointer") << "." << TestLog::EndMessage;
839
840	log << TestLog::Message << "Rendering " << m_spec.drawCallCount << " draw calls with " << m_spec.triangleCount << " triangles per call." << TestLog::EndMessage;
841}
842
843tcu::TestCase::IterateResult DrawCallBatchingTest::iterate (void)
844{
845	if (m_state == STATE_LOG_INFO)
846	{
847		logTestInfo();
848		m_state = STATE_WARMUP_BATCHED;
849	}
850	else if (m_state == STATE_WARMUP_BATCHED)
851	{
852		renderBatched();
853		m_state = STATE_WARMUP_UNBATCHED;
854	}
855	else if (m_state == STATE_WARMUP_UNBATCHED)
856	{
857		renderUnbatched();
858		m_state = STATE_SAMPLE;
859	}
860	else if (m_state == STATE_SAMPLE)
861	{
862		if ((int)m_unbatchedSamplesUs.size() < m_unbatchedSampleCount && (m_unbatchedSamplesUs.size() / ((double)m_unbatchedSampleCount) < m_batchedSamplesUs.size() / ((double)m_batchedSampleCount) || (int)m_batchedSamplesUs.size() >= m_batchedSampleCount))
863			m_unbatchedSamplesUs.push_back(renderUnbatched());
864		else if ((int)m_batchedSamplesUs.size() < m_batchedSampleCount)
865			m_batchedSamplesUs.push_back(renderBatched());
866		else
867			m_state = STATE_CALC_CALIBRATION;
868	}
869	else if (m_state == STATE_CALC_CALIBRATION)
870	{
871		TestLog& log = m_testCtx.getLog();
872
873		tcu::ScopedLogSection	section(log, ("Sampling iteration " + de::toString(m_sampleIteration)).c_str(), ("Sampling iteration " + de::toString(m_sampleIteration)).c_str());
874		const double targetSEM	= 0.02;
875		const double limitSEM	= 0.025;
876
877		Statistics unbatchedStats	= calculateStats(m_unbatchedSamplesUs);
878		Statistics batchedStats		= calculateStats(m_batchedSamplesUs);
879
880		log << TestLog::Message << "Batched samples; Count: " << m_batchedSamplesUs.size() << ", Mean: " << batchedStats.mean << "us, Standard deviation: " << batchedStats.standardDeviation << "us, Standard error of mean: " << batchedStats.standardErrorOfMean << "us(" << (batchedStats.standardErrorOfMean/batchedStats.mean) << ")" << TestLog::EndMessage;
881		log << TestLog::Message << "Unbatched samples; Count: " << m_unbatchedSamplesUs.size() << ", Mean: " << unbatchedStats.mean << "us, Standard deviation: " << unbatchedStats.standardDeviation << "us, Standard error of mean: " << unbatchedStats.standardErrorOfMean << "us(" << (unbatchedStats.standardErrorOfMean/unbatchedStats.mean) << ")" << TestLog::EndMessage;
882
883		if (m_sampleIteration > 2 || (m_sampleIteration > 0 && (unbatchedStats.standardErrorOfMean/unbatchedStats.mean) + (batchedStats.standardErrorOfMean/batchedStats.mean) <= 2.0 * limitSEM))
884		{
885			if (m_sampleIteration > 2)
886				log << TestLog::Message << "Maximum iteration count reached." << TestLog::EndMessage;
887
888			log << TestLog::Message << "Standard errors in target range." << TestLog::EndMessage;
889			log << TestLog::Message << "Batched/Unbatched ratio: " << (batchedStats.mean / unbatchedStats.mean) << TestLog::EndMessage;
890
891			m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString((float)(batchedStats.mean/unbatchedStats.mean), 1).c_str());
892			return STOP;
893		}
894		else
895		{
896			if ((unbatchedStats.standardErrorOfMean/unbatchedStats.mean) > targetSEM)
897				log << TestLog::Message << "Unbatched standard error of mean outside of range." << TestLog::EndMessage;
898
899			if ((batchedStats.standardErrorOfMean/batchedStats.mean) > targetSEM)
900				log << TestLog::Message << "Batched standard error of mean outside of range." << TestLog::EndMessage;
901
902			if (unbatchedStats.standardDeviation > 0.0)
903			{
904				double x = (unbatchedStats.standardDeviation / unbatchedStats.mean) / targetSEM;
905				m_unbatchedSampleCount = std::max((int)m_unbatchedSamplesUs.size(), (int)(x * x));
906			}
907			else
908				m_unbatchedSampleCount = (int)m_unbatchedSamplesUs.size();
909
910			if (batchedStats.standardDeviation > 0.0)
911			{
912				double x = (batchedStats.standardDeviation / batchedStats.mean) / targetSEM;
913				m_batchedSampleCount = std::max((int)m_batchedSamplesUs.size(), (int)(x * x));
914			}
915			else
916				m_batchedSampleCount = (int)m_batchedSamplesUs.size();
917
918			m_batchedSamplesUs.clear();
919			m_unbatchedSamplesUs.clear();
920
921			m_sampleIteration++;
922			m_state = STATE_SAMPLE;
923		}
924	}
925	else
926		DE_ASSERT(false);
927
928	return CONTINUE;
929}
930
931string specToName (const DrawCallBatchingTest::TestSpec& spec)
932{
933	std::ostringstream stream;
934
935	DE_ASSERT(!spec.useStaticBuffer || spec.staticAttributeCount > 0);
936	DE_ASSERT(!spec.useDynamicBuffer|| spec.dynamicAttributeCount > 0);
937
938	if (spec.staticAttributeCount > 0)
939		stream << spec.staticAttributeCount << "_static_";
940
941	if (spec.useStaticBuffer)
942		stream << (spec.staticAttributeCount == 1 ? "buffer_" : "buffers_");
943
944	if (spec.dynamicAttributeCount > 0)
945		stream << spec.dynamicAttributeCount << "_dynamic_";
946
947	if (spec.useDynamicBuffer)
948		stream << (spec.dynamicAttributeCount == 1 ? "buffer_" : "buffers_");
949
950	stream << spec.triangleCount << "_triangles";
951
952	return stream.str();
953}
954
955string specToDescrpition (const DrawCallBatchingTest::TestSpec& spec)
956{
957	DE_UNREF(spec);
958	return "Test performance of batched rendering against non-batched rendering.";
959}
960
961} // anonymous
962
963DrawCallBatchingTests::DrawCallBatchingTests (Context& context)
964	: TestCaseGroup(context, "draw_call_batching", "Draw call batching performance tests.")
965{
966}
967
968DrawCallBatchingTests::~DrawCallBatchingTests (void)
969{
970}
971
972void DrawCallBatchingTests::init (void)
973{
974	int drawCallCounts[] = {
975		10, 100
976	};
977
978	int triangleCounts[] = {
979		2, 10
980	};
981
982	int staticAttributeCounts[] = {
983		1, 0, 4, 8, 0
984	};
985
986	int dynamicAttributeCounts[] = {
987		0, 1, 4, 0, 8
988	};
989
990	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(staticAttributeCounts) == DE_LENGTH_OF_ARRAY(dynamicAttributeCounts));
991
992	for (int drawType = 0; drawType < 2; drawType++)
993	{
994		bool drawElements = (drawType == 1);
995
996		for (int indexBufferNdx = 0; indexBufferNdx < 2; indexBufferNdx++)
997		{
998			bool useIndexBuffer = (indexBufferNdx == 1);
999
1000			if (useIndexBuffer && !drawElements)
1001				continue;
1002
1003			for (int dynamicIndexNdx = 0; dynamicIndexNdx < 2; dynamicIndexNdx++)
1004			{
1005				bool dynamicIndices = (dynamicIndexNdx == 1);
1006
1007				if (dynamicIndices && !drawElements)
1008					continue;
1009
1010				if (dynamicIndices && !useIndexBuffer)
1011					continue;
1012
1013				TestCaseGroup* drawTypeGroup = new TestCaseGroup(m_context, (string(dynamicIndices ? "dynamic_" : "") + (useIndexBuffer ? "buffer_" : "" ) + (drawElements ? "draw_elements" : "draw_arrays")).c_str(), (string("Test batched rendering with ") + (drawElements ? "draw_elements" : "draw_arrays")).c_str());
1014
1015				addChild(drawTypeGroup);
1016
1017				for (int drawCallCountNdx = 0; drawCallCountNdx < DE_LENGTH_OF_ARRAY(drawCallCounts); drawCallCountNdx++)
1018				{
1019					int drawCallCount = drawCallCounts[drawCallCountNdx];
1020
1021					TestCaseGroup*	callCountGroup			= new TestCaseGroup(m_context, (de::toString(drawCallCount) + (drawCallCount == 1 ? "_draw" : "_draws")).c_str(), ("Test batched rendering performance with " + de::toString(drawCallCount) + " draw calls.").c_str());
1022					TestCaseGroup*	attributeCount1Group	= new TestCaseGroup(m_context, "1_attribute", "Test draw call batching with 1 attribute.");
1023					TestCaseGroup*	attributeCount8Group	= new TestCaseGroup(m_context, "8_attributes", "Test draw call batching with 8 attributes.");
1024
1025					callCountGroup->addChild(attributeCount1Group);
1026					callCountGroup->addChild(attributeCount8Group);
1027
1028					drawTypeGroup->addChild(callCountGroup);
1029
1030					for (int attributeCountNdx = 0; attributeCountNdx < DE_LENGTH_OF_ARRAY(dynamicAttributeCounts); attributeCountNdx++)
1031					{
1032						TestCaseGroup*	attributeCountGroup		= NULL;
1033
1034						int				staticAttributeCount	= staticAttributeCounts[attributeCountNdx];
1035						int				dynamicAttributeCount	= dynamicAttributeCounts[attributeCountNdx];
1036
1037						if (staticAttributeCount + dynamicAttributeCount == 1)
1038							attributeCountGroup = attributeCount1Group;
1039						else if (staticAttributeCount + dynamicAttributeCount == 8)
1040							attributeCountGroup = attributeCount8Group;
1041						else
1042							DE_ASSERT(false);
1043
1044						for (int triangleCountNdx = 0; triangleCountNdx < DE_LENGTH_OF_ARRAY(triangleCounts); triangleCountNdx++)
1045						{
1046							int triangleCount = triangleCounts[triangleCountNdx];
1047
1048							for (int dynamicBufferNdx = 0; dynamicBufferNdx < 2; dynamicBufferNdx++)
1049							{
1050								bool useDynamicBuffer = (dynamicBufferNdx != 0);
1051
1052								for (int staticBufferNdx = 0; staticBufferNdx < 2; staticBufferNdx++)
1053								{
1054									bool useStaticBuffer = (staticBufferNdx != 0);
1055
1056									DrawCallBatchingTest::TestSpec spec;
1057
1058									spec.useStaticBuffer		= useStaticBuffer;
1059									spec.staticAttributeCount	= staticAttributeCount;
1060
1061									spec.useDynamicBuffer		= useDynamicBuffer;
1062									spec.dynamicAttributeCount	= dynamicAttributeCount;
1063
1064									spec.drawCallCount			= drawCallCount;
1065									spec.triangleCount			= triangleCount;
1066
1067									spec.useDrawElements		= drawElements;
1068									spec.useIndexBuffer			= useIndexBuffer;
1069									spec.dynamicIndices			= dynamicIndices;
1070
1071									if (spec.useStaticBuffer && spec.staticAttributeCount == 0)
1072										continue;
1073
1074									if (spec.useDynamicBuffer && spec.dynamicAttributeCount == 0)
1075										continue;
1076
1077									attributeCountGroup->addChild(new DrawCallBatchingTest(m_context, specToName(spec).c_str(), specToDescrpition(spec).c_str(), spec));
1078								}
1079							}
1080						}
1081					}
1082				}
1083			}
1084		}
1085	}
1086}
1087
1088} // Performance
1089} // gles2
1090} // deqp
1091