xref: /aosp_15_r20/external/deqp/external/openglcts/modules/gl/gl4cTextureBarrierTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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 /**
25  * \file  gl4cTextureBarrierTests.cpp
26  * \brief Implements conformance tests for "Texture Barrier" functionality.
27  */ /*-------------------------------------------------------------------*/
28 
29 #include "gl4cTextureBarrierTests.hpp"
30 
31 #include "deMath.h"
32 #include "deSharedPtr.hpp"
33 
34 #include "gluContextInfo.hpp"
35 #include "gluDefs.hpp"
36 #include "gluPixelTransfer.hpp"
37 #include "gluShaderProgram.hpp"
38 
39 #include "tcuFuzzyImageCompare.hpp"
40 #include "tcuImageCompare.hpp"
41 #include "tcuRenderTarget.hpp"
42 #include "tcuSurface.hpp"
43 #include "tcuTestLog.hpp"
44 
45 #include "glw.h"
46 #include "glwFunctions.hpp"
47 
48 #include "glcWaiver.hpp"
49 
50 namespace gl4cts
51 {
52 
53 /*
54  Base class of all test cases of the feature. Enforces the requirements below:
55 
56  * Check that the extension string or GL 4.5 is available.
57  */
58 class TextureBarrierBaseTest : public deqp::TestCase
59 {
60 protected:
TextureBarrierBaseTest(deqp::Context & context,TextureBarrierTests::API api,const char * name,const char * description)61     TextureBarrierBaseTest(deqp::Context &context, TextureBarrierTests::API api, const char *name,
62                            const char *description)
63         : TestCase(context, name, description)
64         , m_api(api)
65     {
66     }
67 
68     /* Checks whether the feature is supported. */
featureSupported()69     bool featureSupported()
70     {
71         return (m_api == TextureBarrierTests::API_GL_ARB_texture_barrier &&
72                 m_context.getContextInfo().isExtensionSupported("GL_ARB_texture_barrier")) ||
73                m_api == TextureBarrierTests::API_GL_45core;
74     }
75 
76     /* Basic test init, child classes must call it. */
init()77     virtual void init()
78     {
79         if (!featureSupported())
80         {
81             throw tcu::NotSupportedError("Required texture_barrier extension is not supported");
82         }
83     }
84 
85 protected:
86     const TextureBarrierTests::API m_api;
87 };
88 
89 /*
90  Base class of all rendering test cases of the feature. Implements the basic outline below:
91 
92  This basic outline provides a simple tutorial on how to implement and
93  what to check in the test cases of this feature.
94 
95  * Create a set of color textures and fill each of their texels with unique
96  values using an arbitrary method. Set the minification and magnification
97  filtering modes of the textures to NEAREST. Bind all of them for
98  texturing to subsequent texture units starting from texture unit zero.
99 
100  * Create a framebuffer object and attach the set of textures so that
101  texture #i is attached as color attachment #i. Set the draw buffers so
102  that draw buffer #i is set to color attachment #i. Bind the framebuffer
103  for rendering.
104 
105  * Render a set of primitives that cover each pixel of the framebuffer
106  exactly once using the fragment shader described in the particular
107  test case.
108 
109  * Expect all texels written by the draw command to have well defined
110  values in accordance with the used fragment shader's functionality.
111  */
112 class TextureBarrierBasicOutline : public TextureBarrierBaseTest
113 {
114 protected:
TextureBarrierBasicOutline(deqp::Context & context,TextureBarrierTests::API api,const char * name,const char * description)115     TextureBarrierBasicOutline(deqp::Context &context, TextureBarrierTests::API api, const char *name,
116                                const char *description)
117         : TextureBarrierBaseTest(context, api, name, description)
118         , m_program(0)
119         , m_vao(0)
120         , m_vbo(0)
121         , m_fbo(0)
122         , m_width(0)
123         , m_height(0)
124         , m_actual(DE_NULL)
125     {
126         for (size_t i = 0; i < NUM_TEXTURES; ++i)
127         {
128             m_tex[i]       = 0;
129             m_reference[i] = DE_NULL;
130         }
131     }
132 
133     /* Actual test cases may override it to provide an alternative vertex shader. */
vsh()134     virtual const char *vsh()
135     {
136         return "#version 400 core\n"
137                "in vec2 Position;\n"
138                "void main() {\n"
139                "    gl_Position = vec4(Position, 0.0, 1.0);\n"
140                "}";
141     }
142 
143     /* Actual test cases must override it to provide the fragment shader. */
144     virtual const char *fsh() = 0;
145 
146     /* Rendering test init. */
init()147     virtual void init()
148     {
149         // Call parent init (takes care of API requirements)
150         TextureBarrierBaseTest::init();
151 
152         const tcu::RenderTarget &renderTarget = m_context.getRenderContext().getRenderTarget();
153         m_width                               = renderTarget.getWidth();
154         m_height                              = renderTarget.getHeight();
155 
156 #ifdef WAIVER_WITH_BUG_13788
157         m_width = m_width >= 16383 ? 16382 : m_width;
158 #endif
159 
160         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
161 
162         // Create textures
163         gl.genTextures(NUM_TEXTURES, m_tex);
164         for (GLuint i = 0; i < NUM_TEXTURES; ++i)
165         {
166             gl.activeTexture(GL_TEXTURE0 + i);
167             gl.bindTexture(GL_TEXTURE_2D, m_tex[i]);
168             gl.texStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, m_width, m_height);
169             gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
170             gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
171             m_reference[i] = new GLuint[m_width * m_height];
172         }
173         m_actual = new GLuint[m_width * m_height];
174 
175         // Create framebuffer
176         gl.genFramebuffers(1, &m_fbo);
177         gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
178         GLenum drawBuffers[NUM_TEXTURES];
179         for (GLuint i = 0; i < NUM_TEXTURES; ++i)
180         {
181             gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, m_tex[i], 0);
182             drawBuffers[i] = GL_COLOR_ATTACHMENT0 + i;
183         }
184         gl.drawBuffers(NUM_TEXTURES, drawBuffers);
185 
186         // Create vertex array and buffer
187         gl.genVertexArrays(1, &m_vao);
188         gl.bindVertexArray(m_vao);
189 
190         gl.genBuffers(1, &m_vbo);
191         gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo);
192         gl.bufferData(GL_ARRAY_BUFFER, GRID_SIZE * GRID_SIZE * sizeof(float) * 12, NULL, GL_STATIC_DRAW);
193 
194         generateVertexData((float *)gl.mapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
195         gl.unmapBuffer(GL_ARRAY_BUFFER);
196 
197         gl.vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, NULL);
198         gl.enableVertexAttribArray(0);
199 
200         // Setup state
201         gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
202         gl.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
203     }
204 
205     /* Rendering test deinit. */
deinit()206     virtual void deinit()
207     {
208         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
209 
210         // Cleanup textures
211         gl.activeTexture(GL_TEXTURE0);
212         gl.deleteTextures(NUM_TEXTURES, m_tex);
213         for (GLuint i = 0; i < NUM_TEXTURES; ++i)
214         {
215             if (DE_NULL != m_reference[i])
216             {
217                 delete[] m_reference[i];
218                 m_reference[i] = DE_NULL;
219             }
220         }
221 
222         if (DE_NULL != m_actual)
223         {
224             delete[] m_actual;
225             m_actual = DE_NULL;
226         }
227 
228         // Cleanup framebuffer
229         gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
230         gl.deleteFramebuffers(1, &m_fbo);
231 
232         // Cleanup vertex array and buffer
233         gl.bindBuffer(GL_ARRAY_BUFFER, 0);
234         gl.bindVertexArray(0);
235         gl.deleteBuffers(1, &m_vbo);
236         gl.deleteVertexArrays(1, &m_vao);
237 
238         // Cleanup state
239         gl.pixelStorei(GL_PACK_ALIGNMENT, 4);
240         gl.pixelStorei(GL_UNPACK_ALIGNMENT, 4);
241     }
242 
243     /* Generate vertex data using the following steps:
244      (1) Generate an irregular grid covering the whole screen (i.e. (-1,-1) to (1,1));
245      (2) Generate a list of triangles covering the grid;
246      (3) Shuffle the generated triangle list;
247      (4) Write the vertices of the shuffled triangle list to the destination address. */
generateVertexData(float * destAddr)248     void generateVertexData(float *destAddr)
249     {
250         DE_ASSERT(destAddr != NULL);
251 
252         typedef struct
253         {
254             float x, y;
255         } Vertex;
256 
257         typedef struct
258         {
259             Vertex v0, v1, v2;
260         } Triangle;
261 
262         static Vertex grid[GRID_SIZE + 1][GRID_SIZE + 1];
263 
264         // Generate grid vertices
265         for (int x = 0; x < GRID_SIZE + 1; ++x)
266             for (int y = 0; y < GRID_SIZE + 1; ++y)
267             {
268                 // Calculate normalized coordinates
269                 float normx = (((float)x) / GRID_SIZE);
270                 float normy = (((float)y) / GRID_SIZE);
271 
272                 // Pseudo-random grid vertex coordinate with scale & bias
273                 grid[x][y].x = normx * 2.f - 1.f + deFloatSin(normx * DE_PI * 13.f) * 0.3f / GRID_SIZE;
274                 grid[x][y].y = normy * 2.f - 1.f + deFloatSin(normy * DE_PI * 13.f) * 0.3f / GRID_SIZE;
275             }
276 
277         Triangle list[TRIANGLE_COUNT];
278 
279         // Generate triangle list
280         for (int x = 0; x < GRID_SIZE; ++x)
281             for (int y = 0; y < GRID_SIZE; ++y)
282             {
283                 // Generate first triangle of grid block
284                 list[(x + y * GRID_SIZE) * 2 + 0].v0 = grid[x][y];
285                 list[(x + y * GRID_SIZE) * 2 + 0].v1 = grid[x + 1][y];
286                 list[(x + y * GRID_SIZE) * 2 + 0].v2 = grid[x + 1][y + 1];
287 
288                 // Generate second triangle of grid block
289                 list[(x + y * GRID_SIZE) * 2 + 1].v0 = grid[x + 1][y + 1];
290                 list[(x + y * GRID_SIZE) * 2 + 1].v1 = grid[x][y + 1];
291                 list[(x + y * GRID_SIZE) * 2 + 1].v2 = grid[x][y];
292             }
293 
294         // Shuffle triangle list
295         for (int i = TRIANGLE_COUNT - 2; i > 0; --i)
296         {
297             // Pseudo-random triangle index as one operand of the exchange
298             int j = (int)((list[i].v1.y + list[i].v2.x + 13.f) * 1345.13f) % i;
299 
300             Triangle xchg = list[j];
301             list[j]       = list[i];
302             list[i]       = xchg;
303         }
304 
305         // Write triange list vertices to destination address
306         for (int i = 0; i < TRIANGLE_COUNT; ++i)
307         {
308             // Write first vertex of triangle
309             destAddr[i * 6 + 0] = list[i].v0.x;
310             destAddr[i * 6 + 1] = list[i].v0.y;
311 
312             // Write second vertex of triangle
313             destAddr[i * 6 + 2] = list[i].v1.x;
314             destAddr[i * 6 + 3] = list[i].v1.y;
315 
316             // Write third vertex of triangle
317             destAddr[i * 6 + 4] = list[i].v2.x;
318             destAddr[i * 6 + 5] = list[i].v2.y;
319         }
320     }
321 
322     /* Renders a set of primitives that cover each pixel of the framebuffer exactly once. */
render()323     void render()
324     {
325         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
326 
327         // Issue the whole grid using multiple separate draw commands
328         int minTriCountPerDraw = TRIANGLE_COUNT / 7;
329         int first = 0, count = 0;
330         while (first < VERTEX_COUNT)
331         {
332             // Pseudo-random number of vertices per draw
333             count = deMin32(VERTEX_COUNT - first, (first % 23 + minTriCountPerDraw) * 3);
334 
335             gl.drawArrays(GL_TRIANGLES, first, count);
336 
337             first += count;
338         }
339     }
340 
341     /* Returns a reference to the texel value of the specified image at the specified location. */
texel(GLuint * image,GLuint x,GLuint y)342     GLuint &texel(GLuint *image, GLuint x, GLuint y)
343     {
344         // If out-of-bounds reads should return zero, writes should be ignored
345         if ((static_cast<GLint>(x) < 0) || (x >= m_width) || (static_cast<GLint>(y) < 0) || (y >= m_height))
346         {
347             static GLuint zero;
348             return (zero = 0);
349         }
350 
351         return image[x + y * m_width];
352     }
353 
354     /* Initializes the reference images and uploads them to their corresponding textures. */
initTextureData()355     void initTextureData()
356     {
357         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
358 
359         for (GLuint i = 0; i < NUM_TEXTURES; ++i)
360         {
361             for (GLuint x = 0; x < m_width; ++x)
362                 for (GLuint y = 0; y < m_height; ++y)
363                 {
364                     texel(m_reference[i], x, y) = (i << 24) + (y << 12) + x;
365                 }
366 
367             gl.activeTexture(GL_TEXTURE0 + i);
368             gl.texSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, GL_RED_INTEGER, GL_UNSIGNED_INT,
369                              m_reference[i]);
370         }
371     }
372 
373     /* Updates the reference images according to a single execution of the fragment shader for each pixel. */
374     virtual void updateTextureData() = 0;
375 
376     /* Verifies whether the reference images matches those of the textures we rendered to. */
verifyTextureData()377     bool verifyTextureData()
378     {
379         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
380 
381         for (GLuint i = 0; i < NUM_TEXTURES; ++i)
382         {
383             gl.activeTexture(GL_TEXTURE0 + i);
384             gl.getTexImage(GL_TEXTURE_2D, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, m_actual);
385 
386             for (GLuint x = 0; x < m_width; ++x)
387                 for (GLuint y = 0; y < m_height; ++y)
388                 {
389                     if (texel(m_reference[i], x, y) != texel(m_actual, x, y))
390                     {
391                         return false;
392                     }
393                 }
394         }
395 
396         return true;
397     }
398 
399     /* Should return the number of separate test passes. */
400     virtual int numTestPasses() = 0;
401 
402     /* Should return the number of rendering passes to perform. */
403     virtual int numRenderPasses() = 0;
404 
405     /* Should set up configuration for a particular render pass (e.g. setting uniforms). */
406     virtual void setupRenderPass(int testPass, int renderPass) = 0;
407 
408     /* Should return whether there is need for a TextureBarrier between subsequent render passes. */
409     virtual bool needsBarrier() = 0;
410 
411     /* Test case iterate function. Contains the actual test case logic. */
iterate()412     IterateResult iterate()
413     {
414         tcu::TestLog &log        = m_testCtx.getLog();
415         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
416 
417         // Compile & link the program to use
418         de::SharedPtr<glu::ShaderProgram> program(
419             new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vsh(), fsh())));
420         log << (*program);
421         if (!program->isOk())
422         {
423             TCU_FAIL("Program compilation failed");
424         }
425         m_program = program->getProgram();
426 
427         gl.useProgram(m_program);
428 
429         for (GLuint i = 0; i < NUM_TEXTURES; ++i)
430         {
431             GLchar samplerName[] = "texInput[0]";
432             samplerName[9]       = static_cast<GLchar>('0' + i);
433             GLint loc            = gl.getUniformLocation(m_program, samplerName);
434             gl.uniform1i(loc, i);
435         }
436 
437         for (int testPass = 0; testPass < numTestPasses(); ++testPass)
438         {
439             // Initialize texture data at the beginning of each test pass
440             initTextureData();
441 
442             // Perform rendering passes
443             for (int renderPass = 0; renderPass < numRenderPasses(); ++renderPass)
444             {
445                 // Setup render pass
446                 setupRenderPass(testPass, renderPass);
447 
448                 // Render a set of primitives that cover each pixel of the framebuffer exactly once
449                 render();
450 
451                 // If a TextureBarrier is needed insert it here
452                 if (needsBarrier())
453                     gl.textureBarrier();
454             }
455 
456             // Update reference data after actual rendering to avoid bubbles
457             for (int renderPass = 0; renderPass < numRenderPasses(); ++renderPass)
458             {
459                 // Setup render pass
460                 setupRenderPass(testPass, renderPass);
461 
462                 // Update reference data
463                 updateTextureData();
464             }
465 
466             // Verify results at the end of each test pass
467             if (!verifyTextureData())
468             {
469                 TCU_FAIL("Failed to validate rendering results");
470             }
471         }
472 
473         gl.useProgram(0);
474 
475         // Test case passed
476         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
477 
478         return STOP;
479     }
480 
481 protected:
482     enum
483     {
484         NUM_TEXTURES = 8,
485 
486         GRID_SIZE      = 64,
487         TRIANGLE_COUNT = GRID_SIZE * GRID_SIZE * 2,
488         VERTEX_COUNT   = TRIANGLE_COUNT * 3,
489     };
490 
491     GLuint m_program;
492     GLuint m_vao;
493     GLuint m_vbo;
494     GLuint m_fbo;
495     GLuint m_tex[NUM_TEXTURES];
496     GLuint m_width, m_height;
497     GLuint *m_reference[NUM_TEXTURES];
498     GLuint *m_actual;
499 };
500 
501 /*
502  Base class of the rendering tests which use a fragment shader performing
503  reads and writes from/to disjoint blocks of texels within a single rendering
504  pass. The skeleton of these tests is as follows:
505 
506  * Using the basic outline above test that reads and writes from/to
507  disjoint sets of texels work as expected. Use the following fragment
508  shader as a template:
509 
510  uniform int blockSize;
511  uniform int modulo;
512  uniform sampler2D texture[N];
513  out vec4 output[N];
514 
515  void main() {
516  ivec2 texelCoord = ivec2(gl_FragCoord.xy);
517  ivec2 blockCoord = texelCoord / blockSize;
518  ivec2 xOffset = ivec2(blockSize, 0);
519  ivec2 yOffset = ivec2(0, blockSize);
520 
521  if (((blockCoord.x + blockCoord.y) % 2) == modulo) {
522  for (int i = 0; i < N; ++i) {
523  output[i] = function(
524  texelFetch(texture[i], texelCoord + xOffset, 0),
525  texelFetch(texture[i], texelCoord - xOffset, 0),
526  texelFetch(texture[i], texelCoord + yOffset, 0),
527  texelFetch(texture[i], texelCoord - yOffset, 0)
528  );
529  }
530  } else {
531  discard;
532  }
533  }
534 
535  Where "blockSize" is the size of the disjoint rectangular sets of texels,
536  "modulo" should be either zero or one (depending whether even or odd
537  blocks should be fetched/written), and "function" is an arbitrary
538  function of its parameters.
539  */
540 class TextureBarrierTexelBlocksBase : public TextureBarrierBasicOutline
541 {
542 protected:
TextureBarrierTexelBlocksBase(deqp::Context & context,TextureBarrierTests::API api,const char * name,const char * description)543     TextureBarrierTexelBlocksBase(deqp::Context &context, TextureBarrierTests::API api, const char *name,
544                                   const char *description)
545         : TextureBarrierBasicOutline(context, api, name, description)
546         , m_blockSize(-1)
547         , m_modulo(-1)
548         , m_blockSizeLoc(0)
549         , m_moduloLoc(0)
550     {
551     }
552 
553     /* Actual fragment shader source based on the provided template. */
fsh()554     virtual const char *fsh()
555     {
556         return "#version 400 core\n"
557                "#define NUM_TEXTURES 8\n"
558                "uniform int blockSize;\n"
559                "uniform int modulo;\n"
560                "uniform usampler2D texInput[NUM_TEXTURES];\n"
561                "out uvec4 fragOutput[NUM_TEXTURES];\n"
562                "uvec4 func(uvec4 t0, uvec4 t1, uvec4 t2, uvec4 t3) {\n"
563                "   return t0 + t1 + t2 + t3;\n"
564                "}\n"
565                "void main() {\n"
566                "    ivec2 texelCoord = ivec2(gl_FragCoord.xy);\n"
567                "    ivec2 blockCoord = texelCoord / blockSize;\n"
568                "    ivec2 xOffset = ivec2(blockSize, 0);\n"
569                "    ivec2 yOffset = ivec2(0, blockSize);\n"
570                "    if (((blockCoord.x + blockCoord.y) % 2) == modulo) {\n"
571                "        for (int i = 0; i < NUM_TEXTURES; ++i) {\n"
572                "            fragOutput[i] = func(texelFetch(texInput[i], texelCoord + xOffset, 0),\n"
573                "                                 texelFetch(texInput[i], texelCoord - xOffset, 0),\n"
574                "                                 texelFetch(texInput[i], texelCoord + yOffset, 0),\n"
575                "                                 texelFetch(texInput[i], texelCoord - yOffset, 0));\n"
576                "        }\n"
577                "    } else {\n"
578                "        discard;\n"
579                "    }\n"
580                "}";
581     }
582 
583     /* CPU code equivalent to the fragment shader to update reference data. */
updateTextureData()584     virtual void updateTextureData()
585     {
586         for (GLuint x = 0; x < m_width; ++x)
587             for (GLuint y = 0; y < m_height; ++y)
588             {
589                 GLuint blockX = x / m_blockSize;
590                 GLuint blockY = y / m_blockSize;
591 
592                 if ((static_cast<int>((blockX + blockY) % 2)) == m_modulo)
593                 {
594                     for (GLuint i = 0; i < NUM_TEXTURES; ++i)
595                     {
596                         texel(m_reference[i], x, y) =
597                             texel(m_reference[i], x + m_blockSize, y) + texel(m_reference[i], x - m_blockSize, y) +
598                             texel(m_reference[i], x, y + m_blockSize) + texel(m_reference[i], x, y - m_blockSize);
599                     }
600                 }
601             }
602     }
603 
604     /* Render pass setup code. Updates uniforms used by the fragment shader and
605      member variables used by the reference data update code. */
setupRenderPass(int testPass,int renderPass)606     virtual void setupRenderPass(int testPass, int renderPass)
607     {
608         const glw::Functions &gl = m_context.getRenderContext().getFunctions();
609 
610         if ((testPass == 0) && (renderPass == 0))
611         {
612             // Get the uniform locations in the first pass, reuse it afterwards
613             m_blockSizeLoc = gl.getUniformLocation(m_program, "blockSize");
614             m_moduloLoc    = gl.getUniformLocation(m_program, "modulo");
615         }
616 
617         // Update block size if changed
618         int newBlockSize = getBlockSize(testPass, renderPass);
619         if (newBlockSize != m_blockSize)
620         {
621             m_blockSize = newBlockSize;
622             gl.uniform1i(m_blockSizeLoc, m_blockSize);
623         }
624 
625         // Update modulo if changed
626         int newModulo = getModulo(testPass, renderPass);
627         if (newModulo != m_modulo)
628         {
629             m_modulo = newModulo;
630             gl.uniform1i(m_moduloLoc, m_modulo);
631         }
632     }
633 
634     /* Returns the block size to be used in the specified pass. */
635     virtual int getBlockSize(int testPass, int renderPass) = 0;
636 
637     /* Returns the modulo value to be used in the specified pass. */
638     virtual int getModulo(int testPass, int renderPass) = 0;
639 
640 private:
641     int m_blockSize;
642     int m_modulo;
643     GLint m_blockSizeLoc;
644     GLint m_moduloLoc;
645 };
646 
647 /*
648  Test case #1: Disjoint texels
649 
650  * Using the basic outline above test that reads and writes from/to
651  disjoint sets of texels work as expected.
652 
653  * Repeat the above test case with various values for blockSize (including a
654  block size of one).
655 
656  * Repeat the actual rendering pass multiple times within a single test
657  using a fixed value for "blockSize" and "modulo". Because the set of
658  read and written texels stays disjoint the results should still be well
659  defined even without the use of any synchronization primitive.
660  */
661 class TextureBarrierDisjointTexels : public TextureBarrierTexelBlocksBase
662 {
663 public:
TextureBarrierDisjointTexels(deqp::Context & context,TextureBarrierTests::API api,const char * name)664     TextureBarrierDisjointTexels(deqp::Context &context, TextureBarrierTests::API api, const char *name)
665         : TextureBarrierTexelBlocksBase(
666               context, api, name,
667               "Using the basic outline test that reads and writes from/to disjoint sets of texels work as expected. "
668               "Repeat the test with multiple different block size values (including a block size of one). "
669               "Repeat the actual rendering pass multiple times within a single test using a fixed value for "
670               "blockSize and modulo. Because the set of read and written texels stays disjoint the result "
671               "should still be well defined even without the use of any synchronization primitive.")
672     {
673     }
674 
675     /* Perform multiple test passes, one for each blockSize value. */
numTestPasses()676     virtual int numTestPasses()
677     {
678         return 16;
679     }
680 
681     /* Perform multiple render passes. As the same blockSize and modulo value is used between render passes the rendering
682      results should still stay well defined. */
numRenderPasses()683     virtual int numRenderPasses()
684     {
685         return 5;
686     }
687 
688     /* No need for a texture barrier as reads and writes happen from/to disjoint set of texels within a single test pass. */
needsBarrier()689     virtual bool needsBarrier()
690     {
691         return false;
692     }
693 
694     /* Try different values for blockSize, but the value must stay constant within a single test pass. */
getBlockSize(int testPass,int renderPass)695     virtual int getBlockSize(int testPass, int renderPass)
696     {
697         (void)renderPass;
698         return testPass + 1;
699     }
700 
701     /* Use a fixed value for modulo. */
getModulo(int testPass,int renderPass)702     virtual int getModulo(int testPass, int renderPass)
703     {
704         (void)testPass;
705         (void)renderPass;
706         return 0;
707     }
708 };
709 
710 /*
711  Test case #2: Overlapping texels (with texture barrier)
712 
713  * Using the basic outline above test that reads and writes from/to
714  disjoint sets of texels work as expected.
715 
716  * Repeat the actual rendering pass multiple times within a single test,
717  but this time use different values for "blockSize" and "modulo" and
718  call TextureBarrier between subsequent rendering passes to ensure
719  well defined results.
720  */
721 class TextureBarrierOverlappingTexels : public TextureBarrierTexelBlocksBase
722 {
723 public:
TextureBarrierOverlappingTexels(deqp::Context & context,TextureBarrierTests::API api,const char * name)724     TextureBarrierOverlappingTexels(deqp::Context &context, TextureBarrierTests::API api, const char *name)
725         : TextureBarrierTexelBlocksBase(
726               context, api, name,
727               "Using the basic outline test that reads and writes from/to overlapping sets of texels work "
728               "as expected if there is a call to TextureBarrier between subsequent rendering passes. Test "
729               "this by using different values for blockSize and modulo for each rendering pass.")
730     {
731     }
732 
733     /* A single test pass is sufficient. */
numTestPasses()734     virtual int numTestPasses()
735     {
736         return 1;
737     }
738 
739     /* Perform several render passes to provoke a lot of overlap between read and written texel blocks. */
numRenderPasses()740     virtual int numRenderPasses()
741     {
742         return 42;
743     }
744 
745     /* We need texture barriers between render passes as reads and writes in different render passes do overlap. */
needsBarrier()746     virtual bool needsBarrier()
747     {
748         return true;
749     }
750 
751     /* Use a pseudo-random blockSize for each render pass. */
getBlockSize(int testPass,int renderPass)752     virtual int getBlockSize(int testPass, int renderPass)
753     {
754         (void)testPass;
755         return (5 + renderPass * 3) % 7 + 1;
756     }
757 
758     /* Use a pseudo-random modulo for each render pass. */
getModulo(int testPass,int renderPass)759     virtual int getModulo(int testPass, int renderPass)
760     {
761         (void)testPass;
762         return (renderPass * 3) % 2;
763     }
764 };
765 
766 /*
767  Base class of the rendering tests which use a fragment shader performing a
768  single read and write of each texel. The skeleton of these tests is as follows:
769 
770  * Using the basic outline above test that a single read and write of each
771  texel, where the read is in the fragment shader invocation that writes
772  the same texel, works as expected. Use the following fragment shader as
773  a template:
774 
775  uniform sampler2D texture[N];
776  out vec4 output[N];
777 
778  void main() {
779  ivec2 texelCoord = ivec2(gl_FragCoord.xy);
780 
781  for (int i = 0; i < N; ++i) {
782  output[i] = function(texelFetch(texture[i], texelCoord, 0);
783  }
784  }
785 
786  Where "function" is an arbitrary function of its parameter.
787  */
788 class TextureBarrierSameTexelRWBase : public TextureBarrierBasicOutline
789 {
790 protected:
TextureBarrierSameTexelRWBase(deqp::Context & context,TextureBarrierTests::API api,const char * name,const char * description)791     TextureBarrierSameTexelRWBase(deqp::Context &context, TextureBarrierTests::API api, const char *name,
792                                   const char *description)
793         : TextureBarrierBasicOutline(context, api, name, description)
794     {
795     }
796 
797     /* Actual fragment shader source based on the provided template. */
fsh()798     virtual const char *fsh()
799     {
800         return "#version 400 core\n"
801                "#define NUM_TEXTURES 8\n"
802                "uniform usampler2D texInput[NUM_TEXTURES];\n"
803                "out uvec4 fragOutput[NUM_TEXTURES];\n"
804                "uvec4 func(uvec4 t) {\n"
805                "    return t + 1;\n"
806                "}\n"
807                "void main() {\n"
808                "    ivec2 texelCoord = ivec2(gl_FragCoord.xy);\n"
809                "    for (int i = 0; i < NUM_TEXTURES; ++i) {\n"
810                "        fragOutput[i] = func(texelFetch(texInput[i], texelCoord, 0));\n"
811                "    }\n"
812                "}";
813     }
814 
815     /* CPU code equivalent to the fragment shader to update reference data. */
updateTextureData()816     virtual void updateTextureData()
817     {
818         for (GLuint x = 0; x < m_width; ++x)
819             for (GLuint y = 0; y < m_height; ++y)
820             {
821                 for (GLuint i = 0; i < NUM_TEXTURES; ++i)
822                 {
823                     texel(m_reference[i], x, y)++;
824                 }
825             }
826     }
827 
828     /* The fragment shader used by these tests doesn't have any parameters, thus no need for render pass setup code. */
setupRenderPass(int testPass,int renderPass)829     virtual void setupRenderPass(int testPass, int renderPass)
830     {
831         (void)testPass;
832         (void)renderPass;
833     }
834 };
835 
836 /*
837  Test case #3: Single read and write of the same texel
838 
839  * Using the basic outline above test that a single read and write of each
840  texel, where the read is in the fragment shader invocation that writes
841  the same texel, works as expected.
842  */
843 class TextureBarrierSameTexelRW : public TextureBarrierSameTexelRWBase
844 {
845 public:
TextureBarrierSameTexelRW(deqp::Context & context,TextureBarrierTests::API api,const char * name)846     TextureBarrierSameTexelRW(deqp::Context &context, TextureBarrierTests::API api, const char *name)
847         : TextureBarrierSameTexelRWBase(
848               context, api, name,
849               "Using the basic outline tests that a single read and write of each texel, where the read "
850               "is in the fragment shader invocation that writes the same texel, works as expected.")
851     {
852     }
853 
854     /* A single test pass is sufficient. */
numTestPasses()855     virtual int numTestPasses()
856     {
857         return 1;
858     }
859 
860     /* Well defined behavior is guaranteed only in case of a single pass. */
numRenderPasses()861     virtual int numRenderPasses()
862     {
863         return 1;
864     }
865 
866     /* A single read and write of the same texel doesn't require a texture barrier. */
needsBarrier()867     virtual bool needsBarrier()
868     {
869         return false;
870     }
871 };
872 
873 /*
874  Test case #4: Multipass read and write of the same texel (with texture barrier)
875 
876  * Using the basic outline above test that a single read and write of each
877  texel, where the read is in the fragment shader invocation that writes
878  the same texel, works as expected.
879 
880  * Repeat the above test case but this time perform multiple iterations
881  of the actual rendering pass and use a call to TextureBarrier between
882  them to ensure consistency.
883  */
884 class TextureBarrierSameTexelRWMultipass : public TextureBarrierSameTexelRWBase
885 {
886 public:
TextureBarrierSameTexelRWMultipass(deqp::Context & context,TextureBarrierTests::API api,const char * name)887     TextureBarrierSameTexelRWMultipass(deqp::Context &context, TextureBarrierTests::API api, const char *name)
888         : TextureBarrierSameTexelRWBase(
889               context, api, name,
890               "Using the basic outline tests that multiple reads and writes of each texel, where the read "
891               "is in the fragment shader invocation that writes the same texel, works as expected if there "
892               "is a call to TextureBarrier between each subsequent read-after-write.")
893     {
894     }
895 
896     /* A single test pass is sufficient. */
numTestPasses()897     virtual int numTestPasses()
898     {
899         return 1;
900     }
901 
902     /* Perform several render passes to provoke read-after-write hazards. */
numRenderPasses()903     virtual int numRenderPasses()
904     {
905         return 42;
906     }
907 
908     /* We need to use texture barriers in between rendering passes to avoid read-after-write hazards. */
needsBarrier()909     virtual bool needsBarrier()
910     {
911         return true;
912     }
913 };
914 
apiToTestName(TextureBarrierTests::API api)915 const char *apiToTestName(TextureBarrierTests::API api)
916 {
917     switch (api)
918     {
919     case TextureBarrierTests::API_GL_45core:
920         return "texture_barrier";
921     case TextureBarrierTests::API_GL_ARB_texture_barrier:
922         return "texture_barrier_ARB";
923     }
924     DE_ASSERT(0);
925     return "";
926 }
927 
928 /** Constructor.
929  *
930  *  @param context Rendering context.
931  *  @param api     API to test (core vs ARB extension)
932  **/
TextureBarrierTests(deqp::Context & context,API api)933 TextureBarrierTests::TextureBarrierTests(deqp::Context &context, API api)
934     : TestCaseGroup(context, apiToTestName(api), "Verifies \"texture_barrier\" functionality")
935     , m_api(api)
936 {
937     /* Left blank on purpose */
938 }
939 
940 /** Destructor.
941  *
942  **/
~TextureBarrierTests()943 TextureBarrierTests::~TextureBarrierTests()
944 {
945 }
946 
947 /** Initializes the texture_barrier test group.
948  *
949  **/
init(void)950 void TextureBarrierTests::init(void)
951 {
952     addChild(new TextureBarrierDisjointTexels(m_context, m_api, "disjoint-texels"));
953     addChild(new TextureBarrierOverlappingTexels(m_context, m_api, "overlapping-texels"));
954     addChild(new TextureBarrierSameTexelRW(m_context, m_api, "same-texel-rw"));
955     addChild(new TextureBarrierSameTexelRWMultipass(m_context, m_api, "same-texel-rw-multipass"));
956 }
957 } // namespace gl4cts
958