1/*-------------------------------------------------------------------------
2* OpenGL Conformance Test Suite
3* -----------------------------
4*
5* Copyright (c) 2017 The Khronos Group Inc.
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  glcShaderGroupVoteTests.cpp
21* \brief Conformance tests for the ARB_shader_group_vote functionality.
22*/ /*-------------------------------------------------------------------*/
23
24#include "glcShaderGroupVoteTests.hpp"
25#include "gluContextInfo.hpp"
26#include "gluDefs.hpp"
27#include "gluDrawUtil.hpp"
28#include "gluObjectWrapper.hpp"
29#include "gluShaderProgram.hpp"
30#include "glwEnums.hpp"
31#include "glwFunctions.hpp"
32#include "tcuRenderTarget.hpp"
33#include "tcuTestLog.hpp"
34
35using namespace glw;
36
37namespace glcts
38{
39
40// Helper structure that wpraps workgroup size
41struct WorkGroupSize
42{
43	WorkGroupSize(deqp::Context& context)
44	{
45		width  = 16;
46		height = 16;
47		if (glu::isContextTypeES(context.getRenderContext().getType()))
48			height = 8;
49	}
50
51	GLsizei width;
52	GLsizei height;
53};
54
55ShaderGroupVoteTestCaseBase::ComputeShader::ComputeShader(const std::string& name, const std::string& shader)
56	: m_name(name), m_shader(shader), m_program(NULL), m_compileOnly(true)
57{
58}
59
60ShaderGroupVoteTestCaseBase::ComputeShader::ComputeShader(const std::string& name, const std::string& shader,
61														  const tcu::IVec4& desiredColor)
62	: m_name(name), m_shader(shader), m_program(NULL), m_desiredColor(desiredColor), m_compileOnly(false)
63{
64}
65
66ShaderGroupVoteTestCaseBase::ComputeShader::~ComputeShader()
67{
68	if (m_program)
69	{
70		delete m_program;
71	}
72}
73
74void ShaderGroupVoteTestCaseBase::ComputeShader::create(deqp::Context& context)
75{
76	glu::ProgramSources sourcesCompute;
77	sourcesCompute.sources[glu::SHADERTYPE_COMPUTE].push_back(m_shader);
78	m_program = new glu::ShaderProgram(context.getRenderContext(), sourcesCompute);
79
80	if (!m_program->isOk())
81	{
82		context.getTestContext().getLog()
83			<< tcu::TestLog::Message << m_shader << m_program->getShaderInfo(glu::SHADERTYPE_COMPUTE).infoLog
84			<< m_program->getProgramInfo().infoLog << tcu::TestLog::EndMessage;
85		TCU_FAIL("Shader compilation failed");
86	}
87}
88
89void ShaderGroupVoteTestCaseBase::ComputeShader::execute(deqp::Context& context)
90{
91	if (m_compileOnly)
92	{
93		return;
94	}
95
96	const glw::Functions& gl = context.getRenderContext().getFunctions();
97	const glu::Texture	outputTexture(context.getRenderContext());
98	const WorkGroupSize   renderSize(context);
99
100	gl.clearColor(0.5f, 0.5f, 0.5f, 1.0f);
101	gl.clear(GL_COLOR_BUFFER_BIT);
102
103	gl.useProgram(m_program->getProgram());
104	GLU_EXPECT_NO_ERROR(gl.getError(), "useProgram failed");
105
106	// output image
107	gl.bindTexture(GL_TEXTURE_2D, *outputTexture);
108	gl.texStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, renderSize.width, renderSize.height);
109	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
110	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
111	GLU_EXPECT_NO_ERROR(gl.getError(), "Uploading image data failed");
112
113	// bind image
114	gl.bindImageTexture(2, *outputTexture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
115	GLU_EXPECT_NO_ERROR(gl.getError(), "bindImageTexture failed");
116
117	// dispatch compute
118	gl.dispatchCompute(1, 1, 1);
119	GLU_EXPECT_NO_ERROR(gl.getError(), "dispatchCompute failed");
120
121	glu::GLSLVersion glslVersion		= glu::getContextTypeGLSLVersion(context.getRenderContext().getType());
122	const char*		 versionDeclaration = glu::getGLSLVersionDeclaration(glslVersion);
123
124	// render output texture
125	std::string vs = versionDeclaration;
126	vs += "\n"
127		  "in highp vec2 position;\n"
128		  "in highp vec2 inTexcoord;\n"
129		  "out highp vec2 texcoord;\n"
130		  "void main()\n"
131		  "{\n"
132		  "	texcoord = inTexcoord;\n"
133		  "	gl_Position = vec4(position, 0.0, 1.0);\n"
134		  "}\n";
135
136	std::string fs = versionDeclaration;
137	fs += "\n"
138		  "uniform highp sampler2D sampler;\n"
139		  "in highp vec2 texcoord;\n"
140		  "out highp vec4 color;\n"
141		  "void main()\n"
142		  "{\n"
143		  "	color = texture(sampler, texcoord);\n"
144		  "}\n";
145
146	glu::ProgramSources sources;
147	sources.sources[glu::SHADERTYPE_VERTEX].push_back(vs);
148	sources.sources[glu::SHADERTYPE_FRAGMENT].push_back(fs);
149	glu::ShaderProgram renderShader(context.getRenderContext(), sources);
150
151	if (!m_program->isOk())
152	{
153		TCU_FAIL("Shader compilation failed");
154	}
155
156	gl.bindTexture(GL_TEXTURE_2D, *outputTexture);
157	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed.");
158
159	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
160	gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
161	GLU_EXPECT_NO_ERROR(gl.getError(), "texParameteri failed");
162
163	gl.useProgram(renderShader.getProgram());
164	GLU_EXPECT_NO_ERROR(gl.getError(), "useProgram failed");
165
166	gl.uniform1i(gl.getUniformLocation(renderShader.getProgram(), "sampler"), 0);
167	GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform1i failed");
168
169	deUint16 const quadIndices[] = { 0, 1, 2, 2, 1, 3 };
170
171	float const position[] = { -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f };
172
173	float const texCoord[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f };
174
175	glu::VertexArrayBinding vertexArrays[] = { glu::va::Float("position", 2, 4, 0, position),
176											   glu::va::Float("inTexcoord", 2, 4, 0, texCoord) };
177
178	gl.viewport(0, 0, renderSize.width, renderSize.height);
179	glu::draw(context.getRenderContext(), renderShader.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays,
180			  glu::pr::TriangleStrip(DE_LENGTH_OF_ARRAY(quadIndices), quadIndices));
181
182	GLU_EXPECT_NO_ERROR(gl.getError(), "glu::draw error");
183
184	gl.flush();
185}
186
187void ShaderGroupVoteTestCaseBase::ComputeShader::validate(deqp::Context& context)
188{
189	if (m_compileOnly)
190	{
191		return;
192	}
193
194	bool		validationResult   = validateScreenPixels(context, m_desiredColor);
195	std::string validationErrorMsg = "Validation failed for " + m_name + " test";
196
197	TCU_CHECK_MSG(validationResult, validationErrorMsg.c_str());
198}
199
200bool ShaderGroupVoteTestCaseBase::ComputeShader::validateScreenPixels(deqp::Context& context, tcu::IVec4 desiredColor)
201{
202	const glw::Functions&	 gl		= context.getRenderContext().getFunctions();
203	const WorkGroupSize		  renderSize(context);
204	std::size_t				  totalSize = renderSize.width * renderSize.height * 4;
205	std::vector<glw::GLubyte> pixels(totalSize, 128);
206
207	// read pixels
208	gl.readPixels(0, 0, renderSize.width, renderSize.height, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
209
210	// compare pixels to desired color
211	for (std::size_t i = 0; i < totalSize; i += 4)
212	{
213		if ((pixels[i + 0] != desiredColor.x()) || (pixels[i + 1] != desiredColor.y()) ||
214			(pixels[i + 2] != desiredColor.z()))
215			return false;
216	}
217
218	return true;
219}
220
221/** Constructor.
222*
223*  @param context Rendering context
224*  @param name Test name
225*  @param description Test description
226*/
227ShaderGroupVoteTestCaseBase::ShaderGroupVoteTestCaseBase(deqp::Context& context, ExtParameters& extParam,
228														 const char* name, const char* description)
229	: TestCaseBase(context, glcts::ExtParameters(glu::GLSL_VERSION_450, glcts::EXTENSIONTYPE_EXT), name, description)
230	, m_extensionSupported(true)
231{
232	const WorkGroupSize workGroupSize(context);
233	glu::ContextType contextType   = m_context.getRenderContext().getType();
234	m_specializationMap["VERSION"] = glu::getGLSLVersionDeclaration(extParam.glslVersion);
235
236	std::stringstream stream;
237	stream << workGroupSize.width << " " << workGroupSize.height;
238	stream >> m_specializationMap["SIZE_X"] >> m_specializationMap["SIZE_Y"];
239
240	if (glu::contextSupports(contextType, glu::ApiType::core(4, 6)))
241	{
242		m_specializationMap["GROUP_VOTE_EXTENSION"] = "";
243		m_specializationMap["EXT_TYPE"]				= "";
244	}
245	else
246	{
247		bool		isCoreGL	  = glu::isContextTypeGLCore(contextType);
248		std::string extensionName = isCoreGL ? "GL_ARB_shader_group_vote" : "GL_EXT_shader_group_vote";
249		m_extensionSupported	  = context.getContextInfo().isExtensionSupported(extensionName.c_str());
250		std::stringstream extensionString;
251		extensionString << "#extension " + extensionName + " : enable";
252
253		m_specializationMap["GROUP_VOTE_EXTENSION"] = extensionString.str();
254		m_specializationMap["EXT_TYPE"]				= isCoreGL ? "ARB" : "EXT";
255	}
256}
257
258void ShaderGroupVoteTestCaseBase::init()
259{
260	if (m_extensionSupported)
261	{
262		for (ComputeShaderIter iter = m_shaders.begin(); iter != m_shaders.end(); ++iter)
263		{
264			(*iter)->create(m_context);
265		}
266	}
267}
268
269void ShaderGroupVoteTestCaseBase::deinit()
270{
271	for (ComputeShaderIter iter = m_shaders.begin(); iter != m_shaders.end(); ++iter)
272	{
273		delete (*iter);
274	}
275}
276
277tcu::TestNode::IterateResult ShaderGroupVoteTestCaseBase::iterate()
278{
279	if (!m_extensionSupported)
280	{
281		m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
282		return STOP;
283	}
284
285	for (ComputeShaderIter iter = m_shaders.begin(); iter != m_shaders.end(); ++iter)
286	{
287		(*iter)->execute(m_context);
288		(*iter)->validate(m_context);
289	}
290
291	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
292	return STOP;
293}
294
295/** Constructor.
296*
297*  @param context Rendering context
298*/
299ShaderGroupVoteAvailabilityTestCase::ShaderGroupVoteAvailabilityTestCase(deqp::Context& context,
300																		 ExtParameters& extParam)
301	: ShaderGroupVoteTestCaseBase(context, extParam, "availability", "Implements ...")
302{
303	const char* shader = "${VERSION}\n"
304						 "${GROUP_VOTE_EXTENSION}\n"
305						 "layout(rgba8, binding = 2) writeonly uniform highp image2D destImage;\n"
306						 "layout(local_size_x = ${SIZE_X}, local_size_y = ${SIZE_Y}) in;\n"
307						 "void main (void)\n"
308						 "{\n"
309						 "	vec4 outColor = vec4(0.0);\n"
310						 "	outColor.r = allInvocations${EXT_TYPE}(true) ? 1.0 : 0.0;\n"
311						 "	outColor.g = anyInvocation${EXT_TYPE}(true) ? 1.0 : 0.0;\n"
312						 "	outColor.b = allInvocationsEqual${EXT_TYPE}(true) ? 1.0 : 0.0;\n"
313						 "	imageStore(destImage, ivec2(gl_GlobalInvocationID.xy), outColor);\n"
314						 "}\n";
315
316	m_shaders.push_back(new ComputeShader("availability", specializeShader(1, &shader)));
317}
318
319/** Constructor.
320*
321*  @param context Rendering context
322*  @param name Test name
323*  @param description Test description
324*/
325ShaderGroupVoteFunctionTestCaseBase::ShaderGroupVoteFunctionTestCaseBase(deqp::Context& context,
326																		 ExtParameters& extParam, const char* name,
327																		 const char* description)
328	: ShaderGroupVoteTestCaseBase(context, extParam, name, description)
329{
330	m_shaderBase += "${VERSION}\n"
331					"${GROUP_VOTE_EXTENSION}\n"
332					"layout(rgba8, binding = 2) writeonly uniform highp image2D destImage;\n"
333					"layout(local_size_x = ${SIZE_X}, local_size_y = ${SIZE_Y}) in;\n"
334					"void main (void)\n"
335					"{\n"
336					"	bool result = ${FUNC}${EXT_TYPE}(${FUNC_PARAMETER});\n"
337					"	vec4 outColor = vec4(vec3(result ? 1.0 : 0.0), 1.0);\n"
338					"	imageStore(destImage, ivec2(gl_GlobalInvocationID.xy), outColor);\n"
339					"}\n";
340}
341
342/** Constructor.
343*
344*  @param context Rendering context
345*/
346ShaderGroupVoteAllInvocationsTestCase::ShaderGroupVoteAllInvocationsTestCase(deqp::Context& context,
347																			 ExtParameters& extParam)
348	: ShaderGroupVoteFunctionTestCaseBase(context, extParam, "all_invocations", "Implements ...")
349{
350	const char* shaderBase				  = m_shaderBase.c_str();
351	m_specializationMap["FUNC"]			  = "allInvocations";
352	m_specializationMap["FUNC_PARAMETER"] = "true";
353
354	m_shaders.push_back(
355		new ComputeShader("allInvocationsARB", specializeShader(1, &shaderBase), tcu::IVec4(255, 255, 255, 255)));
356}
357
358/** Constructor.
359*
360*  @param context Rendering context
361*/
362ShaderGroupVoteAnyInvocationTestCase::ShaderGroupVoteAnyInvocationTestCase(deqp::Context& context,
363																		   ExtParameters& extParam)
364	: ShaderGroupVoteFunctionTestCaseBase(context, extParam, "any_invocation", "Implements ...")
365{
366	const char* shaderBase				  = m_shaderBase.c_str();
367	m_specializationMap["FUNC"]			  = "anyInvocation";
368	m_specializationMap["FUNC_PARAMETER"] = "false";
369
370	m_shaders.push_back(
371		new ComputeShader("anyInvocationARB", specializeShader(1, &shaderBase), tcu::IVec4(0, 0, 0, 255)));
372}
373
374/** Constructor.
375*
376*  @param context Rendering context
377*/
378ShaderGroupVoteAllInvocationsEqualTestCase::ShaderGroupVoteAllInvocationsEqualTestCase(deqp::Context& context,
379																					   ExtParameters& extParam)
380	: ShaderGroupVoteFunctionTestCaseBase(context, extParam, "all_invocations_equal", "Implements ...")
381{
382	const char* shaderBase				  = m_shaderBase.c_str();
383	m_specializationMap["FUNC"]			  = "allInvocationsEqual";
384	m_specializationMap["FUNC_PARAMETER"] = "true";
385	m_shaders.push_back(
386		new ComputeShader("allInvocationsEqualARB", specializeShader(1, &shaderBase), tcu::IVec4(255, 255, 255, 255)));
387
388	m_specializationMap["FUNC"]			  = "allInvocationsEqual";
389	m_specializationMap["FUNC_PARAMETER"] = "false";
390	m_shaders.push_back(
391		new ComputeShader("allInvocationsEqualARB", specializeShader(1, &shaderBase), tcu::IVec4(255, 255, 255, 255)));
392}
393
394/** Constructor.
395*
396*  @param context Rendering context
397*/
398ShaderGroupVoteWithVariablesTestCase::ShaderGroupVoteWithVariablesTestCase(deqp::Context& context,
399																		   ExtParameters& extParam)
400	: ShaderGroupVoteTestCaseBase(context, extParam, "invocations_with_variables", "Implements ...")
401{
402	const char* shaderBase = "${VERSION}\n"
403							 "${GROUP_VOTE_EXTENSION}\n"
404							 "layout(rgba8, binding = 2) writeonly uniform highp image2D destImage;\n"
405							 "layout(local_size_x = ${SIZE_X}, local_size_y = ${SIZE_Y}) in;\n"
406							 "void main (void)\n"
407							 "{\n"
408							 "	bool result = ${EXPRESSION};\n"
409							 "	vec4 outColor = vec4(vec3(result ? 1.0 : 0.0), 1.0);\n"
410							 "	imageStore(destImage, ivec2(gl_GlobalInvocationID.xy), outColor);\n"
411							 "}\n";
412
413	// first specialization EXPRESSION and then whole shader
414	const char* expression1 = "allInvocations${EXT_TYPE}((gl_LocalInvocationIndex % 2u) == 1u) && "
415							  "anyInvocation${EXT_TYPE}((gl_LocalInvocationIndex % 2u) == 0u) && "
416							  "anyInvocation${EXT_TYPE}((gl_LocalInvocationIndex % 2u) == 1u)";
417	m_specializationMap["EXPRESSION"] = specializeShader(1, &expression1);
418	m_shaders.push_back(
419		new ComputeShader("allInvocations", specializeShader(1, &shaderBase), tcu::IVec4(0, 0, 0, 255)));
420
421	const char* expression2			  = "anyInvocation${EXT_TYPE}(gl_LocalInvocationIndex < 256u)";
422	m_specializationMap["EXPRESSION"] = specializeShader(1, &expression2);
423	m_shaders.push_back(
424		new ComputeShader("anyInvocation", specializeShader(1, &shaderBase), tcu::IVec4(255, 255, 255, 255)));
425
426	const char* expression3			  = "allInvocationsEqual${EXT_TYPE}(gl_WorkGroupID.x == 0u)";
427	m_specializationMap["EXPRESSION"] = specializeShader(1, &expression3);
428	m_shaders.push_back(
429		new ComputeShader("anyInvocation", specializeShader(1, &shaderBase), tcu::IVec4(255, 255, 255, 255)));
430}
431
432/** Constructor.
433*
434*  @param context Rendering context.
435*/
436ShaderGroupVote::ShaderGroupVote(deqp::Context& context)
437	: TestCaseGroup(context, "shader_group_vote",
438					"Verify conformance of shader_group_vote functionality implementation")
439{
440}
441
442/** Initializes the test group contents. */
443void ShaderGroupVote::init()
444{
445	glu::GLSLVersion glslVersion = getContextTypeGLSLVersion(m_context.getRenderContext().getType());
446	ExtParameters	extParam	= glcts::ExtParameters(glslVersion, glcts::EXTENSIONTYPE_EXT);
447
448	addChild(new ShaderGroupVoteAvailabilityTestCase(m_context, extParam));
449	addChild(new ShaderGroupVoteAllInvocationsTestCase(m_context, extParam));
450	addChild(new ShaderGroupVoteAnyInvocationTestCase(m_context, extParam));
451	addChild(new ShaderGroupVoteAllInvocationsEqualTestCase(m_context, extParam));
452	addChild(new ShaderGroupVoteWithVariablesTestCase(m_context, extParam));
453}
454} /* glcts namespace */
455