1/*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
4 *
5 * Copyright (c) 2014 Intel Corporation
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
23 */ /*-------------------------------------------------------------------*/
24
25#include "glcShaderIntegerMixTests.hpp"
26#include "deMath.h"
27#include "deRandom.hpp"
28#include "deString.h"
29#include "deStringUtil.hpp"
30#include "gluContextInfo.hpp"
31#include "gluDrawUtil.hpp"
32#include "gluPixelTransfer.hpp"
33#include "gluShaderProgram.hpp"
34#include "glw.h"
35#include "glwFunctions.hpp"
36#include "tcuCommandLine.hpp"
37#include "tcuStringTemplate.hpp"
38#include "tcuSurface.hpp"
39#include "tcuTestLog.hpp"
40
41namespace deqp
42{
43
44using tcu::TestLog;
45
46class ShaderIntegerMixCase : public TestCase
47{
48public:
49	ShaderIntegerMixCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion)
50		: TestCase(context, name, description), m_glslVersion(glslVersion)
51	{
52	}
53
54	~ShaderIntegerMixCase()
55	{
56		// empty
57	}
58
59	IterateResult iterate()
60	{
61		qpTestResult result = test();
62
63		m_testCtx.setTestResult(result, qpGetTestResultName(result));
64
65		return STOP;
66	}
67
68protected:
69	glu::GLSLVersion m_glslVersion;
70
71	virtual qpTestResult test() = 0;
72};
73
74class ShaderIntegerMixDefineCase : public ShaderIntegerMixCase
75{
76public:
77	ShaderIntegerMixDefineCase(Context& context, const char* name, const char* description,
78							   glu::GLSLVersion glslVersion)
79		: ShaderIntegerMixCase(context, name, description, glslVersion)
80	{
81		// empty
82	}
83
84	~ShaderIntegerMixDefineCase()
85	{
86		// empty
87	}
88
89protected:
90	virtual qpTestResult test()
91	{
92		const glw::Functions& gl   = m_context.getRenderContext().getFunctions();
93		bool				  pass = true;
94
95		static const char source_template[] = "${VERSION_DECL}\n"
96											  "#extension GL_EXT_shader_integer_mix: require\n"
97											  "\n"
98											  "#if !defined GL_EXT_shader_integer_mix\n"
99											  "#  error GL_EXT_shader_integer_mix is not defined\n"
100											  "#elif GL_EXT_shader_integer_mix != 1\n"
101											  "#  error GL_EXT_shader_integer_mix is not equal to 1\n"
102											  "#endif\n"
103											  "\n"
104											  "void main(void) { ${BODY} }\n";
105
106		static const struct
107		{
108			GLenum		target;
109			const char* body;
110		} shader_targets[] = {
111			{ GL_VERTEX_SHADER, "gl_Position = vec4(0);" }, { GL_FRAGMENT_SHADER, "" },
112		};
113
114		const glu::GLSLVersion v = glslVersionIsES(m_glslVersion) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330;
115
116		if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_integer_mix"))
117			return QP_TEST_RESULT_NOT_SUPPORTED;
118
119		for (int i = 0; i < DE_LENGTH_OF_ARRAY(shader_targets); i++)
120		{
121			std::map<std::string, std::string> args;
122
123			args["VERSION_DECL"] = glu::getGLSLVersionDeclaration(v);
124			args["BODY"]		 = shader_targets[i].body;
125
126			std::string code = tcu::StringTemplate(source_template).specialize(args);
127
128			GLuint		shader	 = gl.createShader(shader_targets[i].target);
129			char const* strings[1] = { code.c_str() };
130			gl.shaderSource(shader, 1, strings, 0);
131			gl.compileShader(shader);
132
133			GLint compileSuccess = 0;
134			gl.getShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess);
135			gl.deleteShader(shader);
136
137			if (!compileSuccess)
138				pass = false;
139		}
140
141		return pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL;
142	}
143};
144
145class ShaderIntegerMixPrototypesCase : public ShaderIntegerMixCase
146{
147public:
148	ShaderIntegerMixPrototypesCase(Context& context, const char* name, const char* description,
149								   glu::GLSLVersion glslVersion, bool _use_extension, bool _is_negative_testing)
150		: ShaderIntegerMixCase(context, name, description, glslVersion)
151		, use_extension(_use_extension)
152		, is_negative_testing(_is_negative_testing)
153	{
154		// empty
155	}
156
157	~ShaderIntegerMixPrototypesCase()
158	{
159		// empty
160	}
161
162protected:
163	bool use_extension;
164	bool is_negative_testing;
165
166	virtual qpTestResult test()
167	{
168		TestLog&			  log  = m_testCtx.getLog();
169		const glw::Functions& gl   = m_context.getRenderContext().getFunctions();
170		bool				  pass = true;
171
172		static const char source_template[] = "${VERSION_DECL}\n"
173											  "${EXTENSION_ENABLE}\n"
174											  "\n"
175											  "void main()\n"
176											  "{\n"
177											  "	mix(ivec2(1), ivec2(2), bvec2(true));\n"
178											  "	mix(ivec3(1), ivec3(2), bvec3(true));\n"
179											  "	mix(ivec4(1), ivec4(2), bvec4(true));\n"
180											  "	mix(uvec2(1), uvec2(2), bvec2(true));\n"
181											  "	mix(uvec3(1), uvec3(2), bvec3(true));\n"
182											  "	mix(uvec4(1), uvec4(2), bvec4(true));\n"
183											  "	mix(bvec2(1), bvec2(0), bvec2(true));\n"
184											  "	mix(bvec3(1), bvec3(0), bvec3(true));\n"
185											  "	mix(bvec4(1), bvec4(0), bvec4(true));\n"
186											  "	${BODY}\n"
187											  "}\n";
188
189		static const struct
190		{
191			GLenum		target;
192			const char* body;
193		} shader_targets[] = {
194			{ GL_VERTEX_SHADER, "gl_Position = vec4(0);" }, { GL_FRAGMENT_SHADER, "" },
195		};
196
197		glu::GLSLVersion v;
198		const char*		 extension_enable;
199
200		if (use_extension)
201		{
202			v				 = glslVersionIsES(m_glslVersion) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330;
203			extension_enable = "#extension GL_EXT_shader_integer_mix: enable";
204
205			if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_integer_mix"))
206				return QP_TEST_RESULT_NOT_SUPPORTED;
207		}
208		else if (is_negative_testing)
209		{
210			v				 = glslVersionIsES(m_glslVersion) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330;
211			extension_enable = "";
212		}
213		else
214		{
215			v				 = m_glslVersion;
216			extension_enable = "";
217			if (glslVersionIsES(m_glslVersion))
218			{
219				if (m_glslVersion < glu::GLSL_VERSION_310_ES)
220					return QP_TEST_RESULT_NOT_SUPPORTED;
221			}
222			else
223			{
224				if (m_glslVersion < glu::GLSL_VERSION_450)
225					return QP_TEST_RESULT_NOT_SUPPORTED;
226			}
227		}
228
229		for (int i = 0; i < DE_LENGTH_OF_ARRAY(shader_targets); i++)
230		{
231			std::map<std::string, std::string> args;
232
233			args["VERSION_DECL"]	 = glu::getGLSLVersionDeclaration(v);
234			args["EXTENSION_ENABLE"] = extension_enable;
235			args["BODY"]			 = shader_targets[i].body;
236
237			std::string code = tcu::StringTemplate(source_template).specialize(args);
238
239			GLuint		shader	 = gl.createShader(shader_targets[i].target);
240			char const* strings[1] = { code.c_str() };
241			gl.shaderSource(shader, 1, strings, 0);
242			gl.compileShader(shader);
243
244			GLint compileSuccess = 0;
245			gl.getShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess);
246
247			if (is_negative_testing)
248			{
249				if (compileSuccess)
250				{
251					TCU_FAIL("The shader compilation was expected to fail, but it was successful.");
252					pass = false;
253				}
254			}
255			else if (!compileSuccess)
256			{
257				GLchar infoLog[1000];
258
259				gl.getShaderInfoLog(shader, sizeof(infoLog), NULL, infoLog);
260				log.writeKernelSource(strings[0]);
261				log.writeCompileInfo("shader", "", false, infoLog);
262
263				pass = false;
264			}
265
266			gl.deleteShader(shader);
267		}
268
269		return pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL;
270	}
271};
272
273class ShaderIntegerMixRenderCase : public ShaderIntegerMixCase
274{
275public:
276	ShaderIntegerMixRenderCase(Context& context, const char* name, const char* description,
277							   glu::GLSLVersion glslVersion, const char* _type)
278		: ShaderIntegerMixCase(context, name, description, glslVersion), type(_type)
279	{
280		// empty
281	}
282
283	~ShaderIntegerMixRenderCase()
284	{
285		// empty
286	}
287
288protected:
289	// Type used for mix() parameters in this test case.
290	const char* type;
291
292	static const unsigned width  = 8 * 8;
293	static const unsigned height = 8 * 8;
294
295	virtual qpTestResult test()
296	{
297		static const char vs_template[] = "${VERSION_DECL}\n"
298										  "${EXTENSION_ENABLE}\n"
299										  "\n"
300										  "in vec2 vertex;\n"
301										  "in ivec4 vs_in_a;\n"
302										  "in ivec4 vs_in_b;\n"
303										  "in ivec4 vs_in_sel;\n"
304										  "\n"
305										  "flat out ivec4 fs_in_a;\n"
306										  "flat out ivec4 fs_in_b;\n"
307										  "flat out ivec4 fs_in_sel;\n"
308										  "flat out ivec4 fs_in_result;\n"
309										  "\n"
310										  "void main()\n"
311										  "{\n"
312										  "    fs_in_a = vs_in_a;\n"
313										  "    fs_in_b = vs_in_b;\n"
314										  "    fs_in_sel = vs_in_sel;\n"
315										  "\n"
316										  "    ${TYPE} a = ${TYPE}(vs_in_a);\n"
317										  "    ${TYPE} b = ${TYPE}(vs_in_b);\n"
318										  "    bvec4 sel = bvec4(vs_in_sel);\n"
319										  "    fs_in_result = ivec4(mix(a, b, sel));\n"
320										  "\n"
321										  "    gl_Position = vec4(vertex, 0, 1);\n"
322										  "    gl_PointSize = 4.;\n"
323										  "}\n";
324
325		static const char fs_template[] = "${VERSION_DECL}\n"
326										  "${EXTENSION_ENABLE}\n"
327										  "\n"
328										  "out ivec4 o;\n"
329										  "\n"
330										  "flat in ivec4 fs_in_a;\n"
331										  "flat in ivec4 fs_in_b;\n"
332										  "flat in ivec4 fs_in_sel;\n"
333										  "flat in ivec4 fs_in_result;\n"
334										  "\n"
335										  "uniform bool use_vs_data;\n"
336										  "\n"
337										  "void main()\n"
338										  "{\n"
339										  "    if (use_vs_data)\n"
340										  "        o = fs_in_result;\n"
341										  "    else {\n"
342										  "        ${TYPE} a = ${TYPE}(fs_in_a);\n"
343										  "        ${TYPE} b = ${TYPE}(fs_in_b);\n"
344										  "        bvec4 sel = bvec4(fs_in_sel);\n"
345										  "        o = ivec4(mix(a, b, sel));\n"
346										  "    }\n"
347										  "}\n";
348
349		TestLog&			  log  = m_testCtx.getLog();
350		const glw::Functions& gl   = m_context.getRenderContext().getFunctions();
351		bool				  pass = true;
352		const char*			  extension_enable;
353		bool				  is_es = glslVersionIsES(m_glslVersion);
354
355		if ((is_es && (m_glslVersion < glu::GLSL_VERSION_310_ES)) || !is_es)
356		{
357			/* For versions that do not support this feature in Core it must be exposed via an extension. */
358			if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_integer_mix"))
359			{
360				return QP_TEST_RESULT_NOT_SUPPORTED;
361			}
362
363			extension_enable = "#extension GL_EXT_shader_integer_mix: enable";
364		}
365		else
366		{
367			extension_enable = "";
368		}
369
370		/* Generate the specialization of the shader for the specific
371		 * type being tested.
372		 */
373		std::map<std::string, std::string> args;
374
375		args["VERSION_DECL"]	 = glu::getGLSLVersionDeclaration(m_glslVersion);
376		args["EXTENSION_ENABLE"] = extension_enable;
377		args["TYPE"]			 = type;
378
379		std::string vs_code = tcu::StringTemplate(vs_template).specialize(args);
380
381		std::string fs_code = tcu::StringTemplate(fs_template).specialize(args);
382
383		glu::ShaderProgram prog(m_context.getRenderContext(),
384								glu::makeVtxFragSources(vs_code.c_str(), fs_code.c_str()));
385
386		if (!prog.isOk())
387		{
388			log << prog;
389			TCU_FAIL("Compile failed");
390		}
391
392		if (!glslVersionIsES(m_glslVersion))
393			glEnable(GL_PROGRAM_POINT_SIZE);
394
395		/* Generate an integer FBO for rendering.
396		 */
397		GLuint fbo;
398		GLuint tex;
399
400		glGenTextures(1, &tex);
401		glBindTexture(GL_TEXTURE_2D, tex);
402		glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA32I, width, height, 0 /* border */, GL_RGBA_INTEGER, GL_INT,
403					 NULL /* data */);
404		glBindTexture(GL_TEXTURE_2D, 0);
405
406		glGenFramebuffers(1, &fbo);
407		glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);
408		glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
409		glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0 /* level */);
410
411		GLU_EXPECT_NO_ERROR(gl.getError(), "Creation of rendering FBO failed.");
412
413		if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
414			TCU_FAIL("Framebuffer not complete.");
415
416		glViewport(0, 0, width, height);
417
418		/* Fill a VBO with some vertex data.
419		 */
420		deUint32   pointIndices[256];
421		float	  vertex[DE_LENGTH_OF_ARRAY(pointIndices) * 2];
422		deInt32	a[DE_LENGTH_OF_ARRAY(pointIndices) * 4];
423		deInt32	b[DE_LENGTH_OF_ARRAY(a)];
424		deInt32	sel[DE_LENGTH_OF_ARRAY(a)];
425		tcu::IVec4 expected[DE_LENGTH_OF_ARRAY(pointIndices)];
426
427		for (int i = 0; i < DE_LENGTH_OF_ARRAY(pointIndices); i++)
428		{
429			pointIndices[i] = deUint16(i);
430
431			const int x = (i / 16) * (width / 16) + (4 / 2);
432			const int y = (i % 16) * (height / 16) + (4 / 2);
433
434			vertex[(i * 2) + 0] = float(x) * 2.0f / float(width) - 1.0f;
435			vertex[(i * 2) + 1] = float(y) * 2.0f / float(height) - 1.0f;
436
437			a[(i * 4) + 0] = i;
438			a[(i * 4) + 1] = i * 5;
439			a[(i * 4) + 2] = i * 7;
440			a[(i * 4) + 3] = i * 11;
441
442			b[(i * 4) + 0] = ~a[(i * 4) + 3];
443			b[(i * 4) + 1] = ~a[(i * 4) + 2];
444			b[(i * 4) + 2] = ~a[(i * 4) + 1];
445			b[(i * 4) + 3] = ~a[(i * 4) + 0];
446
447			sel[(i * 4) + 0] = (i >> 0) & 1;
448			sel[(i * 4) + 1] = (i >> 1) & 1;
449			sel[(i * 4) + 2] = (i >> 2) & 1;
450			sel[(i * 4) + 3] = (i >> 3) & 1;
451
452			expected[i] = tcu::IVec4(
453				sel[(i * 4) + 0] ? b[(i * 4) + 0] : a[(i * 4) + 0], sel[(i * 4) + 1] ? b[(i * 4) + 1] : a[(i * 4) + 1],
454				sel[(i * 4) + 2] ? b[(i * 4) + 2] : a[(i * 4) + 2], sel[(i * 4) + 3] ? b[(i * 4) + 3] : a[(i * 4) + 3]);
455		}
456
457		/* Mask off all but the least significant bit for boolean
458		 * types.
459		 */
460		if (type[0] == 'b')
461		{
462			for (int i = 0; i < DE_LENGTH_OF_ARRAY(a); i++)
463			{
464				a[i] &= 1;
465				b[i] &= 1;
466
467				expected[i / 4][0] &= 1;
468				expected[i / 4][1] &= 1;
469				expected[i / 4][2] &= 1;
470				expected[i / 4][3] &= 1;
471			}
472		}
473
474		glu::VertexArrayBinding vertexArrays[] = {
475			glu::va::Float("vertex", 2, DE_LENGTH_OF_ARRAY(pointIndices), 0, vertex),
476			glu::va::Int32("vs_in_a", 4, DE_LENGTH_OF_ARRAY(pointIndices), 0, a),
477			glu::va::Int32("vs_in_b", 4, DE_LENGTH_OF_ARRAY(pointIndices), 0, b),
478			glu::va::Int32("vs_in_sel", 4, DE_LENGTH_OF_ARRAY(pointIndices), 0, sel)
479		};
480
481		/* Render and verify the results.  Rendering happens twice.
482		 * The first time, use_vs_data is false, and the mix() result
483		 * from the fragment shader is used.  The second time,
484		 * use_vs_data is true, and the mix() result from the vertex
485		 * shader is used.
486		 */
487		const GLint loc = gl.getUniformLocation(prog.getProgram(), "use_vs_data");
488		gl.useProgram(prog.getProgram());
489
490		static const GLint clear[] = { 1, 2, 3, 4 };
491		glClearBufferiv(GL_COLOR, 0, clear);
492
493		gl.uniform1i(loc, 0);
494		glu::draw(m_context.getRenderContext(), prog.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays,
495				  glu::pr::Points(DE_LENGTH_OF_ARRAY(pointIndices), pointIndices));
496
497		for (int i = 0; i < DE_LENGTH_OF_ARRAY(pointIndices); i++)
498		{
499			const int x = int((vertex[(i * 2) + 0] + 1.0f) * float(width) / 2.0f);
500			const int y = int((vertex[(i * 2) + 1] + 1.0f) * float(height) / 2.0f);
501
502			pass = probe_pixel(log, "Fragment", x, y, expected[i]) && pass;
503		}
504
505		gl.uniform1i(loc, 1);
506		glu::draw(m_context.getRenderContext(), prog.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays,
507				  glu::pr::Points(DE_LENGTH_OF_ARRAY(pointIndices), pointIndices));
508
509		for (int i = 0; i < DE_LENGTH_OF_ARRAY(pointIndices); i++)
510		{
511			const int x = int((vertex[(i * 2) + 0] + 1.0f) * float(width) / 2.0f);
512			const int y = int((vertex[(i * 2) + 1] + 1.0f) * float(height) / 2.0f);
513
514			pass = probe_pixel(log, "Vertex", x, y, expected[i]) && pass;
515		}
516
517		glDeleteFramebuffers(1, &fbo);
518		glDeleteTextures(1, &tex);
519
520		return pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL;
521	}
522
523	bool probe_pixel(TestLog& log, const char* stage, int x, int y, const tcu::IVec4& expected)
524	{
525		tcu::IVec4 pixel;
526
527		glReadPixels(x, y, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixel);
528
529		if (expected != pixel)
530		{
531			log << TestLog::Message << stage << " shader failed at pixel (" << x << ", " << y << ").  "
532				<< "Got " << pixel << ", expected " << expected << ")." << TestLog::EndMessage;
533			return false;
534		}
535
536		return true;
537	}
538};
539
540ShaderIntegerMixTests::ShaderIntegerMixTests(Context& context, glu::GLSLVersion glslVersion)
541	: TestCaseGroup(context, "shader_integer_mix", "Shader Integer Mix tests"), m_glslVersion(glslVersion)
542{
543	// empty
544}
545
546ShaderIntegerMixTests::~ShaderIntegerMixTests()
547{
548	// empty
549}
550
551void ShaderIntegerMixTests::init(void)
552{
553	addChild(new ShaderIntegerMixDefineCase(m_context, "define", "Verify GL_EXT_shader_integer_mix is defined to 1.",
554											m_glslVersion));
555	addChild(new ShaderIntegerMixPrototypesCase(m_context, "prototypes-extension",
556												"Verify availability of all function signatures with the extension.",
557												m_glslVersion, true, false));
558	addChild(new ShaderIntegerMixPrototypesCase(
559		m_context, "prototypes", "Verify availability of all function signatures with the proper GLSL version.",
560		m_glslVersion, false, false));
561	addChild(new ShaderIntegerMixPrototypesCase(
562		m_context, "prototypes-negative",
563		"Verify compilation fails if the GLSL version does not support shader_integer_mix", m_glslVersion, false,
564		true));
565
566	static const char* types_to_test[] = { "ivec4", "uvec4", "bvec4" };
567
568	for (int i = 0; i < DE_LENGTH_OF_ARRAY(types_to_test); i++)
569	{
570		std::stringstream name;
571
572		name << "mix-" << types_to_test[i];
573
574		std::stringstream description;
575
576		description << "Verify functionality of mix() with " << types_to_test[i] << " parameters.";
577
578		addChild(new ShaderIntegerMixRenderCase(m_context, name.str().c_str(), description.str().c_str(), m_glslVersion,
579												types_to_test[i]));
580	}
581}
582
583} // namespace deqp
584