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 Shader built-in variable tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es2fShaderBuiltinVarTests.hpp"
25#include "glsShaderRenderCase.hpp"
26#include "deRandom.hpp"
27#include "deString.h"
28#include "deMath.h"
29#include "deStringUtil.hpp"
30#include "tcuTestLog.hpp"
31#include "tcuTestCase.hpp"
32#include "tcuTextureUtil.hpp"
33#include "tcuRenderTarget.hpp"
34#include "tcuImageCompare.hpp"
35#include "gluPixelTransfer.hpp"
36#include "gluDrawUtil.hpp"
37
38#include "glwEnums.hpp"
39#include "glwFunctions.hpp"
40
41using std::string;
42using std::vector;
43using tcu::TestLog;
44
45namespace deqp
46{
47namespace gles2
48{
49namespace Functional
50{
51
52const float builtinConstScale = 4.0f;
53
54void evalBuiltinConstant (gls::ShaderEvalContext& c)
55{
56	bool isOk = 0 == (int)(deFloatFloor(c.coords.x() * builtinConstScale) + 0.05f);
57	c.color = isOk ? tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f) : tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f);
58}
59
60class ShaderBuiltinConstantCase : public gls::ShaderRenderCase
61{
62public:
63						ShaderBuiltinConstantCase				(Context& context, const char* name, const char* desc, const char* varName, deUint32 paramName, bool isVertexCase);
64						~ShaderBuiltinConstantCase				(void);
65
66	int					getRefValue								(void);
67	void				init									(void);
68
69private:
70	const std::string	m_varName;
71	const deUint32		m_paramName;
72};
73
74ShaderBuiltinConstantCase::ShaderBuiltinConstantCase (Context& context, const char* name, const char* desc, const char* varName, deUint32 paramName, bool isVertexCase)
75	: ShaderRenderCase	(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, desc, isVertexCase, evalBuiltinConstant)
76	, m_varName			(varName)
77	, m_paramName		(paramName)
78{
79}
80
81ShaderBuiltinConstantCase::~ShaderBuiltinConstantCase (void)
82{
83}
84
85int ShaderBuiltinConstantCase::getRefValue (void)
86{
87	if (m_varName == "gl_MaxDrawBuffers")
88	{
89		if (m_ctxInfo.isExtensionSupported("GL_EXT_draw_buffers"))
90			return m_ctxInfo.getInt(GL_MAX_DRAW_BUFFERS);
91		else
92			return 1;
93	}
94	else
95	{
96		DE_ASSERT(m_paramName != GL_NONE);
97		return m_ctxInfo.getInt(m_paramName);
98	}
99}
100
101void ShaderBuiltinConstantCase::init (void)
102{
103	const int refValue = getRefValue();
104	m_testCtx.getLog() << tcu::TestLog::Message << m_varName << " = " << refValue << tcu::TestLog::EndMessage;
105
106	static const char* defaultVertSrc =
107		"attribute highp vec4 a_position;\n"
108		"attribute highp vec4 a_coords;\n"
109		"varying mediump vec4 v_coords;\n\n"
110		"void main (void)\n"
111		"{\n"
112		"	v_coords = a_coords;\n"
113		"	gl_Position = a_position;\n"
114		"}\n";
115	static const char* defaultFragSrc =
116		"varying mediump vec4 v_color;\n\n"
117		"void main (void)\n"
118		"{\n"
119		"	gl_FragColor = v_color;\n"
120		"}\n";
121
122	// Construct shader.
123	std::ostringstream src;
124	if (m_isVertexCase)
125	{
126		src << "attribute highp vec4 a_position;\n"
127			<< "attribute highp vec4 a_coords;\n"
128			<< "varying mediump vec4 v_color;\n";
129	}
130	else
131		src << "varying mediump vec4 v_coords;\n";
132
133	src << "void main (void)\n{\n";
134
135	src << "\tbool isOk = " << m_varName << " == (" << refValue << " + int(floor(" << (m_isVertexCase ? "a_coords" : "v_coords") << ".x * " << de::floatToString(builtinConstScale, 1) << ") + 0.05));\n";
136	src << "\t" << (m_isVertexCase ? "v_color" : "gl_FragColor") << " = isOk ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0);\n";
137
138	if (m_isVertexCase)
139		src << "\tgl_Position = a_position;\n";
140
141	src << "}\n";
142
143	m_vertShaderSource		= m_isVertexCase ? src.str()		: defaultVertSrc;
144	m_fragShaderSource		= m_isVertexCase ? defaultFragSrc	: src.str();
145
146	gls::ShaderRenderCase::init();
147}
148
149namespace
150{
151
152struct DepthRangeParams
153{
154	DepthRangeParams (void)
155		: zNear	(0.0f)
156		, zFar	(1.0f)
157	{
158	}
159
160	DepthRangeParams (float zNear_, float zFar_)
161		: zNear	(zNear_)
162		, zFar	(zFar_)
163	{
164	}
165
166	float	zNear;
167	float	zFar;
168};
169
170class DepthRangeEvaluator : public gls::ShaderEvaluator
171{
172public:
173	DepthRangeEvaluator (const DepthRangeParams& params)
174		: m_params(params)
175	{
176	}
177
178	void evaluate (gls::ShaderEvalContext& c)
179	{
180		float zNear	= deFloatClamp(m_params.zNear, 0.0f, 1.0f);
181		float zFar	= deFloatClamp(m_params.zFar, 0.0f, 1.0f);
182		float diff	= zFar - zNear;
183		c.color.xyz() = tcu::Vec3(zNear, zFar, diff*0.5f + 0.5f);
184	}
185
186private:
187	const DepthRangeParams& m_params;
188};
189
190} // anonymous
191
192class ShaderDepthRangeTest : public gls::ShaderRenderCase
193{
194public:
195	ShaderDepthRangeTest (Context& context, const char* name, const char* desc, bool isVertexCase)
196		: ShaderRenderCase	(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, desc, isVertexCase, m_evaluator)
197		, m_evaluator		(m_depthRange)
198		, m_iterNdx			(0)
199	{
200	}
201
202	void init (void)
203	{
204		static const char* defaultVertSrc =
205			"attribute highp vec4 a_position;\n"
206			"void main (void)\n"
207			"{\n"
208			"	gl_Position = a_position;\n"
209			"}\n";
210		static const char* defaultFragSrc =
211			"varying mediump vec4 v_color;\n\n"
212			"void main (void)\n"
213			"{\n"
214			"	gl_FragColor = v_color;\n"
215			"}\n";
216
217		// Construct shader.
218		std::ostringstream src;
219		if (m_isVertexCase)
220			src << "attribute highp vec4 a_position;\n"
221				<< "varying mediump vec4 v_color;\n";
222
223		src << "void main (void)\n{\n";
224		src << "\t" << (m_isVertexCase ? "v_color" : "gl_FragColor") << " = vec4(gl_DepthRange.near, gl_DepthRange.far, gl_DepthRange.diff*0.5 + 0.5, 1.0);\n";
225
226		if (m_isVertexCase)
227			src << "\tgl_Position = a_position;\n";
228
229		src << "}\n";
230
231		m_vertShaderSource		= m_isVertexCase ? src.str()		: defaultVertSrc;
232		m_fragShaderSource		= m_isVertexCase ? defaultFragSrc	: src.str();
233
234		gls::ShaderRenderCase::init();
235	}
236
237	IterateResult iterate (void)
238	{
239		const glw::Functions& gl = m_renderCtx.getFunctions();
240
241		const DepthRangeParams cases[] =
242		{
243			DepthRangeParams(0.0f,  1.0f),
244			DepthRangeParams(1.5f, -1.0f),
245			DepthRangeParams(0.7f,  0.3f)
246		};
247
248		m_depthRange = cases[m_iterNdx];
249		m_testCtx.getLog() << tcu::TestLog::Message << "glDepthRangef(" << m_depthRange.zNear << ", " << m_depthRange.zFar << ")" << tcu::TestLog::EndMessage;
250		gl.depthRangef(m_depthRange.zNear, m_depthRange.zFar);
251		GLU_EXPECT_NO_ERROR(gl.getError(), "glDepthRangef()");
252
253		gls::ShaderRenderCase::iterate();
254		m_iterNdx += 1;
255
256		if (m_iterNdx == DE_LENGTH_OF_ARRAY(cases) || m_testCtx.getTestResult() != QP_TEST_RESULT_PASS)
257			return STOP;
258		else
259			return CONTINUE;
260	}
261
262private:
263	DepthRangeParams		m_depthRange;
264	DepthRangeEvaluator		m_evaluator;
265	int						m_iterNdx;
266};
267
268class FragCoordXYZCase : public TestCase
269{
270public:
271	FragCoordXYZCase (Context& context)
272		: TestCase(context, "fragcoord_xyz", "gl_FragCoord.xyz Test")
273	{
274	}
275
276	IterateResult iterate (void)
277	{
278		TestLog&				log			= m_testCtx.getLog();
279		const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
280		const int				width		= m_context.getRenderTarget().getWidth();
281		const int				height		= m_context.getRenderTarget().getHeight();
282		const tcu::RGBA			threshold	= tcu::RGBA(1,1,1,1) + m_context.getRenderTarget().getPixelFormat().getColorThreshold();
283		const tcu::Vec3			scale		(1.f / float(width), 1.f / float(height), 1.0f);
284
285		tcu::Surface			testImg		(width, height);
286		tcu::Surface			refImg		(width, height);
287
288		const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(
289			"attribute highp vec4 a_position;\n"
290			"void main (void)\n"
291			"{\n"
292			"	gl_Position = a_position;\n"
293			"}\n",
294
295			"uniform mediump vec3 u_scale;\n"
296			"void main (void)\n"
297			"{\n"
298			"	gl_FragColor = vec4(gl_FragCoord.xyz*u_scale, 1.0);\n"
299			"}\n"));
300
301		log << program;
302
303		if (!program.isOk())
304			throw tcu::TestError("Compile failed");
305
306		// Draw with GL.
307		{
308			const float positions[] =
309			{
310				-1.0f,  1.0f, -1.0f, 1.0f,
311				-1.0f, -1.0f,  0.0f, 1.0f,
312				 1.0f,  1.0f,  0.0f, 1.0f,
313				 1.0f, -1.0f,  1.0f, 1.0f
314			};
315			const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 };
316
317			const int				scaleLoc	= gl.getUniformLocation(program.getProgram(), "u_scale");
318			glu::VertexArrayBinding	posBinding	= glu::va::Float("a_position", 4, 4, 0, &positions[0]);
319
320			gl.useProgram(program.getProgram());
321			gl.uniform3fv(scaleLoc, 1, scale.getPtr());
322
323			glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding,
324					  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
325
326			glu::readPixels(m_context.getRenderContext(), 0, 0, testImg.getAccess());
327			GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
328		}
329
330		// Draw reference
331		for (int y = 0; y < refImg.getHeight(); y++)
332		{
333			for (int x = 0; x < refImg.getWidth(); x++)
334			{
335				const float			xf			= (float(x)+.5f) / float(refImg.getWidth());
336				const float			yf			= (float(refImg.getHeight()-y-1)+.5f) / float(refImg.getHeight());
337				const float			z			= (xf + yf) / 2.0f;
338				const tcu::Vec3		fragCoord	(float(x)+.5f, float(y)+.5f, z);
339				const tcu::Vec3		scaledFC	= fragCoord*scale;
340				const tcu::Vec4		color		(scaledFC.x(), scaledFC.y(), scaledFC.z(), 1.0f);
341
342				refImg.setPixel(x, y, tcu::RGBA(color));
343			}
344		}
345
346		// Compare
347		{
348			bool isOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", refImg, testImg, threshold, tcu::COMPARE_LOG_RESULT);
349			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
350									isOk ? "Pass"				: "Image comparison failed");
351		}
352
353		return STOP;
354	}
355};
356
357static inline float projectedTriInterpolate (const tcu::Vec3& s, const tcu::Vec3& w, float nx, float ny)
358{
359	return (s[0]*(1.0f-nx-ny)/w[0] + s[1]*ny/w[1] + s[2]*nx/w[2]) / ((1.0f-nx-ny)/w[0] + ny/w[1] + nx/w[2]);
360}
361
362class FragCoordWCase : public TestCase
363{
364public:
365	FragCoordWCase (Context& context)
366		: TestCase(context, "fragcoord_w", "gl_FragCoord.w Test")
367	{
368	}
369
370	IterateResult iterate (void)
371	{
372		TestLog&				log			= m_testCtx.getLog();
373		const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
374		const int				width		= m_context.getRenderTarget().getWidth();
375		const int				height		= m_context.getRenderTarget().getHeight();
376		const tcu::RGBA			threshold	= tcu::RGBA(1,1,1,1) + m_context.getRenderTarget().getPixelFormat().getColorThreshold();
377
378		tcu::Surface			testImg		(width, height);
379		tcu::Surface			refImg		(width, height);
380
381		const float				w[4]		= { 1.7f, 2.0f, 1.2f, 1.0f };
382
383		const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(
384			"attribute highp vec4 a_position;\n"
385			"void main (void)\n"
386			"{\n"
387			"	gl_Position = a_position;\n"
388			"}\n",
389
390			"void main (void)\n"
391			"{\n"
392			"	gl_FragColor = vec4(0.0, 1.0/gl_FragCoord.w - 1.0, 0.0, 1.0);\n"
393			"}\n"));
394
395		log << program;
396
397		if (!program.isOk())
398			throw tcu::TestError("Compile failed");
399
400		// Draw with GL.
401		{
402			const float positions[] =
403			{
404				-w[0],  w[0], 0.0f, w[0],
405				-w[1], -w[1], 0.0f, w[1],
406				 w[2],  w[2], 0.0f, w[2],
407				 w[3], -w[3], 0.0f, w[3]
408			};
409			const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 };
410
411			glu::VertexArrayBinding	posBinding	= glu::va::Float("a_position", 4, 4, 0, &positions[0]);
412
413			gl.useProgram(program.getProgram());
414
415			glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding,
416					  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
417
418			glu::readPixels(m_context.getRenderContext(), 0, 0, testImg.getAccess());
419			GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
420		}
421
422		// Draw reference
423		for (int y = 0; y < refImg.getHeight(); y++)
424		{
425			for (int x = 0; x < refImg.getWidth(); x++)
426			{
427				const float			xf			= (float(x)+.5f) / float(refImg.getWidth());
428				const float			yf			= (float(refImg.getHeight()-y-1)+.5f) / float(refImg.getHeight());
429				const float			oow			= ((xf + yf) < 1.0f)
430												? projectedTriInterpolate(tcu::Vec3(w[0], w[1], w[2]), tcu::Vec3(w[0], w[1], w[2]), xf, yf)
431												: projectedTriInterpolate(tcu::Vec3(w[3], w[2], w[1]), tcu::Vec3(w[3], w[2], w[1]), 1.0f-xf, 1.0f-yf);
432				const tcu::Vec4		color		(0.0f, oow - 1.0f, 0.0f, 1.0f);
433
434				refImg.setPixel(x, y, tcu::RGBA(color));
435			}
436		}
437
438		// Compare
439		{
440			bool isOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", refImg, testImg, threshold, tcu::COMPARE_LOG_RESULT);
441			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
442									isOk ? "Pass"				: "Image comparison failed");
443		}
444
445		return STOP;
446	}
447};
448
449class PointCoordCase : public TestCase
450{
451public:
452	PointCoordCase (Context& context)
453		: TestCase(context, "pointcoord", "gl_PointCoord Test")
454	{
455	}
456
457	IterateResult iterate (void)
458	{
459		TestLog&				log			= m_testCtx.getLog();
460		const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
461		const int				width		= de::min(256, m_context.getRenderTarget().getWidth());
462		const int				height		= de::min(256, m_context.getRenderTarget().getHeight());
463		const float				threshold	= 0.02f;
464
465		const int				numPoints	= 8;
466
467		vector<tcu::Vec3>		coords		(numPoints);
468		float					pointSizeRange[2]	= { 0.0f, 0.0f };
469
470		de::Random				rnd			(0x145fa);
471		tcu::Surface			testImg		(width, height);
472		tcu::Surface			refImg		(width, height);
473
474		gl.getFloatv(GL_ALIASED_POINT_SIZE_RANGE, &pointSizeRange[0]);
475		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE)");
476
477		if (pointSizeRange[0] <= 0.0f || pointSizeRange[1] <= 0.0f || pointSizeRange[1] < pointSizeRange[0])
478			throw tcu::TestError("Invalid GL_ALIASED_POINT_SIZE_RANGE");
479
480		// Compute coordinates.
481		{
482
483			for (vector<tcu::Vec3>::iterator coord = coords.begin(); coord != coords.end(); ++coord)
484			{
485				coord->x() = rnd.getFloat(-0.9f, 0.9f);
486				coord->y() = rnd.getFloat(-0.9f, 0.9f);
487				coord->z() = rnd.getFloat(pointSizeRange[0], pointSizeRange[1]);
488			}
489		}
490
491		const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(
492			"attribute highp vec3 a_positionSize;\n"
493			"void main (void)\n"
494			"{\n"
495			"	gl_Position = vec4(a_positionSize.xy, 0.0, 1.0);\n"
496			"	gl_PointSize = a_positionSize.z;\n"
497			"}\n",
498
499			"void main (void)\n"
500			"{\n"
501			"	gl_FragColor = vec4(gl_PointCoord, 0.0, 1.0);\n"
502			"}\n"));
503
504		log << program;
505
506		if (!program.isOk())
507			throw tcu::TestError("Compile failed");
508
509		// Draw with GL.
510		{
511			glu::VertexArrayBinding	posBinding	= glu::va::Float("a_positionSize", 3, (int)coords.size(), 0, (const float*)&coords[0]);
512			const int				viewportX	= rnd.getInt(0, m_context.getRenderTarget().getWidth()-width);
513			const int				viewportY	= rnd.getInt(0, m_context.getRenderTarget().getHeight()-height);
514
515			gl.viewport(viewportX, viewportY, width, height);
516			gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
517			gl.clear(GL_COLOR_BUFFER_BIT);
518
519			gl.useProgram(program.getProgram());
520
521			glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding,
522					  glu::pr::Points((int)coords.size()));
523
524			glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, testImg.getAccess());
525			GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
526		}
527
528		// Draw reference
529		tcu::clear(refImg.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
530		for (vector<tcu::Vec3>::const_iterator pointIter = coords.begin(); pointIter != coords.end(); ++pointIter)
531		{
532			const int	x0		= deRoundFloatToInt32(float(width) *(pointIter->x()*0.5f + 0.5f) - pointIter->z()*0.5f);
533			const int	y0		= deRoundFloatToInt32(float(height)*(pointIter->y()*0.5f + 0.5f) - pointIter->z()*0.5f);
534			const int	x1		= deRoundFloatToInt32(float(width) *(pointIter->x()*0.5f + 0.5f) + pointIter->z()*0.5f);
535			const int	y1		= deRoundFloatToInt32(float(height)*(pointIter->y()*0.5f + 0.5f) + pointIter->z()*0.5f);
536			const int	w		= x1-x0;
537			const int	h		= y1-y0;
538
539			for (int yo = 0; yo < h; yo++)
540			{
541				for (int xo = 0; xo < w; xo++)
542				{
543					const float			xf		= (float(xo)+0.5f) / float(w);
544					const float			yf		= (float(h-yo-1)+0.5f) / float(h);
545					const tcu::Vec4		color	(xf, yf, 0.0f, 1.0f);
546					const int			dx		= x0+xo;
547					const int			dy		= y0+yo;
548
549					if (de::inBounds(dx, 0, refImg.getWidth()) && de::inBounds(dy, 0, refImg.getHeight()))
550						refImg.setPixel(dx, dy, tcu::RGBA(color));
551				}
552			}
553		}
554
555		// Compare
556		{
557			bool isOk = tcu::fuzzyCompare(log, "Result", "Image comparison result", refImg, testImg, threshold, tcu::COMPARE_LOG_RESULT);
558			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
559									isOk ? "Pass"				: "Image comparison failed");
560		}
561
562		return STOP;
563	}
564};
565
566class FrontFacingCase : public TestCase
567{
568public:
569	FrontFacingCase (Context& context)
570		: TestCase(context, "frontfacing", "gl_FrontFacing Test")
571	{
572	}
573
574	IterateResult iterate (void)
575	{
576		// Test case renders two adjecent quads, where left is has front-facing
577		// triagles and right back-facing. Color is selected based on gl_FrontFacing
578		// value.
579
580		TestLog&				log			= m_testCtx.getLog();
581		const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
582		de::Random				rnd			(0x89f2c);
583		const int				width		= de::min(64, m_context.getRenderTarget().getWidth());
584		const int				height		= de::min(64, m_context.getRenderTarget().getHeight());
585		const int				viewportX	= rnd.getInt(0, m_context.getRenderTarget().getWidth()-width);
586		const int				viewportY	= rnd.getInt(0, m_context.getRenderTarget().getHeight()-height);
587		const tcu::RGBA			threshold	= tcu::RGBA(1,1,1,1) + m_context.getRenderTarget().getPixelFormat().getColorThreshold();
588
589		tcu::Surface			testImg		(width, height);
590		tcu::Surface			refImg		(width, height);
591
592		const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources(
593			"attribute highp vec4 a_position;\n"
594			"void main (void)\n"
595			"{\n"
596			"	gl_Position = a_position;\n"
597			"}\n",
598
599			"void main (void)\n"
600			"{\n"
601			"	if (gl_FrontFacing)\n"
602			"		gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
603			"	else\n"
604			"		gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
605			"}\n"));
606
607		log << program;
608
609		if (!program.isOk())
610			throw tcu::TestError("Compile failed");
611
612		// Draw with GL.
613		{
614			const float positions[] =
615			{
616				-1.0f,  1.0f, 0.0f, 1.0f,
617				-1.0f, -1.0f, 0.0f, 1.0f,
618				 1.0f,  1.0f, 0.0f, 1.0f,
619				 1.0f, -1.0f, 0.0f, 1.0f
620			};
621			const deUint16 indicesCCW[]	= { 0, 1, 2, 2, 1, 3 };
622			const deUint16 indicesCW[]	= { 2, 1, 0, 3, 1, 2 };
623
624			glu::VertexArrayBinding	posBinding	= glu::va::Float("a_position", 4, 4, 0, &positions[0]);
625
626			gl.useProgram(program.getProgram());
627
628			gl.viewport(viewportX, viewportY, width/2, height);
629			glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding,
630					  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indicesCCW), &indicesCCW[0]));
631
632			gl.viewport(viewportX + width/2, viewportY, width-width/2, height);
633			glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding,
634					  glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indicesCW), &indicesCW[0]));
635
636			glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, testImg.getAccess());
637			GLU_EXPECT_NO_ERROR(gl.getError(), "Draw");
638		}
639
640		// Draw reference
641		for (int y = 0; y < refImg.getHeight(); y++)
642		{
643			for (int x = 0; x < refImg.getWidth()/2; x++)
644				refImg.setPixel(x, y, tcu::RGBA::green());
645
646			for (int x = refImg.getWidth()/2; x < refImg.getWidth(); x++)
647				refImg.setPixel(x, y, tcu::RGBA::blue());
648		}
649
650		// Compare
651		{
652			bool isOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", refImg, testImg, threshold, tcu::COMPARE_LOG_RESULT);
653			m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
654									isOk ? "Pass"				: "Image comparison failed");
655		}
656
657		return STOP;
658	}
659};
660
661ShaderBuiltinVarTests::ShaderBuiltinVarTests (Context& context)
662	: TestCaseGroup(context, "builtin_variable", "Built-in Variable Tests")
663{
664}
665
666ShaderBuiltinVarTests::~ShaderBuiltinVarTests (void)
667{
668}
669
670void ShaderBuiltinVarTests::init (void)
671{
672	// Builtin constants.
673
674	static const struct
675	{
676		const char*		caseName;
677		const char*		varName;
678		deUint32		paramName;
679	} builtinConstants[] =
680	{
681		{ "max_vertex_attribs",					"gl_MaxVertexAttribs",				GL_MAX_VERTEX_ATTRIBS },
682		{ "max_vertex_uniform_vectors",			"gl_MaxVertexUniformVectors",		GL_MAX_VERTEX_UNIFORM_VECTORS },
683		{ "max_fragment_uniform_vectors",		"gl_MaxFragmentUniformVectors",		GL_MAX_FRAGMENT_UNIFORM_VECTORS },
684		{ "max_varying_vectors",				"gl_MaxVaryingVectors",				GL_MAX_VARYING_VECTORS },
685		{ "max_texture_image_units",			"gl_MaxTextureImageUnits",			GL_MAX_TEXTURE_IMAGE_UNITS },
686		{ "max_vertex_texture_image_units",		"gl_MaxVertexTextureImageUnits",	GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS },
687		{ "max_combined_texture_image_units",	"gl_MaxCombinedTextureImageUnits",	GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS },
688		{ "max_draw_buffers",					"gl_MaxDrawBuffers",				GL_NONE }
689	};
690
691	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(builtinConstants); ndx++)
692	{
693		const char*		caseName	= builtinConstants[ndx].caseName;
694		const char*		varName		= builtinConstants[ndx].varName;
695		deUint32		paramName	= builtinConstants[ndx].paramName;
696
697		addChild(new ShaderBuiltinConstantCase(m_context, (string(caseName) + "_vertex").c_str(),	varName, varName, paramName, true));
698		addChild(new ShaderBuiltinConstantCase(m_context, (string(caseName) + "_fragment").c_str(),	varName, varName, paramName, false));
699	}
700
701	addChild(new ShaderDepthRangeTest(m_context, "depth_range_vertex",		"gl_DepthRange", true));
702	addChild(new ShaderDepthRangeTest(m_context, "depth_range_fragment",	"gl_DepthRange", false));
703
704	// Fragment shader builtin variables.
705
706	addChild(new FragCoordXYZCase	(m_context));
707	addChild(new FragCoordWCase		(m_context));
708	addChild(new PointCoordCase		(m_context));
709	addChild(new FrontFacingCase	(m_context));
710}
711
712} // Functional
713} // gles2
714} // deqp
715