/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 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 Multisample texture size tests *//*--------------------------------------------------------------------*/ #include "es31fShaderTextureSizeTests.hpp" #include "gluRenderContext.hpp" #include "gluShaderProgram.hpp" #include "gluPixelTransfer.hpp" #include "gluContextInfo.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "tcuTestLog.hpp" #include "tcuStringTemplate.hpp" #include "tcuSurface.hpp" #include "tcuRenderTarget.hpp" #include "deStringUtil.hpp" using namespace glw; namespace deqp { namespace gles31 { namespace Functional { namespace { static const char* const s_positionVertexShaderSource = "#version 310 es\n" "in highp vec4 a_position;\n" "void main (void)\n" "{\n" " gl_Position = a_position;\n" "}\n"; class TextureSizeCase : public TestCase { public: enum TextureType { TEXTURE_FLOAT_2D = 0, TEXTURE_FLOAT_2D_ARRAY, TEXTURE_INT_2D, TEXTURE_INT_2D_ARRAY, TEXTURE_UINT_2D, TEXTURE_UINT_2D_ARRAY, TEXTURE_LAST }; TextureSizeCase (Context& context, const char* name, const char* desc, TextureType type, int samples); ~TextureSizeCase (void); private: void init (void); void deinit (void); IterateResult iterate (void); std::string genFragmentSource (void); glw::GLenum getTextureGLTarget (void); glw::GLenum getTextureGLInternalFormat (void); void createTexture (const tcu::IVec3& size); void deleteTexture (void); void runShader (tcu::Surface& dst, const tcu::IVec3& size); bool verifyImage (const tcu::Surface& dst); const TextureType m_type; const int m_numSamples; const bool m_isArrayType; glw::GLuint m_texture; glw::GLuint m_vbo; glu::ShaderProgram* m_shader; std::vector m_iterations; int m_iteration; bool m_allIterationsPassed; bool m_allCasesSkipped; }; TextureSizeCase::TextureSizeCase (Context& context, const char* name, const char* desc, TextureType type, int samples) : TestCase (context, name, desc) , m_type (type) , m_numSamples (samples) , m_isArrayType (m_type == TEXTURE_FLOAT_2D_ARRAY || m_type == TEXTURE_INT_2D_ARRAY || m_type == TEXTURE_UINT_2D_ARRAY) , m_texture (0) , m_vbo (0) , m_shader (DE_NULL) , m_iteration (0) , m_allIterationsPassed (true) , m_allCasesSkipped (true) { DE_ASSERT(type < TEXTURE_LAST); } TextureSizeCase::~TextureSizeCase (void) { deinit(); } void TextureSizeCase::init (void) { static const tcu::IVec2 testSizes2D[] = { tcu::IVec2(1, 1), tcu::IVec2(1, 4), tcu::IVec2(4, 8), tcu::IVec2(21, 11), tcu::IVec2(107, 254), tcu::IVec2(-1, 3), tcu::IVec2(3, -1), }; static const tcu::IVec3 testSizes3D[] = { tcu::IVec3(1, 1, 1), tcu::IVec3(1, 4, 7), tcu::IVec3(4, 8, 12), tcu::IVec3(21, 11, 9), tcu::IVec3(107, 254, 2), tcu::IVec3(-1, 3, 3), tcu::IVec3(3, -1, 3), tcu::IVec3(4, 4, -1), }; static const tcu::Vec4 fullscreenQuad[] = { 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 glw::Functions& gl = m_context.getRenderContext().getFunctions(); // requirements if (m_isArrayType && !m_context.getContextInfo().isExtensionSupported("GL_OES_texture_storage_multisample_2d_array")) TCU_THROW(NotSupportedError, "Test requires OES_texture_storage_multisample_2d_array extension"); if (m_context.getRenderTarget().getWidth() < 1 || m_context.getRenderTarget().getHeight() < 1) TCU_THROW(NotSupportedError, "rendertarget size must be at least 1x1"); glw::GLint maxTextureSize = 0; glw::GLint maxTextureLayers = 0; glw::GLint maxSamples = 0; gl.getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); gl.getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxTextureLayers); gl.getInternalformativ(getTextureGLTarget(), getTextureGLInternalFormat(), GL_SAMPLES, 1, &maxSamples); if (m_numSamples > maxSamples) TCU_THROW(NotSupportedError, "sample count is not supported"); // gen shade m_shader = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(s_positionVertexShaderSource) << glu::FragmentSource(genFragmentSource())); m_testCtx.getLog() << *m_shader; if (!m_shader->isOk()) throw tcu::TestError("shader build failed"); // gen buffer gl.genBuffers(1, &m_vbo); gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo); gl.bufferData(GL_ARRAY_BUFFER, sizeof(fullscreenQuad), fullscreenQuad, GL_STATIC_DRAW); // gen iterations m_testCtx.getLog() << tcu::TestLog::Message << "GL_MAX_TEXTURE_SIZE = " << maxTextureSize << "\n" << "GL_MAX_ARRAY_TEXTURE_LAYERS = " << maxTextureLayers << tcu::TestLog::EndMessage; if (!m_isArrayType) { for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(testSizes2D); ++ndx) { if (testSizes2D[ndx].x() <= maxTextureSize && testSizes2D[ndx].y() <= maxTextureSize) { const int w = (testSizes2D[ndx].x() < 0) ? (maxTextureSize) : (testSizes2D[ndx].x()); const int h = (testSizes2D[ndx].y() < 0) ? (maxTextureSize) : (testSizes2D[ndx].y()); m_iterations.push_back(tcu::IVec3(w, h, 0)); } } } else { for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(testSizes3D); ++ndx) { if (testSizes3D[ndx].x() <= maxTextureSize && testSizes3D[ndx].y() <= maxTextureSize && testSizes3D[ndx].z() <= maxTextureLayers) { const int w = (testSizes3D[ndx].x() < 0) ? (maxTextureSize) : (testSizes3D[ndx].x()); const int h = (testSizes3D[ndx].y() < 0) ? (maxTextureSize) : (testSizes3D[ndx].y()); const int d = (testSizes3D[ndx].z() < 0) ? (maxTextureLayers) : (testSizes3D[ndx].z()); m_iterations.push_back(tcu::IVec3(w, h, d)); } } } } void TextureSizeCase::deinit (void) { if (m_texture) { m_context.getRenderContext().getFunctions().deleteTextures(1, &m_texture); m_texture = 0; } if (m_vbo) { m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_vbo); m_vbo = 0; } if (m_shader) { delete m_shader; m_shader = DE_NULL; } } TextureSizeCase::IterateResult TextureSizeCase::iterate (void) { tcu::Surface result (1, 1); bool skipTest = false; m_testCtx.getLog() << tcu::TestLog::Message << "\nIteration " << (m_iteration+1) << " / " << (int)m_iterations.size() << tcu::TestLog::EndMessage; try { // set texture size createTexture(m_iterations[m_iteration]); // query texture size runShader(result, m_iterations[m_iteration]); } catch (glu::OutOfMemoryError&) { m_testCtx.getLog() << tcu::TestLog::Message << "Got GL_OUT_OF_MEMORY, skipping this size" << tcu::TestLog::EndMessage; skipTest = true; } // free resources deleteTexture(); // queried value was correct? if (!skipTest) { m_allCasesSkipped = false; if (!verifyImage(result)) m_allIterationsPassed = false; } // final result if (++m_iteration < (int)m_iterations.size()) return CONTINUE; if (!m_allIterationsPassed) { m_testCtx.getLog() << tcu::TestLog::Message << "One or more test sizes failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got invalid texture size"); } else if (m_allCasesSkipped) { m_testCtx.getLog() << tcu::TestLog::Message << "Could not test any texture size, texture creation failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "All test texture creations failed"); } else { m_testCtx.getLog() << tcu::TestLog::Message << "All texture sizes passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } return STOP; } std::string TextureSizeCase::genFragmentSource (void) { static const char* const templateSource = "#version 310 es\n" "${EXTENSION_STATEMENT}" "layout(location = 0) out highp vec4 fragColor;\n" "uniform highp ${SAMPLERTYPE} u_sampler;\n" "uniform highp ${SIZETYPE} u_size;\n" "void main (void)\n" "{\n" " const highp vec4 okColor = vec4(0.0, 1.0, 0.0, 1.0);\n" " const highp vec4 failColor = vec4(1.0, 0.0, 0.0, 1.0);\n" " fragColor = (textureSize(u_sampler) == u_size) ? (okColor) : (failColor);\n" "}\n"; std::map args; switch (m_type) { case TEXTURE_FLOAT_2D: args["SAMPLERTYPE"] = "sampler2DMS"; break; case TEXTURE_FLOAT_2D_ARRAY: args["SAMPLERTYPE"] = "sampler2DMSArray"; break; case TEXTURE_INT_2D: args["SAMPLERTYPE"] = "isampler2DMS"; break; case TEXTURE_INT_2D_ARRAY: args["SAMPLERTYPE"] = "isampler2DMSArray"; break; case TEXTURE_UINT_2D: args["SAMPLERTYPE"] = "usampler2DMS"; break; case TEXTURE_UINT_2D_ARRAY: args["SAMPLERTYPE"] = "usampler2DMSArray"; break; default: DE_ASSERT(DE_FALSE); } if (!m_isArrayType) args["SIZETYPE"] = "ivec2"; else args["SIZETYPE"] = "ivec3"; if (m_isArrayType) args["EXTENSION_STATEMENT"] = "#extension GL_OES_texture_storage_multisample_2d_array : require\n"; else args["EXTENSION_STATEMENT"] = ""; return tcu::StringTemplate(templateSource).specialize(args); } glw::GLenum TextureSizeCase::getTextureGLTarget (void) { switch (m_type) { case TEXTURE_FLOAT_2D: return GL_TEXTURE_2D_MULTISAMPLE; case TEXTURE_FLOAT_2D_ARRAY: return GL_TEXTURE_2D_MULTISAMPLE_ARRAY; case TEXTURE_INT_2D: return GL_TEXTURE_2D_MULTISAMPLE; case TEXTURE_INT_2D_ARRAY: return GL_TEXTURE_2D_MULTISAMPLE_ARRAY; case TEXTURE_UINT_2D: return GL_TEXTURE_2D_MULTISAMPLE; case TEXTURE_UINT_2D_ARRAY: return GL_TEXTURE_2D_MULTISAMPLE_ARRAY; default: DE_ASSERT(DE_FALSE); return 0; } } glw::GLenum TextureSizeCase::getTextureGLInternalFormat (void) { switch (m_type) { case TEXTURE_FLOAT_2D: return GL_RGBA8; case TEXTURE_FLOAT_2D_ARRAY: return GL_RGBA8; case TEXTURE_INT_2D: return GL_R8I; case TEXTURE_INT_2D_ARRAY: return GL_R8I; case TEXTURE_UINT_2D: return GL_R8UI; case TEXTURE_UINT_2D_ARRAY: return GL_R8UI; default: DE_ASSERT(DE_FALSE); return 0; } } void TextureSizeCase::createTexture (const tcu::IVec3& size) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (!m_isArrayType) m_testCtx.getLog() << tcu::TestLog::Message << "Creating texture with size " << size.x() << "x" << size.y() << tcu::TestLog::EndMessage; else m_testCtx.getLog() << tcu::TestLog::Message << "Creating texture with size " << size.x() << "x" << size.y() << "x" << size.z() << tcu::TestLog::EndMessage; gl.genTextures(1, &m_texture); gl.bindTexture(getTextureGLTarget(), m_texture); GLU_EXPECT_NO_ERROR(gl.getError(), "texture gen"); if (!m_isArrayType) gl.texStorage2DMultisample(getTextureGLTarget(), m_numSamples, getTextureGLInternalFormat(), size.x(), size.y(), GL_FALSE); else gl.texStorage3DMultisample(getTextureGLTarget(), m_numSamples, getTextureGLInternalFormat(), size.x(), size.y(), size.z(), GL_FALSE); GLU_EXPECT_NO_ERROR(gl.getError(), "texStorage"); } void TextureSizeCase::deleteTexture (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (m_texture) { gl.deleteTextures(1, &m_texture); m_texture = 0; GLU_EXPECT_NO_ERROR(gl.getError(), "texture delete"); } } void TextureSizeCase::runShader (tcu::Surface& dst, const tcu::IVec3& size) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const int positionLoc = gl.getAttribLocation(m_shader->getProgram(), "a_position"); const int shaderSamplerLoc = gl.getUniformLocation(m_shader->getProgram(), "u_sampler"); const int shaderSizeLoc = gl.getUniformLocation(m_shader->getProgram(), "u_size"); m_testCtx.getLog() << tcu::TestLog::Message << "Running the verification shader." << tcu::TestLog::EndMessage; GLU_EXPECT_NO_ERROR(gl.getError(), "preclear"); gl.viewport(0, 0, 1, 1); gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo); gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); gl.enableVertexAttribArray(positionLoc); GLU_EXPECT_NO_ERROR(gl.getError(), "vertexAttrib"); gl.useProgram(m_shader->getProgram()); gl.uniform1i(shaderSamplerLoc, 0); if (m_isArrayType) gl.uniform3iv(shaderSizeLoc, 1, size.getPtr()); else gl.uniform2iv(shaderSizeLoc, 1, size.getPtr()); GLU_EXPECT_NO_ERROR(gl.getError(), "setup program"); gl.bindTexture(getTextureGLTarget(), m_texture); GLU_EXPECT_NO_ERROR(gl.getError(), "bindtex"); gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4); GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays"); gl.disableVertexAttribArray(positionLoc); gl.useProgram(0); GLU_EXPECT_NO_ERROR(gl.getError(), "cleanup"); gl.finish(); glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels"); } bool TextureSizeCase::verifyImage (const tcu::Surface& dst) { DE_ASSERT(dst.getWidth() == 1 && dst.getHeight() == 1); const int colorThresholdRed = 1 << (8 - m_context.getRenderTarget().getPixelFormat().redBits); const int colorThresholdGreen = 1 << (8 - m_context.getRenderTarget().getPixelFormat().greenBits); const int colorThresholdBlue = 1 << (8 - m_context.getRenderTarget().getPixelFormat().blueBits); const tcu::RGBA color = dst.getPixel(0,0); m_testCtx.getLog() << tcu::TestLog::Message << "Verifying image." << tcu::TestLog::EndMessage; // green if (color.getRed() < colorThresholdRed && color.getGreen() > 255 - colorThresholdGreen && color.getBlue() < colorThresholdBlue) { m_testCtx.getLog() << tcu::TestLog::Message << "Result ok." << tcu::TestLog::EndMessage; return true; } // red else if (color.getRed() > 255 - colorThresholdRed && color.getGreen() < colorThresholdGreen && color.getBlue() < colorThresholdBlue) { m_testCtx.getLog() << tcu::TestLog::Message << "Image size incorrect." << tcu::TestLog::EndMessage; return false; } m_testCtx.getLog() << tcu::TestLog::Message << "Expected either green or red pixel, got " << color << tcu::TestLog::EndMessage; return false; } } // anonymous ShaderTextureSizeTests::ShaderTextureSizeTests (Context& context) : TestCaseGroup(context, "texture_size", "Texture size tests") { } ShaderTextureSizeTests::~ShaderTextureSizeTests (void) { } void ShaderTextureSizeTests::init (void) { static const struct SamplerType { TextureSizeCase::TextureType type; const char* name; } samplerTypes[] = { { TextureSizeCase::TEXTURE_FLOAT_2D, "texture_2d" }, { TextureSizeCase::TEXTURE_FLOAT_2D_ARRAY, "texture_2d_array" }, { TextureSizeCase::TEXTURE_INT_2D, "texture_int_2d" }, { TextureSizeCase::TEXTURE_INT_2D_ARRAY, "texture_int_2d_array" }, { TextureSizeCase::TEXTURE_UINT_2D, "texture_uint_2d" }, { TextureSizeCase::TEXTURE_UINT_2D_ARRAY, "texture_uint_2d_array" }, }; static const int sampleCounts[] = { 1, 4 }; for (int samplerTypeNdx = 0; samplerTypeNdx < DE_LENGTH_OF_ARRAY(samplerTypes); ++samplerTypeNdx) { for (int sampleCountNdx = 0; sampleCountNdx < DE_LENGTH_OF_ARRAY(sampleCounts); ++sampleCountNdx) { const std::string name = std::string() + "samples_" + de::toString(sampleCounts[sampleCountNdx]) + "_" + samplerTypes[samplerTypeNdx].name; const std::string desc = std::string() + "samples count = " + de::toString(sampleCounts[sampleCountNdx]) + ", type = " + samplerTypes[samplerTypeNdx].name; addChild(new TextureSizeCase(m_context, name.c_str(), desc.c_str(), samplerTypes[samplerTypeNdx].type, sampleCounts[sampleCountNdx])); } } } } // Functional } // gles31 } // deqp