/*-------------------------------------------------------------------------
* 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 Special float stress tests.
*//*--------------------------------------------------------------------*/
#include "es3sSpecialFloatTests.hpp"
#include "gluRenderContext.hpp"
#include "gluShaderProgram.hpp"
#include "gluPixelTransfer.hpp"
#include "gluStrUtil.hpp"
#include "gluContextInfo.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuSurface.hpp"
#include "tcuTestLog.hpp"
#include "tcuVectorUtil.hpp"
#include "deStringUtil.hpp"
#include "deMath.h"
#include "deRandom.hpp"
#include <limits>
#include <sstream>
using namespace glw;
namespace deqp
{
namespace gles3
{
namespace Stress
{
namespace
{
static const int TEST_CANVAS_SIZE = 256;
static const int TEST_TEXTURE_SIZE = 128;
static const int TEST_TEXTURE_CUBE_SIZE = 32;
static const deUint32 s_specialFloats[] =
{
0x00000000, // zero
0x80000000, // negative zero
0x3F800000, // one
0xBF800000, // negative one
0x00800000, // minimum positive normalized value
0x80800000, // maximum negative normalized value
0x00000001, // minimum positive denorm value
0x80000001, // maximum negative denorm value
0x7F7FFFFF, // maximum finite value.
0xFF7FFFFF, // minimum finite value.
0x7F800000, // inf
0xFF800000, // -inf
0x34000000, // epsilon
0xB4000000, // negative epsilon
0x7FC00000, // quiet_NaN
0xFFC00000, // negative quiet_NaN
0x7FC00001, // signaling_NaN
0xFFC00001, // negative signaling_NaN
0x7FEAAAAA, // quiet payloaded NaN (payload of repeated pattern of 101010...)
0xFFEAAAAA, // negative quiet payloaded NaN ( .. )
0x7FAAAAAA, // signaling payloaded NaN ( .. )
0xFFAAAAAA, // negative signaling payloaded NaN ( .. )
};
static const char* const s_colorPassthroughFragmentShaderSource = "#version 300 es\n"
"layout(location = 0) out mediump vec4 fragColor;\n"
"in mediump vec4 v_out;\n"
"void main ()\n"
"{\n"
" fragColor = v_out;\n"
"}\n";
static const char* const s_attrPassthroughVertexShaderSource = "#version 300 es\n"
"in highp vec4 a_pos;\n"
"in highp vec4 a_attr;\n"
"out highp vec4 v_attr;\n"
"void main ()\n"
"{\n"
" v_attr = a_attr;\n"
" gl_Position = a_pos;\n"
"}\n";
class RenderCase : public TestCase
{
public:
enum RenderTargetType
{
RENDERTARGETTYPE_SCREEN,
RENDERTARGETTYPE_FBO
};
RenderCase (Context& context, const char* name, const char* desc, RenderTargetType renderTargetType = RENDERTARGETTYPE_SCREEN);
virtual ~RenderCase (void);
virtual void init (void);
virtual void deinit (void);
protected:
bool checkResultImage (const tcu::Surface& result);
bool drawTestPattern (bool useTexture);
virtual std::string genVertexSource (void) const = 0;
virtual std::string genFragmentSource (void) const = 0;
const glu::ShaderProgram* m_program;
const RenderTargetType m_renderTargetType;
};
RenderCase::RenderCase (Context& context, const char* name, const char* desc, RenderTargetType renderTargetType)
: TestCase (context, name, desc)
, m_program (DE_NULL)
, m_renderTargetType (renderTargetType)
{
}
RenderCase::~RenderCase (void)
{
deinit();
}
void RenderCase::init (void)
{
const int width = m_context.getRenderTarget().getWidth();
const int height = m_context.getRenderTarget().getHeight();
// check target size
if (m_renderTargetType == RENDERTARGETTYPE_SCREEN)
{
if (width < TEST_CANVAS_SIZE || height < TEST_CANVAS_SIZE)
throw tcu::NotSupportedError(std::string("Render target size must be at least ") + de::toString(TEST_CANVAS_SIZE) + "x" + de::toString(TEST_CANVAS_SIZE));
}
else if (m_renderTargetType == RENDERTARGETTYPE_FBO)
{
GLint maxTexSize = 0;
m_context.getRenderContext().getFunctions().getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize);
if (maxTexSize < TEST_CANVAS_SIZE)
throw tcu::NotSupportedError(std::string("GL_MAX_TEXTURE_SIZE must be at least ") + de::toString(TEST_CANVAS_SIZE));
}
else
DE_ASSERT(false);
// gen shader
m_testCtx.getLog() << tcu::TestLog::Message << "Creating test shader." << tcu::TestLog::EndMessage;
m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(genVertexSource()) << glu::FragmentSource(genFragmentSource()));
m_testCtx.getLog() << *m_program;
if (!m_program->isOk())
throw tcu::TestError("shader compile failed");
}
void RenderCase::deinit (void)
{
if (m_program)
{
delete m_program;
m_program = DE_NULL;
}
}
bool RenderCase::checkResultImage (const tcu::Surface& result)
{
tcu::Surface errorMask (TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
bool error = false;
m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output image." << tcu::TestLog::EndMessage;
for (int y = 0; y < TEST_CANVAS_SIZE; ++y)
for (int x = 0; x < TEST_CANVAS_SIZE; ++x)
{
const tcu::RGBA col = result.getPixel(x, y);
if (col.getGreen() == 255)
errorMask.setPixel(x, y, tcu::RGBA::green());
else
{
errorMask.setPixel(x, y, tcu::RGBA::red());
error = true;
}
}
if (error)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Result image has missing or invalid pixels" << tcu::TestLog::EndMessage;
m_testCtx.getLog()
<< tcu::TestLog::ImageSet("Results", "Result verification")
<< tcu::TestLog::Image("Result", "Result", result)
<< tcu::TestLog::Image("Error mask", "Error mask", errorMask)
<< tcu::TestLog::EndImageSet;
}
else
{
m_testCtx.getLog()
<< tcu::TestLog::ImageSet("Results", "Result verification")
<< tcu::TestLog::Image("Result", "Result", result)
<< tcu::TestLog::EndImageSet;
}
return !error;
}
bool RenderCase::drawTestPattern (bool useTexture)
{
static const tcu::Vec4 fullscreenQuad[4] =
{
tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
tcu::Vec4( 1.0f, -1.0f, 0.0f, 1.0f),
tcu::Vec4( 1.0f, 1.0f, 0.0f, 1.0f),
};
const char* const vertexSource = "#version 300 es\n"
"in highp vec4 a_pos;\n"
"out mediump vec4 v_position;\n"
"void main ()\n"
"{\n"
" v_position = a_pos;\n"
" gl_Position = a_pos;\n"
"}\n";
const char* const fragmentSourceNoTex = "#version 300 es\n"
"layout(location = 0) out mediump vec4 fragColor;\n"
"in mediump vec4 v_position;\n"
"void main ()\n"
"{\n"
" fragColor = vec4((v_position.x + 1.0) * 0.5, 1.0, 1.0, 1.0);\n"
"}\n";
const char* const fragmentSourceTex = "#version 300 es\n"
"layout(location = 0) out mediump vec4 fragColor;\n"
"uniform mediump sampler2D u_sampler;\n"
"in mediump vec4 v_position;\n"
"void main ()\n"
"{\n"
" fragColor = texture(u_sampler, v_position.xy);\n"
"}\n";
const char* const fragmentSource = (useTexture) ? (fragmentSourceTex) : (fragmentSourceNoTex);
const tcu::RGBA formatThreshold = m_context.getRenderTarget().getPixelFormat().getColorThreshold();
tcu::Surface resultImage (TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
tcu::Surface errorMask (TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
bool error = false;
m_testCtx.getLog() << tcu::TestLog::Message << "Drawing a test pattern to detect " << ((useTexture) ? ("texture sampling") : ("")) << " side-effects." << tcu::TestLog::EndMessage;
// draw pattern
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const glu::ShaderProgram patternProgram (m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexSource) << glu::FragmentSource(fragmentSource));
const GLint positionLoc = gl.getAttribLocation(patternProgram.getProgram(), "a_pos");
GLuint textureID = 0;
if (useTexture)
{
const int textureSize = 32;
std::vector<tcu::Vector<deUint8, 4> > buffer(textureSize*textureSize);
for (int x = 0; x < textureSize; ++x)
for (int y = 0; y < textureSize; ++y)
{
// sum of two axis aligned gradients. Each gradient is 127 at the edges and 0 at the center.
// pattern is symmetric (x and y) => no discontinuity near boundary => no need to worry of results with LINEAR filtering near boundaries
const deUint8 redComponent = (deUint8)de::clamp(de::abs((float)x / (float)textureSize - 0.5f) * 255.0f + de::abs((float)y / (float)textureSize - 0.5f) * 255.0f, 0.0f, 255.0f);
buffer[x * textureSize + y] = tcu::Vector<deUint8, 4>(redComponent, 255, 255, 255);
}
gl.genTextures(1, &textureID);
gl.bindTexture(GL_TEXTURE_2D, textureID);
gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, textureSize, textureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer[0].getPtr());
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.clear(GL_COLOR_BUFFER_BIT);
gl.viewport(0, 0, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
gl.useProgram(patternProgram.getProgram());
if (useTexture)
gl.uniform1i(gl.getUniformLocation(patternProgram.getProgram(), "u_sampler"), 0);
gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, &fullscreenQuad[0]);
gl.enableVertexAttribArray(positionLoc);
gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
gl.disableVertexAttribArray(positionLoc);
gl.useProgram(0);
gl.finish();
GLU_EXPECT_NO_ERROR(gl.getError(), "RenderCase::drawTestPattern");
if (textureID)
gl.deleteTextures(1, &textureID);
glu::readPixels(m_context.getRenderContext(), 0, 0, resultImage.getAccess());
}
// verify pattern
for (int y = 0; y < TEST_CANVAS_SIZE; ++y)
for (int x = 0; x < TEST_CANVAS_SIZE; ++x)
{
const float texGradientPosX = deFloatFrac((float)x * 2.0f / (float)TEST_CANVAS_SIZE);
const float texGradientPosY = deFloatFrac((float)y * 2.0f / (float)TEST_CANVAS_SIZE);
const deUint8 texRedComponent = (deUint8)de::clamp(de::abs(texGradientPosX - 0.5f) * 255.0f + de::abs(texGradientPosY - 0.5f) * 255.0f, 0.0f, 255.0f);
const tcu::RGBA refColTexture = tcu::RGBA(texRedComponent, 255, 255, 255);
const tcu::RGBA refColGradient = tcu::RGBA((int)((float)x / (float)TEST_CANVAS_SIZE * 255.0f), 255, 255, 255);
const tcu::RGBA& refCol = (useTexture) ? (refColTexture) : (refColGradient);
const int colorThreshold = 10;
const tcu::RGBA col = resultImage.getPixel(x, y);
const tcu::IVec4 colorDiff = tcu::abs(col.toIVec() - refCol.toIVec());
if (colorDiff.x() > formatThreshold.getRed() + colorThreshold ||
colorDiff.y() > formatThreshold.getGreen() + colorThreshold ||
colorDiff.z() > formatThreshold.getBlue() + colorThreshold)
{
errorMask.setPixel(x, y, tcu::RGBA::red());
error = true;
}
else
errorMask.setPixel(x, y, tcu::RGBA::green());
}
// report error
if (error)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Test pattern has missing/invalid pixels" << tcu::TestLog::EndMessage;
m_testCtx.getLog()
<< tcu::TestLog::ImageSet("Results", "Result verification")
<< tcu::TestLog::Image("Result", "Result", resultImage)
<< tcu::TestLog::Image("Error mask", "Error mask", errorMask)
<< tcu::TestLog::EndImageSet;
}
else
m_testCtx.getLog() << tcu::TestLog::Message << "No side-effects found." << tcu::TestLog::EndMessage;
return !error;
}
class FramebufferRenderCase : public RenderCase
{
public:
enum FrameBufferType
{
FBO_DEFAULT = 0,
FBO_RGBA4,
FBO_RGB5_A1,
FBO_RGB565,
FBO_RGBA8,
FBO_RGB10_A2,
FBO_RGBA_FLOAT16,
FBO_RGBA_FLOAT32,
FBO_LAST
};
FramebufferRenderCase (Context& context, const char* name, const char* desc, FrameBufferType fboType);
virtual ~FramebufferRenderCase (void);
virtual void init (void);
virtual void deinit (void);
IterateResult iterate (void);
virtual void testFBO (void) = DE_NULL;
protected:
const FrameBufferType m_fboType;
private:
GLuint m_texID;
GLuint m_fboID;
};
FramebufferRenderCase::FramebufferRenderCase (Context& context, const char* name, const char* desc, FrameBufferType fboType)
: RenderCase (context, name, desc, (fboType == FBO_DEFAULT) ? (RENDERTARGETTYPE_SCREEN) : (RENDERTARGETTYPE_FBO))
, m_fboType (fboType)
, m_texID (0)
, m_fboID (0)
{
DE_ASSERT(m_fboType < FBO_LAST);
}
FramebufferRenderCase::~FramebufferRenderCase (void)
{
deinit();
}
void FramebufferRenderCase::init (void)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
// check requirements
if (m_fboType == FBO_RGBA_FLOAT16)
{
if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_color_buffer_half_float") &&
!m_context.getContextInfo().isExtensionSupported("GL_EXT_color_buffer_float"))
throw tcu::NotSupportedError("Color renderable half float texture required.");
}
else if (m_fboType == FBO_RGBA_FLOAT32)
{
if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_color_buffer_float"))
throw tcu::NotSupportedError("Color renderable float texture required.");
}
// gen shader
RenderCase::init();
// create render target
if (m_fboType == FBO_DEFAULT)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Using default framebuffer." << tcu::TestLog::EndMessage;
}
else
{
GLuint internalFormat = 0;
GLuint format = 0;
GLuint type = 0;
switch (m_fboType)
{
case FBO_RGBA4: internalFormat = GL_RGBA4; format = GL_RGBA; type = GL_UNSIGNED_SHORT_4_4_4_4; break;
case FBO_RGB5_A1: internalFormat = GL_RGB5_A1; format = GL_RGBA; type = GL_UNSIGNED_SHORT_5_5_5_1; break;
case FBO_RGB565: internalFormat = GL_RGB565; format = GL_RGB; type = GL_UNSIGNED_SHORT_5_6_5; break;
case FBO_RGBA8: internalFormat = GL_RGBA8; format = GL_RGBA; type = GL_UNSIGNED_BYTE; break;
case FBO_RGB10_A2: internalFormat = GL_RGB10_A2; format = GL_RGBA; type = GL_UNSIGNED_INT_2_10_10_10_REV; break;
case FBO_RGBA_FLOAT16: internalFormat = GL_RGBA16F; format = GL_RGBA; type = GL_HALF_FLOAT; break;
case FBO_RGBA_FLOAT32: internalFormat = GL_RGBA32F; format = GL_RGBA; type = GL_FLOAT; break;
default:
DE_ASSERT(false);
break;
}
m_testCtx.getLog() << tcu::TestLog::Message
<< "Creating fbo. Texture internalFormat = " << glu::getTextureFormatStr(internalFormat)
<< ", format = " << glu::getTextureFormatStr(format)
<< ", type = " << glu::getTypeStr(type)
<< tcu::TestLog::EndMessage;
// gen texture
gl.genTextures(1, &m_texID);
gl.bindTexture(GL_TEXTURE_2D, m_texID);
gl.texImage2D(GL_TEXTURE_2D, 0, internalFormat, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE, 0, format, type, DE_NULL);
GLU_EXPECT_NO_ERROR(gl.getError(), "texture init");
// gen fbo
gl.genFramebuffers(1, &m_fboID);
gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboID);
gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texID, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "fbo init");
if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
throw tcu::NotSupportedError("could not create fbo for testing.");
}
}
void FramebufferRenderCase::deinit (void)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_texID)
{
gl.deleteTextures(1, &m_texID);
m_texID = 0;
}
if (m_fboID)
{
gl.deleteFramebuffers(1, &m_fboID);
m_fboID = 0;
}
}
FramebufferRenderCase::IterateResult FramebufferRenderCase::iterate (void)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
// bind fbo (or don't if we are using default)
if (m_fboID)
gl.bindFramebuffer(GL_FRAMEBUFFER, m_fboID);
// do something with special floats
testFBO();
return STOP;
}
/*--------------------------------------------------------------------*//*!
* \brief Tests special floats as vertex attributes
*
* Tests that special floats transferred to the shader using vertex
* attributes do not change the results of normal floating point
* calculations. Special floats are put to 4-vector's x and y components and
* value 1.0 is put to z and w. The resulting fragment's green channel
* should be 1.0 everywhere.
*
* After the calculation test a test pattern is drawn to detect possible
* floating point operation anomalies.
*//*--------------------------------------------------------------------*/
class VertexAttributeCase : public RenderCase
{
public:
enum Storage
{
STORAGE_BUFFER = 0,
STORAGE_CLIENT,
STORAGE_LAST
};
enum ShaderType
{
TYPE_VERTEX = 0,
TYPE_FRAGMENT,
TYPE_LAST
};
VertexAttributeCase (Context& context, const char* name, const char* desc, Storage storage, ShaderType type);
~VertexAttributeCase (void);
void init (void);
void deinit (void);
IterateResult iterate (void);
private:
std::string genVertexSource (void) const;
std::string genFragmentSource (void) const;
const Storage m_storage;
const ShaderType m_type;
GLuint m_positionVboID;
GLuint m_attribVboID;
GLuint m_elementVboID;
};
VertexAttributeCase::VertexAttributeCase (Context& context, const char* name, const char* desc, Storage storage, ShaderType type)
: RenderCase (context, name, desc)
, m_storage (storage)
, m_type (type)
, m_positionVboID (0)
, m_attribVboID (0)
, m_elementVboID (0)
{
DE_ASSERT(storage < STORAGE_LAST);
DE_ASSERT(type < TYPE_LAST);
}
VertexAttributeCase::~VertexAttributeCase (void)
{
deinit();
}
void VertexAttributeCase::init (void)
{
RenderCase::init();
// init gl resources
if (m_storage == STORAGE_BUFFER)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.genBuffers(1, &m_positionVboID);
gl.genBuffers(1, &m_attribVboID);
gl.genBuffers(1, &m_elementVboID);
}
}
void VertexAttributeCase::deinit (void)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
RenderCase::deinit();
if (m_attribVboID)
{
gl.deleteBuffers(1, &m_attribVboID);
m_attribVboID = 0;
}
if (m_positionVboID)
{
gl.deleteBuffers(1, &m_positionVboID);
m_positionVboID = 0;
}
if (m_elementVboID)
{
gl.deleteBuffers(1, &m_elementVboID);
m_elementVboID = 0;
}
}
VertexAttributeCase::IterateResult VertexAttributeCase::iterate (void)
{
// Create a [s_specialFloats] X [s_specialFloats] grid of vertices with each vertex having 2 [s_specialFloats] values
// and calculate some basic operations with the floating point values. If all goes well, nothing special should happen
std::vector<tcu::Vec4> gridVertices (DE_LENGTH_OF_ARRAY(s_specialFloats) * DE_LENGTH_OF_ARRAY(s_specialFloats));
std::vector<tcu::UVec4> gridAttributes (DE_LENGTH_OF_ARRAY(s_specialFloats) * DE_LENGTH_OF_ARRAY(s_specialFloats));
std::vector<deUint16> indices ((DE_LENGTH_OF_ARRAY(s_specialFloats) - 1) * (DE_LENGTH_OF_ARRAY(s_specialFloats) - 1) * 6);
tcu::Surface resultImage (TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
// vertices
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats); ++x)
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats); ++y)
{
const deUint32 one = 0x3F800000;
const float posX = (float)x / ((float)DE_LENGTH_OF_ARRAY(s_specialFloats) - 1.0f) * 2.0f - 1.0f; // map from [0, len(s_specialFloats) - 1] to [-1, 1]
const float posY = (float)y / ((float)DE_LENGTH_OF_ARRAY(s_specialFloats) - 1.0f) * 2.0f - 1.0f;
gridVertices[x * DE_LENGTH_OF_ARRAY(s_specialFloats) + y] = tcu::Vec4(posX, posY, 0.0f, 1.0f);
gridAttributes[x * DE_LENGTH_OF_ARRAY(s_specialFloats) + y] = tcu::UVec4(s_specialFloats[x], s_specialFloats[y], one, one);
}
// tiles
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats) - 1; ++x)
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats) - 1; ++y)
{
const int baseNdx = (x * (DE_LENGTH_OF_ARRAY(s_specialFloats) - 1) + y) * 6;
indices[baseNdx + 0] = (deUint16)((x+0) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+0));
indices[baseNdx + 1] = (deUint16)((x+1) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+1));
indices[baseNdx + 2] = (deUint16)((x+1) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+0));
indices[baseNdx + 3] = (deUint16)((x+0) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+0));
indices[baseNdx + 4] = (deUint16)((x+1) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+1));
indices[baseNdx + 5] = (deUint16)((x+0) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+1));
}
m_testCtx.getLog() << tcu::TestLog::Message << "Drawing a grid with the shader. Setting a_attr for each vertex to (special, special, 1, 1)." << tcu::TestLog::EndMessage;
// Draw grid
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const GLint positionLoc = gl.getAttribLocation(m_program->getProgram(), "a_pos");
const GLint attribLoc = gl.getAttribLocation(m_program->getProgram(), "a_attr");
if (m_storage == STORAGE_BUFFER)
{
gl.bindBuffer(GL_ARRAY_BUFFER, m_positionVboID);
gl.bufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)(gridVertices.size() * sizeof(tcu::Vec4)), &gridVertices[0], GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "VertexAttributeCase::iterate");
gl.bindBuffer(GL_ARRAY_BUFFER, m_attribVboID);
gl.bufferData(GL_ARRAY_BUFFER, (glw::GLsizeiptr)(gridAttributes.size() * sizeof(tcu::UVec4)), &gridAttributes[0], GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "VertexAttributeCase::iterate");
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementVboID);
gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, (glw::GLsizeiptr)(indices.size() * sizeof(deUint16)), &indices[0], GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "VertexAttributeCase::iterate");
}
gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.clear(GL_COLOR_BUFFER_BIT);
gl.viewport(0, 0, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
gl.useProgram(m_program->getProgram());
if (m_storage == STORAGE_BUFFER)
{
gl.bindBuffer(GL_ARRAY_BUFFER, m_positionVboID);
gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
gl.bindBuffer(GL_ARRAY_BUFFER, m_attribVboID);
gl.vertexAttribPointer(attribLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL);
gl.enableVertexAttribArray(positionLoc);
gl.enableVertexAttribArray(attribLoc);
gl.drawElements(GL_TRIANGLES, (glw::GLsizei)(indices.size()), GL_UNSIGNED_SHORT, DE_NULL);
gl.disableVertexAttribArray(positionLoc);
gl.disableVertexAttribArray(attribLoc);
gl.bindBuffer(GL_ARRAY_BUFFER, 0);
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
else if (m_storage == STORAGE_CLIENT)
{
gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, &gridVertices[0]);
gl.vertexAttribPointer(attribLoc, 4, GL_FLOAT, GL_FALSE, 0, &gridAttributes[0]);
gl.enableVertexAttribArray(positionLoc);
gl.enableVertexAttribArray(attribLoc);
gl.drawElements(GL_TRIANGLES, (glw::GLsizei)(indices.size()), GL_UNSIGNED_SHORT, &indices[0]);
gl.disableVertexAttribArray(positionLoc);
gl.disableVertexAttribArray(attribLoc);
}
else
DE_ASSERT(false);
gl.useProgram(0);
gl.finish();
GLU_EXPECT_NO_ERROR(gl.getError(), "VertexAttributeCase::iterate");
glu::readPixels(m_context.getRenderContext(), 0, 0, resultImage.getAccess());
}
// verify everywhere was drawn (all pixels have Green = 255)
if (!checkResultImage(resultImage))
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "missing or invalid fragments");
return STOP;
}
// test drawing still works
if (!drawTestPattern(false))
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "test pattern failed");
return STOP;
}
// all ok
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
std::string VertexAttributeCase::genVertexSource (void) const
{
if (m_type == TYPE_VERTEX)
return
"#version 300 es\n"
"in highp vec4 a_pos;\n"
"in highp vec4 a_attr;\n"
"out mediump vec4 v_out;\n"
"void main ()\n"
"{\n"
" highp vec2 a1 = a_attr.xz + a_attr.yw; // add\n"
" highp vec2 a2 = a_attr.xz - a_attr.yw; // sub\n"
" highp vec2 a3 = a_attr.xz * a_attr.yw; // mul\n"
" highp vec2 a4 = a_attr.xz / a_attr.yw; // div\n"
" highp vec2 a5 = a_attr.xz + a_attr.yw * a_attr.xz; // fma\n"
"\n"
" highp float green = 1.0 - abs((a1.y + a2.y + a3.y + a4.y + a5.y) - 6.0);\n"
" v_out = vec4(a1.x*a3.x + a2.x*a4.x, green, a5.x, 1.0);\n"
" gl_Position = a_pos;\n"
"}\n";
else
return s_attrPassthroughVertexShaderSource;
}
std::string VertexAttributeCase::genFragmentSource (void) const
{
if (m_type == TYPE_VERTEX)
return s_colorPassthroughFragmentShaderSource;
else
return
"#version 300 es\n"
"layout(location = 0) out mediump vec4 fragColor;\n"
"in highp vec4 v_attr;\n"
"void main ()\n"
"{\n"
" highp vec2 a1 = v_attr.xz + v_attr.yw; // add\n"
" highp vec2 a2 = v_attr.xz - v_attr.yw; // sub\n"
" highp vec2 a3 = v_attr.xz * v_attr.yw; // mul\n"
" highp vec2 a4 = v_attr.xz / v_attr.yw; // div\n"
" highp vec2 a5 = v_attr.xz + v_attr.yw * v_attr.xz; // fma\n"
" highp vec2 a6 = dFdx(v_attr.xz);\n"
"\n"
" highp float green = 1.0 - abs((a1.y + a2.y + a3.y + a4.y + a5.y + a6.y) - 6.0);\n"
" fragColor = vec4(a1.x*a3.x + a2.x*a4.x, green, a5.x+a6.x, 1.0);\n"
"}\n";
}
/*--------------------------------------------------------------------*//*!
* \brief Tests special floats as uniforms
*
* Tests that special floats transferred to the shader as uniforms do
* not change the results of normal floating point calculations. Special
* floats are put to 4-vector's x and y components and value 1.0 is put to
* z and w. The resulting fragment's green channel should be 1.0
* everywhere.
*
* After the calculation test a test pattern is drawn to detect possible
* floating point operation anomalies.
*//*--------------------------------------------------------------------*/
class UniformCase : public RenderCase
{
public:
enum ShaderType
{
TYPE_VERTEX = 0,
TYPE_FRAGMENT,
};
UniformCase (Context& context, const char* name, const char* desc, ShaderType type);
~UniformCase (void);
void init (void);
void deinit (void);
IterateResult iterate (void);
private:
std::string genVertexSource (void) const;
std::string genFragmentSource (void) const;
const ShaderType m_type;
};
UniformCase::UniformCase (Context& context, const char* name, const char* desc, ShaderType type)
: RenderCase (context, name, desc)
, m_type (type)
{
}
UniformCase::~UniformCase (void)
{
deinit();
}
void UniformCase::init (void)
{
RenderCase::init();
}
void UniformCase::deinit (void)
{
RenderCase::deinit();
}
UniformCase::IterateResult UniformCase::iterate (void)
{
// Create a [s_specialFloats] X [s_specialFloats] grid of tile with each tile having 2 [s_specialFloats] values
// and calculate some basic operations with the floating point values. If all goes well, nothing special should happen
std::vector<tcu::Vec4> gridVertices ((DE_LENGTH_OF_ARRAY(s_specialFloats) + 1) * (DE_LENGTH_OF_ARRAY(s_specialFloats) + 1));
std::vector<deUint16> indices (DE_LENGTH_OF_ARRAY(s_specialFloats) * DE_LENGTH_OF_ARRAY(s_specialFloats) * 6);
tcu::Surface resultImage (TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
// vertices
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats) + 1; ++x)
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats) + 1; ++y)
{
const float posX = (float)x / (float)DE_LENGTH_OF_ARRAY(s_specialFloats) * 2.0f - 1.0f; // map from [0, len(s_specialFloats) ] to [-1, 1]
const float posY = (float)y / (float)DE_LENGTH_OF_ARRAY(s_specialFloats) * 2.0f - 1.0f;
gridVertices[x * (DE_LENGTH_OF_ARRAY(s_specialFloats)+1) + y] = tcu::Vec4(posX, posY, 0.0f, 1.0f);
}
// tiles
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats); ++x)
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats); ++y)
{
const int baseNdx = (x * (DE_LENGTH_OF_ARRAY(s_specialFloats)) + y) * 6;
indices[baseNdx + 0] = (deUint16)((x+0) * (DE_LENGTH_OF_ARRAY(s_specialFloats) + 1) + (y+0));
indices[baseNdx + 1] = (deUint16)((x+1) * (DE_LENGTH_OF_ARRAY(s_specialFloats) + 1) + (y+1));
indices[baseNdx + 2] = (deUint16)((x+1) * (DE_LENGTH_OF_ARRAY(s_specialFloats) + 1) + (y+0));
indices[baseNdx + 3] = (deUint16)((x+0) * (DE_LENGTH_OF_ARRAY(s_specialFloats) + 1) + (y+0));
indices[baseNdx + 4] = (deUint16)((x+1) * (DE_LENGTH_OF_ARRAY(s_specialFloats) + 1) + (y+1));
indices[baseNdx + 5] = (deUint16)((x+0) * (DE_LENGTH_OF_ARRAY(s_specialFloats) + 1) + (y+1));
}
m_testCtx.getLog() << tcu::TestLog::Message << "Drawing a grid with the shader. Setting u_special for vertex each tile to (special, special, 1, 1)." << tcu::TestLog::EndMessage;
// Draw grid
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const GLint positionLoc = gl.getAttribLocation(m_program->getProgram(), "a_pos");
const GLint specialLoc = gl.getUniformLocation(m_program->getProgram(), "u_special");
gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.clear(GL_COLOR_BUFFER_BIT);
gl.viewport(0, 0, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
gl.useProgram(m_program->getProgram());
gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, &gridVertices[0]);
gl.enableVertexAttribArray(positionLoc);
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats); ++x)
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats); ++y)
{
const deUint32 one = 0x3F800000;
const tcu::UVec4 uniformValue = tcu::UVec4(s_specialFloats[x], s_specialFloats[y], one, one);
const int indexIndex = (x * DE_LENGTH_OF_ARRAY(s_specialFloats) + y) * 6;
gl.uniform4fv(specialLoc, 1, (const float*)uniformValue.getPtr());
gl.drawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, &indices[indexIndex]);
}
gl.disableVertexAttribArray(positionLoc);
gl.useProgram(0);
gl.finish();
GLU_EXPECT_NO_ERROR(gl.getError(), "UniformCase::iterate");
glu::readPixels(m_context.getRenderContext(), 0, 0, resultImage.getAccess());
}
// verify everywhere was drawn (all pixels have Green = 255)
if (!checkResultImage(resultImage))
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "missing or invalid fragments");
return STOP;
}
// test drawing still works
if (!drawTestPattern(false))
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "test pattern failed");
return STOP;
}
// all ok
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
std::string UniformCase::genVertexSource (void) const
{
if (m_type == TYPE_VERTEX)
return
"#version 300 es\n"
"in highp vec4 a_pos;\n"
"uniform highp vec4 u_special;\n"
"out mediump vec4 v_out;\n"
"void main ()\n"
"{\n"
" highp vec2 a1 = u_special.xz + u_special.yw; // add\n"
" highp vec2 a2 = u_special.xz - u_special.yw; // sub\n"
" highp vec2 a3 = u_special.xz * u_special.yw; // mul\n"
" highp vec2 a4 = u_special.xz / u_special.yw; // div\n"
" highp vec2 a5 = u_special.xz + u_special.yw * u_special.xz; // fma\n"
"\n"
" highp float green = 1.0 - abs((a1.y + a2.y + a3.y + a4.y + a5.y) - 6.0);\n"
" v_out = vec4(a1.x*a3.x + a2.x*a4.x, green, a5.x, 1.0);\n"
" gl_Position = a_pos;\n"
"}\n";
else
return
"#version 300 es\n"
"in highp vec4 a_pos;\n"
"void main ()\n"
"{\n"
" gl_Position = a_pos;\n"
"}\n";
}
std::string UniformCase::genFragmentSource (void) const
{
if (m_type == TYPE_VERTEX)
return s_colorPassthroughFragmentShaderSource;
else
return
"#version 300 es\n"
"layout(location = 0) out mediump vec4 fragColor;\n"
"uniform highp vec4 u_special;\n"
"void main ()\n"
"{\n"
" highp vec2 a1 = u_special.xz + u_special.yw; // add\n"
" highp vec2 a2 = u_special.xz - u_special.yw; // sub\n"
" highp vec2 a3 = u_special.xz * u_special.yw; // mul\n"
" highp vec2 a4 = u_special.xz / u_special.yw; // div\n"
" highp vec2 a5 = u_special.xz + u_special.yw * u_special.xz; // fma\n"
" highp vec2 a6 = mod(u_special.xz, u_special.yw);\n"
" highp vec2 a7 = mix(u_special.xz, u_special.yw, a6);\n"
"\n"
" highp float green = 1.0 - abs((a1.y + a2.y + a3.y + a4.y + a5.y + a6.y + a7.y) - 7.0);\n"
" fragColor = vec4(a1.x*a3.x, green, a5.x*a4.x + a2.x*a7.x, 1.0);\n"
"}\n";
}
/*--------------------------------------------------------------------*//*!
* \brief Tests special floats in floating point textures
*
* Tests that sampling special floats from a floating point texture
* does not affect the values of other color components of the sample. Test
* samples a RG texture with R channel filled with special floats and G
* channel filled with test values.
*
* Tests that linear sampling using compare mode = COMPARE_REF_TO_TEXTURE
* of a floating point depth texture containing special floating point
* values does not produce values outside the [0, 1] range.
*
* After the calculation test a test pattern is drawn to detect possible
* texture sampling anomalies.
*//*--------------------------------------------------------------------*/
class TextureCase : public RenderCase
{
public:
enum ShaderType
{
TYPE_VERTEX = 0,
TYPE_FRAGMENT,
TYPE_LAST
};
enum TextureType
{
TEXTURE_FLOAT = 0,
TEXTURE_DEPTH,
TEXTURE_LAST
};
enum UploadType
{
UPLOAD_CLIENT = 0,
UPLOAD_PBO,
UPLOAD_LAST
};
TextureCase (Context& context, const char* name, const char* desc, ShaderType type, TextureType texType, UploadType uploadType);
~TextureCase (void);
void init (void);
void deinit (void);
IterateResult iterate (void);
private:
std::string genVertexSource (void) const;
std::string genFragmentSource (void) const;
const ShaderType m_type;
const TextureType m_textureType;
const UploadType m_uploadType;
GLuint m_textureID;
GLuint m_pboID;
};
TextureCase::TextureCase (Context& context, const char* name, const char* desc, ShaderType type, TextureType texType, UploadType uploadType)
: RenderCase (context, name, desc)
, m_type (type)
, m_textureType (texType)
, m_uploadType (uploadType)
, m_textureID (0)
, m_pboID (0)
{
DE_ASSERT(type < TYPE_LAST);
DE_ASSERT(texType < TEXTURE_LAST);
DE_ASSERT(uploadType < UPLOAD_LAST);
}
TextureCase::~TextureCase (void)
{
deinit();
}
void TextureCase::init (void)
{
// requirements
{
GLint maxTextureSize = 0;
m_context.getRenderContext().getFunctions().getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
if (maxTextureSize < TEST_TEXTURE_SIZE)
throw tcu::NotSupportedError(std::string("GL_MAX_TEXTURE_SIZE must be at least ") + de::toString(TEST_TEXTURE_SIZE));
}
RenderCase::init();
// gen texture
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
de::Random rnd (12345);
gl.genTextures(1, &m_textureID);
gl.bindTexture(GL_TEXTURE_2D, m_textureID);
if (m_uploadType == UPLOAD_PBO)
{
gl.genBuffers(1, &m_pboID);
gl.bindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pboID);
GLU_EXPECT_NO_ERROR(gl.getError(), "TextureCase::init");
}
if (m_textureType == TEXTURE_FLOAT)
{
std::vector<deUint32> texData (TEST_TEXTURE_SIZE*TEST_TEXTURE_SIZE*2);
const deUint32* dataPtr = (m_uploadType == UPLOAD_CLIENT) ? (&texData[0]) : (DE_NULL);
m_testCtx.getLog() << tcu::TestLog::Message << "Creating a 2D 2-component float texture. Pixel contents are of form (special, 1)." << tcu::TestLog::EndMessage;
// set green channel to 1.0
for (int x = 0; x < TEST_TEXTURE_SIZE; ++x)
for (int y = 0; y < TEST_TEXTURE_SIZE; ++y)
{
texData[(x * TEST_TEXTURE_SIZE + y) * 2 + 0] = rnd.choose<deUint32>(DE_ARRAY_BEGIN(s_specialFloats), DE_ARRAY_END(s_specialFloats));
texData[(x * TEST_TEXTURE_SIZE + y) * 2 + 1] = 0x3F800000; // one
}
if (m_uploadType == UPLOAD_PBO)
gl.bufferData(GL_PIXEL_UNPACK_BUFFER, (glw::GLsizeiptr)(texData.size() * sizeof(deUint32)), &texData[0], GL_STATIC_DRAW);
gl.texImage2D(GL_TEXTURE_2D, 0, GL_RG32F, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, 0, GL_RG, GL_FLOAT, dataPtr);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
GLU_EXPECT_NO_ERROR(gl.getError(), "TextureCase::init");
}
else if (m_textureType == TEXTURE_DEPTH)
{
std::vector<deUint32> texData (TEST_TEXTURE_SIZE*TEST_TEXTURE_SIZE);
const deUint32* dataPtr = (m_uploadType == UPLOAD_CLIENT) ? (&texData[0]) : (DE_NULL);
m_testCtx.getLog() << tcu::TestLog::Message
<< "Creating a 2D depth texture and filling it with special floating point values.\n"
<< " TEXTURE_COMPARE_MODE = COMPARE_REF_TO_TEXTURE"
<< tcu::TestLog::EndMessage;
for (int x = 0; x < TEST_TEXTURE_SIZE; ++x)
for (int y = 0; y < TEST_TEXTURE_SIZE; ++y)
texData[x * TEST_TEXTURE_SIZE + y] = rnd.choose<deUint32>(DE_ARRAY_BEGIN(s_specialFloats), DE_ARRAY_END(s_specialFloats));
if (m_uploadType == UPLOAD_PBO)
gl.bufferData(GL_PIXEL_UNPACK_BUFFER, (glw::GLsizeiptr)(texData.size() * sizeof(deUint32)), &texData[0], GL_STATIC_DRAW);
gl.texImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, 0, GL_DEPTH_COMPONENT, GL_FLOAT, dataPtr);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GLU_EXPECT_NO_ERROR(gl.getError(), "TextureCase::init");
}
else
DE_ASSERT(false);
if (m_uploadType == UPLOAD_PBO)
{
m_testCtx.getLog() << tcu::TestLog::Message << "PBO used for image upload." << tcu::TestLog::EndMessage;
gl.bindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
gl.deleteBuffers(1, &m_pboID);
m_pboID = 0;
}
}
}
void TextureCase::deinit (void)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
RenderCase::deinit();
if (m_textureID)
{
gl.deleteTextures(1, &m_textureID);
m_textureID = 0;
}
if (m_pboID)
{
gl.deleteBuffers(1, &m_pboID);
m_pboID = 0;
}
}
TextureCase::IterateResult TextureCase::iterate (void)
{
// Draw a grid and texture it with a floating point texture containing special values. If all goes well, nothing special should happen
const int gridSize = 16;
std::vector<tcu::Vec4> gridVertices (gridSize * gridSize);
std::vector<tcu::Vec2> gridTexCoords (gridSize * gridSize);
std::vector<deUint16> indices ((gridSize - 1) * (gridSize - 1) * 6);
tcu::Surface resultImage (TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
// vertices
for (int x = 0; x < gridSize; ++x)
for (int y = 0; y < gridSize; ++y)
{
const float posX = (float)x / ((float)gridSize - 1.0f) * 2.0f - 1.0f; // map from [0, gridSize - 1] to [-1, 1]
const float posY = (float)y / ((float)gridSize - 1.0f) * 2.0f - 1.0f;
const float texCoordX = deFloatPow(2.0f, (float)x - (float)gridSize / 2.0f);
const float texCoordY = deFloatPow(2.0f, (float)y - (float)gridSize / 2.0f);
gridVertices[x * gridSize + y] = tcu::Vec4(posX, posY, 0.0f, 1.0f);
gridTexCoords[x * gridSize + y] = tcu::Vec2(texCoordX, texCoordY);
}
// tiles
for (int x = 0; x < gridSize - 1; ++x)
for (int y = 0; y < gridSize - 1; ++y)
{
const int baseNdx = (x * (gridSize - 1) + y) * 6;
indices[baseNdx + 0] = (deUint16)((x+0) * gridSize + (y+0));
indices[baseNdx + 1] = (deUint16)((x+1) * gridSize + (y+1));
indices[baseNdx + 2] = (deUint16)((x+1) * gridSize + (y+0));
indices[baseNdx + 3] = (deUint16)((x+0) * gridSize + (y+0));
indices[baseNdx + 4] = (deUint16)((x+1) * gridSize + (y+1));
indices[baseNdx + 5] = (deUint16)((x+0) * gridSize + (y+1));
}
m_testCtx.getLog() << tcu::TestLog::Message << "Drawing a textured grid with the shader." << tcu::TestLog::EndMessage;
// Draw grid
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const GLint positionLoc = gl.getAttribLocation(m_program->getProgram(), "a_pos");
const GLint texCoordLoc = gl.getAttribLocation(m_program->getProgram(), "a_attr");
const GLint samplerLoc = gl.getUniformLocation(m_program->getProgram(), "u_sampler");
gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.clear(GL_COLOR_BUFFER_BIT);
gl.viewport(0, 0, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
gl.useProgram(m_program->getProgram());
gl.uniform1i(samplerLoc, 0);
gl.bindTexture(GL_TEXTURE_2D, m_textureID);
gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, &gridVertices[0]);
gl.vertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, &gridTexCoords[0]);
gl.enableVertexAttribArray(positionLoc);
gl.enableVertexAttribArray(texCoordLoc);
gl.drawElements(GL_TRIANGLES, (glw::GLsizei)(indices.size()), GL_UNSIGNED_SHORT, &indices[0]);
gl.disableVertexAttribArray(positionLoc);
gl.disableVertexAttribArray(texCoordLoc);
gl.useProgram(0);
gl.finish();
GLU_EXPECT_NO_ERROR(gl.getError(), "TextureCase::iterate");
glu::readPixels(m_context.getRenderContext(), 0, 0, resultImage.getAccess());
}
// verify everywhere was drawn (all pixels have Green = 255)
if (!checkResultImage(resultImage))
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "missing or invalid fragments");
return STOP;
}
// test drawing and textures still works
if (!drawTestPattern(true))
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "test pattern failed");
return STOP;
}
// all ok
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
std::string TextureCase::genVertexSource (void) const
{
// vertex shader is passthrough, fragment does the calculations
if (m_type == TYPE_FRAGMENT)
return s_attrPassthroughVertexShaderSource;
// vertex shader does the calculations
std::ostringstream buf;
buf << "#version 300 es\n"
"in highp vec4 a_pos;\n"
"in highp vec2 a_attr;\n"
"out mediump vec4 v_out;\n";
if (m_textureType == TEXTURE_FLOAT)
buf << "uniform highp sampler2D u_sampler;\n";
else if (m_textureType == TEXTURE_DEPTH)
buf << "uniform highp sampler2DShadow u_sampler;\n";
else
DE_ASSERT(DE_FALSE);
buf << "void main ()\n"
"{\n";
if (m_textureType == TEXTURE_FLOAT)
buf << " v_out = vec4(textureLod(u_sampler, a_attr, 0.0).rg, 1.0, 1.0);\n";
else if (m_textureType == TEXTURE_DEPTH)
buf << " highp float a1 = textureLod(u_sampler, vec3(a_attr, 0.0), 0.0);\n"
" v_out = vec4(a1, (a1 > 1.0 || a1 < 0.0) ? (0.0) : (1.0), 1.0, 1.0);\n";
else
DE_ASSERT(DE_FALSE);
buf << " gl_Position = a_pos;\n"
"}\n";
return buf.str();
}
std::string TextureCase::genFragmentSource (void) const
{
// fragment shader is passthrough
if (m_type == TYPE_VERTEX)
return s_colorPassthroughFragmentShaderSource;
// fragment shader does the calculations
std::ostringstream buf;
buf << "#version 300 es\n"
"layout(location = 0) out mediump vec4 fragColor;\n";
if (m_textureType == TEXTURE_FLOAT)
buf << "uniform highp sampler2D u_sampler;\n";
else if (m_textureType == TEXTURE_DEPTH)
buf << "uniform highp sampler2DShadow u_sampler;\n";
else
DE_ASSERT(DE_FALSE);
buf << "in highp vec4 v_attr;\n"
"void main ()\n"
"{\n";
if (m_textureType == TEXTURE_FLOAT)
buf << " highp vec2 a1 = texture(u_sampler, v_attr.xy).rg;\n"
" fragColor = vec4(a1.x, a1.y, 1.0, 1.0);\n";
else if (m_textureType == TEXTURE_DEPTH)
buf << " highp float a1 = texture(u_sampler, vec3(v_attr.xy, 0.0));\n"
" fragColor = vec4(a1, (a1 > 1.0 || a1 < 0.0) ? (0.0) : (1.0), 1.0, 1.0);\n";
else
DE_ASSERT(DE_FALSE);
buf << "}\n";
return buf.str();
}
/*--------------------------------------------------------------------*//*!
* \brief Tests special floats as texture samping arguments
*
* Tests that special floats given as texture coordinates or LOD levels
* to sampling functions do not return invalid values (values not in the
* texture). Every texel's green component is 1.0.
*
* After the calculation test a test pattern is drawn to detect possible
* texture sampling anomalies.
*//*--------------------------------------------------------------------*/
class TextureSamplerCase : public RenderCase
{
public:
enum ShaderType
{
TYPE_VERTEX = 0,
TYPE_FRAGMENT,
TYPE_LAST
};
enum TestType
{
TEST_TEX_COORD = 0,
TEST_LOD,
TEST_GRAD,
TEST_TEX_COORD_CUBE,
TEST_LAST
};
TextureSamplerCase (Context& context, const char* name, const char* desc, ShaderType type, TestType testType);
~TextureSamplerCase (void);
void init (void);
void deinit (void);
IterateResult iterate (void);
private:
std::string genVertexSource (void) const;
std::string genFragmentSource (void) const;
const ShaderType m_type;
const TestType m_testType;
GLuint m_textureID;
};
TextureSamplerCase::TextureSamplerCase (Context& context, const char* name, const char* desc, ShaderType type, TestType testType)
: RenderCase (context, name, desc)
, m_type (type)
, m_testType (testType)
, m_textureID (0)
{
DE_ASSERT(type < TYPE_LAST);
DE_ASSERT(testType < TEST_LAST);
}
TextureSamplerCase::~TextureSamplerCase (void)
{
deinit();
}
void TextureSamplerCase::init (void)
{
// requirements
{
GLint maxTextureSize = 0;
m_context.getRenderContext().getFunctions().getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
if (maxTextureSize < TEST_TEXTURE_SIZE)
throw tcu::NotSupportedError(std::string("GL_MAX_TEXTURE_SIZE must be at least ") + de::toString(TEST_TEXTURE_SIZE));
}
RenderCase::init();
// gen texture
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
std::vector<deUint8> texData (TEST_TEXTURE_SIZE*TEST_TEXTURE_SIZE*4);
de::Random rnd (12345);
gl.genTextures(1, &m_textureID);
for (int x = 0; x < TEST_TEXTURE_SIZE; ++x)
for (int y = 0; y < TEST_TEXTURE_SIZE; ++y)
{
// RGBA8, green and alpha channel are always 255 for verification
texData[(x * TEST_TEXTURE_SIZE + y) * 4 + 0] = rnd.getUint32() & 0xFF;
texData[(x * TEST_TEXTURE_SIZE + y) * 4 + 1] = 0xFF;
texData[(x * TEST_TEXTURE_SIZE + y) * 4 + 2] = rnd.getUint32() & 0xFF;
texData[(x * TEST_TEXTURE_SIZE + y) * 4 + 3] = 0xFF;
}
if (m_testType == TEST_TEX_COORD)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Creating a 2D texture with a test pattern." << tcu::TestLog::EndMessage;
gl.bindTexture(GL_TEXTURE_2D, m_textureID);
gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texData[0]);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GLU_EXPECT_NO_ERROR(gl.getError(), "TextureSamplerCase::init");
}
else if (m_testType == TEST_LOD || m_testType == TEST_GRAD)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Creating a mipmapped 2D texture with a test pattern." << tcu::TestLog::EndMessage;
gl.bindTexture(GL_TEXTURE_2D, m_textureID);
for (int level = 0; (TEST_TEXTURE_SIZE >> level); ++level)
gl.texImage2D(GL_TEXTURE_2D, level, GL_RGBA8, TEST_TEXTURE_SIZE >> level, TEST_TEXTURE_SIZE >> level, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texData[0]);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
GLU_EXPECT_NO_ERROR(gl.getError(), "TextureSamplerCase::init");
}
else if (m_testType == TEST_TEX_COORD_CUBE)
{
DE_STATIC_ASSERT(TEST_TEXTURE_CUBE_SIZE <= TEST_TEXTURE_SIZE);
static const GLenum faces[] =
{
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z,
};
m_testCtx.getLog() << tcu::TestLog::Message << "Creating a cube map with a test pattern." << tcu::TestLog::EndMessage;
gl.bindTexture(GL_TEXTURE_CUBE_MAP, m_textureID);
for (int faceNdx = 0; faceNdx < DE_LENGTH_OF_ARRAY(faces); ++faceNdx)
gl.texImage2D(faces[faceNdx], 0, GL_RGBA8, TEST_TEXTURE_CUBE_SIZE, TEST_TEXTURE_CUBE_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texData[0]);
gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl.texParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
GLU_EXPECT_NO_ERROR(gl.getError(), "TextureSamplerCase::init");
}
else
DE_ASSERT(DE_FALSE);
}
}
void TextureSamplerCase::deinit (void)
{
RenderCase::deinit();
if (m_textureID)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.deleteTextures(1, &m_textureID);
m_textureID = 0;
}
}
TextureSamplerCase::IterateResult TextureSamplerCase::iterate (void)
{
// Draw a grid and texture it with a texture and sample it using special special values. The result samples should all have the green channel at 255 as per the test image.
std::vector<tcu::Vec4> gridVertices (DE_LENGTH_OF_ARRAY(s_specialFloats) * DE_LENGTH_OF_ARRAY(s_specialFloats));
std::vector<tcu::UVec2> gridTexCoords (DE_LENGTH_OF_ARRAY(s_specialFloats) * DE_LENGTH_OF_ARRAY(s_specialFloats));
std::vector<deUint16> indices ((DE_LENGTH_OF_ARRAY(s_specialFloats) - 1) * (DE_LENGTH_OF_ARRAY(s_specialFloats) - 1) * 6);
tcu::Surface resultImage (TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
// vertices
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats); ++x)
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats); ++y)
{
const float posX = (float)x / ((float)DE_LENGTH_OF_ARRAY(s_specialFloats) - 1.0f) * 2.0f - 1.0f; // map from [0, len(s_specialFloats) - 1] to [-1, 1]
const float posY = (float)y / ((float)DE_LENGTH_OF_ARRAY(s_specialFloats) - 1.0f) * 2.0f - 1.0f;
gridVertices[x * DE_LENGTH_OF_ARRAY(s_specialFloats) + y] = tcu::Vec4(posX, posY, 0.0f, 1.0f);
gridTexCoords[x * DE_LENGTH_OF_ARRAY(s_specialFloats) + y] = tcu::UVec2(s_specialFloats[x], s_specialFloats[y]);
}
// tiles
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats) - 1; ++x)
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats) - 1; ++y)
{
const int baseNdx = (x * (DE_LENGTH_OF_ARRAY(s_specialFloats) - 1) + y) * 6;
indices[baseNdx + 0] = (deUint16)((x+0) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+0));
indices[baseNdx + 1] = (deUint16)((x+1) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+1));
indices[baseNdx + 2] = (deUint16)((x+1) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+0));
indices[baseNdx + 3] = (deUint16)((x+0) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+0));
indices[baseNdx + 4] = (deUint16)((x+1) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+1));
indices[baseNdx + 5] = (deUint16)((x+0) * DE_LENGTH_OF_ARRAY(s_specialFloats) + (y+1));
}
m_testCtx.getLog() << tcu::TestLog::Message << "Drawing a textured grid with the shader. Sampling from the texture using special floating point values." << tcu::TestLog::EndMessage;
// Draw grid
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const GLint positionLoc = gl.getAttribLocation(m_program->getProgram(), "a_pos");
const GLint texCoordLoc = gl.getAttribLocation(m_program->getProgram(), "a_attr");
const GLint samplerLoc = gl.getUniformLocation(m_program->getProgram(), "u_sampler");
gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.clear(GL_COLOR_BUFFER_BIT);
gl.viewport(0, 0, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
gl.useProgram(m_program->getProgram());
gl.uniform1i(samplerLoc, 0);
if (m_testType != TEST_TEX_COORD_CUBE)
gl.bindTexture(GL_TEXTURE_2D, m_textureID);
else
gl.bindTexture(GL_TEXTURE_CUBE_MAP, m_textureID);
gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, &gridVertices[0]);
gl.vertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, &gridTexCoords[0]);
gl.enableVertexAttribArray(positionLoc);
gl.enableVertexAttribArray(texCoordLoc);
gl.drawElements(GL_TRIANGLES, (glw::GLsizei)(indices.size()), GL_UNSIGNED_SHORT, &indices[0]);
gl.disableVertexAttribArray(positionLoc);
gl.disableVertexAttribArray(texCoordLoc);
gl.useProgram(0);
gl.finish();
GLU_EXPECT_NO_ERROR(gl.getError(), "TextureSamplerCase::iterate");
glu::readPixels(m_context.getRenderContext(), 0, 0, resultImage.getAccess());
}
// verify everywhere was drawn and samples were from the texture (all pixels have Green = 255)
if (!checkResultImage(resultImage))
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "missing or invalid fragments");
return STOP;
}
// test drawing and textures still works
if (!drawTestPattern(true))
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "test pattern failed");
return STOP;
}
// all ok
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
std::string TextureSamplerCase::genVertexSource (void) const
{
// vertex shader is passthrough, fragment does the calculations
if (m_type == TYPE_FRAGMENT)
return s_attrPassthroughVertexShaderSource;
// vertex shader does the calculations
std::ostringstream buf;
buf << "#version 300 es\n"
"in highp vec4 a_pos;\n"
"in highp vec2 a_attr;\n";
if (m_testType != TEST_TEX_COORD_CUBE)
buf << "uniform highp sampler2D u_sampler;\n";
else
buf << "uniform highp samplerCube u_sampler;\n";
buf << "out mediump vec4 v_out;\n"
"void main ()\n"
"{\n";
if (m_testType == TEST_TEX_COORD)
buf << " v_out = textureLod(u_sampler, a_attr, 0.0);\n";
else if (m_testType == TEST_LOD)
buf << " v_out = textureLod(u_sampler, a_attr, a_attr.x);\n";
else if (m_testType == TEST_GRAD)
buf << " v_out = textureGrad(u_sampler, a_attr, a_attr, a_attr.yx);\n";
else if (m_testType == TEST_TEX_COORD_CUBE)
buf << " v_out = textureLod(u_sampler, vec3(a_attr, a_attr.x + a_attr.y), 0.0);\n";
else
DE_ASSERT(DE_FALSE);
buf << "\n"
" gl_Position = a_pos;\n"
"}\n";
return buf.str();
}
std::string TextureSamplerCase::genFragmentSource (void) const
{
// fragment shader is passthrough
if (m_type == TYPE_VERTEX)
return s_colorPassthroughFragmentShaderSource;
// fragment shader does the calculations
std::ostringstream buf;
buf << "#version 300 es\n"
"layout(location = 0) out mediump vec4 fragColor;\n";
if (m_testType != TEST_TEX_COORD_CUBE)
buf << "uniform highp sampler2D u_sampler;\n";
else
buf << "uniform highp samplerCube u_sampler;\n";
buf << "in highp vec4 v_attr;\n"
"void main ()\n"
"{\n";
if (m_testType == TEST_TEX_COORD)
buf << " fragColor = texture(u_sampler, v_attr.xy);\n";
else if (m_testType == TEST_LOD)
buf << " fragColor = texture(u_sampler, v_attr.xy, v_attr.x);\n";
else if (m_testType == TEST_GRAD)
buf << " fragColor = textureGrad(u_sampler, v_attr.xy, v_attr.xy, v_attr.yx);\n";
else if (m_testType == TEST_TEX_COORD_CUBE)
buf << " fragColor = texture(u_sampler, vec3(v_attr.xy,v_attr.x + v_attr.y));\n";
else
DE_ASSERT(DE_FALSE);
buf << "}\n";
return buf.str();
}
/*--------------------------------------------------------------------*//*!
* \brief Tests special floats as fragment shader outputs
*
* Tests that outputting special floats from a fragment shader does not change
* the normal floating point values of outputted from a fragment shader. Special
* floats are outputted in the green component, normal floating point values
* in the red and blue component. Potential changes are tested by rendering
* test pattern two times with different floating point values. The resulting
* images' red and blue channels should be equal.
*//*--------------------------------------------------------------------*/
class OutputCase : public FramebufferRenderCase
{
public:
OutputCase (Context& context, const char* name, const char* desc, FramebufferRenderCase::FrameBufferType type);
~OutputCase (void);
void testFBO (void);
private:
std::string genVertexSource (void) const;
std::string genFragmentSource (void) const;
};
OutputCase::OutputCase (Context& context, const char* name, const char* desc, FramebufferRenderCase::FrameBufferType type)
: FramebufferRenderCase (context, name, desc, type)
{
}
OutputCase::~OutputCase (void)
{
deinit();
}
void OutputCase::testFBO (void)
{
// Create a 1 X [s_specialFloats] grid of tiles (stripes).
std::vector<tcu::Vec4> gridVertices ((DE_LENGTH_OF_ARRAY(s_specialFloats) + 1) * 2);
std::vector<deUint16> indices (DE_LENGTH_OF_ARRAY(s_specialFloats) * 6);
tcu::TextureFormat textureFormat (tcu::TextureFormat::RGBA, (m_fboType == FBO_RGBA_FLOAT16 || m_fboType == FBO_RGBA_FLOAT32) ? (tcu::TextureFormat::FLOAT) : (tcu::TextureFormat::UNORM_INT8));
tcu::TextureLevel specialImage (textureFormat, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
tcu::TextureLevel normalImage (textureFormat, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
// vertices
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats) + 1; ++y)
{
const float posY = (float)y / (float)DE_LENGTH_OF_ARRAY(s_specialFloats) * 2.0f - 1.0f; // map from [0, len(s_specialFloats) ] to [-1, 1]
gridVertices[y * 2 + 0] = tcu::Vec4(-1.0, posY, 0.0f, 1.0f);
gridVertices[y * 2 + 1] = tcu::Vec4( 1.0, posY, 0.0f, 1.0f);
}
// tiles
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats); ++y)
{
const int baseNdx = y * 6;
indices[baseNdx + 0] = (deUint16)((y + 0) * 2);
indices[baseNdx + 1] = (deUint16)((y + 1) * 2);
indices[baseNdx + 2] = (deUint16)((y + 1) * 2 + 1);
indices[baseNdx + 3] = (deUint16)((y + 0) * 2);
indices[baseNdx + 4] = (deUint16)((y + 1) * 2 + 1);
indices[baseNdx + 5] = (deUint16)((y + 0) * 2 + 1);
}
// Draw grids
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const GLint positionLoc = gl.getAttribLocation(m_program->getProgram(), "a_pos");
const GLint specialLoc = gl.getUniformLocation(m_program->getProgram(), "u_special");
gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.viewport(0, 0, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
gl.useProgram(m_program->getProgram());
GLU_EXPECT_NO_ERROR(gl.getError(), "pre-draw");
gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, &gridVertices[0]);
gl.enableVertexAttribArray(positionLoc);
// draw 2 passes. Special and normal.
for (int passNdx = 0; passNdx < 2; ++passNdx)
{
const bool specialPass = (passNdx == 0);
m_testCtx.getLog() << tcu::TestLog::Message << "Pass " << passNdx << ": Drawing stripes with the shader. Setting u_special for each stripe to (" << ((specialPass) ? ("special") : ("1.0")) << ")." << tcu::TestLog::EndMessage;
// draw stripes
gl.clear(GL_COLOR_BUFFER_BIT);
for (int y = 0; y < DE_LENGTH_OF_ARRAY(s_specialFloats); ++y)
{
const deUint32 one = 0x3F800000;
const deUint32 special = s_specialFloats[y];
const deUint32 uniformValue = (specialPass) ? (special) : (one);
const int indexIndex = y * 6;
gl.uniform1fv(specialLoc, 1, (const float*)&uniformValue);
gl.drawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, &indices[indexIndex]);
}
gl.finish();
glu::readPixels(m_context.getRenderContext(), 0, 0, ((specialPass) ? (specialImage) : (normalImage)).getAccess());
}
gl.disableVertexAttribArray(positionLoc);
gl.useProgram(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "OutputCase::iterate");
}
// Check results
{
tcu::Surface errorMask (TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
const tcu::RGBA badPixelColor = tcu::RGBA::red();
const tcu::RGBA okPixelColor = tcu::RGBA::green();
int badPixels = 0;
m_testCtx.getLog() << tcu::TestLog::Message << "Checking passes have identical red and blue channels and the green channel is correct in the constant pass." << tcu::TestLog::EndMessage;
for (int y = 0; y < specialImage.getHeight(); ++y)
for (int x = 0; x < specialImage.getWidth(); ++x)
{
const float greenThreshold = 0.1f;
const tcu::Vec4 cNormal = normalImage.getAccess().getPixel(x, y);
const tcu::Vec4 cSpecial = specialImage.getAccess().getPixel(x, y);
if (cNormal.x() != cSpecial.x() || cNormal.z() != cSpecial.z() || cNormal.y() < 1.0f - greenThreshold)
{
++badPixels;
errorMask.setPixel(x, y, badPixelColor);
}
else
errorMask.setPixel(x, y, okPixelColor);
}
m_testCtx.getLog() << tcu::TestLog::Message << "Found " << badPixels << " invalid pixel(s)." << tcu::TestLog::EndMessage;
if (badPixels)
{
m_testCtx.getLog()
<< tcu::TestLog::ImageSet("Results", "Result verification")
<< tcu::TestLog::Image("Image with special green channel", "Image with special green channel", specialImage)
<< tcu::TestLog::Image("Image with constant green channel", "Image with constant green channel", normalImage)
<< tcu::TestLog::Image("Error Mask", "Error Mask", errorMask)
<< tcu::TestLog::EndImageSet;
// all ok?
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
}
else
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
}
std::string OutputCase::genVertexSource (void) const
{
return
"#version 300 es\n"
"in highp vec4 a_pos;\n"
"out highp vec2 v_pos;\n"
"void main ()\n"
"{\n"
" gl_Position = a_pos;\n"
" v_pos = a_pos.xy;\n"
"}\n";
}
std::string OutputCase::genFragmentSource (void) const
{
return
"#version 300 es\n"
"layout(location = 0) out highp vec4 fragColor;\n"
"uniform highp float u_special;\n"
"in highp vec2 v_pos;\n"
"void main ()\n"
"{\n"
" fragColor = vec4((v_pos.x + 1.0) * 0.5, u_special, (v_pos.y + 1.0) * 0.5, 1.0);\n"
"}\n";
}
/*--------------------------------------------------------------------*//*!
* \brief Tests special floats in blending
*
* Tests special floats as alpha and color components with various blending
* modes. Test draws a test pattern and then does various blend operations
* with special float values. After the blending test another test pattern
* is drawn to detect possible blending anomalies. Test patterns should be
* identical.
*//*--------------------------------------------------------------------*/
class BlendingCase : public FramebufferRenderCase
{
public:
BlendingCase (Context& context, const char* name, const char* desc, FramebufferRenderCase::FrameBufferType type);
~BlendingCase (void);
void testFBO (void);
private:
void drawTestImage (tcu::PixelBufferAccess dst, GLuint uColorLoc, int maxVertexIndex);
std::string genVertexSource (void) const;
std::string genFragmentSource (void) const;
};
BlendingCase::BlendingCase (Context& context, const char* name, const char* desc, FramebufferRenderCase::FrameBufferType type)
: FramebufferRenderCase (context, name, desc, type)
{
}
BlendingCase::~BlendingCase (void)
{
deinit();
}
void BlendingCase::testFBO (void)
{
static const GLenum equations[] =
{
GL_FUNC_ADD,
GL_FUNC_SUBTRACT,
GL_FUNC_REVERSE_SUBTRACT,
GL_MIN,
GL_MAX
};
static const GLenum functions[] =
{
GL_ZERO,
GL_ONE,
GL_SRC_COLOR,
GL_ONE_MINUS_SRC_COLOR,
GL_SRC_ALPHA,
GL_ONE_MINUS_SRC_ALPHA,
};
// Create a [BlendFuncs] X [s_specialFloats] grid of tiles. ( BlendFuncs = equations x functions )
const int numBlendFuncs = DE_LENGTH_OF_ARRAY(equations) * DE_LENGTH_OF_ARRAY(functions);
std::vector<tcu::Vec4> gridVertices ((numBlendFuncs + 1) * (DE_LENGTH_OF_ARRAY(s_specialFloats) + 1));
std::vector<deUint16> indices (numBlendFuncs * DE_LENGTH_OF_ARRAY(s_specialFloats) * 6);
tcu::TextureFormat textureFormat (tcu::TextureFormat::RGBA, (m_fboType == FBO_RGBA_FLOAT16 || m_fboType == FBO_RGBA_FLOAT32) ? (tcu::TextureFormat::FLOAT) : (tcu::TextureFormat::UNORM_INT8));
tcu::TextureLevel beforeImage (textureFormat, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
tcu::TextureLevel afterImage (textureFormat, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
// vertices
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats) + 1; ++x)
for (int y = 0; y < numBlendFuncs + 1; ++y)
{
const float posX = (float)x / (float)DE_LENGTH_OF_ARRAY(s_specialFloats) * 2.0f - 1.0f; // map from [0, len(s_specialFloats)] to [-1, 1]
const float posY = (float)y / (float)numBlendFuncs * 2.0f - 1.0f;
gridVertices[x * (numBlendFuncs + 1) + y] = tcu::Vec4(posX, posY, 0.0f, 1.0f);
}
// tiles
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats); ++x)
for (int y = 0; y < numBlendFuncs; ++y)
{
const int baseNdx = (x * numBlendFuncs + y) * 6;
indices[baseNdx + 0] = (deUint16)((x+0) * (numBlendFuncs + 1) + (y+0));
indices[baseNdx + 1] = (deUint16)((x+1) * (numBlendFuncs + 1) + (y+1));
indices[baseNdx + 2] = (deUint16)((x+1) * (numBlendFuncs + 1) + (y+0));
indices[baseNdx + 3] = (deUint16)((x+0) * (numBlendFuncs + 1) + (y+0));
indices[baseNdx + 4] = (deUint16)((x+1) * (numBlendFuncs + 1) + (y+1));
indices[baseNdx + 5] = (deUint16)((x+0) * (numBlendFuncs + 1) + (y+1));
}
// Draw tiles
{
const int numPasses = 5;
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const GLint positionLoc = gl.getAttribLocation(m_program->getProgram(), "a_pos");
const GLint specialLoc = gl.getUniformLocation(m_program->getProgram(), "u_special");
gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.clear(GL_COLOR_BUFFER_BIT);
gl.viewport(0, 0, TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
gl.useProgram(m_program->getProgram());
gl.enable(GL_BLEND);
GLU_EXPECT_NO_ERROR(gl.getError(), "pre-draw");
gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, &gridVertices[0]);
gl.enableVertexAttribArray(positionLoc);
// draw "before" image
m_testCtx.getLog() << tcu::TestLog::Message << "Drawing pre-draw pattern." << tcu::TestLog::EndMessage;
drawTestImage(beforeImage.getAccess(), specialLoc, (int)gridVertices.size() - 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "pre-draw pattern");
// draw multiple passes with special floats
gl.clear(GL_COLOR_BUFFER_BIT);
for (int passNdx = 0; passNdx < numPasses; ++passNdx)
{
de::Random rnd(123 + 567 * passNdx);
m_testCtx.getLog()
<< tcu::TestLog::Message
<< "Pass " << passNdx << ": Drawing tiles with the shader.\n"
<< "\tVarying u_special for each tile.\n"
<< "\tVarying blend function and blend equation for each tile.\n"
<< tcu::TestLog::EndMessage;
// draw tiles
for (int x = 0; x < DE_LENGTH_OF_ARRAY(s_specialFloats); ++x)
for (int y = 0; y < numBlendFuncs; ++y)
{
const GLenum blendEquation = equations[y % DE_LENGTH_OF_ARRAY(equations)];
const GLenum blendFunction = functions[y / DE_LENGTH_OF_ARRAY(equations)];
const GLenum blendFunctionDst = rnd.choose<GLenum>(DE_ARRAY_BEGIN(functions), DE_ARRAY_END(functions));
const int indexIndex = (x * numBlendFuncs + y) * 6;
// "rnd.get"s are run in a deterministic order
const deUint32 componentR = rnd.choose<deUint32>(DE_ARRAY_BEGIN(s_specialFloats), DE_ARRAY_END(s_specialFloats));
const deUint32 componentG = rnd.choose<deUint32>(DE_ARRAY_BEGIN(s_specialFloats), DE_ARRAY_END(s_specialFloats));
const deUint32 componentB = rnd.choose<deUint32>(DE_ARRAY_BEGIN(s_specialFloats), DE_ARRAY_END(s_specialFloats));
const deUint32 componentA = rnd.choose<deUint32>(DE_ARRAY_BEGIN(s_specialFloats), DE_ARRAY_END(s_specialFloats));
const tcu::UVec4 uniformValue = tcu::UVec4(componentR, componentG, componentB, componentA);
gl.uniform4fv(specialLoc, 1, (const float*)uniformValue.getPtr());
gl.blendEquation(blendEquation);
gl.blendFunc(blendFunction, blendFunctionDst);
gl.drawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, &indices[indexIndex]);
}
}
GLU_EXPECT_NO_ERROR(gl.getError(), "special passes");
// draw "after" image
m_testCtx.getLog() << tcu::TestLog::Message << "Drawing post-draw pattern." << tcu::TestLog::EndMessage;
drawTestImage(afterImage.getAccess(), specialLoc, (int)gridVertices.size() - 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "post-draw pattern");
gl.disableVertexAttribArray(positionLoc);
gl.useProgram(0);
GLU_EXPECT_NO_ERROR(gl.getError(), "OutputCase::iterate");
}
// Check results
{
tcu::Surface errorMask (TEST_CANVAS_SIZE, TEST_CANVAS_SIZE);
const tcu::RGBA badPixelColor = tcu::RGBA::red();
const tcu::RGBA okPixelColor = tcu::RGBA::green();
int badPixels = 0;
m_testCtx.getLog() << tcu::TestLog::Message << "Checking patterns are identical." << tcu::TestLog::EndMessage;
for (int y = 0; y < beforeImage.getHeight(); ++y)
for (int x = 0; x < beforeImage.getWidth(); ++x)
{
const tcu::Vec4 cBefore = beforeImage.getAccess().getPixel(x, y);
const tcu::Vec4 cAfter = afterImage.getAccess().getPixel(x, y);
if (cBefore != cAfter)
{
++badPixels;
errorMask.setPixel(x, y, badPixelColor);
}
else
errorMask.setPixel(x, y, okPixelColor);
}
m_testCtx.getLog() << tcu::TestLog::Message << "Found " << badPixels << " invalid pixel(s)." << tcu::TestLog::EndMessage;
if (badPixels)
{
m_testCtx.getLog()
<< tcu::TestLog::ImageSet("Results", "Result verification")
<< tcu::TestLog::Image("Pattern drawn before special float blending", "Pattern drawn before special float blending", beforeImage)
<< tcu::TestLog::Image("Pattern drawn after special float blending", "Pattern drawn after special float blending", afterImage)
<< tcu::TestLog::EndImageSet;
// all ok?
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed");
}
else
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
}
void BlendingCase::drawTestImage (tcu::PixelBufferAccess dst, GLuint uColorLoc, int maxVertexIndex)
{
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
de::Random rnd (123);
gl.clear(GL_COLOR_BUFFER_BIT);
gl.blendEquation(GL_FUNC_ADD);
gl.blendFunc(GL_ONE, GL_ONE);
for (int tri = 0; tri < 20; ++tri)
{
tcu::Vec4 color;
color.x() = rnd.getFloat();
color.y() = rnd.getFloat();
color.z() = rnd.getFloat();
color.w() = rnd.getFloat();
gl.uniform4fv(uColorLoc, 1, color.getPtr());
deUint16 indices[3];
indices[0] = (deUint16)rnd.getInt(0, maxVertexIndex);
indices[1] = (deUint16)rnd.getInt(0, maxVertexIndex);
indices[2] = (deUint16)rnd.getInt(0, maxVertexIndex);
gl.drawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, indices);
}
gl.finish();
glu::readPixels(m_context.getRenderContext(), 0, 0, dst);
}
std::string BlendingCase::genVertexSource (void) const
{
return
"#version 300 es\n"
"in highp vec4 a_pos;\n"
"void main ()\n"
"{\n"
" gl_Position = a_pos;\n"
"}\n";
}
std::string BlendingCase::genFragmentSource (void) const
{
return
"#version 300 es\n"
"layout(location = 0) out highp vec4 fragColor;\n"
"uniform highp vec4 u_special;\n"
"void main ()\n"
"{\n"
" fragColor = u_special;\n"
"}\n";
}
} //anonymous
SpecialFloatTests::SpecialFloatTests (Context& context)
: TestCaseGroup(context, "special_float", "Special float tests")
{
}
SpecialFloatTests::~SpecialFloatTests (void)
{
}
void SpecialFloatTests::init (void)
{
tcu::TestCaseGroup* const vertexGroup = new tcu::TestCaseGroup(m_testCtx, "vertex", "Run vertex shader with special float values");
tcu::TestCaseGroup* const fragmentGroup = new tcu::TestCaseGroup(m_testCtx, "fragment", "Run fragment shader with special float values");
tcu::TestCaseGroup* const framebufferGroup = new tcu::TestCaseGroup(m_testCtx, "framebuffer", "Test framebuffers containing special float values");
// .vertex
{
vertexGroup->addChild(new VertexAttributeCase (m_context, "attribute_buffer", "special attribute values in a buffer", VertexAttributeCase::STORAGE_BUFFER, VertexAttributeCase::TYPE_VERTEX));
vertexGroup->addChild(new VertexAttributeCase (m_context, "attribute_client", "special attribute values in a client storage", VertexAttributeCase::STORAGE_CLIENT, VertexAttributeCase::TYPE_VERTEX));
vertexGroup->addChild(new UniformCase (m_context, "uniform", "special uniform values", UniformCase::TYPE_VERTEX));
vertexGroup->addChild(new TextureCase (m_context, "texture", "texture with special floating point values", TextureCase::TYPE_VERTEX, TextureCase::TEXTURE_FLOAT, TextureCase::UPLOAD_CLIENT));
vertexGroup->addChild(new TextureCase (m_context, "texture_pbo", "texture (via pbo) with special floating point values", TextureCase::TYPE_VERTEX, TextureCase::TEXTURE_FLOAT, TextureCase::UPLOAD_PBO));
vertexGroup->addChild(new TextureCase (m_context, "texture_shadow", "shadow texture with special floating point values", TextureCase::TYPE_VERTEX, TextureCase::TEXTURE_DEPTH, TextureCase::UPLOAD_CLIENT));
vertexGroup->addChild(new TextureSamplerCase (m_context, "sampler_tex_coord", "special texture coords", TextureSamplerCase::TYPE_VERTEX, TextureSamplerCase::TEST_TEX_COORD));
vertexGroup->addChild(new TextureSamplerCase (m_context, "sampler_tex_coord_cube", "special texture coords to cubemap", TextureSamplerCase::TYPE_VERTEX, TextureSamplerCase::TEST_TEX_COORD_CUBE));
vertexGroup->addChild(new TextureSamplerCase (m_context, "sampler_lod", "special texture lod", TextureSamplerCase::TYPE_VERTEX, TextureSamplerCase::TEST_LOD));
vertexGroup->addChild(new TextureSamplerCase (m_context, "sampler_grad", "special texture grad", TextureSamplerCase::TYPE_VERTEX, TextureSamplerCase::TEST_GRAD));
addChild(vertexGroup);
}
// .fragment
{
fragmentGroup->addChild(new VertexAttributeCase (m_context, "attribute_buffer", "special attribute values in a buffer", VertexAttributeCase::STORAGE_BUFFER, VertexAttributeCase::TYPE_FRAGMENT));
fragmentGroup->addChild(new VertexAttributeCase (m_context, "attribute_client", "special attribute values in a client storage", VertexAttributeCase::STORAGE_CLIENT, VertexAttributeCase::TYPE_FRAGMENT));
fragmentGroup->addChild(new UniformCase (m_context, "uniform", "special uniform values", UniformCase::TYPE_FRAGMENT));
fragmentGroup->addChild(new TextureCase (m_context, "texture", "texture with special floating point values", TextureCase::TYPE_FRAGMENT, TextureCase::TEXTURE_FLOAT, TextureCase::UPLOAD_CLIENT));
fragmentGroup->addChild(new TextureCase (m_context, "texture_pbo", "texture (via pbo) with special floating point values", TextureCase::TYPE_FRAGMENT, TextureCase::TEXTURE_FLOAT, TextureCase::UPLOAD_PBO));
fragmentGroup->addChild(new TextureCase (m_context, "texture_shadow", "shadow texture with special floating point values", TextureCase::TYPE_FRAGMENT, TextureCase::TEXTURE_DEPTH, TextureCase::UPLOAD_CLIENT));
fragmentGroup->addChild(new TextureSamplerCase (m_context, "sampler_tex_coord", "special texture coords", TextureSamplerCase::TYPE_FRAGMENT, TextureSamplerCase::TEST_TEX_COORD));
fragmentGroup->addChild(new TextureSamplerCase (m_context, "sampler_tex_coord_cube", "special texture coords to cubemap", TextureSamplerCase::TYPE_FRAGMENT, TextureSamplerCase::TEST_TEX_COORD_CUBE));
fragmentGroup->addChild(new TextureSamplerCase (m_context, "sampler_lod", "special texture lod", TextureSamplerCase::TYPE_FRAGMENT, TextureSamplerCase::TEST_LOD));
fragmentGroup->addChild(new TextureSamplerCase (m_context, "sampler_grad", "special texture grad", TextureSamplerCase::TYPE_FRAGMENT, TextureSamplerCase::TEST_GRAD));
addChild(fragmentGroup);
}
// .framebuffer
{
framebufferGroup->addChild(new OutputCase (m_context, "write_default", "write special floating point values to default framebuffer", FramebufferRenderCase::FBO_DEFAULT));
framebufferGroup->addChild(new OutputCase (m_context, "write_rgba4", "write special floating point values to RGBA4 framebuffer", FramebufferRenderCase::FBO_RGBA4));
framebufferGroup->addChild(new OutputCase (m_context, "write_rgb5_a1", "write special floating point values to RGB5_A1 framebuffer", FramebufferRenderCase::FBO_RGB5_A1));
framebufferGroup->addChild(new OutputCase (m_context, "write_rgb565", "write special floating point values to RGB565 framebuffer", FramebufferRenderCase::FBO_RGB565));
framebufferGroup->addChild(new OutputCase (m_context, "write_rgba8", "write special floating point values to RGBA8 framebuffer", FramebufferRenderCase::FBO_RGBA8));
framebufferGroup->addChild(new OutputCase (m_context, "write_rgb10_a2", "write special floating point values to RGB10_A2 framebuffer", FramebufferRenderCase::FBO_RGB10_A2));
framebufferGroup->addChild(new OutputCase (m_context, "write_float16", "write special floating point values to float16 framebuffer", FramebufferRenderCase::FBO_RGBA_FLOAT16));
framebufferGroup->addChild(new OutputCase (m_context, "write_float32", "write special floating point values to float32 framebuffer", FramebufferRenderCase::FBO_RGBA_FLOAT32));
framebufferGroup->addChild(new BlendingCase (m_context, "blend_default", "blend special floating point values in a default framebuffer", FramebufferRenderCase::FBO_DEFAULT));
framebufferGroup->addChild(new BlendingCase (m_context, "blend_rgba8", "blend special floating point values in a RGBA8 framebuffer", FramebufferRenderCase::FBO_RGBA8));
framebufferGroup->addChild(new BlendingCase (m_context, "blend_float16", "blend special floating point values in a float16 framebuffer", FramebufferRenderCase::FBO_RGBA_FLOAT16));
addChild(framebufferGroup);
}
}
} // Stress
} // gles3
} // deqp