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