Home | History | Annotate | Download | only in performance
      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 
     48 using tcu::TestLog;
     49 
     50 using namespace glw;
     51 
     52 using std::vector;
     53 using std::string;
     54 
     55 namespace deqp
     56 {
     57 namespace gles2
     58 {
     59 namespace Performance
     60 {
     61 
     62 namespace
     63 {
     64 const int CALIBRATION_SAMPLE_COUNT = 34;
     65 
     66 class DrawCallBatchingTest : public tcu::TestCase
     67 {
     68 public:
     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 
     92 private:
     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 
    149 DrawCallBatchingTest::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 
    165 DrawCallBatchingTest::~DrawCallBatchingTest (void)
    166 {
    167 	deinit();
    168 }
    169 
    170 void 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(deUint8(triangleNdx * 3));
    179 				m_dynamicIndexData.push_back(deUint8(triangleNdx * 3 + 1));
    180 				m_dynamicIndexData.push_back(deUint8(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(deUint8(triangleNdx * 3));
    191 				m_staticIndexData.push_back(deUint8(triangleNdx * 3 + 1));
    192 				m_staticIndexData.push_back(deUint8(triangleNdx * 3 + 2));
    193 			}
    194 		}
    195 	}
    196 }
    197 
    198 void 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 
    265 void 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(deInt8(-127 * sign));
    281 				data.push_back(deInt8(-127 * sign));
    282 				data.push_back(0);
    283 				data.push_back(127);
    284 
    285 				data.push_back(deInt8(127 * sign));
    286 				data.push_back(deInt8(-127 * sign));
    287 				data.push_back(0);
    288 				data.push_back(127);
    289 
    290 				data.push_back(deInt8(127 * sign));
    291 				data.push_back(deInt8(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((deInt8)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(deInt8(-127 * sign));
    321 				data.push_back(deInt8(-127 * sign));
    322 				data.push_back(0);
    323 				data.push_back(127);
    324 
    325 				data.push_back(deInt8(127 * sign));
    326 				data.push_back(deInt8(-127 * sign));
    327 				data.push_back(0);
    328 				data.push_back(127);
    329 
    330 				data.push_back(deInt8(127 * sign));
    331 				data.push_back(deInt8(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((deInt8)m_rnd.getUint32());
    342 		}
    343 
    344 		m_dynamicAttributeDatas.push_back(data);
    345 	}
    346 }
    347 
    348 void 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 
    422 void 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 
    481 void 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 
    496 void 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 
    577 deUint64 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 
    683 deUint64 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 
    786 struct Statistics
    787 {
    788 	double mean;
    789 	double standardDeviation;
    790 	double standardErrorOfMean;
    791 };
    792 
    793 Statistics 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 
    824 void 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 
    843 tcu::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 && ((double)m_unbatchedSamplesUs.size() / ((double)m_unbatchedSampleCount) < (double)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 
    931 string 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 
    955 string 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 
    963 DrawCallBatchingTests::DrawCallBatchingTests (Context& context)
    964 	: TestCaseGroup(context, "draw_call_batching", "Draw call batching performance tests.")
    965 {
    966 }
    967 
    968 DrawCallBatchingTests::~DrawCallBatchingTests (void)
    969 {
    970 }
    971 
    972 void 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