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 
DrawCallBatchingTest(Context & context,const char * name,const char * description,const TestSpec & spec)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 
~DrawCallBatchingTest(void)165 DrawCallBatchingTest::~DrawCallBatchingTest (void)
166 {
167 	deinit();
168 }
169 
createIndexData(void)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 
createShader(void)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 
createAttributeDatas(void)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 
createArrayBuffers(void)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 
createIndexBuffer(void)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 
init(void)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 
deinit(void)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 
renderUnbatched(void)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 
renderBatched(void)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 
calculateStats(const vector<deUint64> & samples)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 
logTestInfo(void)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 
iterate(void)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 
specToName(const DrawCallBatchingTest::TestSpec & spec)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 
specToDescrpition(const DrawCallBatchingTest::TestSpec & spec)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 
DrawCallBatchingTests(Context & context)963 DrawCallBatchingTests::DrawCallBatchingTests (Context& context)
964 	: TestCaseGroup(context, "draw_call_batching", "Draw call batching performance tests.")
965 {
966 }
967 
~DrawCallBatchingTests(void)968 DrawCallBatchingTests::~DrawCallBatchingTests (void)
969 {
970 }
971 
init(void)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