/*-------------------------------------------------------------------------
* 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);
const int readDataSize = readRefrence.getWidth() * readRefrence.getHeight() * readFormat.getPixelSize();
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, readDataSize, 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, readDataSize, 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