/*------------------------------------------------------------------------- * 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 Shader built-in variable tests. *//*--------------------------------------------------------------------*/ #include "es3fShaderBuiltinVarTests.hpp" #include "glsShaderRenderCase.hpp" #include "glsShaderExecUtil.hpp" #include "deRandom.hpp" #include "deString.h" #include "deMath.h" #include "deUniquePtr.hpp" #include "deStringUtil.hpp" #include "tcuTestLog.hpp" #include "tcuTestCase.hpp" #include "tcuTextureUtil.hpp" #include "tcuRenderTarget.hpp" #include "tcuImageCompare.hpp" #include "gluPixelTransfer.hpp" #include "gluDrawUtil.hpp" #include "gluStrUtil.hpp" #include "rrRenderer.hpp" #include "rrFragmentOperations.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" using std::string; using std::vector; using tcu::TestLog; namespace deqp { namespace gles3 { namespace Functional { static int getInteger (const glw::Functions& gl, deUint32 pname) { int value = -1; gl.getIntegerv(pname, &value); GLU_EXPECT_NO_ERROR(gl.getError(), ("glGetIntegerv(" + glu::getGettableStateStr((int)pname).toString() + ")").c_str()); return value; } template<deUint32 Pname> static int getInteger (const glw::Functions& gl) { return getInteger(gl, Pname); } static int getVectorsFromComps (const glw::Functions& gl, deUint32 pname) { int value = -1; gl.getIntegerv(pname, &value); GLU_EXPECT_NO_ERROR(gl.getError(), ("glGetIntegerv(" + glu::getGettableStateStr((int)pname).toString() + ")").c_str()); // Accept truncated division. According to the spec, the number of vectors is number of components divided by four, plain and simple. return value/4; } template<deUint32 Pname> static int getVectorsFromComps (const glw::Functions& gl) { return getVectorsFromComps(gl, Pname); } class ShaderBuiltinConstantCase : public TestCase { public: typedef int (*GetConstantValueFunc) (const glw::Functions& gl); ShaderBuiltinConstantCase (Context& context, const char* name, const char* desc, const char* varName, GetConstantValueFunc getValue, glu::ShaderType shaderType); ~ShaderBuiltinConstantCase (void); IterateResult iterate (void); private: const std::string m_varName; const GetConstantValueFunc m_getValue; const glu::ShaderType m_shaderType; }; ShaderBuiltinConstantCase::ShaderBuiltinConstantCase (Context& context, const char* name, const char* desc, const char* varName, GetConstantValueFunc getValue, glu::ShaderType shaderType) : TestCase (context, name, desc) , m_varName (varName) , m_getValue (getValue) , m_shaderType (shaderType) { } ShaderBuiltinConstantCase::~ShaderBuiltinConstantCase (void) { } static gls::ShaderExecUtil::ShaderExecutor* createGetConstantExecutor (const glu::RenderContext& renderCtx, glu::ShaderType shaderType, const std::string& varName) { using namespace gls::ShaderExecUtil; ShaderSpec shaderSpec; shaderSpec.version = glu::GLSL_VERSION_300_ES; shaderSpec.source = string("result = ") + varName + ";\n"; shaderSpec.outputs.push_back(Symbol("result", glu::VarType(glu::TYPE_INT, glu::PRECISION_HIGHP))); return createExecutor(renderCtx, shaderType, shaderSpec); } ShaderBuiltinConstantCase::IterateResult ShaderBuiltinConstantCase::iterate (void) { using namespace gls::ShaderExecUtil; const de::UniquePtr<ShaderExecutor> shaderExecutor (createGetConstantExecutor(m_context.getRenderContext(), m_shaderType, m_varName)); const int reference = m_getValue(m_context.getRenderContext().getFunctions()); int result = -1; void* const outputs = &result; if (!shaderExecutor->isOk()) { shaderExecutor->log(m_testCtx.getLog()); TCU_FAIL("Compile failed"); } shaderExecutor->useProgram(); shaderExecutor->execute(1, DE_NULL, &outputs); m_testCtx.getLog() << TestLog::Integer(m_varName, m_varName, "", QP_KEY_TAG_NONE, result); if (result != reference) { m_testCtx.getLog() << TestLog::Message << "ERROR: Expected " << m_varName << " = " << reference << TestLog::EndMessage << TestLog::Message << "Test shader:" << TestLog::EndMessage; shaderExecutor->log(m_testCtx.getLog()); m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid builtin constant value"); } else m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } namespace { struct DepthRangeParams { DepthRangeParams (void) : zNear (0.0f) , zFar (1.0f) { } DepthRangeParams (float zNear_, float zFar_) : zNear (zNear_) , zFar (zFar_) { } float zNear; float zFar; }; class DepthRangeEvaluator : public gls::ShaderEvaluator { public: DepthRangeEvaluator (const DepthRangeParams& params) : m_params(params) { } void evaluate (gls::ShaderEvalContext& c) { float zNear = deFloatClamp(m_params.zNear, 0.0f, 1.0f); float zFar = deFloatClamp(m_params.zFar, 0.0f, 1.0f); float diff = zFar - zNear; c.color.xyz() = tcu::Vec3(zNear, zFar, diff*0.5f + 0.5f); } private: const DepthRangeParams& m_params; }; } // anonymous class ShaderDepthRangeTest : public gls::ShaderRenderCase { public: ShaderDepthRangeTest (Context& context, const char* name, const char* desc, bool isVertexCase) : ShaderRenderCase (context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, desc, isVertexCase, m_evaluator) , m_evaluator (m_depthRange) , m_iterNdx (0) { } void init (void) { static const char* defaultVertSrc = "#version 300 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n"; static const char* defaultFragSrc = "#version 300 es\n" "in mediump vec4 v_color;\n" "layout(location = 0) out mediump vec4 o_color;\n\n" "void main (void)\n" "{\n" " o_color = v_color;\n" "}\n"; // Construct shader. std::ostringstream src; src << "#version 300 es\n"; if (m_isVertexCase) src << "in highp vec4 a_position;\n" << "out mediump vec4 v_color;\n"; else src << "layout(location = 0) out mediump vec4 o_color;\n"; src << "void main (void)\n{\n"; src << "\t" << (m_isVertexCase ? "v_color" : "o_color") << " = vec4(gl_DepthRange.near, gl_DepthRange.far, gl_DepthRange.diff*0.5 + 0.5, 1.0);\n"; if (m_isVertexCase) src << "\tgl_Position = a_position;\n"; src << "}\n"; m_vertShaderSource = m_isVertexCase ? src.str() : defaultVertSrc; m_fragShaderSource = m_isVertexCase ? defaultFragSrc : src.str(); gls::ShaderRenderCase::init(); } IterateResult iterate (void) { const glw::Functions& gl = m_renderCtx.getFunctions(); const DepthRangeParams cases[] = { DepthRangeParams(0.0f, 1.0f), DepthRangeParams(1.5f, -1.0f), DepthRangeParams(0.7f, 0.3f) }; m_depthRange = cases[m_iterNdx]; m_testCtx.getLog() << tcu::TestLog::Message << "glDepthRangef(" << m_depthRange.zNear << ", " << m_depthRange.zFar << ")" << tcu::TestLog::EndMessage; gl.depthRangef(m_depthRange.zNear, m_depthRange.zFar); GLU_EXPECT_NO_ERROR(gl.getError(), "glDepthRangef()"); gls::ShaderRenderCase::iterate(); m_iterNdx += 1; if (m_iterNdx == DE_LENGTH_OF_ARRAY(cases) || m_testCtx.getTestResult() != QP_TEST_RESULT_PASS) return STOP; else return CONTINUE; } private: DepthRangeParams m_depthRange; DepthRangeEvaluator m_evaluator; int m_iterNdx; }; class FragCoordXYZCase : public TestCase { public: FragCoordXYZCase (Context& context) : TestCase(context, "fragcoord_xyz", "gl_FragCoord.xyz Test") { } IterateResult iterate (void) { TestLog& log = m_testCtx.getLog(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int width = m_context.getRenderTarget().getWidth(); const int height = m_context.getRenderTarget().getHeight(); const tcu::RGBA threshold = tcu::RGBA(1,1,1,1) + m_context.getRenderTarget().getPixelFormat().getColorThreshold(); const tcu::Vec3 scale (1.f / float(width), 1.f / float(height), 1.0f); tcu::Surface testImg (width, height); tcu::Surface refImg (width, height); const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources( "#version 300 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n", "#version 300 es\n" "uniform highp vec3 u_scale;\n" "layout(location = 0) out mediump vec4 o_color;\n" "void main (void)\n" "{\n" " o_color = vec4(gl_FragCoord.xyz*u_scale, 1.0);\n" "}\n")); log << program; if (!program.isOk()) throw tcu::TestError("Compile failed"); // Draw with GL. { const float positions[] = { -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f }; const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 }; const int scaleLoc = gl.getUniformLocation(program.getProgram(), "u_scale"); glu::VertexArrayBinding posBinding = glu::va::Float("a_position", 4, 4, 0, &positions[0]); gl.useProgram(program.getProgram()); gl.uniform3fv(scaleLoc, 1, scale.getPtr()); glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding, glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0])); glu::readPixels(m_context.getRenderContext(), 0, 0, testImg.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw"); } // Draw reference for (int y = 0; y < refImg.getHeight(); y++) { for (int x = 0; x < refImg.getWidth(); x++) { const float xf = (float(x)+.5f) / float(refImg.getWidth()); const float yf = (float(refImg.getHeight()-y-1)+.5f) / float(refImg.getHeight()); const float z = (xf + yf) / 2.0f; const tcu::Vec3 fragCoord (float(x)+.5f, float(y)+.5f, z); const tcu::Vec3 scaledFC = fragCoord*scale; const tcu::Vec4 color (scaledFC.x(), scaledFC.y(), scaledFC.z(), 1.0f); refImg.setPixel(x, y, tcu::RGBA(color)); } } // Compare { bool isOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", refImg, testImg, threshold, tcu::COMPARE_LOG_RESULT); m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Image comparison failed"); } return STOP; } }; static inline float projectedTriInterpolate (const tcu::Vec3& s, const tcu::Vec3& w, float nx, float ny) { return (s[0]*(1.0f-nx-ny)/w[0] + s[1]*ny/w[1] + s[2]*nx/w[2]) / ((1.0f-nx-ny)/w[0] + ny/w[1] + nx/w[2]); } class FragCoordWCase : public TestCase { public: FragCoordWCase (Context& context) : TestCase(context, "fragcoord_w", "gl_FragCoord.w Test") { } IterateResult iterate (void) { TestLog& log = m_testCtx.getLog(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int width = m_context.getRenderTarget().getWidth(); const int height = m_context.getRenderTarget().getHeight(); const tcu::RGBA threshold = tcu::RGBA(1,1,1,1) + m_context.getRenderTarget().getPixelFormat().getColorThreshold(); tcu::Surface testImg (width, height); tcu::Surface refImg (width, height); const float w[4] = { 1.7f, 2.0f, 1.2f, 1.0f }; const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources( "#version 300 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n", "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n" "void main (void)\n" "{\n" " o_color = vec4(0.0, 1.0/gl_FragCoord.w - 1.0, 0.0, 1.0);\n" "}\n")); log << program; if (!program.isOk()) throw tcu::TestError("Compile failed"); // Draw with GL. { const float positions[] = { -w[0], w[0], 0.0f, w[0], -w[1], -w[1], 0.0f, w[1], w[2], w[2], 0.0f, w[2], w[3], -w[3], 0.0f, w[3] }; const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 }; glu::VertexArrayBinding posBinding = glu::va::Float("a_position", 4, 4, 0, &positions[0]); gl.useProgram(program.getProgram()); glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding, glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0])); glu::readPixels(m_context.getRenderContext(), 0, 0, testImg.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw"); } // Draw reference for (int y = 0; y < refImg.getHeight(); y++) { for (int x = 0; x < refImg.getWidth(); x++) { const float xf = (float(x)+.5f) / float(refImg.getWidth()); const float yf = (float(refImg.getHeight()-y-1)+.5f) / float(refImg.getHeight()); const float oow = ((xf + yf) < 1.0f) ? projectedTriInterpolate(tcu::Vec3(w[0], w[1], w[2]), tcu::Vec3(w[0], w[1], w[2]), xf, yf) : projectedTriInterpolate(tcu::Vec3(w[3], w[2], w[1]), tcu::Vec3(w[3], w[2], w[1]), 1.0f-xf, 1.0f-yf); const tcu::Vec4 color (0.0f, oow - 1.0f, 0.0f, 1.0f); refImg.setPixel(x, y, tcu::RGBA(color)); } } // Compare { bool isOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", refImg, testImg, threshold, tcu::COMPARE_LOG_RESULT); m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Image comparison failed"); } return STOP; } }; class PointCoordCase : public TestCase { public: PointCoordCase (Context& context) : TestCase(context, "pointcoord", "gl_PointCoord Test") { } IterateResult iterate (void) { TestLog& log = m_testCtx.getLog(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int width = de::min(256, m_context.getRenderTarget().getWidth()); const int height = de::min(256, m_context.getRenderTarget().getHeight()); const float threshold = 0.02f; const int numPoints = 8; vector<tcu::Vec3> coords (numPoints); float pointSizeRange[2] = { 0.0f, 0.0f }; de::Random rnd (0x145fa); tcu::Surface testImg (width, height); tcu::Surface refImg (width, height); gl.getFloatv(GL_ALIASED_POINT_SIZE_RANGE, &pointSizeRange[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE)"); if (pointSizeRange[0] <= 0.0f || pointSizeRange[1] <= 0.0f || pointSizeRange[1] < pointSizeRange[0]) throw tcu::TestError("Invalid GL_ALIASED_POINT_SIZE_RANGE"); // Compute coordinates. { for (vector<tcu::Vec3>::iterator coord = coords.begin(); coord != coords.end(); ++coord) { coord->x() = rnd.getFloat(-0.9f, 0.9f); coord->y() = rnd.getFloat(-0.9f, 0.9f); coord->z() = rnd.getFloat(pointSizeRange[0], pointSizeRange[1]); } } const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources( "#version 300 es\n" "in highp vec3 a_positionSize;\n" "void main (void)\n" "{\n" " gl_Position = vec4(a_positionSize.xy, 0.0, 1.0);\n" " gl_PointSize = a_positionSize.z;\n" "}\n", "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n" "void main (void)\n" "{\n" " o_color = vec4(gl_PointCoord, 0.0, 1.0);\n" "}\n")); log << program; if (!program.isOk()) throw tcu::TestError("Compile failed"); // Draw with GL. { glu::VertexArrayBinding posBinding = glu::va::Float("a_positionSize", 3, (int)coords.size(), 0, (const float*)&coords[0]); const int viewportX = rnd.getInt(0, m_context.getRenderTarget().getWidth()-width); const int viewportY = rnd.getInt(0, m_context.getRenderTarget().getHeight()-height); gl.viewport(viewportX, viewportY, width, height); gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); gl.clear(GL_COLOR_BUFFER_BIT); gl.useProgram(program.getProgram()); glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding, glu::pr::Points((int)coords.size())); glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, testImg.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw"); } // Draw reference tcu::clear(refImg.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); for (vector<tcu::Vec3>::const_iterator pointIter = coords.begin(); pointIter != coords.end(); ++pointIter) { const int x0 = deRoundFloatToInt32(float(width) *(pointIter->x()*0.5f + 0.5f) - pointIter->z()*0.5f); const int y0 = deRoundFloatToInt32(float(height)*(pointIter->y()*0.5f + 0.5f) - pointIter->z()*0.5f); const int x1 = deRoundFloatToInt32(float(width) *(pointIter->x()*0.5f + 0.5f) + pointIter->z()*0.5f); const int y1 = deRoundFloatToInt32(float(height)*(pointIter->y()*0.5f + 0.5f) + pointIter->z()*0.5f); const int w = x1-x0; const int h = y1-y0; for (int yo = 0; yo < h; yo++) { for (int xo = 0; xo < w; xo++) { const float xf = float(xo+0.5f) / float(w); const float yf = float((h-yo-1)+0.5f) / float(h); const tcu::Vec4 color (xf, yf, 0.0f, 1.0f); const int dx = x0+xo; const int dy = y0+yo; if (de::inBounds(dx, 0, refImg.getWidth()) && de::inBounds(dy, 0, refImg.getHeight())) refImg.setPixel(dx, dy, tcu::RGBA(color)); } } } // Compare { bool isOk = tcu::fuzzyCompare(log, "Result", "Image comparison result", refImg, testImg, threshold, tcu::COMPARE_LOG_RESULT); m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Image comparison failed"); } return STOP; } }; class FrontFacingCase : public TestCase { public: FrontFacingCase (Context& context) : TestCase(context, "frontfacing", "gl_FrontFacing Test") { } IterateResult iterate (void) { // Test case renders two adjecent quads, where left is has front-facing // triagles and right back-facing. Color is selected based on gl_FrontFacing // value. TestLog& log = m_testCtx.getLog(); const glw::Functions& gl = m_context.getRenderContext().getFunctions(); de::Random rnd (0x89f2c); const int width = de::min(64, m_context.getRenderTarget().getWidth()); const int height = de::min(64, m_context.getRenderTarget().getHeight()); const int viewportX = rnd.getInt(0, m_context.getRenderTarget().getWidth()-width); const int viewportY = rnd.getInt(0, m_context.getRenderTarget().getHeight()-height); const tcu::RGBA threshold = tcu::RGBA(1,1,1,1) + m_context.getRenderTarget().getPixelFormat().getColorThreshold(); tcu::Surface testImg (width, height); tcu::Surface refImg (width, height); const glu::ShaderProgram program(m_context.getRenderContext(), glu::makeVtxFragSources( "#version 300 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n", "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n" "void main (void)\n" "{\n" " if (gl_FrontFacing)\n" " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n" " else\n" " o_color = vec4(0.0, 0.0, 1.0, 1.0);\n" "}\n")); log << program; if (!program.isOk()) throw tcu::TestError("Compile failed"); // Draw with GL. { const float positions[] = { -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f }; const deUint16 indicesCCW[] = { 0, 1, 2, 2, 1, 3 }; const deUint16 indicesCW[] = { 2, 1, 0, 3, 1, 2 }; glu::VertexArrayBinding posBinding = glu::va::Float("a_position", 4, 4, 0, &positions[0]); gl.useProgram(program.getProgram()); gl.viewport(viewportX, viewportY, width/2, height); glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding, glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indicesCCW), &indicesCCW[0])); gl.viewport(viewportX + width/2, viewportY, width-width/2, height); glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posBinding, glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indicesCW), &indicesCW[0])); glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, testImg.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw"); } // Draw reference for (int y = 0; y < refImg.getHeight(); y++) { for (int x = 0; x < refImg.getWidth()/2; x++) refImg.setPixel(x, y, tcu::RGBA::green); for (int x = refImg.getWidth()/2; x < refImg.getWidth(); x++) refImg.setPixel(x, y, tcu::RGBA::blue); } // Compare { bool isOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", refImg, testImg, threshold, tcu::COMPARE_LOG_RESULT); m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, isOk ? "Pass" : "Image comparison failed"); } return STOP; } }; // VertexIDCase class VertexIDCase : public TestCase { public: VertexIDCase (Context& context); ~VertexIDCase (void); void init (void); void deinit (void); IterateResult iterate (void); private: enum { MAX_VERTICES = 8*3 //!< 8 triangles, totals 24 vertices }; void renderReference (const tcu::PixelBufferAccess& dst, const int numVertices, const deUint16* const indices, const tcu::Vec4* const positions, const tcu::Vec4* const colors); glu::ShaderProgram* m_program; deUint32 m_positionBuffer; deUint32 m_elementBuffer; vector<tcu::Vec4> m_positions; vector<tcu::Vec4> m_colors; int m_viewportW; int m_viewportH; int m_iterNdx; }; VertexIDCase::VertexIDCase (Context& context) : TestCase (context, "vertex_id", "gl_VertexID Test") , m_program (DE_NULL) , m_positionBuffer (0) , m_elementBuffer (0) , m_viewportW (0) , m_viewportH (0) , m_iterNdx (0) { } VertexIDCase::~VertexIDCase (void) { VertexIDCase::deinit(); } void VertexIDCase::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int width = m_context.getRenderTarget().getWidth(); const int height = m_context.getRenderTarget().getHeight(); const int quadWidth = 32; const int quadHeight = 32; if (width < quadWidth) throw tcu::NotSupportedError("Too small render target"); const int maxQuadsX = width/quadWidth; const int numVertices = MAX_VERTICES; const int numQuads = numVertices/6 + (numVertices%6 != 0 ? 1 : 0); const int viewportW = de::min(numQuads, maxQuadsX)*quadWidth; const int viewportH = (numQuads/maxQuadsX + (numQuads%maxQuadsX != 0 ? 1 : 0))*quadHeight; if (viewportH > height) throw tcu::NotSupportedError("Too small render target"); DE_ASSERT(viewportW <= width && viewportH <= height); DE_ASSERT(!m_program); m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources( "#version 300 es\n" "in highp vec4 a_position;\n" "out mediump vec4 v_color;\n" "uniform highp vec4 u_colors[24];\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" " v_color = u_colors[gl_VertexID];\n" "}\n", "#version 300 es\n" "in mediump vec4 v_color;\n" "layout(location = 0) out mediump vec4 o_color;\n" "void main (void)\n" "{\n" " o_color = v_color;\n" "}\n")); m_testCtx.getLog() << *m_program; if (!m_program->isOk()) { delete m_program; m_program = DE_NULL; throw tcu::TestError("Compile failed"); } gl.genBuffers(1, &m_positionBuffer); gl.genBuffers(1, &m_elementBuffer); // Set colors (in dynamic memory to save static data space). m_colors.resize(numVertices); m_colors[ 0] = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f); m_colors[ 1] = tcu::Vec4(0.5f, 1.0f, 0.5f, 1.0f); m_colors[ 2] = tcu::Vec4(0.0f, 0.5f, 1.0f, 1.0f); m_colors[ 3] = tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f); m_colors[ 4] = tcu::Vec4(0.0f, 1.0f, 1.0f, 1.0f); m_colors[ 5] = tcu::Vec4(0.5f, 0.0f, 0.0f, 1.0f); m_colors[ 6] = tcu::Vec4(0.5f, 0.0f, 1.0f, 1.0f); m_colors[ 7] = tcu::Vec4(0.5f, 0.0f, 0.5f, 1.0f); m_colors[ 8] = tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f); m_colors[ 9] = tcu::Vec4(0.5f, 1.0f, 0.0f, 1.0f); m_colors[10] = tcu::Vec4(0.0f, 0.5f, 0.0f, 1.0f); m_colors[11] = tcu::Vec4(0.5f, 1.0f, 1.0f, 1.0f); m_colors[12] = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f); m_colors[13] = tcu::Vec4(1.0f, 0.0f, 0.5f, 1.0f); m_colors[14] = tcu::Vec4(0.0f, 0.5f, 0.5f, 1.0f); m_colors[15] = tcu::Vec4(1.0f, 1.0f, 0.5f, 1.0f); m_colors[16] = tcu::Vec4(1.0f, 0.0f, 1.0f, 1.0f); m_colors[17] = tcu::Vec4(1.0f, 0.5f, 0.0f, 1.0f); m_colors[18] = tcu::Vec4(0.0f, 1.0f, 0.5f, 1.0f); m_colors[19] = tcu::Vec4(1.0f, 0.5f, 1.0f, 1.0f); m_colors[20] = tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f); m_colors[21] = tcu::Vec4(1.0f, 0.5f, 0.5f, 1.0f); m_colors[22] = tcu::Vec4(0.0f, 0.0f, 0.5f, 1.0f); m_colors[23] = tcu::Vec4(1.0f, 1.0f, 1.0f, 1.0f); // Compute positions. m_positions.resize(numVertices); DE_ASSERT(numVertices%3 == 0); for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx += 3) { const float h = 2.0f * float(quadHeight)/float(viewportH); const float w = 2.0f * float(quadWidth)/float(viewportW); const int triNdx = vtxNdx/3; const int quadNdx = triNdx/2; const int quadY = quadNdx/maxQuadsX; const int quadX = quadNdx%maxQuadsX; const float x0 = -1.0f + quadX*w; const float y0 = -1.0f + quadY*h; if (triNdx%2 == 0) { m_positions[vtxNdx+0] = tcu::Vec4(x0, y0, 0.0f, 1.0f); m_positions[vtxNdx+1] = tcu::Vec4(x0+w, y0+h, 0.0f, 1.0f); m_positions[vtxNdx+2] = tcu::Vec4(x0, y0+h, 0.0f, 1.0f); } else { m_positions[vtxNdx+0] = tcu::Vec4(x0+w, y0+h, 0.0f, 1.0f); m_positions[vtxNdx+1] = tcu::Vec4(x0, y0, 0.0f, 1.0f); m_positions[vtxNdx+2] = tcu::Vec4(x0+w, y0, 0.0f, 1.0f); } } m_viewportW = viewportW; m_viewportH = viewportH; m_iterNdx = 0; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } void VertexIDCase::deinit (void) { delete m_program; m_program = DE_NULL; if (m_positionBuffer) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_positionBuffer); m_positionBuffer = 0; } if (m_elementBuffer) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_elementBuffer); m_elementBuffer = 0; } m_positions.clear(); m_colors.clear(); } class VertexIDReferenceShader : public rr::VertexShader, public rr::FragmentShader { public: enum { VARYINGLOC_COLOR = 0 }; VertexIDReferenceShader () : rr::VertexShader (2, 1) // color and pos in => color out , rr::FragmentShader(1, 1) // color in => color out { this->rr::VertexShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; this->rr::VertexShader::m_inputs[1].type = rr::GENERICVECTYPE_FLOAT; this->rr::VertexShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; this->rr::VertexShader::m_outputs[0].flatshade = false; this->rr::FragmentShader::m_inputs[0].type = rr::GENERICVECTYPE_FLOAT; this->rr::FragmentShader::m_inputs[0].flatshade = false; this->rr::FragmentShader::m_outputs[0].type = rr::GENERICVECTYPE_FLOAT; } void shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const { for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) { const int positionAttrLoc = 0; const int colorAttrLoc = 1; rr::VertexPacket& packet = *packets[packetNdx]; // Transform to position packet.position = rr::readVertexAttribFloat(inputs[positionAttrLoc], packet.instanceNdx, packet.vertexNdx); // Pass color to FS packet.outputs[VARYINGLOC_COLOR] = rr::readVertexAttribFloat(inputs[colorAttrLoc], packet.instanceNdx, packet.vertexNdx); } } void shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const { for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) { rr::FragmentPacket& packet = packets[packetNdx]; for (int fragNdx = 0; fragNdx < 4; ++fragNdx) rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, rr::readVarying<float>(packet, context, VARYINGLOC_COLOR, fragNdx)); } } }; void VertexIDCase::renderReference (const tcu::PixelBufferAccess& dst, const int numVertices, const deUint16* const indices, const tcu::Vec4* const positions, const tcu::Vec4* const colors) { const rr::Renderer referenceRenderer; const rr::RenderState referenceState ((rr::ViewportState)(rr::MultisamplePixelBufferAccess::fromSinglesampleAccess(dst))); const rr::RenderTarget referenceTarget (rr::MultisamplePixelBufferAccess::fromSinglesampleAccess(dst)); const VertexIDReferenceShader referenceShader; rr::VertexAttrib attribs[2]; attribs[0].type = rr::VERTEXATTRIBTYPE_FLOAT; attribs[0].size = 4; attribs[0].stride = 0; attribs[0].instanceDivisor = 0; attribs[0].pointer = positions; attribs[1].type = rr::VERTEXATTRIBTYPE_FLOAT; attribs[1].size = 4; attribs[1].stride = 0; attribs[1].instanceDivisor = 0; attribs[1].pointer = colors; referenceRenderer.draw( rr::DrawCommand( referenceState, referenceTarget, rr::Program(&referenceShader, &referenceShader), 2, attribs, rr::PrimitiveList(rr::PRIMITIVETYPE_TRIANGLES, numVertices, rr::DrawIndices(indices)))); } VertexIDCase::IterateResult VertexIDCase::iterate (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int width = m_context.getRenderTarget().getWidth(); const int height = m_context.getRenderTarget().getHeight(); const int viewportW = m_viewportW; const int viewportH = m_viewportH; const float threshold = 0.02f; de::Random rnd (0xcf23ab1 ^ deInt32Hash(m_iterNdx)); tcu::Surface refImg (viewportW, viewportH); tcu::Surface testImg (viewportW, viewportH); const int viewportX = rnd.getInt(0, width-viewportW); const int viewportY = rnd.getInt(0, height-viewportH); const int posLoc = gl.getAttribLocation(m_program->getProgram(), "a_position"); const int colorsLoc = gl.getUniformLocation(m_program->getProgram(), "u_colors[0]"); const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 1.0f); // Setup common state. gl.viewport (viewportX, viewportY, viewportW, viewportH); gl.useProgram (m_program->getProgram()); gl.bindBuffer (GL_ARRAY_BUFFER, m_positionBuffer); gl.enableVertexAttribArray (posLoc); gl.vertexAttribPointer (posLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); gl.uniform4fv (colorsLoc, (int)m_colors.size(), (const float*)&m_colors[0]); // Clear render target to black. gl.clearColor (clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w()); gl.clear (GL_COLOR_BUFFER_BIT); tcu::clear(refImg.getAccess(), clearColor); if (m_iterNdx == 0) { tcu::ScopedLogSection logSection (m_testCtx.getLog(), "Iter0", "glDrawArrays()"); vector<deUint16> indices (m_positions.size()); gl.bufferData(GL_ARRAY_BUFFER, (int)(m_positions.size()*sizeof(tcu::Vec4)), &m_positions[0], GL_DYNAMIC_DRAW); gl.drawArrays(GL_TRIANGLES, 0, (int)m_positions.size()); glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, testImg.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw"); // Reference indices for (int ndx = 0; ndx < (int)indices.size(); ndx++) indices[ndx] = (deUint16)ndx; renderReference(refImg.getAccess(), (int)m_positions.size(), &indices[0], &m_positions[0], &m_colors[0]); } else if (m_iterNdx == 1) { tcu::ScopedLogSection logSection (m_testCtx.getLog(), "Iter1", "glDrawElements(), indices in client-side array"); vector<deUint16> indices (m_positions.size()); vector<tcu::Vec4> mappedPos (m_positions.size()); // Compute initial indices and suffle for (int ndx = 0; ndx < (int)indices.size(); ndx++) indices[ndx] = (deUint16)ndx; rnd.shuffle(indices.begin(), indices.end()); // Use indices to re-map positions. for (int ndx = 0; ndx < (int)indices.size(); ndx++) mappedPos[indices[ndx]] = m_positions[ndx]; gl.bufferData(GL_ARRAY_BUFFER, (int)(m_positions.size()*sizeof(tcu::Vec4)), &mappedPos[0], GL_DYNAMIC_DRAW); gl.drawElements(GL_TRIANGLES, (int)indices.size(), GL_UNSIGNED_SHORT, &indices[0]); glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, testImg.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw"); renderReference(refImg.getAccess(), (int)indices.size(), &indices[0], &mappedPos[0], &m_colors[0]); } else if (m_iterNdx == 2) { tcu::ScopedLogSection logSection (m_testCtx.getLog(), "Iter2", "glDrawElements(), indices in buffer"); vector<deUint16> indices (m_positions.size()); vector<tcu::Vec4> mappedPos (m_positions.size()); // Compute initial indices and suffle for (int ndx = 0; ndx < (int)indices.size(); ndx++) indices[ndx] = (deUint16)ndx; rnd.shuffle(indices.begin(), indices.end()); // Use indices to re-map positions. for (int ndx = 0; ndx < (int)indices.size(); ndx++) mappedPos[indices[ndx]] = m_positions[ndx]; gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_elementBuffer); gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, (int)(indices.size()*sizeof(deUint16)), &indices[0], GL_DYNAMIC_DRAW); gl.bufferData(GL_ARRAY_BUFFER, (int)(m_positions.size()*sizeof(tcu::Vec4)), &mappedPos[0], GL_DYNAMIC_DRAW); gl.drawElements(GL_TRIANGLES, (int)indices.size(), GL_UNSIGNED_SHORT, DE_NULL); glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, testImg.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "Draw"); tcu::clear(refImg.getAccess(), clearColor); renderReference(refImg.getAccess(), (int)indices.size(), &indices[0], &mappedPos[0], &m_colors[0]); } else DE_ASSERT(false); if (!tcu::fuzzyCompare(m_testCtx.getLog(), "Result", "Image comparison result", refImg, testImg, threshold, tcu::COMPARE_LOG_RESULT)) m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); m_iterNdx += 1; return (m_iterNdx < 3) ? CONTINUE : STOP; } ShaderBuiltinVarTests::ShaderBuiltinVarTests (Context& context) : TestCaseGroup(context, "builtin_variable", "Built-in Variable Tests") { } ShaderBuiltinVarTests::~ShaderBuiltinVarTests (void) { } void ShaderBuiltinVarTests::init (void) { // Builtin constants. static const struct { const char* caseName; const char* varName; ShaderBuiltinConstantCase::GetConstantValueFunc getValue; } builtinConstants[] = { // GLES 2. { "max_vertex_attribs", "gl_MaxVertexAttribs", getInteger<GL_MAX_VERTEX_ATTRIBS> }, { "max_vertex_uniform_vectors", "gl_MaxVertexUniformVectors", getInteger<GL_MAX_VERTEX_UNIFORM_VECTORS> }, { "max_fragment_uniform_vectors", "gl_MaxFragmentUniformVectors", getInteger<GL_MAX_FRAGMENT_UNIFORM_VECTORS> }, { "max_texture_image_units", "gl_MaxTextureImageUnits", getInteger<GL_MAX_TEXTURE_IMAGE_UNITS> }, { "max_vertex_texture_image_units", "gl_MaxVertexTextureImageUnits", getInteger<GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS> }, { "max_combined_texture_image_units", "gl_MaxCombinedTextureImageUnits", getInteger<GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS> }, { "max_draw_buffers", "gl_MaxDrawBuffers", getInteger<GL_MAX_DRAW_BUFFERS> }, // GLES 3. { "max_vertex_output_vectors", "gl_MaxVertexOutputVectors", getVectorsFromComps<GL_MAX_VERTEX_OUTPUT_COMPONENTS> }, { "max_fragment_input_vectors", "gl_MaxFragmentInputVectors", getVectorsFromComps<GL_MAX_FRAGMENT_INPUT_COMPONENTS> }, { "min_program_texel_offset", "gl_MinProgramTexelOffset", getInteger<GL_MIN_PROGRAM_TEXEL_OFFSET> }, { "max_program_texel_offset", "gl_MaxProgramTexelOffset", getInteger<GL_MAX_PROGRAM_TEXEL_OFFSET> } }; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(builtinConstants); ndx++) { const char* const caseName = builtinConstants[ndx].caseName; const char* const varName = builtinConstants[ndx].varName; const ShaderBuiltinConstantCase::GetConstantValueFunc getValue = builtinConstants[ndx].getValue; addChild(new ShaderBuiltinConstantCase(m_context, (string(caseName) + "_vertex").c_str(), varName, varName, getValue, glu::SHADERTYPE_VERTEX)); addChild(new ShaderBuiltinConstantCase(m_context, (string(caseName) + "_fragment").c_str(), varName, varName, getValue, glu::SHADERTYPE_FRAGMENT)); } addChild(new ShaderDepthRangeTest(m_context, "depth_range_vertex", "gl_DepthRange", true)); addChild(new ShaderDepthRangeTest(m_context, "depth_range_fragment", "gl_DepthRange", false)); // Vertex shader builtin variables. addChild(new VertexIDCase (m_context)); // \todo [2013-03-20 pyry] gl_InstanceID -- tested in instancing tests quite thoroughly. // Fragment shader builtin variables. addChild(new FragCoordXYZCase (m_context)); addChild(new FragCoordWCase (m_context)); addChild(new PointCoordCase (m_context)); addChild(new FrontFacingCase (m_context)); } } // Functional } // gles3 } // deqp