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