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