text: /*-------------------------------------------------------------------------
   * drawElements Quality Program OpenGL ES 3.0 Module
   * -------------------------------------------------
   *
   * Copyright 2014 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   *//*!
   * \file
   * \brief Pixel Buffer Object tests
   *//*--------------------------------------------------------------------*/
  
  #include "es3fPixelBufferObjectTests.hpp"
  
  #include "tcuTexture.hpp"
  #include "tcuTextureUtil.hpp"
  #include "tcuImageCompare.hpp"
  #include "tcuTestLog.hpp"
  #include "tcuRenderTarget.hpp"
  
  #include "gluTextureUtil.hpp"
  #include "gluPixelTransfer.hpp"
  #include "gluShaderProgram.hpp"
  
  #include "deRandom.hpp"
  #include "deString.h"
  
  #include <string>
  #include <sstream>
  
  #include "glw.h"
  
  using std::string;
  using std::stringstream;
  
  namespace deqp
  {
  namespace gles3
  {
  namespace Functional
  {
  
  namespace
  {
  
  class ReadPixelsTest : public TestCase
  {
  public:
  	struct TestSpec
  	{
  		enum FramebufferType
  		{
  			FRAMEBUFFERTYPE_NATIVE = 0,
  			FRAMEBUFFERTYPE_RENDERBUFFER
  		};
  
  		string			name;
  		string			description;
  
  		bool			useColorClear;
  		bool			renderTriangles;
  
  		FramebufferType	framebufferType;
  		GLenum			renderbufferFormat;
  	};
  
  					ReadPixelsTest				(Context& context, const TestSpec& spec);
  					~ReadPixelsTest				(void);
  
  	void			init						(void);
  	void			deinit						(void);
  
  	IterateResult	iterate						(void);
  
  private:
  	void						renderTriangle	(const tcu::Vec3& a, const tcu::Vec3& b, const tcu::Vec3& c);
  	void						clearColor		(float r, float g, float b, float a);
  
  	de::Random					m_random;
  	tcu::TestLog&				m_log;
  	glu::ShaderProgram*			m_program;
  
  	TestSpec::FramebufferType	m_framebuffeType;
  
  	GLenum						m_renderbufferFormat;
  	tcu::TextureChannelClass	m_texChannelClass;
  
  	bool						m_useColorClears;
  	bool						m_renderTriangles;
  
  	GLfloat						m_colorScale;
  };
  
  
  ReadPixelsTest::ReadPixelsTest (Context& context, const TestSpec& spec)
  	: TestCase				(context, spec.name.c_str(), spec.description.c_str())
  	, m_random				(deStringHash(spec.name.c_str()))
  	, m_log					(m_testCtx.getLog())
  	, m_program				(NULL)
  	, m_framebuffeType		(spec.framebufferType)
  	, m_renderbufferFormat	(spec.renderbufferFormat)
  	, m_texChannelClass		(tcu::TEXTURECHANNELCLASS_LAST)
  	, m_useColorClears		(spec.useColorClear)
  	, m_renderTriangles		(spec.renderTriangles)
  	, m_colorScale			(1.0f)
  {
  
  	if (m_framebuffeType == TestSpec::FRAMEBUFFERTYPE_NATIVE)
  	{
  		m_colorScale = 1.0f;
  	}
  	else if (m_framebuffeType == TestSpec::FRAMEBUFFERTYPE_RENDERBUFFER)
  	{
  		m_texChannelClass = tcu::getTextureChannelClass(glu::mapGLInternalFormat(spec.renderbufferFormat).type);
  		switch (m_texChannelClass)
  		{
  			case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
  				m_colorScale = 1.0f;
  				break;
  
  			case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
  				m_colorScale = 100.0f;
  				break;
  
  			case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
  				m_colorScale = 100.0f;
  				break;
  
  			case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
  				m_colorScale = 100.0f;
  				break;
  
  			default:
  				DE_ASSERT(false);
  		}
  	}
  	else
  		DE_ASSERT(false);
  }
  
  ReadPixelsTest::~ReadPixelsTest (void)
  {
  }
  
  void ReadPixelsTest::init (void)
  {
  	// Check extensions
  	if (m_framebuffeType == TestSpec::FRAMEBUFFERTYPE_RENDERBUFFER)
  	{
  		bool supported = false;
  
  		if (m_renderbufferFormat == GL_RGBA16F
  			|| m_renderbufferFormat == GL_RG16F)
  		{
  			std::istringstream extensions(std::string((const char*)glGetString(GL_EXTENSIONS)));
  			std::string extension;
  
  			while (std::getline(extensions, extension, ' '))
  			{
  				if (extension=="GL_EXT_color_buffer_half_float")
  				{
  					supported = true;
  					break;
  				}
  				if (extension=="GL_EXT_color_buffer_float")
  				{
  					supported = true;
  					break;
  				}
  			}
  		}
  		else if (m_renderbufferFormat == GL_RGBA32F
  				|| m_renderbufferFormat == GL_RG32F
  				|| m_renderbufferFormat == GL_R11F_G11F_B10F)
  		{
  			std::istringstream extensions(std::string((const char*)glGetString(GL_EXTENSIONS)));
  			std::string extension;
  
  			while (std::getline(extensions, extension, ' '))
  			{
  				if (extension=="GL_EXT_color_buffer_float")
  				{
  					supported = true;
  					break;
  				}
  			}
  		}
  		else
  			supported = true;
  
  		if (!supported)
  			throw tcu::NotSupportedError("Renderbuffer format not supported", "", __FILE__, __LINE__);
  	}
  
  	std::string outtype = "";
  
  	if (m_framebuffeType == TestSpec::FRAMEBUFFERTYPE_NATIVE)
  		outtype = "vec4";
  	else if (m_framebuffeType == TestSpec::FRAMEBUFFERTYPE_RENDERBUFFER)
  	{
  		switch (m_texChannelClass)
  		{
  			case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
  				outtype = "vec4";
  				break;
  
  			case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
  				outtype = "ivec4";
  				break;
  
  			case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
  				outtype = "uvec4";
  				break;
  
  			case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
  				outtype = "vec4";
  				break;
  
  			default:
  				DE_ASSERT(false);
  		}
  	}
  	else
  		DE_ASSERT(false);
  
  
  	const char* vertexShaderSource =
  	"#version 300 es\n"
  	"in mediump vec3 a_position;\n"
  	"in mediump vec4 a_color;\n"
  	"uniform mediump float u_colorScale;\n"
  	"out mediump vec4 v_color;\n"
  	"void main(void)\n"
  	"{\n"
  	"\tgl_Position = vec4(a_position, 1.0);\n"
  	"\tv_color = u_colorScale * a_color;\n"
  	"}";
  
  	stringstream fragmentShaderSource;
  
  	fragmentShaderSource <<
  	"#version 300 es\n"
  	"in mediump vec4 v_color;\n";
  
  
  	fragmentShaderSource << "layout (location = 0) out mediump " << outtype << " o_color;\n"
  	"void main(void)\n"
  	"{\n"
  	"\to_color = " << outtype << "(v_color);\n"
  	"}";
  
  	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource.str()));
  
  	if (!m_program->isOk())
  	{
  		m_log << *m_program;
  		TCU_FAIL("Failed to compile shader");
  	}
  }
  
  void ReadPixelsTest::deinit (void)
  {
  	if (m_program)
  		delete m_program;
  	m_program = NULL;
  }
  
  void ReadPixelsTest::renderTriangle (const tcu::Vec3& a, const tcu::Vec3& b, const tcu::Vec3& c)
  {
  	float positions[3*3];
  
  	positions[0] = a.x();
  	positions[1] = a.y();
  	positions[2] = a.z();
  
  	positions[3] = b.x();
  	positions[4] = b.y();
  	positions[5] = b.z();
  
  	positions[6] = c.x();
  	positions[7] = c.y();
  	positions[8] = c.z();
  
  	float colors[] = {
  		1.0f, 0.0f, 0.0f, 1.0f,
  		0.0f, 1.0f, 0.0f, 1.0f,
  		0.0f, 0.0f, 1.0f, 1.0f
  	};
  
  	GLU_CHECK_CALL(glUseProgram(m_program->getProgram()));
  
  	GLuint coordLoc = (GLuint)-1;
  	GLuint colorLoc = (GLuint)-1;
  	GLuint colorScaleLoc = (GLuint)-1;
  
  	colorScaleLoc = glGetUniformLocation(m_program->getProgram(), "u_colorScale");
  	TCU_CHECK(colorScaleLoc != (GLuint)-1);
  
  	GLU_CHECK_CALL(glUniform1f(colorScaleLoc, m_colorScale));
  
  	coordLoc = glGetAttribLocation(m_program->getProgram(), "a_position");
  	TCU_CHECK(coordLoc != (GLuint)-1);
  
  	colorLoc = glGetAttribLocation(m_program->getProgram(), "a_color");
  	TCU_CHECK(colorLoc != (GLuint)-1);
  
  	GLU_CHECK_CALL(glEnableVertexAttribArray(colorLoc));
  	GLU_CHECK_CALL(glEnableVertexAttribArray(coordLoc));
  
  	GLU_CHECK_CALL(glVertexAttribPointer(coordLoc, 3, GL_FLOAT, GL_FALSE, 0, positions));
  	GLU_CHECK_CALL(glVertexAttribPointer(colorLoc, 4, GL_FLOAT, GL_FALSE, 0, colors));
  
  	GLU_CHECK_CALL(glDrawArrays(GL_TRIANGLES, 0, 3));
  
  	GLU_CHECK_CALL(glDisableVertexAttribArray(colorLoc));
  	GLU_CHECK_CALL(glDisableVertexAttribArray(coordLoc));
  
  }
  
  
  void ReadPixelsTest::clearColor (float r, float g, float b, float a)
  {
  	if (m_framebuffeType == TestSpec::FRAMEBUFFERTYPE_NATIVE)
  	{
  		GLU_CHECK_CALL(glClearColor(r, g, b, a));
  		GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
  	}
  	else if (m_framebuffeType == TestSpec::FRAMEBUFFERTYPE_RENDERBUFFER)
  	{
  		switch (m_texChannelClass)
  		{
  			case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
  			{
  				GLU_CHECK_CALL(glClearColor(r, g, b, a));
  				GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT));
  				break;
  			}
  
  			case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
  			{
  				GLint color[4] = { (GLint)r, (GLint)g, (GLint)b, (GLint)a };
  
  				GLU_CHECK_CALL(glClearBufferiv(GL_COLOR, 0, color));
  				break;
  			}
  
  			case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
  			{
  				GLuint color[4] = { (GLuint)r, (GLuint)g, (GLuint)b, (GLuint)a };
  
  				GLU_CHECK_CALL(glClearBufferuiv(GL_COLOR, 0, color));
  				break;
  			}
  
  			case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
  			{
  				GLfloat color[4] = { (GLfloat)r, (GLfloat)g, (GLfloat)b, (GLfloat)a };
  
  				GLU_CHECK_CALL(glClearBufferfv(GL_COLOR, 0, color));
  				break;
  			}
  
  			default:
  				DE_ASSERT(false);
  		}
  	}
  	else
  		DE_ASSERT(false);
  
  }
  
  TestCase::IterateResult ReadPixelsTest::iterate(void)
  {
  	int width				= m_context.getRenderTarget().getWidth();
  	int height				= m_context.getRenderTarget().getHeight();
  
  	GLuint framebuffer	= 0;
  	GLuint renderbuffer	= 0;
  
  	switch (m_framebuffeType)
  	{
  		case TestSpec::FRAMEBUFFERTYPE_NATIVE:
  			GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer));
  			break;
  
  		case TestSpec::FRAMEBUFFERTYPE_RENDERBUFFER:
  		{
  			GLU_CHECK_CALL(glGenFramebuffers(1, &framebuffer));
  			GLU_CHECK_CALL(glGenRenderbuffers(1, &renderbuffer));
  
  			GLU_CHECK_CALL(glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer));
  			GLU_CHECK_CALL(glRenderbufferStorage(GL_RENDERBUFFER, m_renderbufferFormat, width, height));
  
  			GLU_CHECK_CALL(glBindFramebuffer(GL_FRAMEBUFFER, framebuffer));
  			GLU_CHECK_CALL(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer));
  
  			break;
  		}
  
  		default:
  			DE_ASSERT(false);
  	}
  
  	clearColor(m_colorScale * 0.4f, m_colorScale * 1.0f, m_colorScale * 0.5f, m_colorScale * 1.0f);
  
  	if (m_useColorClears)
  	{
  		const int maxClearCount	= 10;
  		const int minClearCount	= 6;
  		const int minClearSize	= 15;
  
  		int clearCount = m_random.getInt(minClearCount, maxClearCount);
  
  		for (int clearNdx = 0; clearNdx < clearCount; clearNdx++)
  		{
  			int clearX = m_random.getInt(0, width - minClearSize);
  			int clearY = m_random.getInt(0, height - minClearSize);
  
  			int clearWidth = m_random.getInt(minClearSize, width - clearX);
  			int clearHeight = m_random.getInt(minClearSize, height - clearY);
  
  			float clearRed		= m_colorScale * m_random.getFloat();
  			float clearGreen	= m_colorScale * m_random.getFloat();
  			float clearBlue		= m_colorScale * m_random.getFloat();
  			float clearAlpha	= m_colorScale * (0.5f + 0.5f * m_random.getFloat());
  
  			GLU_CHECK_CALL(glEnable(GL_SCISSOR_TEST));
  			GLU_CHECK_CALL(glScissor(clearX, clearY, clearWidth, clearHeight));
  
  			clearColor(clearRed, clearGreen, clearBlue, clearAlpha);
  		}
  
  		GLU_CHECK_CALL(glDisable(GL_SCISSOR_TEST));
  	}
  
  	if (m_renderTriangles)
  	{
  		const int minTriangleCount = 4;
  		const int maxTriangleCount = 10;
  
  		int triangleCount = m_random.getInt(minTriangleCount, maxTriangleCount);
  
  		for (int triangleNdx = 0; triangleNdx < triangleCount; triangleNdx++)
  		{
  			float x1 = 2.0f * m_random.getFloat() - 1.0f;
  			float y1 = 2.0f * m_random.getFloat() - 1.0f;
  			float z1 = 2.0f * m_random.getFloat() - 1.0f;
  
  			float x2 = 2.0f * m_random.getFloat() - 1.0f;
  			float y2 = 2.0f * m_random.getFloat() - 1.0f;
  			float z2 = 2.0f * m_random.getFloat() - 1.0f;
  
  			float x3 = 2.0f * m_random.getFloat() - 1.0f;
  			float y3 = 2.0f * m_random.getFloat() - 1.0f;
  			float z3 = 2.0f * m_random.getFloat() - 1.0f;
  
  			renderTriangle(tcu::Vec3(x1, y1, z1), tcu::Vec3(x2, y2, z2), tcu::Vec3(x3, y3, z3));
  		}
  	}
  
  	tcu::TextureFormat	readFormat;
  	GLenum				readPixelsFormat;
  	GLenum				readPixelsType;
  	bool				floatCompare;
  
  
  	if (m_framebuffeType == TestSpec::FRAMEBUFFERTYPE_NATIVE)
  	{
  		readFormat			= glu::mapGLTransferFormat(GL_RGBA, GL_UNSIGNED_BYTE);
  		readPixelsFormat	= GL_RGBA;
  		readPixelsType		= GL_UNSIGNED_BYTE;
  		floatCompare		= false;
  	}
  	else if (m_framebuffeType == TestSpec::FRAMEBUFFERTYPE_RENDERBUFFER)
  	{
  		switch (m_texChannelClass)
  		{
  			case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
  				readFormat			= glu::mapGLTransferFormat(GL_RGBA, GL_UNSIGNED_BYTE);
  				readPixelsFormat	= GL_RGBA;
  				readPixelsType		= GL_UNSIGNED_BYTE;
  				floatCompare		= true;
  				break;
  
  			case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
  				readFormat			= glu::mapGLTransferFormat(GL_RGBA_INTEGER, GL_INT);
  				readPixelsFormat	= GL_RGBA_INTEGER;
  				readPixelsType		= GL_INT;
  				floatCompare		= false;
  				break;
  
  			case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
  				readFormat			= glu::mapGLTransferFormat(GL_RGBA_INTEGER, GL_UNSIGNED_INT);
  				readPixelsFormat	= GL_RGBA_INTEGER;
  				readPixelsType		= GL_UNSIGNED_INT;
  				floatCompare		= false;
  				break;
  
  			case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
  				readFormat			= glu::mapGLTransferFormat(GL_RGBA, GL_FLOAT);
  				readPixelsFormat	= GL_RGBA;
  				readPixelsType		= GL_FLOAT;
  				floatCompare		= true;
  				break;
  
  			default:
  				DE_ASSERT(false);
  				// Silence warnings
  				readFormat			= glu::mapGLTransferFormat(GL_RGBA, GL_FLOAT);
  				readPixelsFormat	= GL_RGBA;
  				readPixelsType		= GL_FLOAT;
  				floatCompare		= true;
  		}
  	}
  	else
  	{
  		// Silence warnings
  		readFormat			= glu::mapGLTransferFormat(GL_RGBA, GL_FLOAT);
  		readPixelsFormat	= GL_RGBA;
  		readPixelsType		= GL_FLOAT;
  		floatCompare		= true;
  		DE_ASSERT(false);
  	}
  
  	tcu::Texture2D readRefrence(readFormat, width, height);
  	readRefrence.allocLevel(0);
  
  	GLuint pixelBuffer = (GLuint)-1;
  
  	GLU_CHECK_CALL(glGenBuffers(1, &pixelBuffer));
  	GLU_CHECK_CALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBuffer));
  	GLU_CHECK_CALL(glBufferData(GL_PIXEL_PACK_BUFFER, readRefrence.getLevel(0).getDataSize(), NULL, GL_STREAM_READ));
  
  	GLU_CHECK_CALL(glReadPixels(0, 0, width, height, readPixelsFormat, readPixelsType, 0));
  
  	const deUint8* bufferData = (const deUint8*)glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, readRefrence.getLevel(0).getDataSize(), GL_MAP_READ_BIT);
  	GLU_CHECK_MSG("glMapBufferRange() failed");
  
  	tcu::ConstPixelBufferAccess readResult(readFormat, width, height, 1, bufferData);
  
  	GLU_CHECK_CALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, 0));
  
  	GLU_CHECK_CALL(glReadPixels(0, 0, width, height, readPixelsFormat, readPixelsType, readRefrence.getLevel(0).getDataPtr()));
  
  	if (framebuffer)
  		GLU_CHECK_CALL(glDeleteFramebuffers(1, &framebuffer));
  
  	if (renderbuffer)
  		GLU_CHECK_CALL(glDeleteRenderbuffers(1, &renderbuffer));
  
  
  	bool isOk = false;
  
  	if (floatCompare)
  		isOk = tcu::floatThresholdCompare(m_log, "Result comparision", "Result of read pixels to memory compared with result of read pixels to buffer", readRefrence.getLevel(0), readResult, tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f), tcu::COMPARE_LOG_RESULT);
  	else
  		isOk = tcu::intThresholdCompare(m_log, "Result comparision", "Result of read pixels to memory compared with result of read pixels to buffer", readRefrence.getLevel(0), readResult, tcu::UVec4(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
  
  	GLU_CHECK_CALL(glBindBuffer(GL_PIXEL_PACK_BUFFER, pixelBuffer));
  	GLU_CHECK_CALL(glUnmapBuffer(GL_PIXEL_PACK_BUFFER));
  	GLU_CHECK_CALL(glDeleteBuffers(1, &pixelBuffer));
  
  	if (isOk)
  	{
  		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
  		return STOP;
  	}
  	else
  	{
  		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
  		return STOP;
  	}
  }
  
  } // anonymous
  
  PixelBufferObjectTests::PixelBufferObjectTests (Context& context)
  	: TestCaseGroup (context, "pbo", "Pixel buffer objects tests")
  {
  }
  
  PixelBufferObjectTests::~PixelBufferObjectTests (void)
  {
  }
  
  void PixelBufferObjectTests::init (void)
  {
  	TestCaseGroup* nativeFramebufferGroup = new TestCaseGroup(m_context, "native", "Tests with reading from native framebuffer");
  
  	ReadPixelsTest::TestSpec nativeFramebufferTests[] = {
  		{
  			"clears",
  			"Simple read pixels test with color clears",
  			true,
  			false,
  			ReadPixelsTest::TestSpec::FRAMEBUFFERTYPE_NATIVE,
  			GL_NONE
  		},
  		{
  			"triangles",
  			"Simple read pixels test rendering triangles",
  			false,
  			true,
  			ReadPixelsTest::TestSpec::FRAMEBUFFERTYPE_NATIVE,
  			GL_NONE
  		}
  	};
  
  	for (int testNdx = 0; testNdx < DE_LENGTH_OF_ARRAY(nativeFramebufferTests); testNdx++)
  	{
  		nativeFramebufferGroup->addChild(new ReadPixelsTest(m_context, nativeFramebufferTests[testNdx]));
  	}
  
  	addChild(nativeFramebufferGroup);
  
  	TestCaseGroup* renderbufferGroup = new TestCaseGroup(m_context, "renderbuffer", "Tests with reading from renderbuffer");
  
  	GLenum renderbufferFormats[] = {
  		GL_RGBA8,
  		GL_RGBA8I,
  		GL_RGBA8UI,
  		GL_RGBA16F,
  		GL_RGBA16I,
  		GL_RGBA16UI,
  		GL_RGBA32F,
  		GL_RGBA32I,
  		GL_RGBA32UI,
  
  		GL_SRGB8_ALPHA8,
  		GL_RGB10_A2,
  		GL_RGB10_A2UI,
  		GL_RGBA4,
  		GL_RGB5_A1,
  
  		GL_RGB8,
  		GL_RGB565,
  
  		GL_R11F_G11F_B10F,
  
  		GL_RG8,
  		GL_RG8I,
  		GL_RG8UI,
  		GL_RG16F,
  		GL_RG16I,
  		GL_RG16UI,
  		GL_RG32F,
  		GL_RG32I,
  		GL_RG32UI
  	};
  
  	const char* renderbufferFormatsStr[] = {
  		"rgba8",
  		"rgba8i",
  		"rgba8ui",
  		"rgba16f",
  		"rgba16i",
  		"rgba16ui",
  		"rgba32f",
  		"rgba32i",
  		"rgba32ui",
  
  		"srgb8_alpha8",
  		"rgb10_a2",
  		"rgb10_a2ui",
  		"rgba4",
  		"rgb5_a1",
  
  		"rgb8",
  		"rgb565",
  
  		"r11f_g11f_b10f",
  
  		"rg8",
  		"rg8i",
  		"rg8ui",
  		"rg16f",
  		"rg16i",
  		"rg16ui",
  		"rg32f",
  		"rg32i",
  		"rg32ui"
  	};
  
  	DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(renderbufferFormatsStr) == DE_LENGTH_OF_ARRAY(renderbufferFormats));
  
  	for (int formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(renderbufferFormats); formatNdx++)
  	{
  		for (int trianglesClears = 0; trianglesClears < 2; trianglesClears++)
  		{
  			ReadPixelsTest::TestSpec testSpec;
  
  			testSpec.name					= string(renderbufferFormatsStr [formatNdx]) + "_" + (trianglesClears == 0 ? "triangles" : "clears"),
  			testSpec.description			= testSpec.name;
  			testSpec.useColorClear			= trianglesClears == 1,
  			testSpec.renderTriangles		= trianglesClears == 0,
  			testSpec.framebufferType		= ReadPixelsTest::TestSpec::FRAMEBUFFERTYPE_RENDERBUFFER,
  			testSpec.renderbufferFormat		= renderbufferFormats[formatNdx];
  
  			renderbufferGroup->addChild(new ReadPixelsTest(m_context, testSpec));
  		}
  	}
  
  	addChild(renderbufferGroup);
  }
  
  } // Functional
  } // gles3
  } // deqp
  