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 Buffer test utilities.
22 *//*--------------------------------------------------------------------*/
23
24#include "es2fBufferTestUtil.hpp"
25#include "tcuRandomValueIterator.hpp"
26#include "tcuSurface.hpp"
27#include "tcuImageCompare.hpp"
28#include "tcuVector.hpp"
29#include "tcuFormatUtil.hpp"
30#include "tcuTextureUtil.hpp"
31#include "tcuRenderTarget.hpp"
32#include "gluPixelTransfer.hpp"
33#include "gluRenderContext.hpp"
34#include "gluStrUtil.hpp"
35#include "gluShaderProgram.hpp"
36#include "deMemory.h"
37#include "deStringUtil.hpp"
38
39#include <algorithm>
40
41#include "glwEnums.hpp"
42#include "glwFunctions.hpp"
43
44namespace deqp
45{
46namespace gles2
47{
48namespace Functional
49{
50namespace BufferTestUtil
51{
52
53enum
54{
55	VERIFY_QUAD_SIZE					= 8,		//!< Quad size in VertexArrayVerifier
56	MAX_LINES_PER_INDEX_ARRAY_DRAW		= 128,		//!< Maximum number of lines per one draw in IndexArrayVerifier
57	INDEX_ARRAY_DRAW_VIEWPORT_WIDTH		= 128,
58	INDEX_ARRAY_DRAW_VIEWPORT_HEIGHT	= 128
59};
60
61static const bool LOG_VERIFIER_CALLS = false; //! \note Especially array verifier generates a lot of calls.
62
63using tcu::TestLog;
64using std::vector;
65using std::string;
66using std::set;
67
68// Helper functions.
69
70void fillWithRandomBytes (deUint8* ptr, int numBytes, deUint32 seed)
71{
72	std::copy(tcu::RandomValueIterator<deUint8>::begin(seed, numBytes), tcu::RandomValueIterator<deUint8>::end(), ptr);
73}
74
75bool compareByteArrays (tcu::TestLog& log, const deUint8* resPtr, const deUint8* refPtr, int numBytes)
76{
77	bool			isOk			= true;
78	const int		maxSpanLen		= 8;
79	const int		maxDiffSpans	= 4;
80	int				numDiffSpans	= 0;
81	int				diffSpanStart	= -1;
82	int				ndx				= 0;
83
84	log << TestLog::Section("Verify", "Verification result");
85
86	for (;ndx < numBytes; ndx++)
87	{
88		if (resPtr[ndx] != refPtr[ndx])
89		{
90			if (diffSpanStart < 0)
91				diffSpanStart = ndx;
92
93			isOk = false;
94		}
95		else if (diffSpanStart >= 0)
96		{
97			if (numDiffSpans < maxDiffSpans)
98			{
99				int len			= ndx-diffSpanStart;
100				int	printLen	= de::min(len, maxSpanLen);
101
102				log << TestLog::Message << len << " byte difference at offset " << diffSpanStart << "\n"
103										<< "  expected "	<< tcu::formatArray(tcu::Format::HexIterator<deUint8>(refPtr+diffSpanStart), tcu::Format::HexIterator<deUint8>(refPtr+diffSpanStart+printLen)) << "\n"
104										<< "  got "			<< tcu::formatArray(tcu::Format::HexIterator<deUint8>(resPtr+diffSpanStart), tcu::Format::HexIterator<deUint8>(resPtr+diffSpanStart+printLen))
105					<< TestLog::EndMessage;
106			}
107			else
108				log << TestLog::Message << "(output too long, truncated)" << TestLog::EndMessage;
109
110			numDiffSpans	+= 1;
111			diffSpanStart	 = -1;
112		}
113	}
114
115	if (diffSpanStart >= 0)
116	{
117		if (numDiffSpans < maxDiffSpans)
118		{
119				int len			= ndx-diffSpanStart;
120				int	printLen	= de::min(len, maxSpanLen);
121
122				log << TestLog::Message << len << " byte difference at offset " << diffSpanStart << "\n"
123										<< "  expected "	<< tcu::formatArray(tcu::Format::HexIterator<deUint8>(refPtr+diffSpanStart), tcu::Format::HexIterator<deUint8>(refPtr+diffSpanStart+printLen)) << "\n"
124										<< "  got "			<< tcu::formatArray(tcu::Format::HexIterator<deUint8>(resPtr+diffSpanStart), tcu::Format::HexIterator<deUint8>(resPtr+diffSpanStart+printLen))
125					<< TestLog::EndMessage;
126		}
127		else
128			log << TestLog::Message << "(output too long, truncated)" << TestLog::EndMessage;
129	}
130
131	log << TestLog::Message << (isOk ? "Verification passed." : "Verification FAILED!") << TestLog::EndMessage;
132	log << TestLog::EndSection;
133
134	return isOk;
135}
136
137const char* getBufferTargetName (deUint32 target)
138{
139	switch (target)
140	{
141		case GL_ARRAY_BUFFER:				return "array";
142		case GL_ELEMENT_ARRAY_BUFFER:		return "element_array";
143		default:
144			DE_ASSERT(false);
145			return DE_NULL;
146	}
147}
148
149const char* getUsageHintName (deUint32 hint)
150{
151	switch (hint)
152	{
153		case GL_STREAM_DRAW:	return "stream_draw";
154		case GL_STATIC_DRAW:	return "static_draw";
155		case GL_DYNAMIC_DRAW:	return "dynamic_draw";
156		default:
157			DE_ASSERT(false);
158			return DE_NULL;
159	}
160}
161
162// BufferCase
163
164BufferCase::BufferCase (Context& context, const char* name, const char* description)
165	: TestCase			(context, name, description)
166	, CallLogWrapper	(context.getRenderContext().getFunctions(), m_context.getTestContext().getLog())
167{
168}
169
170BufferCase::~BufferCase (void)
171{
172	enableLogging(false);
173	BufferCase::deinit();
174}
175
176void BufferCase::init (void)
177{
178	enableLogging(true);
179}
180
181void BufferCase::deinit (void)
182{
183	for (set<deUint32>::const_iterator bufIter = m_allocatedBuffers.begin(); bufIter != m_allocatedBuffers.end(); bufIter++)
184		glDeleteBuffers(1, &(*bufIter));
185}
186
187deUint32 BufferCase::genBuffer (void)
188{
189	deUint32 buf = 0;
190	glGenBuffers(1, &buf);
191	if (buf != 0)
192	{
193		try
194		{
195			m_allocatedBuffers.insert(buf);
196		}
197		catch (const std::exception&)
198		{
199			glDeleteBuffers(1, &buf);
200			throw;
201		}
202	}
203	return buf;
204}
205
206void BufferCase::deleteBuffer (deUint32 buffer)
207{
208	glDeleteBuffers(1, &buffer);
209	m_allocatedBuffers.erase(buffer);
210}
211
212void BufferCase::checkError (void)
213{
214	glw::GLenum err = glGetError();
215	if (err != GL_NO_ERROR)
216		throw tcu::TestError(string("Got ") + glu::getErrorStr(err).toString());
217}
218
219// ReferenceBuffer
220
221void ReferenceBuffer::setSize (int numBytes)
222{
223	m_data.resize(numBytes);
224}
225
226void ReferenceBuffer::setData (int numBytes, const deUint8* bytes)
227{
228	m_data.resize(numBytes);
229	std::copy(bytes, bytes+numBytes, m_data.begin());
230}
231
232void ReferenceBuffer::setSubData (int offset, int numBytes, const deUint8* bytes)
233{
234	DE_ASSERT(de::inBounds(offset, 0, (int)m_data.size()) && de::inRange(offset+numBytes, offset, (int)m_data.size()));
235	std::copy(bytes, bytes+numBytes, m_data.begin()+offset);
236}
237
238// BufferVerifierBase
239
240BufferVerifierBase::BufferVerifierBase (Context& context)
241	: CallLogWrapper	(context.getRenderContext().getFunctions(), context.getTestContext().getLog())
242	, m_context			(context)
243{
244	enableLogging(LOG_VERIFIER_CALLS);
245}
246
247// BufferVerifier
248
249BufferVerifier::BufferVerifier (Context& context, VerifyType verifyType)
250	: m_verifier(DE_NULL)
251{
252	switch (verifyType)
253	{
254		case VERIFY_AS_VERTEX_ARRAY:	m_verifier = new VertexArrayVerifier(context);	break;
255		case VERIFY_AS_INDEX_ARRAY:		m_verifier = new IndexArrayVerifier	(context);	break;
256		default:
257			TCU_FAIL("Unsupported verifier");
258	}
259}
260
261BufferVerifier::~BufferVerifier (void)
262{
263	delete m_verifier;
264}
265
266bool BufferVerifier::verify (deUint32 buffer, const deUint8* reference, int offset, int numBytes)
267{
268	DE_ASSERT(numBytes >= getMinSize());
269	DE_ASSERT(offset%getAlignment() == 0);
270	DE_ASSERT((offset+numBytes)%getAlignment() == 0);
271	return m_verifier->verify(buffer, reference, offset, numBytes);
272}
273
274// VertexArrayVerifier
275
276VertexArrayVerifier::VertexArrayVerifier (Context& context)
277	: BufferVerifierBase	(context)
278	, m_program				(DE_NULL)
279	, m_posLoc				(0)
280	, m_byteVecLoc			(0)
281{
282	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(
283		"attribute highp vec2 a_position;\n"
284		"attribute mediump vec3 a_byteVec;\n"
285		"varying mediump vec3 v_byteVec;\n"
286		"void main (void)\n"
287		"{\n"
288		"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
289		"	v_byteVec = a_byteVec;\n"
290		"}\n",
291
292		"varying mediump vec3 v_byteVec;\n"
293		"void main (void)\n"
294		"{\n"
295		"	gl_FragColor = vec4(v_byteVec, 1.0);\n"
296		"}\n"));
297
298	if (!m_program->isOk())
299	{
300		m_context.getTestContext().getLog() << *m_program;
301		delete m_program;
302		TCU_FAIL("Compile failed");
303	}
304
305	const glw::Functions& funcs = context.getRenderContext().getFunctions();
306	m_posLoc		= funcs.getAttribLocation(m_program->getProgram(), "a_position");
307	m_byteVecLoc	= funcs.getAttribLocation(m_program->getProgram(), "a_byteVec");
308}
309
310VertexArrayVerifier::~VertexArrayVerifier (void)
311{
312	delete m_program;
313}
314
315static void computePositions (vector<tcu::Vec2>& positions, int gridSizeX, int gridSizeY)
316{
317	positions.resize(gridSizeX*gridSizeY*4);
318
319	for (int y = 0; y < gridSizeY; y++)
320	for (int x = 0; x < gridSizeX; x++)
321	{
322		float	sx0			= (x+0) / (float)gridSizeX;
323		float	sy0			= (y+0) / (float)gridSizeY;
324		float	sx1			= (x+1) / (float)gridSizeX;
325		float	sy1			= (y+1) / (float)gridSizeY;
326		float	fx0			= 2.0f * sx0 - 1.0f;
327		float	fy0			= 2.0f * sy0 - 1.0f;
328		float	fx1			= 2.0f * sx1 - 1.0f;
329		float	fy1			= 2.0f * sy1 - 1.0f;
330		int		baseNdx		= (y * gridSizeX + x)*4;
331
332		positions[baseNdx+0] = tcu::Vec2(fx0, fy0);
333		positions[baseNdx+1] = tcu::Vec2(fx0, fy1);
334		positions[baseNdx+2] = tcu::Vec2(fx1, fy0);
335		positions[baseNdx+3] = tcu::Vec2(fx1, fy1);
336	}
337}
338
339static void computeIndices (vector<deUint16>& indices, int gridSizeX, int gridSizeY)
340{
341	indices.resize(3 * 2 * gridSizeX * gridSizeY);
342
343	for (int quadNdx = 0; quadNdx < gridSizeX*gridSizeY; quadNdx++)
344	{
345		int v00 = quadNdx*4 + 0;
346		int v01 = quadNdx*4 + 1;
347		int v10 = quadNdx*4 + 2;
348		int v11 = quadNdx*4 + 3;
349
350		DE_ASSERT(v11 < (1<<16));
351
352		indices[quadNdx*6 + 0] = (deUint16)v10;
353		indices[quadNdx*6 + 1] = (deUint16)v00;
354		indices[quadNdx*6 + 2] = (deUint16)v01;
355
356		indices[quadNdx*6 + 3] = (deUint16)v10;
357		indices[quadNdx*6 + 4] = (deUint16)v01;
358		indices[quadNdx*6 + 5] = (deUint16)v11;
359	}
360}
361
362static inline tcu::Vec4 fetchVtxColor (const deUint8* ptr, int vtxNdx)
363{
364	return tcu::RGBA(*(ptr + vtxNdx*3 + 0),
365					 *(ptr + vtxNdx*3 + 1),
366					 *(ptr + vtxNdx*3 + 2),
367					 255).toVec();
368}
369
370static void renderQuadGridReference (tcu::Surface& dst, int numQuads, int rowLength, const deUint8* inPtr)
371{
372	using tcu::Vec4;
373
374	dst.setSize(rowLength*VERIFY_QUAD_SIZE, (numQuads/rowLength + (numQuads%rowLength != 0 ? 1 : 0))*VERIFY_QUAD_SIZE);
375
376	tcu::PixelBufferAccess dstAccess = dst.getAccess();
377	tcu::clear(dstAccess, tcu::IVec4(0, 0, 0, 0xff));
378
379	for (int quadNdx = 0; quadNdx < numQuads; quadNdx++)
380	{
381		int		x0		= (quadNdx%rowLength)*VERIFY_QUAD_SIZE;
382		int		y0		= (quadNdx/rowLength)*VERIFY_QUAD_SIZE;
383		Vec4	v00		= fetchVtxColor(inPtr, quadNdx*4 + 0);
384		Vec4	v10		= fetchVtxColor(inPtr, quadNdx*4 + 1);
385		Vec4	v01		= fetchVtxColor(inPtr, quadNdx*4 + 2);
386		Vec4	v11		= fetchVtxColor(inPtr, quadNdx*4 + 3);
387
388		for (int y = 0; y < VERIFY_QUAD_SIZE; y++)
389		for (int x = 0; x < VERIFY_QUAD_SIZE; x++)
390		{
391			float		fx		= (float)(x+0.5f) / (float)VERIFY_QUAD_SIZE;
392			float		fy		= (float)(y+0.5f) / (float)VERIFY_QUAD_SIZE;
393
394			bool		tri		= fx + fy <= 1.0f;
395			float		tx		= tri ? fx : (1.0f-fx);
396			float		ty		= tri ? fy : (1.0f-fy);
397			const Vec4&	t0		= tri ? v00 : v11;
398			const Vec4&	t1		= tri ? v01 : v10;
399			const Vec4&	t2		= tri ? v10 : v01;
400			Vec4		color	= t0 + (t1-t0)*tx + (t2-t0)*ty;
401
402			dstAccess.setPixel(color, x0+x, y0+y);
403		}
404	}
405}
406
407bool VertexArrayVerifier::verify (deUint32 buffer, const deUint8* refPtr, int offset, int numBytes)
408{
409	const tcu::RenderTarget&	renderTarget		= m_context.getRenderContext().getRenderTarget();
410	const int					numBytesInVtx		= 3;
411	const int					numBytesInQuad		= numBytesInVtx*4;
412	int							maxQuadsX			= de::min(128, renderTarget.getWidth()	/ VERIFY_QUAD_SIZE);
413	int							maxQuadsY			= de::min(128, renderTarget.getHeight()	/ VERIFY_QUAD_SIZE);
414	int							maxQuadsPerBatch	= maxQuadsX*maxQuadsY;
415	int							numVerified			= 0;
416	deUint32					program				= m_program->getProgram();
417	tcu::RGBA					threshold			= renderTarget.getPixelFormat().getColorThreshold() + tcu::RGBA(3,3,3,3);
418	bool						isOk				= true;
419
420	vector<tcu::Vec2>			positions;
421	vector<deUint16>			indices;
422
423	tcu::Surface				rendered;
424	tcu::Surface				reference;
425
426	DE_ASSERT(numBytes >= numBytesInQuad); // Can't render full quad with smaller buffers.
427
428	computePositions(positions, maxQuadsX, maxQuadsY);
429	computeIndices(indices, maxQuadsX, maxQuadsY);
430
431	// Reset buffer bindings.
432	glBindBuffer				(GL_ELEMENT_ARRAY_BUFFER,	0);
433	glBindBuffer				(GL_ARRAY_BUFFER,			0);
434
435	// Setup rendering state.
436	glViewport					(0, 0, maxQuadsX*VERIFY_QUAD_SIZE, maxQuadsY*VERIFY_QUAD_SIZE);
437	glClearColor				(0.0f, 0.0f, 0.0f, 1.0f);
438	glUseProgram				(program);
439	glEnableVertexAttribArray	(m_posLoc);
440	glVertexAttribPointer		(m_posLoc, 2, GL_FLOAT, GL_FALSE, 0, &positions[0]);
441	glEnableVertexAttribArray	(m_byteVecLoc);
442	glBindBuffer				(GL_ARRAY_BUFFER, buffer);
443
444	while (numVerified < numBytes)
445	{
446		int		numRemaining		= numBytes-numVerified;
447		bool	isLeftoverBatch		= numRemaining < numBytesInQuad;
448		int		numBytesToVerify	= isLeftoverBatch ? numBytesInQuad				: de::min(maxQuadsPerBatch*numBytesInQuad, numRemaining - numRemaining%numBytesInQuad);
449		int		curOffset			= isLeftoverBatch ? (numBytes-numBytesInQuad)	: numVerified;
450		int		numQuads			= numBytesToVerify/numBytesInQuad;
451		int		numCols				= de::min(maxQuadsX, numQuads);
452		int		numRows				= numQuads/maxQuadsX + (numQuads%maxQuadsX != 0 ? 1 : 0);
453		string	imageSetDesc		= string("Bytes ") + de::toString(offset+curOffset) + " to " + de::toString(offset+curOffset+numBytesToVerify-1);
454
455		DE_ASSERT(numBytesToVerify > 0 && numBytesToVerify%numBytesInQuad == 0);
456		DE_ASSERT(de::inBounds(curOffset, 0, numBytes));
457		DE_ASSERT(de::inRange(curOffset+numBytesToVerify, curOffset, numBytes));
458
459		// Render batch.
460		glClear					(GL_COLOR_BUFFER_BIT);
461		glVertexAttribPointer	(m_byteVecLoc, 3, GL_UNSIGNED_BYTE, GL_TRUE, 0, (const glw::GLvoid*)(deUintptr)(offset + curOffset));
462		glDrawElements			(GL_TRIANGLES, numQuads*6, GL_UNSIGNED_SHORT, &indices[0]);
463
464		renderQuadGridReference(reference,  numQuads, numCols, refPtr + offset + curOffset);
465
466		rendered.setSize(numCols*VERIFY_QUAD_SIZE, numRows*VERIFY_QUAD_SIZE);
467		glu::readPixels(m_context.getRenderContext(), 0, 0, rendered.getAccess());
468
469		if (!tcu::pixelThresholdCompare(m_context.getTestContext().getLog(), "RenderResult", imageSetDesc.c_str(), reference, rendered, threshold, tcu::COMPARE_LOG_RESULT))
470		{
471			isOk = false;
472			break;
473		}
474
475		numVerified += isLeftoverBatch ? numRemaining : numBytesToVerify;
476	}
477
478	glDisableVertexAttribArray	(m_posLoc);
479	glDisableVertexAttribArray	(m_byteVecLoc);
480
481	return isOk;
482}
483
484// IndexArrayVerifier
485
486IndexArrayVerifier::IndexArrayVerifier (Context& context)
487	: BufferVerifierBase	(context)
488	, m_program				(DE_NULL)
489	, m_posLoc				(0)
490	, m_colorLoc			(0)
491{
492	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(
493		"attribute highp vec2 a_position;\n"
494		"attribute mediump vec3 a_color;\n"
495		"varying mediump vec3 v_color;\n"
496		"void main (void)\n"
497		"{\n"
498		"	gl_Position = vec4(a_position, 0.0, 1.0);\n"
499		"	v_color = a_color;\n"
500		"}\n",
501
502		"varying mediump vec3 v_color;\n"
503		"void main (void)\n"
504		"{\n"
505		"	gl_FragColor = vec4(v_color, 1.0);\n"
506		"}\n"));
507
508	if (!m_program->isOk())
509	{
510		m_context.getTestContext().getLog() << *m_program;
511		delete m_program;
512		TCU_FAIL("Compile failed");
513	}
514
515	const glw::Functions& funcs = context.getRenderContext().getFunctions();
516	m_posLoc	= funcs.getAttribLocation(m_program->getProgram(), "a_position");
517	m_colorLoc	= funcs.getAttribLocation(m_program->getProgram(), "a_color");
518}
519
520IndexArrayVerifier::~IndexArrayVerifier (void)
521{
522	delete m_program;
523}
524
525static void computeIndexVerifierPositions (std::vector<tcu::Vec2>& dst)
526{
527	const int	numPosX		= 16;
528	const int	numPosY		= 16;
529
530	dst.resize(numPosX*numPosY);
531
532	for (int y = 0; y < numPosY; y++)
533	{
534		for (int x = 0; x < numPosX; x++)
535		{
536			float	xf	= float(x) / float(numPosX-1);
537			float	yf	= float(y) / float(numPosY-1);
538
539			dst[y*numPosX + x] = tcu::Vec2(2.0f*xf - 1.0f, 2.0f*yf - 1.0f);
540		}
541	}
542}
543
544static void computeIndexVerifierColors (std::vector<tcu::Vec3>& dst)
545{
546	const int	numColors	= 256;
547	const float	minVal		= 0.1f;
548	const float maxVal		= 0.5f;
549	de::Random	rnd			(0xabc231);
550
551	dst.resize(numColors);
552
553	for (std::vector<tcu::Vec3>::iterator i = dst.begin(); i != dst.end(); ++i)
554	{
555		i->x()	= rnd.getFloat(minVal, maxVal);
556		i->y()	= rnd.getFloat(minVal, maxVal);
557		i->z()	= rnd.getFloat(minVal, maxVal);
558	}
559}
560
561template<typename T>
562static void execVertexFetch (T* dst, const T* src, const deUint8* indices, int numIndices)
563{
564	for (int i = 0; i < numIndices; ++i)
565		dst[i] = src[indices[i]];
566}
567
568bool IndexArrayVerifier::verify (deUint32 buffer, const deUint8* refPtr, int offset, int numBytes)
569{
570	const tcu::RenderTarget&	renderTarget		= m_context.getRenderContext().getRenderTarget();
571	const int					viewportW			= de::min<int>(INDEX_ARRAY_DRAW_VIEWPORT_WIDTH, renderTarget.getWidth());
572	const int					viewportH			= de::min<int>(INDEX_ARRAY_DRAW_VIEWPORT_HEIGHT, renderTarget.getHeight());
573	const int					minBytesPerBatch	= 2;
574	const tcu::RGBA				threshold			(0,0,0,0);
575
576	std::vector<tcu::Vec2>		positions;
577	std::vector<tcu::Vec3>		colors;
578
579	std::vector<tcu::Vec2>		fetchedPos			(MAX_LINES_PER_INDEX_ARRAY_DRAW+1);
580	std::vector<tcu::Vec3>		fetchedColor		(MAX_LINES_PER_INDEX_ARRAY_DRAW+1);
581
582	tcu::Surface				indexBufferImg		(viewportW, viewportH);
583	tcu::Surface				referenceImg		(viewportW, viewportH);
584
585	int							numVerified			= 0;
586	bool						isOk				= true;
587
588	DE_STATIC_ASSERT(sizeof(tcu::Vec2) == sizeof(float)*2);
589	DE_STATIC_ASSERT(sizeof(tcu::Vec3) == sizeof(float)*3);
590
591	computeIndexVerifierPositions(positions);
592	computeIndexVerifierColors(colors);
593
594	// Reset buffer bindings.
595	glBindBuffer				(GL_ARRAY_BUFFER,			0);
596	glBindBuffer				(GL_ELEMENT_ARRAY_BUFFER,	buffer);
597
598	// Setup rendering state.
599	glViewport					(0, 0, viewportW, viewportH);
600	glClearColor				(0.0f, 0.0f, 0.0f, 1.0f);
601	glUseProgram				(m_program->getProgram());
602	glEnableVertexAttribArray	(m_posLoc);
603	glEnableVertexAttribArray	(m_colorLoc);
604	glEnable					(GL_BLEND);
605	glBlendFunc					(GL_ONE, GL_ONE);
606	glBlendEquation				(GL_FUNC_ADD);
607
608	while (numVerified < numBytes)
609	{
610		int		numRemaining		= numBytes-numVerified;
611		bool	isLeftoverBatch		= numRemaining < minBytesPerBatch;
612		int		numBytesToVerify	= isLeftoverBatch ? minBytesPerBatch			: de::min(MAX_LINES_PER_INDEX_ARRAY_DRAW+1, numRemaining);
613		int		curOffset			= isLeftoverBatch ? (numBytes-minBytesPerBatch)	: numVerified;
614		string	imageSetDesc		= string("Bytes ") + de::toString(offset+curOffset) + " to " + de::toString(offset+curOffset+numBytesToVerify-1);
615
616		// Step 1: Render using index buffer.
617		glClear					(GL_COLOR_BUFFER_BIT);
618		glVertexAttribPointer	(m_posLoc, 2, GL_FLOAT, GL_FALSE, 0, &positions[0]);
619		glVertexAttribPointer	(m_colorLoc, 3, GL_FLOAT, GL_FALSE, 0, &colors[0]);
620		glDrawElements			(GL_LINE_STRIP, numBytesToVerify, GL_UNSIGNED_BYTE, (void*)(deUintptr)(offset+curOffset));
621		glu::readPixels			(m_context.getRenderContext(), 0, 0, indexBufferImg.getAccess());
622
623		// Step 2: Do manual fetch and render without index buffer.
624		execVertexFetch(&fetchedPos[0], &positions[0], refPtr+offset+curOffset, numBytesToVerify);
625		execVertexFetch(&fetchedColor[0], &colors[0], refPtr+offset+curOffset, numBytesToVerify);
626
627		glClear					(GL_COLOR_BUFFER_BIT);
628		glVertexAttribPointer	(m_posLoc, 2, GL_FLOAT, GL_FALSE, 0, &fetchedPos[0]);
629		glVertexAttribPointer	(m_colorLoc, 3, GL_FLOAT, GL_FALSE, 0, &fetchedColor[0]);
630		glDrawArrays			(GL_LINE_STRIP, 0, numBytesToVerify);
631		glu::readPixels			(m_context.getRenderContext(), 0, 0, referenceImg.getAccess());
632
633		if (!tcu::pixelThresholdCompare(m_context.getTestContext().getLog(), "RenderResult", imageSetDesc.c_str(), referenceImg, indexBufferImg, threshold, tcu::COMPARE_LOG_RESULT))
634		{
635			isOk = false;
636			break;
637		}
638
639		numVerified += isLeftoverBatch ? numRemaining : numBytesToVerify;
640	}
641
642	return isOk;
643}
644
645} // BufferTestUtil
646} // Functional
647} // gles2
648} // deqp
649