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 Tessellation and geometry shader interaction tests.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "es31fTessellationGeometryInteractionTests.hpp"
25 
26 #include "tcuTestLog.hpp"
27 #include "tcuRenderTarget.hpp"
28 #include "tcuSurface.hpp"
29 #include "tcuImageCompare.hpp"
30 #include "tcuVectorUtil.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuStringTemplate.hpp"
33 #include "gluRenderContext.hpp"
34 #include "gluShaderProgram.hpp"
35 #include "gluStrUtil.hpp"
36 #include "gluContextInfo.hpp"
37 #include "gluObjectWrapper.hpp"
38 #include "gluPixelTransfer.hpp"
39 #include "glwFunctions.hpp"
40 #include "glwEnums.hpp"
41 #include "deStringUtil.hpp"
42 #include "deUniquePtr.hpp"
43 
44 #include <sstream>
45 #include <algorithm>
46 #include <iterator>
47 
48 namespace deqp
49 {
50 namespace gles31
51 {
52 namespace Functional
53 {
54 namespace
55 {
56 
specializeShader(const std::string & shaderSource,const glu::ContextType & contextType)57 static std::string specializeShader(const std::string &shaderSource, const glu::ContextType &contextType)
58 {
59     const bool supportsES32orGL45 = glu::contextSupports(contextType, glu::ApiType::es(3, 2)) ||
60                                     glu::contextSupports(contextType, glu::ApiType::core(4, 5));
61 
62     const bool supportsGL45 = glu::contextSupports(contextType, glu::ApiType::core(4, 5));
63 
64     std::map<std::string, std::string> shaderArgs;
65 
66     shaderArgs["VERSION_DECL"] = glu::getGLSLVersionDeclaration(glu::getContextTypeGLSLVersion(contextType));
67     shaderArgs["EXTENSION_GEOMETRY_SHADER"] =
68         (supportsES32orGL45) ? ("") : ("#extension GL_EXT_geometry_shader : require\n");
69     shaderArgs["EXTENSION_TESSELATION_SHADER"] =
70         (supportsES32orGL45) ? ("") : ("#extension GL_EXT_tessellation_shader : require\n");
71     shaderArgs["EXTENSION_TESSELATION_POINT_SIZE"] =
72         (supportsGL45) ? ("") : ("#extension GL_EXT_tessellation_point_size : require\n");
73     shaderArgs["EXTENSION_GEOMETRY_POINT_SIZE"] =
74         (supportsGL45) ? ("") : ("#extension GL_EXT_geometry_point_size : require\n");
75 
76     return tcu::StringTemplate(shaderSource).specialize(shaderArgs);
77 }
78 
79 static const char *const s_positionVertexShader      = "${VERSION_DECL}\n"
80                                                        "in highp vec4 a_position;\n"
81                                                        "void main (void)\n"
82                                                        "{\n"
83                                                        "    gl_Position = a_position;\n"
84                                                        "}\n";
85 static const char *const s_whiteOutputFragmentShader = "${VERSION_DECL}\n"
86                                                        "layout(location = 0) out mediump vec4 fragColor;\n"
87                                                        "void main (void)\n"
88                                                        "{\n"
89                                                        "    fragColor = vec4(1.0);\n"
90                                                        "}\n";
91 
isBlack(const tcu::RGBA & c)92 static bool isBlack(const tcu::RGBA &c)
93 {
94     return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0;
95 }
96 
97 class IdentityShaderCase : public TestCase
98 {
99 public:
100     IdentityShaderCase(Context &context, const char *name, const char *description);
101 
102 protected:
103     std::string getVertexSource(void) const;
104     std::string getFragmentSource(void) const;
105 };
106 
IdentityShaderCase(Context & context,const char * name,const char * description)107 IdentityShaderCase::IdentityShaderCase(Context &context, const char *name, const char *description)
108     : TestCase(context, name, description)
109 {
110 }
111 
getVertexSource(void) const112 std::string IdentityShaderCase::getVertexSource(void) const
113 {
114     std::string source = "${VERSION_DECL}\n"
115                          "in highp vec4 a_position;\n"
116                          "out highp vec4 v_vertex_color;\n"
117                          "void main (void)\n"
118                          "{\n"
119                          "    gl_Position = a_position;\n"
120                          "    v_vertex_color = vec4(a_position.x * 0.5 + 0.5, a_position.y * 0.5 + 0.5, 1.0, 0.4);\n"
121                          "}\n";
122 
123     return specializeShader(source, m_context.getRenderContext().getType());
124 }
125 
getFragmentSource(void) const126 std::string IdentityShaderCase::getFragmentSource(void) const
127 {
128     std::string source = "${VERSION_DECL}\n"
129                          "in mediump vec4 v_fragment_color;\n"
130                          "layout(location = 0) out mediump vec4 fragColor;\n"
131                          "void main (void)\n"
132                          "{\n"
133                          "    fragColor = v_fragment_color;\n"
134                          "}\n";
135 
136     return specializeShader(source, m_context.getRenderContext().getType());
137 }
138 
139 class IdentityGeometryShaderCase : public IdentityShaderCase
140 {
141 public:
142     enum CaseType
143     {
144         CASE_TRIANGLES = 0,
145         CASE_QUADS,
146         CASE_ISOLINES,
147     };
148 
149     IdentityGeometryShaderCase(Context &context, const char *name, const char *description, CaseType caseType);
150     ~IdentityGeometryShaderCase(void);
151 
152 private:
153     void init(void);
154     void deinit(void);
155     IterateResult iterate(void);
156 
157     std::string getTessellationControlSource(void) const;
158     std::string getTessellationEvaluationSource(bool geometryActive) const;
159     std::string getGeometrySource(void) const;
160 
161     enum
162     {
163         RENDER_SIZE = 128,
164     };
165 
166     const CaseType m_case;
167     uint32_t m_patchBuffer;
168 };
169 
IdentityGeometryShaderCase(Context & context,const char * name,const char * description,CaseType caseType)170 IdentityGeometryShaderCase::IdentityGeometryShaderCase(Context &context, const char *name, const char *description,
171                                                        CaseType caseType)
172     : IdentityShaderCase(context, name, description)
173     , m_case(caseType)
174     , m_patchBuffer(0)
175 {
176 }
177 
~IdentityGeometryShaderCase(void)178 IdentityGeometryShaderCase::~IdentityGeometryShaderCase(void)
179 {
180     deinit();
181 }
182 
init(void)183 void IdentityGeometryShaderCase::init(void)
184 {
185     // Requirements
186     const bool supportsES32orGL45 =
187         glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
188         glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
189 
190     if (!supportsES32orGL45 && (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
191                                 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
192         throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
193 
194     if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE)
195         throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" +
196                                      de::toString<int>(RENDER_SIZE) + " or larger render target.");
197 
198     // Log
199 
200     m_testCtx.getLog()
201         << tcu::TestLog::Message
202         << "Testing tessellating shader program output does not change when a passthrough geometry shader is "
203            "attached.\n"
204         << "Rendering two images, first with and second without a geometry shader. Expecting similar results.\n"
205         << "Using additive blending to detect overlap.\n"
206         << tcu::TestLog::EndMessage;
207 
208     // Resources
209 
210     {
211         static const tcu::Vec4 patchBufferData[4] = {
212             tcu::Vec4(-0.9f, -0.9f, 0.0f, 1.0f),
213             tcu::Vec4(-0.9f, 0.9f, 0.0f, 1.0f),
214             tcu::Vec4(0.9f, -0.9f, 0.0f, 1.0f),
215             tcu::Vec4(0.9f, 0.9f, 0.0f, 1.0f),
216         };
217 
218         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
219 
220         gl.genBuffers(1, &m_patchBuffer);
221         gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
222         gl.bufferData(GL_ARRAY_BUFFER, sizeof(patchBufferData), patchBufferData, GL_STATIC_DRAW);
223         GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
224     }
225 }
226 
deinit(void)227 void IdentityGeometryShaderCase::deinit(void)
228 {
229     if (m_patchBuffer)
230     {
231         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_patchBuffer);
232         m_patchBuffer = 0;
233     }
234 }
235 
iterate(void)236 IdentityGeometryShaderCase::IterateResult IdentityGeometryShaderCase::iterate(void)
237 {
238     const float innerTessellationLevel = 14.0f;
239     const float outerTessellationLevel = 14.0f;
240     const glw::Functions &gl           = m_context.getRenderContext().getFunctions();
241     tcu::Surface resultWithGeometry(RENDER_SIZE, RENDER_SIZE);
242     tcu::Surface resultWithoutGeometry(RENDER_SIZE, RENDER_SIZE);
243 
244     const struct
245     {
246         const char *name;
247         const char *description;
248         bool containsGeometryShader;
249         tcu::PixelBufferAccess surfaceAccess;
250     } renderTargets[] = {
251         {"RenderWithGeometryShader", "Render with geometry shader", true, resultWithGeometry.getAccess()},
252         {"RenderWithoutGeometryShader", "Render without geometry shader", false, resultWithoutGeometry.getAccess()},
253     };
254 
255     gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
256     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
257     GLU_EXPECT_NO_ERROR(gl.getError(), "set viewport");
258 
259     gl.enable(GL_BLEND);
260     gl.blendFunc(GL_SRC_ALPHA, GL_ONE);
261     gl.blendEquation(GL_FUNC_ADD);
262     GLU_EXPECT_NO_ERROR(gl.getError(), "set blend");
263 
264     m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation level: inner " << innerTessellationLevel << ", outer "
265                        << outerTessellationLevel << tcu::TestLog::EndMessage;
266 
267     // render with and without geometry shader
268     for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renderTargets); ++renderNdx)
269     {
270         const tcu::ScopedLogSection section(m_testCtx.getLog(), renderTargets[renderNdx].name,
271                                             renderTargets[renderNdx].description);
272         glu::ProgramSources sources;
273 
274         sources << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
275                 << glu::TessellationControlSource(getTessellationControlSource())
276                 << glu::TessellationEvaluationSource(
277                        getTessellationEvaluationSource(renderTargets[renderNdx].containsGeometryShader));
278 
279         if (renderTargets[renderNdx].containsGeometryShader)
280             sources << glu::GeometrySource(getGeometrySource());
281 
282         {
283             const glu::ShaderProgram program(m_context.getRenderContext(), sources);
284             const glu::VertexArray vao(m_context.getRenderContext());
285             const int posLocation          = gl.getAttribLocation(program.getProgram(), "a_position");
286             const int innerTessellationLoc = gl.getUniformLocation(program.getProgram(), "u_innerTessellationLevel");
287             const int outerTessellationLoc = gl.getUniformLocation(program.getProgram(), "u_outerTessellationLevel");
288 
289             m_testCtx.getLog() << program;
290 
291             if (!program.isOk())
292                 throw tcu::TestError("could not build program");
293             if (posLocation == -1)
294                 throw tcu::TestError("a_position location was -1");
295             if (outerTessellationLoc == -1)
296                 throw tcu::TestError("u_outerTessellationLevel location was -1");
297 
298             gl.bindVertexArray(*vao);
299             gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
300             gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
301             gl.enableVertexAttribArray(posLocation);
302             GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
303 
304             gl.useProgram(program.getProgram());
305             gl.uniform1f(outerTessellationLoc, outerTessellationLevel);
306 
307             if (innerTessellationLoc == -1)
308                 gl.uniform1f(innerTessellationLoc, innerTessellationLevel);
309 
310             GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
311 
312             gl.patchParameteri(GL_PATCH_VERTICES, (m_case == CASE_TRIANGLES) ? (3) : (4));
313             GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
314 
315             gl.clear(GL_COLOR_BUFFER_BIT);
316             GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
317 
318             gl.drawArrays(GL_PATCHES, 0, 4);
319             GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
320 
321             glu::readPixels(m_context.getRenderContext(), 0, 0, renderTargets[renderNdx].surfaceAccess);
322         }
323     }
324 
325     if (tcu::intThresholdPositionDeviationCompare(m_testCtx.getLog(), "ImageCompare", "Image comparison",
326                                                   resultWithoutGeometry.getAccess(), resultWithGeometry.getAccess(),
327                                                   tcu::UVec4(8, 8, 8, 255), tcu::IVec3(1, 1, 0), true,
328                                                   tcu::COMPARE_LOG_RESULT))
329         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
330     else
331         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
332 
333     return STOP;
334 }
335 
getTessellationControlSource(void) const336 std::string IdentityGeometryShaderCase::getTessellationControlSource(void) const
337 {
338     std::ostringstream buf;
339 
340     buf << "${VERSION_DECL}\n"
341            "${EXTENSION_TESSELATION_SHADER}"
342            "layout(vertices = 4) out;\n"
343            "\n"
344            "uniform highp float u_innerTessellationLevel;\n"
345            "uniform highp float u_outerTessellationLevel;\n"
346            "in highp vec4 v_vertex_color[];\n"
347            "out highp vec4 v_patch_color[];\n"
348            "\n"
349            "void main (void)\n"
350            "{\n"
351            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
352            "    v_patch_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
353            "\n";
354 
355     if (m_case == CASE_TRIANGLES)
356         buf << "    gl_TessLevelOuter[0] = u_outerTessellationLevel;\n"
357                "    gl_TessLevelOuter[1] = u_outerTessellationLevel;\n"
358                "    gl_TessLevelOuter[2] = u_outerTessellationLevel;\n"
359                "    gl_TessLevelInner[0] = u_innerTessellationLevel;\n";
360     else if (m_case == CASE_QUADS)
361         buf << "    gl_TessLevelOuter[0] = u_outerTessellationLevel;\n"
362                "    gl_TessLevelOuter[1] = u_outerTessellationLevel;\n"
363                "    gl_TessLevelOuter[2] = u_outerTessellationLevel;\n"
364                "    gl_TessLevelOuter[3] = u_outerTessellationLevel;\n"
365                "    gl_TessLevelInner[0] = u_innerTessellationLevel;\n"
366                "    gl_TessLevelInner[1] = u_innerTessellationLevel;\n";
367     else if (m_case == CASE_ISOLINES)
368         buf << "    gl_TessLevelOuter[0] = u_outerTessellationLevel;\n"
369                "    gl_TessLevelOuter[1] = u_outerTessellationLevel;\n";
370     else
371         DE_ASSERT(false);
372 
373     buf << "}\n";
374 
375     return specializeShader(buf.str(), m_context.getRenderContext().getType());
376 }
377 
getTessellationEvaluationSource(bool geometryActive) const378 std::string IdentityGeometryShaderCase::getTessellationEvaluationSource(bool geometryActive) const
379 {
380     const char *const colorOutputName = ((geometryActive) ? ("v_evaluated_color") : ("v_fragment_color"));
381     std::ostringstream buf;
382 
383     buf << "${VERSION_DECL}\n"
384            "${EXTENSION_TESSELATION_SHADER}"
385            "layout("
386         << ((m_case == CASE_TRIANGLES) ? ("triangles") :
387             (m_case == CASE_QUADS)     ? ("quads") :
388                                          ("isolines"))
389         << ") in;\n"
390            "\n"
391            "in highp vec4 v_patch_color[];\n"
392            "out highp vec4 "
393         << colorOutputName
394         << ";\n"
395            "\n"
396            "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
397            "void main (void)\n"
398            "{\n";
399 
400     if (m_case == CASE_TRIANGLES)
401         buf << "    vec3 weights = vec3(pow(gl_TessCoord.x, 1.3), pow(gl_TessCoord.y, 1.3), pow(gl_TessCoord.z, "
402                "1.3));\n"
403                "    vec3 cweights = gl_TessCoord;\n"
404                "    gl_Position = vec4(weights.x * gl_in[0].gl_Position.xyz + weights.y * gl_in[1].gl_Position.xyz + "
405                "weights.z * gl_in[2].gl_Position.xyz, 1.0);\n"
406                "    "
407             << colorOutputName
408             << " = cweights.x * v_patch_color[0] + cweights.y * v_patch_color[1] + cweights.z * v_patch_color[2];\n";
409     else if (m_case == CASE_QUADS || m_case == CASE_ISOLINES)
410         buf << "    vec2 normalizedCoord = (gl_TessCoord.xy * 2.0 - vec2(1.0));\n"
411                "    vec2 normalizedWeights = normalizedCoord * (vec2(1.0) - 0.3 * cos(normalizedCoord.yx * 1.57));\n"
412                "    vec2 weights = normalizedWeights * 0.5 + vec2(0.5);\n"
413                "    vec2 cweights = gl_TessCoord.xy;\n"
414                "    gl_Position = mix(mix(gl_in[0].gl_Position, gl_in[1].gl_Position, weights.y), "
415                "mix(gl_in[2].gl_Position, gl_in[3].gl_Position, weights.y), weights.x);\n"
416                "    "
417             << colorOutputName
418             << " = mix(mix(v_patch_color[0], v_patch_color[1], cweights.y), mix(v_patch_color[2], v_patch_color[3], "
419                "cweights.y), cweights.x);\n";
420     else
421         DE_ASSERT(false);
422 
423     buf << "}\n";
424 
425     return specializeShader(buf.str(), m_context.getRenderContext().getType());
426 }
427 
getGeometrySource(void) const428 std::string IdentityGeometryShaderCase::getGeometrySource(void) const
429 {
430     const char *const geometryInputPrimitive  = (m_case == CASE_ISOLINES) ? ("lines") : ("triangles");
431     const char *const geometryOutputPrimitive = (m_case == CASE_ISOLINES) ? ("line_strip") : ("triangle_strip");
432     const int numEmitVertices                 = (m_case == CASE_ISOLINES) ? (2) : (3);
433     std::ostringstream buf;
434 
435     buf << "${VERSION_DECL}\n"
436            "${EXTENSION_GEOMETRY_SHADER}"
437            "layout("
438         << geometryInputPrimitive
439         << ") in;\n"
440            "layout("
441         << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices
442         << ") out;\n"
443            "\n"
444            "in highp vec4 v_evaluated_color[];\n"
445            "out highp vec4 v_fragment_color;\n"
446            "\n"
447            "void main (void)\n"
448            "{\n"
449            "    for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n"
450            "    {\n"
451            "        gl_Position = gl_in[ndx].gl_Position;\n"
452            "        v_fragment_color = v_evaluated_color[ndx];\n"
453            "        EmitVertex();\n"
454            "    }\n"
455            "}\n";
456 
457     return specializeShader(buf.str(), m_context.getRenderContext().getType());
458 }
459 
460 class IdentityTessellationShaderCase : public IdentityShaderCase
461 {
462 public:
463     enum CaseType
464     {
465         CASE_TRIANGLES = 0,
466         CASE_ISOLINES,
467     };
468 
469     IdentityTessellationShaderCase(Context &context, const char *name, const char *description, CaseType caseType);
470     ~IdentityTessellationShaderCase(void);
471 
472 private:
473     void init(void);
474     void deinit(void);
475     IterateResult iterate(void);
476 
477     std::string getTessellationControlSource(void) const;
478     std::string getTessellationEvaluationSource(void) const;
479     std::string getGeometrySource(bool tessellationActive) const;
480 
481     enum
482     {
483         RENDER_SIZE = 256,
484     };
485 
486     const CaseType m_case;
487     uint32_t m_dataBuffer;
488 };
489 
IdentityTessellationShaderCase(Context & context,const char * name,const char * description,CaseType caseType)490 IdentityTessellationShaderCase::IdentityTessellationShaderCase(Context &context, const char *name,
491                                                                const char *description, CaseType caseType)
492     : IdentityShaderCase(context, name, description)
493     , m_case(caseType)
494     , m_dataBuffer(0)
495 {
496 }
497 
~IdentityTessellationShaderCase(void)498 IdentityTessellationShaderCase::~IdentityTessellationShaderCase(void)
499 {
500     deinit();
501 }
502 
init(void)503 void IdentityTessellationShaderCase::init(void)
504 {
505     // Requirements
506     const bool supportsES32orGL45 =
507         glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
508         glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
509 
510     if (!supportsES32orGL45 && (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
511                                 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
512         throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
513 
514     if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE)
515         throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" +
516                                      de::toString<int>(RENDER_SIZE) + " or larger render target.");
517 
518     // Log
519 
520     m_testCtx.getLog()
521         << tcu::TestLog::Message
522         << "Testing geometry shading shader program output does not change when a passthrough tessellation shader is "
523            "attached.\n"
524         << "Rendering two images, first with and second without a tessellation shader. Expecting similar results.\n"
525         << "Using additive blending to detect overlap.\n"
526         << tcu::TestLog::EndMessage;
527 
528     // Resources
529 
530     {
531         static const tcu::Vec4 pointData[] = {
532             tcu::Vec4(-0.4f, 0.4f, 0.0f, 1.0f),
533             tcu::Vec4(0.0f, -0.5f, 0.0f, 1.0f),
534             tcu::Vec4(0.4f, 0.4f, 0.0f, 1.0f),
535         };
536         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
537 
538         gl.genBuffers(1, &m_dataBuffer);
539         gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBuffer);
540         gl.bufferData(GL_ARRAY_BUFFER, sizeof(pointData), pointData, GL_STATIC_DRAW);
541         GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
542     }
543 }
544 
deinit(void)545 void IdentityTessellationShaderCase::deinit(void)
546 {
547     if (m_dataBuffer)
548     {
549         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_dataBuffer);
550         m_dataBuffer = 0;
551     }
552 }
553 
iterate(void)554 IdentityTessellationShaderCase::IterateResult IdentityTessellationShaderCase::iterate(void)
555 {
556     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
557     tcu::Surface resultWithTessellation(RENDER_SIZE, RENDER_SIZE);
558     tcu::Surface resultWithoutTessellation(RENDER_SIZE, RENDER_SIZE);
559     const int numPrimitiveVertices = (m_case == CASE_TRIANGLES) ? (3) : (2);
560 
561     const struct
562     {
563         const char *name;
564         const char *description;
565         bool containsTessellationShaders;
566         tcu::PixelBufferAccess surfaceAccess;
567     } renderTargets[] = {
568         {"RenderWithTessellationShader", "Render with tessellation shader", true, resultWithTessellation.getAccess()},
569         {"RenderWithoutTessellationShader", "Render without tessellation shader", false,
570          resultWithoutTessellation.getAccess()},
571     };
572 
573     gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
574     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
575     GLU_EXPECT_NO_ERROR(gl.getError(), "set viewport");
576 
577     gl.enable(GL_BLEND);
578     gl.blendFunc(GL_SRC_ALPHA, GL_ONE);
579     gl.blendEquation(GL_FUNC_ADD);
580     GLU_EXPECT_NO_ERROR(gl.getError(), "set blend");
581 
582     // render with and without tessellation shader
583     for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renderTargets); ++renderNdx)
584     {
585         const tcu::ScopedLogSection section(m_testCtx.getLog(), renderTargets[renderNdx].name,
586                                             renderTargets[renderNdx].description);
587         glu::ProgramSources sources;
588 
589         sources << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
590                 << glu::GeometrySource(getGeometrySource(renderTargets[renderNdx].containsTessellationShaders));
591 
592         if (renderTargets[renderNdx].containsTessellationShaders)
593             sources << glu::TessellationControlSource(getTessellationControlSource())
594                     << glu::TessellationEvaluationSource(getTessellationEvaluationSource());
595 
596         {
597             const glu::ShaderProgram program(m_context.getRenderContext(), sources);
598             const glu::VertexArray vao(m_context.getRenderContext());
599             const int posLocation = gl.getAttribLocation(program.getProgram(), "a_position");
600 
601             m_testCtx.getLog() << program;
602 
603             if (!program.isOk())
604                 throw tcu::TestError("could not build program");
605             if (posLocation == -1)
606                 throw tcu::TestError("a_position location was -1");
607 
608             gl.bindVertexArray(*vao);
609             gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBuffer);
610             gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
611             gl.enableVertexAttribArray(posLocation);
612             GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
613 
614             gl.useProgram(program.getProgram());
615             GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
616 
617             gl.clear(GL_COLOR_BUFFER_BIT);
618             GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
619 
620             if (renderTargets[renderNdx].containsTessellationShaders)
621             {
622                 gl.patchParameteri(GL_PATCH_VERTICES, numPrimitiveVertices);
623                 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
624 
625                 gl.drawArrays(GL_PATCHES, 0, numPrimitiveVertices);
626                 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
627             }
628             else
629             {
630                 gl.drawArrays((m_case == CASE_TRIANGLES) ? (GL_TRIANGLES) : (GL_LINES), 0, numPrimitiveVertices);
631                 GLU_EXPECT_NO_ERROR(gl.getError(), "draw primitives");
632             }
633 
634             glu::readPixels(m_context.getRenderContext(), 0, 0, renderTargets[renderNdx].surfaceAccess);
635         }
636     }
637 
638     // compare
639     {
640         bool imageOk;
641 
642         if (m_context.getRenderTarget().getNumSamples() > 1)
643             imageOk = tcu::fuzzyCompare(m_testCtx.getLog(), "ImageCompare", "Image comparison",
644                                         resultWithoutTessellation.getAccess(), resultWithTessellation.getAccess(),
645                                         0.03f, tcu::COMPARE_LOG_RESULT);
646         else
647             imageOk = tcu::intThresholdPositionDeviationCompare(
648                 m_testCtx.getLog(), "ImageCompare", "Image comparison", resultWithoutTessellation.getAccess(),
649                 resultWithTessellation.getAccess(), tcu::UVec4(8, 8, 8, 255), //!< threshold
650                 tcu::IVec3(1, 1, 0),                                          //!< 3x3 search kernel
651                 true, //!< fragments may end up over the viewport, just ignore them
652                 tcu::COMPARE_LOG_RESULT);
653 
654         if (imageOk)
655             m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
656         else
657             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
658     }
659 
660     return STOP;
661 }
662 
getTessellationControlSource(void) const663 std::string IdentityTessellationShaderCase::getTessellationControlSource(void) const
664 {
665     std::ostringstream buf;
666 
667     buf << "${VERSION_DECL}\n"
668            "${EXTENSION_TESSELATION_SHADER}"
669            "layout(vertices = "
670         << ((m_case == CASE_TRIANGLES) ? (3) : (2))
671         << ") out;\n"
672            "\n"
673            "in highp vec4 v_vertex_color[];\n"
674            "out highp vec4 v_control_color[];\n"
675            "\n"
676            "void main (void)\n"
677            "{\n"
678            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
679            "    v_control_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
680            "\n";
681 
682     if (m_case == CASE_TRIANGLES)
683         buf << "    gl_TessLevelOuter[0] = 1.0;\n"
684                "    gl_TessLevelOuter[1] = 1.0;\n"
685                "    gl_TessLevelOuter[2] = 1.0;\n"
686                "    gl_TessLevelInner[0] = 1.0;\n";
687     else if (m_case == CASE_ISOLINES)
688         buf << "    gl_TessLevelOuter[0] = 1.0;\n"
689                "    gl_TessLevelOuter[1] = 1.0;\n";
690     else
691         DE_ASSERT(false);
692 
693     buf << "}\n";
694 
695     return specializeShader(buf.str(), m_context.getRenderContext().getType());
696 }
697 
getTessellationEvaluationSource(void) const698 std::string IdentityTessellationShaderCase::getTessellationEvaluationSource(void) const
699 {
700     std::ostringstream buf;
701 
702     buf << "${VERSION_DECL}\n"
703            "${EXTENSION_TESSELATION_SHADER}"
704            "layout("
705         << ((m_case == CASE_TRIANGLES) ? ("triangles") : ("isolines"))
706         << ") in;\n"
707            "\n"
708            "in highp vec4 v_control_color[];\n"
709            "out highp vec4 v_evaluated_color;\n"
710            "\n"
711            "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
712            "void main (void)\n"
713            "{\n";
714 
715     if (m_case == CASE_TRIANGLES)
716         buf << "    gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + "
717                "gl_TessCoord.z * gl_in[2].gl_Position;\n"
718                "    v_evaluated_color = gl_TessCoord.x * v_control_color[0] + gl_TessCoord.y * v_control_color[1] + "
719                "gl_TessCoord.z * v_control_color[2];\n";
720     else if (m_case == CASE_ISOLINES)
721         buf << "    gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n"
722                "    v_evaluated_color = mix(v_control_color[0], v_control_color[1], gl_TessCoord.x);\n";
723     else
724         DE_ASSERT(false);
725 
726     buf << "}\n";
727 
728     return specializeShader(buf.str(), m_context.getRenderContext().getType());
729 }
730 
getGeometrySource(bool tessellationActive) const731 std::string IdentityTessellationShaderCase::getGeometrySource(bool tessellationActive) const
732 {
733     const char *const colorSourceName         = (tessellationActive) ? ("v_evaluated_color") : ("v_vertex_color");
734     const char *const geometryInputPrimitive  = (m_case == CASE_ISOLINES) ? ("lines") : ("triangles");
735     const char *const geometryOutputPrimitive = (m_case == CASE_ISOLINES) ? ("line_strip") : ("triangle_strip");
736     const int numEmitVertices                 = (m_case == CASE_ISOLINES) ? (11) : (8);
737     std::ostringstream buf;
738 
739     buf << "${VERSION_DECL}\n"
740            "${EXTENSION_GEOMETRY_SHADER}"
741            "layout("
742         << geometryInputPrimitive
743         << ") in;\n"
744            "layout("
745         << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices
746         << ") out;\n"
747            "\n"
748            "in highp vec4 "
749         << colorSourceName
750         << "[];\n"
751            "out highp vec4 v_fragment_color;\n"
752            "\n"
753            "void main (void)\n"
754            "{\n";
755 
756     if (m_case == CASE_TRIANGLES)
757     {
758         buf << "    vec4 centerPos = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3.0f;\n"
759                "\n"
760                "    for (int ndx = 0; ndx < 4; ++ndx)\n"
761                "    {\n"
762                "        gl_Position = centerPos + (centerPos - gl_in[ndx % 3].gl_Position);\n"
763                "        v_fragment_color = "
764             << colorSourceName
765             << "[ndx % 3];\n"
766                "        EmitVertex();\n"
767                "\n"
768                "        gl_Position = centerPos + 0.7 * (centerPos - gl_in[ndx % 3].gl_Position);\n"
769                "        v_fragment_color = "
770             << colorSourceName
771             << "[ndx % 3];\n"
772                "        EmitVertex();\n"
773                "    }\n";
774     }
775     else if (m_case == CASE_ISOLINES)
776     {
777         buf << "    vec4 mdir = vec4(gl_in[0].gl_Position.y - gl_in[1].gl_Position.y, gl_in[1].gl_Position.x - "
778                "gl_in[0].gl_Position.x, 0.0, 0.0);\n"
779                "    for (int i = 0; i <= 10; ++i)\n"
780                "    {\n"
781                "        float xweight = cos(float(i) / 10.0 * 6.28) * 0.5 + 0.5;\n"
782                "        float mweight = sin(float(i) / 10.0 * 6.28) * 0.1 + 0.1;\n"
783                "        gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, xweight) + mweight * mdir;\n"
784                "        v_fragment_color = mix("
785             << colorSourceName << "[0], " << colorSourceName
786             << "[1], xweight);\n"
787                "        EmitVertex();\n"
788                "    }\n";
789     }
790     else
791         DE_ASSERT(false);
792 
793     buf << "}\n";
794 
795     return specializeShader(buf.str(), m_context.getRenderContext().getType());
796 }
797 
798 class FeedbackPrimitiveTypeCase : public TestCase
799 {
800 public:
801     enum TessellationOutputType
802     {
803         TESSELLATION_OUT_TRIANGLES = 0,
804         TESSELLATION_OUT_QUADS,
805         TESSELLATION_OUT_ISOLINES,
806 
807         TESSELLATION_OUT_LAST
808     };
809     enum TessellationPointMode
810     {
811         TESSELLATION_POINTMODE_OFF = 0,
812         TESSELLATION_POINTMODE_ON,
813 
814         TESSELLATION_POINTMODE_LAST
815     };
816     enum GeometryOutputType
817     {
818         GEOMETRY_OUTPUT_POINTS = 0,
819         GEOMETRY_OUTPUT_LINES,
820         GEOMETRY_OUTPUT_TRIANGLES,
821 
822         GEOMETRY_OUTPUT_LAST
823     };
824 
825     FeedbackPrimitiveTypeCase(Context &context, const char *name, const char *description,
826                               TessellationOutputType tessellationOutput, TessellationPointMode tessellationPointMode,
827                               GeometryOutputType geometryOutputType);
828     ~FeedbackPrimitiveTypeCase(void);
829 
830 private:
831     void init(void);
832     void deinit(void);
833     IterateResult iterate(void);
834 
835     void renderWithFeedback(tcu::Surface &dst);
836     void renderWithoutFeedback(tcu::Surface &dst);
837     void verifyFeedbackResults(const std::vector<tcu::Vec4> &feedbackResult);
838     void verifyRenderedImage(const tcu::Surface &image, const std::vector<tcu::Vec4> &vertices);
839 
840     void genTransformFeedback(void);
841     int getNumGeneratedElementsPerPrimitive(void) const;
842     int getNumGeneratedPrimitives(void) const;
843     int getNumTessellatedPrimitives(void) const;
844     int getGeometryAmplification(void) const;
845 
846     std::string getVertexSource(void) const;
847     std::string getFragmentSource(void) const;
848     std::string getTessellationControlSource(void) const;
849     std::string getTessellationEvaluationSource(void) const;
850     std::string getGeometrySource(void) const;
851 
852     static const char *getTessellationOutputDescription(TessellationOutputType tessellationOutput,
853                                                         TessellationPointMode tessellationPointMode);
854     static const char *getGeometryInputDescription(TessellationOutputType tessellationOutput,
855                                                    TessellationPointMode tessellationPointMode);
856     static const char *getGeometryOutputDescription(GeometryOutputType geometryOutput);
857     glw::GLenum getOutputPrimitiveGLType(void) const;
858 
859     enum
860     {
861         RENDER_SIZE = 128,
862     };
863 
864     const TessellationOutputType m_tessellationOutput;
865     const TessellationPointMode m_tessellationPointMode;
866     const GeometryOutputType m_geometryOutputType;
867 
868     glu::ShaderProgram *m_feedbackProgram;
869     glu::ShaderProgram *m_nonFeedbackProgram;
870     uint32_t m_patchBuffer;
871     uint32_t m_feedbackID;
872     uint32_t m_feedbackBuffer;
873 };
874 
FeedbackPrimitiveTypeCase(Context & context,const char * name,const char * description,TessellationOutputType tessellationOutput,TessellationPointMode tessellationPointMode,GeometryOutputType geometryOutputType)875 FeedbackPrimitiveTypeCase::FeedbackPrimitiveTypeCase(Context &context, const char *name, const char *description,
876                                                      TessellationOutputType tessellationOutput,
877                                                      TessellationPointMode tessellationPointMode,
878                                                      GeometryOutputType geometryOutputType)
879     : TestCase(context, name, description)
880     , m_tessellationOutput(tessellationOutput)
881     , m_tessellationPointMode(tessellationPointMode)
882     , m_geometryOutputType(geometryOutputType)
883     , m_feedbackProgram(DE_NULL)
884     , m_nonFeedbackProgram(DE_NULL)
885     , m_patchBuffer(0)
886     , m_feedbackID(0)
887     , m_feedbackBuffer(0)
888 {
889     DE_ASSERT(tessellationOutput < TESSELLATION_OUT_LAST);
890     DE_ASSERT(tessellationPointMode < TESSELLATION_POINTMODE_LAST);
891     DE_ASSERT(geometryOutputType < GEOMETRY_OUTPUT_LAST);
892 }
893 
~FeedbackPrimitiveTypeCase(void)894 FeedbackPrimitiveTypeCase::~FeedbackPrimitiveTypeCase(void)
895 {
896     deinit();
897 }
898 
init(void)899 void FeedbackPrimitiveTypeCase::init(void)
900 {
901     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
902 
903     // Requirements
904     const bool supportsES32orGL45 =
905         glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
906         glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
907 
908     if (!supportsES32orGL45 && (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
909                                 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
910         throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
911 
912     if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || m_context.getRenderTarget().getHeight() < RENDER_SIZE)
913         throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" +
914                                      de::toString<int>(RENDER_SIZE) + " or larger render target.");
915 
916     // Log
917 
918     m_testCtx.getLog() << tcu::TestLog::Message << "Testing "
919                        << getTessellationOutputDescription(m_tessellationOutput, m_tessellationPointMode) << "->"
920                        << getGeometryInputDescription(m_tessellationOutput, m_tessellationPointMode)
921                        << " primitive conversion with and without transform feedback.\n"
922                        << "Sending a patch of 4 vertices (2x2 uniform grid) to tessellation control shader.\n"
923                        << "Control shader emits a patch of 9 vertices (3x3 uniform grid).\n"
924                        << "Setting outer tessellation level = 3, inner = 3.\n"
925                        << "Primitive generator emits "
926                        << getTessellationOutputDescription(m_tessellationOutput, m_tessellationPointMode) << "\n"
927                        << "Geometry shader transforms emitted primitives to "
928                        << getGeometryOutputDescription(m_geometryOutputType) << "\n"
929                        << "Reading back vertex positions of generated primitives using transform feedback.\n"
930                        << "Verifying rendered image and feedback vertices are consistent.\n"
931                        << "Rendering scene again with identical shader program, but without setting feedback varying. "
932                           "Expecting similar output image."
933                        << tcu::TestLog::EndMessage;
934 
935     // Resources
936 
937     {
938         static const tcu::Vec4 patchBufferData[4] = {
939             tcu::Vec4(-0.9f, -0.9f, 0.0f, 1.0f),
940             tcu::Vec4(-0.9f, 0.9f, 0.0f, 1.0f),
941             tcu::Vec4(0.9f, -0.9f, 0.0f, 1.0f),
942             tcu::Vec4(0.9f, 0.9f, 0.0f, 1.0f),
943         };
944 
945         gl.genBuffers(1, &m_patchBuffer);
946         gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
947         gl.bufferData(GL_ARRAY_BUFFER, sizeof(patchBufferData), patchBufferData, GL_STATIC_DRAW);
948         GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer");
949     }
950 
951     m_feedbackProgram = new glu::ShaderProgram(
952         m_context.getRenderContext(),
953         glu::ProgramSources() << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
954                               << glu::TessellationControlSource(getTessellationControlSource())
955                               << glu::TessellationEvaluationSource(getTessellationEvaluationSource())
956                               << glu::GeometrySource(getGeometrySource())
957                               << glu::TransformFeedbackVarying("tf_someVertexPosition")
958                               << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS));
959     m_testCtx.getLog() << *m_feedbackProgram;
960     if (!m_feedbackProgram->isOk())
961         throw tcu::TestError("failed to build program");
962 
963     m_nonFeedbackProgram = new glu::ShaderProgram(
964         m_context.getRenderContext(),
965         glu::ProgramSources() << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
966                               << glu::TessellationControlSource(getTessellationControlSource())
967                               << glu::TessellationEvaluationSource(getTessellationEvaluationSource())
968                               << glu::GeometrySource(getGeometrySource()));
969     if (!m_nonFeedbackProgram->isOk())
970     {
971         m_testCtx.getLog() << *m_nonFeedbackProgram;
972         throw tcu::TestError("failed to build program");
973     }
974 
975     genTransformFeedback();
976 }
977 
deinit(void)978 void FeedbackPrimitiveTypeCase::deinit(void)
979 {
980     if (m_patchBuffer)
981     {
982         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_patchBuffer);
983         m_patchBuffer = 0;
984     }
985 
986     if (m_feedbackBuffer)
987     {
988         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_feedbackBuffer);
989         m_feedbackBuffer = 0;
990     }
991 
992     if (m_feedbackID)
993     {
994         m_context.getRenderContext().getFunctions().deleteTransformFeedbacks(1, &m_feedbackID);
995         m_feedbackID = 0;
996     }
997 
998     if (m_feedbackProgram)
999     {
1000         delete m_feedbackProgram;
1001         m_feedbackProgram = DE_NULL;
1002     }
1003 
1004     if (m_nonFeedbackProgram)
1005     {
1006         delete m_nonFeedbackProgram;
1007         m_nonFeedbackProgram = DE_NULL;
1008     }
1009 }
1010 
iterate(void)1011 FeedbackPrimitiveTypeCase::IterateResult FeedbackPrimitiveTypeCase::iterate(void)
1012 {
1013     tcu::Surface feedbackResult(RENDER_SIZE, RENDER_SIZE);
1014     tcu::Surface nonFeedbackResult(RENDER_SIZE, RENDER_SIZE);
1015 
1016     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1017 
1018     // render with and without XFB
1019     renderWithFeedback(feedbackResult);
1020     renderWithoutFeedback(nonFeedbackResult);
1021 
1022     // compare
1023     {
1024         bool imageOk;
1025 
1026         m_testCtx.getLog() << tcu::TestLog::Message
1027                            << "Comparing the image rendered with no transform feedback against the image rendered with "
1028                               "enabled transform feedback."
1029                            << tcu::TestLog::EndMessage;
1030 
1031         if (m_context.getRenderTarget().getNumSamples() > 1)
1032             imageOk =
1033                 tcu::fuzzyCompare(m_testCtx.getLog(), "ImageCompare", "Image comparison", feedbackResult.getAccess(),
1034                                   nonFeedbackResult.getAccess(), 0.03f, tcu::COMPARE_LOG_RESULT);
1035         else
1036             imageOk = tcu::intThresholdPositionDeviationCompare(
1037                 m_testCtx.getLog(), "ImageCompare", "Image comparison", feedbackResult.getAccess(),
1038                 nonFeedbackResult.getAccess(), tcu::UVec4(8, 8, 8, 255), //!< threshold
1039                 tcu::IVec3(1, 1, 0),                                     //!< 3x3 search kernel
1040                 true, //!< fragments may end up over the viewport, just ignore them
1041                 tcu::COMPARE_LOG_RESULT);
1042 
1043         if (!imageOk)
1044             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
1045     }
1046 
1047     return STOP;
1048 }
1049 
renderWithFeedback(tcu::Surface & dst)1050 void FeedbackPrimitiveTypeCase::renderWithFeedback(tcu::Surface &dst)
1051 {
1052     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1053     const glu::VertexArray vao(m_context.getRenderContext());
1054     const glu::Query primitivesGeneratedQuery(m_context.getRenderContext());
1055     const int posLocation                   = gl.getAttribLocation(m_feedbackProgram->getProgram(), "a_position");
1056     const glw::GLenum feedbackPrimitiveMode = getOutputPrimitiveGLType();
1057 
1058     if (posLocation == -1)
1059         throw tcu::TestError("a_position was -1");
1060 
1061     m_testCtx.getLog() << tcu::TestLog::Message << "Rendering with transform feedback" << tcu::TestLog::EndMessage;
1062 
1063     gl.viewport(0, 0, dst.getWidth(), dst.getHeight());
1064     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1065     gl.clear(GL_COLOR_BUFFER_BIT);
1066     GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
1067 
1068     gl.bindVertexArray(*vao);
1069     gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
1070     gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
1071     gl.enableVertexAttribArray(posLocation);
1072     GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
1073 
1074     gl.useProgram(m_feedbackProgram->getProgram());
1075     GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
1076 
1077     gl.patchParameteri(GL_PATCH_VERTICES, 4);
1078     GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
1079 
1080     gl.beginQuery(GL_PRIMITIVES_GENERATED, *primitivesGeneratedQuery);
1081     GLU_EXPECT_NO_ERROR(gl.getError(), "begin GL_PRIMITIVES_GENERATED query");
1082 
1083     m_testCtx.getLog() << tcu::TestLog::Message << "Begin transform feedback with mode "
1084                        << glu::getPrimitiveTypeStr(feedbackPrimitiveMode) << tcu::TestLog::EndMessage;
1085 
1086     gl.beginTransformFeedback(feedbackPrimitiveMode);
1087     GLU_EXPECT_NO_ERROR(gl.getError(), "begin xfb");
1088 
1089     m_testCtx.getLog() << tcu::TestLog::Message << "Calling drawArrays with mode GL_PATCHES"
1090                        << tcu::TestLog::EndMessage;
1091 
1092     gl.drawArrays(GL_PATCHES, 0, 4);
1093     GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
1094 
1095     gl.endTransformFeedback();
1096     GLU_EXPECT_NO_ERROR(gl.getError(), "end xfb");
1097 
1098     gl.endQuery(GL_PRIMITIVES_GENERATED);
1099     GLU_EXPECT_NO_ERROR(gl.getError(), "end GL_PRIMITIVES_GENERATED query");
1100 
1101     glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
1102     GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels");
1103 
1104     // verify GL_PRIMITIVES_GENERATED
1105     {
1106         glw::GLuint primitivesGeneratedResult = 0;
1107         gl.getQueryObjectuiv(*primitivesGeneratedQuery, GL_QUERY_RESULT, &primitivesGeneratedResult);
1108         GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_PRIMITIVES_GENERATED value");
1109 
1110         m_testCtx.getLog() << tcu::TestLog::Message << "Verifying GL_PRIMITIVES_GENERATED, expecting "
1111                            << getNumGeneratedPrimitives() << tcu::TestLog::EndMessage;
1112 
1113         if ((int)primitivesGeneratedResult != getNumGeneratedPrimitives())
1114         {
1115             m_testCtx.getLog() << tcu::TestLog::Message << "Error, GL_PRIMITIVES_GENERATED was "
1116                                << primitivesGeneratedResult << tcu::TestLog::EndMessage;
1117             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected GL_PRIMITIVES_GENERATED");
1118         }
1119         else
1120             m_testCtx.getLog() << tcu::TestLog::Message << "GL_PRIMITIVES_GENERATED valid." << tcu::TestLog::EndMessage;
1121     }
1122 
1123     // feedback
1124     {
1125         std::vector<tcu::Vec4> feedbackResults(getNumGeneratedElementsPerPrimitive() * getNumGeneratedPrimitives());
1126         const void *mappedPtr =
1127             gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0,
1128                               (glw::GLsizeiptr)(feedbackResults.size() * sizeof(tcu::Vec4)), GL_MAP_READ_BIT);
1129         glw::GLboolean unmapResult;
1130 
1131         GLU_EXPECT_NO_ERROR(gl.getError(), "mapBufferRange");
1132 
1133         m_testCtx.getLog() << tcu::TestLog::Message << "Reading transform feedback buffer." << tcu::TestLog::EndMessage;
1134         if (!mappedPtr)
1135             throw tcu::TestError("mapBufferRange returned null");
1136 
1137         deMemcpy(feedbackResults[0].getPtr(), mappedPtr, (int)(feedbackResults.size() * sizeof(tcu::Vec4)));
1138 
1139         unmapResult = gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
1140         GLU_EXPECT_NO_ERROR(gl.getError(), "unmapBuffer");
1141 
1142         if (unmapResult != GL_TRUE)
1143             throw tcu::TestError("unmapBuffer failed, did not return true");
1144 
1145         // verify transform results
1146         verifyFeedbackResults(feedbackResults);
1147 
1148         // verify feedback results are consistent with rendered image
1149         verifyRenderedImage(dst, feedbackResults);
1150     }
1151 }
1152 
renderWithoutFeedback(tcu::Surface & dst)1153 void FeedbackPrimitiveTypeCase::renderWithoutFeedback(tcu::Surface &dst)
1154 {
1155     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1156     const glu::VertexArray vao(m_context.getRenderContext());
1157     const int posLocation = gl.getAttribLocation(m_nonFeedbackProgram->getProgram(), "a_position");
1158 
1159     if (posLocation == -1)
1160         throw tcu::TestError("a_position was -1");
1161 
1162     m_testCtx.getLog() << tcu::TestLog::Message << "Rendering without transform feedback" << tcu::TestLog::EndMessage;
1163 
1164     gl.viewport(0, 0, dst.getWidth(), dst.getHeight());
1165     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1166     gl.clear(GL_COLOR_BUFFER_BIT);
1167     GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
1168 
1169     gl.bindVertexArray(*vao);
1170     gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer);
1171     gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
1172     gl.enableVertexAttribArray(posLocation);
1173     GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs");
1174 
1175     gl.useProgram(m_nonFeedbackProgram->getProgram());
1176     GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
1177 
1178     gl.patchParameteri(GL_PATCH_VERTICES, 4);
1179     GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
1180 
1181     m_testCtx.getLog() << tcu::TestLog::Message << "Calling drawArrays with mode GL_PATCHES"
1182                        << tcu::TestLog::EndMessage;
1183 
1184     gl.drawArrays(GL_PATCHES, 0, 4);
1185     GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
1186 
1187     glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
1188     GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels");
1189 }
1190 
verifyFeedbackResults(const std::vector<tcu::Vec4> & feedbackResult)1191 void FeedbackPrimitiveTypeCase::verifyFeedbackResults(const std::vector<tcu::Vec4> &feedbackResult)
1192 {
1193     const int geometryAmplification = getGeometryAmplification();
1194     const int elementsPerPrimitive  = getNumGeneratedElementsPerPrimitive();
1195     const int errorFloodThreshold   = 8;
1196     int readNdx                     = 0;
1197     int numErrors                   = 0;
1198 
1199     m_testCtx.getLog() << tcu::TestLog::Message << "Verifying feedback results." << tcu::TestLog::EndMessage;
1200 
1201     for (int tessellatedPrimitiveNdx = 0; tessellatedPrimitiveNdx < getNumTessellatedPrimitives();
1202          ++tessellatedPrimitiveNdx)
1203     {
1204         const tcu::Vec4 primitiveVertex = feedbackResult[readNdx];
1205 
1206         // check the generated vertices are in the proper range (range: -0.4 <-> 0.4)
1207         {
1208             const float equalThreshold = 1.0e-6f;
1209             const bool centroidOk =
1210                 (primitiveVertex.x() >= -0.4f - equalThreshold) && (primitiveVertex.x() <= 0.4f + equalThreshold) &&
1211                 (primitiveVertex.y() >= -0.4f - equalThreshold) && (primitiveVertex.y() <= 0.4f + equalThreshold) &&
1212                 (de::abs(primitiveVertex.z()) < equalThreshold) &&
1213                 (de::abs(primitiveVertex.w() - 1.0f) < equalThreshold);
1214 
1215             if (!centroidOk && numErrors++ < errorFloodThreshold)
1216             {
1217                 m_testCtx.getLog() << tcu::TestLog::Message << "Element at index " << (readNdx)
1218                                    << " (tessellation invocation " << tessellatedPrimitiveNdx << ")\n"
1219                                    << "\texpected vertex in range: ( [-0.4, 0.4], [-0.4, 0.4], 0.0, 1.0 )\n"
1220                                    << "\tgot: " << primitiveVertex << tcu::TestLog::EndMessage;
1221 
1222                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid feedback output");
1223 
1224                 ++readNdx;
1225                 continue;
1226             }
1227         }
1228 
1229         // check all other primitives generated from this tessellated primitive have the same feedback value
1230         for (int generatedPrimitiveNdx = 0; generatedPrimitiveNdx < geometryAmplification; ++generatedPrimitiveNdx)
1231             for (int primitiveVertexNdx = 0; primitiveVertexNdx < elementsPerPrimitive; ++primitiveVertexNdx)
1232             {
1233                 const tcu::Vec4 generatedElementVertex = feedbackResult[readNdx];
1234                 const tcu::Vec4 equalThreshold(1.0e-6f);
1235 
1236                 if (tcu::boolAny(tcu::greaterThan(tcu::abs(primitiveVertex - generatedElementVertex), equalThreshold)))
1237                 {
1238                     if (numErrors++ < errorFloodThreshold)
1239                     {
1240                         m_testCtx.getLog()
1241                             << tcu::TestLog::Message << "Element at index " << (readNdx) << " (tessellation invocation "
1242                             << tessellatedPrimitiveNdx << ", geometry primitive " << generatedPrimitiveNdx
1243                             << ", emitted vertex " << primitiveVertexNdx << "):\n"
1244                             << "\tfeedback result was not contant over whole primitive.\n"
1245                             << "\tfirst emitted value: " << primitiveVertex << "\n"
1246                             << "\tcurrent emitted value:" << generatedElementVertex << "\n"
1247                             << tcu::TestLog::EndMessage;
1248                     }
1249 
1250                     m_testCtx.setTestResult(QP_TEST_RESULT_FAIL,
1251                                             "got multiple different feedback values for a single primitive");
1252                 }
1253 
1254                 readNdx++;
1255             }
1256     }
1257 
1258     if (numErrors > errorFloodThreshold)
1259         m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (numErrors - errorFloodThreshold) << " error(s)."
1260                            << tcu::TestLog::EndMessage;
1261 }
1262 
feedbackResultCompare(const tcu::Vec4 & a,const tcu::Vec4 & b)1263 static bool feedbackResultCompare(const tcu::Vec4 &a, const tcu::Vec4 &b)
1264 {
1265     if (a.x() < b.x())
1266         return true;
1267     if (a.x() > b.x())
1268         return false;
1269 
1270     return a.y() < b.y();
1271 }
1272 
verifyRenderedImage(const tcu::Surface & image,const std::vector<tcu::Vec4> & tfVertices)1273 void FeedbackPrimitiveTypeCase::verifyRenderedImage(const tcu::Surface &image, const std::vector<tcu::Vec4> &tfVertices)
1274 {
1275     std::vector<tcu::Vec4> vertices;
1276 
1277     m_testCtx.getLog() << tcu::TestLog::Message << "Comparing result image against feedback results."
1278                        << tcu::TestLog::EndMessage;
1279 
1280     // Check only unique vertices
1281     std::unique_copy(tfVertices.begin(), tfVertices.end(), std::back_insert_iterator<std::vector<tcu::Vec4>>(vertices));
1282     std::sort(vertices.begin(), vertices.end(), feedbackResultCompare);
1283     vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end());
1284 
1285     // Verifying vertices recorded with feedback actually ended up on the result image
1286     for (int ndx = 0; ndx < (int)vertices.size(); ++ndx)
1287     {
1288         // Rasterization (of lines) may deviate by one pixel. In addition to that, allow minimal errors in rasterized position vs. feedback result.
1289         // This minimal error could result in a difference in rounding => allow one additional pixel in deviation
1290 
1291         const int rasterDeviation = 2;
1292         const tcu::IVec2 rasterPos((int)deFloatRound((vertices[ndx].x() * 0.5f + 0.5f) * (float)image.getWidth()),
1293                                    (int)deFloatRound((vertices[ndx].y() * 0.5f + 0.5f) * (float)image.getHeight()));
1294 
1295         // Find produced rasterization results
1296         bool found = false;
1297 
1298         for (int dy = -rasterDeviation; dy <= rasterDeviation && !found; ++dy)
1299             for (int dx = -rasterDeviation; dx <= rasterDeviation && !found; ++dx)
1300             {
1301                 // Raster result could end up outside the viewport
1302                 if (rasterPos.x() + dx < 0 || rasterPos.x() + dx >= image.getWidth() || rasterPos.y() + dy < 0 ||
1303                     rasterPos.y() + dy >= image.getHeight())
1304                     found = true;
1305                 else
1306                 {
1307                     const tcu::RGBA result = image.getPixel(rasterPos.x() + dx, rasterPos.y() + dy);
1308 
1309                     if (!isBlack(result))
1310                         found = true;
1311                 }
1312             }
1313 
1314         if (!found)
1315         {
1316             m_testCtx.getLog() << tcu::TestLog::Message << "Vertex " << vertices[ndx] << "\n"
1317                                << "\tCould not find rasterization output for vertex.\n"
1318                                << "\tExpected non-black pixels near " << rasterPos << tcu::TestLog::EndMessage;
1319 
1320             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid result image");
1321         }
1322     }
1323 }
1324 
genTransformFeedback(void)1325 void FeedbackPrimitiveTypeCase::genTransformFeedback(void)
1326 {
1327     const glw::Functions &gl       = m_context.getRenderContext().getFunctions();
1328     const int elementsPerPrimitive = getNumGeneratedElementsPerPrimitive();
1329     const int feedbackPrimitives   = getNumGeneratedPrimitives();
1330     const int feedbackElements     = elementsPerPrimitive * feedbackPrimitives;
1331     const std::vector<tcu::Vec4> initialBuffer(feedbackElements, tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f));
1332 
1333     gl.genTransformFeedbacks(1, &m_feedbackID);
1334     gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_feedbackID);
1335     GLU_EXPECT_NO_ERROR(gl.getError(), "gen transform feedback");
1336 
1337     gl.genBuffers(1, &m_feedbackBuffer);
1338     gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_feedbackBuffer);
1339     gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(tcu::Vec4) * initialBuffer.size(), initialBuffer[0].getPtr(),
1340                   GL_STATIC_COPY);
1341     GLU_EXPECT_NO_ERROR(gl.getError(), "gen feedback buffer");
1342 
1343     gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_feedbackBuffer);
1344     GLU_EXPECT_NO_ERROR(gl.getError(), "bind feedback buffer");
1345 }
1346 
getTriangleNumOutputPrimitives(int tessellationLevel)1347 static int getTriangleNumOutputPrimitives(int tessellationLevel)
1348 {
1349     if (tessellationLevel == 1)
1350         return 1;
1351     else if (tessellationLevel == 2)
1352         return 6;
1353     else
1354         return 3 * (2 + 2 * (tessellationLevel - 2)) + getTriangleNumOutputPrimitives(tessellationLevel - 2);
1355 }
1356 
getTriangleNumOutputPrimitivesPoints(int tessellationLevel)1357 static int getTriangleNumOutputPrimitivesPoints(int tessellationLevel)
1358 {
1359     if (tessellationLevel == 0)
1360         return 1;
1361     else if (tessellationLevel == 1)
1362         return 3;
1363     else
1364         return 3 + 3 * (tessellationLevel - 1) + getTriangleNumOutputPrimitivesPoints(tessellationLevel - 2);
1365 }
1366 
getNumGeneratedElementsPerPrimitive(void) const1367 int FeedbackPrimitiveTypeCase::getNumGeneratedElementsPerPrimitive(void) const
1368 {
1369     if (m_geometryOutputType == GEOMETRY_OUTPUT_TRIANGLES)
1370         return 3;
1371     else if (m_geometryOutputType == GEOMETRY_OUTPUT_LINES)
1372         return 2;
1373     else if (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS)
1374         return 1;
1375     else
1376     {
1377         DE_ASSERT(false);
1378         return -1;
1379     }
1380 }
1381 
getNumGeneratedPrimitives(void) const1382 int FeedbackPrimitiveTypeCase::getNumGeneratedPrimitives(void) const
1383 {
1384     return getNumTessellatedPrimitives() * getGeometryAmplification();
1385 }
1386 
getNumTessellatedPrimitives(void) const1387 int FeedbackPrimitiveTypeCase::getNumTessellatedPrimitives(void) const
1388 {
1389     const int tessellationLevel = 3;
1390 
1391     if (m_tessellationPointMode == TESSELLATION_POINTMODE_OFF)
1392     {
1393         if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
1394             return getTriangleNumOutputPrimitives(tessellationLevel);
1395         else if (m_tessellationOutput == TESSELLATION_OUT_QUADS)
1396             return tessellationLevel * tessellationLevel * 2; // tessellated as triangles
1397         else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
1398             return tessellationLevel * tessellationLevel;
1399     }
1400     else if (m_tessellationPointMode == TESSELLATION_POINTMODE_ON)
1401     {
1402         if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
1403             return getTriangleNumOutputPrimitivesPoints(tessellationLevel);
1404         else if (m_tessellationOutput == TESSELLATION_OUT_QUADS)
1405             return (tessellationLevel + 1) * (tessellationLevel + 1);
1406         else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
1407             return tessellationLevel * (tessellationLevel + 1);
1408     }
1409 
1410     DE_ASSERT(false);
1411     return -1;
1412 }
1413 
getGeometryAmplification(void) const1414 int FeedbackPrimitiveTypeCase::getGeometryAmplification(void) const
1415 {
1416     const int outputAmplification = (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? (2) : (1);
1417     const int numInputVertices    = (m_tessellationPointMode)                           ? (1) :
1418                                     (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? (2) :
1419                                                                                           (3);
1420 
1421     return outputAmplification * numInputVertices;
1422 }
1423 
getOutputPrimitiveGLType(void) const1424 glw::GLenum FeedbackPrimitiveTypeCase::getOutputPrimitiveGLType(void) const
1425 {
1426     if (m_geometryOutputType == GEOMETRY_OUTPUT_TRIANGLES)
1427         return GL_TRIANGLES;
1428     else if (m_geometryOutputType == GEOMETRY_OUTPUT_LINES)
1429         return GL_LINES;
1430     else if (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS)
1431         return GL_POINTS;
1432     else
1433     {
1434         DE_ASSERT(false);
1435         return -1;
1436     }
1437 }
1438 
getVertexSource(void) const1439 std::string FeedbackPrimitiveTypeCase::getVertexSource(void) const
1440 {
1441     return specializeShader(s_positionVertexShader, m_context.getRenderContext().getType());
1442 }
1443 
getFragmentSource(void) const1444 std::string FeedbackPrimitiveTypeCase::getFragmentSource(void) const
1445 {
1446     return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
1447 }
1448 
getTessellationControlSource(void) const1449 std::string FeedbackPrimitiveTypeCase::getTessellationControlSource(void) const
1450 {
1451     std::ostringstream buf;
1452 
1453     buf << "${VERSION_DECL}\n"
1454            "${EXTENSION_TESSELATION_SHADER}"
1455            "layout(vertices = 9) out;\n"
1456            "\n"
1457            "uniform highp float u_innerTessellationLevel;\n"
1458            "uniform highp float u_outerTessellationLevel;\n"
1459            "\n"
1460            "void main (void)\n"
1461            "{\n"
1462            "    if (gl_PatchVerticesIn != 4)\n"
1463            "        return;\n"
1464            "\n"
1465            "    // Convert input 2x2 grid to 3x3 grid\n"
1466            "    float xweight = float(gl_InvocationID % 3) / 2.0f;\n"
1467            "    float yweight = float(gl_InvocationID / 3) / 2.0f;\n"
1468            "\n"
1469            "    vec4 y0 = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, yweight);\n"
1470            "    vec4 y1 = mix(gl_in[2].gl_Position, gl_in[3].gl_Position, yweight);\n"
1471            "\n"
1472            "    gl_out[gl_InvocationID].gl_Position = mix(y0, y1, xweight);\n"
1473            "\n";
1474 
1475     if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
1476         buf << "    gl_TessLevelOuter[0] = 3.0;\n"
1477                "    gl_TessLevelOuter[1] = 3.0;\n"
1478                "    gl_TessLevelOuter[2] = 3.0;\n"
1479                "    gl_TessLevelInner[0] = 3.0;\n";
1480     else if (m_tessellationOutput == TESSELLATION_OUT_QUADS)
1481         buf << "    gl_TessLevelOuter[0] = 3.0;\n"
1482                "    gl_TessLevelOuter[1] = 3.0;\n"
1483                "    gl_TessLevelOuter[2] = 3.0;\n"
1484                "    gl_TessLevelOuter[3] = 3.0;\n"
1485                "    gl_TessLevelInner[0] = 3.0;\n"
1486                "    gl_TessLevelInner[1] = 3.0;\n";
1487     else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
1488         buf << "    gl_TessLevelOuter[0] = 3.0;\n"
1489                "    gl_TessLevelOuter[1] = 3.0;\n";
1490     else
1491         DE_ASSERT(false);
1492 
1493     buf << "}\n";
1494 
1495     return specializeShader(buf.str(), m_context.getRenderContext().getType());
1496 }
1497 
getTessellationEvaluationSource(void) const1498 std::string FeedbackPrimitiveTypeCase::getTessellationEvaluationSource(void) const
1499 {
1500     std::ostringstream buf;
1501 
1502     buf << "${VERSION_DECL}\n"
1503            "${EXTENSION_TESSELATION_SHADER}"
1504            "layout("
1505         << ((m_tessellationOutput == TESSELLATION_OUT_TRIANGLES) ? ("triangles") :
1506             (m_tessellationOutput == TESSELLATION_OUT_QUADS)     ? ("quads") :
1507                                                                    ("isolines"))
1508         << ((m_tessellationPointMode) ? (", point_mode") : (""))
1509         << ") in;\n"
1510            "\n"
1511            "out highp vec4 v_tessellationCoords;\n"
1512            "\n"
1513            "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
1514            "void main (void)\n"
1515            "{\n"
1516            "    if (gl_PatchVerticesIn != 9)\n"
1517            "        return;\n"
1518            "\n"
1519            "    vec4 patchCentroid = vec4(0.0);\n"
1520            "    for (int ndx = 0; ndx < gl_PatchVerticesIn; ++ndx)\n"
1521            "        patchCentroid += gl_in[ndx].gl_Position;\n"
1522            "    patchCentroid /= patchCentroid.w;\n"
1523            "\n";
1524 
1525     if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES)
1526         buf << "    // map barycentric coords to 2d coords\n"
1527                "    const vec3 tessDirX = vec3( 0.4,  0.4, 0.0);\n"
1528                "    const vec3 tessDirY = vec3( 0.0, -0.4, 0.0);\n"
1529                "    const vec3 tessDirZ = vec3(-0.4,  0.4, 0.0);\n"
1530                "    gl_Position = patchCentroid + vec4(gl_TessCoord.x * tessDirX + gl_TessCoord.y * tessDirY + "
1531                "gl_TessCoord.z * tessDirZ, 0.0);\n";
1532     else if (m_tessellationOutput == TESSELLATION_OUT_QUADS || m_tessellationOutput == TESSELLATION_OUT_ISOLINES)
1533         buf << "    gl_Position = patchCentroid + vec4(gl_TessCoord.x * 0.8 - 0.4, gl_TessCoord.y * 0.8 - 0.4, 0.0, "
1534                "0.0);\n";
1535     else
1536         DE_ASSERT(false);
1537 
1538     buf << "    v_tessellationCoords = vec4(gl_TessCoord, 0.0);\n"
1539            "}\n";
1540 
1541     return specializeShader(buf.str(), m_context.getRenderContext().getType());
1542 }
1543 
getGeometrySource(void) const1544 std::string FeedbackPrimitiveTypeCase::getGeometrySource(void) const
1545 {
1546     const char *const geometryInputPrimitive  = (m_tessellationPointMode)                           ? ("points") :
1547                                                 (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? ("lines") :
1548                                                                                                       ("triangles");
1549     const char *const geometryOutputPrimitive = (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) ? ("points") :
1550                                                 (m_geometryOutputType == GEOMETRY_OUTPUT_LINES)  ? ("line_strip") :
1551                                                                                                    ("triangle_strip");
1552     const int numInputVertices                = (m_tessellationPointMode)                           ? (1) :
1553                                                 (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? (2) :
1554                                                                                                       (3);
1555     const int numSingleVertexOutputVertices   = (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) ? (1) :
1556                                                 (m_geometryOutputType == GEOMETRY_OUTPUT_LINES)  ? (4) :
1557                                                                                                    (3);
1558     const int numEmitVertices                 = numInputVertices * numSingleVertexOutputVertices;
1559     std::ostringstream buf;
1560 
1561     buf << "${VERSION_DECL}\n"
1562            "${EXTENSION_GEOMETRY_SHADER}"
1563            "layout("
1564         << geometryInputPrimitive
1565         << ") in;\n"
1566            "layout("
1567         << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices
1568         << ") out;\n"
1569            "\n"
1570            "in highp vec4 v_tessellationCoords[];\n"
1571            "out highp vec4 tf_someVertexPosition;\n"
1572            "\n"
1573            "void main (void)\n"
1574            "{\n"
1575            "    // Emit primitive\n"
1576            "    for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n"
1577            "    {\n";
1578 
1579     switch (m_geometryOutputType)
1580     {
1581     case GEOMETRY_OUTPUT_POINTS:
1582         buf << "        // Draw point on vertex\n"
1583                "        gl_Position = gl_in[ndx].gl_Position;\n"
1584                "        tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1585                "        EmitVertex();\n";
1586         break;
1587 
1588     case GEOMETRY_OUTPUT_LINES:
1589         buf << "        // Draw cross on vertex\n"
1590                "        gl_Position = gl_in[ndx].gl_Position + vec4(-0.02, -0.02, 0.0, 0.0);\n"
1591                "        tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1592                "        EmitVertex();\n"
1593                "        gl_Position = gl_in[ndx].gl_Position + vec4( 0.02,  0.02, 0.0, 0.0);\n"
1594                "        tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1595                "        EmitVertex();\n"
1596                "        EndPrimitive();\n"
1597                "        gl_Position = gl_in[ndx].gl_Position + vec4( 0.02, -0.02, 0.0, 0.0);\n"
1598                "        tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1599                "        EmitVertex();\n"
1600                "        gl_Position = gl_in[ndx].gl_Position + vec4(-0.02,  0.02, 0.0, 0.0);\n"
1601                "        tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1602                "        EmitVertex();\n"
1603                "        EndPrimitive();\n";
1604         break;
1605 
1606     case GEOMETRY_OUTPUT_TRIANGLES:
1607         buf << "        // Draw triangle on vertex\n"
1608                "        gl_Position = gl_in[ndx].gl_Position + vec4(  0.00, -0.02, 0.0, 0.0);\n"
1609                "        tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1610                "        EmitVertex();\n"
1611                "        gl_Position = gl_in[ndx].gl_Position + vec4(  0.02,  0.00, 0.0, 0.0);\n"
1612                "        tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1613                "        EmitVertex();\n"
1614                "        gl_Position = gl_in[ndx].gl_Position + vec4( -0.02,  0.00, 0.0, 0.0);\n"
1615                "        tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n"
1616                "        EmitVertex();\n"
1617                "        EndPrimitive();\n";
1618         break;
1619 
1620     default:
1621         DE_ASSERT(false);
1622         return "";
1623     }
1624 
1625     buf << "    }\n"
1626            "}\n";
1627 
1628     return specializeShader(buf.str(), m_context.getRenderContext().getType());
1629 }
1630 
getTessellationOutputDescription(TessellationOutputType tessellationOutput,TessellationPointMode pointMode)1631 const char *FeedbackPrimitiveTypeCase::getTessellationOutputDescription(TessellationOutputType tessellationOutput,
1632                                                                         TessellationPointMode pointMode)
1633 {
1634     switch (tessellationOutput)
1635     {
1636     case TESSELLATION_OUT_TRIANGLES:
1637         return (pointMode) ? ("points (triangles in point mode)") : ("triangles");
1638     case TESSELLATION_OUT_QUADS:
1639         return (pointMode) ? ("points (quads in point mode)") : ("quads");
1640     case TESSELLATION_OUT_ISOLINES:
1641         return (pointMode) ? ("points (isolines in point mode)") : ("isolines");
1642     default:
1643         DE_ASSERT(false);
1644         return DE_NULL;
1645     }
1646 }
1647 
getGeometryInputDescription(TessellationOutputType tessellationOutput,TessellationPointMode pointMode)1648 const char *FeedbackPrimitiveTypeCase::getGeometryInputDescription(TessellationOutputType tessellationOutput,
1649                                                                    TessellationPointMode pointMode)
1650 {
1651     switch (tessellationOutput)
1652     {
1653     case TESSELLATION_OUT_TRIANGLES:
1654         return (pointMode) ? ("points") : ("triangles");
1655     case TESSELLATION_OUT_QUADS:
1656         return (pointMode) ? ("points") : ("triangles");
1657     case TESSELLATION_OUT_ISOLINES:
1658         return (pointMode) ? ("points") : ("lines");
1659     default:
1660         DE_ASSERT(false);
1661         return DE_NULL;
1662     }
1663 }
1664 
getGeometryOutputDescription(GeometryOutputType geometryOutput)1665 const char *FeedbackPrimitiveTypeCase::getGeometryOutputDescription(GeometryOutputType geometryOutput)
1666 {
1667     switch (geometryOutput)
1668     {
1669     case GEOMETRY_OUTPUT_POINTS:
1670         return "points";
1671     case GEOMETRY_OUTPUT_LINES:
1672         return "lines";
1673     case GEOMETRY_OUTPUT_TRIANGLES:
1674         return "triangles";
1675     default:
1676         DE_ASSERT(false);
1677         return DE_NULL;
1678     }
1679 }
1680 
1681 class PointSizeCase : public TestCase
1682 {
1683 public:
1684     enum Flags
1685     {
1686         FLAG_VERTEX_SET                  = 0x01, // !< set gl_PointSize in vertex shader
1687         FLAG_TESSELLATION_CONTROL_SET    = 0x02, // !< set gl_PointSize in tessellation evaluation shader
1688         FLAG_TESSELLATION_EVALUATION_SET = 0x04, // !< set gl_PointSize in tessellation control shader
1689         FLAG_TESSELLATION_ADD            = 0x08, // !< read and add to gl_PointSize in tessellation shader pair
1690         FLAG_TESSELLATION_DONT_SET       = 0x10, // !< don't set gl_PointSize in tessellation shader
1691         FLAG_GEOMETRY_SET                = 0x20, // !< set gl_PointSize in geometry shader
1692         FLAG_GEOMETRY_ADD                = 0x40, // !< read and add to gl_PointSize in geometry shader
1693         FLAG_GEOMETRY_DONT_SET           = 0x80, // !< don't set gl_PointSize in geometry shader
1694     };
1695 
1696     PointSizeCase(Context &context, const char *name, const char *description, int flags);
1697     ~PointSizeCase(void);
1698 
1699     static std::string genTestCaseName(int flags);
1700     static std::string genTestCaseDescription(int flags);
1701 
1702 private:
1703     void init(void);
1704     void deinit(void);
1705     IterateResult iterate(void);
1706 
1707     void checkExtensions(void) const;
1708     void checkPointSizeRequirements(void) const;
1709 
1710     void renderTo(tcu::Surface &dst);
1711     bool verifyImage(const tcu::Surface &src);
1712     int getExpectedPointSize(void) const;
1713 
1714     std::string genVertexSource(void) const;
1715     std::string genFragmentSource(void) const;
1716     std::string genTessellationControlSource(void) const;
1717     std::string genTessellationEvaluationSource(void) const;
1718     std::string genGeometrySource(void) const;
1719 
1720     enum
1721     {
1722         RENDER_SIZE = 32,
1723     };
1724 
1725     const int m_flags;
1726     glu::ShaderProgram *m_program;
1727 };
1728 
PointSizeCase(Context & context,const char * name,const char * description,int flags)1729 PointSizeCase::PointSizeCase(Context &context, const char *name, const char *description, int flags)
1730     : TestCase(context, name, description)
1731     , m_flags(flags)
1732     , m_program(DE_NULL)
1733 {
1734 }
1735 
~PointSizeCase(void)1736 PointSizeCase::~PointSizeCase(void)
1737 {
1738     deinit();
1739 }
1740 
genTestCaseName(int flags)1741 std::string PointSizeCase::genTestCaseName(int flags)
1742 {
1743     std::ostringstream buf;
1744 
1745     // join per-bit descriptions into a single string with '_' separator
1746     if (flags & FLAG_VERTEX_SET)
1747         buf << "vertex_set";
1748     if (flags & FLAG_TESSELLATION_CONTROL_SET)
1749         buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET - 1)) ? ("_") : ("")) << "control_set";
1750     if (flags & FLAG_TESSELLATION_EVALUATION_SET)
1751         buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET - 1)) ? ("_") : ("")) << "evaluation_set";
1752     if (flags & FLAG_TESSELLATION_ADD)
1753         buf << ((flags & (FLAG_TESSELLATION_ADD - 1)) ? ("_") : ("")) << "control_pass_eval_add";
1754     if (flags & FLAG_TESSELLATION_DONT_SET)
1755         buf << ((flags & (FLAG_TESSELLATION_DONT_SET - 1)) ? ("_") : ("")) << "eval_default";
1756     if (flags & FLAG_GEOMETRY_SET)
1757         buf << ((flags & (FLAG_GEOMETRY_SET - 1)) ? ("_") : ("")) << "geometry_set";
1758     if (flags & FLAG_GEOMETRY_ADD)
1759         buf << ((flags & (FLAG_GEOMETRY_ADD - 1)) ? ("_") : ("")) << "geometry_add";
1760     if (flags & FLAG_GEOMETRY_DONT_SET)
1761         buf << ((flags & (FLAG_GEOMETRY_DONT_SET - 1)) ? ("_") : ("")) << "geometry_default";
1762 
1763     return buf.str();
1764 }
1765 
genTestCaseDescription(int flags)1766 std::string PointSizeCase::genTestCaseDescription(int flags)
1767 {
1768     std::ostringstream buf;
1769 
1770     // join per-bit descriptions into a single string with ", " separator
1771     if (flags & FLAG_VERTEX_SET)
1772         buf << "set point size in vertex shader";
1773     if (flags & FLAG_TESSELLATION_CONTROL_SET)
1774         buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET - 1)) ? (", ") : (""))
1775             << "set point size in tessellation control shader";
1776     if (flags & FLAG_TESSELLATION_EVALUATION_SET)
1777         buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET - 1)) ? (", ") : (""))
1778             << "set point size in tessellation evaluation shader";
1779     if (flags & FLAG_TESSELLATION_ADD)
1780         buf << ((flags & (FLAG_TESSELLATION_ADD - 1)) ? (", ") : ("")) << "add to point size in tessellation shader";
1781     if (flags & FLAG_TESSELLATION_DONT_SET)
1782         buf << ((flags & (FLAG_TESSELLATION_DONT_SET - 1)) ? (", ") : (""))
1783             << "don't set point size in tessellation evaluation shader";
1784     if (flags & FLAG_GEOMETRY_SET)
1785         buf << ((flags & (FLAG_GEOMETRY_SET - 1)) ? (", ") : ("")) << "set point size in geometry shader";
1786     if (flags & FLAG_GEOMETRY_ADD)
1787         buf << ((flags & (FLAG_GEOMETRY_ADD - 1)) ? (", ") : ("")) << "add to point size in geometry shader";
1788     if (flags & FLAG_GEOMETRY_DONT_SET)
1789         buf << ((flags & (FLAG_GEOMETRY_DONT_SET - 1)) ? (", ") : ("")) << "don't set point size in geometry shader";
1790 
1791     return buf.str();
1792 }
1793 
init(void)1794 void PointSizeCase::init(void)
1795 {
1796     checkExtensions();
1797     checkPointSizeRequirements();
1798 
1799     if (glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
1800     {
1801         m_context.getRenderContext().getFunctions().enable(GL_PROGRAM_POINT_SIZE);
1802     }
1803 
1804     // log
1805 
1806     if (m_flags & FLAG_VERTEX_SET)
1807         m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in vertex shader to 2.0."
1808                            << tcu::TestLog::EndMessage;
1809     if (m_flags & FLAG_TESSELLATION_CONTROL_SET)
1810         m_testCtx.getLog()
1811             << tcu::TestLog::Message
1812             << "Setting point size in tessellation control shader to 4.0. (And ignoring it in evaluation)."
1813             << tcu::TestLog::EndMessage;
1814     if (m_flags & FLAG_TESSELLATION_EVALUATION_SET)
1815         m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in tessellation evaluation shader to 4.0."
1816                            << tcu::TestLog::EndMessage;
1817     if (m_flags & FLAG_TESSELLATION_ADD)
1818         m_testCtx.getLog() << tcu::TestLog::Message
1819                            << "Reading point size in tessellation control shader and adding 2.0 to it in evaluation."
1820                            << tcu::TestLog::EndMessage;
1821     if (m_flags & FLAG_TESSELLATION_DONT_SET)
1822         m_testCtx.getLog()
1823             << tcu::TestLog::Message
1824             << "Not setting point size in tessellation evaluation shader (resulting in the default point size)."
1825             << tcu::TestLog::EndMessage;
1826     if (m_flags & FLAG_GEOMETRY_SET)
1827         m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in geometry shader to 6.0."
1828                            << tcu::TestLog::EndMessage;
1829     if (m_flags & FLAG_GEOMETRY_ADD)
1830         m_testCtx.getLog() << tcu::TestLog::Message << "Reading point size in geometry shader and adding 2.0."
1831                            << tcu::TestLog::EndMessage;
1832     if (m_flags & FLAG_GEOMETRY_DONT_SET)
1833         m_testCtx.getLog() << tcu::TestLog::Message
1834                            << "Not setting point size in geometry shader (resulting in the default point size)."
1835                            << tcu::TestLog::EndMessage;
1836 
1837     // program
1838 
1839     {
1840         glu::ProgramSources sources;
1841         sources << glu::VertexSource(genVertexSource()) << glu::FragmentSource(genFragmentSource());
1842 
1843         if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD |
1844                        FLAG_TESSELLATION_DONT_SET))
1845             sources << glu::TessellationControlSource(genTessellationControlSource())
1846                     << glu::TessellationEvaluationSource(genTessellationEvaluationSource());
1847 
1848         if (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD | FLAG_GEOMETRY_DONT_SET))
1849             sources << glu::GeometrySource(genGeometrySource());
1850 
1851         m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources);
1852 
1853         m_testCtx.getLog() << *m_program;
1854         if (!m_program->isOk())
1855             throw tcu::TestError("failed to build program");
1856     }
1857 }
1858 
deinit(void)1859 void PointSizeCase::deinit(void)
1860 {
1861     if (glu::isContextTypeGLCore(m_context.getRenderContext().getType()))
1862     {
1863         m_context.getRenderContext().getFunctions().disable(GL_PROGRAM_POINT_SIZE);
1864     }
1865 
1866     delete m_program;
1867     m_program = DE_NULL;
1868 }
1869 
iterate(void)1870 PointSizeCase::IterateResult PointSizeCase::iterate(void)
1871 {
1872     tcu::Surface resultImage(RENDER_SIZE, RENDER_SIZE);
1873 
1874     renderTo(resultImage);
1875 
1876     if (verifyImage(resultImage))
1877         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1878     else
1879         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
1880 
1881     return STOP;
1882 }
1883 
checkExtensions(void) const1884 void PointSizeCase::checkExtensions(void) const
1885 {
1886     glu::ContextType contextType = m_context.getRenderContext().getType();
1887     if (glu::contextSupports(contextType, glu::ApiType::core(4, 5)))
1888         return;
1889 
1890     std::vector<std::string> requiredExtensions;
1891     const bool supportsES32 = glu::contextSupports(contextType, glu::ApiType::es(3, 2));
1892     bool allOk              = true;
1893 
1894     if ((m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD |
1895                     FLAG_TESSELLATION_DONT_SET)) &&
1896         !supportsES32)
1897         requiredExtensions.push_back("GL_EXT_tessellation_shader");
1898 
1899     if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD))
1900         requiredExtensions.push_back("GL_EXT_tessellation_point_size");
1901 
1902     if ((m_flags & (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD | FLAG_GEOMETRY_DONT_SET))) && !supportsES32)
1903         requiredExtensions.push_back("GL_EXT_geometry_shader");
1904 
1905     if (m_flags & (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD)))
1906         requiredExtensions.push_back("GL_EXT_geometry_point_size");
1907 
1908     for (int ndx = 0; ndx < (int)requiredExtensions.size(); ++ndx)
1909         if (!m_context.getContextInfo().isExtensionSupported(requiredExtensions[ndx].c_str()))
1910             allOk = false;
1911 
1912     if (!allOk)
1913     {
1914         std::ostringstream extensionList;
1915 
1916         for (int ndx = 0; ndx < (int)requiredExtensions.size(); ++ndx)
1917         {
1918             if (ndx != 0)
1919                 extensionList << ", ";
1920             extensionList << requiredExtensions[ndx];
1921         }
1922 
1923         throw tcu::NotSupportedError("Test requires {" + extensionList.str() + "} extension(s)");
1924     }
1925 }
1926 
checkPointSizeRequirements(void) const1927 void PointSizeCase::checkPointSizeRequirements(void) const
1928 {
1929     const glw::Functions &gl  = m_context.getRenderContext().getFunctions();
1930     float aliasedSizeRange[2] = {0.0f, 0.0f};
1931     const int requiredSize    = getExpectedPointSize();
1932 
1933     gl.getFloatv(GL_ALIASED_POINT_SIZE_RANGE, aliasedSizeRange);
1934 
1935     if (float(requiredSize) > aliasedSizeRange[1])
1936         throw tcu::NotSupportedError("Test requires point size " + de::toString(requiredSize));
1937 }
1938 
renderTo(tcu::Surface & dst)1939 void PointSizeCase::renderTo(tcu::Surface &dst)
1940 {
1941     const glw::Functions &gl      = m_context.getRenderContext().getFunctions();
1942     const bool tessellationActive = (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET |
1943                                                 FLAG_TESSELLATION_ADD | FLAG_TESSELLATION_DONT_SET)) != 0;
1944     const int positionLocation    = gl.getAttribLocation(m_program->getProgram(), "a_position");
1945     const glu::VertexArray vao(m_context.getRenderContext());
1946 
1947     m_testCtx.getLog() << tcu::TestLog::Message << "Rendering single point." << tcu::TestLog::EndMessage;
1948 
1949     if (positionLocation == -1)
1950         throw tcu::TestError("Attribute a_position location was -1");
1951 
1952     gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE);
1953     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
1954     gl.clear(GL_COLOR_BUFFER_BIT);
1955     GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
1956 
1957     gl.bindVertexArray(*vao);
1958     GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao");
1959 
1960     gl.useProgram(m_program->getProgram());
1961     GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
1962 
1963     gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
1964 
1965     if (tessellationActive)
1966     {
1967         gl.patchParameteri(GL_PATCH_VERTICES, 1);
1968         GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
1969 
1970         gl.drawArrays(GL_PATCHES, 0, 1);
1971         GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches");
1972     }
1973     else
1974     {
1975         gl.drawArrays(GL_POINTS, 0, 1);
1976         GLU_EXPECT_NO_ERROR(gl.getError(), "draw points");
1977     }
1978 
1979     glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess());
1980 }
1981 
verifyImage(const tcu::Surface & src)1982 bool PointSizeCase::verifyImage(const tcu::Surface &src)
1983 {
1984     const bool MSAATarget  = (m_context.getRenderTarget().getNumSamples() > 1);
1985     const int expectedSize = getExpectedPointSize();
1986 
1987     m_testCtx.getLog() << tcu::TestLog::Message << "Verifying rendered point size. Expecting " << expectedSize
1988                        << " pixels." << tcu::TestLog::EndMessage;
1989     m_testCtx.getLog() << tcu::TestLog::Image("RenderImage", "Rendered image", src.getAccess());
1990 
1991     {
1992         bool resultAreaFound = false;
1993         tcu::IVec4 resultArea;
1994 
1995         // Find rasterization output area
1996 
1997         for (int y = 0; y < src.getHeight(); ++y)
1998             for (int x = 0; x < src.getWidth(); ++x)
1999             {
2000                 if (!isBlack(src.getPixel(x, y)))
2001                 {
2002                     if (!resultAreaFound)
2003                     {
2004                         // first fragment
2005                         resultArea      = tcu::IVec4(x, y, x + 1, y + 1);
2006                         resultAreaFound = true;
2007                     }
2008                     else
2009                     {
2010                         // union area
2011                         resultArea.x() = de::min(resultArea.x(), x);
2012                         resultArea.y() = de::min(resultArea.y(), y);
2013                         resultArea.z() = de::max(resultArea.z(), x + 1);
2014                         resultArea.w() = de::max(resultArea.w(), y + 1);
2015                     }
2016                 }
2017             }
2018 
2019         if (!resultAreaFound)
2020         {
2021             m_testCtx.getLog() << tcu::TestLog::Message << "Verification failed, could not find any point fragments."
2022                                << tcu::TestLog::EndMessage;
2023             return false;
2024         }
2025 
2026         // verify area size
2027         if (MSAATarget)
2028         {
2029             const tcu::IVec2 pointSize = resultArea.swizzle(2, 3) - resultArea.swizzle(0, 1);
2030 
2031             // MSAA: edges may be a little fuzzy
2032             if (de::abs(pointSize.x() - pointSize.y()) > 1)
2033             {
2034                 m_testCtx.getLog() << tcu::TestLog::Message
2035                                    << "ERROR! Rasterized point is not a square. Detected point size was " << pointSize
2036                                    << tcu::TestLog::EndMessage;
2037                 return false;
2038             }
2039 
2040             // MSAA may produce larger areas, allow one pixel larger
2041             if (expectedSize != de::max(pointSize.x(), pointSize.y()) &&
2042                 (expectedSize + 1) != de::max(pointSize.x(), pointSize.y()))
2043             {
2044                 m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize
2045                                    << ", got " << de::max(pointSize.x(), pointSize.y()) << tcu::TestLog::EndMessage;
2046                 return false;
2047             }
2048         }
2049         else
2050         {
2051             const tcu::IVec2 pointSize = resultArea.swizzle(2, 3) - resultArea.swizzle(0, 1);
2052 
2053             if (pointSize.x() != pointSize.y())
2054             {
2055                 m_testCtx.getLog() << tcu::TestLog::Message
2056                                    << "ERROR! Rasterized point is not a square. Point size was " << pointSize
2057                                    << tcu::TestLog::EndMessage;
2058                 return false;
2059             }
2060 
2061             if (pointSize.x() != expectedSize)
2062             {
2063                 m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize
2064                                    << ", got " << pointSize.x() << tcu::TestLog::EndMessage;
2065                 return false;
2066             }
2067         }
2068     }
2069 
2070     return true;
2071 }
2072 
getExpectedPointSize(void) const2073 int PointSizeCase::getExpectedPointSize(void) const
2074 {
2075     int addition = 0;
2076 
2077     // geometry
2078     if (m_flags & FLAG_GEOMETRY_DONT_SET)
2079         return 1;
2080     else if (m_flags & FLAG_GEOMETRY_SET)
2081         return 6;
2082     else if (m_flags & FLAG_GEOMETRY_ADD)
2083         addition += 2;
2084 
2085     // tessellation
2086     if (m_flags & FLAG_TESSELLATION_EVALUATION_SET)
2087         return 4 + addition;
2088     else if (m_flags & FLAG_TESSELLATION_ADD)
2089         addition += 2;
2090     else if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_DONT_SET))
2091     {
2092         DE_ASSERT((m_flags & FLAG_GEOMETRY_ADD) == 0); // reading pointSize undefined
2093         return 1;
2094     }
2095 
2096     // vertex
2097     if (m_flags & FLAG_VERTEX_SET)
2098         return 2 + addition;
2099 
2100     // undefined
2101     DE_ASSERT(false);
2102     return -1;
2103 }
2104 
genVertexSource(void) const2105 std::string PointSizeCase::genVertexSource(void) const
2106 {
2107     std::ostringstream buf;
2108 
2109     buf << "${VERSION_DECL}\n"
2110         << "in highp vec4 a_position;\n"
2111         << "void main ()\n"
2112         << "{\n"
2113         << "    gl_Position = a_position;\n";
2114 
2115     if (m_flags & FLAG_VERTEX_SET)
2116         buf << "    gl_PointSize = 2.0;\n";
2117 
2118     buf << "}\n";
2119 
2120     return specializeShader(buf.str(), m_context.getRenderContext().getType());
2121 }
2122 
genFragmentSource(void) const2123 std::string PointSizeCase::genFragmentSource(void) const
2124 {
2125     return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
2126 }
2127 
genTessellationControlSource(void) const2128 std::string PointSizeCase::genTessellationControlSource(void) const
2129 {
2130     std::ostringstream buf;
2131 
2132     buf << "${VERSION_DECL}\n"
2133         << "${EXTENSION_TESSELATION_SHADER}"
2134         << ((m_flags & FLAG_TESSELLATION_DONT_SET) ? ("") : ("${EXTENSION_TESSELATION_POINT_SIZE}"))
2135         << "layout(vertices = 1) out;\n"
2136         << "void main ()\n"
2137         << "{\n"
2138         << "    gl_TessLevelOuter[0] = 3.0;\n"
2139         << "    gl_TessLevelOuter[1] = 3.0;\n"
2140         << "    gl_TessLevelOuter[2] = 3.0;\n"
2141         << "    gl_TessLevelInner[0] = 3.0;\n"
2142         << "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n";
2143 
2144     if (m_flags & FLAG_TESSELLATION_ADD)
2145         buf << "    // pass as is to eval\n"
2146             << "    gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n";
2147     else if (m_flags & FLAG_TESSELLATION_CONTROL_SET)
2148         buf << "    // thrown away\n"
2149             << "    gl_out[gl_InvocationID].gl_PointSize = 4.0;\n";
2150 
2151     buf << "}\n";
2152 
2153     return specializeShader(buf.str(), m_context.getRenderContext().getType());
2154 }
2155 
genTessellationEvaluationSource(void) const2156 std::string PointSizeCase::genTessellationEvaluationSource(void) const
2157 {
2158     std::ostringstream buf;
2159 
2160     buf << "${VERSION_DECL}\n"
2161         << "${EXTENSION_TESSELATION_SHADER}"
2162         << ((m_flags & FLAG_TESSELLATION_DONT_SET) ? ("") : ("${EXTENSION_TESSELATION_POINT_SIZE}"))
2163         << "layout(triangles, point_mode) in;\n"
2164         << "void main ()\n"
2165         << "{\n"
2166         << "    // hide all but one vertex\n"
2167         << "    if (gl_TessCoord.x < 0.99)\n"
2168         << "        gl_Position = vec4(-2.0, 0.0, 0.0, 1.0);\n"
2169         << "    else\n"
2170         << "        gl_Position = gl_in[0].gl_Position;\n";
2171 
2172     if (m_flags & FLAG_TESSELLATION_ADD)
2173         buf << "\n"
2174             << "    // add to point size\n"
2175             << "    gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
2176     else if (m_flags & FLAG_TESSELLATION_EVALUATION_SET)
2177         buf << "\n"
2178             << "    // set point size\n"
2179             << "    gl_PointSize = 4.0;\n";
2180 
2181     buf << "}\n";
2182 
2183     return specializeShader(buf.str(), m_context.getRenderContext().getType());
2184 }
2185 
genGeometrySource(void) const2186 std::string PointSizeCase::genGeometrySource(void) const
2187 {
2188     std::ostringstream buf;
2189 
2190     buf << "${VERSION_DECL}\n"
2191         << "${EXTENSION_GEOMETRY_SHADER}"
2192         << ((m_flags & FLAG_GEOMETRY_DONT_SET) ? ("") : ("${EXTENSION_GEOMETRY_POINT_SIZE}")) << "layout (points) in;\n"
2193         << "layout (points, max_vertices=1) out;\n"
2194         << "\n"
2195         << "void main ()\n"
2196         << "{\n";
2197 
2198     if (m_flags & FLAG_GEOMETRY_SET)
2199         buf << "    gl_Position = gl_in[0].gl_Position;\n"
2200             << "    gl_PointSize = 6.0;\n";
2201     else if (m_flags & FLAG_GEOMETRY_ADD)
2202         buf << "    gl_Position = gl_in[0].gl_Position;\n"
2203             << "    gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
2204     else if (m_flags & FLAG_GEOMETRY_DONT_SET)
2205         buf << "    gl_Position = gl_in[0].gl_Position;\n";
2206 
2207     buf << "    EmitVertex();\n"
2208         << "}\n";
2209 
2210     return specializeShader(buf.str(), m_context.getRenderContext().getType());
2211 }
2212 
2213 class AllowedRenderFailureException : public std::runtime_error
2214 {
2215 public:
AllowedRenderFailureException(const char * message)2216     AllowedRenderFailureException(const char *message) : std::runtime_error(message)
2217     {
2218     }
2219 };
2220 
2221 class GridRenderCase : public TestCase
2222 {
2223 public:
2224     enum Flags
2225     {
2226         FLAG_TESSELLATION_MAX_SPEC                   = 0x0001,
2227         FLAG_TESSELLATION_MAX_IMPLEMENTATION         = 0x0002,
2228         FLAG_GEOMETRY_MAX_SPEC                       = 0x0004,
2229         FLAG_GEOMETRY_MAX_IMPLEMENTATION             = 0x0008,
2230         FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC           = 0x0010,
2231         FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION = 0x0020,
2232 
2233         FLAG_GEOMETRY_SCATTER_INSTANCES  = 0x0040,
2234         FLAG_GEOMETRY_SCATTER_PRIMITIVES = 0x0080,
2235         FLAG_GEOMETRY_SEPARATE_PRIMITIVES =
2236             0x0100, //!< if set, geometry shader outputs separate grid cells and not continuous slices
2237         FLAG_GEOMETRY_SCATTER_LAYERS = 0x0200,
2238 
2239         FLAG_ALLOW_OUT_OF_MEMORY = 0x0400, //!< allow draw command to set GL_OUT_OF_MEMORY
2240     };
2241 
2242     GridRenderCase(Context &context, const char *name, const char *description, int flags);
2243     ~GridRenderCase(void);
2244 
2245 private:
2246     void init(void);
2247     void deinit(void);
2248     IterateResult iterate(void);
2249 
2250     void renderTo(std::vector<tcu::Surface> &dst);
2251     bool verifyResultLayer(int layerNdx, const tcu::Surface &dst);
2252 
2253     std::string getVertexSource(void);
2254     std::string getFragmentSource(void);
2255     std::string getTessellationControlSource(int tessLevel);
2256     std::string getTessellationEvaluationSource(int tessLevel);
2257     std::string getGeometryShaderSource(int numPrimitives, int numInstances, int tessLevel);
2258 
2259     enum
2260     {
2261         RENDER_SIZE = 256
2262     };
2263 
2264     std::string m_description;
2265 
2266     const int m_flags;
2267 
2268     glu::ShaderProgram *m_program;
2269     uint32_t m_texture;
2270     int m_numLayers;
2271 };
2272 
GridRenderCase(Context & context,const char * name,const char * description,int flags)2273 GridRenderCase::GridRenderCase(Context &context, const char *name, const char *description, int flags)
2274     : TestCase(context, name, description)
2275     , m_description(description)
2276     , m_flags(flags)
2277     , m_program(DE_NULL)
2278     , m_texture(0)
2279     , m_numLayers(1)
2280 {
2281     DE_ASSERT(((m_flags & FLAG_TESSELLATION_MAX_SPEC) == 0) || ((m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION) == 0));
2282     DE_ASSERT(((m_flags & FLAG_GEOMETRY_MAX_SPEC) == 0) || ((m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION) == 0));
2283     DE_ASSERT(((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) == 0) ||
2284               ((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION) == 0));
2285     DE_ASSERT(((m_flags & (FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) != 0) ==
2286               ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0));
2287 }
2288 
~GridRenderCase(void)2289 GridRenderCase::~GridRenderCase(void)
2290 {
2291     deinit();
2292 }
2293 
init(void)2294 void GridRenderCase::init(void)
2295 {
2296     const glw::Functions &gl      = m_context.getRenderContext().getFunctions();
2297     glu::ContextType contextType  = m_context.getRenderContext().getType();
2298     const bool supportsES32orGL45 = glu::contextSupports(contextType, glu::ApiType::es(3, 2)) ||
2299                                     glu::contextSupports(contextType, glu::ApiType::core(4, 5));
2300 
2301     // Requirements
2302 
2303     if (!supportsES32orGL45 && (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
2304                                 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
2305         throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
2306 
2307     if ((m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) == 0)
2308     {
2309         if (m_context.getRenderTarget().getWidth() < RENDER_SIZE ||
2310             m_context.getRenderTarget().getHeight() < RENDER_SIZE)
2311             throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" +
2312                                          de::toString<int>(RENDER_SIZE) + " or larger render target.");
2313     }
2314 
2315     // Log
2316 
2317     m_testCtx.getLog() << tcu::TestLog::Message
2318                        << "Testing tessellation and geometry shaders that output a large number of primitives.\n"
2319                        << m_description << tcu::TestLog::EndMessage;
2320 
2321     // Render target
2322     if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2323     {
2324         // set limits
2325         m_numLayers = 8;
2326 
2327         m_testCtx.getLog() << tcu::TestLog::Message << "Rendering to 2d texture array, numLayers = " << m_numLayers
2328                            << tcu::TestLog::EndMessage;
2329 
2330         gl.genTextures(1, &m_texture);
2331         gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_texture);
2332         gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, RENDER_SIZE, RENDER_SIZE, m_numLayers);
2333 
2334         gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2335         gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2336         gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2337         gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2338 
2339         GLU_EXPECT_NO_ERROR(gl.getError(), "gen texture");
2340     }
2341 
2342     // Gen program
2343     {
2344         glu::ProgramSources sources;
2345         int tessGenLevel = -1;
2346 
2347         sources << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource());
2348 
2349         // Tessellation limits
2350         {
2351             if (m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION)
2352             {
2353                 gl.getIntegerv(GL_MAX_TESS_GEN_LEVEL, &tessGenLevel);
2354                 GLU_EXPECT_NO_ERROR(gl.getError(), "query tessellation limits");
2355             }
2356             else if (m_flags & FLAG_TESSELLATION_MAX_SPEC)
2357             {
2358                 tessGenLevel = 64;
2359             }
2360             else
2361             {
2362                 tessGenLevel = 5;
2363             }
2364 
2365             m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation level: " << tessGenLevel << ", mode = quad.\n"
2366                                << "\tEach input patch produces " << (tessGenLevel * tessGenLevel) << " ("
2367                                << (tessGenLevel * tessGenLevel * 2) << " triangles)\n"
2368                                << tcu::TestLog::EndMessage;
2369 
2370             sources << glu::TessellationControlSource(getTessellationControlSource(tessGenLevel))
2371                     << glu::TessellationEvaluationSource(getTessellationEvaluationSource(tessGenLevel));
2372         }
2373 
2374         // Geometry limits
2375         {
2376             int geometryOutputComponents      = -1;
2377             int geometryOutputVertices        = -1;
2378             int geometryTotalOutputComponents = -1;
2379             int geometryShaderInvocations     = -1;
2380             bool logGeometryLimits            = false;
2381             bool logInvocationLimits          = false;
2382 
2383             if (m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION)
2384             {
2385                 m_testCtx.getLog() << tcu::TestLog::Message
2386                                    << "Using implementation maximum geometry shader output limits."
2387                                    << tcu::TestLog::EndMessage;
2388 
2389                 gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, &geometryOutputComponents);
2390                 gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &geometryOutputVertices);
2391                 gl.getIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &geometryTotalOutputComponents);
2392                 GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry limits");
2393 
2394                 logGeometryLimits = true;
2395             }
2396             else if (m_flags & FLAG_GEOMETRY_MAX_SPEC)
2397             {
2398                 m_testCtx.getLog() << tcu::TestLog::Message
2399                                    << "Using geometry shader extension minimum maximum output limits."
2400                                    << tcu::TestLog::EndMessage;
2401 
2402                 geometryOutputComponents      = 128;
2403                 geometryOutputVertices        = 256;
2404                 geometryTotalOutputComponents = 1024;
2405                 logGeometryLimits             = true;
2406             }
2407             else
2408             {
2409                 geometryOutputComponents      = 128;
2410                 geometryOutputVertices        = 16;
2411                 geometryTotalOutputComponents = 1024;
2412             }
2413 
2414             if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION)
2415             {
2416                 gl.getIntegerv(GL_MAX_GEOMETRY_SHADER_INVOCATIONS, &geometryShaderInvocations);
2417                 GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry invocation limits");
2418 
2419                 logInvocationLimits = true;
2420             }
2421             else if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)
2422             {
2423                 geometryShaderInvocations = 32;
2424                 logInvocationLimits       = true;
2425             }
2426             else
2427             {
2428                 geometryShaderInvocations = 4;
2429             }
2430 
2431             if (logGeometryLimits || logInvocationLimits)
2432             {
2433                 tcu::MessageBuilder msg(&m_testCtx.getLog());
2434 
2435                 msg << "Geometry shader, targeting following limits:\n";
2436 
2437                 if (logGeometryLimits)
2438                     msg << "\tGL_MAX_GEOMETRY_OUTPUT_COMPONENTS = " << geometryOutputComponents << "\n"
2439                         << "\tGL_MAX_GEOMETRY_OUTPUT_VERTICES = " << geometryOutputVertices << "\n"
2440                         << "\tGL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = " << geometryTotalOutputComponents << "\n";
2441 
2442                 if (logInvocationLimits)
2443                     msg << "\tGL_MAX_GEOMETRY_SHADER_INVOCATIONS = " << geometryShaderInvocations;
2444 
2445                 msg << tcu::TestLog::EndMessage;
2446             }
2447 
2448             {
2449                 const bool separatePrimitives    = (m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0;
2450                 const int numComponentsPerVertex = 8; // vec4 pos, vec4 color
2451                 int numVerticesPerInvocation;
2452                 int numPrimitivesPerInvocation;
2453                 int geometryVerticesPerPrimitive;
2454                 int geometryPrimitivesOutPerPrimitive;
2455 
2456                 if (separatePrimitives)
2457                 {
2458                     const int numComponentLimit = geometryTotalOutputComponents / (4 * numComponentsPerVertex);
2459                     const int numOutputLimit    = geometryOutputVertices / 4;
2460 
2461                     numPrimitivesPerInvocation = de::min(numComponentLimit, numOutputLimit);
2462                     numVerticesPerInvocation   = numPrimitivesPerInvocation * 4;
2463                 }
2464                 else
2465                 {
2466                     // If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices.
2467                     // Each slice is a triangle strip and is generated by a single shader invocation.
2468                     // One slice with 4 segment ends (nodes) and 3 segments:
2469                     //    .__.__.__.
2470                     //    |\ |\ |\ |
2471                     //    |_\|_\|_\|
2472 
2473                     const int numSliceNodesComponentLimit =
2474                         geometryTotalOutputComponents / (2 * numComponentsPerVertex); // each node 2 vertices
2475                     const int numSliceNodesOutputLimit = geometryOutputVertices / 2;  // each node 2 vertices
2476                     const int numSliceNodes            = de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit);
2477 
2478                     numVerticesPerInvocation   = numSliceNodes * 2;
2479                     numPrimitivesPerInvocation = (numSliceNodes - 1) * 2;
2480                 }
2481 
2482                 geometryVerticesPerPrimitive      = numVerticesPerInvocation * geometryShaderInvocations;
2483                 geometryPrimitivesOutPerPrimitive = numPrimitivesPerInvocation * geometryShaderInvocations;
2484 
2485                 m_testCtx.getLog() << tcu::TestLog::Message << "Geometry shader:\n"
2486                                    << "\tTotal output vertex count per invocation: " << (numVerticesPerInvocation)
2487                                    << "\n"
2488                                    << "\tTotal output primitive count per invocation: " << (numPrimitivesPerInvocation)
2489                                    << "\n"
2490                                    << "\tNumber of invocations per primitive: " << geometryShaderInvocations << "\n"
2491                                    << "\tTotal output vertex count per input primitive: "
2492                                    << (geometryVerticesPerPrimitive) << "\n"
2493                                    << "\tTotal output primitive count per input primitive: "
2494                                    << (geometryPrimitivesOutPerPrimitive) << "\n"
2495                                    << tcu::TestLog::EndMessage;
2496 
2497                 sources << glu::GeometrySource(
2498                     getGeometryShaderSource(numPrimitivesPerInvocation, geometryShaderInvocations, tessGenLevel));
2499 
2500                 m_testCtx.getLog() << tcu::TestLog::Message << "Program:\n"
2501                                    << "\tTotal program output vertices count per input patch: "
2502                                    << (tessGenLevel * tessGenLevel * 2 * geometryVerticesPerPrimitive) << "\n"
2503                                    << "\tTotal program output primitive count per input patch: "
2504                                    << (tessGenLevel * tessGenLevel * 2 * geometryPrimitivesOutPerPrimitive) << "\n"
2505                                    << tcu::TestLog::EndMessage;
2506             }
2507         }
2508 
2509         m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources);
2510         m_testCtx.getLog() << *m_program;
2511         if (!m_program->isOk())
2512             throw tcu::TestError("failed to build program");
2513     }
2514 }
2515 
deinit(void)2516 void GridRenderCase::deinit(void)
2517 {
2518     delete m_program;
2519     m_program = DE_NULL;
2520 
2521     if (m_texture)
2522     {
2523         m_context.getRenderContext().getFunctions().deleteTextures(1, &m_texture);
2524         m_texture = 0;
2525     }
2526 }
2527 
iterate(void)2528 GridRenderCase::IterateResult GridRenderCase::iterate(void)
2529 {
2530     std::vector<tcu::Surface> renderedLayers(m_numLayers);
2531     bool allLayersOk = true;
2532 
2533     for (int ndx = 0; ndx < m_numLayers; ++ndx)
2534         renderedLayers[ndx].setSize(RENDER_SIZE, RENDER_SIZE);
2535 
2536     m_testCtx.getLog() << tcu::TestLog::Message
2537                        << "Rendering single point at the origin. Expecting yellow and green colored grid-like image. "
2538                           "(High-frequency grid may appear unicolored)."
2539                        << tcu::TestLog::EndMessage;
2540 
2541     try
2542     {
2543         renderTo(renderedLayers);
2544     }
2545     catch (const AllowedRenderFailureException &ex)
2546     {
2547         // Got accepted failure
2548         m_testCtx.getLog() << tcu::TestLog::Message << "Could not render, reason: " << ex.what() << "\n"
2549                            << "Failure is allowed." << tcu::TestLog::EndMessage;
2550 
2551         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2552         return STOP;
2553     }
2554 
2555     for (int ndx = 0; ndx < m_numLayers; ++ndx)
2556         allLayersOk &= verifyResultLayer(ndx, renderedLayers[ndx]);
2557 
2558     if (allLayersOk)
2559         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2560     else
2561         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed");
2562     return STOP;
2563 }
2564 
renderTo(std::vector<tcu::Surface> & dst)2565 void GridRenderCase::renderTo(std::vector<tcu::Surface> &dst)
2566 {
2567     const glw::Functions &gl   = m_context.getRenderContext().getFunctions();
2568     const int positionLocation = gl.getAttribLocation(m_program->getProgram(), "a_position");
2569     const glu::VertexArray vao(m_context.getRenderContext());
2570     de::MovePtr<glu::Framebuffer> fbo;
2571 
2572     if (positionLocation == -1)
2573         throw tcu::TestError("Attribute a_position location was -1");
2574 
2575     gl.viewport(0, 0, dst.front().getWidth(), dst.front().getHeight());
2576     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2577     GLU_EXPECT_NO_ERROR(gl.getError(), "viewport");
2578 
2579     gl.bindVertexArray(*vao);
2580     GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao");
2581 
2582     gl.useProgram(m_program->getProgram());
2583     GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
2584 
2585     gl.patchParameteri(GL_PATCH_VERTICES, 1);
2586     GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
2587 
2588     gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
2589 
2590     if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2591     {
2592         // clear texture contents
2593         {
2594             glu::Framebuffer clearFbo(m_context.getRenderContext());
2595             gl.bindFramebuffer(GL_FRAMEBUFFER, *clearFbo);
2596 
2597             for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
2598             {
2599                 gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx);
2600                 gl.clear(GL_COLOR_BUFFER_BIT);
2601             }
2602 
2603             GLU_EXPECT_NO_ERROR(gl.getError(), "clear tex contents");
2604         }
2605 
2606         // create and bind layered fbo
2607 
2608         fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext()));
2609 
2610         gl.bindFramebuffer(GL_FRAMEBUFFER, **fbo);
2611         gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0);
2612         GLU_EXPECT_NO_ERROR(gl.getError(), "gen fbo");
2613     }
2614     else
2615     {
2616         // clear viewport
2617         gl.clear(GL_COLOR_BUFFER_BIT);
2618     }
2619 
2620     // draw
2621     {
2622         glw::GLenum glerror;
2623 
2624         gl.drawArrays(GL_PATCHES, 0, 1);
2625 
2626         glerror = gl.getError();
2627         if (glerror == GL_OUT_OF_MEMORY && (m_flags & FLAG_ALLOW_OUT_OF_MEMORY))
2628             throw AllowedRenderFailureException("got GL_OUT_OF_MEMORY while drawing");
2629 
2630         GLU_EXPECT_NO_ERROR(glerror, "draw patches");
2631     }
2632 
2633     // Read layers
2634 
2635     if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2636     {
2637         glu::Framebuffer readFbo(m_context.getRenderContext());
2638         gl.bindFramebuffer(GL_FRAMEBUFFER, *readFbo);
2639 
2640         for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx)
2641         {
2642             gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx);
2643             glu::readPixels(m_context.getRenderContext(), 0, 0, dst[layerNdx].getAccess());
2644             GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
2645         }
2646     }
2647     else
2648     {
2649         glu::readPixels(m_context.getRenderContext(), 0, 0, dst.front().getAccess());
2650         GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels");
2651     }
2652 }
2653 
verifyResultLayer(int layerNdx,const tcu::Surface & image)2654 bool GridRenderCase::verifyResultLayer(int layerNdx, const tcu::Surface &image)
2655 {
2656     tcu::Surface errorMask(image.getWidth(), image.getHeight());
2657     bool foundError = false;
2658 
2659     tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
2660 
2661     m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output layer " << layerNdx << tcu::TestLog::EndMessage;
2662 
2663     for (int y = 0; y < image.getHeight(); ++y)
2664         for (int x = 0; x < image.getWidth(); ++x)
2665         {
2666             const int threshold   = 8;
2667             const tcu::RGBA color = image.getPixel(x, y);
2668 
2669             // Color must be a linear combination of green and yellow
2670             if (color.getGreen() < 255 - threshold || color.getBlue() > threshold)
2671             {
2672                 errorMask.setPixel(x, y, tcu::RGBA::red());
2673                 foundError = true;
2674             }
2675         }
2676 
2677     if (!foundError)
2678     {
2679         m_testCtx.getLog() << tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage
2680                            << tcu::TestLog::ImageSet("ImageVerification", "Image verification")
2681                            << tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
2682                            << tcu::TestLog::EndImageSet;
2683         return true;
2684     }
2685     else
2686     {
2687         m_testCtx.getLog() << tcu::TestLog::Message << "Image verification failed, found invalid pixels."
2688                            << tcu::TestLog::EndMessage
2689                            << tcu::TestLog::ImageSet("ImageVerification", "Image verification")
2690                            << tcu::TestLog::Image("Result", "Rendered result", image.getAccess())
2691                            << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess())
2692                            << tcu::TestLog::EndImageSet;
2693         return false;
2694     }
2695 }
2696 
getVertexSource(void)2697 std::string GridRenderCase::getVertexSource(void)
2698 {
2699     return specializeShader(s_positionVertexShader, m_context.getRenderContext().getType());
2700 }
2701 
getFragmentSource(void)2702 std::string GridRenderCase::getFragmentSource(void)
2703 {
2704     const char *source = "${VERSION_DECL}\n"
2705                          "flat in mediump vec4 v_color;\n"
2706                          "layout(location = 0) out mediump vec4 fragColor;\n"
2707                          "void main (void)\n"
2708                          "{\n"
2709                          "    fragColor = v_color;\n"
2710                          "}\n";
2711 
2712     return specializeShader(source, m_context.getRenderContext().getType());
2713 }
2714 
getTessellationControlSource(int tessLevel)2715 std::string GridRenderCase::getTessellationControlSource(int tessLevel)
2716 {
2717     std::ostringstream buf;
2718 
2719     buf << "${VERSION_DECL}\n"
2720            "${EXTENSION_TESSELATION_SHADER}"
2721            "layout(vertices=1) out;\n"
2722            "\n"
2723            "void main()\n"
2724            "{\n"
2725            "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
2726            "    gl_TessLevelOuter[0] = "
2727         << tessLevel
2728         << ".0;\n"
2729            "    gl_TessLevelOuter[1] = "
2730         << tessLevel
2731         << ".0;\n"
2732            "    gl_TessLevelOuter[2] = "
2733         << tessLevel
2734         << ".0;\n"
2735            "    gl_TessLevelOuter[3] = "
2736         << tessLevel
2737         << ".0;\n"
2738            "    gl_TessLevelInner[0] = "
2739         << tessLevel
2740         << ".0;\n"
2741            "    gl_TessLevelInner[1] = "
2742         << tessLevel
2743         << ".0;\n"
2744            "}\n";
2745 
2746     return specializeShader(buf.str(), m_context.getRenderContext().getType());
2747 }
2748 
getTessellationEvaluationSource(int tessLevel)2749 std::string GridRenderCase::getTessellationEvaluationSource(int tessLevel)
2750 {
2751     std::ostringstream buf;
2752 
2753     buf << "${VERSION_DECL}\n"
2754            "${EXTENSION_TESSELATION_SHADER}"
2755            "layout(quads) in;\n"
2756            "\n"
2757            "out mediump ivec2 v_tessellationGridPosition;\n"
2758            "\n"
2759            "// note: No need to use precise gl_Position since position does not depend on order\n"
2760            "void main (void)\n"
2761            "{\n";
2762 
2763     if (m_flags & (FLAG_GEOMETRY_SCATTER_INSTANCES | FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS))
2764         buf << "    // Cover only a small area in a corner. The area will be expanded in geometry shader to cover "
2765                "whole viewport\n"
2766                "    gl_Position = vec4(gl_TessCoord.x * 0.3 - 1.0, gl_TessCoord.y * 0.3 - 1.0, 0.0, 1.0);\n";
2767     else
2768         buf << "    // Fill the whole viewport\n"
2769                "    gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n";
2770 
2771     buf << "    // Calculate position in tessellation grid\n"
2772            "    v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float("
2773         << tessLevel
2774         << ")));\n"
2775            "}\n";
2776 
2777     return specializeShader(buf.str(), m_context.getRenderContext().getType());
2778 }
2779 
getGeometryShaderSource(int numPrimitives,int numInstances,int tessLevel)2780 std::string GridRenderCase::getGeometryShaderSource(int numPrimitives, int numInstances, int tessLevel)
2781 {
2782     std::ostringstream buf;
2783 
2784     buf << "${VERSION_DECL}\n"
2785            "${EXTENSION_GEOMETRY_SHADER}"
2786            "layout(triangles, invocations="
2787         << numInstances
2788         << ") in;\n"
2789            "layout(triangle_strip, max_vertices="
2790         << ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) ? (4 * numPrimitives) : (numPrimitives + 2))
2791         << ") out;\n"
2792            "\n"
2793            "in mediump ivec2 v_tessellationGridPosition[];\n"
2794            "flat out highp vec4 v_color;\n"
2795            "\n"
2796            "void main ()\n"
2797            "{\n"
2798            "    const float equalThreshold = 0.001;\n"
2799            "    const float gapOffset = 0.0001; // subdivision performed by the geometry shader might produce gaps. "
2800            "Fill potential gaps by enlarging the output slice a little.\n"
2801            "\n"
2802            "    // Input triangle is generated from an axis-aligned rectangle by splitting it in half\n"
2803            "    // Original rectangle can be found by finding the bounding AABB of the triangle\n"
2804            "    vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
2805            "                     min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)),\n"
2806            "                     max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n"
2807            "                     max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)));\n"
2808            "\n"
2809            "    // Location in tessellation grid\n"
2810            "    ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], "
2811            "v_tessellationGridPosition[2])));\n"
2812            "\n"
2813            "    // Which triangle of the two that split the grid cell\n"
2814            "    int numVerticesOnBottomEdge = 0;\n"
2815            "    for (int ndx = 0; ndx < 3; ++ndx)\n"
2816            "        if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n"
2817            "            ++numVerticesOnBottomEdge;\n"
2818            "    bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n"
2819            "\n";
2820 
2821     if (m_flags & FLAG_GEOMETRY_SCATTER_PRIMITIVES)
2822     {
2823         // scatter primitives
2824         buf << "    // Draw grid cells\n"
2825                "    int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
2826                "    for (int ndx = 0; ndx < "
2827             << numPrimitives
2828             << "; ++ndx)\n"
2829                "    {\n"
2830                "        ivec2 dstGridSize = ivec2("
2831             << tessLevel << " * " << numPrimitives << ", 2 * " << tessLevel << " * " << numInstances
2832             << ");\n"
2833                "        ivec2 dstGridNdx = ivec2("
2834             << tessLevel << " * ndx + gridPosition.x, " << tessLevel
2835             << " * inputTriangleNdx + 2 * gridPosition.y + ndx * 127) % dstGridSize;\n"
2836                "        vec4 dstArea;\n"
2837                "        dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
2838                "        dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
2839                "        dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
2840                "        dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
2841                "\n"
2842                "        vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
2843                "        vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
2844                "        vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
2845                "\n"
2846                "        gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
2847                "        v_color = outputColor;\n"
2848                "        EmitVertex();\n"
2849                "\n"
2850                "        gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
2851                "        v_color = outputColor;\n"
2852                "        EmitVertex();\n"
2853                "\n"
2854                "        gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
2855                "        v_color = outputColor;\n"
2856                "        EmitVertex();\n"
2857                "\n"
2858                "        gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
2859                "        v_color = outputColor;\n"
2860                "        EmitVertex();\n"
2861                "        EndPrimitive();\n"
2862                "    }\n";
2863     }
2864     else if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS)
2865     {
2866         // Number of subrectangle instances = num layers
2867         DE_ASSERT(m_numLayers == numInstances * 2);
2868 
2869         buf << "    // Draw grid cells, send each primitive to a separate layer\n"
2870                "    int baseLayer = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
2871                "    for (int ndx = 0; ndx < "
2872             << numPrimitives
2873             << "; ++ndx)\n"
2874                "    {\n"
2875                "        ivec2 dstGridSize = ivec2("
2876             << tessLevel << " * " << numPrimitives << ", " << tessLevel
2877             << ");\n"
2878                "        ivec2 dstGridNdx = ivec2((gridPosition.x * "
2879             << numPrimitives
2880             << " * 7 + ndx)*13, (gridPosition.y * 127 + ndx) * 19) % dstGridSize;\n"
2881                "        vec4 dstArea;\n"
2882                "        dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n"
2883                "        dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n"
2884                "        dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n"
2885                "        dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n"
2886                "\n"
2887                "        vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
2888                "        vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
2889                "        vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n"
2890                "\n"
2891                "        gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n"
2892                "        v_color = outputColor;\n"
2893                "        gl_Layer = ((baseLayer + ndx) * 11) % "
2894             << m_numLayers
2895             << ";\n"
2896                "        EmitVertex();\n"
2897                "\n"
2898                "        gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n"
2899                "        v_color = outputColor;\n"
2900                "        gl_Layer = ((baseLayer + ndx) * 11) % "
2901             << m_numLayers
2902             << ";\n"
2903                "        EmitVertex();\n"
2904                "\n"
2905                "        gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n"
2906                "        v_color = outputColor;\n"
2907                "        gl_Layer = ((baseLayer + ndx) * 11) % "
2908             << m_numLayers
2909             << ";\n"
2910                "        EmitVertex();\n"
2911                "\n"
2912                "        gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n"
2913                "        v_color = outputColor;\n"
2914                "        gl_Layer = ((baseLayer + ndx) * 11) % "
2915             << m_numLayers
2916             << ";\n"
2917                "        EmitVertex();\n"
2918                "        EndPrimitive();\n"
2919                "    }\n";
2920     }
2921     else
2922     {
2923         if (m_flags & FLAG_GEOMETRY_SCATTER_INSTANCES)
2924         {
2925             buf << "    // Scatter slices\n"
2926                    "    int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n"
2927                    "    ivec2 srcSliceNdx = ivec2(gridPosition.x, gridPosition.y * "
2928                 << (numInstances * 2)
2929                 << " + inputTriangleNdx);\n"
2930                    "    ivec2 dstSliceNdx = ivec2(7 * srcSliceNdx.x, 127 * srcSliceNdx.y) % ivec2("
2931                 << tessLevel << ", " << tessLevel << " * " << (numInstances * 2)
2932                 << ");\n"
2933                    "\n"
2934                    "    // Draw slice to the dstSlice slot\n"
2935                    "    vec4 outputSliceArea;\n"
2936                    "    outputSliceArea.x = float(dstSliceNdx.x) / float("
2937                 << tessLevel
2938                 << ") * 2.0 - 1.0 - gapOffset;\n"
2939                    "    outputSliceArea.y = float(dstSliceNdx.y) / float("
2940                 << (tessLevel * numInstances * 2)
2941                 << ") * 2.0 - 1.0 - gapOffset;\n"
2942                    "    outputSliceArea.z = float(dstSliceNdx.x+1) / float("
2943                 << tessLevel
2944                 << ") * 2.0 - 1.0 + gapOffset;\n"
2945                    "    outputSliceArea.w = float(dstSliceNdx.y+1) / float("
2946                 << (tessLevel * numInstances * 2) << ") * 2.0 - 1.0 + gapOffset;\n";
2947         }
2948         else
2949         {
2950             buf << "    // Fill the input area with slices\n"
2951                    "    // Upper triangle produces slices only to the upper half of the quad and vice-versa\n"
2952                    "    float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n"
2953                    "    // Each slice is a invocation\n"
2954                    "    float sliceHeight = (aabb.w - aabb.y) / float(2 * "
2955                 << numInstances
2956                 << ");\n"
2957                    "    float invocationOffset = float(gl_InvocationID) * sliceHeight;\n"
2958                    "\n"
2959                    "    vec4 outputSliceArea;\n"
2960                    "    outputSliceArea.x = aabb.x - gapOffset;\n"
2961                    "    outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n"
2962                    "    outputSliceArea.z = aabb.z + gapOffset;\n"
2963                    "    outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n";
2964         }
2965 
2966         buf << "\n"
2967                "    // Draw slice\n"
2968                "    for (int ndx = 0; ndx < "
2969             << ((numPrimitives + 2) / 2)
2970             << "; ++ndx)\n"
2971                "    {\n"
2972                "        vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n"
2973                "        vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n"
2974                "        vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n"
2975                "        float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float("
2976             << (numPrimitives / 2)
2977             << "));\n"
2978                "\n"
2979                "        gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n"
2980                "        v_color = outputColor;\n"
2981                "        EmitVertex();\n"
2982                "\n"
2983                "        gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n"
2984                "        v_color = outputColor;\n"
2985                "        EmitVertex();\n"
2986                "    }\n";
2987     }
2988 
2989     buf << "}\n";
2990 
2991     return specializeShader(buf.str(), m_context.getRenderContext().getType());
2992 }
2993 
2994 class FeedbackRecordVariableSelectionCase : public TestCase
2995 {
2996 public:
2997     FeedbackRecordVariableSelectionCase(Context &context, const char *name, const char *description);
2998     ~FeedbackRecordVariableSelectionCase(void);
2999 
3000 private:
3001     void init(void);
3002     void deinit(void);
3003     IterateResult iterate(void);
3004 
3005     std::string getVertexSource(void);
3006     std::string getFragmentSource(void);
3007     std::string getTessellationControlSource(void);
3008     std::string getTessellationEvaluationSource(void);
3009     std::string getGeometrySource(void);
3010 
3011     glu::ShaderProgram *m_program;
3012     uint32_t m_xfbBuf;
3013 };
3014 
FeedbackRecordVariableSelectionCase(Context & context,const char * name,const char * description)3015 FeedbackRecordVariableSelectionCase::FeedbackRecordVariableSelectionCase(Context &context, const char *name,
3016                                                                          const char *description)
3017     : TestCase(context, name, description)
3018     , m_program(DE_NULL)
3019     , m_xfbBuf(0)
3020 {
3021 }
3022 
~FeedbackRecordVariableSelectionCase(void)3023 FeedbackRecordVariableSelectionCase::~FeedbackRecordVariableSelectionCase(void)
3024 {
3025     deinit();
3026 }
3027 
init(void)3028 void FeedbackRecordVariableSelectionCase::init(void)
3029 {
3030     const bool supportsES32   = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2));
3031     const bool supportsCore40 = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0));
3032 
3033     if ((!supportsES32 && !supportsCore40) &&
3034         (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
3035          !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")))
3036         throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions");
3037 
3038     m_testCtx.getLog() << tcu::TestLog::Message
3039                        << "Declaring multiple output variables with the same name in multiple shader stages. Capturing "
3040                           "the value of the varying using transform feedback."
3041                        << tcu::TestLog::EndMessage;
3042 
3043     // gen feedback buffer fit for 1 triangle (4 components)
3044     {
3045         static const tcu::Vec4 initialData[3] = {
3046             tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
3047             tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
3048             tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f),
3049         };
3050 
3051         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3052 
3053         m_testCtx.getLog()
3054             << tcu::TestLog::Message
3055             << "Creating buffer for transform feedback. Allocating storage for one triangle. Filling with -1.0"
3056             << tcu::TestLog::EndMessage;
3057 
3058         gl.genBuffers(1, &m_xfbBuf);
3059         gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_xfbBuf);
3060         gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, (int)(sizeof(tcu::Vec4[3])), initialData, GL_DYNAMIC_READ);
3061         GLU_EXPECT_NO_ERROR(gl.getError(), "gen xfb buf");
3062     }
3063 
3064     // gen shader
3065     m_program = new glu::ShaderProgram(
3066         m_context.getRenderContext(),
3067         glu::ProgramSources() << glu::VertexSource(getVertexSource()) << glu::FragmentSource(getFragmentSource())
3068                               << glu::TessellationControlSource(getTessellationControlSource())
3069                               << glu::TessellationEvaluationSource(getTessellationEvaluationSource())
3070                               << glu::GeometrySource(getGeometrySource())
3071                               << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS)
3072                               << glu::TransformFeedbackVarying("tf_feedback"));
3073     m_testCtx.getLog() << *m_program;
3074 
3075     if (!m_program->isOk())
3076         throw tcu::TestError("could not build program");
3077 }
3078 
deinit(void)3079 void FeedbackRecordVariableSelectionCase::deinit(void)
3080 {
3081     delete m_program;
3082     m_program = DE_NULL;
3083 
3084     if (m_xfbBuf)
3085     {
3086         m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_xfbBuf);
3087         m_xfbBuf = 0;
3088     }
3089 }
3090 
iterate(void)3091 FeedbackRecordVariableSelectionCase::IterateResult FeedbackRecordVariableSelectionCase::iterate(void)
3092 {
3093     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3094     const int posLoc         = gl.getAttribLocation(m_program->getProgram(), "a_position");
3095     const glu::VertexArray vao(m_context.getRenderContext());
3096 
3097     if (posLoc == -1)
3098         throw tcu::TestError("a_position attribute location was -1");
3099 
3100     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3101 
3102     m_testCtx.getLog() << tcu::TestLog::Message << "Rendering a patch of size 3." << tcu::TestLog::EndMessage;
3103 
3104     // Render and feed back
3105 
3106     gl.viewport(0, 0, 1, 1);
3107     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
3108     gl.clear(GL_COLOR_BUFFER_BIT);
3109     GLU_EXPECT_NO_ERROR(gl.getError(), "clear");
3110 
3111     gl.bindVertexArray(*vao);
3112     GLU_EXPECT_NO_ERROR(gl.getError(), "bindVertexArray");
3113 
3114     gl.vertexAttrib4f(posLoc, 0.0f, 0.0f, 0.0f, 1.0f);
3115     GLU_EXPECT_NO_ERROR(gl.getError(), "vertexAttrib4f");
3116 
3117     gl.useProgram(m_program->getProgram());
3118     GLU_EXPECT_NO_ERROR(gl.getError(), "use program");
3119 
3120     gl.patchParameteri(GL_PATCH_VERTICES, 3);
3121     GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param");
3122 
3123     gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_xfbBuf);
3124     GLU_EXPECT_NO_ERROR(gl.getError(), "bind xfb buf");
3125 
3126     gl.beginTransformFeedback(GL_TRIANGLES);
3127     GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback");
3128 
3129     gl.drawArrays(GL_PATCHES, 0, 3);
3130     GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays");
3131 
3132     gl.endTransformFeedback();
3133     GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback");
3134 
3135     m_testCtx.getLog() << tcu::TestLog::Message
3136                        << "Verifying the value of tf_feedback using transform feedback, expecting (3.0, 3.0, 3.0, 3.0)."
3137                        << tcu::TestLog::EndMessage;
3138 
3139     // Read back result (one triangle)
3140     {
3141         tcu::Vec4 feedbackValues[3];
3142         const void *mapPtr =
3143             gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, (int)sizeof(feedbackValues), GL_MAP_READ_BIT);
3144         GLU_EXPECT_NO_ERROR(gl.getError(), "mapBufferRange");
3145 
3146         if (mapPtr == DE_NULL)
3147             throw tcu::TestError("mapBufferRange returned null");
3148 
3149         deMemcpy(feedbackValues, mapPtr, sizeof(feedbackValues));
3150 
3151         if (gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER) != GL_TRUE)
3152             throw tcu::TestError("unmapBuffer did not return TRUE");
3153 
3154         for (int ndx = 0; ndx < 3; ++ndx)
3155         {
3156             if (!tcu::boolAll(tcu::lessThan(tcu::abs(feedbackValues[ndx] - tcu::Vec4(3.0f)), tcu::Vec4(0.001f))))
3157             {
3158                 m_testCtx.getLog() << tcu::TestLog::Message << "Feedback vertex " << ndx
3159                                    << ": expected (3.0, 3.0, 3.0, 3.0), got " << feedbackValues[ndx]
3160                                    << tcu::TestLog::EndMessage;
3161                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected feedback results");
3162             }
3163         }
3164     }
3165 
3166     return STOP;
3167 }
3168 
getVertexSource(void)3169 std::string FeedbackRecordVariableSelectionCase::getVertexSource(void)
3170 {
3171     std::string source = "${VERSION_DECL}\n"
3172                          "in highp vec4 a_position;\n"
3173                          "out highp vec4 tf_feedback;\n"
3174                          "void main()\n"
3175                          "{\n"
3176                          "    gl_Position = a_position;\n"
3177                          "    tf_feedback = vec4(1.0, 1.0, 1.0, 1.0);\n"
3178                          "}\n";
3179 
3180     return specializeShader(source, m_context.getRenderContext().getType());
3181 }
3182 
getFragmentSource(void)3183 std::string FeedbackRecordVariableSelectionCase::getFragmentSource(void)
3184 {
3185     return specializeShader(s_whiteOutputFragmentShader, m_context.getRenderContext().getType());
3186 }
3187 
getTessellationControlSource(void)3188 std::string FeedbackRecordVariableSelectionCase::getTessellationControlSource(void)
3189 {
3190     std::string source = "${VERSION_DECL}\n"
3191                          "${EXTENSION_TESSELATION_SHADER}"
3192                          "layout(vertices=3) out;\n"
3193                          "void main()\n"
3194                          "{\n"
3195                          "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
3196                          "    gl_TessLevelOuter[0] = 1.0;\n"
3197                          "    gl_TessLevelOuter[1] = 1.0;\n"
3198                          "    gl_TessLevelOuter[2] = 1.0;\n"
3199                          "    gl_TessLevelInner[0] = 1.0;\n"
3200                          "}\n";
3201 
3202     return specializeShader(source, m_context.getRenderContext().getType());
3203 }
3204 
getTessellationEvaluationSource(void)3205 std::string FeedbackRecordVariableSelectionCase::getTessellationEvaluationSource(void)
3206 {
3207     std::string source = "${VERSION_DECL}\n"
3208                          "${EXTENSION_TESSELATION_SHADER}"
3209                          "layout(triangles) in;\n"
3210                          "out highp vec4 tf_feedback;\n"
3211                          "void main()\n"
3212                          "{\n"
3213                          "    gl_Position = gl_in[0].gl_Position * gl_TessCoord.x + gl_in[1].gl_Position * "
3214                          "gl_TessCoord.y + gl_in[2].gl_Position * gl_TessCoord.z;\n"
3215                          "    tf_feedback = vec4(2.0, 2.0, 2.0, 2.0);\n"
3216                          "}\n";
3217 
3218     return specializeShader(source, m_context.getRenderContext().getType());
3219 }
3220 
getGeometrySource(void)3221 std::string FeedbackRecordVariableSelectionCase::getGeometrySource(void)
3222 {
3223     std::string source =
3224         "${VERSION_DECL}\n"
3225         "${EXTENSION_GEOMETRY_SHADER}"
3226         "layout (triangles) in;\n"
3227         "layout (triangle_strip, max_vertices=3) out;\n"
3228         "out highp vec4 tf_feedback;\n"
3229         "void main()\n"
3230         "{\n"
3231         "    for (int ndx = 0; ndx < 3; ++ndx)\n"
3232         "    {\n"
3233         "        gl_Position = gl_in[ndx].gl_Position + vec4(float(ndx), float(ndx)*float(ndx), 0.0, 0.0);\n"
3234         "        tf_feedback = vec4(3.0, 3.0, 3.0, 3.0);\n"
3235         "        EmitVertex();\n"
3236         "    }\n"
3237         "    EndPrimitive();\n"
3238         "}\n";
3239 
3240     return specializeShader(source, m_context.getRenderContext().getType());
3241 }
3242 
3243 } // namespace
3244 
TessellationGeometryInteractionTests(Context & context,bool isGL45)3245 TessellationGeometryInteractionTests::TessellationGeometryInteractionTests(Context &context, bool isGL45)
3246     : TestCaseGroup(context, "tessellation_geometry_interaction", "Tessellation and geometry shader interaction tests")
3247     , m_isGL45(isGL45)
3248 {
3249 }
3250 
~TessellationGeometryInteractionTests(void)3251 TessellationGeometryInteractionTests::~TessellationGeometryInteractionTests(void)
3252 {
3253 }
3254 
init(void)3255 void TessellationGeometryInteractionTests::init(void)
3256 {
3257     tcu::TestCaseGroup *const renderGroup    = new tcu::TestCaseGroup(m_testCtx, "render", "Various render tests");
3258     tcu::TestCaseGroup *const feedbackGroup  = new tcu::TestCaseGroup(m_testCtx, "feedback", "Test transform feedback");
3259     tcu::TestCaseGroup *const pointSizeGroup = new tcu::TestCaseGroup(m_testCtx, "point_size", "Test point size");
3260 
3261     addChild(renderGroup);
3262     addChild(feedbackGroup);
3263     addChild(pointSizeGroup);
3264 
3265     // .render
3266     {
3267         tcu::TestCaseGroup *const passthroughGroup = new tcu::TestCaseGroup(
3268             m_testCtx, "passthrough", "Render various types with either passthrough geometry or tessellation shader");
3269         tcu::TestCaseGroup *const limitGroup =
3270             new tcu::TestCaseGroup(m_testCtx, "limits", "Render with properties near their limits");
3271         tcu::TestCaseGroup *const scatterGroup =
3272             new tcu::TestCaseGroup(m_testCtx, "scatter", "Scatter output primitives");
3273 
3274         renderGroup->addChild(passthroughGroup);
3275         renderGroup->addChild(limitGroup);
3276         renderGroup->addChild(scatterGroup);
3277 
3278         // .passthrough
3279         {
3280             // tessellate_tris_passthrough_geometry_no_change
3281             // tessellate_quads_passthrough_geometry_no_change
3282             // tessellate_isolines_passthrough_geometry_no_change
3283             passthroughGroup->addChild(new IdentityGeometryShaderCase(
3284                 m_context, "tessellate_tris_passthrough_geometry_no_change",
3285                 "Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_TRIANGLES));
3286             passthroughGroup->addChild(new IdentityGeometryShaderCase(
3287                 m_context, "tessellate_quads_passthrough_geometry_no_change",
3288                 "Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_QUADS));
3289             passthroughGroup->addChild(new IdentityGeometryShaderCase(
3290                 m_context, "tessellate_isolines_passthrough_geometry_no_change",
3291                 "Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_ISOLINES));
3292 
3293             // passthrough_tessellation_geometry_shade_triangles_no_change
3294             // passthrough_tessellation_geometry_shade_lines_no_change
3295             passthroughGroup->addChild(new IdentityTessellationShaderCase(
3296                 m_context, "passthrough_tessellation_geometry_shade_triangles_no_change",
3297                 "Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_TRIANGLES));
3298             passthroughGroup->addChild(new IdentityTessellationShaderCase(
3299                 m_context, "passthrough_tessellation_geometry_shade_lines_no_change",
3300                 "Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_ISOLINES));
3301         }
3302 
3303         // .limits
3304         {
3305             static const struct LimitCaseDef
3306             {
3307                 const char *name;
3308                 const char *desc;
3309                 int flags;
3310             } cases[] = {
3311                 // Test single limit
3312                 {"output_required_max_tessellation", "Minimum maximum tessellation level",
3313                  GridRenderCase::FLAG_TESSELLATION_MAX_SPEC},
3314                 {"output_implementation_max_tessellation", "Maximum tessellation level supported by the implementation",
3315                  GridRenderCase::FLAG_TESSELLATION_MAX_IMPLEMENTATION},
3316                 {"output_required_max_geometry", "Output minimum maximum number of vertices the geometry shader",
3317                  GridRenderCase::FLAG_GEOMETRY_MAX_SPEC},
3318                 {"output_implementation_max_geometry",
3319                  "Output maximum number of vertices in the geometry shader supported by the implementation",
3320                  GridRenderCase::FLAG_GEOMETRY_MAX_IMPLEMENTATION},
3321                 {"output_required_max_invocations", "Minimum maximum number of geometry shader invocations",
3322                  GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC},
3323                 {"output_implementation_max_invocations",
3324                  "Maximum number of geometry shader invocations supported by the implementation",
3325                  GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION},
3326             };
3327 
3328             for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx)
3329                 limitGroup->addChild(new GridRenderCase(m_context, cases[ndx].name, cases[ndx].desc, cases[ndx].flags));
3330         }
3331 
3332         // .scatter
3333         {
3334             scatterGroup->addChild(new GridRenderCase(
3335                 m_context, "geometry_scatter_instances",
3336                 "Each geometry shader instance outputs its primitives far from other instances of the same execution",
3337                 GridRenderCase::FLAG_GEOMETRY_SCATTER_INSTANCES));
3338             scatterGroup->addChild(new GridRenderCase(
3339                 m_context, "geometry_scatter_primitives",
3340                 "Each geometry shader instance outputs its primitives far from other primitives of the same instance",
3341                 GridRenderCase::FLAG_GEOMETRY_SCATTER_PRIMITIVES | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES));
3342             scatterGroup->addChild(new GridRenderCase(
3343                 m_context, "geometry_scatter_layers",
3344                 "Each geometry shader instance outputs its primitives to multiple layers and far from other primitives "
3345                 "of the same instance",
3346                 GridRenderCase::FLAG_GEOMETRY_SCATTER_LAYERS | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES));
3347         }
3348     }
3349 
3350     // .feedback
3351     {
3352         static const struct PrimitiveCaseConfig
3353         {
3354             const char *name;
3355             const char *description;
3356             FeedbackPrimitiveTypeCase::TessellationOutputType tessellationOutput;
3357             FeedbackPrimitiveTypeCase::TessellationPointMode tessellationPointMode;
3358             FeedbackPrimitiveTypeCase::GeometryOutputType geometryOutputType;
3359         } caseConfigs[] = {
3360             // tess output triangles -> geo input triangles, output points
3361             {"tessellation_output_triangles_geometry_output_points",
3362              "Tessellation outputs triangles, geometry outputs points",
3363              FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES,
3364              FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF, FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS},
3365 
3366             // tess output quads <-> geo input triangles, output points
3367             {"tessellation_output_quads_geometry_output_points", "Tessellation outputs quads, geometry outputs points",
3368              FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS, FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF,
3369              FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS},
3370 
3371             // tess output isolines <-> geo input lines, output points
3372             {"tessellation_output_isolines_geometry_output_points",
3373              "Tessellation outputs isolines, geometry outputs points",
3374              FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES,
3375              FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF, FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS},
3376 
3377             // tess output triangles, point_mode <-> geo input points, output lines
3378             {"tessellation_output_triangles_point_mode_geometry_output_lines",
3379              "Tessellation outputs triangles in point mode, geometry outputs lines",
3380              FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES,
3381              FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON, FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES},
3382 
3383             // tess output quads, point_mode <-> geo input points, output lines
3384             {"tessellation_output_quads_point_mode_geometry_output_lines",
3385              "Tessellation outputs quads in point mode, geometry outputs lines",
3386              FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS, FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
3387              FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES},
3388 
3389             // tess output isolines, point_mode <-> geo input points, output triangles
3390             {"tessellation_output_isolines_point_mode_geometry_output_triangles",
3391              "Tessellation outputs isolines in point mode, geometry outputs triangles",
3392              FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES, FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON,
3393              FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_TRIANGLES},
3394         };
3395 
3396         for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseConfigs); ++ndx)
3397         {
3398             feedbackGroup->addChild(new FeedbackPrimitiveTypeCase(
3399                 m_context, caseConfigs[ndx].name, caseConfigs[ndx].description, caseConfigs[ndx].tessellationOutput,
3400                 caseConfigs[ndx].tessellationPointMode, caseConfigs[ndx].geometryOutputType));
3401         }
3402 
3403         feedbackGroup->addChild(new FeedbackRecordVariableSelectionCase(
3404             m_context, "record_variable_selection",
3405             "Record a variable that has been declared as an output variable in multiple shader stages"));
3406     }
3407 
3408     // .point_size
3409     {
3410         static const struct PointSizeCaseConfig
3411         {
3412             const int caseMask;
3413             const bool isSupportedInGL; // is this case supported in OpenGL
3414         } caseConfigs[] = {
3415             {PointSizeCase::FLAG_VERTEX_SET, true},
3416             {PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET, true},
3417             {PointSizeCase::FLAG_GEOMETRY_SET, true},
3418             {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_CONTROL_SET, false},
3419             {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET, true},
3420             {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_DONT_SET, false},
3421             {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_GEOMETRY_SET, true},
3422             {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET |
3423                  PointSizeCase::FLAG_GEOMETRY_SET,
3424              true},
3425             {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_ADD | PointSizeCase::FLAG_GEOMETRY_ADD,
3426              true},
3427             {PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET |
3428                  PointSizeCase::FLAG_GEOMETRY_DONT_SET,
3429              false},
3430         };
3431 
3432         for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseConfigs); ++ndx)
3433         {
3434             if (m_isGL45 && !caseConfigs[ndx].isSupportedInGL)
3435                 continue;
3436 
3437             const std::string name = PointSizeCase::genTestCaseName(caseConfigs[ndx].caseMask);
3438             const std::string desc = PointSizeCase::genTestCaseDescription(caseConfigs[ndx].caseMask);
3439 
3440             pointSizeGroup->addChild(
3441                 new PointSizeCase(m_context, name.c_str(), desc.c_str(), caseConfigs[ndx].caseMask));
3442         }
3443     }
3444 }
3445 
3446 } // namespace Functional
3447 } // namespace gles31
3448 } // namespace deqp
3449