/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 Module * ------------------------------------------------- * * Copyright 2016 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 Negative Shader Storage Buffer Object (SSBO) tests. *//*--------------------------------------------------------------------*/ #include "es31fNegativeSSBOBlockTests.hpp" #include "glwDefs.hpp" #include "glwEnums.hpp" #include "tcuStringTemplate.hpp" #include "gluShaderProgram.hpp" #include namespace deqp { namespace gles31 { namespace Functional { namespace NegativeTestShared { namespace { using tcu::TestLog; using glu::CallLogWrapper; using namespace glw; namespace args { enum ArgMember { ARGMEMBER_FORMAT = 0, ARGMEMBER_BINDING_POINT, ARGMEMBER_MATRIX_ORDER, ARGMEMBER_MEMBER_TYPE, ARGMEMBER_NAME, ARGMEMBER_FIXED_ARRAY, ARGMEMBER_VARIABLE_ARRAY, ARGMEMBER_REORDER }; // key pair ssbo arg data struct SsboArgData { ArgMember member; std::string data; SsboArgData(const ArgMember& member_, const std::string& data_) { member = member_; data = data_; } }; // class which manages string based argument used to build varying ssbo interface blocks and members class SsboArgs { public: SsboArgs(const std::string version, tcu::TestLog& log); void setSingleValue (const SsboArgData argData); bool setAllValues (const std::vector argDataList); bool getMemberReorder (void) const; void resetValues (void); std::map populateArgsMap (void) const; private: std::string m_negativeContextVersion; std::string m_stdFormat; std::string m_bindingPoint; std::string m_matrixOrder; std::string m_memberType; std::string m_memberName; std::string m_memberFixedArrayerName; std::string m_memberVariableArray; bool m_memberReorder; int m_numberMembers; tcu::TestLog& m_testLog; void setDefaultValues (void); }; //constructor which ensure a proper context is passed into the struct SsboArgs::SsboArgs(const std::string version, tcu::TestLog& log) : m_negativeContextVersion (version) , m_numberMembers (8) , m_testLog (log) { setDefaultValues(); } void SsboArgs::setSingleValue (const SsboArgData argData) { std::string message; switch (argData.member) { case ARGMEMBER_FORMAT: m_stdFormat = argData.data; return; case ARGMEMBER_BINDING_POINT: m_bindingPoint = argData.data; return; case ARGMEMBER_MATRIX_ORDER: m_matrixOrder = argData.data; return; case ARGMEMBER_MEMBER_TYPE: m_memberType = argData.data; return; case ARGMEMBER_NAME: m_memberName = argData.data; return; case ARGMEMBER_FIXED_ARRAY: m_memberFixedArrayerName = argData.data; return; case ARGMEMBER_VARIABLE_ARRAY: m_memberVariableArray = argData.data; return; case ARGMEMBER_REORDER: if (argData.data == "true") { m_memberReorder = true; } return; default: message = "auto loop argument data member not recognised."; m_testLog << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; } } bool SsboArgs::setAllValues (const std::vector argDataList) { std::string message; if ((argDataList.size() == 0) || (argDataList.size() > (size_t)m_numberMembers)) { message = "set of args does not match the number of args struct changeable members."; m_testLog << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; return false; } else { for (unsigned int idx = 0; idx < argDataList.size(); idx++) { setSingleValue(argDataList[idx]); } } return true; } bool SsboArgs::getMemberReorder (void) const { return m_memberReorder; } void SsboArgs::resetValues (void) { setDefaultValues(); } //converts SsboArgs member variable into a map object to be used by tcu::StringTemplate std::map SsboArgs::populateArgsMap (void) const { std::map argsMap; // key placeholders located at specific points in the ssbo block argsMap["NEGATIVE_CONTEXT_VERSION"] = m_negativeContextVersion; argsMap["STD_FORMAT"] = m_stdFormat; argsMap["BINDING_POINT"] = m_bindingPoint; argsMap["MATRIX_ORDER"] = m_matrixOrder; argsMap["MEMBER_TYPE"] = m_memberType; argsMap["MEMBER_NAME"] = m_memberName; argsMap["MEMBER_FIXED_ARRAY"] = m_memberFixedArrayerName; argsMap["MEMBER_VARIABLE_ARRAY"] = m_memberVariableArray; return argsMap; } // default values i.e. same shader template void SsboArgs::setDefaultValues (void) { m_stdFormat = "std430"; m_bindingPoint = "0"; m_matrixOrder = "column_major"; m_memberType = "int"; m_memberName = "matrix"; m_memberFixedArrayerName = "10"; m_memberVariableArray = ""; m_memberReorder = false; } } // args std::string generateVaryingSSBOShader(const glw::GLenum shaderType, const args::SsboArgs& args, tcu::TestLog& log) { std::map argsMap; std::ostringstream source; std::string sourceString; std::stringstream ssboString; std::string message; if (args.getMemberReorder()) { ssboString << " mediump vec4 array_1[${MEMBER_FIXED_ARRAY}];\n" << " highp mat4 ${MEMBER_NAME};\n" << " lowp ${MEMBER_TYPE} data;\n" << " mediump float array_2[${MEMBER_VARIABLE_ARRAY}];\n"; } else { ssboString << " lowp ${MEMBER_TYPE} data;\n" << " highp mat4 ${MEMBER_NAME};\n" << " mediump vec4 array_1[${MEMBER_FIXED_ARRAY}];\n" << " mediump float array_2[${MEMBER_VARIABLE_ARRAY}];\n"; } argsMap = args.populateArgsMap(); switch (shaderType) { case GL_VERTEX_SHADER: { source << "${NEGATIVE_CONTEXT_VERSION}\n" << "layout (location = 0) in highp vec4 position;\n" << "layout (location = 1) in mediump vec4 colour;\n" << "out mediump vec4 vertex_colour;\n" << "layout (${STD_FORMAT}, binding = ${BINDING_POINT}, ${MATRIX_ORDER}) buffer ssbo_block\n" << "{\n"; source << ssboString.str(); source << "} ssbo;\n" << "void main()\n" << "{\n" << " mediump vec4 variable;\n" << " gl_Position = ssbo.${MEMBER_NAME} * position;\n" << " for (int idx = 0; idx < ${MEMBER_FIXED_ARRAY}; idx++)\n" << " {\n" << " variable += ssbo.array_1[idx];\n" << " }\n" << " vertex_colour = colour + variable;\n" << "}\n"; sourceString = source.str(); sourceString = tcu::StringTemplate(sourceString).specialize(argsMap); return sourceString; } case GL_FRAGMENT_SHADER: { source << "${NEGATIVE_CONTEXT_VERSION}\n" << "in mediump vec4 vertex_colour;\n" << "layout (location = 0) out mediump vec4 fragment_colour;\n" << "layout (${STD_FORMAT}, binding = ${BINDING_POINT}, ${MATRIX_ORDER}) buffer ssbo_block\n" << "{\n"; source << ssboString.str(); source << "} ssbo;\n" << "void main()\n" << "{\n" << " mediump vec4 variable;\n" << " variable * ssbo.${MEMBER_NAME};\n" << " for (int idx = 0; idx < ${MEMBER_FIXED_ARRAY}; idx++)\n" << " {\n" << " variable += ssbo.array_1[idx];\n" << " }\n" << " fragment_colour = vertex_colour + variable;\n" << "}\n"; sourceString = source.str(); sourceString = tcu::StringTemplate(sourceString).specialize(argsMap); return sourceString; } case GL_GEOMETRY_SHADER: { // TODO: return sourceString; } case GL_TESS_CONTROL_SHADER: { // TODO: return sourceString; } case GL_TESS_EVALUATION_SHADER: { // TODO: return sourceString; } case GL_COMPUTE_SHADER: { // TODO: return sourceString; } default: { message = "shader type not recognised."; log << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; } } return std::string(); } void logProgramInfo(NegativeTestContext& ctx, GLint program) { GLint maxLength = 0; std::string message; tcu::TestLog& log = ctx.getLog(); ctx.glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); message = "Program log:"; log << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; if (maxLength == 0) { message = "No available info log."; log << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; return; } std::vector infoLog(maxLength); ctx.glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]); std::string programLogMessage(&infoLog[0], maxLength); log << tcu::TestLog::Message << programLogMessage << tcu::TestLog::EndMessage; } void ssbo_block_matching(NegativeTestContext& ctx) { const bool isES32 = contextSupports(ctx.getRenderContext().getType(), glu::ApiType::es(3, 2)); const glu::GLSLVersion version = isES32 ? glu::GLSL_VERSION_320_ES : glu::GLSL_VERSION_310_ES; tcu::TestLog& log = ctx.getLog(); std::string message; std::string versionString(glu::getGLSLVersionDeclaration(version)); args::SsboArgs ssboArgs(versionString, log); GLint shaderVertexGL; std::string shaderVertexString; const char* shaderVertexCharPtr; // List of arguments used to create varying ssbo objects in the fragment shader const args::SsboArgData argDataArrayFrag[] = { args::SsboArgData(args::ARGMEMBER_FORMAT, "std140"), args::SsboArgData(args::ARGMEMBER_BINDING_POINT, "10"), args::SsboArgData(args::ARGMEMBER_MATRIX_ORDER, "row_major"), args::SsboArgData(args::ARGMEMBER_MEMBER_TYPE, "vec2"), args::SsboArgData(args::ARGMEMBER_NAME, "name_changed"), args::SsboArgData(args::ARGMEMBER_FIXED_ARRAY, "20"), args::SsboArgData(args::ARGMEMBER_VARIABLE_ARRAY, "5"), args::SsboArgData(args::ARGMEMBER_REORDER, "true") }; std::vector argDataVectorFrag(argDataArrayFrag, argDataArrayFrag + sizeof(argDataArrayFrag) / sizeof(argDataArrayFrag[0])); // create default vertex shader shaderVertexString = generateVaryingSSBOShader(GL_VERTEX_SHADER, ssboArgs, log); shaderVertexCharPtr = shaderVertexString.c_str(); shaderVertexGL = ctx.glCreateShader(GL_VERTEX_SHADER); // log message = shaderVertexString; log << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; // compile ctx.glShaderSource(shaderVertexGL, 1, &shaderVertexCharPtr, DE_NULL); ctx.glCompileShader(shaderVertexGL); for (std::size_t idx = 0; idx < argDataVectorFrag.size(); ++idx) { GLint linkStatus = -1; GLint program; GLint shaderFragmentGL; std::string shaderFragmentString; const char* shaderFragmentCharPtr; ctx.beginSection("Multiple shaders created using SSBO's sharing the same name but not matching layouts"); program = ctx.glCreateProgram(); // reset args to default and make a single change ssboArgs.resetValues(); ssboArgs.setSingleValue(argDataVectorFrag[idx]); // create fragment shader shaderFragmentString = generateVaryingSSBOShader(GL_FRAGMENT_SHADER, ssboArgs, log); shaderFragmentCharPtr = shaderFragmentString.c_str(); shaderFragmentGL = ctx.glCreateShader(GL_FRAGMENT_SHADER); // log message = shaderFragmentString; log << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; // compile ctx.glShaderSource(shaderFragmentGL, 1, &shaderFragmentCharPtr, DE_NULL); ctx.glCompileShader(shaderFragmentGL); // attach shaders to program and attempt to link ctx.glAttachShader(program, shaderVertexGL); ctx.glAttachShader(program, shaderFragmentGL); ctx.glLinkProgram(program); ctx.glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); logProgramInfo(ctx, program); if (linkStatus == GL_TRUE) { ctx.fail("Program should not have linked"); } // clean up resources ctx.glDeleteShader(shaderFragmentGL); ctx.glDeleteProgram(program); ctx.endSection(); } // clean up default resources ctx.glDeleteShader(shaderVertexGL); } void ssbo_block_shared_qualifier(NegativeTestContext& ctx) { const bool isES32 = contextSupports(ctx.getRenderContext().getType(), glu::ApiType::es(3, 2)); const glu::GLSLVersion version = isES32 ? glu::GLSL_VERSION_320_ES : glu::GLSL_VERSION_310_ES; tcu::TestLog& log = ctx.getLog(); std::string message; std::string versionString(glu::getGLSLVersionDeclaration(version)); args::SsboArgs ssboArgs(versionString, log); bool result; GLint shaderVertexGL; std::string shaderVertexString; const char* shaderVertexCharPtr; // default args used in vertex shader ssbo const args::SsboArgData argDataArrayVert[] = { args::SsboArgData(args::ARGMEMBER_FORMAT, "shared"), args::SsboArgData(args::ARGMEMBER_BINDING_POINT, "0"), args::SsboArgData(args::ARGMEMBER_MATRIX_ORDER, "column_major"), args::SsboArgData(args::ARGMEMBER_FIXED_ARRAY, "10"), args::SsboArgData(args::ARGMEMBER_VARIABLE_ARRAY, "10"), args::SsboArgData(args::ARGMEMBER_REORDER, "false") }; std::vector argDataVectorVert(argDataArrayVert, argDataArrayVert + sizeof(argDataArrayVert) / sizeof(argDataArrayVert[0])); // args changed in fragment shader ssbo const args::SsboArgData argDataArrayFrag[] = { args::SsboArgData(args::ARGMEMBER_MATRIX_ORDER, "row_major"), args::SsboArgData(args::ARGMEMBER_VARIABLE_ARRAY, ""), args::SsboArgData(args::ARGMEMBER_FIXED_ARRAY, "20") }; std::vector argDataVectorFrag(argDataArrayFrag, argDataArrayFrag + sizeof(argDataArrayFrag) / sizeof(argDataArrayFrag[0])); // set default vertex ssbo args result = ssboArgs.setAllValues(argDataVectorVert); if (result == false) { message = "Invalid use of args.setAllValues()"; log << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; return; } // create default vertex shader shaderVertexString = generateVaryingSSBOShader(GL_VERTEX_SHADER, ssboArgs, log); shaderVertexCharPtr = shaderVertexString.c_str(); shaderVertexGL = ctx.glCreateShader(GL_VERTEX_SHADER); // log message = shaderVertexString; log << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; // compile ctx.glShaderSource(shaderVertexGL, 1, &shaderVertexCharPtr, DE_NULL); ctx.glCompileShader(shaderVertexGL); for (std::size_t idx = 0; idx < argDataVectorFrag.size(); idx++) { GLint linkStatus = -1; GLint program; GLint shaderFragmentGL; std::string shaderFragmentString; const char* shaderFragmentCharPtr; ctx.beginSection("Multiple shaders created using SSBO's sharing the same name but not matching layouts"); program = ctx.glCreateProgram(); // reset args to default and make a single change ssboArgs.setAllValues(argDataVectorVert); ssboArgs.setSingleValue(argDataVectorFrag[idx]); // create fragment shader shaderFragmentString = generateVaryingSSBOShader(GL_FRAGMENT_SHADER, ssboArgs, log); shaderFragmentCharPtr = shaderFragmentString.c_str(); shaderFragmentGL = ctx.glCreateShader(GL_FRAGMENT_SHADER); // log message = shaderFragmentString; log << tcu::TestLog::Message << message << tcu::TestLog::EndMessage; // compile ctx.glShaderSource(shaderFragmentGL, 1, &shaderFragmentCharPtr, DE_NULL); ctx.glCompileShader(shaderFragmentGL); // attach shaders to the program and attempt to link ctx.glAttachShader(program, shaderVertexGL); ctx.glAttachShader(program, shaderFragmentGL); ctx.glLinkProgram(program); ctx.glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); logProgramInfo(ctx, program); if (linkStatus == GL_TRUE) { ctx.fail("Program should not have linked"); } // clean up resources ctx.glDeleteShader(shaderFragmentGL); ctx.glDeleteProgram(program); ctx.endSection(); } // clean up default resources ctx.glDeleteShader(shaderVertexGL); } } // anonymous std::vector getNegativeSSBOBlockTestFunctions (void) { const FunctionContainer funcs[] = { { ssbo_block_matching, "ssbo_block_interface_matching_tests", "Invalid Shader Linkage" }, { ssbo_block_shared_qualifier, "ssbo_using_shared_qualifier_tests", "Invalid Shader Linkage" }, }; return std::vector(DE_ARRAY_BEGIN(funcs), DE_ARRAY_END(funcs)); } } // NegativeTestShared } //Functional } //gles31 } //deqp