| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL (ES) 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 Compiler test case. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "glsShaderLibraryCase.hpp" |
| |
| #include "tcuTestLog.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuSurface.hpp" |
| |
| #include "tcuStringTemplate.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "gluDrawUtil.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluStrUtil.hpp" |
| |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| |
| #include "deRandom.hpp" |
| #include "deInt32.h" |
| #include "deMath.h" |
| #include "deString.h" |
| #include "deStringUtil.hpp" |
| #include "deSharedPtr.hpp" |
| |
| #include <map> |
| #include <vector> |
| #include <string> |
| #include <sstream> |
| |
| namespace deqp |
| { |
| namespace gls |
| { |
| |
| using namespace tcu; |
| using namespace glu; |
| using namespace glu::sl; |
| |
| using std::map; |
| using std::ostringstream; |
| using std::pair; |
| using std::string; |
| using std::vector; |
| |
| using de::SharedPtr; |
| |
| // OpenGL-specific specialization utils |
| |
| static vector<RequiredExtension> checkAndSpecializeExtensions(const vector<RequiredExtension> &src, |
| const ContextInfo &ctxInfo) |
| { |
| vector<RequiredExtension> specialized; |
| |
| for (size_t extNdx = 0; extNdx < src.size(); ++extNdx) |
| { |
| const RequiredExtension &extension = src[extNdx]; |
| int supportedAltNdx = -1; |
| |
| for (size_t alternativeNdx = 0; alternativeNdx < extension.alternatives.size(); ++alternativeNdx) |
| { |
| if (ctxInfo.isExtensionSupported(extension.alternatives[alternativeNdx].c_str())) |
| { |
| supportedAltNdx = (int)alternativeNdx; |
| break; |
| } |
| } |
| |
| if (supportedAltNdx >= 0) |
| { |
| specialized.push_back( |
| RequiredExtension(extension.alternatives[supportedAltNdx], extension.effectiveStages)); |
| } |
| else |
| { |
| // no extension(s). Make a nice output |
| std::ostringstream extensionList; |
| |
| for (size_t ndx = 0; ndx < extension.alternatives.size(); ++ndx) |
| { |
| if (!extensionList.str().empty()) |
| extensionList << ", "; |
| extensionList << extension.alternatives[ndx]; |
| } |
| |
| if (extension.alternatives.size() == 1) |
| throw tcu::NotSupportedError("Test requires extension " + extensionList.str()); |
| else |
| throw tcu::NotSupportedError("Test requires any extension of " + extensionList.str()); |
| } |
| } |
| |
| return specialized; |
| } |
| |
| static void checkImplementationLimits(const vector<RequiredCapability> &requiredCaps, const ContextInfo &ctxInfo) |
| { |
| for (size_t capNdx = 0; capNdx < requiredCaps.size(); ++capNdx) |
| { |
| const RequiredCapability &capability = requiredCaps[capNdx]; |
| if (capability.type != CAPABILITY_LIMIT) |
| continue; |
| |
| const uint32_t pname = capability.enumName; |
| const int requiredValue = capability.referenceValue; |
| const int supportedValue = ctxInfo.getInt((int)pname); |
| |
| if (supportedValue <= requiredValue) |
| throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(pname)) + " (" + |
| de::toString(supportedValue) + ") >= " + de::toString(requiredValue)); |
| } |
| } |
| |
| // Shader source specialization |
| |
| // This functions builds a matching vertex shader for a 'both' case, when |
| // the fragment shader is being tested. |
| // We need to build attributes and varyings for each 'input'. |
| static string genVertexShader(const ShaderCaseSpecification &spec) |
| { |
| ostringstream res; |
| const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); |
| const char *const vtxIn = usesInout ? "in" : "attribute"; |
| const char *const vtxOut = usesInout ? "out" : "varying"; |
| |
| res << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n"; |
| |
| // Declarations (position + attribute/varying for each input). |
| res << "precision highp float;\n"; |
| res << "precision highp int;\n"; |
| res << "\n"; |
| res << vtxIn << " highp vec4 dEQP_Position;\n"; |
| |
| for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) |
| { |
| const Value &val = spec.values.inputs[ndx]; |
| const DataType basicType = val.type.getBasicType(); |
| const DataType floatType = getDataTypeFloatScalars(basicType); |
| const char *const typeStr = getDataTypeName(floatType); |
| |
| res << vtxIn << " " << typeStr << " a_" << val.name << ";\n"; |
| |
| if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| res << vtxOut << " " << typeStr << " " << val.name << ";\n"; |
| else |
| res << vtxOut << " " << typeStr << " v_" << val.name << ";\n"; |
| } |
| res << "\n"; |
| |
| // Main function. |
| // - gl_Position = dEQP_Position; |
| // - for each input: write attribute directly to varying |
| res << "void main()\n"; |
| res << "{\n"; |
| res << " gl_Position = dEQP_Position;\n"; |
| for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) |
| { |
| const Value &val = spec.values.inputs[ndx]; |
| const string &name = val.name; |
| |
| if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT) |
| res << " " << name << " = a_" << name << ";\n"; |
| else |
| res << " v_" << name << " = a_" << name << ";\n"; |
| } |
| |
| res << "}\n"; |
| return res.str(); |
| } |
| |
| static void genCompareOp(ostringstream &output, const char *dstVec4Var, const ValueBlock &valueBlock, |
| const char *nonFloatNamePrefix, const char *checkVarName) |
| { |
| bool isFirstOutput = true; |
| |
| for (size_t ndx = 0; ndx < valueBlock.outputs.size(); ndx++) |
| { |
| const Value &val = valueBlock.outputs[ndx]; |
| |
| // Check if we're only interested in one variable (then skip if not the right one). |
| if (checkVarName && val.name != checkVarName) |
| continue; |
| |
| // Prefix. |
| if (isFirstOutput) |
| { |
| output << "bool RES = "; |
| isFirstOutput = false; |
| } |
| else |
| output << "RES = RES && "; |
| |
| // Generate actual comparison. |
| if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT) |
| output << "isOk(" << val.name << ", ref_" << val.name << ", 0.05);\n"; |
| else |
| output << "isOk(" << nonFloatNamePrefix << val.name << ", ref_" << val.name << ");\n"; |
| } |
| |
| if (isFirstOutput) |
| output << dstVec4Var << " = vec4(1.0);\n"; // \todo [petri] Should we give warning if not expect-failure case? |
| else |
| output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n"; |
| } |
| |
| static inline bool supportsFragmentHighp(glu::GLSLVersion version) |
| { |
| return version != glu::GLSL_VERSION_100_ES; |
| } |
| |
| static string genFragmentShader(const ShaderCaseSpecification &spec) |
| { |
| ostringstream shader; |
| const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); |
| const bool customColorOut = usesInout; |
| const char *const fragIn = usesInout ? "in" : "varying"; |
| const char *const prec = supportsFragmentHighp(spec.targetVersion) ? "highp" : "mediump"; |
| |
| shader << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n"; |
| |
| shader << "precision " << prec << " float;\n"; |
| shader << "precision " << prec << " int;\n"; |
| shader << "\n"; |
| |
| if (customColorOut) |
| { |
| shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; |
| shader << "\n"; |
| } |
| |
| genCompareFunctions(shader, spec.values, true); |
| shader << "\n"; |
| |
| // Declarations (varying, reference for each output). |
| for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++) |
| { |
| const Value &val = spec.values.outputs[ndx]; |
| const DataType basicType = val.type.getBasicType(); |
| const DataType floatType = getDataTypeFloatScalars(basicType); |
| const char *const floatTypeStr = getDataTypeName(floatType); |
| const char *const refTypeStr = getDataTypeName(basicType); |
| |
| if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| shader << fragIn << " " << floatTypeStr << " " << val.name << ";\n"; |
| else |
| shader << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n"; |
| |
| shader << "uniform " << refTypeStr << " ref_" << val.name << ";\n"; |
| } |
| |
| shader << "\n"; |
| shader << "void main()\n"; |
| shader << "{\n"; |
| |
| shader << " "; |
| genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", spec.values, "v_", DE_NULL); |
| |
| shader << "}\n"; |
| return shader.str(); |
| } |
| |
| // Specialize a shader for the vertex shader test case. |
| static string specializeVertexShader(const ShaderCaseSpecification &spec, const std::string &src, |
| const vector<RequiredExtension> &extensions) |
| { |
| ostringstream decl; |
| ostringstream setup; |
| ostringstream output; |
| const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); |
| const char *const vtxIn = usesInout ? "in" : "attribute"; |
| const char *const vtxOut = usesInout ? "out" : "varying"; |
| |
| // generated from "both" case |
| DE_ASSERT(spec.caseType == CASETYPE_VERTEX_ONLY); |
| |
| // Output (write out position). |
| output << "gl_Position = dEQP_Position;\n"; |
| |
| // Declarations (position + attribute for each input, varying for each output). |
| decl << vtxIn << " highp vec4 dEQP_Position;\n"; |
| |
| for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) |
| { |
| const Value &val = spec.values.inputs[ndx]; |
| const DataType basicType = val.type.getBasicType(); |
| const DataType floatType = getDataTypeFloatScalars(basicType); |
| const char *const floatTypeStr = getDataTypeName(floatType); |
| const char *const refTypeStr = getDataTypeName(basicType); |
| |
| if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| { |
| decl << vtxIn << " " << floatTypeStr << " " << val.name << ";\n"; |
| } |
| else |
| { |
| decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n"; |
| setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(a_" << val.name << ");\n"; |
| } |
| } |
| |
| // \todo [2015-07-24 pyry] Why are uniforms missing? |
| |
| for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++) |
| { |
| const Value &val = spec.values.outputs[ndx]; |
| const DataType basicType = val.type.getBasicType(); |
| const DataType floatType = getDataTypeFloatScalars(basicType); |
| const char *const floatTypeStr = getDataTypeName(floatType); |
| const char *const refTypeStr = getDataTypeName(basicType); |
| |
| if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| decl << vtxOut << " " << floatTypeStr << " " << val.name << ";\n"; |
| else |
| { |
| decl << vtxOut << " " << floatTypeStr << " v_" << val.name << ";\n"; |
| decl << refTypeStr << " " << val.name << ";\n"; |
| |
| output << "v_" << val.name << " = " << floatTypeStr << "(" << val.name << ");\n"; |
| } |
| } |
| |
| // Shader specialization. |
| map<string, string> params; |
| params.insert(pair<string, string>("DECLARATIONS", decl.str())); |
| params.insert(pair<string, string>("SETUP", setup.str())); |
| params.insert(pair<string, string>("OUTPUT", output.str())); |
| params.insert(pair<string, string>("POSITION_FRAG_COLOR", "gl_Position")); |
| |
| StringTemplate tmpl(src); |
| const string baseSrc = tmpl.specialize(params); |
| const string withExt = injectExtensionRequirements(baseSrc, extensions, SHADERTYPE_VERTEX); |
| |
| return withExt; |
| } |
| |
| // Specialize a shader for the fragment shader test case. |
| static string specializeFragmentShader(const ShaderCaseSpecification &spec, const std::string &src, |
| const vector<RequiredExtension> &extensions) |
| { |
| ostringstream decl; |
| ostringstream setup; |
| ostringstream output; |
| |
| const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); |
| const bool customColorOut = usesInout; |
| const char *const fragIn = usesInout ? "in" : "varying"; |
| const char *const fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor"; |
| |
| // generated from "both" case |
| DE_ASSERT(spec.caseType == CASETYPE_FRAGMENT_ONLY); |
| |
| genCompareFunctions(decl, spec.values, false); |
| genCompareOp(output, fragColor, spec.values, "", DE_NULL); |
| |
| if (customColorOut) |
| decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; |
| |
| for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) |
| { |
| const Value &val = spec.values.inputs[ndx]; |
| const DataType basicType = val.type.getBasicType(); |
| const DataType floatType = getDataTypeFloatScalars(basicType); |
| const char *const floatTypeStr = getDataTypeName(floatType); |
| const char *const refTypeStr = getDataTypeName(basicType); |
| |
| if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| decl << fragIn << " " << floatTypeStr << " " << val.name << ";\n"; |
| else |
| { |
| decl << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n"; |
| std::string offset = |
| isDataTypeIntOrIVec(basicType) ? |
| " * 1.0025" : |
| ""; // \todo [petri] bit of a hack to avoid errors in chop() due to varying interpolation |
| setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(v_" << val.name << offset << ");\n"; |
| } |
| } |
| |
| // \todo [2015-07-24 pyry] Why are uniforms missing? |
| |
| for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++) |
| { |
| const Value &val = spec.values.outputs[ndx]; |
| const DataType basicType = val.type.getBasicType(); |
| const char *const refTypeStr = getDataTypeName(basicType); |
| |
| decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n"; |
| decl << refTypeStr << " " << val.name << ";\n"; |
| } |
| |
| /* \todo [2010-04-01 petri] Check all outputs. */ |
| |
| // Shader specialization. |
| map<string, string> params; |
| params.insert(pair<string, string>("DECLARATIONS", decl.str())); |
| params.insert(pair<string, string>("SETUP", setup.str())); |
| params.insert(pair<string, string>("OUTPUT", output.str())); |
| params.insert(pair<string, string>("POSITION_FRAG_COLOR", fragColor)); |
| |
| StringTemplate tmpl(src); |
| const string baseSrc = tmpl.specialize(params); |
| const string withExt = injectExtensionRequirements(baseSrc, extensions, SHADERTYPE_FRAGMENT); |
| |
| return withExt; |
| } |
| |
| static void generateUniformDeclarations(std::ostream &dst, const ValueBlock &valueBlock) |
| { |
| for (size_t ndx = 0; ndx < valueBlock.uniforms.size(); ndx++) |
| { |
| const Value &val = valueBlock.uniforms[ndx]; |
| const char *const typeStr = getDataTypeName(val.type.getBasicType()); |
| |
| if (val.name.find('.') == string::npos) |
| dst << "uniform " << typeStr << " " << val.name << ";\n"; |
| } |
| } |
| |
| static map<string, string> generateVertexSpecialization(const ProgramSpecializationParams &specParams) |
| { |
| const bool usesInout = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion); |
| const char *vtxIn = usesInout ? "in" : "attribute"; |
| ostringstream decl; |
| ostringstream setup; |
| map<string, string> params; |
| |
| decl << vtxIn << " highp vec4 dEQP_Position;\n"; |
| |
| for (size_t ndx = 0; ndx < specParams.caseSpec.values.inputs.size(); ndx++) |
| { |
| const Value &val = specParams.caseSpec.values.inputs[ndx]; |
| const DataType basicType = val.type.getBasicType(); |
| const char *const typeStr = getDataTypeName(val.type.getBasicType()); |
| |
| if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| { |
| decl << vtxIn << " " << typeStr << " " << val.name << ";\n"; |
| } |
| else |
| { |
| const DataType floatType = getDataTypeFloatScalars(basicType); |
| const char *const floatTypeStr = getDataTypeName(floatType); |
| |
| decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n"; |
| setup << typeStr << " " << val.name << " = " << typeStr << "(a_" << val.name << ");\n"; |
| } |
| } |
| |
| generateUniformDeclarations(decl, specParams.caseSpec.values); |
| |
| params.insert(pair<string, string>("VERTEX_DECLARATIONS", decl.str())); |
| params.insert(pair<string, string>("VERTEX_SETUP", setup.str())); |
| params.insert(pair<string, string>("VERTEX_OUTPUT", string("gl_Position = dEQP_Position;\n"))); |
| |
| return params; |
| } |
| |
| static map<string, string> generateFragmentSpecialization(const ProgramSpecializationParams &specParams) |
| { |
| const bool usesInout = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion); |
| const bool customColorOut = usesInout; |
| const char *const fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor"; |
| ostringstream decl; |
| ostringstream output; |
| map<string, string> params; |
| |
| genCompareFunctions(decl, specParams.caseSpec.values, false); |
| genCompareOp(output, fragColor, specParams.caseSpec.values, "", DE_NULL); |
| |
| if (customColorOut) |
| decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; |
| |
| for (size_t ndx = 0; ndx < specParams.caseSpec.values.outputs.size(); ndx++) |
| { |
| const Value &val = specParams.caseSpec.values.outputs[ndx]; |
| const char *const refTypeStr = getDataTypeName(val.type.getBasicType()); |
| |
| decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n"; |
| decl << refTypeStr << " " << val.name << ";\n"; |
| } |
| |
| generateUniformDeclarations(decl, specParams.caseSpec.values); |
| |
| params.insert(pair<string, string>("FRAGMENT_DECLARATIONS", decl.str())); |
| params.insert(pair<string, string>("FRAGMENT_OUTPUT", output.str())); |
| params.insert(pair<string, string>("FRAG_COLOR", fragColor)); |
| |
| return params; |
| } |
| |
| static map<string, string> generateGeometrySpecialization(const ProgramSpecializationParams &specParams) |
| { |
| ostringstream decl; |
| map<string, string> params; |
| |
| decl << "layout (triangles) in;\n"; |
| decl << "layout (triangle_strip, max_vertices=3) out;\n"; |
| decl << "\n"; |
| |
| generateUniformDeclarations(decl, specParams.caseSpec.values); |
| |
| params.insert(pair<string, string>("GEOMETRY_DECLARATIONS", decl.str())); |
| |
| return params; |
| } |
| |
| static map<string, string> generateTessControlSpecialization(const ProgramSpecializationParams &specParams) |
| { |
| ostringstream decl; |
| ostringstream output; |
| map<string, string> params; |
| |
| decl << "layout (vertices=3) out;\n"; |
| decl << "\n"; |
| |
| generateUniformDeclarations(decl, specParams.caseSpec.values); |
| |
| output << "gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" |
| "gl_TessLevelInner[0] = 2.0;\n" |
| "gl_TessLevelInner[1] = 2.0;\n" |
| "gl_TessLevelOuter[0] = 2.0;\n" |
| "gl_TessLevelOuter[1] = 2.0;\n" |
| "gl_TessLevelOuter[2] = 2.0;\n" |
| "gl_TessLevelOuter[3] = 2.0;"; |
| |
| params.insert(pair<string, string>("TESSELLATION_CONTROL_DECLARATIONS", decl.str())); |
| params.insert(pair<string, string>("TESSELLATION_CONTROL_OUTPUT", output.str())); |
| params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices))); |
| |
| return params; |
| } |
| |
| static map<string, string> generateTessEvalSpecialization(const ProgramSpecializationParams &specParams) |
| { |
| ostringstream decl; |
| ostringstream output; |
| map<string, string> params; |
| |
| decl << "layout (triangles) in;\n"; |
| decl << "\n"; |
| |
| generateUniformDeclarations(decl, specParams.caseSpec.values); |
| |
| output << "gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + " |
| "gl_TessCoord[2] * gl_in[2].gl_Position;\n"; |
| |
| params.insert(pair<string, string>("TESSELLATION_EVALUATION_DECLARATIONS", decl.str())); |
| params.insert(pair<string, string>("TESSELLATION_EVALUATION_OUTPUT", output.str())); |
| params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices))); |
| |
| return params; |
| } |
| |
| static void specializeShaderSources( |
| ProgramSources &dst, const ProgramSources &src, const ProgramSpecializationParams &specParams, |
| glu::ShaderType shaderType, |
| map<string, string> (*specializationGenerator)(const ProgramSpecializationParams &specParams)) |
| { |
| if (!src.sources[shaderType].empty()) |
| { |
| const map<string, string> tmplParams = specializationGenerator(specParams); |
| |
| for (size_t ndx = 0; ndx < src.sources[shaderType].size(); ++ndx) |
| { |
| const StringTemplate tmpl(src.sources[shaderType][ndx]); |
| const std::string baseGLSLCode = tmpl.specialize(tmplParams); |
| const std::string sourceWithExts = |
| injectExtensionRequirements(baseGLSLCode, specParams.requiredExtensions, shaderType); |
| |
| dst << glu::ShaderSource(shaderType, sourceWithExts); |
| } |
| } |
| } |
| |
| static void specializeProgramSources(glu::ProgramSources &dst, const glu::ProgramSources &src, |
| const ProgramSpecializationParams &specParams) |
| { |
| specializeShaderSources(dst, src, specParams, SHADERTYPE_VERTEX, generateVertexSpecialization); |
| specializeShaderSources(dst, src, specParams, SHADERTYPE_FRAGMENT, generateFragmentSpecialization); |
| specializeShaderSources(dst, src, specParams, SHADERTYPE_GEOMETRY, generateGeometrySpecialization); |
| specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_CONTROL, generateTessControlSpecialization); |
| specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_EVALUATION, generateTessEvalSpecialization); |
| |
| dst << ProgramSeparable(src.separable); |
| } |
| |
| enum |
| { |
| VIEWPORT_WIDTH = 128, |
| VIEWPORT_HEIGHT = 128 |
| }; |
| |
| class BeforeDrawValidator : public glu::DrawUtilCallback |
| { |
| public: |
| enum TargetType |
| { |
| TARGETTYPE_PROGRAM = 0, |
| TARGETTYPE_PIPELINE, |
| |
| TARGETTYPE_LAST |
| }; |
| |
| BeforeDrawValidator(const glw::Functions &gl, glw::GLuint target, TargetType targetType); |
| |
| void beforeDrawCall(void); |
| |
| const std::string &getInfoLog(void) const; |
| glw::GLint getValidateStatus(void) const; |
| |
| private: |
| const glw::Functions &m_gl; |
| const glw::GLuint m_target; |
| const TargetType m_targetType; |
| |
| glw::GLint m_validateStatus; |
| std::string m_logMessage; |
| }; |
| |
| BeforeDrawValidator::BeforeDrawValidator(const glw::Functions &gl, glw::GLuint target, TargetType targetType) |
| : m_gl(gl) |
| , m_target(target) |
| , m_targetType(targetType) |
| , m_validateStatus(-1) |
| { |
| DE_ASSERT(targetType < TARGETTYPE_LAST); |
| } |
| |
| void BeforeDrawValidator::beforeDrawCall(void) |
| { |
| glw::GLint bytesWritten = 0; |
| glw::GLint infoLogLength; |
| std::vector<glw::GLchar> logBuffer; |
| int stringLength; |
| |
| // validate |
| if (m_targetType == TARGETTYPE_PROGRAM) |
| m_gl.validateProgram(m_target); |
| else if (m_targetType == TARGETTYPE_PIPELINE) |
| m_gl.validateProgramPipeline(m_target); |
| else |
| DE_ASSERT(false); |
| |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "validate"); |
| |
| // check status |
| m_validateStatus = -1; |
| |
| if (m_targetType == TARGETTYPE_PROGRAM) |
| m_gl.getProgramiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus); |
| else if (m_targetType == TARGETTYPE_PIPELINE) |
| m_gl.getProgramPipelineiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus); |
| else |
| DE_ASSERT(false); |
| |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "get validate status"); |
| TCU_CHECK(m_validateStatus == GL_TRUE || m_validateStatus == GL_FALSE); |
| |
| // read log |
| |
| infoLogLength = 0; |
| |
| if (m_targetType == TARGETTYPE_PROGRAM) |
| m_gl.getProgramiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength); |
| else if (m_targetType == TARGETTYPE_PIPELINE) |
| m_gl.getProgramPipelineiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength); |
| else |
| DE_ASSERT(false); |
| |
| GLU_EXPECT_NO_ERROR(m_gl.getError(), "get info log length"); |
| |
| if (infoLogLength <= 0) |
| { |
| m_logMessage.clear(); |
| return; |
| } |
| |
| logBuffer.resize( |
| infoLogLength + 2, |
| '0'); // +1 for zero terminator (infoLogLength should include it, but better play it safe), +1 to make sure buffer is always larger |
| |
| if (m_targetType == TARGETTYPE_PROGRAM) |
| m_gl.getProgramInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]); |
| else if (m_targetType == TARGETTYPE_PIPELINE) |
| m_gl.getProgramPipelineInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]); |
| else |
| DE_ASSERT(false); |
| |
| // just ignore bytesWritten to be safe, find the null terminator |
| stringLength = (int)(std::find(logBuffer.begin(), logBuffer.end(), '0') - logBuffer.begin()); |
| m_logMessage.assign(&logBuffer[0], stringLength); |
| } |
| |
| const std::string &BeforeDrawValidator::getInfoLog(void) const |
| { |
| return m_logMessage; |
| } |
| |
| glw::GLint BeforeDrawValidator::getValidateStatus(void) const |
| { |
| return m_validateStatus; |
| } |
| |
| // ShaderCase. |
| |
| ShaderLibraryCase::ShaderLibraryCase(tcu::TestContext &testCtx, RenderContext &renderCtx, |
| const glu::ContextInfo &contextInfo, const char *name, const char *description, |
| const ShaderCaseSpecification &specification) |
| : tcu::TestCase(testCtx, name, description) |
| , m_renderCtx(renderCtx) |
| , m_contextInfo(contextInfo) |
| , m_spec(specification) |
| { |
| } |
| |
| ShaderLibraryCase::~ShaderLibraryCase(void) |
| { |
| } |
| |
| static inline void requireExtension(const glu::ContextInfo &info, const ShaderCaseSpecification &spec, |
| const char *extension) |
| { |
| if (!info.isExtensionSupported(extension)) |
| TCU_THROW(NotSupportedError, (string(getGLSLVersionName(spec.targetVersion)) + " is not supported").c_str()); |
| } |
| |
| void ShaderLibraryCase::init(void) |
| { |
| DE_ASSERT(isValid(m_spec)); |
| |
| // Check for ES compatibility extensions, e.g. if we are on desktop context but require GLSL ES |
| if (!isContextTypeES(m_renderCtx.getType()) && glslVersionIsES(m_spec.targetVersion)) |
| { |
| switch (m_spec.targetVersion) |
| { |
| case GLSL_VERSION_300_ES: |
| requireExtension(m_contextInfo, m_spec, "GL_ARB_ES3_compatibility"); |
| break; |
| case GLSL_VERSION_310_ES: |
| requireExtension(m_contextInfo, m_spec, "GL_ARB_ES3_1_compatibility"); |
| break; |
| case GLSL_VERSION_320_ES: |
| requireExtension(m_contextInfo, m_spec, "GL_ARB_ES3_2_compatibility"); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else |
| { |
| if (!isGLSLVersionSupported(m_renderCtx.getType(), m_spec.targetVersion)) |
| TCU_THROW(NotSupportedError, |
| (string(getGLSLVersionName(m_spec.targetVersion)) + " is not supported").c_str()); |
| } |
| |
| checkImplementationLimits(m_spec.requiredCaps, m_contextInfo); |
| |
| // log the expected result |
| switch (m_spec.expectResult) |
| { |
| case EXPECT_PASS: |
| // Don't write anything |
| break; |
| |
| case EXPECT_COMPILE_FAIL: |
| m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation to fail." |
| << tcu::TestLog::EndMessage; |
| break; |
| |
| case EXPECT_LINK_FAIL: |
| m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program linking to fail." << tcu::TestLog::EndMessage; |
| break; |
| |
| case EXPECT_COMPILE_LINK_FAIL: |
| m_testCtx.getLog() << tcu::TestLog::Message << "Expecting either shader compilation or program linking to fail." |
| << tcu::TestLog::EndMessage; |
| break; |
| |
| case EXPECT_VALIDATION_FAIL: |
| m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program validation to fail." |
| << tcu::TestLog::EndMessage; |
| break; |
| |
| case EXPECT_BUILD_SUCCESSFUL: |
| m_testCtx.getLog() |
| << tcu::TestLog::Message |
| << "Expecting shader compilation and program linking to succeed. Resulting program will not be executed." |
| << tcu::TestLog::EndMessage; |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| } |
| |
| static void setUniformValue(const glw::Functions &gl, const std::vector<uint32_t> &pipelinePrograms, |
| const std::string &name, const Value &val, int arrayNdx, tcu::TestLog &log) |
| { |
| bool foundAnyMatch = false; |
| |
| for (int programNdx = 0; programNdx < (int)pipelinePrograms.size(); ++programNdx) |
| { |
| const DataType dataType = val.type.getBasicType(); |
| const int scalarSize = getDataTypeScalarSize(dataType); |
| const int loc = gl.getUniformLocation(pipelinePrograms[programNdx], name.c_str()); |
| const int elemNdx = arrayNdx * scalarSize; |
| |
| DE_ASSERT(elemNdx + scalarSize <= (int)val.elements.size()); |
| |
| if (loc == -1) |
| continue; |
| |
| foundAnyMatch = true; |
| |
| DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLfloat)); |
| DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLint)); |
| |
| gl.useProgram(pipelinePrograms[programNdx]); |
| |
| switch (dataType) |
| { |
| case TYPE_FLOAT: |
| gl.uniform1fv(loc, 1, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_VEC2: |
| gl.uniform2fv(loc, 1, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_VEC3: |
| gl.uniform3fv(loc, 1, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_VEC4: |
| gl.uniform4fv(loc, 1, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_MAT2: |
| gl.uniformMatrix2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_MAT3: |
| gl.uniformMatrix3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_MAT4: |
| gl.uniformMatrix4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_INT: |
| gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); |
| break; |
| case TYPE_INT_VEC2: |
| gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); |
| break; |
| case TYPE_INT_VEC3: |
| gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); |
| break; |
| case TYPE_INT_VEC4: |
| gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); |
| break; |
| case TYPE_BOOL: |
| gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); |
| break; |
| case TYPE_BOOL_VEC2: |
| gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); |
| break; |
| case TYPE_BOOL_VEC3: |
| gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); |
| break; |
| case TYPE_BOOL_VEC4: |
| gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); |
| break; |
| case TYPE_UINT: |
| gl.uniform1uiv(loc, 1, (const uint32_t *)&val.elements[elemNdx].int32); |
| break; |
| case TYPE_UINT_VEC2: |
| gl.uniform2uiv(loc, 1, (const uint32_t *)&val.elements[elemNdx].int32); |
| break; |
| case TYPE_UINT_VEC3: |
| gl.uniform3uiv(loc, 1, (const uint32_t *)&val.elements[elemNdx].int32); |
| break; |
| case TYPE_UINT_VEC4: |
| gl.uniform4uiv(loc, 1, (const uint32_t *)&val.elements[elemNdx].int32); |
| break; |
| case TYPE_FLOAT_MAT2X3: |
| gl.uniformMatrix2x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_MAT2X4: |
| gl.uniformMatrix2x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_MAT3X2: |
| gl.uniformMatrix3x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_MAT3X4: |
| gl.uniformMatrix3x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_MAT4X2: |
| gl.uniformMatrix4x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); |
| break; |
| case TYPE_FLOAT_MAT4X3: |
| gl.uniformMatrix4x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); |
| break; |
| |
| case TYPE_SAMPLER_2D: |
| case TYPE_SAMPLER_CUBE: |
| DE_FATAL("implement!"); |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| |
| if (!foundAnyMatch) |
| log << tcu::TestLog::Message << "WARNING // Uniform \"" << name |
| << "\" location is not valid, location = -1. Cannot set value to the uniform." << tcu::TestLog::EndMessage; |
| } |
| |
| static bool isTessellationPresent(const ShaderCaseSpecification &spec) |
| { |
| if (spec.programs[0].sources.separable) |
| { |
| const uint32_t tessellationBits = |
| (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) | (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION); |
| |
| for (int programNdx = 0; programNdx < (int)spec.programs.size(); ++programNdx) |
| if (spec.programs[programNdx].activeStages & tessellationBits) |
| return true; |
| return false; |
| } |
| else |
| return !spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty() || |
| !spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_EVALUATION].empty(); |
| } |
| |
| static bool isTessellationSupported(const glu::RenderContext &renderCtx, const glu::ContextInfo &ctxInfo) |
| { |
| if (renderCtx.getType().getProfile() == PROFILE_ES) |
| { |
| const int majorVer = renderCtx.getType().getMajorVersion(); |
| const int minorVer = renderCtx.getType().getMinorVersion(); |
| |
| return (majorVer > 3) || (majorVer == 3 && minorVer >= 2) || |
| ctxInfo.isExtensionSupported("GL_EXT_tessellation_shader"); |
| } |
| else |
| return false; |
| } |
| |
| static bool checkPixels(tcu::TestLog &log, const tcu::ConstPixelBufferAccess &surface) |
| { |
| bool allWhite = true; |
| bool allBlack = true; |
| bool anyUnexpected = false; |
| |
| for (int y = 0; y < surface.getHeight(); y++) |
| { |
| for (int x = 0; x < surface.getWidth(); x++) |
| { |
| const tcu::IVec4 pixel = surface.getPixelInt(x, y); |
| // Note: we really do not want to involve alpha in the check comparison |
| // \todo [2010-09-22 kalle] Do we know that alpha would be one? If yes, could use color constants white and black. |
| const bool isWhite = (pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255); |
| const bool isBlack = (pixel[0] == 0) && (pixel[1] == 0) && (pixel[2] == 0); |
| |
| allWhite = allWhite && isWhite; |
| allBlack = allBlack && isBlack; |
| anyUnexpected = anyUnexpected || (!isWhite && !isBlack); |
| } |
| } |
| |
| if (!allWhite) |
| { |
| if (anyUnexpected) |
| log << TestLog::Message |
| << "WARNING: expecting all rendered pixels to be white or black, but got other colors as well!" |
| << TestLog::EndMessage; |
| else if (!allBlack) |
| log << TestLog::Message |
| << "WARNING: got inconsistent results over the image, when all pixels should be the same color!" |
| << TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool ShaderLibraryCase::execute(void) |
| { |
| const float quadSize = 1.0f; |
| static const float s_positions[4 * 4] = {-quadSize, -quadSize, 0.0f, 1.0f, -quadSize, +quadSize, 0.0f, 1.0f, |
| +quadSize, -quadSize, 0.0f, 1.0f, +quadSize, +quadSize, 0.0f, 1.0f}; |
| |
| static const uint16_t s_indices[2 * 3] = {0, 1, 2, 1, 3, 2}; |
| |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| |
| // Compute viewport. |
| const tcu::RenderTarget &renderTarget = m_renderCtx.getRenderTarget(); |
| de::Random rnd(deStringHash(getName())); |
| const int width = deMin32(renderTarget.getWidth(), VIEWPORT_WIDTH); |
| const int height = deMin32(renderTarget.getHeight(), VIEWPORT_HEIGHT); |
| const int viewportX = rnd.getInt(0, renderTarget.getWidth() - width); |
| const int viewportY = rnd.getInt(0, renderTarget.getHeight() - height); |
| const int numVerticesPerDraw = 4; |
| const bool tessellationPresent = isTessellationPresent(m_spec); |
| const bool separablePrograms = m_spec.programs[0].sources.separable; |
| |
| bool allCompilesOk = true; |
| bool allLinksOk = true; |
| const char *failReason = DE_NULL; |
| |
| vector<ProgramSources> specializedSources(m_spec.programs.size()); |
| |
| uint32_t vertexProgramID = -1; |
| vector<uint32_t> pipelineProgramIDs; |
| vector<SharedPtr<ShaderProgram>> programs; |
| SharedPtr<ProgramPipeline> programPipeline; |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): start"); |
| |
| if (isCapabilityRequired(CAPABILITY_ONLY_GLSL_ES_100_SUPPORT, m_spec) && glu::IsES3Compatible(gl)) |
| return true; |
| |
| if (isCapabilityRequired(CAPABILITY_EXACTLY_ONE_DRAW_BUFFER, m_spec)) |
| { |
| // on unextended ES2 there is only one draw buffer |
| // and there is no GL_MAX_DRAW_BUFFERS query |
| glw::GLint maxDrawBuffers = 0; |
| gl.getIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers); |
| if ((gl.getError() == GL_NO_ERROR) && (maxDrawBuffers > 1)) |
| throw tcu::NotSupportedError("Test requires exactly one draw buffer"); |
| } |
| |
| // Specialize shaders |
| if (m_spec.caseType == CASETYPE_VERTEX_ONLY) |
| { |
| const vector<RequiredExtension> reqExt = |
| checkAndSpecializeExtensions(m_spec.programs[0].requiredExtensions, m_contextInfo); |
| |
| DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX].size() == 1); |
| specializedSources[0] << glu::VertexSource(specializeVertexShader( |
| m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX][0], reqExt)) |
| << glu::FragmentSource(genFragmentShader(m_spec)); |
| } |
| else if (m_spec.caseType == CASETYPE_FRAGMENT_ONLY) |
| { |
| const vector<RequiredExtension> reqExt = |
| checkAndSpecializeExtensions(m_spec.programs[0].requiredExtensions, m_contextInfo); |
| |
| DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].size() == 1); |
| specializedSources[0] << glu::VertexSource(genVertexShader(m_spec)) |
| << glu::FragmentSource(specializeFragmentShader( |
| m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT][0], reqExt)); |
| } |
| else |
| { |
| DE_ASSERT(m_spec.caseType == CASETYPE_COMPLETE); |
| |
| const int maxPatchVertices = |
| isTessellationPresent(m_spec) && isTessellationSupported(m_renderCtx, m_contextInfo) ? |
| m_contextInfo.getInt(GL_MAX_PATCH_VERTICES) : |
| 0; |
| |
| for (size_t progNdx = 0; progNdx < m_spec.programs.size(); progNdx++) |
| { |
| const ProgramSpecializationParams progSpecParams( |
| m_spec, checkAndSpecializeExtensions(m_spec.programs[progNdx].requiredExtensions, m_contextInfo), |
| maxPatchVertices); |
| |
| specializeProgramSources(specializedSources[progNdx], m_spec.programs[progNdx].sources, progSpecParams); |
| } |
| } |
| |
| if (!separablePrograms) |
| { |
| de::SharedPtr<glu::ShaderProgram> program(new glu::ShaderProgram(m_renderCtx, specializedSources[0])); |
| |
| vertexProgramID = program->getProgram(); |
| pipelineProgramIDs.push_back(program->getProgram()); |
| programs.push_back(program); |
| |
| // Check that compile/link results are what we expect. |
| |
| DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0); |
| for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) |
| if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk) |
| allCompilesOk = false; |
| |
| if (!program->getProgramInfo().linkOk) |
| allLinksOk = false; |
| |
| log << *program; |
| } |
| else |
| { |
| // Separate programs |
| for (size_t programNdx = 0; programNdx < m_spec.programs.size(); ++programNdx) |
| { |
| de::SharedPtr<glu::ShaderProgram> program( |
| new glu::ShaderProgram(m_renderCtx, specializedSources[programNdx])); |
| |
| if (m_spec.programs[programNdx].activeStages & (1u << glu::SHADERTYPE_VERTEX)) |
| vertexProgramID = program->getProgram(); |
| |
| pipelineProgramIDs.push_back(program->getProgram()); |
| programs.push_back(program); |
| |
| // Check that compile/link results are what we expect. |
| |
| DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0); |
| for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) |
| if (program->hasShader((glu::ShaderType)stage) && |
| !program->getShaderInfo((glu::ShaderType)stage).compileOk) |
| allCompilesOk = false; |
| |
| if (!program->getProgramInfo().linkOk) |
| allLinksOk = false; |
| |
| // Log program and active stages |
| { |
| const tcu::ScopedLogSection section(log, "Program", "Program " + de::toString(programNdx + 1)); |
| tcu::MessageBuilder builder(&log); |
| bool firstStage = true; |
| |
| builder << "Pipeline uses stages: "; |
| for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) |
| { |
| if (m_spec.programs[programNdx].activeStages & (1u << stage)) |
| { |
| if (!firstStage) |
| builder << ", "; |
| builder << glu::getShaderTypeName((glu::ShaderType)stage); |
| firstStage = true; |
| } |
| } |
| builder << tcu::TestLog::EndMessage; |
| |
| log << *program; |
| } |
| } |
| } |
| |
| switch (m_spec.expectResult) |
| { |
| case EXPECT_PASS: |
| case EXPECT_VALIDATION_FAIL: |
| case EXPECT_BUILD_SUCCESSFUL: |
| if (!allCompilesOk) |
| failReason = "expected shaders to compile and link properly, but failed to compile."; |
| else if (!allLinksOk) |
| failReason = "expected shaders to compile and link properly, but failed to link."; |
| break; |
| |
| case EXPECT_COMPILE_FAIL: |
| if (allCompilesOk && !allLinksOk) |
| failReason = "expected compilation to fail, but shaders compiled and link failed."; |
| else if (allCompilesOk) |
| failReason = "expected compilation to fail, but shaders compiled correctly."; |
| break; |
| |
| case EXPECT_LINK_FAIL: |
| if (!allCompilesOk) |
| failReason = "expected linking to fail, but unable to compile."; |
| else if (allLinksOk) |
| failReason = "expected linking to fail, but passed."; |
| break; |
| |
| case EXPECT_COMPILE_LINK_FAIL: |
| if (allCompilesOk && allLinksOk) |
| failReason = "expected compile or link to fail, but passed."; |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| return false; |
| } |
| |
| if (failReason != DE_NULL) |
| { |
| // \todo [2010-06-07 petri] These should be handled in the test case? |
| log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage; |
| |
| if (isCapabilityRequired(CAPABILITY_FULL_GLSL_ES_100_SUPPORT, m_spec)) |
| { |
| log << TestLog::Message |
| << "Assuming build failure is caused by implementation not supporting full GLSL ES 100 specification, " |
| "which is not required." |
| << TestLog::EndMessage; |
| |
| if (allCompilesOk && !allLinksOk) |
| { |
| // Used features are detectable at compile time. If implementation parses shader |
| // at link time, report it as quality warning. |
| m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason); |
| } |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Full GLSL ES 100 is not supported"); |
| } |
| else if (m_spec.expectResult == EXPECT_COMPILE_FAIL && allCompilesOk && !allLinksOk) |
| { |
| // If implementation parses shader at link time, report it as quality warning. |
| m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason); |
| } |
| else |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason); |
| return false; |
| } |
| |
| // Return if shader is not intended to be run |
| if (m_spec.expectResult == EXPECT_COMPILE_FAIL || m_spec.expectResult == EXPECT_COMPILE_LINK_FAIL || |
| m_spec.expectResult == EXPECT_LINK_FAIL || m_spec.expectResult == EXPECT_BUILD_SUCCESSFUL) |
| return true; |
| |
| // Setup viewport. |
| gl.viewport(viewportX, viewportY, width, height); |
| |
| if (separablePrograms) |
| { |
| programPipeline = de::SharedPtr<glu::ProgramPipeline>(new glu::ProgramPipeline(m_renderCtx)); |
| |
| // Setup pipeline |
| gl.bindProgramPipeline(programPipeline->getPipeline()); |
| for (int programNdx = 0; programNdx < (int)m_spec.programs.size(); ++programNdx) |
| { |
| uint32_t shaderFlags = 0; |
| for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) |
| if (m_spec.programs[programNdx].activeStages & (1u << stage)) |
| shaderFlags |= glu::getGLShaderTypeBit((glu::ShaderType)stage); |
| |
| programPipeline->useProgramStages(shaderFlags, pipelineProgramIDs[programNdx]); |
| } |
| |
| programPipeline->activeShaderProgram(vertexProgramID); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "setup pipeline"); |
| } |
| else |
| { |
| // Start using program |
| gl.useProgram(vertexProgramID); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()"); |
| } |
| |
| // Fetch location for positions positions. |
| int positionLoc = gl.getAttribLocation(vertexProgramID, "dEQP_Position"); |
| if (positionLoc == -1) |
| { |
| string errStr = string("no location found for attribute 'dEQP_Position'"); |
| TCU_FAIL(errStr.c_str()); |
| } |
| |
| // Iterate all value blocks. |
| { |
| const ValueBlock &valueBlock = m_spec.values; |
| |
| // always render at least one pass even if there is no input/output data |
| const int numRenderPasses = valueBlock.outputs.empty() ? 1 : |
| (int)valueBlock.outputs[0].elements.size() / |
| valueBlock.outputs[0].type.getScalarSize(); |
| |
| // Iterate all array sub-cases. |
| for (int arrayNdx = 0; arrayNdx < numRenderPasses; arrayNdx++) |
| { |
| vector<VertexArrayBinding> vertexArrays; |
| int attribValueNdx = 0; |
| vector<vector<float>> attribValues(valueBlock.inputs.size()); |
| glw::GLenum postDrawError; |
| BeforeDrawValidator beforeDrawValidator( |
| gl, (separablePrograms) ? (programPipeline->getPipeline()) : (vertexProgramID), |
| (separablePrograms) ? (BeforeDrawValidator::TARGETTYPE_PIPELINE) : |
| (BeforeDrawValidator::TARGETTYPE_PROGRAM)); |
| |
| vertexArrays.push_back(va::Float(positionLoc, 4, numVerticesPerDraw, 0, &s_positions[0])); |
| |
| // Collect VA pointer for inputs |
| for (size_t valNdx = 0; valNdx < valueBlock.inputs.size(); valNdx++) |
| { |
| const Value &val = valueBlock.inputs[valNdx]; |
| const char *const valueName = val.name.c_str(); |
| const DataType dataType = val.type.getBasicType(); |
| const int scalarSize = getDataTypeScalarSize(dataType); |
| |
| // Replicate values four times. |
| std::vector<float> &scalars = attribValues[attribValueNdx++]; |
| scalars.resize(numVerticesPerDraw * scalarSize); |
| if (isDataTypeFloatOrVec(dataType) || isDataTypeMatrix(dataType)) |
| { |
| for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++) |
| for (int ndx = 0; ndx < scalarSize; ndx++) |
| scalars[repNdx * scalarSize + ndx] = val.elements[arrayNdx * scalarSize + ndx].float32; |
| } |
| else |
| { |
| // convert to floats. |
| for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++) |
| { |
| for (int ndx = 0; ndx < scalarSize; ndx++) |
| { |
| float v = (float)val.elements[arrayNdx * scalarSize + ndx].int32; |
| DE_ASSERT(val.elements[arrayNdx * scalarSize + ndx].int32 == (int)v); |
| scalars[repNdx * scalarSize + ndx] = v; |
| } |
| } |
| } |
| |
| // Attribute name prefix. |
| string attribPrefix = ""; |
| // \todo [2010-05-27 petri] Should latter condition only apply for vertex cases (or actually non-fragment cases)? |
| if ((m_spec.caseType == CASETYPE_FRAGMENT_ONLY) || (getDataTypeScalarType(dataType) != TYPE_FLOAT)) |
| attribPrefix = "a_"; |
| |
| // Input always given as attribute. |
| string attribName = attribPrefix + valueName; |
| int attribLoc = gl.getAttribLocation(vertexProgramID, attribName.c_str()); |
| if (attribLoc == -1) |
| { |
| log << TestLog::Message << "Warning: no location found for attribute '" << attribName << "'" |
| << TestLog::EndMessage; |
| continue; |
| } |
| |
| if (isDataTypeMatrix(dataType)) |
| { |
| int numCols = getDataTypeMatrixNumColumns(dataType); |
| int numRows = getDataTypeMatrixNumRows(dataType); |
| DE_ASSERT(scalarSize == numCols * numRows); |
| |
| for (int i = 0; i < numCols; i++) |
| vertexArrays.push_back(va::Float(attribLoc + i, numRows, numVerticesPerDraw, |
| scalarSize * (int)sizeof(float), &scalars[i * numRows])); |
| } |
| else |
| { |
| DE_ASSERT(isDataTypeFloatOrVec(dataType) || isDataTypeIntOrIVec(dataType) || |
| isDataTypeUintOrUVec(dataType) || isDataTypeBoolOrBVec(dataType)); |
| vertexArrays.push_back(va::Float(attribLoc, scalarSize, numVerticesPerDraw, 0, &scalars[0])); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "set vertex attrib array"); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "before set uniforms"); |
| |
| // set reference values for outputs. |
| for (size_t valNdx = 0; valNdx < valueBlock.outputs.size(); valNdx++) |
| { |
| const Value &val = valueBlock.outputs[valNdx]; |
| const char *const valueName = val.name.c_str(); |
| |
| // Set reference value. |
| string refName = string("ref_") + valueName; |
| setUniformValue(gl, pipelineProgramIDs, refName, val, arrayNdx, m_testCtx.getLog()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "set reference uniforms"); |
| } |
| |
| // set uniform values |
| for (size_t valNdx = 0; valNdx < valueBlock.uniforms.size(); valNdx++) |
| { |
| const Value &val = valueBlock.uniforms[valNdx]; |
| const char *const valueName = val.name.c_str(); |
| |
| setUniformValue(gl, pipelineProgramIDs, valueName, val, arrayNdx, m_testCtx.getLog()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms"); |
| } |
| |
| // Clear. |
| gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f); |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer"); |
| |
| // Use program or pipeline |
| if (separablePrograms) |
| gl.useProgram(0); |
| else |
| gl.useProgram(vertexProgramID); |
| |
| // Draw. |
| if (tessellationPresent) |
| { |
| gl.patchParameteri(GL_PATCH_VERTICES, 3); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "set patchParameteri(PATCH_VERTICES, 3)"); |
| } |
| |
| draw(m_renderCtx, vertexProgramID, (int)vertexArrays.size(), &vertexArrays[0], |
| (tessellationPresent) ? (pr::Patches(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])) : |
| (pr::Triangles(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])), |
| (m_spec.expectResult == EXPECT_VALIDATION_FAIL) ? (&beforeDrawValidator) : (DE_NULL)); |
| |
| postDrawError = gl.getError(); |
| |
| if (m_spec.expectResult == EXPECT_PASS) |
| { |
| // Read back results. |
| Surface surface(width, height); |
| const float w = s_positions[3]; |
| const int minY = deCeilFloatToInt32(((-quadSize / w) * 0.5f + 0.5f) * (float)height + 1.0f); |
| const int maxY = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)height - 0.5f); |
| const int minX = deCeilFloatToInt32(((-quadSize / w) * 0.5f + 0.5f) * (float)width + 1.0f); |
| const int maxX = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)width - 0.5f); |
| |
| GLU_EXPECT_NO_ERROR(postDrawError, "draw"); |
| |
| glu::readPixels(m_renderCtx, viewportX, viewportY, surface.getAccess()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels"); |
| |
| if (!checkPixels(log, |
| tcu::getSubregion(surface.getAccess(), minX, minY, maxX - minX + 1, maxY - minY + 1))) |
| { |
| log << TestLog::Message << "INCORRECT RESULT for sub-case " << arrayNdx + 1 << " of " |
| << numRenderPasses << "):" << TestLog::EndMessage; |
| |
| log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage; |
| dumpValues(log, valueBlock, arrayNdx); |
| |
| // Dump image on failure. |
| log << TestLog::Image("Result", "Rendered result image", surface); |
| |
| gl.useProgram(0); |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); |
| return false; |
| } |
| } |
| else if (m_spec.expectResult == EXPECT_VALIDATION_FAIL) |
| { |
| log << TestLog::Message << "Draw call generated error: " << glu::getErrorStr(postDrawError) << " " |
| << ((postDrawError == GL_INVALID_OPERATION) ? ("(expected)") : ("(unexpected)")) << "\n" |
| << "Validate status: " << glu::getBooleanStr(beforeDrawValidator.getValidateStatus()) << " " |
| << ((beforeDrawValidator.getValidateStatus() == GL_FALSE) ? ("(expected)") : ("(unexpected)")) |
| << "\n" |
| << "Info log: " |
| << ((beforeDrawValidator.getInfoLog().empty()) ? ("[empty string]") : |
| (beforeDrawValidator.getInfoLog())) |
| << "\n" |
| << TestLog::EndMessage; |
| |
| // test result |
| |
| if (postDrawError != GL_NO_ERROR && postDrawError != GL_INVALID_OPERATION) |
| { |
| m_testCtx.setTestResult( |
| QP_TEST_RESULT_FAIL, |
| ("Draw: got unexpected error: " + de::toString(glu::getErrorStr(postDrawError))).c_str()); |
| return false; |
| } |
| |
| if (beforeDrawValidator.getValidateStatus() == GL_TRUE) |
| { |
| if (postDrawError == GL_NO_ERROR) |
| m_testCtx.setTestResult( |
| QP_TEST_RESULT_FAIL, |
| "expected validation and rendering to fail but validation and rendering succeeded"); |
| else if (postDrawError == GL_INVALID_OPERATION) |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, |
| "expected validation and rendering to fail but validation succeeded " |
| "(rendering failed as expected)"); |
| else |
| DE_ASSERT(false); |
| return false; |
| } |
| else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_NO_ERROR) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but " |
| "rendering succeeded (validation failed as expected)"); |
| return false; |
| } |
| else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_INVALID_OPERATION) |
| { |
| // Validation does not depend on input values, no need to test all values |
| return true; |
| } |
| else |
| DE_ASSERT(false); |
| } |
| else |
| DE_ASSERT(false); |
| } |
| } |
| |
| gl.useProgram(0); |
| if (separablePrograms) |
| gl.bindProgramPipeline(0); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): end"); |
| return true; |
| } |
| |
| TestCase::IterateResult ShaderLibraryCase::iterate(void) |
| { |
| // Initialize state to pass. |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| |
| bool executeOk = execute(); |
| |
| DE_ASSERT(executeOk ? m_testCtx.getTestResult() == QP_TEST_RESULT_PASS : |
| m_testCtx.getTestResult() != QP_TEST_RESULT_PASS); |
| DE_UNREF(executeOk); |
| return TestCase::STOP; |
| } |
| |
| } // namespace gls |
| } // namespace deqp |