1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.1 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 Multisample interpolation tests
22 *//*--------------------------------------------------------------------*/
23
24#include "es31fShaderMultisampleInterpolationTests.hpp"
25#include "es31fMultisampleShaderRenderCase.hpp"
26#include "tcuTestLog.hpp"
27#include "tcuRGBA.hpp"
28#include "tcuSurface.hpp"
29#include "tcuRenderTarget.hpp"
30#include "gluContextInfo.hpp"
31#include "gluShaderProgram.hpp"
32#include "gluRenderContext.hpp"
33#include "glwFunctions.hpp"
34#include "glwEnums.hpp"
35#include "deStringUtil.hpp"
36#include "deMath.h"
37
38#include <map>
39
40namespace deqp
41{
42namespace gles31
43{
44namespace Functional
45{
46namespace
47{
48
49
50static bool verifyGreenImage (const tcu::Surface& image, tcu::TestLog& log)
51{
52	bool error = false;
53
54	log << tcu::TestLog::Message << "Verifying result image, expecting green." << tcu::TestLog::EndMessage;
55
56	// all pixels must be green
57
58	for (int y = 0; y < image.getHeight(); ++y)
59	for (int x = 0; x < image.getWidth(); ++x)
60	{
61		const tcu::RGBA color			= image.getPixel(x, y);
62		const int		greenThreshold	= 8;
63
64		if (color.getRed() > 0 || color.getGreen() < 255-greenThreshold || color.getBlue() > 0)
65			error = true;
66	}
67
68	if (error)
69		log	<< tcu::TestLog::Image("ResultImage", "Result Image", image.getAccess())
70			<< tcu::TestLog::Message
71			<< "Image verification failed."
72			<< tcu::TestLog::EndMessage;
73	else
74		log	<< tcu::TestLog::Image("ResultImage", "Result Image", image.getAccess())
75			<< tcu::TestLog::Message
76			<< "Image verification passed."
77			<< tcu::TestLog::EndMessage;
78
79	return !error;
80}
81
82class MultisampleShadeCountRenderCase : public MultisampleShaderRenderUtil::MultisampleRenderCase
83{
84public:
85						MultisampleShadeCountRenderCase		(Context& context, const char* name, const char* description, int numSamples, RenderTarget target);
86	virtual				~MultisampleShadeCountRenderCase	(void);
87
88	void				init								(void);
89
90private:
91	enum
92	{
93		RENDER_SIZE = 128
94	};
95
96	virtual std::string	getIterationDescription				(int iteration) const;
97	bool				verifyImage							(const tcu::Surface& resultImage);
98};
99
100MultisampleShadeCountRenderCase::MultisampleShadeCountRenderCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target)
101	: MultisampleShaderRenderUtil::MultisampleRenderCase(context, name, description, numSamples, target, RENDER_SIZE, MultisampleShaderRenderUtil::MultisampleRenderCase::FLAG_PER_ITERATION_SHADER)
102{
103	m_numIterations = -1; // must be set by deriving class
104}
105
106MultisampleShadeCountRenderCase::~MultisampleShadeCountRenderCase (void)
107{
108}
109
110void MultisampleShadeCountRenderCase::init (void)
111{
112	// requirements
113	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_multisample_interpolation"))
114		throw tcu::NotSupportedError("Test requires GL_OES_shader_multisample_interpolation extension");
115
116	MultisampleShaderRenderUtil::MultisampleRenderCase::init();
117}
118
119std::string	MultisampleShadeCountRenderCase::getIterationDescription (int iteration) const
120{
121	// must be overriden
122	DE_UNREF(iteration);
123	DE_ASSERT(false);
124	return "";
125}
126
127bool MultisampleShadeCountRenderCase::verifyImage (const tcu::Surface& resultImage)
128{
129	const bool				isSingleSampleTarget	= (m_renderTarget != TARGET_DEFAULT && m_numRequestedSamples == 0) || (m_renderTarget == TARGET_DEFAULT && m_context.getRenderTarget().getNumSamples() <= 1);
130	const int				numShadesRequired		= (isSingleSampleTarget) ? (2) : (m_numTargetSamples + 1);
131	const int				rareThreshold			= 100;
132	int						rareCount				= 0;
133	std::map<deUint32, int>	shadeFrequency;
134
135	m_testCtx.getLog()
136		<< tcu::TestLog::Image("ResultImage", "Result Image", resultImage.getAccess())
137		<< tcu::TestLog::Message
138		<< "Verifying image has (at least) " << numShadesRequired << " different shades.\n"
139		<< "Excluding pixels with no full coverage (pixels on the shared edge of the triangle pair)."
140		<< tcu::TestLog::EndMessage;
141
142	for (int y = 0; y < RENDER_SIZE; ++y)
143	for (int x = 0; x < RENDER_SIZE; ++x)
144	{
145		const tcu::RGBA	color	= resultImage.getPixel(x, y);
146		const deUint32	packed	= ((deUint32)color.getRed()) + ((deUint32)color.getGreen() << 8) + ((deUint32)color.getGreen() << 16);
147
148		// on the triangle edge, skip
149		if (x == y)
150			continue;
151
152		if (shadeFrequency.find(packed) == shadeFrequency.end())
153			shadeFrequency[packed] = 1;
154		else
155			shadeFrequency[packed] = shadeFrequency[packed] + 1;
156	}
157
158	for (std::map<deUint32, int>::const_iterator it = shadeFrequency.begin(); it != shadeFrequency.end(); ++it)
159		if (it->second < rareThreshold)
160			rareCount++;
161
162	m_testCtx.getLog()
163		<< tcu::TestLog::Message
164		<< "Found " << (int)shadeFrequency.size() << " different shades.\n"
165		<< "\tRare (less than " << rareThreshold << " pixels): " << rareCount << "\n"
166		<< "\tCommon: " << (int)shadeFrequency.size() - rareCount << "\n"
167		<< tcu::TestLog::EndMessage;
168
169	if ((int)shadeFrequency.size() < numShadesRequired)
170	{
171		m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed." << tcu::TestLog::EndMessage;
172		return false;
173	}
174	return true;
175}
176
177class SampleQualifierRenderCase : public MultisampleShadeCountRenderCase
178{
179public:
180				SampleQualifierRenderCase	(Context& context, const char* name, const char* description, int numSamples, RenderTarget target);
181				~SampleQualifierRenderCase	(void);
182
183	void		init						(void);
184
185private:
186	std::string	genVertexSource				(int numTargetSamples) const;
187	std::string	genFragmentSource			(int numTargetSamples) const;
188	std::string	getIterationDescription		(int iteration) const;
189};
190
191SampleQualifierRenderCase::SampleQualifierRenderCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target)
192	: MultisampleShadeCountRenderCase(context, name, description, numSamples, target)
193{
194	m_numIterations = 6; // float, vec2, .3, .4, array, struct
195}
196
197SampleQualifierRenderCase::~SampleQualifierRenderCase (void)
198{
199}
200
201void SampleQualifierRenderCase::init (void)
202{
203	const bool isSingleSampleTarget = (m_renderTarget != TARGET_DEFAULT && m_numRequestedSamples == 0) || (m_renderTarget == TARGET_DEFAULT && m_context.getRenderTarget().getNumSamples() <= 1);
204
205	// test purpose and expectations
206	if (isSingleSampleTarget)
207	{
208		m_testCtx.getLog()
209			<< tcu::TestLog::Message
210			<< "Verifying that a sample-qualified varying is given different values for different samples.\n"
211			<< "	Render high-frequency function, map result to black/white.\n"
212			<< "	=> Resulting image image should contain both black and white pixels.\n"
213			<< tcu::TestLog::EndMessage;
214	}
215	else
216	{
217		m_testCtx.getLog()
218			<< tcu::TestLog::Message
219			<< "Verifying that a sample-qualified varying is given different values for different samples.\n"
220			<< "	Render high-frequency function, map result to black/white.\n"
221			<< "	=> Resulting image should contain n+1 shades of gray, n = sample count.\n"
222			<< tcu::TestLog::EndMessage;
223	}
224
225	MultisampleShadeCountRenderCase::init();
226}
227
228std::string	SampleQualifierRenderCase::genVertexSource (int numTargetSamples) const
229{
230	DE_UNREF(numTargetSamples);
231
232	std::ostringstream buf;
233
234	buf <<	"#version 310 es\n"
235			"#extension GL_OES_shader_multisample_interpolation : require\n"
236			"in highp vec4 a_position;\n";
237
238	if (m_iteration == 0)
239		buf << "sample out highp float v_input;\n";
240	else if (m_iteration == 1)
241		buf << "sample out highp vec2 v_input;\n";
242	else if (m_iteration == 2)
243		buf << "sample out highp vec3 v_input;\n";
244	else if (m_iteration == 3)
245		buf << "sample out highp vec4 v_input;\n";
246	else if (m_iteration == 4)
247		buf << "sample out highp float[2] v_input;\n";
248	else if (m_iteration == 5)
249		buf << "struct VaryingStruct { highp float a; highp float b; };\n"
250			   "sample out VaryingStruct v_input;\n";
251	else
252		DE_ASSERT(false);
253
254	buf <<	"void main (void)\n"
255			"{\n"
256			"	gl_Position = a_position;\n";
257
258	if (m_iteration == 0)
259		buf << "	v_input = a_position.x + exp(a_position.y) + step(0.9, a_position.x)*step(a_position.y, -0.9)*8.0;\n";
260	else if (m_iteration == 1)
261		buf << "	v_input = a_position.xy;\n";
262	else if (m_iteration == 2)
263		buf << "	v_input = vec3(a_position.xy, a_position.x * 2.0 - a_position.y);\n";
264	else if (m_iteration == 3)
265		buf << "	v_input = vec4(a_position.xy, a_position.x * 2.0 - a_position.y, a_position.x*a_position.y);\n";
266	else if (m_iteration == 4)
267		buf << "	v_input[0] = a_position.x;\n"
268			   "	v_input[1] = a_position.y;\n";
269	else if (m_iteration == 5)
270		buf << "	v_input.a = a_position.x;\n"
271			   "	v_input.b = a_position.y;\n";
272	else
273		DE_ASSERT(false);
274
275	buf <<	"}";
276
277	return buf.str();
278}
279
280std::string	SampleQualifierRenderCase::genFragmentSource (int numTargetSamples) const
281{
282	DE_UNREF(numTargetSamples);
283
284	std::ostringstream buf;
285
286	buf <<	"#version 310 es\n"
287			"#extension GL_OES_shader_multisample_interpolation : require\n";
288
289	if (m_iteration == 0)
290		buf << "sample in highp float v_input;\n";
291	else if (m_iteration == 1)
292		buf << "sample in highp vec2 v_input;\n";
293	else if (m_iteration == 2)
294		buf << "sample in highp vec3 v_input;\n";
295	else if (m_iteration == 3)
296		buf << "sample in highp vec4 v_input;\n";
297	else if (m_iteration == 4)
298		buf << "sample in highp float[2] v_input;\n";
299	else if (m_iteration == 5)
300		buf << "struct VaryingStruct { highp float a; highp float b; };\n"
301			   "sample in VaryingStruct v_input;\n";
302	else
303		DE_ASSERT(false);
304
305	buf <<	"layout(location = 0) out mediump vec4 fragColor;\n"
306			"void main (void)\n"
307			"{\n";
308
309	if (m_iteration == 0)
310		buf << "	highp float field = exp(v_input) + v_input*v_input;\n";
311	else if (m_iteration == 1)
312		buf << "	highp float field = dot(v_input.xy, v_input.xy) + dot(21.0 * v_input.xx, sin(3.1 * v_input.xy));\n";
313	else if (m_iteration == 2)
314		buf << "	highp float field = dot(v_input.xy, v_input.xy) + dot(21.0 * v_input.zx, sin(3.1 * v_input.zy));\n";
315	else if (m_iteration == 3)
316		buf << "	highp float field = dot(v_input.xy, v_input.zw) + dot(21.0 * v_input.zy, sin(3.1 * v_input.zw));\n";
317	else if (m_iteration == 4)
318		buf << "	highp float field = dot(vec2(v_input[0], v_input[1]), vec2(v_input[0], v_input[1])) + dot(21.0 * vec2(v_input[0]), sin(3.1 * vec2(v_input[0], v_input[1])));\n";
319	else if (m_iteration == 5)
320		buf << "	highp float field = dot(vec2(v_input.a, v_input.b), vec2(v_input.a, v_input.b)) + dot(21.0 * vec2(v_input.a), sin(3.1 * vec2(v_input.a, v_input.b)));\n";
321	else
322		DE_ASSERT(false);
323
324	buf <<	"	fragColor = vec4(1.0, 1.0, 1.0, 1.0);\n"
325			"\n"
326			"	if (fract(field) > 0.5)\n"
327			"		fragColor = vec4(0.0, 0.0, 0.0, 1.0);\n"
328			"}";
329
330	return buf.str();
331}
332
333std::string	SampleQualifierRenderCase::getIterationDescription (int iteration) const
334{
335	if (iteration == 0)
336		return "Test with float varying";
337	else if (iteration == 1)
338		return "Test with vec2 varying";
339	else if (iteration == 2)
340		return "Test with vec3 varying";
341	else if (iteration == 3)
342		return "Test with vec4 varying";
343	else if (iteration == 4)
344		return "Test with array varying";
345	else if (iteration == 5)
346		return "Test with struct varying";
347
348	DE_ASSERT(false);
349	return "";
350}
351
352class InterpolateAtSampleRenderCase : public MultisampleShadeCountRenderCase
353{
354public:
355	enum IndexingMode
356	{
357		INDEXING_STATIC,
358		INDEXING_DYNAMIC,
359
360		INDEXING_LAST
361	};
362						InterpolateAtSampleRenderCase	(Context& context, const char* name, const char* description, int numSamples, RenderTarget target, IndexingMode mode);
363						~InterpolateAtSampleRenderCase	(void);
364
365	void				init							(void);
366	void				preDraw							(void);
367
368private:
369	std::string			genVertexSource					(int numTargetSamples) const;
370	std::string			genFragmentSource				(int numTargetSamples) const;
371	std::string			getIterationDescription			(int iteration) const;
372
373	const IndexingMode	m_indexMode;
374};
375
376InterpolateAtSampleRenderCase::InterpolateAtSampleRenderCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target, IndexingMode mode)
377	: MultisampleShadeCountRenderCase	(context, name, description, numSamples, target)
378	, m_indexMode						(mode)
379{
380	DE_ASSERT(mode < INDEXING_LAST);
381
382	m_numIterations = 5; // float, vec2, .3, .4, array
383}
384
385InterpolateAtSampleRenderCase::~InterpolateAtSampleRenderCase (void)
386{
387}
388
389void InterpolateAtSampleRenderCase::init (void)
390{
391	const bool isSingleSampleTarget = (m_renderTarget != TARGET_DEFAULT && m_numRequestedSamples == 0) || (m_renderTarget == TARGET_DEFAULT && m_context.getRenderTarget().getNumSamples() <= 1);
392
393	// test purpose and expectations
394	if (isSingleSampleTarget)
395	{
396		m_testCtx.getLog()
397			<< tcu::TestLog::Message
398			<< "Verifying that a interpolateAtSample returns different values for different samples.\n"
399			<< "	Render high-frequency function, map result to black/white.\n"
400			<< "	=> Resulting image image should contain both black and white pixels.\n"
401			<< tcu::TestLog::EndMessage;
402	}
403	else
404	{
405		m_testCtx.getLog()
406			<< tcu::TestLog::Message
407			<< "Verifying that a interpolateAtSample returns different values for different samples.\n"
408			<< "	Render high-frequency function, map result to black/white.\n"
409			<< "	=> Resulting image should contain n+1 shades of gray, n = sample count.\n"
410			<< tcu::TestLog::EndMessage;
411	}
412
413	MultisampleShadeCountRenderCase::init();
414}
415
416void InterpolateAtSampleRenderCase::preDraw (void)
417{
418	if (m_indexMode == INDEXING_DYNAMIC)
419	{
420		const deInt32			range		= m_numTargetSamples;
421		const deInt32			offset		= 1;
422		const glw::Functions&	gl			= m_context.getRenderContext().getFunctions();
423		const deInt32			offsetLoc	= gl.getUniformLocation(m_program->getProgram(), "u_offset");
424		const deInt32			rangeLoc	= gl.getUniformLocation(m_program->getProgram(), "u_range");
425
426		if (offsetLoc == -1)
427			throw tcu::TestError("Location of u_offset was -1");
428		if (rangeLoc == -1)
429			throw tcu::TestError("Location of u_range was -1");
430
431		gl.uniform1i(offsetLoc, 0);
432		gl.uniform1i(rangeLoc, m_numTargetSamples);
433		GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms");
434
435		m_testCtx.getLog()
436			<< tcu::TestLog::Message
437			<< "Set u_offset = " << offset << "\n"
438			<< "Set u_range = " << range
439			<< tcu::TestLog::EndMessage;
440	}
441}
442
443std::string InterpolateAtSampleRenderCase::genVertexSource (int numTargetSamples) const
444{
445	DE_UNREF(numTargetSamples);
446
447	std::ostringstream buf;
448
449	buf <<	"#version 310 es\n"
450			"in highp vec4 a_position;\n";
451
452	if (m_iteration == 0)
453		buf << "out highp float v_input;\n";
454	else if (m_iteration == 1)
455		buf << "out highp vec2 v_input;\n";
456	else if (m_iteration == 2)
457		buf << "out highp vec3 v_input;\n";
458	else if (m_iteration == 3)
459		buf << "out highp vec4 v_input;\n";
460	else if (m_iteration == 4)
461		buf << "out highp vec2[2] v_input;\n";
462	else
463		DE_ASSERT(false);
464
465	buf <<	"void main (void)\n"
466			"{\n"
467			"	gl_Position = a_position;\n";
468
469	if (m_iteration == 0)
470		buf << "	v_input = a_position.x + exp(a_position.y) + step(0.9, a_position.x)*step(a_position.y, -0.9)*8.0;\n";
471	else if (m_iteration == 1)
472		buf << "	v_input = a_position.xy;\n";
473	else if (m_iteration == 2)
474		buf << "	v_input = vec3(a_position.xy, a_position.x * 2.0 - a_position.y);\n";
475	else if (m_iteration == 3)
476		buf << "	v_input = vec4(a_position.xy, a_position.x * 2.0 - a_position.y, a_position.x*a_position.y);\n";
477	else if (m_iteration == 4)
478		buf << "	v_input[0] = a_position.yx + vec2(0.5, 0.5);\n"
479			   "	v_input[1] = a_position.xy;\n";
480	else
481		DE_ASSERT(false);
482
483	buf <<	"}";
484
485	return buf.str();
486}
487
488std::string InterpolateAtSampleRenderCase::genFragmentSource (int numTargetSamples) const
489{
490	std::ostringstream buf;
491
492	buf <<	"#version 310 es\n"
493			"#extension GL_OES_shader_multisample_interpolation : require\n";
494
495	if (m_iteration == 0)
496		buf << "in highp float v_input;\n";
497	else if (m_iteration == 1)
498		buf << "in highp vec2 v_input;\n";
499	else if (m_iteration == 2)
500		buf << "in highp vec3 v_input;\n";
501	else if (m_iteration == 3)
502		buf << "in highp vec4 v_input;\n";
503	else if (m_iteration == 4)
504		buf << "in highp vec2[2] v_input;\n";
505	else
506		DE_ASSERT(false);
507
508	buf << "layout(location = 0) out mediump vec4 fragColor;\n";
509
510	if (m_indexMode == INDEXING_DYNAMIC)
511		buf <<	"uniform highp int u_offset;\n"
512				"uniform highp int u_range;\n";
513
514	buf <<	"void main (void)\n"
515			"{\n"
516			"	mediump int coverage = 0;\n"
517			"\n";
518
519	if (m_indexMode == INDEXING_STATIC)
520	{
521		for (int ndx = 0; ndx < numTargetSamples; ++ndx)
522		{
523			if (m_iteration == 0)
524				buf <<	"	highp float sampleInput" << ndx << " = interpolateAtSample(v_input, " << ndx << ");\n";
525			else if (m_iteration == 1)
526				buf <<	"	highp vec2 sampleInput" << ndx << " = interpolateAtSample(v_input, " << ndx << ");\n";
527			else if (m_iteration == 2)
528				buf <<	"	highp vec3 sampleInput" << ndx << " = interpolateAtSample(v_input, " << ndx << ");\n";
529			else if (m_iteration == 3)
530				buf <<	"	highp vec4 sampleInput" << ndx << " = interpolateAtSample(v_input, " << ndx << ");\n";
531			else if (m_iteration == 4)
532				buf <<	"	highp vec2 sampleInput" << ndx << " = interpolateAtSample(v_input[1], " << ndx << ");\n";
533			else
534				DE_ASSERT(false);
535		}
536		buf <<	"\n";
537
538		for (int ndx = 0; ndx < numTargetSamples; ++ndx)
539		{
540			if (m_iteration == 0)
541				buf << "	highp float field" << ndx << " = exp(sampleInput" << ndx << ") + sampleInput" << ndx << "*sampleInput" << ndx << ";\n";
542			else if (m_iteration == 1 || m_iteration == 4)
543				buf << "	highp float field" << ndx << " = dot(sampleInput" << ndx << ", sampleInput" << ndx << ") + dot(21.0 * sampleInput" << ndx << ".xx, sin(3.1 * sampleInput" << ndx << "));\n";
544			else if (m_iteration == 2)
545				buf << "	highp float field" << ndx << " = dot(sampleInput" << ndx << ".xy, sampleInput" << ndx << ".xy) + dot(21.0 * sampleInput" << ndx << ".zx, sin(3.1 * sampleInput" << ndx << ".zy));\n";
546			else if (m_iteration == 3)
547				buf << "	highp float field" << ndx << " = dot(sampleInput" << ndx << ".xy, sampleInput" << ndx << ".zw) + dot(21.0 * sampleInput" << ndx << ".zy, sin(3.1 * sampleInput" << ndx << ".zw));\n";
548			else
549				DE_ASSERT(false);
550		}
551		buf <<	"\n";
552
553		for (int ndx = 0; ndx < numTargetSamples; ++ndx)
554			buf <<	"	if (fract(field" << ndx << ") <= 0.5)\n"
555					"		++coverage;\n";
556	}
557	else if (m_indexMode == INDEXING_DYNAMIC)
558	{
559		buf <<	"	for (int ndx = 0; ndx < " << numTargetSamples << "; ++ndx)\n"
560				"	{\n";
561
562		if (m_iteration == 0)
563			buf <<	"		highp float sampleInput = interpolateAtSample(v_input, (u_offset + ndx) % u_range);\n";
564		else if (m_iteration == 1)
565			buf <<	"		highp vec2 sampleInput = interpolateAtSample(v_input, (u_offset + ndx) % u_range);\n";
566		else if (m_iteration == 2)
567			buf <<	"		highp vec3 sampleInput = interpolateAtSample(v_input, (u_offset + ndx) % u_range);\n";
568		else if (m_iteration == 3)
569			buf <<	"		highp vec4 sampleInput = interpolateAtSample(v_input, (u_offset + ndx) % u_range);\n";
570		else if (m_iteration == 4)
571			buf <<	"		highp vec2 sampleInput = interpolateAtSample(v_input[1], (u_offset + ndx) % u_range);\n";
572		else
573			DE_ASSERT(false);
574
575		if (m_iteration == 0)
576			buf << "		highp float field = exp(sampleInput) + sampleInput*sampleInput;\n";
577		else if (m_iteration == 1 || m_iteration == 4)
578			buf << "		highp float field = dot(sampleInput, sampleInput) + dot(21.0 * sampleInput.xx, sin(3.1 * sampleInput));\n";
579		else if (m_iteration == 2)
580			buf << "		highp float field = dot(sampleInput.xy, sampleInput.xy) + dot(21.0 * sampleInput.zx, sin(3.1 * sampleInput.zy));\n";
581		else if (m_iteration == 3)
582			buf << "		highp float field = dot(sampleInput.xy, sampleInput.zw) + dot(21.0 * sampleInput.zy, sin(3.1 * sampleInput.zw));\n";
583		else
584			DE_ASSERT(false);
585
586		buf <<	"		if (fract(field) <= 0.5)\n"
587				"			++coverage;\n"
588				"	}\n";
589	}
590
591	buf <<	"	fragColor = vec4(vec3(float(coverage) / float(" << numTargetSamples << ")), 1.0);\n"
592			"}";
593
594	return buf.str();
595}
596
597std::string InterpolateAtSampleRenderCase::getIterationDescription (int iteration) const
598{
599	if (iteration == 0)
600		return "Test with float varying";
601	else if (iteration < 4)
602		return "Test with vec" + de::toString(iteration+1) + " varying";
603	else if (iteration == 4)
604		return "Test with array varying";
605
606	DE_ASSERT(false);
607	return "";
608}
609
610class SingleSampleInterpolateAtSampleCase : public MultisampleShaderRenderUtil::MultisampleRenderCase
611{
612public:
613	enum SampleCase
614	{
615		SAMPLE_0 = 0,
616		SAMPLE_N,
617
618		SAMPLE_LAST
619	};
620
621						SingleSampleInterpolateAtSampleCase		(Context& context, const char* name, const char* description, int numSamples, RenderTarget target, SampleCase sampleCase);
622	virtual				~SingleSampleInterpolateAtSampleCase	(void);
623
624	void				init									(void);
625
626private:
627	enum
628	{
629		RENDER_SIZE = 32
630	};
631
632	std::string			genVertexSource							(int numTargetSamples) const;
633	std::string			genFragmentSource						(int numTargetSamples) const;
634	bool				verifyImage								(const tcu::Surface& resultImage);
635
636	const SampleCase	m_sampleCase;
637};
638
639SingleSampleInterpolateAtSampleCase::SingleSampleInterpolateAtSampleCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target, SampleCase sampleCase)
640	: MultisampleShaderRenderUtil::MultisampleRenderCase	(context, name, description, numSamples, target, RENDER_SIZE)
641	, m_sampleCase											(sampleCase)
642{
643	DE_ASSERT(numSamples == 0);
644	DE_ASSERT(sampleCase < SAMPLE_LAST);
645}
646
647SingleSampleInterpolateAtSampleCase::~SingleSampleInterpolateAtSampleCase (void)
648{
649}
650
651void SingleSampleInterpolateAtSampleCase::init (void)
652{
653	// requirements
654	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_multisample_interpolation"))
655		throw tcu::NotSupportedError("Test requires GL_OES_shader_multisample_interpolation extension");
656	if (m_renderTarget == TARGET_DEFAULT && m_context.getRenderTarget().getNumSamples() > 1)
657		throw tcu::NotSupportedError("Non-multisample framebuffer required");
658
659	// test purpose and expectations
660	m_testCtx.getLog()
661		<< tcu::TestLog::Message
662		<< "Verifying that using interpolateAtSample with multisample buffers not available returns sample evaluated at the center of the pixel.\n"
663		<< "	Interpolate varying containing screen space location.\n"
664		<< "	=> fract(screen space location) should be (about) (0.5, 0.5)\n"
665		<< tcu::TestLog::EndMessage;
666
667	MultisampleShaderRenderUtil::MultisampleRenderCase::init();
668}
669
670std::string SingleSampleInterpolateAtSampleCase::genVertexSource (int numTargetSamples) const
671{
672	DE_UNREF(numTargetSamples);
673
674	std::ostringstream buf;
675
676	buf <<	"#version 310 es\n"
677			"in highp vec4 a_position;\n"
678			"out highp vec2 v_position;\n"
679			"void main (void)\n"
680			"{\n"
681			"	gl_Position = a_position;\n"
682			"	v_position = (a_position.xy + vec2(1.0, 1.0)) / 2.0 * vec2(" << (int)RENDER_SIZE << ".0, " << (int)RENDER_SIZE << ".0);\n"
683			"}\n";
684
685	return buf.str();
686}
687
688std::string SingleSampleInterpolateAtSampleCase::genFragmentSource (int numTargetSamples) const
689{
690	DE_UNREF(numTargetSamples);
691
692	std::ostringstream buf;
693
694	buf <<	"#version 310 es\n"
695			"#extension GL_OES_shader_multisample_interpolation : require\n"
696			"in highp vec2 v_position;\n"
697			"layout(location = 0) out mediump vec4 fragColor;\n"
698			"void main (void)\n"
699			"{\n"
700			"	const highp float threshold = 0.15625; // 4 subpixel bits. Assume 3 accurate bits + 0.03125 for other errors\n"; // 0.03125 = mediump epsilon when value = 32 (RENDER_SIZE)
701
702	if (m_sampleCase == SAMPLE_0)
703	{
704		buf <<	"	highp vec2 samplePosition = interpolateAtSample(v_position, 0);\n"
705				"	highp vec2 positionInsideAPixel = fract(samplePosition);\n"
706				"\n"
707				"	if (abs(positionInsideAPixel.x - 0.5) <= threshold && abs(positionInsideAPixel.y - 0.5) <= threshold)\n"
708				"		fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
709				"	else\n"
710				"		fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
711				"}\n";
712	}
713	else if (m_sampleCase == SAMPLE_N)
714	{
715		buf <<	"	bool allOk = true;\n"
716				"	for (int sampleNdx = 159; sampleNdx < 163; ++sampleNdx)\n"
717				"	{\n"
718				"		highp vec2 samplePosition = interpolateAtSample(v_position, sampleNdx);\n"
719				"		highp vec2 positionInsideAPixel = fract(samplePosition);\n"
720				"		if (abs(positionInsideAPixel.x - 0.5) > threshold || abs(positionInsideAPixel.y - 0.5) > threshold)\n"
721				"			allOk = false;\n"
722				"	}\n"
723				"\n"
724				"	if (allOk)\n"
725				"		fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
726				"	else\n"
727				"		fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
728				"}\n";
729	}
730	else
731		DE_ASSERT(false);
732
733	return buf.str();
734}
735
736bool SingleSampleInterpolateAtSampleCase::verifyImage (const tcu::Surface& resultImage)
737{
738	return verifyGreenImage(resultImage, m_testCtx.getLog());
739}
740
741class CentroidRenderCase : public MultisampleShaderRenderUtil::MultisampleRenderCase
742{
743public:
744									CentroidRenderCase	(Context& context, const char* name, const char* description, int numSamples, RenderTarget target, int renderSize);
745	virtual							~CentroidRenderCase	(void);
746
747	void							init				(void);
748
749private:
750	void							setupRenderData		(void);
751};
752
753CentroidRenderCase::CentroidRenderCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target, int renderSize)
754	: MultisampleShaderRenderUtil::MultisampleRenderCase(context, name, description, numSamples, target, renderSize)
755{
756}
757
758CentroidRenderCase::~CentroidRenderCase (void)
759{
760}
761
762void CentroidRenderCase::init (void)
763{
764	// requirements
765	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_multisample_interpolation"))
766		throw tcu::NotSupportedError("Test requires GL_OES_shader_multisample_interpolation extension");
767
768	MultisampleShaderRenderUtil::MultisampleRenderCase::init();
769}
770
771void CentroidRenderCase::setupRenderData (void)
772{
773	const int				numTriangles	= 200;
774	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
775	std::vector<tcu::Vec4>	data			(numTriangles * 3 * 3);
776
777	m_renderMode = GL_TRIANGLES;
778	m_renderCount = numTriangles * 3;
779	m_renderSceneDescription = "triangle fan of narrow triangles";
780
781	m_renderAttribs["a_position"].offset = 0;
782	m_renderAttribs["a_position"].stride = sizeof(float[4]) * 3;
783	m_renderAttribs["a_barycentricsA"].offset = sizeof(float[4]);
784	m_renderAttribs["a_barycentricsA"].stride = sizeof(float[4]) * 3;
785	m_renderAttribs["a_barycentricsB"].offset = sizeof(float[4]) * 2;
786	m_renderAttribs["a_barycentricsB"].stride = sizeof(float[4]) * 3;
787
788	for (int triangleNdx = 0; triangleNdx < numTriangles; ++triangleNdx)
789	{
790		const float angle		= ((float)triangleNdx) / numTriangles * 2.0f * DE_PI;
791		const float nextAngle	= ((float)triangleNdx + 1.0f) / numTriangles * 2.0f * DE_PI;
792
793		data[(triangleNdx * 3 + 0) * 3 + 0] = tcu::Vec4(0.2f, -0.3f, 0.0f, 1.0f);
794		data[(triangleNdx * 3 + 0) * 3 + 1] = tcu::Vec4(1.0f,  0.0f, 0.0f, 0.0f);
795		data[(triangleNdx * 3 + 0) * 3 + 2] = tcu::Vec4(1.0f,  0.0f, 0.0f, 0.0f);
796
797		data[(triangleNdx * 3 + 1) * 3 + 0] = tcu::Vec4(2.0f * cos(angle), 2.0f * sin(angle), 0.0f, 1.0f);
798		data[(triangleNdx * 3 + 1) * 3 + 1] = tcu::Vec4(0.0f,  1.0f, 0.0f, 0.0f);
799		data[(triangleNdx * 3 + 1) * 3 + 2] = tcu::Vec4(0.0f,  1.0f, 0.0f, 0.0f);
800
801		data[(triangleNdx * 3 + 2) * 3 + 0] = tcu::Vec4(2.0f * cos(nextAngle), 2.0f * sin(nextAngle), 0.0f, 1.0f);
802		data[(triangleNdx * 3 + 2) * 3 + 1] = tcu::Vec4(0.0f,  0.0f, 1.0f, 0.0f);
803		data[(triangleNdx * 3 + 2) * 3 + 2] = tcu::Vec4(0.0f,  0.0f, 1.0f, 0.0f);
804	}
805
806	gl.bindBuffer(GL_ARRAY_BUFFER, m_buffer);
807	gl.bufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)(data.size() * sizeof(tcu::Vec4)), data[0].getPtr(), GL_STATIC_DRAW);
808}
809
810class CentroidQualifierAtSampleCase : public CentroidRenderCase
811{
812public:
813									CentroidQualifierAtSampleCase	(Context& context, const char* name, const char* description, int numSamples, RenderTarget target);
814	virtual							~CentroidQualifierAtSampleCase	(void);
815
816	void							init						(void);
817
818private:
819	enum
820	{
821		RENDER_SIZE = 128
822	};
823
824	std::string						genVertexSource				(int numTargetSamples) const;
825	std::string						genFragmentSource			(int numTargetSamples) const;
826	bool							verifyImage					(const tcu::Surface& resultImage);
827};
828
829CentroidQualifierAtSampleCase::CentroidQualifierAtSampleCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target)
830	: CentroidRenderCase(context, name, description, numSamples, target, RENDER_SIZE)
831{
832}
833
834CentroidQualifierAtSampleCase::~CentroidQualifierAtSampleCase (void)
835{
836}
837
838void CentroidQualifierAtSampleCase::init (void)
839{
840	// test purpose and expectations
841	m_testCtx.getLog()
842		<< tcu::TestLog::Message
843		<< "Verifying that interpolateAtSample ignores the centroid-qualifier.\n"
844		<< "	Draw a fan of narrow triangles (large number of pixels on the edges).\n"
845		<< "	Set varyings 'barycentricsA' and 'barycentricsB' to contain barycentric coordinates.\n"
846		<< "	Add centroid-qualifier for barycentricsB.\n"
847		<< "	=> interpolateAtSample(barycentricsB, N) ~= interpolateAtSample(barycentricsA, N)\n"
848		<< tcu::TestLog::EndMessage;
849
850	CentroidRenderCase::init();
851}
852
853std::string CentroidQualifierAtSampleCase::genVertexSource (int numTargetSamples) const
854{
855	DE_UNREF(numTargetSamples);
856
857	return	"#version 310 es\n"
858			"in highp vec4 a_position;\n"
859			"in highp vec4 a_barycentricsA;\n"
860			"in highp vec4 a_barycentricsB;\n"
861			"out highp vec3 v_barycentricsA;\n"
862			"centroid out highp vec3 v_barycentricsB;\n"
863			"void main (void)\n"
864			"{\n"
865			"	gl_Position = a_position;\n"
866			"	v_barycentricsA = a_barycentricsA.xyz;\n"
867			"	v_barycentricsB = a_barycentricsB.xyz;\n"
868			"}\n";
869}
870
871std::string CentroidQualifierAtSampleCase::genFragmentSource (int numTargetSamples) const
872{
873	DE_UNREF(numTargetSamples);
874
875	std::ostringstream buf;
876
877	buf <<	"#version 310 es\n"
878			"#extension GL_OES_shader_multisample_interpolation : require\n"
879			"in highp vec3 v_barycentricsA;\n"
880			"centroid in highp vec3 v_barycentricsB;\n"
881			"layout(location = 0) out mediump vec4 fragColor;\n"
882			"void main (void)\n"
883			"{\n"
884			"	const highp float threshold = 0.0005;\n"
885			"	bool allOk = true;\n"
886			"\n"
887			"	for (int sampleNdx = 0; sampleNdx < " << numTargetSamples << "; ++sampleNdx)\n"
888			"	{\n"
889			"		highp vec3 sampleA = interpolateAtSample(v_barycentricsA, sampleNdx);\n"
890			"		highp vec3 sampleB = interpolateAtSample(v_barycentricsB, sampleNdx);\n"
891			"		bool valuesEqual = all(lessThan(abs(sampleA - sampleB), vec3(threshold)));\n"
892			"		if (!valuesEqual)\n"
893			"			allOk = false;\n"
894			"	}\n"
895			"\n"
896			"	if (allOk)\n"
897			"		fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
898			"	else\n"
899			"		fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
900			"}\n";
901
902	return buf.str();
903}
904
905bool CentroidQualifierAtSampleCase::verifyImage (const tcu::Surface& resultImage)
906{
907	return verifyGreenImage(resultImage, m_testCtx.getLog());
908}
909
910class InterpolateAtSampleIDCase : public MultisampleShaderRenderUtil::MultisampleRenderCase
911{
912public:
913						InterpolateAtSampleIDCase	(Context& context, const char* name, const char* description, int numSamples, RenderTarget target);
914	virtual				~InterpolateAtSampleIDCase	(void);
915
916	void				init						(void);
917private:
918	enum
919	{
920		RENDER_SIZE = 32
921	};
922
923	std::string			genVertexSource				(int numTargetSamples) const;
924	std::string			genFragmentSource			(int numTargetSamples) const;
925	bool				verifyImage					(const tcu::Surface& resultImage);
926};
927
928InterpolateAtSampleIDCase::InterpolateAtSampleIDCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target)
929	: MultisampleShaderRenderUtil::MultisampleRenderCase(context, name, description, numSamples, target, RENDER_SIZE)
930{
931}
932
933InterpolateAtSampleIDCase::~InterpolateAtSampleIDCase (void)
934{
935}
936
937void InterpolateAtSampleIDCase::init (void)
938{
939	// requirements
940	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_multisample_interpolation"))
941		throw tcu::NotSupportedError("Test requires GL_OES_shader_multisample_interpolation extension");
942	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_sample_variables"))
943		throw tcu::NotSupportedError("Test requires GL_OES_sample_variables extension");
944
945	// test purpose and expectations
946	m_testCtx.getLog()
947		<< tcu::TestLog::Message
948		<< "Verifying that interpolateAtSample with the sample set to the current sampleID returns consistent values.\n"
949		<< "	Interpolate varying containing screen space location.\n"
950		<< "	=> interpolateAtSample(varying, sampleID) = varying"
951		<< tcu::TestLog::EndMessage;
952
953	MultisampleShaderRenderUtil::MultisampleRenderCase::init();
954}
955
956std::string InterpolateAtSampleIDCase::genVertexSource (int numTargetSamples) const
957{
958	DE_UNREF(numTargetSamples);
959
960	std::ostringstream buf;
961
962	buf <<	"#version 310 es\n"
963			"#extension GL_OES_shader_multisample_interpolation : require\n"
964			"in highp vec4 a_position;\n"
965			"sample out highp vec2 v_screenPosition;\n"
966			"void main (void)\n"
967			"{\n"
968			"	gl_Position = a_position;\n"
969			"	v_screenPosition = (a_position.xy + vec2(1.0, 1.0)) / 2.0 * vec2(" << (int)RENDER_SIZE << ".0, " << (int)RENDER_SIZE << ".0);\n"
970			"}\n";
971
972	return buf.str();
973}
974
975std::string InterpolateAtSampleIDCase::genFragmentSource (int numTargetSamples) const
976{
977	DE_UNREF(numTargetSamples);
978
979	return	"#version 310 es\n"
980			"#extension GL_OES_sample_variables : require\n"
981			"#extension GL_OES_shader_multisample_interpolation : require\n"
982			"sample in highp vec2 v_screenPosition;\n"
983			"layout(location = 0) out mediump vec4 fragColor;\n"
984			"void main (void)\n"
985			"{\n"
986			"	const highp float threshold = 0.15625; // 4 subpixel bits. Assume 3 accurate bits + 0.03125 for other errors\n" // 0.03125 = mediump epsilon when value = 32 (RENDER_SIZE)
987			"\n"
988			"	highp vec2 offsetValue = interpolateAtSample(v_screenPosition, gl_SampleID);\n"
989			"	highp vec2 refValue = v_screenPosition;\n"
990			"\n"
991			"	bool valuesEqual = all(lessThan(abs(offsetValue - refValue), vec2(threshold)));\n"
992			"	if (valuesEqual)\n"
993			"		fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
994			"	else\n"
995			"		fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
996			"}\n";
997}
998
999bool InterpolateAtSampleIDCase::verifyImage (const tcu::Surface& resultImage)
1000{
1001	return verifyGreenImage(resultImage, m_testCtx.getLog());
1002}
1003
1004class InterpolateAtCentroidCase : public CentroidRenderCase
1005{
1006public:
1007	enum TestType
1008	{
1009		TEST_CONSISTENCY = 0,
1010		TEST_ARRAY_ELEMENT,
1011
1012		TEST_LAST
1013	};
1014
1015									InterpolateAtCentroidCase	(Context& context, const char* name, const char* description, int numSamples, RenderTarget target, TestType type);
1016	virtual							~InterpolateAtCentroidCase	(void);
1017
1018	void							init						(void);
1019
1020private:
1021	enum
1022	{
1023		RENDER_SIZE = 128
1024	};
1025
1026	std::string						genVertexSource				(int numTargetSamples) const;
1027	std::string						genFragmentSource			(int numTargetSamples) const;
1028	bool							verifyImage					(const tcu::Surface& resultImage);
1029
1030	const TestType					m_type;
1031};
1032
1033InterpolateAtCentroidCase::InterpolateAtCentroidCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target, TestType type)
1034	: CentroidRenderCase	(context, name, description, numSamples, target, RENDER_SIZE)
1035	, m_type				(type)
1036{
1037}
1038
1039InterpolateAtCentroidCase::~InterpolateAtCentroidCase (void)
1040{
1041}
1042
1043void InterpolateAtCentroidCase::init (void)
1044{
1045	// test purpose and expectations
1046	if (m_type == TEST_CONSISTENCY)
1047	{
1048		m_testCtx.getLog()
1049			<< tcu::TestLog::Message
1050			<< "Verifying that interpolateAtCentroid does not return different values than a corresponding centroid-qualified varying.\n"
1051			<< "	Draw a fan of narrow triangles (large number of pixels on the edges).\n"
1052			<< "	Set varyings 'barycentricsA' and 'barycentricsB' to contain barycentric coordinates.\n"
1053			<< "	Add centroid-qualifier for barycentricsB.\n"
1054			<< "	=> interpolateAtCentroid(barycentricsA) ~= barycentricsB\n"
1055			<< tcu::TestLog::EndMessage;
1056	}
1057	else if (m_type == TEST_ARRAY_ELEMENT)
1058	{
1059		m_testCtx.getLog()
1060			<< tcu::TestLog::Message
1061			<< "Testing interpolateAtCentroid with element of array as an argument."
1062			<< tcu::TestLog::EndMessage;
1063	}
1064	else
1065		DE_ASSERT(false);
1066
1067	CentroidRenderCase::init();
1068}
1069
1070std::string InterpolateAtCentroidCase::genVertexSource (int numTargetSamples) const
1071{
1072	DE_UNREF(numTargetSamples);
1073
1074	if (m_type == TEST_CONSISTENCY)
1075		return	"#version 310 es\n"
1076				"in highp vec4 a_position;\n"
1077				"in highp vec4 a_barycentricsA;\n"
1078				"in highp vec4 a_barycentricsB;\n"
1079				"out highp vec3 v_barycentricsA;\n"
1080				"centroid out highp vec3 v_barycentricsB;\n"
1081				"void main (void)\n"
1082				"{\n"
1083				"	gl_Position = a_position;\n"
1084				"	v_barycentricsA = a_barycentricsA.xyz;\n"
1085				"	v_barycentricsB = a_barycentricsB.xyz;\n"
1086				"}\n";
1087	else if (m_type == TEST_ARRAY_ELEMENT)
1088		return	"#version 310 es\n"
1089				"in highp vec4 a_position;\n"
1090				"in highp vec4 a_barycentricsA;\n"
1091				"in highp vec4 a_barycentricsB;\n"
1092				"out highp vec3[2] v_barycentrics;\n"
1093				"void main (void)\n"
1094				"{\n"
1095				"	gl_Position = a_position;\n"
1096				"	v_barycentrics[0] = a_barycentricsA.xyz;\n"
1097				"	v_barycentrics[1] = a_barycentricsB.xyz;\n"
1098				"}\n";
1099
1100	DE_ASSERT(false);
1101	return "";
1102}
1103
1104std::string InterpolateAtCentroidCase::genFragmentSource (int numTargetSamples) const
1105{
1106	DE_UNREF(numTargetSamples);
1107
1108	if (m_type == TEST_CONSISTENCY)
1109		return	"#version 310 es\n"
1110				"#extension GL_OES_shader_multisample_interpolation : require\n"
1111				"in highp vec3 v_barycentricsA;\n"
1112				"centroid in highp vec3 v_barycentricsB;\n"
1113				"layout(location = 0) out highp vec4 fragColor;\n"
1114				"void main (void)\n"
1115				"{\n"
1116				"	const highp float threshold = 0.0005;\n"
1117				"\n"
1118				"	highp vec3 centroidASampled = interpolateAtCentroid(v_barycentricsA);\n"
1119				"	bool valuesEqual = all(lessThan(abs(centroidASampled - v_barycentricsB), vec3(threshold)));\n"
1120				"	bool centroidAIsInvalid = any(greaterThan(centroidASampled, vec3(1.0))) ||\n"
1121				"	                          any(lessThan(centroidASampled, vec3(0.0)));\n"
1122				"	bool centroidBIsInvalid = any(greaterThan(v_barycentricsB, vec3(1.0))) ||\n"
1123				"	                          any(lessThan(v_barycentricsB, vec3(0.0)));\n"
1124				"\n"
1125				"	if (valuesEqual && !centroidAIsInvalid && !centroidBIsInvalid)\n"
1126				"		fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
1127				"	else if (centroidAIsInvalid || centroidBIsInvalid)\n"
1128				"		fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
1129				"	else\n"
1130				"		fragColor = vec4(1.0, 1.0, 0.0, 1.0);\n"
1131				"}\n";
1132	else if (m_type == TEST_ARRAY_ELEMENT)
1133		return	"#version 310 es\n"
1134				"#extension GL_OES_shader_multisample_interpolation : require\n"
1135				"in highp vec3[2] v_barycentrics;\n"
1136				"layout(location = 0) out mediump vec4 fragColor;\n"
1137				"void main (void)\n"
1138				"{\n"
1139				"	const highp float threshold = 0.0005;\n"
1140				"\n"
1141				"	highp vec3 centroidInterpolated = interpolateAtCentroid(v_barycentrics[1]);\n"
1142				"	bool centroidIsInvalid = any(greaterThan(centroidInterpolated, vec3(1.0))) ||\n"
1143				"	                         any(lessThan(centroidInterpolated, vec3(0.0)));\n"
1144				"\n"
1145				"	if (!centroidIsInvalid)\n"
1146				"		fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
1147				"	else\n"
1148				"		fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
1149				"}\n";
1150
1151	DE_ASSERT(false);
1152	return "";
1153}
1154
1155bool InterpolateAtCentroidCase::verifyImage (const tcu::Surface& resultImage)
1156{
1157	return verifyGreenImage(resultImage, m_testCtx.getLog());
1158}
1159
1160class InterpolateAtOffsetCase : public MultisampleShaderRenderUtil::MultisampleRenderCase
1161{
1162public:
1163	enum TestType
1164	{
1165		TEST_QUALIFIER_NONE = 0,
1166		TEST_QUALIFIER_CENTROID,
1167		TEST_QUALIFIER_SAMPLE,
1168		TEST_ARRAY_ELEMENT,
1169
1170		TEST_LAST
1171	};
1172						InterpolateAtOffsetCase		(Context& context, const char* name, const char* description, int numSamples, RenderTarget target, TestType testType);
1173	virtual				~InterpolateAtOffsetCase	(void);
1174
1175	void				init						(void);
1176private:
1177	enum
1178	{
1179		RENDER_SIZE = 32
1180	};
1181
1182	std::string			genVertexSource				(int numTargetSamples) const;
1183	std::string			genFragmentSource			(int numTargetSamples) const;
1184	bool				verifyImage					(const tcu::Surface& resultImage);
1185
1186	const TestType		m_testType;
1187};
1188
1189InterpolateAtOffsetCase::InterpolateAtOffsetCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target, TestType testType)
1190	: MultisampleShaderRenderUtil::MultisampleRenderCase	(context, name, description, numSamples, target, RENDER_SIZE)
1191	, m_testType											(testType)
1192{
1193	DE_ASSERT(testType < TEST_LAST);
1194}
1195
1196InterpolateAtOffsetCase::~InterpolateAtOffsetCase (void)
1197{
1198}
1199
1200void InterpolateAtOffsetCase::init (void)
1201{
1202	// requirements
1203	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_multisample_interpolation"))
1204		throw tcu::NotSupportedError("Test requires GL_OES_shader_multisample_interpolation extension");
1205
1206	// test purpose and expectations
1207	m_testCtx.getLog()
1208		<< tcu::TestLog::Message
1209		<< "Verifying that interpolateAtOffset returns correct values.\n"
1210		<< "	Interpolate varying containing screen space location.\n"
1211		<< "	=> interpolateAtOffset(varying, offset) should be \"varying value at the pixel center\" + offset"
1212		<< tcu::TestLog::EndMessage;
1213
1214	MultisampleShaderRenderUtil::MultisampleRenderCase::init();
1215}
1216
1217std::string InterpolateAtOffsetCase::genVertexSource (int numTargetSamples) const
1218{
1219	DE_UNREF(numTargetSamples);
1220
1221	std::ostringstream buf;
1222	buf << "#version 310 es\n"
1223		<< "#extension GL_OES_shader_multisample_interpolation : require\n"
1224		<< "in highp vec4 a_position;\n";
1225
1226	if (m_testType == TEST_QUALIFIER_NONE || m_testType == TEST_QUALIFIER_CENTROID || m_testType == TEST_QUALIFIER_SAMPLE)
1227	{
1228		const char* const qualifier = (m_testType == TEST_QUALIFIER_CENTROID) ? ("centroid ") : (m_testType == TEST_QUALIFIER_SAMPLE) ? ("sample ") : ("");
1229		buf << qualifier << "out highp vec2 v_screenPosition;\n"
1230			<< qualifier << "out highp vec2 v_offset;\n";
1231	}
1232	else if (m_testType == TEST_ARRAY_ELEMENT)
1233	{
1234		buf << "out highp vec2[2] v_screenPosition;\n"
1235			<< "out highp vec2 v_offset;\n";
1236	}
1237	else
1238		DE_ASSERT(false);
1239
1240	buf	<< "void main (void)\n"
1241		<< "{\n"
1242		<< "	gl_Position = a_position;\n";
1243
1244	if (m_testType != TEST_ARRAY_ELEMENT)
1245		buf	<< "	v_screenPosition = (a_position.xy + vec2(1.0, 1.0)) / 2.0 * vec2(" << (int)RENDER_SIZE << ".0, " << (int)RENDER_SIZE << ".0);\n";
1246	else
1247		buf	<< "	v_screenPosition[0] = a_position.xy; // not used\n"
1248				"	v_screenPosition[1] = (a_position.xy + vec2(1.0, 1.0)) / 2.0 * vec2(" << (int)RENDER_SIZE << ".0, " << (int)RENDER_SIZE << ".0);\n";
1249
1250	buf	<< "	v_offset = a_position.xy * 0.5f;\n"
1251		<< "}\n";
1252
1253	return buf.str();
1254}
1255
1256std::string InterpolateAtOffsetCase::genFragmentSource (int numTargetSamples) const
1257{
1258	DE_UNREF(numTargetSamples);
1259
1260	const char* const	arrayIndexing = (m_testType == TEST_ARRAY_ELEMENT) ? ("[1]") : ("");
1261	std::ostringstream	buf;
1262
1263	buf <<	"#version 310 es\n"
1264			"#extension GL_OES_shader_multisample_interpolation : require\n";
1265
1266	if (m_testType == TEST_QUALIFIER_NONE || m_testType == TEST_QUALIFIER_CENTROID || m_testType == TEST_QUALIFIER_SAMPLE)
1267	{
1268		const char* const qualifier = (m_testType == TEST_QUALIFIER_CENTROID) ? ("centroid ") : (m_testType == TEST_QUALIFIER_SAMPLE) ? ("sample ") : ("");
1269		buf	<< qualifier << "in highp vec2 v_screenPosition;\n"
1270			<< qualifier << "in highp vec2 v_offset;\n";
1271	}
1272	else if (m_testType == TEST_ARRAY_ELEMENT)
1273	{
1274		buf << "in highp vec2[2] v_screenPosition;\n"
1275			<< "in highp vec2 v_offset;\n";
1276	}
1277	else
1278		DE_ASSERT(false);
1279
1280	buf	<<	"layout(location = 0) out mediump vec4 fragColor;\n"
1281			"void main (void)\n"
1282			"{\n"
1283			"	const highp float threshold = 0.15625; // 4 subpixel bits. Assume 3 accurate bits + 0.03125 for other errors\n" // 0.03125 = mediump epsilon when value = 32 (RENDER_SIZE)
1284			"\n"
1285			"	highp vec2 pixelCenter = floor(v_screenPosition" << arrayIndexing << ") + vec2(0.5, 0.5);\n"
1286			"	highp vec2 offsetValue = interpolateAtOffset(v_screenPosition" << arrayIndexing << ", v_offset);\n"
1287			"	highp vec2 refValue = pixelCenter + v_offset;\n"
1288			"\n"
1289			"	bool valuesEqual = all(lessThan(abs(offsetValue - refValue), vec2(threshold)));\n"
1290			"	if (valuesEqual)\n"
1291			"		fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
1292			"	else\n"
1293			"		fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
1294			"}\n";
1295
1296	return buf.str();
1297}
1298
1299bool InterpolateAtOffsetCase::verifyImage (const tcu::Surface& resultImage)
1300{
1301	return verifyGreenImage(resultImage, m_testCtx.getLog());
1302}
1303
1304class InterpolateAtSamplePositionCase : public MultisampleShaderRenderUtil::MultisampleRenderCase
1305{
1306public:
1307						InterpolateAtSamplePositionCase		(Context& context, const char* name, const char* description, int numSamples, RenderTarget target);
1308	virtual				~InterpolateAtSamplePositionCase	(void);
1309
1310	void				init								(void);
1311private:
1312	enum
1313	{
1314		RENDER_SIZE = 32
1315	};
1316
1317	std::string			genVertexSource						(int numTargetSamples) const;
1318	std::string			genFragmentSource					(int numTargetSamples) const;
1319	bool				verifyImage							(const tcu::Surface& resultImage);
1320};
1321
1322InterpolateAtSamplePositionCase::InterpolateAtSamplePositionCase (Context& context, const char* name, const char* description, int numSamples, RenderTarget target)
1323	: MultisampleShaderRenderUtil::MultisampleRenderCase(context, name, description, numSamples, target, RENDER_SIZE)
1324{
1325}
1326
1327InterpolateAtSamplePositionCase::~InterpolateAtSamplePositionCase (void)
1328{
1329}
1330
1331void InterpolateAtSamplePositionCase::init (void)
1332{
1333	// requirements
1334	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_shader_multisample_interpolation"))
1335		throw tcu::NotSupportedError("Test requires GL_OES_shader_multisample_interpolation extension");
1336	if (!m_context.getContextInfo().isExtensionSupported("GL_OES_sample_variables"))
1337		throw tcu::NotSupportedError("Test requires GL_OES_sample_variables extension");
1338
1339	// test purpose and expectations
1340	m_testCtx.getLog()
1341		<< tcu::TestLog::Message
1342		<< "Verifying that interpolateAtOffset with the offset of current sample position returns consistent values.\n"
1343		<< "	Interpolate varying containing screen space location.\n"
1344		<< "	=> interpolateAtOffset(varying, currentOffset) = varying"
1345		<< tcu::TestLog::EndMessage;
1346
1347	MultisampleShaderRenderUtil::MultisampleRenderCase::init();
1348}
1349
1350std::string InterpolateAtSamplePositionCase::genVertexSource (int numTargetSamples) const
1351{
1352	DE_UNREF(numTargetSamples);
1353
1354	std::ostringstream buf;
1355
1356	buf <<	"#version 310 es\n"
1357			"#extension GL_OES_shader_multisample_interpolation : require\n"
1358			"in highp vec4 a_position;\n"
1359			"sample out highp vec2 v_screenPosition;\n"
1360			"void main (void)\n"
1361			"{\n"
1362			"	gl_Position = a_position;\n"
1363			"	v_screenPosition = (a_position.xy + vec2(1.0, 1.0)) / 2.0 * vec2(" << (int)RENDER_SIZE << ".0, " << (int)RENDER_SIZE << ".0);\n"
1364			"}\n";
1365
1366	return buf.str();
1367}
1368
1369std::string InterpolateAtSamplePositionCase::genFragmentSource (int numTargetSamples) const
1370{
1371	DE_UNREF(numTargetSamples);
1372
1373	return	"#version 310 es\n"
1374			"#extension GL_OES_sample_variables : require\n"
1375			"#extension GL_OES_shader_multisample_interpolation : require\n"
1376			"sample in highp vec2 v_screenPosition;\n"
1377			"layout(location = 0) out mediump vec4 fragColor;\n"
1378			"void main (void)\n"
1379			"{\n"
1380			"	const highp float threshold = 0.15625; // 4 subpixel bits. Assume 3 accurate bits + 0.03125 for other errors\n" // 0.03125 = mediump epsilon when value = 32 (RENDER_SIZE)
1381			"\n"
1382			"	highp vec2 offset = gl_SamplePosition - vec2(0.5, 0.5);\n"
1383			"	highp vec2 offsetValue = interpolateAtOffset(v_screenPosition, offset);\n"
1384			"	highp vec2 refValue = v_screenPosition;\n"
1385			"\n"
1386			"	bool valuesEqual = all(lessThan(abs(offsetValue - refValue), vec2(threshold)));\n"
1387			"	if (valuesEqual)\n"
1388			"		fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
1389			"	else\n"
1390			"		fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
1391			"}\n";
1392}
1393
1394bool InterpolateAtSamplePositionCase::verifyImage (const tcu::Surface& resultImage)
1395{
1396	return verifyGreenImage(resultImage, m_testCtx.getLog());
1397}
1398
1399} // anonymous
1400
1401ShaderMultisampleInterpolationTests::ShaderMultisampleInterpolationTests (Context& context)
1402	: TestCaseGroup(context, "multisample_interpolation", "Test multisample interpolation")
1403{
1404}
1405
1406ShaderMultisampleInterpolationTests::~ShaderMultisampleInterpolationTests (void)
1407{
1408}
1409
1410void ShaderMultisampleInterpolationTests::init (void)
1411{
1412	using namespace MultisampleShaderRenderUtil;
1413
1414	static const struct RenderTarget
1415	{
1416		const char*							name;
1417		const char*							desc;
1418		int									numSamples;
1419		MultisampleRenderCase::RenderTarget	target;
1420	} targets[] =
1421	{
1422		{ "default_framebuffer",		"Test with default framebuffer",	0,	MultisampleRenderCase::TARGET_DEFAULT		},
1423		{ "singlesample_texture",		"Test with singlesample texture",	0,	MultisampleRenderCase::TARGET_TEXTURE		},
1424		{ "multisample_texture_1",		"Test with multisample texture",	1,	MultisampleRenderCase::TARGET_TEXTURE		},
1425		{ "multisample_texture_2",		"Test with multisample texture",	2,	MultisampleRenderCase::TARGET_TEXTURE		},
1426		{ "multisample_texture_4",		"Test with multisample texture",	4,	MultisampleRenderCase::TARGET_TEXTURE		},
1427		{ "multisample_texture_8",		"Test with multisample texture",	8,	MultisampleRenderCase::TARGET_TEXTURE		},
1428		{ "multisample_texture_16",		"Test with multisample texture",	16,	MultisampleRenderCase::TARGET_TEXTURE		},
1429		{ "singlesample_rbo",			"Test with singlesample rbo",		0,	MultisampleRenderCase::TARGET_RENDERBUFFER	},
1430		{ "multisample_rbo_1",			"Test with multisample rbo",		1,	MultisampleRenderCase::TARGET_RENDERBUFFER	},
1431		{ "multisample_rbo_2",			"Test with multisample rbo",		2,	MultisampleRenderCase::TARGET_RENDERBUFFER	},
1432		{ "multisample_rbo_4",			"Test with multisample rbo",		4,	MultisampleRenderCase::TARGET_RENDERBUFFER	},
1433		{ "multisample_rbo_8",			"Test with multisample rbo",		8,	MultisampleRenderCase::TARGET_RENDERBUFFER	},
1434		{ "multisample_rbo_16",			"Test with multisample rbo",		16,	MultisampleRenderCase::TARGET_RENDERBUFFER	},
1435	};
1436
1437	// .sample_qualifier
1438	{
1439		tcu::TestCaseGroup* const sampleQualifierGroup = new tcu::TestCaseGroup(m_testCtx, "sample_qualifier", "Test sample qualifier");
1440		addChild(sampleQualifierGroup);
1441
1442		for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1443			sampleQualifierGroup->addChild(new SampleQualifierRenderCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target));
1444	}
1445
1446	// .interpolate_at_sample
1447	{
1448		tcu::TestCaseGroup* const interpolateAtSampleGroup = new tcu::TestCaseGroup(m_testCtx, "interpolate_at_sample", "Test interpolateAtSample");
1449		addChild(interpolateAtSampleGroup);
1450
1451		// .static_sample_number
1452		{
1453			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "static_sample_number", "Test interpolateAtSample sample number");
1454			interpolateAtSampleGroup->addChild(group);
1455
1456			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1457				group->addChild(new InterpolateAtSampleRenderCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target, InterpolateAtSampleRenderCase::INDEXING_STATIC));
1458		}
1459
1460		// .dynamic_sample_number
1461		{
1462			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "dynamic_sample_number", "Test interpolateAtSample sample number");
1463			interpolateAtSampleGroup->addChild(group);
1464
1465			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1466				group->addChild(new InterpolateAtSampleRenderCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target, InterpolateAtSampleRenderCase::INDEXING_DYNAMIC));
1467		}
1468
1469		// .non_multisample_buffer
1470		{
1471			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "non_multisample_buffer", "Test interpolateAtSample with non-multisample buffers");
1472			interpolateAtSampleGroup->addChild(group);
1473
1474			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1475				if (targets[targetNdx].numSamples == 0)
1476					group->addChild(new SingleSampleInterpolateAtSampleCase(m_context, std::string("sample_0_").append(targets[targetNdx].name).c_str(), targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target, SingleSampleInterpolateAtSampleCase::SAMPLE_0));
1477
1478			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1479				if (targets[targetNdx].numSamples == 0)
1480					group->addChild(new SingleSampleInterpolateAtSampleCase(m_context, std::string("sample_n_").append(targets[targetNdx].name).c_str(), targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target, SingleSampleInterpolateAtSampleCase::SAMPLE_N));
1481		}
1482
1483		// .centroid_qualifier
1484		{
1485			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "centroid_qualified", "Test interpolateAtSample with centroid qualified varying");
1486			interpolateAtSampleGroup->addChild(group);
1487
1488			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1489				group->addChild(new CentroidQualifierAtSampleCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target));
1490		}
1491
1492		// .at_sample_id
1493		{
1494			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "at_sample_id", "Test interpolateAtOffset at current sample id");
1495			interpolateAtSampleGroup->addChild(group);
1496
1497			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1498				group->addChild(new InterpolateAtSampleIDCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target));
1499		}
1500	}
1501
1502	// .interpolate_at_centroid
1503	{
1504		tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, "interpolate_at_centroid", "Test interpolateAtCentroid");
1505		addChild(methodGroup);
1506
1507		// .consistency
1508		{
1509			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "consistency", "Test interpolateAtCentroid return value is consistent to centroid qualified value");
1510			methodGroup->addChild(group);
1511
1512			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1513				group->addChild(new InterpolateAtCentroidCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target, InterpolateAtCentroidCase::TEST_CONSISTENCY));
1514		}
1515
1516		// .array_element
1517		{
1518			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "array_element", "Test interpolateAtCentroid with array element");
1519			methodGroup->addChild(group);
1520
1521			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1522				group->addChild(new InterpolateAtCentroidCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target, InterpolateAtCentroidCase::TEST_ARRAY_ELEMENT));
1523		}
1524	}
1525
1526	// .interpolate_at_offset
1527	{
1528		static const struct TestConfig
1529		{
1530			const char*							name;
1531			InterpolateAtOffsetCase::TestType	type;
1532		} configs[] =
1533		{
1534			{ "no_qualifiers",		InterpolateAtOffsetCase::TEST_QUALIFIER_NONE		},
1535			{ "centroid_qualifier",	InterpolateAtOffsetCase::TEST_QUALIFIER_CENTROID	},
1536			{ "sample_qualifier",	InterpolateAtOffsetCase::TEST_QUALIFIER_SAMPLE		},
1537		};
1538
1539		tcu::TestCaseGroup* const methodGroup = new tcu::TestCaseGroup(m_testCtx, "interpolate_at_offset", "Test interpolateAtOffset");
1540		addChild(methodGroup);
1541
1542		// .no_qualifiers
1543		// .centroid_qualifier
1544		// .sample_qualifier
1545		for (int configNdx = 0; configNdx < DE_LENGTH_OF_ARRAY(configs); ++configNdx)
1546		{
1547			tcu::TestCaseGroup* const qualifierGroup = new tcu::TestCaseGroup(m_testCtx, configs[configNdx].name, "Test interpolateAtOffset with qualified/non-qualified varying");
1548			methodGroup->addChild(qualifierGroup);
1549
1550			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1551				qualifierGroup->addChild(new InterpolateAtOffsetCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target, configs[configNdx].type));
1552		}
1553
1554		// .at_sample_position
1555		{
1556			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "at_sample_position", "Test interpolateAtOffset at sample position");
1557			methodGroup->addChild(group);
1558
1559			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1560				group->addChild(new InterpolateAtSamplePositionCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target));
1561		}
1562
1563		// .array_element
1564		{
1565			tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(m_testCtx, "array_element", "Test interpolateAtOffset with array element");
1566			methodGroup->addChild(group);
1567
1568			for (int targetNdx = 0; targetNdx < DE_LENGTH_OF_ARRAY(targets); ++targetNdx)
1569				group->addChild(new InterpolateAtOffsetCase(m_context, targets[targetNdx].name, targets[targetNdx].desc, targets[targetNdx].numSamples, targets[targetNdx].target, InterpolateAtOffsetCase::TEST_ARRAY_ELEMENT));
1570		}
1571	}
1572}
1573
1574} // Functional
1575} // gles31
1576} // deqp
1577