• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2016 Google Inc.
6  * Copyright (c) 2016 The Khronos Group Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */ /*!
21  * \file
22  * \brief Shader execute test.
23  */ /*-------------------------------------------------------------------*/
24 
25 #include "glcShaderRenderCase.hpp"
26 
27 #include "tcuImageCompare.hpp"
28 #include "tcuRenderTarget.hpp"
29 #include "tcuSurface.hpp"
30 #include "tcuTestLog.hpp"
31 #include "tcuVector.hpp"
32 
33 #include "gluDrawUtil.hpp"
34 #include "gluPixelTransfer.hpp"
35 #include "gluTexture.hpp"
36 #include "gluTextureUtil.hpp"
37 
38 #include "glwEnums.hpp"
39 #include "glwFunctions.hpp"
40 
41 #include "deMath.h"
42 #include "deMemory.h"
43 #include "deRandom.hpp"
44 #include "deString.h"
45 #include "deStringUtil.hpp"
46 
47 #include <stdio.h>
48 #include <string>
49 #include <vector>
50 
51 namespace deqp
52 {
53 
54 using namespace std;
55 using namespace tcu;
56 using namespace glu;
57 
58 static const int	   GRID_SIZE		   = 64;
59 static const int	   MAX_RENDER_WIDTH	= 128;
60 static const int	   MAX_RENDER_HEIGHT   = 112;
61 static const tcu::Vec4 DEFAULT_CLEAR_COLOR = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
62 
toRGBA(const Vec4 & a)63 inline RGBA toRGBA(const Vec4& a)
64 {
65 	return RGBA(
66 		deClamp32(deRoundFloatToInt32(a.x() * 255.0f), 0, 255), deClamp32(deRoundFloatToInt32(a.y() * 255.0f), 0, 255),
67 		deClamp32(deRoundFloatToInt32(a.z() * 255.0f), 0, 255), deClamp32(deRoundFloatToInt32(a.w() * 255.0f), 0, 255));
68 }
69 
toVec(const RGBA & c)70 inline tcu::Vec4 toVec(const RGBA& c)
71 {
72 	return tcu::Vec4(static_cast<float>(c.getRed()) / 255.0f, static_cast<float>(c.getGreen()) / 255.0f,
73 					 static_cast<float>(c.getBlue()) / 255.0f, static_cast<float>(c.getAlpha()) / 255.0f);
74 }
75 
76 // TextureBinding
77 
TextureBinding(const glu::Texture2D * tex2D,const tcu::Sampler & sampler)78 TextureBinding::TextureBinding(const glu::Texture2D* tex2D, const tcu::Sampler& sampler)
79 	: m_type(TYPE_2D), m_sampler(sampler)
80 {
81 	m_binding.tex2D = tex2D;
82 }
83 
TextureBinding(const glu::TextureCube * texCube,const tcu::Sampler & sampler)84 TextureBinding::TextureBinding(const glu::TextureCube* texCube, const tcu::Sampler& sampler)
85 	: m_type(TYPE_CUBE_MAP), m_sampler(sampler)
86 {
87 	m_binding.texCube = texCube;
88 }
89 
TextureBinding(const glu::Texture2DArray * tex2DArray,const tcu::Sampler & sampler)90 TextureBinding::TextureBinding(const glu::Texture2DArray* tex2DArray, const tcu::Sampler& sampler)
91 	: m_type(TYPE_2D_ARRAY), m_sampler(sampler)
92 {
93 	m_binding.tex2DArray = tex2DArray;
94 }
95 
TextureBinding(const glu::Texture3D * tex3D,const tcu::Sampler & sampler)96 TextureBinding::TextureBinding(const glu::Texture3D* tex3D, const tcu::Sampler& sampler)
97 	: m_type(TYPE_3D), m_sampler(sampler)
98 {
99 	m_binding.tex3D = tex3D;
100 }
101 
TextureBinding(void)102 TextureBinding::TextureBinding(void) : m_type(TYPE_NONE)
103 {
104 	m_binding.tex2D = DE_NULL;
105 }
106 
TextureBinding(const glu::TextureCubeArray * texCubeArray,const tcu::Sampler & sampler)107 TextureBinding::TextureBinding(const glu::TextureCubeArray* texCubeArray, const tcu::Sampler& sampler)
108 	: m_type(TYPE_CUBE_MAP_ARRAY), m_sampler(sampler)
109 {
110 	m_binding.texCubeArray = texCubeArray;
111 }
112 
setSampler(const tcu::Sampler & sampler)113 void TextureBinding::setSampler(const tcu::Sampler& sampler)
114 {
115 	m_sampler = sampler;
116 }
117 
setTexture(const glu::Texture2D * tex2D)118 void TextureBinding::setTexture(const glu::Texture2D* tex2D)
119 {
120 	m_type			= TYPE_2D;
121 	m_binding.tex2D = tex2D;
122 }
123 
setTexture(const glu::TextureCube * texCube)124 void TextureBinding::setTexture(const glu::TextureCube* texCube)
125 {
126 	m_type			  = TYPE_CUBE_MAP;
127 	m_binding.texCube = texCube;
128 }
129 
setTexture(const glu::Texture2DArray * tex2DArray)130 void TextureBinding::setTexture(const glu::Texture2DArray* tex2DArray)
131 {
132 	m_type				 = TYPE_2D_ARRAY;
133 	m_binding.tex2DArray = tex2DArray;
134 }
135 
setTexture(const glu::Texture3D * tex3D)136 void TextureBinding::setTexture(const glu::Texture3D* tex3D)
137 {
138 	m_type			= TYPE_3D;
139 	m_binding.tex3D = tex3D;
140 }
141 
setTexture(const glu::TextureCubeArray * texCubeArray)142 void TextureBinding::setTexture(const glu::TextureCubeArray* texCubeArray)
143 {
144 	m_type				   = TYPE_CUBE_MAP_ARRAY;
145 	m_binding.texCubeArray = texCubeArray;
146 }
147 
148 // QuadGrid.
149 
150 class QuadGrid
151 {
152 public:
153 	QuadGrid(int gridSize, int screenWidth, int screenHeight, const Vec4& constCoords,
154 			 const vector<Mat4>& userAttribTransforms, const vector<TextureBinding>& textures);
155 	~QuadGrid(void);
156 
getGridSize(void) const157 	int getGridSize(void) const
158 	{
159 		return m_gridSize;
160 	}
getNumVertices(void) const161 	int getNumVertices(void) const
162 	{
163 		return m_numVertices;
164 	}
getNumTriangles(void) const165 	int getNumTriangles(void) const
166 	{
167 		return m_numTriangles;
168 	}
getConstCoords(void) const169 	const Vec4& getConstCoords(void) const
170 	{
171 		return m_constCoords;
172 	}
getUserAttribTransforms(void) const173 	const vector<Mat4> getUserAttribTransforms(void) const
174 	{
175 		return m_userAttribTransforms;
176 	}
getTextures(void) const177 	const vector<TextureBinding>& getTextures(void) const
178 	{
179 		return m_textures;
180 	}
181 
getPositions(void) const182 	const Vec4* getPositions(void) const
183 	{
184 		return &m_positions[0];
185 	}
getAttribOne(void) const186 	const float* getAttribOne(void) const
187 	{
188 		return &m_attribOne[0];
189 	}
getCoords(void) const190 	const Vec4* getCoords(void) const
191 	{
192 		return &m_coords[0];
193 	}
getUnitCoords(void) const194 	const Vec4* getUnitCoords(void) const
195 	{
196 		return &m_unitCoords[0];
197 	}
getUserAttrib(int attribNdx) const198 	const Vec4* getUserAttrib(int attribNdx) const
199 	{
200 		return &m_userAttribs[attribNdx][0];
201 	}
getIndices(void) const202 	const deUint16* getIndices(void) const
203 	{
204 		return &m_indices[0];
205 	}
206 
207 	Vec4 getCoords(float sx, float sy) const;
208 	Vec4 getUnitCoords(float sx, float sy) const;
209 
getNumUserAttribs(void) const210 	int getNumUserAttribs(void) const
211 	{
212 		return (int)m_userAttribTransforms.size();
213 	}
214 	Vec4 getUserAttrib(int attribNdx, float sx, float sy) const;
215 
216 private:
217 	int					   m_gridSize;
218 	int					   m_numVertices;
219 	int					   m_numTriangles;
220 	Vec4				   m_constCoords;
221 	vector<Mat4>		   m_userAttribTransforms;
222 	vector<TextureBinding> m_textures;
223 
224 	vector<Vec4>	 m_screenPos;
225 	vector<Vec4>	 m_positions;
226 	vector<Vec4>	 m_coords;	 //!< Near-unit coordinates, roughly [-2.0 .. 2.0].
227 	vector<Vec4>	 m_unitCoords; //!< Positive-only coordinates [0.0 .. 1.5].
228 	vector<float>	m_attribOne;
229 	vector<Vec4>	 m_userAttribs[ShaderEvalContext::MAX_TEXTURES];
230 	vector<deUint16> m_indices;
231 };
232 
QuadGrid(int gridSize,int width,int height,const Vec4 & constCoords,const vector<Mat4> & userAttribTransforms,const vector<TextureBinding> & textures)233 QuadGrid::QuadGrid(int gridSize, int width, int height, const Vec4& constCoords,
234 				   const vector<Mat4>& userAttribTransforms, const vector<TextureBinding>& textures)
235 	: m_gridSize(gridSize)
236 	, m_numVertices((gridSize + 1) * (gridSize + 1))
237 	, m_numTriangles(gridSize * gridSize * 2)
238 	, m_constCoords(constCoords)
239 	, m_userAttribTransforms(userAttribTransforms)
240 	, m_textures(textures)
241 {
242 	Vec4 viewportScale = Vec4((float)width, (float)height, 0.0f, 0.0f);
243 
244 	// Compute vertices.
245 	m_positions.resize(m_numVertices);
246 	m_coords.resize(m_numVertices);
247 	m_unitCoords.resize(m_numVertices);
248 	m_attribOne.resize(m_numVertices);
249 	m_screenPos.resize(m_numVertices);
250 
251 	// User attributes.
252 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(m_userAttribs); i++)
253 		m_userAttribs[i].resize(m_numVertices);
254 
255 	for (int y = 0; y < gridSize + 1; y++)
256 		for (int x = 0; x < gridSize + 1; x++)
257 		{
258 			float sx	 = static_cast<float>(x) / static_cast<float>(gridSize);
259 			float sy	 = static_cast<float>(y) / static_cast<float>(gridSize);
260 			float fx	 = 2.0f * sx - 1.0f;
261 			float fy	 = 2.0f * sy - 1.0f;
262 			int   vtxNdx = ((y * (gridSize + 1)) + x);
263 
264 			m_positions[vtxNdx]  = Vec4(fx, fy, 0.0f, 1.0f);
265 			m_attribOne[vtxNdx]  = 1.0f;
266 			m_screenPos[vtxNdx]  = Vec4(sx, sy, 0.0f, 1.0f) * viewportScale;
267 			m_coords[vtxNdx]	 = getCoords(sx, sy);
268 			m_unitCoords[vtxNdx] = getUnitCoords(sx, sy);
269 
270 			for (int attribNdx					 = 0; attribNdx < getNumUserAttribs(); attribNdx++)
271 				m_userAttribs[attribNdx][vtxNdx] = getUserAttrib(attribNdx, sx, sy);
272 		}
273 
274 	// Compute indices.
275 	m_indices.resize(3 * m_numTriangles);
276 	for (int y = 0; y < gridSize; y++)
277 		for (int x = 0; x < gridSize; x++)
278 		{
279 			int stride = gridSize + 1;
280 			int v00	= (y * stride) + x;
281 			int v01	= (y * stride) + x + 1;
282 			int v10	= ((y + 1) * stride) + x;
283 			int v11	= ((y + 1) * stride) + x + 1;
284 
285 			int baseNdx			   = ((y * gridSize) + x) * 6;
286 			m_indices[baseNdx + 0] = static_cast<deUint16>(v10);
287 			m_indices[baseNdx + 1] = static_cast<deUint16>(v00);
288 			m_indices[baseNdx + 2] = static_cast<deUint16>(v01);
289 
290 			m_indices[baseNdx + 3] = static_cast<deUint16>(v10);
291 			m_indices[baseNdx + 4] = static_cast<deUint16>(v01);
292 			m_indices[baseNdx + 5] = static_cast<deUint16>(v11);
293 		}
294 }
295 
~QuadGrid(void)296 QuadGrid::~QuadGrid(void)
297 {
298 }
299 
getCoords(float sx,float sy) const300 inline Vec4 QuadGrid::getCoords(float sx, float sy) const
301 {
302 	float fx = 2.0f * sx - 1.0f;
303 	float fy = 2.0f * sy - 1.0f;
304 	return Vec4(fx, fy, -fx + 0.33f * fy, -0.275f * fx - fy);
305 }
306 
getUnitCoords(float sx,float sy) const307 inline Vec4 QuadGrid::getUnitCoords(float sx, float sy) const
308 {
309 	return Vec4(sx, sy, 0.33f * sx + 0.5f * sy, 0.5f * sx + 0.25f * sy);
310 }
311 
getUserAttrib(int attribNdx,float sx,float sy) const312 inline Vec4 QuadGrid::getUserAttrib(int attribNdx, float sx, float sy) const
313 {
314 	// homogeneous normalized screen-space coordinates
315 	return m_userAttribTransforms[attribNdx] * Vec4(sx, sy, 0.0f, 1.0f);
316 }
317 
318 // ShaderEvalContext.
319 
ShaderEvalContext(const QuadGrid & quadGrid_)320 ShaderEvalContext::ShaderEvalContext(const QuadGrid& quadGrid_)
321 	: constCoords(quadGrid_.getConstCoords()), isDiscarded(false), quadGrid(quadGrid_)
322 {
323 	const vector<TextureBinding>& bindings = quadGrid.getTextures();
324 	DE_ASSERT((int)bindings.size() <= MAX_TEXTURES);
325 
326 	// Fill in texture array.
327 	for (int ndx = 0; ndx < (int)bindings.size(); ndx++)
328 	{
329 		const TextureBinding& binding = bindings[ndx];
330 
331 		if (binding.getType() == TextureBinding::TYPE_NONE)
332 			continue;
333 
334 		textures[ndx].sampler = binding.getSampler();
335 
336 		switch (binding.getType())
337 		{
338 		case TextureBinding::TYPE_2D:
339 			textures[ndx].tex2D = &binding.get2D()->getRefTexture();
340 			break;
341 		case TextureBinding::TYPE_CUBE_MAP:
342 			textures[ndx].texCube = &binding.getCube()->getRefTexture();
343 			break;
344 		case TextureBinding::TYPE_2D_ARRAY:
345 			textures[ndx].tex2DArray = &binding.get2DArray()->getRefTexture();
346 			break;
347 		case TextureBinding::TYPE_3D:
348 			textures[ndx].tex3D = &binding.get3D()->getRefTexture();
349 			break;
350 		case TextureBinding::TYPE_CUBE_MAP_ARRAY:
351 			textures[ndx].texCubeArray = &binding.getCubeArray()->getRefTexture();
352 			break;
353 		default:
354 			DE_ASSERT(DE_FALSE);
355 		}
356 	}
357 }
358 
~ShaderEvalContext(void)359 ShaderEvalContext::~ShaderEvalContext(void)
360 {
361 }
362 
reset(float sx,float sy)363 void ShaderEvalContext::reset(float sx, float sy)
364 {
365 	// Clear old values
366 	color		= Vec4(0.0f, 0.0f, 0.0f, 1.0f);
367 	isDiscarded = false;
368 
369 	// Compute coords
370 	coords	 = quadGrid.getCoords(sx, sy);
371 	unitCoords = quadGrid.getUnitCoords(sx, sy);
372 
373 	// Compute user attributes.
374 	int numAttribs = quadGrid.getNumUserAttribs();
375 	DE_ASSERT(numAttribs <= MAX_USER_ATTRIBS);
376 	for (int attribNdx = 0; attribNdx < numAttribs; attribNdx++)
377 		in[attribNdx]  = quadGrid.getUserAttrib(attribNdx, sx, sy);
378 }
379 
texture2D(int unitNdx,const tcu::Vec2 & texCoords)380 tcu::Vec4 ShaderEvalContext::texture2D(int unitNdx, const tcu::Vec2& texCoords)
381 {
382 	if (textures[unitNdx].tex2D)
383 		return textures[unitNdx].tex2D->sample(textures[unitNdx].sampler, texCoords.x(), texCoords.y(), 0.0f);
384 	else
385 		return tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
386 }
387 
388 // ShaderEvaluator
389 
ShaderEvaluator(void)390 ShaderEvaluator::ShaderEvaluator(void) : m_evalFunc(DE_NULL)
391 {
392 }
393 
ShaderEvaluator(ShaderEvalFunc evalFunc)394 ShaderEvaluator::ShaderEvaluator(ShaderEvalFunc evalFunc) : m_evalFunc(evalFunc)
395 {
396 }
397 
~ShaderEvaluator(void)398 ShaderEvaluator::~ShaderEvaluator(void)
399 {
400 }
401 
evaluate(ShaderEvalContext & ctx)402 void ShaderEvaluator::evaluate(ShaderEvalContext& ctx)
403 {
404 	DE_ASSERT(m_evalFunc);
405 	m_evalFunc(ctx);
406 }
407 
408 // ShaderRenderCase.
409 
ShaderRenderCase(TestContext & testCtx,RenderContext & renderCtx,const ContextInfo & ctxInfo,const char * name,const char * description,bool isVertexCase,ShaderEvalFunc evalFunc)410 ShaderRenderCase::ShaderRenderCase(TestContext& testCtx, RenderContext& renderCtx, const ContextInfo& ctxInfo,
411 								   const char* name, const char* description, bool isVertexCase,
412 								   ShaderEvalFunc evalFunc)
413 	: TestCase(testCtx, name, description)
414 	, m_renderCtx(renderCtx)
415 	, m_ctxInfo(ctxInfo)
416 	, m_isVertexCase(isVertexCase)
417 	, m_defaultEvaluator(evalFunc)
418 	, m_evaluator(m_defaultEvaluator)
419 	, m_clearColor(DEFAULT_CLEAR_COLOR)
420 	, m_program(DE_NULL)
421 {
422 }
423 
ShaderRenderCase(TestContext & testCtx,RenderContext & renderCtx,const ContextInfo & ctxInfo,const char * name,const char * description,bool isVertexCase,ShaderEvaluator & evaluator)424 ShaderRenderCase::ShaderRenderCase(TestContext& testCtx, RenderContext& renderCtx, const ContextInfo& ctxInfo,
425 								   const char* name, const char* description, bool isVertexCase,
426 								   ShaderEvaluator& evaluator)
427 	: TestCase(testCtx, name, description)
428 	, m_renderCtx(renderCtx)
429 	, m_ctxInfo(ctxInfo)
430 	, m_isVertexCase(isVertexCase)
431 	, m_defaultEvaluator(DE_NULL)
432 	, m_evaluator(evaluator)
433 	, m_clearColor(DEFAULT_CLEAR_COLOR)
434 	, m_program(DE_NULL)
435 {
436 }
437 
~ShaderRenderCase(void)438 ShaderRenderCase::~ShaderRenderCase(void)
439 {
440 	ShaderRenderCase::deinit();
441 }
442 
init(void)443 void ShaderRenderCase::init(void)
444 {
445 	TestLog&			  log = m_testCtx.getLog();
446 	const glw::Functions& gl  = m_renderCtx.getFunctions();
447 
448 	GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderRenderCase::init() begin");
449 
450 	DE_ASSERT(!m_program);
451 	m_program =
452 		new ShaderProgram(m_renderCtx, glu::makeVtxFragSources(m_vertShaderSource.c_str(), m_fragShaderSource.c_str()));
453 
454 	try
455 	{
456 		log << *m_program; // Always log shader program.
457 
458 		if (!m_program->isOk())
459 			TCU_FAIL("Failed to compile shader program");
460 
461 		GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderRenderCase::init() end");
462 	}
463 	catch (const std::exception&)
464 	{
465 		// Clean up.
466 		ShaderRenderCase::deinit();
467 		throw;
468 	}
469 }
470 
deinit(void)471 void ShaderRenderCase::deinit(void)
472 {
473 	delete m_program;
474 	m_program = DE_NULL;
475 }
476 
getViewportSize(void) const477 tcu::IVec2 ShaderRenderCase::getViewportSize(void) const
478 {
479 	return tcu::IVec2(de::min(m_renderCtx.getRenderTarget().getWidth(), MAX_RENDER_WIDTH),
480 					  de::min(m_renderCtx.getRenderTarget().getHeight(), MAX_RENDER_HEIGHT));
481 }
482 
iterate(void)483 TestNode::IterateResult ShaderRenderCase::iterate(void)
484 {
485 	const glw::Functions& gl = m_renderCtx.getFunctions();
486 
487 	GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderRenderCase::iterate() begin");
488 
489 	DE_ASSERT(m_program);
490 	deUint32 programID = m_program->getProgram();
491 	gl.useProgram(programID);
492 
493 	// Create quad grid.
494 	IVec2 viewportSize = getViewportSize();
495 	int   width		   = viewportSize.x();
496 	int   height	   = viewportSize.y();
497 
498 	// \todo [petri] Better handling of constCoords (render in multiple chunks, vary coords).
499 	QuadGrid quadGrid(m_isVertexCase ? GRID_SIZE : 4, width, height, Vec4(0.0f, 0.0f, 0.0f, 1.0f),
500 					  m_userAttribTransforms, m_textures);
501 
502 	// Render result.
503 	Surface resImage(width, height);
504 	render(resImage, programID, quadGrid);
505 
506 	// Compute reference.
507 	Surface refImage(width, height);
508 	if (m_isVertexCase)
509 		computeVertexReference(refImage, quadGrid);
510 	else
511 		computeFragmentReference(refImage, quadGrid);
512 
513 	// Compare.
514 	bool testOk = compareImages(resImage, refImage, 0.07f);
515 
516 	// De-initialize.
517 	gl.useProgram(0);
518 
519 	m_testCtx.setTestResult(testOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, testOk ? "Pass" : "Fail");
520 	return TestNode::STOP;
521 }
522 
setup(deUint32 programID)523 void ShaderRenderCase::setup(deUint32 programID)
524 {
525 	DE_UNREF(programID);
526 }
527 
setupUniforms(deUint32 programID,const Vec4 & constCoords)528 void ShaderRenderCase::setupUniforms(deUint32 programID, const Vec4& constCoords)
529 {
530 	DE_UNREF(programID);
531 	DE_UNREF(constCoords);
532 }
533 
setupDefaultInputs(int programID)534 void ShaderRenderCase::setupDefaultInputs(int programID)
535 {
536 	const glw::Functions& gl = m_renderCtx.getFunctions();
537 
538 	// SETUP UNIFORMS.
539 
540 	setupDefaultUniforms(m_renderCtx, programID);
541 
542 	GLU_EXPECT_NO_ERROR(gl.getError(), "post uniform setup");
543 
544 	// SETUP TEXTURES.
545 
546 	for (int ndx = 0; ndx < (int)m_textures.size(); ndx++)
547 	{
548 		const TextureBinding& tex		= m_textures[ndx];
549 		const tcu::Sampler&   sampler   = tex.getSampler();
550 		deUint32			  texTarget = GL_NONE;
551 		deUint32			  texObj	= 0;
552 
553 		if (tex.getType() == TextureBinding::TYPE_NONE)
554 			continue;
555 
556 		// Feature check.
557 		if (m_renderCtx.getType().getAPI() == glu::ApiType(2, 0, glu::PROFILE_ES))
558 		{
559 			if (tex.getType() == TextureBinding::TYPE_2D_ARRAY)
560 				throw tcu::NotSupportedError("2D array texture binding is not supported");
561 
562 			if (tex.getType() == TextureBinding::TYPE_3D)
563 				throw tcu::NotSupportedError("3D texture binding is not supported");
564 
565 			if (sampler.compare != tcu::Sampler::COMPAREMODE_NONE)
566 				throw tcu::NotSupportedError("Shadow lookups are not supported");
567 		}
568 
569 		switch (tex.getType())
570 		{
571 		case TextureBinding::TYPE_2D:
572 			texTarget = GL_TEXTURE_2D;
573 			texObj	= tex.get2D()->getGLTexture();
574 			break;
575 		case TextureBinding::TYPE_CUBE_MAP:
576 			texTarget = GL_TEXTURE_CUBE_MAP;
577 			texObj	= tex.getCube()->getGLTexture();
578 			break;
579 		case TextureBinding::TYPE_2D_ARRAY:
580 			texTarget = GL_TEXTURE_2D_ARRAY;
581 			texObj	= tex.get2DArray()->getGLTexture();
582 			break;
583 		case TextureBinding::TYPE_3D:
584 			texTarget = GL_TEXTURE_3D;
585 			texObj	= tex.get3D()->getGLTexture();
586 			break;
587 		case TextureBinding::TYPE_CUBE_MAP_ARRAY:
588 			texTarget = GL_TEXTURE_CUBE_MAP_ARRAY;
589 			texObj	= tex.getCubeArray()->getGLTexture();
590 			break;
591 		default:
592 			DE_ASSERT(DE_FALSE);
593 		}
594 
595 		gl.activeTexture(GL_TEXTURE0 + ndx);
596 		gl.bindTexture(texTarget, texObj);
597 		gl.texParameteri(texTarget, GL_TEXTURE_WRAP_S, glu::getGLWrapMode(sampler.wrapS));
598 		gl.texParameteri(texTarget, GL_TEXTURE_WRAP_T, glu::getGLWrapMode(sampler.wrapT));
599 		gl.texParameteri(texTarget, GL_TEXTURE_MIN_FILTER, glu::getGLFilterMode(sampler.minFilter));
600 		gl.texParameteri(texTarget, GL_TEXTURE_MAG_FILTER, glu::getGLFilterMode(sampler.magFilter));
601 
602 		if (texTarget == GL_TEXTURE_3D)
603 			gl.texParameteri(texTarget, GL_TEXTURE_WRAP_R, glu::getGLWrapMode(sampler.wrapR));
604 
605 		if (sampler.compare != tcu::Sampler::COMPAREMODE_NONE)
606 		{
607 			gl.texParameteri(texTarget, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
608 			gl.texParameteri(texTarget, GL_TEXTURE_COMPARE_FUNC, glu::getGLCompareFunc(sampler.compare));
609 		}
610 	}
611 
612 	GLU_EXPECT_NO_ERROR(gl.getError(), "texture sampler setup");
613 }
614 
getDefaultVertexArrays(const glw::Functions & gl,const QuadGrid & quadGrid,deUint32 program,vector<VertexArrayBinding> & vertexArrays)615 static void getDefaultVertexArrays(const glw::Functions& gl, const QuadGrid& quadGrid, deUint32 program,
616 								   vector<VertexArrayBinding>& vertexArrays)
617 {
618 	const int numElements = quadGrid.getNumVertices();
619 
620 	vertexArrays.push_back(va::Float("a_position", 4, numElements, 0, (const float*)quadGrid.getPositions()));
621 	vertexArrays.push_back(va::Float("a_coords", 4, numElements, 0, (const float*)quadGrid.getCoords()));
622 	vertexArrays.push_back(va::Float("a_unitCoords", 4, numElements, 0, (const float*)quadGrid.getUnitCoords()));
623 	vertexArrays.push_back(va::Float("a_one", 1, numElements, 0, quadGrid.getAttribOne()));
624 
625 	// a_inN.
626 	for (int userNdx = 0; userNdx < quadGrid.getNumUserAttribs(); userNdx++)
627 	{
628 		string name = string("a_in") + de::toString(userNdx);
629 		vertexArrays.push_back(va::Float(name, 4, numElements, 0, (const float*)quadGrid.getUserAttrib(userNdx)));
630 	}
631 
632 	// Matrix attributes - these are set by location
633 	static const struct
634 	{
635 		const char* name;
636 		int			numCols;
637 		int			numRows;
638 	} matrices[] = { { "a_mat2", 2, 2 },   { "a_mat2x3", 2, 3 }, { "a_mat2x4", 2, 4 },
639 					 { "a_mat3x2", 3, 2 }, { "a_mat3", 3, 3 },   { "a_mat3x4", 3, 4 },
640 					 { "a_mat4x2", 4, 2 }, { "a_mat4x3", 4, 3 }, { "a_mat4", 4, 4 } };
641 
642 	for (int matNdx = 0; matNdx < DE_LENGTH_OF_ARRAY(matrices); matNdx++)
643 	{
644 		int loc = gl.getAttribLocation(program, matrices[matNdx].name);
645 
646 		if (loc < 0)
647 			continue; // Not used in shader.
648 
649 		int numRows = matrices[matNdx].numRows;
650 		int numCols = matrices[matNdx].numCols;
651 
652 		for (int colNdx = 0; colNdx < numCols; colNdx++)
653 			vertexArrays.push_back(va::Float(loc + colNdx, numRows, numElements, 4 * (int)sizeof(float),
654 											 (const float*)quadGrid.getUserAttrib(colNdx)));
655 	}
656 }
657 
render(Surface & result,int programID,const QuadGrid & quadGrid)658 void ShaderRenderCase::render(Surface& result, int programID, const QuadGrid& quadGrid)
659 {
660 	const glw::Functions& gl = m_renderCtx.getFunctions();
661 
662 	GLU_EXPECT_NO_ERROR(gl.getError(), "pre render");
663 
664 	// Buffer info.
665 	int width  = result.getWidth();
666 	int height = result.getHeight();
667 
668 	int xOffsetMax = m_renderCtx.getRenderTarget().getWidth() - width;
669 	int yOffsetMax = m_renderCtx.getRenderTarget().getHeight() - height;
670 
671 	deUint32   hash = deStringHash(m_vertShaderSource.c_str()) + deStringHash(m_fragShaderSource.c_str());
672 	de::Random rnd(hash);
673 
674 	int xOffset = rnd.getInt(0, xOffsetMax);
675 	int yOffset = rnd.getInt(0, yOffsetMax);
676 
677 	gl.viewport(xOffset, yOffset, width, height);
678 
679 	// Setup program.
680 	setupUniforms(programID, quadGrid.getConstCoords());
681 	setupDefaultInputs(programID);
682 
683 	// Disable dither.
684 	gl.disable(GL_DITHER);
685 
686 	// Clear.
687 	gl.clearColor(m_clearColor.x(), m_clearColor.y(), m_clearColor.z(), m_clearColor.w());
688 	gl.clear(GL_COLOR_BUFFER_BIT);
689 
690 	// Draw.
691 	{
692 		std::vector<VertexArrayBinding> vertexArrays;
693 		const int						numElements = quadGrid.getNumTriangles() * 3;
694 
695 		getDefaultVertexArrays(gl, quadGrid, programID, vertexArrays);
696 		draw(m_renderCtx, programID, (int)vertexArrays.size(), &vertexArrays[0],
697 			 pr::Triangles(numElements, quadGrid.getIndices()));
698 	}
699 	GLU_EXPECT_NO_ERROR(gl.getError(), "draw");
700 
701 	// Read back results.
702 	glu::readPixels(m_renderCtx, xOffset, yOffset, result.getAccess());
703 
704 	GLU_EXPECT_NO_ERROR(gl.getError(), "post render");
705 }
706 
computeVertexReference(Surface & result,const QuadGrid & quadGrid)707 void ShaderRenderCase::computeVertexReference(Surface& result, const QuadGrid& quadGrid)
708 {
709 	// Buffer info.
710 	int				  width	= result.getWidth();
711 	int				  height   = result.getHeight();
712 	int				  gridSize = quadGrid.getGridSize();
713 	int				  stride   = gridSize + 1;
714 	bool			  hasAlpha = m_renderCtx.getRenderTarget().getPixelFormat().alphaBits > 0;
715 	ShaderEvalContext evalCtx(quadGrid);
716 
717 	// Evaluate color for each vertex.
718 	vector<Vec4> colors((gridSize + 1) * (gridSize + 1));
719 	for (int y = 0; y < gridSize + 1; y++)
720 		for (int x = 0; x < gridSize + 1; x++)
721 		{
722 			float sx	 = static_cast<float>(x) / static_cast<float>(gridSize);
723 			float sy	 = static_cast<float>(y) / static_cast<float>(gridSize);
724 			int   vtxNdx = ((y * (gridSize + 1)) + x);
725 
726 			evalCtx.reset(sx, sy);
727 			m_evaluator.evaluate(evalCtx);
728 			DE_ASSERT(!evalCtx.isDiscarded); // Discard is not available in vertex shader.
729 			Vec4 color = evalCtx.color;
730 
731 			if (!hasAlpha)
732 				color.w() = 1.0f;
733 
734 			colors[vtxNdx] = color;
735 		}
736 
737 	// Render quads.
738 	for (int y = 0; y < gridSize; y++)
739 		for (int x = 0; x < gridSize; x++)
740 		{
741 			float x0 = static_cast<float>(x) / static_cast<float>(gridSize);
742 			float x1 = static_cast<float>(x + 1) / static_cast<float>(gridSize);
743 			float y0 = static_cast<float>(y) / static_cast<float>(gridSize);
744 			float y1 = static_cast<float>(y + 1) / static_cast<float>(gridSize);
745 
746 			float sx0  = x0 * (float)width;
747 			float sx1  = x1 * (float)width;
748 			float sy0  = y0 * (float)height;
749 			float sy1  = y1 * (float)height;
750 			float oosx = 1.0f / (sx1 - sx0);
751 			float oosy = 1.0f / (sy1 - sy0);
752 
753 			int ix0 = deCeilFloatToInt32(sx0 - 0.5f);
754 			int ix1 = deCeilFloatToInt32(sx1 - 0.5f);
755 			int iy0 = deCeilFloatToInt32(sy0 - 0.5f);
756 			int iy1 = deCeilFloatToInt32(sy1 - 0.5f);
757 
758 			int  v00 = (y * stride) + x;
759 			int  v01 = (y * stride) + x + 1;
760 			int  v10 = ((y + 1) * stride) + x;
761 			int  v11 = ((y + 1) * stride) + x + 1;
762 			Vec4 c00 = colors[v00];
763 			Vec4 c01 = colors[v01];
764 			Vec4 c10 = colors[v10];
765 			Vec4 c11 = colors[v11];
766 
767 			//printf("(%d,%d) -> (%f..%f, %f..%f) (%d..%d, %d..%d)\n", x, y, sx0, sx1, sy0, sy1, ix0, ix1, iy0, iy1);
768 
769 			for (int iy = iy0; iy < iy1; iy++)
770 				for (int ix = ix0; ix < ix1; ix++)
771 				{
772 					DE_ASSERT(deInBounds32(ix, 0, width));
773 					DE_ASSERT(deInBounds32(iy, 0, height));
774 
775 					float sfx = (float)ix + 0.5f;
776 					float sfy = (float)iy + 0.5f;
777 					float fx1 = deFloatClamp((sfx - sx0) * oosx, 0.0f, 1.0f);
778 					float fy1 = deFloatClamp((sfy - sy0) * oosy, 0.0f, 1.0f);
779 
780 					// Triangle quad interpolation.
781 					bool		tri   = fx1 + fy1 <= 1.0f;
782 					float		tx	= tri ? fx1 : (1.0f - fx1);
783 					float		ty	= tri ? fy1 : (1.0f - fy1);
784 					const Vec4& t0	= tri ? c00 : c11;
785 					const Vec4& t1	= tri ? c01 : c10;
786 					const Vec4& t2	= tri ? c10 : c01;
787 					Vec4		color = t0 + (t1 - t0) * tx + (t2 - t0) * ty;
788 
789 					// Quantizing for 1-bit alpha
790 					if ((m_renderCtx.getRenderTarget().getPixelFormat().alphaBits) == 1)
791 						color.w() = deFloatRound(color.w());
792 
793 					result.setPixel(ix, iy, toRGBA(color));
794 				}
795 		}
796 }
797 
computeFragmentReference(Surface & result,const QuadGrid & quadGrid)798 void ShaderRenderCase::computeFragmentReference(Surface& result, const QuadGrid& quadGrid)
799 {
800 	// Buffer info.
801 	int				  width	= result.getWidth();
802 	int				  height   = result.getHeight();
803 	bool			  hasAlpha = m_renderCtx.getRenderTarget().getPixelFormat().alphaBits > 0;
804 	ShaderEvalContext evalCtx(quadGrid);
805 
806 	// Render.
807 	for (int y = 0; y < height; y++)
808 		for (int x = 0; x < width; x++)
809 		{
810 			float sx = ((float)x + 0.5f) / (float)width;
811 			float sy = ((float)y + 0.5f) / (float)height;
812 
813 			evalCtx.reset(sx, sy);
814 			m_evaluator.evaluate(evalCtx);
815 			// Select either clear color or computed color based on discarded bit.
816 			Vec4 color = evalCtx.isDiscarded ? m_clearColor : evalCtx.color;
817 
818 			if (!hasAlpha)
819 				color.w() = 1.0f;
820 
821 			// Quantizing for 1-bit alpha
822 			if ((m_renderCtx.getRenderTarget().getPixelFormat().alphaBits) == 1)
823 				color.w() = deFloatRound(color.w());
824 
825 			result.setPixel(x, y, toRGBA(color));
826 		}
827 }
828 
compareImages(const Surface & resImage,const Surface & refImage,float errorThreshold)829 bool ShaderRenderCase::compareImages(const Surface& resImage, const Surface& refImage, float errorThreshold)
830 {
831 	return tcu::fuzzyCompare(m_testCtx.getLog(), "ComparisonResult", "Image comparison result", refImage, resImage,
832 							 errorThreshold, tcu::COMPARE_LOG_RESULT);
833 }
834 
835 // Uniform name helpers.
836 
getIntUniformName(int number)837 const char* getIntUniformName(int number)
838 {
839 	switch (number)
840 	{
841 	case 0:
842 		return "ui_zero";
843 	case 1:
844 		return "ui_one";
845 	case 2:
846 		return "ui_two";
847 	case 3:
848 		return "ui_three";
849 	case 4:
850 		return "ui_four";
851 	case 5:
852 		return "ui_five";
853 	case 6:
854 		return "ui_six";
855 	case 7:
856 		return "ui_seven";
857 	case 8:
858 		return "ui_eight";
859 	case 101:
860 		return "ui_oneHundredOne";
861 	default:
862 		DE_ASSERT(false);
863 		return "";
864 	}
865 }
866 
getFloatUniformName(int number)867 const char* getFloatUniformName(int number)
868 {
869 	switch (number)
870 	{
871 	case 0:
872 		return "uf_zero";
873 	case 1:
874 		return "uf_one";
875 	case 2:
876 		return "uf_two";
877 	case 3:
878 		return "uf_three";
879 	case 4:
880 		return "uf_four";
881 	case 5:
882 		return "uf_five";
883 	case 6:
884 		return "uf_six";
885 	case 7:
886 		return "uf_seven";
887 	case 8:
888 		return "uf_eight";
889 	default:
890 		DE_ASSERT(false);
891 		return "";
892 	}
893 }
894 
getFloatFractionUniformName(int number)895 const char* getFloatFractionUniformName(int number)
896 {
897 	switch (number)
898 	{
899 	case 1:
900 		return "uf_one";
901 	case 2:
902 		return "uf_half";
903 	case 3:
904 		return "uf_third";
905 	case 4:
906 		return "uf_fourth";
907 	case 5:
908 		return "uf_fifth";
909 	case 6:
910 		return "uf_sixth";
911 	case 7:
912 		return "uf_seventh";
913 	case 8:
914 		return "uf_eighth";
915 	default:
916 		DE_ASSERT(false);
917 		return "";
918 	}
919 }
920 
setupDefaultUniforms(const glu::RenderContext & context,deUint32 programID)921 void setupDefaultUniforms(const glu::RenderContext& context, deUint32 programID)
922 {
923 	const glw::Functions& gl = context.getFunctions();
924 
925 	// Bool.
926 	struct BoolUniform
927 	{
928 		const char* name;
929 		bool		value;
930 	};
931 	static const BoolUniform s_boolUniforms[] = {
932 		{ "ub_true", true }, { "ub_false", false },
933 	};
934 
935 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_boolUniforms); i++)
936 	{
937 		int uniLoc = gl.getUniformLocation(programID, s_boolUniforms[i].name);
938 		if (uniLoc != -1)
939 			gl.uniform1i(uniLoc, s_boolUniforms[i].value);
940 	}
941 
942 	// BVec4.
943 	struct BVec4Uniform
944 	{
945 		const char* name;
946 		BVec4		value;
947 	};
948 	static const BVec4Uniform s_bvec4Uniforms[] = {
949 		{ "ub4_true", BVec4(true) }, { "ub4_false", BVec4(false) },
950 	};
951 
952 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_bvec4Uniforms); i++)
953 	{
954 		const BVec4Uniform& uni = s_bvec4Uniforms[i];
955 		int					arr[4];
956 		arr[0]	 = (int)uni.value.x();
957 		arr[1]	 = (int)uni.value.y();
958 		arr[2]	 = (int)uni.value.z();
959 		arr[3]	 = (int)uni.value.w();
960 		int uniLoc = gl.getUniformLocation(programID, uni.name);
961 		if (uniLoc != -1)
962 			gl.uniform4iv(uniLoc, 1, &arr[0]);
963 	}
964 
965 	// Int.
966 	struct IntUniform
967 	{
968 		const char* name;
969 		int			value;
970 	};
971 	static const IntUniform s_intUniforms[] = {
972 		{ "ui_minusOne", -1 },		{ "ui_zero", 0 }, { "ui_one", 1 }, { "ui_two", 2 },   { "ui_three", 3 },
973 		{ "ui_four", 4 },			{ "ui_five", 5 }, { "ui_six", 6 }, { "ui_seven", 7 }, { "ui_eight", 8 },
974 		{ "ui_oneHundredOne", 101 }
975 	};
976 
977 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_intUniforms); i++)
978 	{
979 		int uniLoc = gl.getUniformLocation(programID, s_intUniforms[i].name);
980 		if (uniLoc != -1)
981 			gl.uniform1i(uniLoc, s_intUniforms[i].value);
982 	}
983 
984 	// IVec2.
985 	struct IVec2Uniform
986 	{
987 		const char* name;
988 		IVec2		value;
989 	};
990 	static const IVec2Uniform s_ivec2Uniforms[] = { { "ui2_minusOne", IVec2(-1) }, { "ui2_zero", IVec2(0) },
991 													{ "ui2_one", IVec2(1) },	   { "ui2_two", IVec2(2) },
992 													{ "ui2_four", IVec2(4) },	  { "ui2_five", IVec2(5) } };
993 
994 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_ivec2Uniforms); i++)
995 	{
996 		int uniLoc = gl.getUniformLocation(programID, s_ivec2Uniforms[i].name);
997 		if (uniLoc != -1)
998 			gl.uniform2iv(uniLoc, 1, s_ivec2Uniforms[i].value.getPtr());
999 	}
1000 
1001 	// IVec3.
1002 	struct IVec3Uniform
1003 	{
1004 		const char* name;
1005 		IVec3		value;
1006 	};
1007 	static const IVec3Uniform s_ivec3Uniforms[] = { { "ui3_minusOne", IVec3(-1) }, { "ui3_zero", IVec3(0) },
1008 													{ "ui3_one", IVec3(1) },	   { "ui3_two", IVec3(2) },
1009 													{ "ui3_four", IVec3(4) },	  { "ui3_five", IVec3(5) } };
1010 
1011 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_ivec3Uniforms); i++)
1012 	{
1013 		int uniLoc = gl.getUniformLocation(programID, s_ivec3Uniforms[i].name);
1014 		if (uniLoc != -1)
1015 			gl.uniform3iv(uniLoc, 1, s_ivec3Uniforms[i].value.getPtr());
1016 	}
1017 
1018 	// IVec4.
1019 	struct IVec4Uniform
1020 	{
1021 		const char* name;
1022 		IVec4		value;
1023 	};
1024 	static const IVec4Uniform s_ivec4Uniforms[] = { { "ui4_minusOne", IVec4(-1) }, { "ui4_zero", IVec4(0) },
1025 													{ "ui4_one", IVec4(1) },	   { "ui4_two", IVec4(2) },
1026 													{ "ui4_four", IVec4(4) },	  { "ui4_five", IVec4(5) } };
1027 
1028 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_ivec4Uniforms); i++)
1029 	{
1030 		int uniLoc = gl.getUniformLocation(programID, s_ivec4Uniforms[i].name);
1031 		if (uniLoc != -1)
1032 			gl.uniform4iv(uniLoc, 1, s_ivec4Uniforms[i].value.getPtr());
1033 	}
1034 
1035 	// Float.
1036 	struct FloatUniform
1037 	{
1038 		const char* name;
1039 		float		value;
1040 	};
1041 	static const FloatUniform s_floatUniforms[] = {
1042 		{ "uf_zero", 0.0f },		 { "uf_one", 1.0f },		  { "uf_two", 2.0f },
1043 		{ "uf_three", 3.0f },		 { "uf_four", 4.0f },		  { "uf_five", 5.0f },
1044 		{ "uf_six", 6.0f },			 { "uf_seven", 7.0f },		  { "uf_eight", 8.0f },
1045 		{ "uf_half", 1.0f / 2.0f },  { "uf_third", 1.0f / 3.0f }, { "uf_fourth", 1.0f / 4.0f },
1046 		{ "uf_fifth", 1.0f / 5.0f }, { "uf_sixth", 1.0f / 6.0f }, { "uf_seventh", 1.0f / 7.0f },
1047 		{ "uf_eighth", 1.0f / 8.0f }
1048 	};
1049 
1050 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_floatUniforms); i++)
1051 	{
1052 		int uniLoc = gl.getUniformLocation(programID, s_floatUniforms[i].name);
1053 		if (uniLoc != -1)
1054 			gl.uniform1f(uniLoc, s_floatUniforms[i].value);
1055 	}
1056 
1057 	// Vec2.
1058 	struct Vec2Uniform
1059 	{
1060 		const char* name;
1061 		Vec2		value;
1062 	};
1063 	static const Vec2Uniform s_vec2Uniforms[] = {
1064 		{ "uv2_minusOne", Vec2(-1.0f) }, { "uv2_zero", Vec2(0.0f) }, { "uv2_half", Vec2(0.5f) },
1065 		{ "uv2_one", Vec2(1.0f) },		 { "uv2_two", Vec2(2.0f) },
1066 	};
1067 
1068 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_vec2Uniforms); i++)
1069 	{
1070 		int uniLoc = gl.getUniformLocation(programID, s_vec2Uniforms[i].name);
1071 		if (uniLoc != -1)
1072 			gl.uniform2fv(uniLoc, 1, s_vec2Uniforms[i].value.getPtr());
1073 	}
1074 
1075 	// Vec3.
1076 	struct Vec3Uniform
1077 	{
1078 		const char* name;
1079 		Vec3		value;
1080 	};
1081 	static const Vec3Uniform s_vec3Uniforms[] = {
1082 		{ "uv3_minusOne", Vec3(-1.0f) }, { "uv3_zero", Vec3(0.0f) }, { "uv3_half", Vec3(0.5f) },
1083 		{ "uv3_one", Vec3(1.0f) },		 { "uv3_two", Vec3(2.0f) },
1084 	};
1085 
1086 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_vec3Uniforms); i++)
1087 	{
1088 		int uniLoc = gl.getUniformLocation(programID, s_vec3Uniforms[i].name);
1089 		if (uniLoc != -1)
1090 			gl.uniform3fv(uniLoc, 1, s_vec3Uniforms[i].value.getPtr());
1091 	}
1092 
1093 	// Vec4.
1094 	struct Vec4Uniform
1095 	{
1096 		const char* name;
1097 		Vec4		value;
1098 	};
1099 	static const Vec4Uniform s_vec4Uniforms[] = {
1100 		{ "uv4_minusOne", Vec4(-1.0f) },
1101 		{ "uv4_zero", Vec4(0.0f) },
1102 		{ "uv4_half", Vec4(0.5f) },
1103 		{ "uv4_one", Vec4(1.0f) },
1104 		{ "uv4_two", Vec4(2.0f) },
1105 		{ "uv4_black", Vec4(0.0f, 0.0f, 0.0f, 1.0f) },
1106 		{ "uv4_gray", Vec4(0.5f, 0.5f, 0.5f, 1.0f) },
1107 		{ "uv4_white", Vec4(1.0f, 1.0f, 1.0f, 1.0f) },
1108 	};
1109 
1110 	for (int i = 0; i < DE_LENGTH_OF_ARRAY(s_vec4Uniforms); i++)
1111 	{
1112 		int uniLoc = gl.getUniformLocation(programID, s_vec4Uniforms[i].name);
1113 		if (uniLoc != -1)
1114 			gl.uniform4fv(uniLoc, 1, s_vec4Uniforms[i].value.getPtr());
1115 	}
1116 }
1117 
1118 } // deqp
1119