Home | History | Annotate | Download | only in gl
      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:
     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. */
     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. */
     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:
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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. */
    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:
    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. */
    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. */
    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. */
    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:
    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. */
    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. */
    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. */
    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. */
    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. */
    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:
    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. */
    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. */
    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. */
    745 	virtual bool needsBarrier()
    746 	{
    747 		return true;
    748 	}
    749 
    750 	/* Use a pseudo-random blockSize for each render pass. */
    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. */
    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:
    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. */
    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. */
    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. */
    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:
    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. */
    854 	virtual int numTestPasses()
    855 	{
    856 		return 1;
    857 	}
    858 
    859 	/* Well defined behavior is guaranteed only in case of a single pass. */
    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. */
    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:
    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. */
    896 	virtual int numTestPasses()
    897 	{
    898 		return 1;
    899 	}
    900 
    901 	/* Perform several render passes to provoke read-after-write hazards. */
    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. */
    908 	virtual bool needsBarrier()
    909 	{
    910 		return true;
    911 	}
    912 };
    913 
    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  **/
    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  **/
    941 TextureBarrierTests::~TextureBarrierTests()
    942 {
    943 }
    944 
    945 /** Initializes the texture_barrier test group.
    946  *
    947  **/
    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