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