• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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