1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2014-2016 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  */ /*!
20  * \file
21  * \brief
22  */ /*-------------------------------------------------------------------*/
23 
24 #include "esextcTessellationShaderBarrier.hpp"
25 #include "gluContextInfo.hpp"
26 #include "gluDefs.hpp"
27 #include "glwEnums.hpp"
28 #include "glwFunctions.hpp"
29 #include "tcuTestLog.hpp"
30 
31 namespace glcts
32 {
33 
34 /** Constructor
35  *
36  * @param context       Test context
37  * @param name          Test case's name
38  * @param description   Test case's description
39  **/
TessellationShaderBarrierTests(Context & context,const ExtParameters & extParams)40 TessellationShaderBarrierTests::TessellationShaderBarrierTests(Context &context, const ExtParameters &extParams)
41     : TestCaseGroupBase(context, extParams, "tessellation_shader_tc_barriers",
42                         "Verifies memory barrier work correctly when used in"
43                         "tessellation control shaders")
44 {
45     /* Left blank on purpose */
46 }
47 
48 /* Instantiates all tests and adds them as children to the node */
init(void)49 void TessellationShaderBarrierTests::init(void)
50 {
51     addChild(new glcts::TessellationShaderBarrier1(m_context, m_extParams));
52     addChild(new glcts::TessellationShaderBarrier2(m_context, m_extParams));
53     addChild(new glcts::TessellationShaderBarrier3(m_context, m_extParams));
54 }
55 
56 /** Constructor
57  *
58  * @param context       Test context
59  * @param name          Test case's name
60  * @param description   Test case's desricption
61  **/
TessellationShaderBarrierTestCase(Context & context,const ExtParameters & extParams,const char * name,const char * description)62 TessellationShaderBarrierTestCase::TessellationShaderBarrierTestCase(Context &context, const ExtParameters &extParams,
63                                                                      const char *name, const char *description)
64     : TestCaseBase(context, extParams, name, description)
65     , m_bo_id(0)
66     , m_fs_id(0)
67     , m_po_id(0)
68     , m_tcs_id(0)
69     , m_tes_id(0)
70     , m_vao_id(0)
71     , m_vs_id(0)
72 {
73     /* Left blank on purpose */
74 }
75 
76 /** Deinitializes all ES objects created for the test. */
deinit()77 void TessellationShaderBarrierTestCase::deinit()
78 {
79     /** Call base class' deinit() function */
80     TestCaseBase::deinit();
81 
82     if (!m_is_tessellation_shader_supported)
83     {
84         return;
85     }
86 
87     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
88 
89     /* Disable GL_RASTERIZER_DISCARD mode the test has enabled */
90     gl.disable(GL_RASTERIZER_DISCARD);
91 
92     /* Bring back the original GL_PATCH_VERTICES_EXT setting */
93     gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3);
94 
95     /* Revert buffer object bindings */
96     gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */);
97     gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */);
98 
99     /* Unbind vertex array object */
100     gl.bindVertexArray(0);
101 
102     /* Free all the objects that might have been initialized */
103     if (m_bo_id != 0)
104     {
105         gl.deleteBuffers(1, &m_bo_id);
106 
107         m_bo_id = 0;
108     }
109 
110     if (m_fs_id != 0)
111     {
112         gl.deleteShader(m_fs_id);
113 
114         m_fs_id = 0;
115     }
116 
117     if (m_po_id != 0)
118     {
119         gl.deleteProgram(m_po_id);
120 
121         m_po_id = 0;
122     }
123 
124     if (m_tcs_id != 0)
125     {
126         gl.deleteShader(m_tcs_id);
127 
128         m_tcs_id = 0;
129     }
130 
131     if (m_tes_id != 0)
132     {
133         gl.deleteShader(m_tes_id);
134 
135         m_tes_id = 0;
136     }
137 
138     if (m_vs_id != 0)
139     {
140         gl.deleteShader(m_vs_id);
141 
142         m_vs_id = 0;
143     }
144 
145     if (m_vao_id != 0)
146     {
147         gl.deleteVertexArrays(1, &m_vao_id);
148 
149         m_vao_id = 0;
150     }
151 }
152 
153 /** Initializes all ES objects that will be used for the test. */
initTest()154 void TessellationShaderBarrierTestCase::initTest()
155 {
156     /* The test requires EXT_tessellation_shader */
157     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
158 
159     if (!m_is_tessellation_shader_supported)
160     {
161         return;
162     }
163 
164     gl.genVertexArrays(1, &m_vao_id);
165     GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
166 
167     gl.bindVertexArray(m_vao_id);
168     GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
169 
170     /* Set up storage for XFB data. Also configure the BO binding points */
171     const unsigned int xfb_data_size = getXFBBufferSize();
172 
173     gl.genBuffers(1, &m_bo_id);
174     gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id);
175     gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id);
176     gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, xfb_data_size, NULL /* data */, GL_STATIC_DRAW);
177 
178     GLU_EXPECT_NO_ERROR(gl.getError(), "Could not set up storage for XFB data");
179 
180     /* Set up shader objects */
181     m_fs_id  = gl.createShader(GL_FRAGMENT_SHADER);
182     m_tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER);
183     m_tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER);
184     m_vs_id  = gl.createShader(GL_VERTEX_SHADER);
185 
186     GLU_EXPECT_NO_ERROR(gl.getError(), "Could not create shader objects");
187 
188     /* Set up fragment shader body */
189     const char *fs_body = "${VERSION}\n"
190                           "\n"
191                           "void main()\n"
192                           "{\n"
193                           "}\n";
194 
195     /* Set up tessellation control shader body */
196     const char *tcs_body = getTCSCode();
197 
198     /* Set up tessellation evaluation shader body */
199     const char *tes_body = getTESCode();
200 
201     /* Set up vertex shader body */
202     const char *vs_body = getVSCode();
203 
204     /* Set up a program object */
205     m_po_id = gl.createProgram();
206 
207     GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() failed");
208 
209     /* Set up XFB */
210     const char **names = NULL;
211     int n_names        = 0;
212 
213     getXFBProperties(&n_names, &names);
214 
215     gl.transformFeedbackVaryings(m_po_id, n_names, names, GL_INTERLEAVED_ATTRIBS);
216     GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() failed");
217 
218     /* Try to compile and link all the shader objects */
219     if (!buildProgram(m_po_id, m_fs_id, 1, &fs_body, m_tcs_id, 1, &tcs_body, m_tes_id, 1, &tes_body, m_vs_id, 1,
220                       &vs_body))
221     {
222         TCU_FAIL("Program linking failed");
223     }
224 
225     /* All set! */
226 }
227 
228 /** Executes the test.
229  *
230  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
231  *
232  *  Note the function throws exception should an error occur!
233  *
234  *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
235  **/
iterate(void)236 tcu::TestNode::IterateResult TessellationShaderBarrierTestCase::iterate(void)
237 {
238     initTest();
239 
240     /* Do not execute if required extensions are not supported. */
241     if (!m_is_tessellation_shader_supported)
242     {
243         throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
244     }
245 
246     /* Prepare for the draw call */
247     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
248 
249     glw::GLint drawcall_count       = 0;
250     glw::GLenum drawcall_mode       = GL_NONE;
251     glw::GLint drawcall_n_instances = 1;
252     glw::GLint n_patch_vertices     = 0;
253     glw::GLenum tf_mode             = GL_NONE;
254 
255     getDrawCallArgs(&drawcall_mode, &drawcall_count, &tf_mode, &n_patch_vertices, &drawcall_n_instances);
256 
257     gl.enable(GL_RASTERIZER_DISCARD);
258     GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed");
259 
260     gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, n_patch_vertices);
261     GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed");
262 
263     gl.useProgram(m_po_id);
264     GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed");
265 
266     gl.beginTransformFeedback(tf_mode);
267     GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() failed");
268     {
269         if (drawcall_n_instances == 1)
270         {
271             gl.drawArrays(drawcall_mode, 0 /* first */, drawcall_count);
272         }
273         else
274         {
275             DE_ASSERT(drawcall_n_instances > 1);
276 
277             gl.drawArraysInstanced(drawcall_mode, 0 /* first */, drawcall_count, drawcall_n_instances);
278         }
279         GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() or glDrawArraysInstanced() failed");
280     }
281     gl.endTransformFeedback();
282     GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() failed");
283 
284     /* Retrieve the data generated by XFB */
285     int bo_size          = getXFBBufferSize();
286     const void *xfb_data = NULL;
287 
288     xfb_data = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* offset */, bo_size, GL_MAP_READ_BIT);
289 
290     GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() failed");
291 
292     /* Verify the data */
293     bool is_xfb_data_valid = verifyXFBBuffer(xfb_data);
294 
295     /* Unmap the buffer object */
296     gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
297 
298     GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() failed");
299 
300     /* Set the test result, depending on the verification outcome */
301     if (is_xfb_data_valid)
302     {
303         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
304     }
305     else
306     {
307         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
308     }
309 
310     return STOP;
311 }
312 
313 /** Constructor
314  *
315  * @param context Test context
316  **/
TessellationShaderBarrier1(Context & context,const ExtParameters & extParams)317 TessellationShaderBarrier1::TessellationShaderBarrier1(Context &context, const ExtParameters &extParams)
318     : TessellationShaderBarrierTestCase(context, extParams, "barrier_guarded_read_calls",
319                                         "Verifies invocation A can correctly read a per-vertex and"
320                                         " per-patch attribute modified by invocation B after a barrier() call")
321     , m_n_result_vertices(2048)
322 {
323     m_n_input_vertices = (m_n_result_vertices / 2 /* result points per isoline */) * 4 /* vertices per patch */;
324 }
325 
326 /** Retrieves arguments to be used for the rendering process.
327  *
328  *  @param out_mode             Deref will be used to store draw call mode. Must not be NULL.
329  *  @param out_count            Deref will be used to store count argument to be used for the draw call.
330  *                              Must not be NULL.
331  *  @param out_tf_mode          Deref will be used to store transform feed-back mode to be used for
332  *                              glBeginTransformFeedback() call, prior to issuing the draw call. Must
333  *                              not be NULL.
334  *  @param out_n_patch_vertices Deref will be used to store GL_PATCH_VERTICES_EXT pname value, to be set with
335  *                              glPatchParameteriEXT() call prior to issuing the draw call. Must not be NULL.
336  *  @param out_n_instances      Deref will be used to store amount of instances to use for the draw call.
337  *                              Using a value of 1 will result in a glDrawArrays() call. Using values larger
338  *                              than 1 will trigger glDrawArraysInstanced() call. Values smaller than 1 are
339  *                              forbidden.
340  **/
getDrawCallArgs(glw::GLenum * out_mode,glw::GLint * out_count,glw::GLenum * out_tf_mode,glw::GLint * out_n_patch_vertices,glw::GLint * out_n_instances)341 void TessellationShaderBarrier1::getDrawCallArgs(glw::GLenum *out_mode, glw::GLint *out_count, glw::GLenum *out_tf_mode,
342                                                  glw::GLint *out_n_patch_vertices, glw::GLint *out_n_instances)
343 {
344     *out_count            = m_n_input_vertices;
345     *out_mode             = GL_PATCHES_EXT;
346     *out_n_instances      = 1;
347     *out_n_patch_vertices = 4;
348     *out_tf_mode          = GL_POINTS;
349 }
350 
351 /** Retrieves tessellation control shader body.
352  *
353  *  @return TC stage shader body.
354  **/
getTCSCode()355 const char *TessellationShaderBarrier1::getTCSCode()
356 {
357     static const char *tcs_code =
358         "${VERSION}\n"
359         "\n"
360         "${TESSELLATION_SHADER_REQUIRE}\n"
361         "\n"
362         "layout (vertices = 4) out;\n"
363         "\n"
364         "flat  in  int   vertex_id[];\n"
365         "\n"
366         "patch out int   test_patch_value;\n"
367         "      out ivec4 test_vector [];\n"
368         "      out ivec4 test_vector2[];\n"
369         "\n"
370         "void main()\n"
371         "{\n"
372         "    if (gl_InvocationID == 0)\n"
373         "    {\n"
374         "        test_patch_value = vertex_id[0];\n"
375         "    }\n"
376         "\n"
377         "    test_vector[gl_InvocationID] = ivec4(vertex_id[gl_InvocationID],\n"
378         "                                         vertex_id[gl_InvocationID] + 1,\n"
379         "                                         vertex_id[gl_InvocationID] + 2,\n"
380         "                                         vertex_id[gl_InvocationID] + 3);\n"
381         "\n"
382         "    barrier();\n"
383         "\n"
384         "    int next_invocation = (gl_InvocationID + 1) % 4;\n"
385         "\n"
386         "    test_vector2[gl_InvocationID] = ivec4(test_vector[next_invocation].xyz, test_patch_value);\n"
387         "\n"
388         "    gl_TessLevelOuter[0] = 1.0;\n"
389         "    gl_TessLevelOuter[1] = 1.0;\n"
390         "}\n";
391 
392     return tcs_code;
393 }
394 
395 /** Retrieves tessellation evaluation shader body.
396  *
397  *  @return TE stage shader body.
398  **/
getTESCode()399 const char *TessellationShaderBarrier1::getTESCode()
400 {
401     static const char *tes_code = "${VERSION}\n"
402                                   "\n"
403                                   "${TESSELLATION_SHADER_REQUIRE}\n"
404                                   "\n"
405                                   "layout(isolines, point_mode) in;\n"
406                                   "\n"
407                                   "in ivec4 test_vector2[];\n"
408                                   "\n"
409                                   "out ivec4 result_vector2;\n"
410                                   "\n"
411                                   "void main()\n"
412                                   "{\n"
413                                   "    int base = gl_PrimitiveID * 4;\n"
414                                   "\n"
415                                   "    if (test_vector2[0].x == (base + 1) && test_vector2[0].y == (base + 2) && "
416                                   "test_vector2[0].z == (base + 3) && test_vector2[0].w == (base + 0)  &&\n"
417                                   "        test_vector2[1].x == (base + 2) && test_vector2[1].y == (base + 3) && "
418                                   "test_vector2[1].z == (base + 4) && test_vector2[1].w == (base + 0)  &&\n"
419                                   "        test_vector2[2].x == (base + 3) && test_vector2[2].y == (base + 4) && "
420                                   "test_vector2[2].z == (base + 5) && test_vector2[2].w == (base + 0)  &&\n"
421                                   "        test_vector2[3].x == (base + 0) && test_vector2[3].y == (base + 1) && "
422                                   "test_vector2[3].z == (base + 2) && test_vector2[3].w == (base + 0) )\n"
423                                   "    {\n"
424                                   "        result_vector2 = ivec4(1);\n"
425                                   "    }\n"
426                                   "    else\n"
427                                   "    {\n"
428                                   "        result_vector2 = ivec4(0);\n"
429                                   "    }\n"
430                                   "}\n";
431 
432     return tes_code;
433 }
434 
435 /** Retrieves vertex shader body.
436  *
437  *  @return Vertex shader body.
438  **/
getVSCode()439 const char *TessellationShaderBarrier1::getVSCode()
440 {
441     static const char *vs_code = "${VERSION}\n"
442                                  "\n"
443                                  "flat out int vertex_id;\n"
444                                  "\n"
445                                  "void main()\n"
446                                  "{\n"
447                                  "    vertex_id = gl_VertexID;\n"
448                                  "}\n";
449 
450     return vs_code;
451 }
452 
453 /** Retrieves amount of bytes that should be used for allocating storage space for
454  *  a buffer object that will later be used to hold XFB result data.
455  *
456  *  @return Amount of bytes required by the test.
457  */
getXFBBufferSize()458 int TessellationShaderBarrier1::getXFBBufferSize()
459 {
460     return static_cast<int>(m_n_result_vertices * sizeof(int) * 4 /* components */ * 2 /* points per isoline */);
461 }
462 
463 /** Retrieves names of transform feedback varyings and amount of those. These should be used
464  *  prior to link the test program object.
465  *
466  *  @param out_n_names Deref will be used to store the number of names that @param out_names array
467  *                     holds. Must not be NULL.
468  *  @param out_names   Deref will be used to store a pointer to an array holding TF varying names;
469  *                     Must not be NULL.
470  **/
getXFBProperties(int * out_n_names,const char *** out_names)471 void TessellationShaderBarrier1::getXFBProperties(int *out_n_names, const char ***out_names)
472 {
473     static const char *names[1] = {"result_vector2"};
474 
475     *out_n_names = 1;
476     *out_names   = names;
477 }
478 
479 /** Verifies data captured by XFB is correct.
480  *
481  *  @param data Buffer holding the result XFB data. Must not be NULL.
482  *
483  *  @return true if the result data is confirmed to be valid, false otherwise.
484  **/
verifyXFBBuffer(const void * data)485 bool TessellationShaderBarrier1::verifyXFBBuffer(const void *data)
486 {
487     int *data_int = (int *)data;
488 
489     /* Run through all vertices */
490     for (unsigned int n_vertex = 0; n_vertex < m_n_result_vertices; ++n_vertex)
491     {
492         int retrieved_x = data_int[n_vertex * 4];
493         int retrieved_y = data_int[n_vertex * 4 + 1];
494         int retrieved_z = data_int[n_vertex * 4 + 2];
495         int retrieved_w = data_int[n_vertex * 4 + 3];
496 
497         if (retrieved_x != 1 || retrieved_y != 1 || retrieved_z != 1 || retrieved_w != 1)
498         {
499             m_testCtx.getLog() << tcu::TestLog::Message << "Invalid value returned: expected:[1, 1, 1, 1]"
500                                << " retrieved: "
501                                << "[" << retrieved_x << ", " << retrieved_y << ", " << retrieved_z << ", "
502                                << retrieved_w << tcu::TestLog::EndMessage;
503 
504             TCU_FAIL("Invalid rendering result");
505         }
506     }
507 
508     return true;
509 }
510 
511 /** Constructor
512  *
513  * @param context Test context
514  **/
TessellationShaderBarrier2(Context & context,const ExtParameters & extParams)515 TessellationShaderBarrier2::TessellationShaderBarrier2(Context &context, const ExtParameters &extParams)
516     : TessellationShaderBarrierTestCase(context, extParams, "barrier_guarded_write_calls",
517                                         "Verifies it is safe to write to the same per-patch output "
518                                         "from multiple TC invocations, as long as each write happens "
519                                         "in a separate phase.")
520     , m_n_result_vertices(2048)
521 {
522     m_n_input_vertices = (m_n_result_vertices / 2 /* result points per isoline */) * 4 /* vertices per patch */;
523 }
524 
525 /** Retrieves arguments to be used for the rendering process.
526  *
527  *  @param out_mode             Deref will be used to store draw call mode. Must not be NULL.
528  *  @param out_count            Deref will be used to store count argument to be used for the draw call.
529  *                              Must not be NULL.
530  *  @param out_tf_mode          Deref will be used to store transform feed-back mode to be used for
531  *                              glBeginTransformFeedback() call, prior to issuing the draw call. Must
532  *                              not be NULL.
533  *  @param out_n_patch_vertices Deref will be used to store GL_PATCH_VERTICES_EXT pname value, to be set with
534  *                              glPatchParameteriEXT() call prior to issuing the draw call. Must not be NULL.
535  *  @param out_n_instances      Deref will be used to store amount of instances to use for the draw call.
536  *                              Using a value of 1 will result in a glDrawArrays() call. Using values larger
537  *                              than 1 will trigger glDrawArraysInstanced() call. Values smaller than 1 are
538  *                              forbidden.
539  **/
getDrawCallArgs(glw::GLenum * out_mode,glw::GLint * out_count,glw::GLenum * out_tf_mode,glw::GLint * out_n_patch_vertices,glw::GLint * out_n_instances)540 void TessellationShaderBarrier2::getDrawCallArgs(glw::GLenum *out_mode, glw::GLint *out_count, glw::GLenum *out_tf_mode,
541                                                  glw::GLint *out_n_patch_vertices, glw::GLint *out_n_instances)
542 {
543     *out_count            = m_n_input_vertices;
544     *out_mode             = GL_PATCHES_EXT;
545     *out_n_instances      = 1;
546     *out_n_patch_vertices = 4;
547     *out_tf_mode          = GL_POINTS;
548 }
549 
550 /** Retrieves tessellation control shader body.
551  *
552  *  @return TC stage shader body.
553  **/
getTCSCode()554 const char *TessellationShaderBarrier2::getTCSCode()
555 {
556     static const char *tcs_code = "${VERSION}\n"
557                                   "\n"
558                                   "${TESSELLATION_SHADER_REQUIRE}\n"
559                                   "\n"
560                                   "layout (vertices = 4) out;\n"
561                                   "\n"
562                                   "      out float tcs_result[];\n"
563                                   "patch out int   test_patch_value;\n"
564                                   "\n"
565                                   "void main()\n"
566                                   "{\n"
567                                   /* Four invocations per input patch will be executed. Have the first one write its
568                                    * secret value to the output per-patch attribute.
569                                    */
570                                   "    if (gl_InvocationID == 0)\n"
571                                   "    {\n"
572                                   "        test_patch_value = 123;\n"
573                                   "    }\n"
574                                   "\n"
575                                   "    barrier();\n"
576                                   "\n"
577                                   /* Let's have the second invocation update the value at this point */
578                                   "    if (gl_InvocationID == 1)\n"
579                                   "    {\n"
580                                   "        test_patch_value = 234;\n"
581                                   "    }\n"
582                                   "\n"
583                                   "    barrier();\n"
584                                   "\n"
585                                   /* Finally, if this is invocation number one, check if test_patch_value
586                                    * stores a correct value.
587                                    */
588                                   "    tcs_result[gl_InvocationID] = 2.0;\n"
589                                   "\n"
590                                   "    if (gl_InvocationID == 0)\n"
591                                   "    {\n"
592                                   "        if (test_patch_value == 234)\n"
593                                   "        {\n"
594                                   "            tcs_result[gl_InvocationID] = 1.0;\n"
595                                   "        }\n"
596                                   "    }\n"
597                                   "\n"
598                                   "    gl_TessLevelOuter[0] = 1.0;\n"
599                                   "    gl_TessLevelOuter[1] = 1.0;\n"
600                                   "}\n";
601 
602     return tcs_code;
603 }
604 
605 /** Retrieves tessellation evaluation shader body.
606  *
607  *  @return TE stage shader body.
608  **/
getTESCode()609 const char *TessellationShaderBarrier2::getTESCode()
610 {
611     static const char *tes_code = "${VERSION}\n"
612                                   "\n"
613                                   "${TESSELLATION_SHADER_REQUIRE}\n"
614                                   "\n"
615                                   "layout(isolines, point_mode) in;\n"
616                                   "\n"
617                                   "      in float tcs_result[];\n"
618                                   "\n"
619                                   "out float tes_result;\n"
620                                   "\n"
621                                   "void main()\n"
622                                   "{\n"
623                                   /* TCS invocation order is undefined so take a maximum of all values we received */
624                                   "    if (tcs_result[0] == 1.0 &&\n"
625                                   "        tcs_result[1] == 2.0 &&\n"
626                                   "        tcs_result[2] == 2.0 &&\n"
627                                   "        tcs_result[3] == 2.0)\n"
628                                   "    {\n"
629                                   "        tes_result = 1.0;\n"
630                                   "    }\n"
631                                   "    else\n"
632                                   "    {\n"
633                                   "        tes_result = 0.0;\n"
634                                   "    }\n"
635                                   "}\n";
636 
637     return tes_code;
638 }
639 
640 /** Retrieves vertex shader body.
641  *
642  *  @return Vertex shader body.
643  **/
getVSCode()644 const char *TessellationShaderBarrier2::getVSCode()
645 {
646     static const char *vs_code = "${VERSION}\n"
647                                  "\n"
648                                  "void main()\n"
649                                  "{\n"
650                                  "}\n";
651 
652     return vs_code;
653 }
654 
655 /** Retrieves amount of bytes that should be used for allocating storage space for
656  *  a buffer object that will later be used to hold XFB result data.
657  *
658  *  @return Amount of bytes required by the test.
659  */
getXFBBufferSize()660 int TessellationShaderBarrier2::getXFBBufferSize()
661 {
662     return static_cast<int>(m_n_result_vertices * sizeof(float) * 2 /* points per isoline */);
663 }
664 
665 /** Retrieves names of transform feedback varyings and amount of those. These should be used
666  *  prior to link the test program object.
667  *
668  *  @param out_n_names Deref will be used to store the number of names that @param out_names array
669  *                     holds. Must not be NULL.
670  *  @param out_names   Deref will be used to store a pointer to an array holding TF varying names;
671  *                     Must not be NULL.
672  **/
getXFBProperties(int * out_n_names,const char *** out_names)673 void TessellationShaderBarrier2::getXFBProperties(int *out_n_names, const char ***out_names)
674 {
675     static const char *names[1] = {"tes_result"};
676 
677     *out_n_names = 1;
678     *out_names   = names;
679 }
680 
681 /** Verifies data captured by XFB is correct.
682  *
683  *  @param data Buffer holding the result XFB data. Must not be NULL.
684  *
685  *  @return true if the result data is confirmed to be valid, false otherwise.
686  **/
verifyXFBBuffer(const void * data)687 bool TessellationShaderBarrier2::verifyXFBBuffer(const void *data)
688 {
689     float *data_float   = (float *)data;
690     const float epsilon = (float)1e-5;
691 
692     /* Run through all vertices */
693     for (unsigned int n_vertex = 0; n_vertex < m_n_result_vertices; ++n_vertex)
694     {
695         if (de::abs(data_float[n_vertex] - 1.0f /* valid result value */) > epsilon)
696         {
697             TCU_FAIL("Invalid data retrieved");
698         }
699     }
700 
701     return true;
702 }
703 
704 /** Constructor
705  *
706  * @param context Test context
707  **/
TessellationShaderBarrier3(Context & context,const ExtParameters & extParams)708 TessellationShaderBarrier3::TessellationShaderBarrier3(Context &context, const ExtParameters &extParams)
709     : TessellationShaderBarrierTestCase(context, extParams, "barrier_guarded_read_write_calls",
710                                         "Verifies it is safe to write to the same per-patch output "
711                                         "from multiple TC invocations, as long as each write happens "
712                                         "in a separate phase.")
713     , m_n_instances(10)             /* as per test spec */
714     , m_n_invocations(16)           /* as per test spec */
715     , m_n_patch_vertices(8)         /* as per test spec */
716     , m_n_patches_per_invocation(2) /* as per test spec */
717     , m_n_result_vertices(2 /* result points per isoline */ * m_n_patches_per_invocation * m_n_invocations *
718                           m_n_instances)
719 {
720     m_n_input_vertices = (m_n_result_vertices / 2 /* result points per isoline */) * 4 /* vertices per patch */;
721 }
722 
723 /** Retrieves arguments to be used for the rendering process.
724  *
725  *  @param out_mode             Deref will be used to store draw call mode. Must not be NULL.
726  *  @param out_count            Deref will be used to store count argument to be used for the draw call.
727  *                              Must not be NULL.
728  *  @param out_tf_mode          Deref will be used to store transform feed-back mode to be used for
729  *                              glBeginTransformFeedback() call, prior to issuing the draw call. Must
730  *                              not be NULL.
731  *  @param out_n_patch_vertices Deref will be used to store GL_PATCH_VERTICES_EXT pname value, to be set with
732  *                              glPatchParameteriEXT() call prior to issuing the draw call. Must not be NULL.
733  *  @param out_n_instances      Deref will be used to store amount of instances to use for the draw call.
734  *                              Using a value of 1 will result in a glDrawArrays() call. Using values larger
735  *                              than 1 will trigger glDrawArraysInstanced() call. Values smaller than 1 are
736  *                              forbidden.
737  **/
getDrawCallArgs(glw::GLenum * out_mode,glw::GLint * out_count,glw::GLenum * out_tf_mode,glw::GLint * out_n_patch_vertices,glw::GLint * out_n_instances)738 void TessellationShaderBarrier3::getDrawCallArgs(glw::GLenum *out_mode, glw::GLint *out_count, glw::GLenum *out_tf_mode,
739                                                  glw::GLint *out_n_patch_vertices, glw::GLint *out_n_instances)
740 {
741     *out_count            = m_n_patches_per_invocation * m_n_patch_vertices * m_n_instances;
742     *out_mode             = GL_PATCHES_EXT;
743     *out_n_instances      = m_n_instances;
744     *out_n_patch_vertices = m_n_patch_vertices;
745     *out_tf_mode          = GL_POINTS;
746 }
747 
748 /** Retrieves tessellation control shader body.
749  *
750  *  @return TC stage shader body.
751  **/
getTCSCode()752 const char *TessellationShaderBarrier3::getTCSCode()
753 {
754     static const char *tcs_code =
755         "${VERSION}\n"
756         "\n"
757         "${TESSELLATION_SHADER_REQUIRE}\n"
758         "\n"
759         "layout (vertices = 16) out;\n"
760         "\n"
761         "      out int tcs_data        [];\n"
762         "patch out int tcs_patch_result[16];\n"
763         "\n"
764         "void main()\n"
765         "{\n"
766         /* Even invocations should write their gl_InvocationID value to their per-vertex output. */
767         "    if ((gl_InvocationID % 2) == 0)\n"
768         "    {\n"
769         "        tcs_data[gl_InvocationID] = gl_InvocationID;\n"
770         "    }\n"
771         "\n"
772         "    barrier();\n"
773         "\n"
774         /* Odd invocations should read values stored by preceding even invocation,
775          * add current invocation's ID to that value, and then write it to its per-vertex
776          * output.
777          */
778         "    if ((gl_InvocationID % 2) == 1)\n"
779         "    {\n"
780         "        tcs_data[gl_InvocationID] = tcs_data[gl_InvocationID - 1] + gl_InvocationID;\n"
781         "    }\n"
782         "\n"
783         "    barrier();\n"
784         "\n"
785         /* Every fourth invocation should now read & sum up per-vertex outputs for four invocations
786          * following it (including the one discussed), and store it in a per-patch variable */
787         "    tcs_patch_result[gl_InvocationID] = 0;\n"
788         "\n"
789         "    if ((gl_InvocationID % 4) == 0)\n"
790         "    {\n"
791         "        tcs_patch_result[gl_InvocationID] += tcs_data[gl_InvocationID];\n"
792         "        tcs_patch_result[gl_InvocationID] += tcs_data[gl_InvocationID+1];\n"
793         "        tcs_patch_result[gl_InvocationID] += tcs_data[gl_InvocationID+2];\n"
794         "        tcs_patch_result[gl_InvocationID] += tcs_data[gl_InvocationID+3];\n"
795         "    }\n"
796         "\n"
797         "    gl_TessLevelOuter[0] = 1.0;\n"
798         "    gl_TessLevelOuter[1] = 1.0;\n"
799         "}\n";
800 
801     return tcs_code;
802 }
803 
804 /** Retrieves tessellation evaluation shader body.
805  *
806  *  @return TC stage shader body.
807  **/
getTESCode()808 const char *TessellationShaderBarrier3::getTESCode()
809 {
810     static const char *tes_code = "${VERSION}\n"
811                                   "\n"
812                                   "${TESSELLATION_SHADER_REQUIRE}\n"
813                                   "\n"
814                                   "layout(isolines, point_mode) in;\n"
815                                   "\n"
816                                   "patch in int tcs_patch_result[16];\n"
817                                   "\n"
818                                   "flat out ivec4 tes_result1;\n"
819                                   "flat out ivec4 tes_result2;\n"
820                                   "flat out ivec4 tes_result3;\n"
821                                   "flat out ivec4 tes_result4;\n"
822                                   "\n"
823                                   "void main()\n"
824                                   "{\n"
825                                   "    tes_result1 = ivec4(tcs_patch_result[0],  tcs_patch_result[1],  "
826                                   "tcs_patch_result[2],  tcs_patch_result[3]);\n"
827                                   "    tes_result2 = ivec4(tcs_patch_result[4],  tcs_patch_result[5],  "
828                                   "tcs_patch_result[6],  tcs_patch_result[7]);\n"
829                                   "    tes_result3 = ivec4(tcs_patch_result[8],  tcs_patch_result[9],  "
830                                   "tcs_patch_result[10], tcs_patch_result[11]);\n"
831                                   "    tes_result4 = ivec4(tcs_patch_result[12], tcs_patch_result[13], "
832                                   "tcs_patch_result[14], tcs_patch_result[15]);\n"
833                                   "}\n";
834 
835     return tes_code;
836 }
837 
838 /** Retrieves vertex shader body.
839  *
840  *  @return Vertex shader body.
841  **/
getVSCode()842 const char *TessellationShaderBarrier3::getVSCode()
843 {
844     static const char *vs_code = "${VERSION}\n"
845                                  "\n"
846                                  "void main()\n"
847                                  "{\n"
848                                  "}\n";
849 
850     return vs_code;
851 }
852 
853 /** Retrieves amount of bytes that should be used for allocating storage space for
854  *  a buffer object that will later be used to hold XFB result data.
855  *
856  *  @return Amount of bytes required by the test.
857  */
getXFBBufferSize()858 int TessellationShaderBarrier3::getXFBBufferSize()
859 {
860     return static_cast<int>(m_n_instances * m_n_result_vertices * sizeof(int) * 4 /* ivec4 */ *
861                             2 /* points per isoline */);
862 }
863 
864 /** Retrieves names of transform feedback varyings and amount of those. These should be used
865  *  prior to link the test program object.
866  *
867  *  @param out_n_names Deref will be used to store the number of names that @param out_names array
868  *                     holds. Must not be NULL.
869  *  @param out_names   Deref will be used to store a pointer to an array holding TF varying names;
870  *                     Must not be NULL.
871  **/
getXFBProperties(int * out_n_names,const char *** out_names)872 void TessellationShaderBarrier3::getXFBProperties(int *out_n_names, const char ***out_names)
873 {
874     static const char *names[] = {"tes_result1", "tes_result2", "tes_result3", "tes_result4"};
875 
876     *out_n_names = 4;
877     *out_names   = names;
878 }
879 
880 /** Verifies data captured by XFB is correct.
881  *
882  *  @param data Buffer holding the result XFB data. Must not be NULL.
883  *
884  *  @return true if the result data is confirmed to be valid, false otherwise.
885  **/
verifyXFBBuffer(const void * data)886 bool TessellationShaderBarrier3::verifyXFBBuffer(const void *data)
887 {
888     const int *data_int = (const int *)data;
889     std::vector<int> tcs_data(m_n_invocations, 0);
890     std::vector<int> tcs_patch_result(m_n_invocations, 0);
891 
892     /* This is a simple C++ port of the TCS used for the test.
893      *
894      * Note: We only need to consider a single set of values stored by TES
895      *       for a single result point, as the same set of values will be
896      *       reported for the other point. Owing to the fact gl_InvocationID
897      *       in TCS will iterate from 0 to 15 for all input patches and instances,
898      *       we can re-use the data for all subsequent input patches. */
899     /* Phase 1 */
900     for (unsigned int n = 0; n < m_n_invocations; n += 2)
901     {
902         tcs_data[n] = n;
903     }
904 
905     /* Phase 2 */
906     for (unsigned int n = 1; n < m_n_invocations; n += 2)
907     {
908         tcs_data[n] = tcs_data[n - 1] + n;
909     }
910 
911     /* Phase 3 */
912     for (unsigned int n_patch_vertex = 0; n_patch_vertex < m_n_invocations; ++n_patch_vertex)
913     {
914         const unsigned int invocation_id = n_patch_vertex;
915 
916         tcs_patch_result[invocation_id] = 0;
917 
918         if ((invocation_id % 4) == 0)
919         {
920             tcs_patch_result[invocation_id] += tcs_data[invocation_id];
921             tcs_patch_result[invocation_id] += tcs_data[invocation_id + 1];
922             tcs_patch_result[invocation_id] += tcs_data[invocation_id + 2];
923             tcs_patch_result[invocation_id] += tcs_data[invocation_id + 3];
924         }
925     } /* for (all patch vertices) */
926 
927     /* Time to do the actual comparison. */
928     for (unsigned int n_patch = 0; n_patch < m_n_result_vertices / m_n_invocations; ++n_patch)
929     {
930         bool are_equal                      = true;
931         const int n_points_per_line_segment = 2;
932         const int *patch_data_int           = data_int + n_patch * m_n_invocations * n_points_per_line_segment;
933 
934         for (unsigned int n_invocation = 0; n_invocation < m_n_invocations; ++n_invocation)
935         {
936             if (patch_data_int[n_invocation] != tcs_patch_result[n_invocation])
937             {
938                 are_equal = false;
939 
940                 break;
941             }
942         } /* for (all patch vertices which have contributed for given input patch being considered) */
943 
944         if (!are_equal)
945         {
946             std::stringstream logMessage;
947 
948             logMessage << "Result data for patch [" << n_patch << "]: (";
949 
950             for (unsigned int n_patch_vertex = 0; n_patch_vertex < m_n_patch_vertices; ++n_patch_vertex)
951             {
952                 logMessage << patch_data_int[n_patch_vertex];
953 
954                 if (n_patch_vertex == (m_n_patch_vertices - 1))
955                 {
956                     logMessage << "), ";
957                 }
958                 else
959                 {
960                     logMessage << ", ";
961                 }
962             } /* for (all patch vertices) */
963 
964             logMessage << "expected: ";
965 
966             for (unsigned int n_patch_vertex = 0; n_patch_vertex < m_n_patch_vertices; ++n_patch_vertex)
967             {
968                 logMessage << tcs_patch_result[n_patch_vertex];
969 
970                 if (n_patch_vertex == (m_n_patch_vertices - 1))
971                 {
972                     logMessage << "). ";
973                 }
974                 else
975                 {
976                     logMessage << ", ";
977                 }
978             } /* for (all patch vertices) */
979 
980             /* Log the message */
981             m_testCtx.getLog() << tcu::TestLog::Message << logMessage.str().c_str() << tcu::TestLog::EndMessage;
982 
983             /* Bail out */
984             TCU_FAIL("Invalid data captured");
985         } /* if (!are_equal) */
986     }     /* for (all patches) */
987 
988     return true;
989 }
990 
991 } /* namespace glcts */
992