Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1 | /*------------------------------------------------------------------------- |
| 2 | * drawElements Quality Program OpenGL (ES) Module |
| 3 | * ----------------------------------------------- |
| 4 | * |
| 5 | * Copyright 2014 The Android Open Source Project |
| 6 | * |
| 7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 8 | * you may not use this file except in compliance with the License. |
| 9 | * You may obtain a copy of the License at |
| 10 | * |
| 11 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 12 | * |
| 13 | * Unless required by applicable law or agreed to in writing, software |
| 14 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 16 | * See the License for the specific language governing permissions and |
| 17 | * limitations under the License. |
| 18 | * |
| 19 | *//*! |
| 20 | * \file |
| 21 | * \brief Compiler test case. |
| 22 | *//*--------------------------------------------------------------------*/ |
| 23 | |
| 24 | #include "glsShaderLibraryCase.hpp" |
| 25 | |
| 26 | #include "tcuTestLog.hpp" |
| 27 | #include "tcuRenderTarget.hpp" |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 28 | #include "tcuTextureUtil.hpp" |
| 29 | #include "tcuSurface.hpp" |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 30 | |
| 31 | #include "tcuStringTemplate.hpp" |
| 32 | #include "gluShaderProgram.hpp" |
| 33 | #include "gluPixelTransfer.hpp" |
| 34 | #include "gluDrawUtil.hpp" |
| 35 | #include "gluContextInfo.hpp" |
| 36 | #include "gluStrUtil.hpp" |
| 37 | |
| 38 | #include "glwFunctions.hpp" |
| 39 | #include "glwEnums.hpp" |
| 40 | |
| 41 | #include "deRandom.hpp" |
| 42 | #include "deInt32.h" |
| 43 | #include "deMath.h" |
| 44 | #include "deString.h" |
| 45 | #include "deStringUtil.hpp" |
| 46 | #include "deSharedPtr.hpp" |
| 47 | |
| 48 | #include <map> |
| 49 | #include <vector> |
| 50 | #include <string> |
| 51 | #include <sstream> |
| 52 | |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 53 | namespace deqp |
| 54 | { |
| 55 | namespace gls |
| 56 | { |
Pyry Haulos | 877323d | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 57 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 58 | using namespace tcu; |
| 59 | using namespace glu; |
| 60 | using namespace glu::sl; |
Pyry Haulos | 877323d | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 61 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 62 | using std::vector; |
| 63 | using std::string; |
| 64 | using std::ostringstream; |
| 65 | using std::map; |
| 66 | using std::pair; |
| 67 | |
| 68 | using de::SharedPtr; |
| 69 | |
| 70 | // OpenGL-specific specialization utils |
| 71 | |
| 72 | static vector<RequiredExtension> checkAndSpecializeExtensions (const vector<RequiredExtension>& src, |
| 73 | const ContextInfo& ctxInfo) |
Pyry Haulos | 877323d | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 74 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 75 | vector<RequiredExtension> specialized; |
| 76 | |
| 77 | for (size_t extNdx = 0; extNdx < src.size(); ++extNdx) |
Pyry Haulos | 877323d | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 78 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 79 | const RequiredExtension& extension = src[extNdx]; |
| 80 | int supportedAltNdx = -1; |
Pyry Haulos | 877323d | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 81 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 82 | for (size_t alternativeNdx = 0; alternativeNdx < extension.alternatives.size(); ++alternativeNdx) |
| 83 | { |
| 84 | if (ctxInfo.isExtensionSupported(extension.alternatives[alternativeNdx].c_str())) |
| 85 | { |
| 86 | supportedAltNdx = (int)alternativeNdx; |
| 87 | break; |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | if (supportedAltNdx >= 0) |
| 92 | { |
| 93 | specialized.push_back(RequiredExtension(extension.alternatives[supportedAltNdx], extension.effectiveStages)); |
| 94 | } |
| 95 | else |
| 96 | { |
| 97 | // no extension(s). Make a nice output |
| 98 | std::ostringstream extensionList; |
| 99 | |
| 100 | for (size_t ndx = 0; ndx < extension.alternatives.size(); ++ndx) |
| 101 | { |
| 102 | if (!extensionList.str().empty()) |
| 103 | extensionList << ", "; |
| 104 | extensionList << extension.alternatives[ndx]; |
| 105 | } |
| 106 | |
| 107 | if (extension.alternatives.size() == 1) |
| 108 | throw tcu::NotSupportedError("Test requires extension " + extensionList.str()); |
| 109 | else |
| 110 | throw tcu::NotSupportedError("Test requires any extension of " + extensionList.str()); |
| 111 | } |
Pyry Haulos | 877323d | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 112 | } |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 113 | |
| 114 | return specialized; |
| 115 | } |
| 116 | |
| 117 | static void checkImplementationLimits (const vector<RequiredCapability>& requiredCaps, |
| 118 | const ContextInfo& ctxInfo) |
| 119 | { |
| 120 | for (size_t capNdx = 0; capNdx < requiredCaps.size(); ++capNdx) |
| 121 | { |
| 122 | const deUint32 pname = requiredCaps[capNdx].enumName; |
| 123 | const int requiredValue = requiredCaps[capNdx].referenceValue; |
| 124 | const int supportedValue = ctxInfo.getInt((int)pname); |
| 125 | |
| 126 | if (supportedValue <= requiredValue) |
| 127 | throw tcu::NotSupportedError("Test requires " + de::toString(glu::getGettableStateStr(pname)) + " (" + de::toString(supportedValue) + ") >= " + de::toString(requiredValue)); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | // Shader source specialization |
| 132 | |
| 133 | // This functions builds a matching vertex shader for a 'both' case, when |
| 134 | // the fragment shader is being tested. |
| 135 | // We need to build attributes and varyings for each 'input'. |
| 136 | static string genVertexShader (const ShaderCaseSpecification& spec) |
| 137 | { |
| 138 | ostringstream res; |
| 139 | const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); |
| 140 | const char* const vtxIn = usesInout ? "in" : "attribute"; |
| 141 | const char* const vtxOut = usesInout ? "out" : "varying"; |
| 142 | |
| 143 | res << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n"; |
| 144 | |
| 145 | // Declarations (position + attribute/varying for each input). |
| 146 | res << "precision highp float;\n"; |
| 147 | res << "precision highp int;\n"; |
| 148 | res << "\n"; |
| 149 | res << vtxIn << " highp vec4 dEQP_Position;\n"; |
| 150 | |
| 151 | for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) |
| 152 | { |
| 153 | const Value& val = spec.values.inputs[ndx]; |
| 154 | const DataType basicType = val.type.getBasicType(); |
| 155 | const DataType floatType = getDataTypeFloatScalars(basicType); |
| 156 | const char* const typeStr = getDataTypeName(floatType); |
| 157 | |
| 158 | res << vtxIn << " " << typeStr << " a_" << val.name << ";\n"; |
| 159 | |
| 160 | if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| 161 | res << vtxOut << " " << typeStr << " " << val.name << ";\n"; |
| 162 | else |
| 163 | res << vtxOut << " " << typeStr << " v_" << val.name << ";\n"; |
| 164 | } |
| 165 | res << "\n"; |
| 166 | |
| 167 | // Main function. |
| 168 | // - gl_Position = dEQP_Position; |
| 169 | // - for each input: write attribute directly to varying |
| 170 | res << "void main()\n"; |
| 171 | res << "{\n"; |
| 172 | res << " gl_Position = dEQP_Position;\n"; |
| 173 | for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) |
| 174 | { |
| 175 | const Value& val = spec.values.inputs[ndx]; |
| 176 | const string& name = val.name; |
| 177 | |
| 178 | if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT) |
| 179 | res << " " << name << " = a_" << name << ";\n"; |
| 180 | else |
| 181 | res << " v_" << name << " = a_" << name << ";\n"; |
| 182 | } |
| 183 | |
| 184 | res << "}\n"; |
| 185 | return res.str(); |
| 186 | } |
| 187 | |
| 188 | static void genCompareOp (ostringstream& output, const char* dstVec4Var, const ValueBlock& valueBlock, const char* nonFloatNamePrefix, const char* checkVarName) |
| 189 | { |
| 190 | bool isFirstOutput = true; |
| 191 | |
| 192 | for (size_t ndx = 0; ndx < valueBlock.outputs.size(); ndx++) |
| 193 | { |
| 194 | const Value& val = valueBlock.outputs[ndx]; |
| 195 | |
| 196 | // Check if we're only interested in one variable (then skip if not the right one). |
| 197 | if (checkVarName && val.name != checkVarName) |
| 198 | continue; |
| 199 | |
| 200 | // Prefix. |
| 201 | if (isFirstOutput) |
| 202 | { |
| 203 | output << "bool RES = "; |
| 204 | isFirstOutput = false; |
| 205 | } |
| 206 | else |
| 207 | output << "RES = RES && "; |
| 208 | |
| 209 | // Generate actual comparison. |
| 210 | if (getDataTypeScalarType(val.type.getBasicType()) == TYPE_FLOAT) |
| 211 | output << "isOk(" << val.name << ", ref_" << val.name << ", 0.05);\n"; |
| 212 | else |
| 213 | output << "isOk(" << nonFloatNamePrefix << val.name << ", ref_" << val.name << ");\n"; |
| 214 | } |
| 215 | |
| 216 | if (isFirstOutput) |
| 217 | output << dstVec4Var << " = vec4(1.0);\n"; // \todo [petri] Should we give warning if not expect-failure case? |
| 218 | else |
| 219 | output << dstVec4Var << " = vec4(RES, RES, RES, 1.0);\n"; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 220 | } |
| 221 | |
| 222 | static inline bool supportsFragmentHighp (glu::GLSLVersion version) |
| 223 | { |
| 224 | return version != glu::GLSL_VERSION_100_ES; |
| 225 | } |
| 226 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 227 | static string genFragmentShader (const ShaderCaseSpecification& spec) |
Jarkko Pöyry | c611ca5 | 2015-06-09 19:08:33 -0700 | [diff] [blame] | 228 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 229 | ostringstream shader; |
| 230 | const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); |
| 231 | const bool customColorOut = usesInout; |
| 232 | const char* const fragIn = usesInout ? "in" : "varying"; |
| 233 | const char* const prec = supportsFragmentHighp(spec.targetVersion) ? "highp" : "mediump"; |
Jarkko Pöyry | c611ca5 | 2015-06-09 19:08:33 -0700 | [diff] [blame] | 234 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 235 | shader << glu::getGLSLVersionDeclaration(spec.targetVersion) << "\n"; |
Jarkko Pöyry | c611ca5 | 2015-06-09 19:08:33 -0700 | [diff] [blame] | 236 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 237 | shader << "precision " << prec << " float;\n"; |
| 238 | shader << "precision " << prec << " int;\n"; |
| 239 | shader << "\n"; |
Pyry Haulos | 877323d | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 240 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 241 | if (customColorOut) |
Pyry Haulos | 877323d | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 242 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 243 | shader << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; |
| 244 | shader << "\n"; |
| 245 | } |
| 246 | |
| 247 | genCompareFunctions(shader, spec.values, true); |
| 248 | shader << "\n"; |
| 249 | |
| 250 | // Declarations (varying, reference for each output). |
| 251 | for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++) |
| 252 | { |
| 253 | const Value& val = spec.values.outputs[ndx]; |
| 254 | const DataType basicType = val.type.getBasicType(); |
| 255 | const DataType floatType = getDataTypeFloatScalars(basicType); |
| 256 | const char* const floatTypeStr = getDataTypeName(floatType); |
| 257 | const char* const refTypeStr = getDataTypeName(basicType); |
| 258 | |
| 259 | if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| 260 | shader << fragIn << " " << floatTypeStr << " " << val.name << ";\n"; |
| 261 | else |
| 262 | shader << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n"; |
| 263 | |
| 264 | shader << "uniform " << refTypeStr << " ref_" << val.name << ";\n"; |
| 265 | } |
| 266 | |
| 267 | shader << "\n"; |
| 268 | shader << "void main()\n"; |
| 269 | shader << "{\n"; |
| 270 | |
| 271 | shader << " "; |
| 272 | genCompareOp(shader, customColorOut ? "dEQP_FragColor" : "gl_FragColor", spec.values, "v_", DE_NULL); |
| 273 | |
| 274 | shader << "}\n"; |
| 275 | return shader.str(); |
| 276 | } |
| 277 | |
| 278 | // Specialize a shader for the vertex shader test case. |
Pyry Haulos | 677e464 | 2015-09-30 10:00:12 -0700 | [diff] [blame^] | 279 | static string specializeVertexShader (const ShaderCaseSpecification& spec, const std::string& src, const vector<RequiredExtension>& extensions) |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 280 | { |
| 281 | ostringstream decl; |
| 282 | ostringstream setup; |
| 283 | ostringstream output; |
| 284 | const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); |
| 285 | const char* const vtxIn = usesInout ? "in" : "attribute"; |
| 286 | const char* const vtxOut = usesInout ? "out" : "varying"; |
| 287 | |
| 288 | // generated from "both" case |
| 289 | DE_ASSERT(spec.caseType == CASETYPE_VERTEX_ONLY); |
| 290 | |
| 291 | // Output (write out position). |
| 292 | output << "gl_Position = dEQP_Position;\n"; |
| 293 | |
| 294 | // Declarations (position + attribute for each input, varying for each output). |
| 295 | decl << vtxIn << " highp vec4 dEQP_Position;\n"; |
| 296 | |
| 297 | for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) |
| 298 | { |
| 299 | const Value& val = spec.values.inputs[ndx]; |
| 300 | const DataType basicType = val.type.getBasicType(); |
| 301 | const DataType floatType = getDataTypeFloatScalars(basicType); |
| 302 | const char* const floatTypeStr = getDataTypeName(floatType); |
| 303 | const char* const refTypeStr = getDataTypeName(basicType); |
| 304 | |
| 305 | if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 306 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 307 | decl << vtxIn << " " << floatTypeStr << " " << val.name << ";\n"; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 308 | } |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 309 | else |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 310 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 311 | decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n"; |
| 312 | setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(a_" << val.name << ");\n"; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 313 | } |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 314 | } |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 315 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 316 | // \todo [2015-07-24 pyry] Why are uniforms missing? |
| 317 | |
| 318 | for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++) |
| 319 | { |
| 320 | const Value& val = spec.values.outputs[ndx]; |
| 321 | const DataType basicType = val.type.getBasicType(); |
| 322 | const DataType floatType = getDataTypeFloatScalars(basicType); |
| 323 | const char* const floatTypeStr = getDataTypeName(floatType); |
| 324 | const char* const refTypeStr = getDataTypeName(basicType); |
| 325 | |
| 326 | if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| 327 | decl << vtxOut << " " << floatTypeStr << " " << val.name << ";\n"; |
| 328 | else |
Jarkko Pöyry | 789ca93 | 2015-03-12 14:23:10 -0700 | [diff] [blame] | 329 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 330 | decl << vtxOut << " " << floatTypeStr << " v_" << val.name << ";\n"; |
| 331 | decl << refTypeStr << " " << val.name << ";\n"; |
Pyry Haulos | 877323d | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 332 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 333 | output << "v_" << val.name << " = " << floatTypeStr << "(" << val.name << ");\n"; |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | // Shader specialization. |
| 338 | map<string, string> params; |
| 339 | params.insert(pair<string, string>("DECLARATIONS", decl.str())); |
| 340 | params.insert(pair<string, string>("SETUP", setup.str())); |
| 341 | params.insert(pair<string, string>("OUTPUT", output.str())); |
| 342 | params.insert(pair<string, string>("POSITION_FRAG_COLOR", "gl_Position")); |
| 343 | |
| 344 | StringTemplate tmpl (src); |
| 345 | const string baseSrc = tmpl.specialize(params); |
Pyry Haulos | 677e464 | 2015-09-30 10:00:12 -0700 | [diff] [blame^] | 346 | const string withExt = injectExtensionRequirements(baseSrc, extensions, SHADERTYPE_VERTEX); |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 347 | |
| 348 | return withExt; |
| 349 | } |
| 350 | |
| 351 | // Specialize a shader for the fragment shader test case. |
Pyry Haulos | 677e464 | 2015-09-30 10:00:12 -0700 | [diff] [blame^] | 352 | static string specializeFragmentShader (const ShaderCaseSpecification& spec, const std::string& src, const vector<RequiredExtension>& extensions) |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 353 | { |
| 354 | ostringstream decl; |
| 355 | ostringstream setup; |
| 356 | ostringstream output; |
| 357 | |
| 358 | const bool usesInout = glslVersionUsesInOutQualifiers(spec.targetVersion); |
| 359 | const bool customColorOut = usesInout; |
| 360 | const char* const fragIn = usesInout ? "in" : "varying"; |
| 361 | const char* const fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor"; |
| 362 | |
| 363 | // generated from "both" case |
| 364 | DE_ASSERT(spec.caseType == CASETYPE_FRAGMENT_ONLY); |
| 365 | |
| 366 | genCompareFunctions(decl, spec.values, false); |
| 367 | genCompareOp(output, fragColor, spec.values, "", DE_NULL); |
| 368 | |
| 369 | if (customColorOut) |
| 370 | decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; |
| 371 | |
| 372 | for (size_t ndx = 0; ndx < spec.values.inputs.size(); ndx++) |
| 373 | { |
| 374 | const Value& val = spec.values.inputs[ndx]; |
| 375 | const DataType basicType = val.type.getBasicType(); |
| 376 | const DataType floatType = getDataTypeFloatScalars(basicType); |
| 377 | const char* const floatTypeStr = getDataTypeName(floatType); |
| 378 | const char* const refTypeStr = getDataTypeName(basicType); |
| 379 | |
| 380 | if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| 381 | decl << fragIn << " " << floatTypeStr << " " << val.name << ";\n"; |
| 382 | else |
| 383 | { |
| 384 | decl << fragIn << " " << floatTypeStr << " v_" << val.name << ";\n"; |
| 385 | std::string offset = isDataTypeIntOrIVec(basicType) ? " * 1.0025" : ""; // \todo [petri] bit of a hack to avoid errors in chop() due to varying interpolation |
| 386 | setup << refTypeStr << " " << val.name << " = " << refTypeStr << "(v_" << val.name << offset << ");\n"; |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | // \todo [2015-07-24 pyry] Why are uniforms missing? |
| 391 | |
| 392 | for (size_t ndx = 0; ndx < spec.values.outputs.size(); ndx++) |
| 393 | { |
| 394 | const Value& val = spec.values.outputs[ndx]; |
| 395 | const DataType basicType = val.type.getBasicType(); |
| 396 | const char* const refTypeStr = getDataTypeName(basicType); |
| 397 | |
| 398 | decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n"; |
| 399 | decl << refTypeStr << " " << val.name << ";\n"; |
| 400 | } |
| 401 | |
| 402 | /* \todo [2010-04-01 petri] Check all outputs. */ |
| 403 | |
| 404 | // Shader specialization. |
| 405 | map<string, string> params; |
| 406 | params.insert(pair<string, string>("DECLARATIONS", decl.str())); |
| 407 | params.insert(pair<string, string>("SETUP", setup.str())); |
| 408 | params.insert(pair<string, string>("OUTPUT", output.str())); |
| 409 | params.insert(pair<string, string>("POSITION_FRAG_COLOR", fragColor)); |
| 410 | |
| 411 | StringTemplate tmpl (src); |
| 412 | const string baseSrc = tmpl.specialize(params); |
Pyry Haulos | 677e464 | 2015-09-30 10:00:12 -0700 | [diff] [blame^] | 413 | const string withExt = injectExtensionRequirements(baseSrc, extensions, SHADERTYPE_FRAGMENT); |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 414 | |
| 415 | return withExt; |
| 416 | } |
| 417 | |
| 418 | static void generateUniformDeclarations (std::ostream& dst, const ValueBlock& valueBlock) |
| 419 | { |
| 420 | for (size_t ndx = 0; ndx < valueBlock.uniforms.size(); ndx++) |
| 421 | { |
| 422 | const Value& val = valueBlock.uniforms[ndx]; |
| 423 | const char* const typeStr = getDataTypeName(val.type.getBasicType()); |
| 424 | |
| 425 | if (val.name.find('.') == string::npos) |
| 426 | dst << "uniform " << typeStr << " " << val.name << ";\n"; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 427 | } |
| 428 | } |
| 429 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 430 | static map<string, string> generateVertexSpecialization (const ProgramSpecializationParams& specParams) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 431 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 432 | const bool usesInout = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion); |
| 433 | const char* vtxIn = usesInout ? "in" : "attribute"; |
| 434 | ostringstream decl; |
| 435 | ostringstream setup; |
| 436 | map<string, string> params; |
| 437 | |
| 438 | decl << vtxIn << " highp vec4 dEQP_Position;\n"; |
| 439 | |
| 440 | for (size_t ndx = 0; ndx < specParams.caseSpec.values.inputs.size(); ndx++) |
| 441 | { |
| 442 | const Value& val = specParams.caseSpec.values.inputs[ndx]; |
| 443 | const DataType basicType = val.type.getBasicType(); |
| 444 | const char* const typeStr = getDataTypeName(val.type.getBasicType()); |
| 445 | |
| 446 | if (getDataTypeScalarType(basicType) == TYPE_FLOAT) |
| 447 | { |
| 448 | decl << vtxIn << " " << typeStr << " " << val.name << ";\n"; |
| 449 | } |
| 450 | else |
| 451 | { |
| 452 | const DataType floatType = getDataTypeFloatScalars(basicType); |
| 453 | const char* const floatTypeStr = getDataTypeName(floatType); |
| 454 | |
| 455 | decl << vtxIn << " " << floatTypeStr << " a_" << val.name << ";\n"; |
| 456 | setup << typeStr << " " << val.name << " = " << typeStr << "(a_" << val.name << ");\n"; |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | generateUniformDeclarations(decl, specParams.caseSpec.values); |
| 461 | |
| 462 | params.insert(pair<string, string>("VERTEX_DECLARATIONS", decl.str())); |
| 463 | params.insert(pair<string, string>("VERTEX_SETUP", setup.str())); |
| 464 | params.insert(pair<string, string>("VERTEX_OUTPUT", string("gl_Position = dEQP_Position;\n"))); |
| 465 | |
| 466 | return params; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 467 | } |
| 468 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 469 | static map<string, string> generateFragmentSpecialization (const ProgramSpecializationParams& specParams) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 470 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 471 | const bool usesInout = glslVersionUsesInOutQualifiers(specParams.caseSpec.targetVersion); |
| 472 | const bool customColorOut = usesInout; |
| 473 | const char* const fragColor = customColorOut ? "dEQP_FragColor" : "gl_FragColor"; |
| 474 | ostringstream decl; |
| 475 | ostringstream output; |
| 476 | map<string, string> params; |
| 477 | |
| 478 | genCompareFunctions(decl, specParams.caseSpec.values, false); |
| 479 | genCompareOp(output, fragColor, specParams.caseSpec.values, "", DE_NULL); |
| 480 | |
| 481 | if (customColorOut) |
| 482 | decl << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"; |
| 483 | |
| 484 | for (size_t ndx = 0; ndx < specParams.caseSpec.values.outputs.size(); ndx++) |
| 485 | { |
| 486 | const Value& val = specParams.caseSpec.values.outputs[ndx]; |
| 487 | const char* const refTypeStr = getDataTypeName(val.type.getBasicType()); |
| 488 | |
| 489 | decl << "uniform " << refTypeStr << " ref_" << val.name << ";\n"; |
| 490 | decl << refTypeStr << " " << val.name << ";\n"; |
| 491 | } |
| 492 | |
| 493 | generateUniformDeclarations(decl, specParams.caseSpec.values); |
| 494 | |
| 495 | params.insert(pair<string, string>("FRAGMENT_DECLARATIONS", decl.str())); |
| 496 | params.insert(pair<string, string>("FRAGMENT_OUTPUT", output.str())); |
| 497 | params.insert(pair<string, string>("FRAG_COLOR", fragColor)); |
| 498 | |
| 499 | return params; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 500 | } |
| 501 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 502 | static map<string, string> generateGeometrySpecialization (const ProgramSpecializationParams& specParams) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 503 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 504 | ostringstream decl; |
| 505 | map<string, string> params; |
| 506 | |
| 507 | decl << "layout (triangles) in;\n"; |
| 508 | decl << "layout (triangle_strip, max_vertices=3) out;\n"; |
| 509 | decl << "\n"; |
| 510 | |
| 511 | generateUniformDeclarations(decl, specParams.caseSpec.values); |
| 512 | |
| 513 | params.insert(pair<string, string>("GEOMETRY_DECLARATIONS", decl.str())); |
| 514 | |
| 515 | return params; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 516 | } |
| 517 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 518 | static map<string, string> generateTessControlSpecialization (const ProgramSpecializationParams& specParams) |
| 519 | { |
| 520 | ostringstream decl; |
| 521 | ostringstream output; |
| 522 | map<string, string> params; |
| 523 | |
| 524 | decl << "layout (vertices=3) out;\n"; |
| 525 | decl << "\n"; |
| 526 | |
| 527 | generateUniformDeclarations(decl, specParams.caseSpec.values); |
| 528 | |
| 529 | output << "gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" |
| 530 | "gl_TessLevelInner[0] = 2.0;\n" |
| 531 | "gl_TessLevelInner[1] = 2.0;\n" |
| 532 | "gl_TessLevelOuter[0] = 2.0;\n" |
| 533 | "gl_TessLevelOuter[1] = 2.0;\n" |
| 534 | "gl_TessLevelOuter[2] = 2.0;\n" |
| 535 | "gl_TessLevelOuter[3] = 2.0;"; |
| 536 | |
| 537 | params.insert(pair<string, string>("TESSELLATION_CONTROL_DECLARATIONS", decl.str())); |
| 538 | params.insert(pair<string, string>("TESSELLATION_CONTROL_OUTPUT", output.str())); |
| 539 | params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices))); |
| 540 | |
| 541 | return params; |
| 542 | } |
| 543 | |
| 544 | static map<string, string> generateTessEvalSpecialization (const ProgramSpecializationParams& specParams) |
| 545 | { |
| 546 | ostringstream decl; |
| 547 | ostringstream output; |
| 548 | map<string, string> params; |
| 549 | |
| 550 | decl << "layout (triangles) in;\n"; |
| 551 | decl << "\n"; |
| 552 | |
| 553 | generateUniformDeclarations(decl, specParams.caseSpec.values); |
| 554 | |
| 555 | 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"; |
| 556 | |
| 557 | params.insert(pair<string, string>("TESSELLATION_EVALUATION_DECLARATIONS", decl.str())); |
| 558 | params.insert(pair<string, string>("TESSELLATION_EVALUATION_OUTPUT", output.str())); |
| 559 | params.insert(pair<string, string>("GL_MAX_PATCH_VERTICES", de::toString(specParams.maxPatchVertices))); |
| 560 | |
| 561 | return params; |
| 562 | } |
| 563 | |
| 564 | static void specializeShaderSources (ProgramSources& dst, |
| 565 | const ProgramSources& src, |
| 566 | const ProgramSpecializationParams& specParams, |
| 567 | glu::ShaderType shaderType, |
| 568 | map<string, string> (*specializationGenerator) (const ProgramSpecializationParams& specParams)) |
| 569 | { |
| 570 | if (!src.sources[shaderType].empty()) |
| 571 | { |
| 572 | const map<string, string> tmplParams = specializationGenerator(specParams); |
| 573 | |
| 574 | for (size_t ndx = 0; ndx < src.sources[shaderType].size(); ++ndx) |
| 575 | { |
| 576 | const StringTemplate tmpl (src.sources[shaderType][ndx]); |
| 577 | const std::string baseGLSLCode = tmpl.specialize(tmplParams); |
| 578 | const std::string sourceWithExts = injectExtensionRequirements(baseGLSLCode, specParams.requiredExtensions, shaderType); |
| 579 | |
| 580 | dst << glu::ShaderSource(shaderType, sourceWithExts); |
| 581 | } |
| 582 | } |
| 583 | } |
| 584 | |
| 585 | static void specializeProgramSources (glu::ProgramSources& dst, |
| 586 | const glu::ProgramSources& src, |
| 587 | const ProgramSpecializationParams& specParams) |
| 588 | { |
| 589 | specializeShaderSources(dst, src, specParams, SHADERTYPE_VERTEX, generateVertexSpecialization); |
| 590 | specializeShaderSources(dst, src, specParams, SHADERTYPE_FRAGMENT, generateFragmentSpecialization); |
| 591 | specializeShaderSources(dst, src, specParams, SHADERTYPE_GEOMETRY, generateGeometrySpecialization); |
| 592 | specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_CONTROL, generateTessControlSpecialization); |
| 593 | specializeShaderSources(dst, src, specParams, SHADERTYPE_TESSELLATION_EVALUATION, generateTessEvalSpecialization); |
| 594 | |
| 595 | dst << ProgramSeparable(src.separable); |
| 596 | } |
| 597 | |
| 598 | enum |
| 599 | { |
| 600 | VIEWPORT_WIDTH = 128, |
| 601 | VIEWPORT_HEIGHT = 128 |
| 602 | }; |
| 603 | |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 604 | class BeforeDrawValidator : public glu::DrawUtilCallback |
| 605 | { |
| 606 | public: |
| 607 | enum TargetType |
| 608 | { |
| 609 | TARGETTYPE_PROGRAM = 0, |
| 610 | TARGETTYPE_PIPELINE, |
| 611 | |
| 612 | TARGETTYPE_LAST |
| 613 | }; |
| 614 | |
| 615 | BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType); |
| 616 | |
| 617 | void beforeDrawCall (void); |
| 618 | |
| 619 | const std::string& getInfoLog (void) const; |
| 620 | glw::GLint getValidateStatus (void) const; |
| 621 | |
| 622 | private: |
| 623 | const glw::Functions& m_gl; |
| 624 | const glw::GLuint m_target; |
| 625 | const TargetType m_targetType; |
| 626 | |
| 627 | glw::GLint m_validateStatus; |
| 628 | std::string m_logMessage; |
| 629 | }; |
| 630 | |
| 631 | BeforeDrawValidator::BeforeDrawValidator (const glw::Functions& gl, glw::GLuint target, TargetType targetType) |
| 632 | : m_gl (gl) |
| 633 | , m_target (target) |
| 634 | , m_targetType (targetType) |
| 635 | , m_validateStatus (-1) |
| 636 | { |
| 637 | DE_ASSERT(targetType < TARGETTYPE_LAST); |
| 638 | } |
| 639 | |
| 640 | void BeforeDrawValidator::beforeDrawCall (void) |
| 641 | { |
| 642 | glw::GLint bytesWritten = 0; |
| 643 | glw::GLint infoLogLength; |
| 644 | std::vector<glw::GLchar> logBuffer; |
| 645 | int stringLength; |
| 646 | |
| 647 | // validate |
| 648 | if (m_targetType == TARGETTYPE_PROGRAM) |
| 649 | m_gl.validateProgram(m_target); |
| 650 | else if (m_targetType == TARGETTYPE_PIPELINE) |
| 651 | m_gl.validateProgramPipeline(m_target); |
| 652 | else |
| 653 | DE_ASSERT(false); |
| 654 | |
| 655 | GLU_EXPECT_NO_ERROR(m_gl.getError(), "validate"); |
| 656 | |
| 657 | // check status |
| 658 | m_validateStatus = -1; |
| 659 | |
| 660 | if (m_targetType == TARGETTYPE_PROGRAM) |
| 661 | m_gl.getProgramiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus); |
| 662 | else if (m_targetType == TARGETTYPE_PIPELINE) |
| 663 | m_gl.getProgramPipelineiv(m_target, GL_VALIDATE_STATUS, &m_validateStatus); |
| 664 | else |
| 665 | DE_ASSERT(false); |
| 666 | |
| 667 | GLU_EXPECT_NO_ERROR(m_gl.getError(), "get validate status"); |
| 668 | TCU_CHECK(m_validateStatus == GL_TRUE || m_validateStatus == GL_FALSE); |
| 669 | |
| 670 | // read log |
| 671 | |
| 672 | infoLogLength = 0; |
| 673 | |
| 674 | if (m_targetType == TARGETTYPE_PROGRAM) |
| 675 | m_gl.getProgramiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength); |
| 676 | else if (m_targetType == TARGETTYPE_PIPELINE) |
| 677 | m_gl.getProgramPipelineiv(m_target, GL_INFO_LOG_LENGTH, &infoLogLength); |
| 678 | else |
| 679 | DE_ASSERT(false); |
| 680 | |
| 681 | GLU_EXPECT_NO_ERROR(m_gl.getError(), "get info log length"); |
| 682 | |
| 683 | if (infoLogLength <= 0) |
| 684 | { |
| 685 | m_logMessage.clear(); |
| 686 | return; |
| 687 | } |
| 688 | |
| 689 | 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 |
| 690 | |
| 691 | if (m_targetType == TARGETTYPE_PROGRAM) |
| 692 | m_gl.getProgramInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]); |
| 693 | else if (m_targetType == TARGETTYPE_PIPELINE) |
| 694 | m_gl.getProgramPipelineInfoLog(m_target, infoLogLength + 1, &bytesWritten, &logBuffer[0]); |
| 695 | else |
| 696 | DE_ASSERT(false); |
| 697 | |
| 698 | // just ignore bytesWritten to be safe, find the null terminator |
| 699 | stringLength = (int)(std::find(logBuffer.begin(), logBuffer.end(), '0') - logBuffer.begin()); |
| 700 | m_logMessage.assign(&logBuffer[0], stringLength); |
| 701 | } |
| 702 | |
| 703 | const std::string& BeforeDrawValidator::getInfoLog (void) const |
| 704 | { |
| 705 | return m_logMessage; |
| 706 | } |
| 707 | |
| 708 | glw::GLint BeforeDrawValidator::getValidateStatus (void) const |
| 709 | { |
| 710 | return m_validateStatus; |
| 711 | } |
| 712 | |
| 713 | // ShaderCase. |
| 714 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 715 | ShaderLibraryCase::ShaderLibraryCase (tcu::TestContext& testCtx, RenderContext& renderCtx, const glu::ContextInfo& contextInfo, const char* name, const char* description, const ShaderCaseSpecification& specification) |
| 716 | : tcu::TestCase (testCtx, name, description) |
| 717 | , m_renderCtx (renderCtx) |
| 718 | , m_contextInfo (contextInfo) |
| 719 | , m_spec (specification) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 720 | { |
| 721 | } |
| 722 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 723 | ShaderLibraryCase::~ShaderLibraryCase (void) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 724 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 725 | } |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 726 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 727 | void ShaderLibraryCase::init (void) |
| 728 | { |
| 729 | DE_ASSERT(isValid(m_spec)); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 730 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 731 | checkImplementationLimits(m_spec.requiredCaps, m_contextInfo); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 732 | |
| 733 | // log the expected result |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 734 | switch (m_spec.expectResult) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 735 | { |
| 736 | case EXPECT_PASS: |
| 737 | // Don't write anything |
| 738 | break; |
| 739 | |
| 740 | case EXPECT_COMPILE_FAIL: |
| 741 | m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation to fail." << tcu::TestLog::EndMessage; |
| 742 | break; |
| 743 | |
| 744 | case EXPECT_LINK_FAIL: |
| 745 | m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program linking to fail." << tcu::TestLog::EndMessage; |
| 746 | break; |
| 747 | |
| 748 | case EXPECT_COMPILE_LINK_FAIL: |
| 749 | m_testCtx.getLog() << tcu::TestLog::Message << "Expecting either shader compilation or program linking to fail." << tcu::TestLog::EndMessage; |
| 750 | break; |
| 751 | |
| 752 | case EXPECT_VALIDATION_FAIL: |
| 753 | m_testCtx.getLog() << tcu::TestLog::Message << "Expecting program validation to fail." << tcu::TestLog::EndMessage; |
| 754 | break; |
| 755 | |
Jarkko Pöyry | 9e57435 | 2014-12-16 11:33:56 -0800 | [diff] [blame] | 756 | case EXPECT_BUILD_SUCCESSFUL: |
| 757 | m_testCtx.getLog() << tcu::TestLog::Message << "Expecting shader compilation and program linking to succeed. Resulting program will not be executed." << tcu::TestLog::EndMessage; |
| 758 | break; |
| 759 | |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 760 | default: |
| 761 | DE_ASSERT(false); |
| 762 | break; |
| 763 | } |
| 764 | } |
| 765 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 766 | static void setUniformValue (const glw::Functions& gl, const std::vector<deUint32>& pipelinePrograms, const std::string& name, const Value& val, int arrayNdx, tcu::TestLog& log) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 767 | { |
| 768 | bool foundAnyMatch = false; |
| 769 | |
| 770 | for (int programNdx = 0; programNdx < (int)pipelinePrograms.size(); ++programNdx) |
| 771 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 772 | const DataType dataType = val.type.getBasicType(); |
| 773 | const int scalarSize = getDataTypeScalarSize(dataType); |
| 774 | const int loc = gl.getUniformLocation(pipelinePrograms[programNdx], name.c_str()); |
| 775 | const int elemNdx = arrayNdx * scalarSize; |
| 776 | |
| 777 | DE_ASSERT(elemNdx+scalarSize <= (int)val.elements.size()); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 778 | |
| 779 | if (loc == -1) |
| 780 | continue; |
| 781 | |
| 782 | foundAnyMatch = true; |
| 783 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 784 | DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLfloat)); |
| 785 | DE_STATIC_ASSERT(sizeof(Value::Element) == sizeof(glw::GLint)); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 786 | |
| 787 | gl.useProgram(pipelinePrograms[programNdx]); |
| 788 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 789 | switch (dataType) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 790 | { |
| 791 | case TYPE_FLOAT: gl.uniform1fv(loc, 1, &val.elements[elemNdx].float32); break; |
| 792 | case TYPE_FLOAT_VEC2: gl.uniform2fv(loc, 1, &val.elements[elemNdx].float32); break; |
| 793 | case TYPE_FLOAT_VEC3: gl.uniform3fv(loc, 1, &val.elements[elemNdx].float32); break; |
| 794 | case TYPE_FLOAT_VEC4: gl.uniform4fv(loc, 1, &val.elements[elemNdx].float32); break; |
| 795 | case TYPE_FLOAT_MAT2: gl.uniformMatrix2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break; |
| 796 | case TYPE_FLOAT_MAT3: gl.uniformMatrix3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break; |
| 797 | case TYPE_FLOAT_MAT4: gl.uniformMatrix4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break; |
| 798 | case TYPE_INT: gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); break; |
| 799 | case TYPE_INT_VEC2: gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); break; |
| 800 | case TYPE_INT_VEC3: gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); break; |
| 801 | case TYPE_INT_VEC4: gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); break; |
| 802 | case TYPE_BOOL: gl.uniform1iv(loc, 1, &val.elements[elemNdx].int32); break; |
| 803 | case TYPE_BOOL_VEC2: gl.uniform2iv(loc, 1, &val.elements[elemNdx].int32); break; |
| 804 | case TYPE_BOOL_VEC3: gl.uniform3iv(loc, 1, &val.elements[elemNdx].int32); break; |
| 805 | case TYPE_BOOL_VEC4: gl.uniform4iv(loc, 1, &val.elements[elemNdx].int32); break; |
| 806 | case TYPE_UINT: gl.uniform1uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break; |
| 807 | case TYPE_UINT_VEC2: gl.uniform2uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break; |
| 808 | case TYPE_UINT_VEC3: gl.uniform3uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break; |
| 809 | case TYPE_UINT_VEC4: gl.uniform4uiv(loc, 1, (const deUint32*)&val.elements[elemNdx].int32); break; |
| 810 | case TYPE_FLOAT_MAT2X3: gl.uniformMatrix2x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break; |
| 811 | case TYPE_FLOAT_MAT2X4: gl.uniformMatrix2x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break; |
| 812 | case TYPE_FLOAT_MAT3X2: gl.uniformMatrix3x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break; |
| 813 | case TYPE_FLOAT_MAT3X4: gl.uniformMatrix3x4fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break; |
| 814 | case TYPE_FLOAT_MAT4X2: gl.uniformMatrix4x2fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break; |
| 815 | case TYPE_FLOAT_MAT4X3: gl.uniformMatrix4x3fv(loc, 1, GL_FALSE, &val.elements[elemNdx].float32); break; |
| 816 | |
| 817 | case TYPE_SAMPLER_2D: |
| 818 | case TYPE_SAMPLER_CUBE: |
Jarkko Pöyry | 1cc61b7 | 2015-05-29 16:36:30 -0700 | [diff] [blame] | 819 | DE_FATAL("implement!"); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 820 | break; |
| 821 | |
| 822 | default: |
| 823 | DE_ASSERT(false); |
| 824 | } |
| 825 | } |
| 826 | |
| 827 | if (!foundAnyMatch) |
| 828 | log << tcu::TestLog::Message << "WARNING // Uniform \"" << name << "\" location is not valid, location = -1. Cannot set value to the uniform." << tcu::TestLog::EndMessage; |
| 829 | } |
| 830 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 831 | static bool isTessellationPresent (const ShaderCaseSpecification& spec) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 832 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 833 | if (spec.programs[0].sources.separable) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 834 | { |
| 835 | const deUint32 tessellationBits = (1 << glu::SHADERTYPE_TESSELLATION_CONTROL) | |
| 836 | (1 << glu::SHADERTYPE_TESSELLATION_EVALUATION); |
| 837 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 838 | for (int programNdx = 0; programNdx < (int)spec.programs.size(); ++programNdx) |
| 839 | if (spec.programs[programNdx].activeStages & tessellationBits) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 840 | return true; |
| 841 | return false; |
| 842 | } |
| 843 | else |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 844 | return !spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_CONTROL].empty() || |
| 845 | !spec.programs[0].sources.sources[glu::SHADERTYPE_TESSELLATION_EVALUATION].empty(); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 846 | } |
| 847 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 848 | static bool checkPixels (tcu::TestLog& log, const tcu::ConstPixelBufferAccess& surface) |
Jarkko Pöyry | 789ca93 | 2015-03-12 14:23:10 -0700 | [diff] [blame] | 849 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 850 | bool allWhite = true; |
| 851 | bool allBlack = true; |
| 852 | bool anyUnexpected = false; |
| 853 | |
| 854 | for (int y = 0; y < surface.getHeight(); y++) |
Jarkko Pöyry | 789ca93 | 2015-03-12 14:23:10 -0700 | [diff] [blame] | 855 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 856 | for (int x = 0; x < surface.getWidth(); x++) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 857 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 858 | const tcu::IVec4 pixel = surface.getPixelInt(x, y); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 859 | // Note: we really do not want to involve alpha in the check comparison |
| 860 | // \todo [2010-09-22 kalle] Do we know that alpha would be one? If yes, could use color constants white and black. |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 861 | const bool isWhite = (pixel[0] == 255) && (pixel[1] == 255) && (pixel[2] == 255); |
| 862 | const bool isBlack = (pixel[0] == 0) && (pixel[1] == 0) && (pixel[2] == 0); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 863 | |
| 864 | allWhite = allWhite && isWhite; |
| 865 | allBlack = allBlack && isBlack; |
| 866 | anyUnexpected = anyUnexpected || (!isWhite && !isBlack); |
| 867 | } |
| 868 | } |
| 869 | |
| 870 | if (!allWhite) |
| 871 | { |
| 872 | if (anyUnexpected) |
| 873 | log << TestLog::Message << "WARNING: expecting all rendered pixels to be white or black, but got other colors as well!" << TestLog::EndMessage; |
| 874 | else if (!allBlack) |
| 875 | log << TestLog::Message << "WARNING: got inconsistent results over the image, when all pixels should be the same color!" << TestLog::EndMessage; |
| 876 | |
| 877 | return false; |
| 878 | } |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 879 | |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 880 | return true; |
| 881 | } |
| 882 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 883 | bool ShaderLibraryCase::execute (void) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 884 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 885 | const float quadSize = 1.0f; |
| 886 | static const float s_positions[4*4] = |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 887 | { |
| 888 | -quadSize, -quadSize, 0.0f, 1.0f, |
| 889 | -quadSize, +quadSize, 0.0f, 1.0f, |
| 890 | +quadSize, -quadSize, 0.0f, 1.0f, |
| 891 | +quadSize, +quadSize, 0.0f, 1.0f |
| 892 | }; |
| 893 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 894 | static const deUint16 s_indices[2*3] = |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 895 | { |
| 896 | 0, 1, 2, |
| 897 | 1, 3, 2 |
| 898 | }; |
| 899 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 900 | TestLog& log = m_testCtx.getLog(); |
| 901 | const glw::Functions& gl = m_renderCtx.getFunctions(); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 902 | |
| 903 | // Compute viewport. |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 904 | const tcu::RenderTarget& renderTarget = m_renderCtx.getRenderTarget(); |
| 905 | de::Random rnd (deStringHash(getName())); |
| 906 | const int width = deMin32(renderTarget.getWidth(), VIEWPORT_WIDTH); |
| 907 | const int height = deMin32(renderTarget.getHeight(), VIEWPORT_HEIGHT); |
| 908 | const int viewportX = rnd.getInt(0, renderTarget.getWidth() - width); |
| 909 | const int viewportY = rnd.getInt(0, renderTarget.getHeight() - height); |
| 910 | const int numVerticesPerDraw = 4; |
| 911 | const bool tessellationPresent = isTessellationPresent(m_spec); |
| 912 | const bool separablePrograms = m_spec.programs[0].sources.separable; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 913 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 914 | bool allCompilesOk = true; |
| 915 | bool allLinksOk = true; |
| 916 | const char* failReason = DE_NULL; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 917 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 918 | vector<ProgramSources> specializedSources (m_spec.programs.size()); |
| 919 | |
| 920 | deUint32 vertexProgramID = -1; |
| 921 | vector<deUint32> pipelineProgramIDs; |
| 922 | vector<SharedPtr<ShaderProgram> > programs; |
| 923 | SharedPtr<ProgramPipeline> programPipeline; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 924 | |
| 925 | GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): start"); |
| 926 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 927 | // Specialize shaders |
| 928 | if (m_spec.caseType == CASETYPE_VERTEX_ONLY) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 929 | { |
Pyry Haulos | 677e464 | 2015-09-30 10:00:12 -0700 | [diff] [blame^] | 930 | const vector<RequiredExtension> reqExt = checkAndSpecializeExtensions(m_spec.programs[0].requiredExtensions, m_contextInfo); |
| 931 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 932 | DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX].size() == 1); |
Pyry Haulos | 677e464 | 2015-09-30 10:00:12 -0700 | [diff] [blame^] | 933 | specializedSources[0] << glu::VertexSource(specializeVertexShader(m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_VERTEX][0], reqExt)) |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 934 | << glu::FragmentSource(genFragmentShader(m_spec)); |
| 935 | } |
| 936 | else if (m_spec.caseType == CASETYPE_FRAGMENT_ONLY) |
| 937 | { |
Pyry Haulos | 677e464 | 2015-09-30 10:00:12 -0700 | [diff] [blame^] | 938 | const vector<RequiredExtension> reqExt = checkAndSpecializeExtensions(m_spec.programs[0].requiredExtensions, m_contextInfo); |
| 939 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 940 | DE_ASSERT(m_spec.programs.size() == 1 && m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT].size() == 1); |
| 941 | specializedSources[0] << glu::VertexSource(genVertexShader(m_spec)) |
Pyry Haulos | 677e464 | 2015-09-30 10:00:12 -0700 | [diff] [blame^] | 942 | << glu::FragmentSource(specializeFragmentShader(m_spec, m_spec.programs[0].sources.sources[SHADERTYPE_FRAGMENT][0], reqExt)); |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 943 | } |
| 944 | else |
| 945 | { |
| 946 | DE_ASSERT(m_spec.caseType == CASETYPE_COMPLETE); |
| 947 | |
| 948 | const int maxPatchVertices = isTessellationPresent(m_spec) ? m_contextInfo.getInt(GL_MAX_PATCH_VERTICES) : 0; |
| 949 | |
| 950 | for (size_t progNdx = 0; progNdx < m_spec.programs.size(); progNdx++) |
| 951 | { |
| 952 | const ProgramSpecializationParams progSpecParams (m_spec, checkAndSpecializeExtensions(m_spec.programs[progNdx].requiredExtensions, m_contextInfo), maxPatchVertices); |
| 953 | |
| 954 | specializeProgramSources(specializedSources[progNdx], m_spec.programs[progNdx].sources, progSpecParams); |
| 955 | } |
| 956 | } |
| 957 | |
| 958 | if (!separablePrograms) |
| 959 | { |
| 960 | de::SharedPtr<glu::ShaderProgram> program (new glu::ShaderProgram(m_renderCtx, specializedSources[0])); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 961 | |
| 962 | vertexProgramID = program->getProgram(); |
| 963 | pipelineProgramIDs.push_back(program->getProgram()); |
| 964 | programs.push_back(program); |
| 965 | |
| 966 | // Check that compile/link results are what we expect. |
| 967 | |
| 968 | DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0); |
| 969 | for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) |
| 970 | if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk) |
| 971 | allCompilesOk = false; |
| 972 | |
| 973 | if (!program->getProgramInfo().linkOk) |
| 974 | allLinksOk = false; |
| 975 | |
| 976 | log << *program; |
| 977 | } |
| 978 | else |
| 979 | { |
| 980 | // Separate programs |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 981 | for (size_t programNdx = 0; programNdx < m_spec.programs.size(); ++programNdx) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 982 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 983 | de::SharedPtr<glu::ShaderProgram> program(new glu::ShaderProgram(m_renderCtx, specializedSources[programNdx])); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 984 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 985 | if (m_spec.programs[programNdx].activeStages & (1u << glu::SHADERTYPE_VERTEX)) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 986 | vertexProgramID = program->getProgram(); |
| 987 | |
| 988 | pipelineProgramIDs.push_back(program->getProgram()); |
| 989 | programs.push_back(program); |
| 990 | |
| 991 | // Check that compile/link results are what we expect. |
| 992 | |
| 993 | DE_STATIC_ASSERT(glu::SHADERTYPE_VERTEX == 0); |
| 994 | for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) |
| 995 | if (program->hasShader((glu::ShaderType)stage) && !program->getShaderInfo((glu::ShaderType)stage).compileOk) |
| 996 | allCompilesOk = false; |
| 997 | |
| 998 | if (!program->getProgramInfo().linkOk) |
| 999 | allLinksOk = false; |
| 1000 | |
| 1001 | // Log program and active stages |
| 1002 | { |
| 1003 | const tcu::ScopedLogSection section (log, "Program", "Program " + de::toString(programNdx+1)); |
| 1004 | tcu::MessageBuilder builder (&log); |
| 1005 | bool firstStage = true; |
| 1006 | |
| 1007 | builder << "Pipeline uses stages: "; |
| 1008 | for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) |
| 1009 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1010 | if (m_spec.programs[programNdx].activeStages & (1u << stage)) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1011 | { |
| 1012 | if (!firstStage) |
| 1013 | builder << ", "; |
| 1014 | builder << glu::getShaderTypeName((glu::ShaderType)stage); |
| 1015 | firstStage = true; |
| 1016 | } |
| 1017 | } |
| 1018 | builder << tcu::TestLog::EndMessage; |
| 1019 | |
| 1020 | log << *program; |
| 1021 | } |
| 1022 | } |
| 1023 | } |
| 1024 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1025 | switch (m_spec.expectResult) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1026 | { |
| 1027 | case EXPECT_PASS: |
| 1028 | case EXPECT_VALIDATION_FAIL: |
Jarkko Pöyry | 9e57435 | 2014-12-16 11:33:56 -0800 | [diff] [blame] | 1029 | case EXPECT_BUILD_SUCCESSFUL: |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1030 | if (!allCompilesOk) |
| 1031 | failReason = "expected shaders to compile and link properly, but failed to compile."; |
| 1032 | else if (!allLinksOk) |
| 1033 | failReason = "expected shaders to compile and link properly, but failed to link."; |
| 1034 | break; |
| 1035 | |
| 1036 | case EXPECT_COMPILE_FAIL: |
| 1037 | if (allCompilesOk && !allLinksOk) |
| 1038 | failReason = "expected compilation to fail, but shaders compiled and link failed."; |
| 1039 | else if (allCompilesOk) |
| 1040 | failReason = "expected compilation to fail, but shaders compiled correctly."; |
| 1041 | break; |
| 1042 | |
| 1043 | case EXPECT_LINK_FAIL: |
| 1044 | if (!allCompilesOk) |
| 1045 | failReason = "expected linking to fail, but unable to compile."; |
| 1046 | else if (allLinksOk) |
| 1047 | failReason = "expected linking to fail, but passed."; |
| 1048 | break; |
| 1049 | |
| 1050 | case EXPECT_COMPILE_LINK_FAIL: |
| 1051 | if (allCompilesOk && allLinksOk) |
| 1052 | failReason = "expected compile or link to fail, but passed."; |
| 1053 | break; |
| 1054 | |
| 1055 | default: |
| 1056 | DE_ASSERT(false); |
| 1057 | return false; |
| 1058 | } |
| 1059 | |
| 1060 | if (failReason != DE_NULL) |
| 1061 | { |
| 1062 | // \todo [2010-06-07 petri] These should be handled in the test case? |
| 1063 | log << TestLog::Message << "ERROR: " << failReason << TestLog::EndMessage; |
| 1064 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1065 | if (m_spec.fullGLSLES100Required) |
Jarkko Pöyry | 789ca93 | 2015-03-12 14:23:10 -0700 | [diff] [blame] | 1066 | { |
| 1067 | log << TestLog::Message |
| 1068 | << "Assuming build failure is caused by implementation not supporting full GLSL ES 100 specification, which is not required." |
| 1069 | << TestLog::EndMessage; |
| 1070 | |
| 1071 | if (allCompilesOk && !allLinksOk) |
| 1072 | { |
| 1073 | // Used features are detectable at compile time. If implementation parses shader |
| 1074 | // at link time, report it as quality warning. |
| 1075 | m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason); |
| 1076 | } |
| 1077 | else |
| 1078 | m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Full GLSL ES 100 is not supported"); |
| 1079 | } |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1080 | else if (m_spec.expectResult == EXPECT_COMPILE_FAIL && allCompilesOk && !allLinksOk) |
Jarkko Pöyry | 789ca93 | 2015-03-12 14:23:10 -0700 | [diff] [blame] | 1081 | { |
| 1082 | // If implementation parses shader at link time, report it as quality warning. |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1083 | m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, failReason); |
Jarkko Pöyry | 789ca93 | 2015-03-12 14:23:10 -0700 | [diff] [blame] | 1084 | } |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1085 | else |
| 1086 | m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason); |
| 1087 | return false; |
| 1088 | } |
| 1089 | |
Jarkko Pöyry | 9e57435 | 2014-12-16 11:33:56 -0800 | [diff] [blame] | 1090 | // Return if shader is not intended to be run |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1091 | if (m_spec.expectResult == EXPECT_COMPILE_FAIL || |
| 1092 | m_spec.expectResult == EXPECT_COMPILE_LINK_FAIL || |
| 1093 | m_spec.expectResult == EXPECT_LINK_FAIL || |
| 1094 | m_spec.expectResult == EXPECT_BUILD_SUCCESSFUL) |
Jarkko Pöyry | 789ca93 | 2015-03-12 14:23:10 -0700 | [diff] [blame] | 1095 | return true; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1096 | |
| 1097 | // Setup viewport. |
| 1098 | gl.viewport(viewportX, viewportY, width, height); |
| 1099 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1100 | if (separablePrograms) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1101 | { |
| 1102 | programPipeline = de::SharedPtr<glu::ProgramPipeline>(new glu::ProgramPipeline(m_renderCtx)); |
| 1103 | |
| 1104 | // Setup pipeline |
| 1105 | gl.bindProgramPipeline(programPipeline->getPipeline()); |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1106 | for (int programNdx = 0; programNdx < (int)m_spec.programs.size(); ++programNdx) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1107 | { |
| 1108 | deUint32 shaderFlags = 0; |
| 1109 | for (int stage = glu::SHADERTYPE_VERTEX; stage < glu::SHADERTYPE_LAST; ++stage) |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1110 | if (m_spec.programs[programNdx].activeStages & (1u << stage)) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1111 | shaderFlags |= glu::getGLShaderTypeBit((glu::ShaderType)stage); |
| 1112 | |
| 1113 | programPipeline->useProgramStages(shaderFlags, pipelineProgramIDs[programNdx]); |
| 1114 | } |
| 1115 | |
| 1116 | programPipeline->activeShaderProgram(vertexProgramID); |
| 1117 | GLU_EXPECT_NO_ERROR(gl.getError(), "setup pipeline"); |
| 1118 | } |
| 1119 | else |
| 1120 | { |
| 1121 | // Start using program |
| 1122 | gl.useProgram(vertexProgramID); |
| 1123 | GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()"); |
| 1124 | } |
| 1125 | |
| 1126 | // Fetch location for positions positions. |
| 1127 | int positionLoc = gl.getAttribLocation(vertexProgramID, "dEQP_Position"); |
| 1128 | if (positionLoc == -1) |
| 1129 | { |
| 1130 | string errStr = string("no location found for attribute 'dEQP_Position'"); |
| 1131 | TCU_FAIL(errStr.c_str()); |
| 1132 | } |
| 1133 | |
| 1134 | // Iterate all value blocks. |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1135 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1136 | const ValueBlock& valueBlock = m_spec.values; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1137 | |
| 1138 | // always render at least one pass even if there is no input/output data |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1139 | const int numRenderPasses = valueBlock.outputs.empty() ? 1 : (int)valueBlock.outputs[0].elements.size() / valueBlock.outputs[0].type.getScalarSize(); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1140 | |
| 1141 | // Iterate all array sub-cases. |
| 1142 | for (int arrayNdx = 0; arrayNdx < numRenderPasses; arrayNdx++) |
| 1143 | { |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1144 | vector<VertexArrayBinding> vertexArrays; |
| 1145 | int attribValueNdx = 0; |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1146 | vector<vector<float> > attribValues (valueBlock.inputs.size()); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1147 | glw::GLenum postDrawError; |
| 1148 | BeforeDrawValidator beforeDrawValidator (gl, |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1149 | (separablePrograms) ? (programPipeline->getPipeline()) : (vertexProgramID), |
| 1150 | (separablePrograms) ? (BeforeDrawValidator::TARGETTYPE_PIPELINE) : (BeforeDrawValidator::TARGETTYPE_PROGRAM)); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1151 | |
| 1152 | vertexArrays.push_back(va::Float(positionLoc, 4, numVerticesPerDraw, 0, &s_positions[0])); |
| 1153 | |
| 1154 | // Collect VA pointer for inputs |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1155 | for (size_t valNdx = 0; valNdx < valueBlock.inputs.size(); valNdx++) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1156 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1157 | const Value& val = valueBlock.inputs[valNdx]; |
| 1158 | const char* const valueName = val.name.c_str(); |
| 1159 | const DataType dataType = val.type.getBasicType(); |
| 1160 | const int scalarSize = getDataTypeScalarSize(dataType); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1161 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1162 | // Replicate values four times. |
| 1163 | std::vector<float>& scalars = attribValues[attribValueNdx++]; |
| 1164 | scalars.resize(numVerticesPerDraw * scalarSize); |
| 1165 | if (isDataTypeFloatOrVec(dataType) || isDataTypeMatrix(dataType)) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1166 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1167 | for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++) |
| 1168 | for (int ndx = 0; ndx < scalarSize; ndx++) |
| 1169 | scalars[repNdx*scalarSize + ndx] = val.elements[arrayNdx*scalarSize + ndx].float32; |
| 1170 | } |
| 1171 | else |
| 1172 | { |
| 1173 | // convert to floats. |
| 1174 | for (int repNdx = 0; repNdx < numVerticesPerDraw; repNdx++) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1175 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1176 | for (int ndx = 0; ndx < scalarSize; ndx++) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1177 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1178 | float v = (float)val.elements[arrayNdx*scalarSize + ndx].int32; |
| 1179 | DE_ASSERT(val.elements[arrayNdx*scalarSize + ndx].int32 == (int)v); |
| 1180 | scalars[repNdx*scalarSize + ndx] = v; |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1181 | } |
| 1182 | } |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1183 | } |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1184 | |
| 1185 | // Attribute name prefix. |
| 1186 | string attribPrefix = ""; |
| 1187 | // \todo [2010-05-27 petri] Should latter condition only apply for vertex cases (or actually non-fragment cases)? |
| 1188 | if ((m_spec.caseType == CASETYPE_FRAGMENT_ONLY) || (getDataTypeScalarType(dataType) != TYPE_FLOAT)) |
| 1189 | attribPrefix = "a_"; |
| 1190 | |
| 1191 | // Input always given as attribute. |
| 1192 | string attribName = attribPrefix + valueName; |
| 1193 | int attribLoc = gl.getAttribLocation(vertexProgramID, attribName.c_str()); |
| 1194 | if (attribLoc == -1) |
| 1195 | { |
| 1196 | log << TestLog::Message << "Warning: no location found for attribute '" << attribName << "'" << TestLog::EndMessage; |
| 1197 | continue; |
| 1198 | } |
| 1199 | |
| 1200 | if (isDataTypeMatrix(dataType)) |
| 1201 | { |
| 1202 | int numCols = getDataTypeMatrixNumColumns(dataType); |
| 1203 | int numRows = getDataTypeMatrixNumRows(dataType); |
| 1204 | DE_ASSERT(scalarSize == numCols*numRows); |
| 1205 | |
| 1206 | for (int i = 0; i < numCols; i++) |
| 1207 | vertexArrays.push_back(va::Float(attribLoc + i, numRows, numVerticesPerDraw, scalarSize*(int)sizeof(float), &scalars[i * numRows])); |
| 1208 | } |
| 1209 | else |
| 1210 | { |
| 1211 | DE_ASSERT(isDataTypeFloatOrVec(dataType) || isDataTypeIntOrIVec(dataType) || isDataTypeUintOrUVec(dataType) || isDataTypeBoolOrBVec(dataType)); |
| 1212 | vertexArrays.push_back(va::Float(attribLoc, scalarSize, numVerticesPerDraw, 0, &scalars[0])); |
| 1213 | } |
| 1214 | |
| 1215 | GLU_EXPECT_NO_ERROR(gl.getError(), "set vertex attrib array"); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1216 | } |
| 1217 | |
| 1218 | GLU_EXPECT_NO_ERROR(gl.getError(), "before set uniforms"); |
| 1219 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1220 | // set reference values for outputs. |
| 1221 | for (size_t valNdx = 0; valNdx < valueBlock.outputs.size(); valNdx++) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1222 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1223 | const Value& val = valueBlock.outputs[valNdx]; |
| 1224 | const char* const valueName = val.name.c_str(); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1225 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1226 | // Set reference value. |
| 1227 | string refName = string("ref_") + valueName; |
| 1228 | setUniformValue(gl, pipelineProgramIDs, refName, val, arrayNdx, m_testCtx.getLog()); |
| 1229 | GLU_EXPECT_NO_ERROR(gl.getError(), "set reference uniforms"); |
| 1230 | } |
| 1231 | |
| 1232 | // set uniform values |
| 1233 | for (size_t valNdx = 0; valNdx < valueBlock.uniforms.size(); valNdx++) |
| 1234 | { |
| 1235 | const Value& val = valueBlock.uniforms[valNdx]; |
| 1236 | const char* const valueName = val.name.c_str(); |
| 1237 | |
| 1238 | setUniformValue(gl, pipelineProgramIDs, valueName, val, arrayNdx, m_testCtx.getLog()); |
| 1239 | GLU_EXPECT_NO_ERROR(gl.getError(), "set uniforms"); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1240 | } |
| 1241 | |
| 1242 | // Clear. |
| 1243 | gl.clearColor(0.125f, 0.25f, 0.5f, 1.0f); |
| 1244 | gl.clear(GL_COLOR_BUFFER_BIT); |
| 1245 | GLU_EXPECT_NO_ERROR(gl.getError(), "clear buffer"); |
| 1246 | |
| 1247 | // Use program or pipeline |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1248 | if (separablePrograms) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1249 | gl.useProgram(0); |
| 1250 | else |
| 1251 | gl.useProgram(vertexProgramID); |
| 1252 | |
| 1253 | // Draw. |
| 1254 | if (tessellationPresent) |
| 1255 | { |
| 1256 | gl.patchParameteri(GL_PATCH_VERTICES, 3); |
| 1257 | GLU_EXPECT_NO_ERROR(gl.getError(), "set patchParameteri(PATCH_VERTICES, 3)"); |
| 1258 | } |
| 1259 | |
| 1260 | draw(m_renderCtx, |
| 1261 | vertexProgramID, |
| 1262 | (int)vertexArrays.size(), |
| 1263 | &vertexArrays[0], |
| 1264 | (tessellationPresent) ? |
| 1265 | (pr::Patches(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])) : |
| 1266 | (pr::Triangles(DE_LENGTH_OF_ARRAY(s_indices), &s_indices[0])), |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1267 | (m_spec.expectResult == EXPECT_VALIDATION_FAIL) ? |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1268 | (&beforeDrawValidator) : |
| 1269 | (DE_NULL)); |
| 1270 | |
| 1271 | postDrawError = gl.getError(); |
| 1272 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1273 | if (m_spec.expectResult == EXPECT_PASS) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1274 | { |
| 1275 | // Read back results. |
| 1276 | Surface surface (width, height); |
| 1277 | const float w = s_positions[3]; |
Jarkko Pöyry | c8e526b | 2015-05-19 19:31:53 -0700 | [diff] [blame] | 1278 | const int minY = deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * (float)height + 1.0f); |
| 1279 | const int maxY = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)height - 0.5f); |
| 1280 | const int minX = deCeilFloatToInt32 (((-quadSize / w) * 0.5f + 0.5f) * (float)width + 1.0f); |
| 1281 | const int maxX = deFloorFloatToInt32(((+quadSize / w) * 0.5f + 0.5f) * (float)width - 0.5f); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1282 | |
| 1283 | GLU_EXPECT_NO_ERROR(postDrawError, "draw"); |
| 1284 | |
| 1285 | glu::readPixels(m_renderCtx, viewportX, viewportY, surface.getAccess()); |
| 1286 | GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels"); |
| 1287 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1288 | if (!checkPixels(log, tcu::getSubregion(surface.getAccess(), minX, minY, maxX-minX+1, maxY-minY+1))) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1289 | { |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1290 | log << TestLog::Message << "INCORRECT RESULT for sub-case " << arrayNdx+1 << " of " << numRenderPasses << "):" |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1291 | << TestLog::EndMessage; |
| 1292 | |
| 1293 | log << TestLog::Message << "Failing shader input/output values:" << TestLog::EndMessage; |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1294 | dumpValues(log, valueBlock, arrayNdx); |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1295 | |
| 1296 | // Dump image on failure. |
| 1297 | log << TestLog::Image("Result", "Rendered result image", surface); |
| 1298 | |
| 1299 | gl.useProgram(0); |
| 1300 | m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); |
| 1301 | return false; |
| 1302 | } |
| 1303 | } |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1304 | else if (m_spec.expectResult == EXPECT_VALIDATION_FAIL) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1305 | { |
| 1306 | log << TestLog::Message |
| 1307 | << "Draw call generated error: " |
| 1308 | << glu::getErrorStr(postDrawError) << " " |
| 1309 | << ((postDrawError == GL_INVALID_OPERATION) ? ("(expected)") : ("(unexpected)")) << "\n" |
| 1310 | << "Validate status: " |
| 1311 | << glu::getBooleanStr(beforeDrawValidator.getValidateStatus()) << " " |
| 1312 | << ((beforeDrawValidator.getValidateStatus() == GL_FALSE) ? ("(expected)") : ("(unexpected)")) << "\n" |
| 1313 | << "Info log: " |
| 1314 | << ((beforeDrawValidator.getInfoLog().empty()) ? ("[empty string]") : (beforeDrawValidator.getInfoLog())) << "\n" |
| 1315 | << TestLog::EndMessage; |
| 1316 | |
| 1317 | // test result |
| 1318 | |
| 1319 | if (postDrawError != GL_NO_ERROR && postDrawError != GL_INVALID_OPERATION) |
| 1320 | { |
| 1321 | m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, ("Draw: got unexpected error: " + de::toString(glu::getErrorStr(postDrawError))).c_str()); |
| 1322 | return false; |
| 1323 | } |
| 1324 | |
| 1325 | if (beforeDrawValidator.getValidateStatus() == GL_TRUE) |
| 1326 | { |
| 1327 | if (postDrawError == GL_NO_ERROR) |
| 1328 | m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation and rendering succeeded"); |
| 1329 | else if (postDrawError == GL_INVALID_OPERATION) |
| 1330 | m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but validation succeeded (rendering failed as expected)"); |
| 1331 | else |
| 1332 | DE_ASSERT(false); |
| 1333 | return false; |
| 1334 | } |
| 1335 | else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_NO_ERROR) |
| 1336 | { |
| 1337 | m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "expected validation and rendering to fail but rendering succeeded (validation failed as expected)"); |
| 1338 | return false; |
| 1339 | } |
| 1340 | else if (beforeDrawValidator.getValidateStatus() == GL_FALSE && postDrawError == GL_INVALID_OPERATION) |
| 1341 | { |
| 1342 | // Validation does not depend on input values, no need to test all values |
| 1343 | return true; |
| 1344 | } |
| 1345 | else |
| 1346 | DE_ASSERT(false); |
| 1347 | } |
| 1348 | else |
| 1349 | DE_ASSERT(false); |
| 1350 | } |
| 1351 | } |
| 1352 | |
| 1353 | gl.useProgram(0); |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1354 | if (separablePrograms) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1355 | gl.bindProgramPipeline(0); |
| 1356 | |
| 1357 | GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderCase::execute(): end"); |
| 1358 | return true; |
| 1359 | } |
| 1360 | |
Pyry Haulos | fb19324 | 2015-09-01 13:56:41 -0700 | [diff] [blame] | 1361 | TestCase::IterateResult ShaderLibraryCase::iterate (void) |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1362 | { |
| 1363 | // Initialize state to pass. |
| 1364 | m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| 1365 | |
| 1366 | bool executeOk = execute(); |
| 1367 | |
| 1368 | DE_ASSERT(executeOk ? m_testCtx.getTestResult() == QP_TEST_RESULT_PASS : m_testCtx.getTestResult() != QP_TEST_RESULT_PASS); |
| 1369 | DE_UNREF(executeOk); |
| 1370 | return TestCase::STOP; |
| 1371 | } |
| 1372 | |
Jarkko Poyry | 3c82736 | 2014-09-02 11:48:52 +0300 | [diff] [blame] | 1373 | } // gls |
| 1374 | } // deqp |