blob: 80625f1f347f5909191187acb7eaa8192f797585 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2015-2016 The Khronos Group Inc.
*
* 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
*/ /*-------------------------------------------------------------------*/
/*!
* \file esextcDrawBuffersIndexedBlending.hpp
* \brief Draw Buffers Indexed tests 5. Blending
*/ /*-------------------------------------------------------------------*/
#include "esextcDrawBuffersIndexedBlending.hpp"
#include "gluPixelTransfer.hpp"
#include "gluShaderProgram.hpp"
#include "tcuTestLog.hpp"
#include <cmath>
namespace glcts
{
/** Constructor
*
* @param context Test context
* @param name Test case's name
* @param description Test case's description
**/
DrawBuffersIndexedBlending::DrawBuffersIndexedBlending(Context& context, const ExtParameters& extParams,
const char* name, const char* description)
: DrawBuffersIndexedBase(context, extParams, name, description)
{
/* Left blank on purpose */
}
void DrawBuffersIndexedBlending::prepareFramebuffer()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
glw::GLint maxDrawBuffers = 0;
gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
if (maxDrawBuffers < 4)
{
throw tcu::ResourceError("Minimum number of draw buffers too low");
}
gl.genFramebuffers(1, &m_fbo);
gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
std::vector<glw::GLenum> bufs(maxDrawBuffers);
for (int i = 0; i < maxDrawBuffers; ++i)
{
bufs[i] = GL_COLOR_ATTACHMENT0 + i;
}
gl.drawBuffers(maxDrawBuffers, &bufs[0]);
}
void DrawBuffersIndexedBlending::releaseFramebuffer()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
glw::GLint maxDrawBuffers = 0;
gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
if (maxDrawBuffers < 4)
{
throw tcu::ResourceError("Minimum number of draw buffers too low");
}
BlendMaskStateMachine state(m_context, m_testCtx.getLog(), maxDrawBuffers);
state.SetDefaults();
gl.deleteFramebuffers(1, &m_fbo);
gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
glw::GLenum bufs[1] = { GL_BACK };
gl.drawBuffers(1, bufs);
gl.readBuffer(GL_BACK);
}
tcu::TestNode::IterateResult DrawBuffersIndexedBlending::iterate()
{
static const glw::GLenum BlendFormats[] = {
GL_R8, GL_RG8, GL_RGB8, GL_RGB565, GL_RGBA4, GL_RGBA8,
};
static const int kSize = 32;
static unsigned int formatId = 0;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
glw::GLenum format = BlendFormats[formatId];
prepareFramebuffer();
// Check number of available draw buffers
glw::GLint maxDrawBuffers = 0;
gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
if (maxDrawBuffers < 4)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Minimum number of draw buffers too low");
return STOP;
}
// Prepare render targets
glw::GLuint tex;
gl.genTextures(1, &tex);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, tex);
gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1, format, kSize, kSize, maxDrawBuffers);
for (int i = 0; i < maxDrawBuffers; ++i)
{
gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, tex, 0, i);
}
// Clear background color
tcu::Vec4 background(0.5f, 0.5f, 0.5f, 0.5f);
for (int i = 0; i < maxDrawBuffers; ++i)
{
gl.clearBufferfv(GL_COLOR, i, &background[0]);
}
// Prepare expected, blended color values
tcu::Vec4 colors[] = { tcu::Vec4(0.86f, 0.22f, 0.31f, 0.45f), tcu::Vec4(0.12f, 0.83f, 0.34f, 0.42f),
tcu::Vec4(0.56f, 0.63f, 0.76f, 0.99f), tcu::Vec4(0.14f, 0.34f, 0.34f, 0.22f) };
int numComponents = NumComponents(format);
tcu::RGBA expected[] = {
// GL_MIN
tcu::RGBA(static_cast<unsigned int>(background.x() * 255),
static_cast<unsigned int>((numComponents >= 2 ? colors[0].y() : 0.0f) * 255),
static_cast<unsigned int>((numComponents >= 3 ? colors[0].z() : 0.0f) * 255),
static_cast<unsigned int>((numComponents == 4 ? background.w() : 1.0f) * 255)),
// GL_FUNC_ADD
tcu::RGBA(static_cast<unsigned int>(background.x() * 255),
static_cast<unsigned int>((numComponents >= 2 ? background.y() : 0.0f) * 255),
static_cast<unsigned int>((numComponents >= 3 ? background.z() : 0.0f) * 255),
static_cast<unsigned int>((numComponents == 4 ? colors[1].w() : 1.0f) * 255)),
// GL_FUNC_SUBTRACT
tcu::RGBA(
static_cast<unsigned int>((colors[2].x() * (numComponents == 4 ? colors[2].w() : 1.0f) -
background.x() * (numComponents == 4 ? background.w() : 1.0f)) *
255),
static_cast<unsigned int>((numComponents >= 2 ?
(colors[2].y() * (numComponents == 4 ? colors[2].w() : 1.0f) -
background.y() * (numComponents == 4 ? background.w() : 1.0f)) :
0.0f) *
255),
static_cast<unsigned int>((numComponents >= 3 ?
(colors[2].z() * (numComponents == 4 ? colors[2].w() : 1.0f) -
background.z() * (numComponents == 4 ? background.w() : 1.0f)) :
0.0f) *
255),
static_cast<unsigned int>(
(numComponents == 4 ? (colors[2].w() * colors[2].w() - background.w() * background.w()) : 1.0f) * 255)),
// GL_FUNC_REVERSE_SUBTRACT
tcu::RGBA(static_cast<unsigned int>((background.x() - colors[3].x()) * 255),
static_cast<unsigned int>((numComponents >= 2 ? (background.y() - colors[3].y()) : 0.0f) * 255),
static_cast<unsigned int>((numComponents >= 3 ? (background.z() - colors[3].z()) : 0.0f) * 255),
static_cast<unsigned int>((numComponents == 4 ? (background.w() - colors[3].w()) : 1.0f) * 255))
};
// Setup blending operations
BlendMaskStateMachine state(m_context, m_testCtx.getLog(), maxDrawBuffers);
for (int i = 0; i < maxDrawBuffers; ++i)
{
switch (i % 4)
{
case 0:
// GL_MIN
state.SetEnablei(i);
state.SetBlendEquationSeparatei(i, GL_MIN, GL_MAX);
state.SetBlendFunci(i, GL_ONE, GL_ONE);
break;
case 1:
// GL_FUNC_ADD
state.SetEnablei(i);
state.SetBlendEquationi(i, GL_FUNC_ADD);
state.SetBlendFuncSeparatei(i, GL_ZERO, GL_ONE, GL_ONE, GL_ZERO);
break;
case 2:
// GL_FUNC_SUBTRACT
state.SetEnablei(i);
state.SetBlendEquationi(i, GL_FUNC_SUBTRACT);
state.SetBlendFunci(i, GL_SRC_ALPHA, GL_DST_ALPHA);
break;
case 3:
// GL_FUNC_REVERSE_SUBTRACT
state.SetEnablei(i);
state.SetBlendEquationi(i, GL_FUNC_REVERSE_SUBTRACT);
state.SetBlendFunci(i, GL_ONE, GL_ONE);
break;
}
}
// Prepare shader programs and draw fullscreen quad
glu::ShaderProgram program(m_context.getRenderContext(),
glu::makeVtxFragSources(GenVS().c_str(), GenFS(maxDrawBuffers).c_str()));
if (!program.isOk())
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Could not create shader program");
return STOP;
}
gl.useProgram(program.getProgram());
glw::GLuint positionLocation = gl.getAttribLocation(program.getProgram(), "position");
tcu::Vec3 vertices[] = {
tcu::Vec3(-1.0f, -1.0f, 0.0f), tcu::Vec3(1.0f, -1.0f, 0.0f), tcu::Vec3(-1.0f, 1.0f, 0.0f),
tcu::Vec3(1.0f, 1.0f, 0.0f), tcu::Vec3(-1.0f, 1.0f, 0.0f), tcu::Vec3(1.0f, -1.0f, 0.0f)
};
gl.vertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
gl.enableVertexAttribArray(positionLocation);
for (int i = 0; i < maxDrawBuffers; ++i)
{
std::ostringstream os;
os << "c" << i;
// i.e.: glUniform4fv(glGetUniformLocation(m_program, "c0"), 1, &colors[i].r);
gl.uniform4fv(gl.getUniformLocation(program.getProgram(), os.str().c_str()), 1, &colors[i % 4][0]);
}
gl.drawArrays(GL_TRIANGLES, 0, 6);
// Read buffer colors and validate proper blending behaviour
bool success = true;
tcu::RGBA epsilon = GetEpsilon();
for (int i = 0; i < maxDrawBuffers; ++i)
{
gl.readBuffer(GL_COLOR_ATTACHMENT0 + i);
tcu::TextureLevel textureLevel(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
kSize, kSize);
glu::readPixels(m_context.getRenderContext(), 0, 0, textureLevel.getAccess());
if (!VerifyImg(textureLevel, expected[i % 4], epsilon))
{
m_testCtx.getLog() << tcu::TestLog::Message << "Blending error in texture format " << format
<< " occurred for draw buffer #" << i << "\n"
<< tcu::TestLog::EndMessage;
m_testCtx.getLog() << tcu::TestLog::Image("Result", "Rendered result image", textureLevel.getAccess());
success = false;
}
}
gl.disable(GL_BLEND);
gl.useProgram(0);
gl.bindTexture(GL_TEXTURE_2D_ARRAY, 0);
gl.deleteTextures(1, &tex);
releaseFramebuffer();
// Check for error
glw::GLenum error_code = gl.getError();
if (error_code != GL_NO_ERROR)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Some functions generated error");
formatId = 0;
return STOP;
}
if (!success)
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Blending error occurred");
formatId = 0;
return STOP;
}
else
{
++formatId;
if (formatId < (sizeof(BlendFormats) / sizeof(BlendFormats[0])))
{
return CONTINUE;
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
formatId = 0;
return STOP;
}
}
}
std::string DrawBuffersIndexedBlending::GenVS()
{
std::ostringstream os;
os << "#version 300 es \n"
"precision highp float; \n"
"precision highp int; \n"
"layout(location = 0) in vec4 position; \n"
"void main() { \n"
" gl_Position = position; \n"
"}";
return os.str();
}
std::string DrawBuffersIndexedBlending::GenFS(int maxDrawBuffers)
{
std::ostringstream os;
os << "#version 300 es \n"
"precision highp float; \n"
"precision highp int; \n";
for (int i = 0; i < maxDrawBuffers; ++i)
{
os << "\nlayout(location = " << i << ") out vec4 color" << i << ";";
}
for (int i = 0; i < maxDrawBuffers; ++i)
{
os << "\nuniform vec4 c" << i << ";";
}
os << "\nvoid main() {";
for (int i = 0; i < maxDrawBuffers; ++i)
{
os << "\n color" << i << " = c" << i << ";";
}
os << "\n}";
return os.str();
}
unsigned int DrawBuffersIndexedBlending::NumComponents(glw::GLenum format)
{
switch (format)
{
case GL_R8:
case GL_R8I:
case GL_R8UI:
case GL_R16I:
case GL_R16UI:
case GL_R32I:
case GL_R32UI:
return 1;
case GL_RG8:
case GL_RG8I:
case GL_RG8UI:
case GL_RG16I:
case GL_RG16UI:
case GL_RG32I:
case GL_RG32UI:
return 2;
case GL_RGB8:
case GL_RGB565:
return 3;
case GL_RGBA4:
case GL_RGB5_A1:
case GL_RGBA8:
case GL_RGB10_A2:
case GL_RGBA8I:
case GL_RGBA8UI:
case GL_RGBA16I:
case GL_RGBA16UI:
case GL_RGBA32I:
case GL_RGBA32UI:
return 4;
default:
return 0;
}
}
tcu::RGBA DrawBuffersIndexedBlending::GetEpsilon()
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
tcu::IVec4 bits;
tcu::UVec4 epsilon;
for (int i = 0; i < 4; ++i)
{
gl.getIntegerv(GL_RED_BITS + i, &bits[i]);
epsilon[i] = de::min(
255u, static_cast<unsigned int>(ceil(1.0 + 255.0 * (1.0 / pow(2.0, static_cast<double>(bits[i]))))));
}
return tcu::RGBA(epsilon.x(), epsilon.y(), epsilon.z(), epsilon.w());
}
bool DrawBuffersIndexedBlending::VerifyImg(const tcu::TextureLevel& textureLevel, tcu::RGBA expectedColor,
tcu::RGBA epsilon)
{
for (int y = 0; y < textureLevel.getHeight(); ++y)
{
for (int x = 0; x < textureLevel.getWidth(); ++x)
{
tcu::RGBA pixel(textureLevel.getAccess().getPixel(x, y));
if (!tcu::compareThreshold(pixel, expectedColor, epsilon))
{
m_testCtx.getLog() << tcu::TestLog::Message << "Expected value: " << expectedColor << "\n"
<< "Read value: " << pixel << "\n"
<< "Epsilon: " << epsilon << tcu::TestLog::EndMessage;
return false;
}
}
}
return true;
}
} // namespace glcts