1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL (ES) 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 Shader - render state interaction case.
22 *//*--------------------------------------------------------------------*/
23
24#include "glsFragOpInteractionCase.hpp"
25
26#include "glsRandomShaderProgram.hpp"
27#include "glsFragmentOpUtil.hpp"
28#include "glsInteractionTestUtil.hpp"
29
30#include "gluRenderContext.hpp"
31#include "gluContextInfo.hpp"
32
33#include "rsgShader.hpp"
34#include "rsgProgramGenerator.hpp"
35#include "rsgUtils.hpp"
36
37#include "sglrContext.hpp"
38#include "sglrReferenceContext.hpp"
39#include "sglrGLContext.hpp"
40#include "sglrContextUtil.hpp"
41
42#include "tcuRenderTarget.hpp"
43#include "tcuImageCompare.hpp"
44
45#include "deRandom.hpp"
46#include "deString.h"
47#include "deStringUtil.hpp"
48
49#include "glwEnums.hpp"
50
51#include "gluDrawUtil.hpp"
52
53namespace deqp
54{
55namespace gls
56{
57
58using std::vector;
59using std::string;
60using tcu::Vec2;
61using tcu::Vec4;
62using tcu::IVec2;
63using tcu::IVec4;
64
65using gls::InteractionTestUtil::RenderState;
66using gls::InteractionTestUtil::StencilState;
67
68enum
69{
70	NUM_ITERATIONS					= 5,
71	NUM_COMMANDS_PER_ITERATION		= 5,
72	VIEWPORT_WIDTH					= 64,
73	VIEWPORT_HEIGHT					= 64
74};
75
76namespace
77{
78
79static void computeVertexLayout (const vector<rsg::ShaderInput*>& attributes, int numVertices, vector<glu::VertexArrayBinding>* layout, int* stride)
80{
81	DE_ASSERT(layout->empty());
82
83	int curOffset = 0;
84
85	for (vector<rsg::ShaderInput*>::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
86	{
87		const rsg::ShaderInput*		attrib		= *iter;
88		const rsg::Variable*		var			= attrib->getVariable();
89		const rsg::VariableType&	type		= var->getType();
90		const int					numComps	= type.getNumElements();
91
92		TCU_CHECK_INTERNAL(type.getBaseType() == rsg::VariableType::TYPE_FLOAT && de::inRange(type.getNumElements(), 1, 4));
93
94		layout->push_back(glu::va::Float(var->getName(), numComps, numVertices, 0 /* computed later */, (const float*)(deUintptr)curOffset));
95
96		curOffset += numComps * (int)sizeof(float);
97	}
98
99	for (vector<glu::VertexArrayBinding>::iterator vaIter = layout->begin(); vaIter != layout->end(); ++vaIter)
100		vaIter->pointer.stride = curOffset;
101
102	*stride = curOffset;
103}
104
105class VertexDataStorage
106{
107public:
108												VertexDataStorage	(const vector<rsg::ShaderInput*>& attributes, int numVertices);
109
110	int											getDataSize			(void) const	{ return (int)m_data.size();					}
111	void*										getBasePtr			(void)			{ return m_data.empty() ? DE_NULL : &m_data[0]; }
112	const void*									getBasePtr			(void) const	{ return m_data.empty() ? DE_NULL : &m_data[0]; }
113
114	const std::vector<glu::VertexArrayBinding>&	getLayout			(void) const	{ return m_layout; }
115
116	int											getNumEntries		(void) const	{ return (int)m_layout.size();	}
117	const glu::VertexArrayBinding&				getLayoutEntry		(int ndx) const	{ return m_layout[ndx];			}
118
119private:
120	std::vector<deUint8>						m_data;
121	std::vector<glu::VertexArrayBinding>		m_layout;
122};
123
124VertexDataStorage::VertexDataStorage (const vector<rsg::ShaderInput*>& attributes, int numVertices)
125{
126	int stride = 0;
127	computeVertexLayout(attributes, numVertices, &m_layout, &stride);
128	m_data.resize(stride * numVertices);
129}
130
131static inline glu::VertexArrayBinding getEntryWithPointer (const VertexDataStorage& data, int ndx)
132{
133	const glu::VertexArrayBinding& entry = data.getLayoutEntry(ndx);
134	return glu::VertexArrayBinding(entry.binding, glu::VertexArrayPointer(entry.pointer.componentType,
135																		  entry.pointer.convert,
136																		  entry.pointer.numComponents,
137																		  entry.pointer.numElements,
138																		  entry.pointer.stride,
139																		  (const void*)((deUintptr)entry.pointer.data+(deUintptr)data.getBasePtr())));
140}
141
142template<int Size>
143static void setVertex (const glu::VertexArrayPointer& pointer, int vertexNdx, const tcu::Vector<float, Size>& value)
144{
145	// \todo [2013-12-14 pyry] Implement other modes.
146	DE_ASSERT(pointer.componentType == glu::VTX_COMP_FLOAT && pointer.convert == glu::VTX_COMP_CONVERT_NONE);
147	DE_ASSERT(pointer.numComponents == Size);
148	DE_ASSERT(de::inBounds(vertexNdx, 0, pointer.numElements));
149
150	float* dst = (float*)((deUint8*)pointer.data + pointer.stride*vertexNdx);
151
152	for (int ndx = 0; ndx < Size; ndx++)
153		dst[ndx] = value[ndx];
154}
155
156template<int Size>
157static tcu::Vector<float, Size> interpolateRange (const rsg::ConstValueRangeAccess& range, const tcu::Vector<float, Size>& t)
158{
159	tcu::Vector<float, Size> result;
160
161	for (int ndx = 0; ndx < Size; ndx++)
162		result[ndx] = range.getMin().component(ndx).asFloat()*(1.0f - t[ndx]) + range.getMax().component(ndx).asFloat()*t[ndx];
163
164	return result;
165}
166
167struct Quad
168{
169	tcu::IVec2	posA;
170	tcu::IVec2	posB;
171};
172
173struct RenderCommand
174{
175	Quad		quad;
176	float		depth;
177	RenderState	state;
178
179	RenderCommand (void) : depth(0.0f) {}
180};
181
182static Quad getRandomQuad (de::Random& rnd, int targetW, int targetH)
183{
184	// \note In viewport coordinates.
185	// \todo [2012-12-18 pyry] Out-of-bounds values.
186	const int		maxOutOfBounds	= 0;
187	const float		minSize			= 0.5f;
188
189	const int		minW			= deCeilFloatToInt32(minSize*targetW);
190	const int		minH			= deCeilFloatToInt32(minSize*targetH);
191	const int		maxW			= targetW + 2*maxOutOfBounds;
192	const int		maxH			= targetH + 2*maxOutOfBounds;
193
194	const int		width			= rnd.getInt(minW, maxW);
195	const int		height			= rnd.getInt(minH, maxH);
196	const int		x				= rnd.getInt(-maxOutOfBounds, targetW+maxOutOfBounds-width);
197	const int		y				= rnd.getInt(-maxOutOfBounds, targetH+maxOutOfBounds-height);
198
199	const bool		flipX			= rnd.getBool();
200	const bool		flipY			= rnd.getBool();
201
202	Quad			quad;
203
204	quad.posA	= tcu::IVec2(flipX ? (x+width-1) : x, flipY ? (y+height-1) : y);
205	quad.posB	= tcu::IVec2(flipX ? x : (x+width-1), flipY ? y : (y+height-1));
206
207	return quad;
208}
209
210static float getRandomDepth (de::Random& rnd)
211{
212	// \note Not using depth 1.0 since clearing with 1.0 and rendering with 1.0 may not be same value.
213	static const float depthValues[] = { 0.0f, 0.2f, 0.4f, 0.5f, 0.51f, 0.6f, 0.8f, 0.95f };
214	return rnd.choose<float>(DE_ARRAY_BEGIN(depthValues), DE_ARRAY_END(depthValues));
215}
216
217static void computeRandomRenderCommand (de::Random& rnd, RenderCommand& command, glu::ApiType apiType, int targetW, int targetH)
218{
219	command.quad	= getRandomQuad(rnd, targetW, targetH);
220	command.depth	= getRandomDepth(rnd);
221	gls::InteractionTestUtil::computeRandomRenderState(rnd, command.state, apiType, targetW, targetH);
222}
223
224static void setRenderState (sglr::Context& ctx, const RenderState& state)
225{
226	if (state.scissorTestEnabled)
227	{
228		ctx.enable(GL_SCISSOR_TEST);
229		ctx.scissor(state.scissorRectangle.left, state.scissorRectangle.bottom,
230					state.scissorRectangle.width, state.scissorRectangle.height);
231	}
232	else
233		ctx.disable(GL_SCISSOR_TEST);
234
235	if (state.stencilTestEnabled)
236	{
237		ctx.enable(GL_STENCIL_TEST);
238
239		for (int face = 0; face < rr::FACETYPE_LAST; face++)
240		{
241			deUint32				glFace	= face == rr::FACETYPE_BACK ? GL_BACK : GL_FRONT;
242			const StencilState&		sParams	= state.stencil[face];
243
244			ctx.stencilFuncSeparate(glFace, sParams.function, sParams.reference, sParams.compareMask);
245			ctx.stencilOpSeparate(glFace, sParams.stencilFailOp, sParams.depthFailOp, sParams.depthPassOp);
246			ctx.stencilMaskSeparate(glFace, sParams.writeMask);
247		}
248	}
249	else
250		ctx.disable(GL_STENCIL_TEST);
251
252	if (state.depthTestEnabled)
253	{
254		ctx.enable(GL_DEPTH_TEST);
255		ctx.depthFunc(state.depthFunc);
256		ctx.depthMask(state.depthWriteMask ? GL_TRUE : GL_FALSE);
257	}
258	else
259		ctx.disable(GL_DEPTH_TEST);
260
261	if (state.blendEnabled)
262	{
263		ctx.enable(GL_BLEND);
264		ctx.blendEquationSeparate(state.blendRGBState.equation, state.blendAState.equation);
265		ctx.blendFuncSeparate(state.blendRGBState.srcFunc, state.blendRGBState.dstFunc, state.blendAState.srcFunc, state.blendAState.dstFunc);
266		ctx.blendColor(state.blendColor.x(), state.blendColor.y(), state.blendColor.z(), state.blendColor.w());
267	}
268	else
269		ctx.disable(GL_BLEND);
270
271	if (state.ditherEnabled)
272		ctx.enable(GL_DITHER);
273	else
274		ctx.disable(GL_DITHER);
275
276	ctx.colorMask(state.colorMask[0] ? GL_TRUE : GL_FALSE,
277				  state.colorMask[1] ? GL_TRUE : GL_FALSE,
278				  state.colorMask[2] ? GL_TRUE : GL_FALSE,
279				  state.colorMask[3] ? GL_TRUE : GL_FALSE);
280}
281
282static void renderQuad (sglr::Context& ctx, const glu::VertexArrayPointer& posPtr, const Quad& quad, const float depth)
283{
284	const deUint16	indices[]	= { 0, 1, 2, 2, 1, 3 };
285
286	const bool		flipX		= quad.posB.x() < quad.posA.x();
287	const bool		flipY		= quad.posB.y() < quad.posA.y();
288	const int		viewportX	= de::min(quad.posA.x(), quad.posB.x());
289	const int		viewportY	= de::min(quad.posA.y(), quad.posB.y());
290	const int		viewportW	= de::abs(quad.posA.x()-quad.posB.x())+1;
291	const int		viewportH	= de::abs(quad.posA.y()-quad.posB.y())+1;
292
293	const Vec2		pA			(flipX ? 1.0f : -1.0f, flipY ? 1.0f : -1.0f);
294	const Vec2		pB			(flipX ? -1.0f : 1.0f, flipY ? -1.0f : 1.0f);
295
296	setVertex(posPtr, 0, Vec4(pA.x(), pA.y(), depth, 1.0f));
297	setVertex(posPtr, 1, Vec4(pB.x(), pA.y(), depth, 1.0f));
298	setVertex(posPtr, 2, Vec4(pA.x(), pB.y(), depth, 1.0f));
299	setVertex(posPtr, 3, Vec4(pB.x(), pB.y(), depth, 1.0f));
300
301	ctx.viewport(viewportX, viewportY, viewportW, viewportH);
302	ctx.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(indices), GL_UNSIGNED_SHORT, &indices[0]);
303}
304
305static void render (sglr::Context& ctx, const glu::VertexArrayPointer& posPtr, const RenderCommand& cmd)
306{
307	setRenderState(ctx, cmd.state);
308	renderQuad(ctx, posPtr, cmd.quad, cmd.depth);
309}
310
311static void setupAttributes (sglr::Context& ctx, const VertexDataStorage& vertexData, deUint32 program)
312{
313	for (int attribNdx = 0; attribNdx < vertexData.getNumEntries(); ++attribNdx)
314	{
315		const glu::VertexArrayBinding	bindingPtr	= getEntryWithPointer(vertexData, attribNdx);
316		const int						attribLoc	= bindingPtr.binding.type == glu::BindingPoint::TYPE_NAME ? ctx.getAttribLocation(program, bindingPtr.binding.name.c_str()) : bindingPtr.binding.location;
317
318		DE_ASSERT(bindingPtr.pointer.componentType == glu::VTX_COMP_FLOAT);
319
320		if (attribLoc >= 0)
321		{
322			ctx.enableVertexAttribArray(attribLoc);
323			ctx.vertexAttribPointer(attribLoc, bindingPtr.pointer.numComponents, GL_FLOAT, GL_FALSE, bindingPtr.pointer.stride, bindingPtr.pointer.data);
324		}
325	}
326}
327
328void setUniformValue (sglr::Context& ctx, int location, rsg::ConstValueAccess value)
329{
330	DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(float));
331	DE_STATIC_ASSERT(sizeof(rsg::Scalar) == sizeof(int));
332
333	switch (value.getType().getBaseType())
334	{
335		case rsg::VariableType::TYPE_FLOAT:
336			switch (value.getType().getNumElements())
337			{
338				case 1:		ctx.uniform1fv(location, 1, (float*)value.value().getValuePtr());	break;
339				case 2:		ctx.uniform2fv(location, 1, (float*)value.value().getValuePtr());	break;
340				case 3:		ctx.uniform3fv(location, 1, (float*)value.value().getValuePtr());	break;
341				case 4:		ctx.uniform4fv(location, 1, (float*)value.value().getValuePtr());	break;
342				default:	TCU_FAIL("Unsupported type");										break;
343			}
344			break;
345
346		case rsg::VariableType::TYPE_INT:
347		case rsg::VariableType::TYPE_BOOL:
348		case rsg::VariableType::TYPE_SAMPLER_2D:
349		case rsg::VariableType::TYPE_SAMPLER_CUBE:
350			switch (value.getType().getNumElements())
351			{
352				case 1:		ctx.uniform1iv(location, 1, (int*)value.value().getValuePtr());		break;
353				case 2:		ctx.uniform2iv(location, 1, (int*)value.value().getValuePtr());		break;
354				case 3:		ctx.uniform3iv(location, 1, (int*)value.value().getValuePtr());		break;
355				case 4:		ctx.uniform4iv(location, 1, (int*)value.value().getValuePtr());		break;
356				default:	TCU_FAIL("Unsupported type");										break;
357			}
358			break;
359
360		default:
361			throw tcu::InternalError("Unsupported type", "", __FILE__, __LINE__);
362	}
363}
364
365static int findShaderInputIndex (const vector<rsg::ShaderInput*>& vars, const char* name)
366{
367	for (int ndx = 0; ndx < (int)vars.size(); ++ndx)
368	{
369		if (deStringEqual(vars[ndx]->getVariable()->getName(), name))
370			return ndx;
371	}
372
373	throw tcu::InternalError(string(name) + " not found in shader inputs");
374}
375
376static float getWellBehavingChannelColor (float v, int numBits)
377{
378	DE_ASSERT(de::inRange(numBits, 0, 32));
379
380	// clear color may not be accurately representable in the target format. If the clear color is
381	// on a representable value mapping range border, it could be rounded differently by the GL and in
382	// SGLR adding an unexpected error source. However, selecting an accurately representable background
383	// color would effectively disable dithering. To allow dithering and to prevent undefined rounding
384	// direction from affecting results, round accurate color to target color format with 8 sub-units
385	// (3 bits). If the selected sub-unit value is 3 or 4 (bordering 0.5), replace it with 2 and 5,
386	// respectively.
387
388	if (numBits == 0 || v <= 0.0f || v >= 1.0f)
389	{
390		// already accurately representable
391		return v;
392	}
393	else
394	{
395		const deUint64 numSubBits		= 3;
396		const deUint64 subUnitBorderLo	= (1u << (numSubBits - 1u)) - 1u;
397		const deUint64 subUnitBorderHi	= 1u << (numSubBits - 1u);
398		const deUint64 maxFixedValue	= (1u << (numBits + numSubBits)) - 1u;
399		const deUint64 fixedValue		= deRoundFloatToInt64(v * maxFixedValue);
400
401		const deUint64 units			= fixedValue >> numSubBits;
402		const deUint64 subUnits			= fixedValue & ((1u << numSubBits) - 1u);
403
404		const deUint64 tweakedSubUnits	= (subUnits == subUnitBorderLo) ? (subUnitBorderLo - 1)
405										: (subUnits == subUnitBorderHi) ? (subUnitBorderHi + 1)
406										: (subUnits);
407		const deUint64 tweakedValue		= (units << numSubBits) | (tweakedSubUnits);
408
409		return float(tweakedValue) / float(maxFixedValue);
410	}
411}
412
413static tcu::Vec4 getWellBehavingColor (const tcu::Vec4& accurateColor, const tcu::PixelFormat& format)
414{
415	return tcu::Vec4(getWellBehavingChannelColor(accurateColor[0], format.redBits),
416					 getWellBehavingChannelColor(accurateColor[1], format.greenBits),
417					 getWellBehavingChannelColor(accurateColor[2], format.blueBits),
418					 getWellBehavingChannelColor(accurateColor[3], format.alphaBits));
419}
420
421} // anonymous
422
423struct FragOpInteractionCase::ReferenceContext
424{
425	const sglr::ReferenceContextLimits	limits;
426	sglr::ReferenceContextBuffers		buffers;
427	sglr::ReferenceContext				context;
428
429	ReferenceContext (glu::RenderContext& renderCtx, int width, int height)
430		: limits	(renderCtx)
431		, buffers	(renderCtx.getRenderTarget().getPixelFormat(), renderCtx.getRenderTarget().getDepthBits(), renderCtx.getRenderTarget().getStencilBits(), width, height)
432		, context	(limits, buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer())
433	{
434	}
435};
436
437FragOpInteractionCase::FragOpInteractionCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const glu::ContextInfo& ctxInfo, const char* name, const rsg::ProgramParameters& params)
438	: TestCase			(testCtx, name, "")
439	, m_renderCtx		(renderCtx)
440	, m_ctxInfo			(ctxInfo)
441	, m_params			(params)
442	, m_vertexShader	(rsg::Shader::TYPE_VERTEX)
443	, m_fragmentShader	(rsg::Shader::TYPE_FRAGMENT)
444	, m_program			(DE_NULL)
445	, m_glCtx			(DE_NULL)
446	, m_referenceCtx	(DE_NULL)
447	, m_glProgram		(0)
448	, m_refProgram		(0)
449	, m_iterNdx			(0)
450{
451}
452
453FragOpInteractionCase::~FragOpInteractionCase (void)
454{
455	FragOpInteractionCase::deinit();
456}
457
458void FragOpInteractionCase::init (void)
459{
460	de::Random				rnd				(m_params.seed ^ 0x232faac);
461	const int				viewportW		= de::min<int>(m_renderCtx.getRenderTarget().getWidth(),	VIEWPORT_WIDTH);
462	const int				viewportH		= de::min<int>(m_renderCtx.getRenderTarget().getHeight(),	VIEWPORT_HEIGHT);
463	const int				viewportX		= rnd.getInt(0, m_renderCtx.getRenderTarget().getWidth()	- viewportW);
464	const int				viewportY		= rnd.getInt(0, m_renderCtx.getRenderTarget().getHeight()	- viewportH);
465
466	rsg::ProgramGenerator	generator;
467
468	generator.generate(m_params, m_vertexShader, m_fragmentShader);
469	rsg::computeUnifiedUniforms(m_vertexShader, m_fragmentShader, m_unifiedUniforms);
470
471	try
472	{
473		DE_ASSERT(!m_program);
474		m_program = new gls::RandomShaderProgram(m_vertexShader, m_fragmentShader, (int)m_unifiedUniforms.size(), m_unifiedUniforms.empty() ? DE_NULL : &m_unifiedUniforms[0]);
475
476		DE_ASSERT(!m_referenceCtx);
477		m_referenceCtx = new ReferenceContext(m_renderCtx, viewportW, viewportH);
478
479		DE_ASSERT(!m_glCtx);
480		m_glCtx = new sglr::GLContext(m_renderCtx, m_testCtx.getLog(), sglr::GLCONTEXT_LOG_CALLS|sglr::GLCONTEXT_LOG_PROGRAMS, IVec4(viewportX, viewportY, viewportW, viewportH));
481
482		m_refProgram	= m_referenceCtx->context.createProgram(m_program);
483		m_glProgram		= m_glCtx->createProgram(m_program);
484
485		m_viewportSize	= tcu::IVec2(viewportW, viewportH);
486		m_iterNdx		= 0;
487		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
488	}
489	catch (...)
490	{
491		// Save some memory by cleaning up stuff.
492		FragOpInteractionCase::deinit();
493		throw;
494	}
495}
496
497void FragOpInteractionCase::deinit (void)
498{
499	delete m_referenceCtx;
500	m_referenceCtx = DE_NULL;
501
502	delete m_glCtx;
503	m_glCtx = DE_NULL;
504
505	delete m_program;
506	m_program = DE_NULL;
507}
508
509FragOpInteractionCase::IterateResult FragOpInteractionCase::iterate (void)
510{
511	de::Random							rnd					(m_params.seed ^ deInt32Hash(m_iterNdx));
512	const tcu::ScopedLogSection			section				(m_testCtx.getLog(), string("Iter") + de::toString(m_iterNdx), string("Iteration ") + de::toString(m_iterNdx));
513
514	const int							positionNdx			= findShaderInputIndex(m_vertexShader.getInputs(), "dEQP_Position");
515
516	const int							numVertices			= 4;
517	VertexDataStorage					vertexData			(m_vertexShader.getInputs(), numVertices);
518	std::vector<rsg::VariableValue>		uniformValues;
519	std::vector<RenderCommand>			renderCmds			(NUM_COMMANDS_PER_ITERATION);
520
521	tcu::Surface						rendered			(m_viewportSize.x(), m_viewportSize.y());
522	tcu::Surface						reference			(m_viewportSize.x(), m_viewportSize.y());
523
524	const tcu::Vec4						vtxInterpFactors[]	=
525	{
526		tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f),
527		tcu::Vec4(1.0f, 0.0f, 0.5f, 0.5f),
528		tcu::Vec4(0.0f, 1.0f, 0.5f, 0.5f),
529		tcu::Vec4(1.0f, 1.0f, 1.0f, 0.0f)
530	};
531
532	rsg::computeUniformValues(rnd, uniformValues, m_unifiedUniforms);
533
534	for (int attribNdx = 0; attribNdx < (int)m_vertexShader.getInputs().size(); ++attribNdx)
535	{
536		if (attribNdx == positionNdx)
537			continue;
538
539		const rsg::ShaderInput*				shaderIn		= m_vertexShader.getInputs()[attribNdx];
540		const rsg::VariableType&			varType			= shaderIn->getVariable()->getType();
541		const rsg::ConstValueRangeAccess	valueRange		= shaderIn->getValueRange();
542		const int							numComponents	= varType.getNumElements();
543		const glu::VertexArrayBinding		layoutEntry		= getEntryWithPointer(vertexData, attribNdx);
544
545		DE_ASSERT(varType.getBaseType() == rsg::VariableType::TYPE_FLOAT);
546
547		for (int vtxNdx = 0; vtxNdx < 4; vtxNdx++)
548		{
549			const int			fNdx	= (attribNdx+vtxNdx+m_iterNdx)%DE_LENGTH_OF_ARRAY(vtxInterpFactors);
550			const tcu::Vec4&	f		= vtxInterpFactors[fNdx];
551
552			switch (numComponents)
553			{
554				case 1:	setVertex(layoutEntry.pointer, vtxNdx, interpolateRange(valueRange, f.toWidth<1>()));	break;
555				case 2:	setVertex(layoutEntry.pointer, vtxNdx, interpolateRange(valueRange, f.toWidth<2>()));	break;
556				case 3:	setVertex(layoutEntry.pointer, vtxNdx, interpolateRange(valueRange, f.toWidth<3>()));	break;
557				case 4:	setVertex(layoutEntry.pointer, vtxNdx, interpolateRange(valueRange, f.toWidth<4>()));	break;
558				default:
559					DE_ASSERT(false);
560			}
561		}
562	}
563
564	for (vector<RenderCommand>::iterator cmdIter = renderCmds.begin(); cmdIter != renderCmds.end(); ++cmdIter)
565		computeRandomRenderCommand(rnd, *cmdIter, m_renderCtx.getType().getAPI(), m_viewportSize.x(), m_viewportSize.y());
566
567	// Workaround for inaccurate barycentric/depth computation in current reference renderer:
568	// Small bias is added to the draw call depths, in increasing order, to avoid accuracy issues in depth comparison.
569	for (int cmdNdx = 0; cmdNdx < (int)renderCmds.size(); cmdNdx++)
570		renderCmds[cmdNdx].depth += 0.0231725f * float(cmdNdx);
571
572	{
573		const glu::VertexArrayPointer		posPtr				= getEntryWithPointer(vertexData, positionNdx).pointer;
574
575		sglr::Context* const				contexts[]			= { m_glCtx, &m_referenceCtx->context };
576		const deUint32						programs[]			= { m_glProgram, m_refProgram };
577		tcu::PixelBufferAccess				readDst[]			= { rendered.getAccess(), reference.getAccess() };
578
579		const tcu::Vec4						accurateClearColor	= tcu::Vec4(0.0f, 0.25f, 0.5f, 1.0f);
580		const tcu::Vec4						clearColor			= getWellBehavingColor(accurateClearColor, m_renderCtx.getRenderTarget().getPixelFormat());
581
582		for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(contexts); ndx++)
583		{
584			sglr::Context&	ctx			= *contexts[ndx];
585			const deUint32	program		= programs[ndx];
586
587			setupAttributes(ctx, vertexData, program);
588
589			ctx.disable		(GL_SCISSOR_TEST);
590			ctx.colorMask	(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
591			ctx.depthMask	(GL_TRUE);
592			ctx.stencilMask	(~0u);
593			ctx.clearColor	(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
594			ctx.clear		(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
595
596			ctx.useProgram	(program);
597
598			for (vector<rsg::VariableValue>::const_iterator uniformIter = uniformValues.begin(); uniformIter != uniformValues.end(); ++uniformIter)
599				setUniformValue(ctx, ctx.getUniformLocation(program, uniformIter->getVariable()->getName()), uniformIter->getValue());
600
601			for (vector<RenderCommand>::const_iterator cmdIter = renderCmds.begin(); cmdIter != renderCmds.end(); ++cmdIter)
602				render(ctx, posPtr, *cmdIter);
603
604			GLU_EXPECT_NO_ERROR(ctx.getError(), "Rendering failed");
605
606			ctx.readPixels(0, 0, m_viewportSize.x(), m_viewportSize.y(), GL_RGBA, GL_UNSIGNED_BYTE, readDst[ndx].getDataPtr());
607		}
608	}
609
610	{
611		const tcu::RGBA		threshold		= m_renderCtx.getRenderTarget().getPixelFormat().getColorThreshold()+tcu::RGBA(3,3,3,3);
612		const bool			compareOk		= tcu::bilinearCompare(m_testCtx.getLog(), "CompareResult", "Image comparison result", reference.getAccess(), rendered.getAccess(), threshold, tcu::COMPARE_LOG_RESULT);
613
614		if (!compareOk)
615		{
616			m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
617			return STOP;
618		}
619	}
620
621	m_iterNdx += 1;
622	return (m_iterNdx < NUM_ITERATIONS) ? CONTINUE : STOP;
623}
624
625} // gls
626} // deqp
627