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