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