/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.0 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Shader compilation performance tests. *//*--------------------------------------------------------------------*/ #include "es3pShaderCompilationCases.hpp" #include "tcuTestLog.hpp" #include "tcuVector.hpp" #include "tcuMatrix.hpp" #include "tcuTextureUtil.hpp" #include "tcuPlatform.hpp" #include "tcuCommandLine.hpp" #include "tcuRenderTarget.hpp" #include "tcuCPUWarmup.hpp" #include "tcuStringTemplate.hpp" #include "gluTexture.hpp" #include "gluPixelTransfer.hpp" #include "gluRenderContext.hpp" #include "deStringUtil.hpp" #include "deRandom.hpp" #include "deClock.h" #include "deMath.h" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include <map> #include <algorithm> #include <limits> #include <iomanip> using tcu::TestLog; using tcu::Vec3; using tcu::Vec4; using tcu::Mat3; using tcu::Mat4; using std::string; using std::vector; using namespace glw; // GL types namespace deqp { namespace gles3 { namespace Performance { static const bool WARMUP_CPU_AT_BEGINNING_OF_CASE = false; static const bool WARMUP_CPU_BEFORE_EACH_MEASUREMENT = true; static const int MAX_VIEWPORT_WIDTH = 64; static const int MAX_VIEWPORT_HEIGHT = 64; static const int DEFAULT_MINIMUM_MEASUREMENT_COUNT = 15; static const float RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD = 0.05f; // Texture size for the light shader and texture lookup shader cases. static const int TEXTURE_WIDTH = 64; static const int TEXTURE_HEIGHT = 64; template <typename T> inline string toStringWithPadding (T value, int minLength) { std::ostringstream s; s << std::setfill('0') << std::setw(minLength) << value; return s.str(); } // Add some whitespace and comments to str. They should depend on uniqueNumber. static string strWithWhiteSpaceAndComments (const string& str, deUint32 uniqueNumber) { string res(""); // Find the first newline. int firstLineEndNdx = 0; while (firstLineEndNdx < (int)str.size() && str[firstLineEndNdx] != '\n') { res += str[firstLineEndNdx]; firstLineEndNdx++; } res += '\n'; DE_ASSERT(firstLineEndNdx < (int)str.size()); // Add the whitespaces and comments just after the first line. de::Random rnd (uniqueNumber); int numWS = rnd.getInt(10, 20); for (int i = 0; i < numWS; i++) res += " \t\n"[rnd.getInt(0, 2)]; res += "/* unique comment " + de::toString(uniqueNumber) + " */\n"; res += "// unique comment " + de::toString(uniqueNumber) + "\n"; for (int i = 0; i < numWS; i++) res += " \t\n"[rnd.getInt(0, 2)]; // Add the rest of the string. res.append(&str.c_str()[firstLineEndNdx + 1]); return res; } //! Helper for computing relative magnitudes while avoiding division by zero. static float hackySafeRelativeResult (float x, float y) { // \note A possible case is that x is standard deviation, and y is average // (or similarly for median or some such). So, if y is 0, that // probably means that x is also 0(ish) (because in practice we're // dealing with non-negative values, in which case an average of 0 // implies that the samples are all 0 - note that the same isn't // strictly true for things like median) so a relative result of 0 // wouldn't be that far from the truth. return y == 0.0f ? 0.0f : x/y; } template <typename T> static float vectorFloatAverage (const vector<T>& v) { DE_ASSERT(!v.empty()); float result = 0.0f; for (int i = 0; i < (int)v.size(); i++) result += (float)v[i]; return result / (float)v.size(); } template <typename T> static float vectorFloatMedian (const vector<T>& v) { DE_ASSERT(!v.empty()); vector<T> temp = v; std::sort(temp.begin(), temp.end()); return temp.size() % 2 == 0 ? 0.5f * ((float)temp[temp.size()/2-1] + (float)temp[temp.size()/2]) : (float)temp[temp.size()/2]; } template <typename T> static float vectorFloatMinimum (const vector<T>& v) { DE_ASSERT(!v.empty()); return (float)*std::min_element(v.begin(), v.end()); } template <typename T> static float vectorFloatMaximum (const vector<T>& v) { DE_ASSERT(!v.empty()); return (float)*std::max_element(v.begin(), v.end()); } template <typename T> static float vectorFloatStandardDeviation (const vector<T>& v) { float average = vectorFloatAverage(v); float result = 0.0f; for (int i = 0; i < (int)v.size(); i++) { float d = (float)v[i] - average; result += d*d; } return deFloatSqrt(result/(float)v.size()); } template <typename T> static float vectorFloatRelativeStandardDeviation (const vector<T>& v) { return hackySafeRelativeResult(vectorFloatStandardDeviation(v), vectorFloatAverage(v)); } template <typename T> static float vectorFloatMedianAbsoluteDeviation (const vector<T>& v) { float median = vectorFloatMedian(v); vector<float> absoluteDeviations (v.size()); for (int i = 0; i < (int)v.size(); i++) absoluteDeviations[i] = deFloatAbs((float)v[i] - median); return vectorFloatMedian(absoluteDeviations); } template <typename T> static float vectorFloatRelativeMedianAbsoluteDeviation (const vector<T>& v) { return hackySafeRelativeResult(vectorFloatMedianAbsoluteDeviation(v), vectorFloatMedian(v)); } template <typename T> static float vectorFloatMaximumMinusMinimum (const vector<T>& v) { return vectorFloatMaximum(v) - vectorFloatMinimum(v); } template <typename T> static float vectorFloatRelativeMaximumMinusMinimum (const vector<T>& v) { return hackySafeRelativeResult(vectorFloatMaximumMinusMinimum(v), vectorFloatMaximum(v)); } template <typename T> static vector<T> vectorLowestPercentage (const vector<T>& v, float factor) { DE_ASSERT(0.0f < factor && factor <= 1.0f); int targetSize = (int)(deFloatCeil(factor*(float)v.size())); vector<T> temp = v; std::sort(temp.begin(), temp.end()); while ((int)temp.size() > targetSize) temp.pop_back(); return temp; } template <typename T> static float vectorFloatFirstQuartile (const vector<T>& v) { return vectorFloatMedian(vectorLowestPercentage(v, 0.5f)); } // Helper function for combining 4 tcu::Vec4's into one tcu::Vector<float, 16>. static tcu::Vector<float, 16> combineVec4ToVec16 (const Vec4& a0, const Vec4& a1, const Vec4& a2, const Vec4& a3) { tcu::Vector<float, 16> result; for (int vecNdx = 0; vecNdx < 4; vecNdx++) { const Vec4& srcVec = vecNdx == 0 ? a0 : vecNdx == 1 ? a1 : vecNdx == 2 ? a2 : a3; for (int i = 0; i < 4; i++) result[vecNdx*4 + i] = srcVec[i]; } return result; } // Helper function for extending an n-sized (n <= 16) vector to a 16-sized vector (padded with zeros). template <int Size> static tcu::Vector<float, 16> vecTo16 (const tcu::Vector<float, Size>& vec) { DE_STATIC_ASSERT(Size <= 16); tcu::Vector<float, 16> res(0.0f); for (int i = 0; i < Size; i++) res[i] = vec[i]; return res; } // Helper function for extending an n-sized (n <= 16) array to a 16-sized vector (padded with zeros). template <int Size> static tcu::Vector<float, 16> arrTo16 (const tcu::Array<float, Size>& arr) { DE_STATIC_ASSERT(Size <= 16); tcu::Vector<float, 16> res(0.0f); for(int i = 0; i < Size; i++) res[i] = arr[i]; return res; } static string getShaderInfoLog (const glw::Functions& gl, deUint32 shader) { string result; int infoLogLen; vector<char> infoLogBuf; gl.getShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLen); infoLogBuf.resize(infoLogLen + 1); gl.getShaderInfoLog(shader, infoLogLen + 1, DE_NULL, &infoLogBuf[0]); result = &infoLogBuf[0]; return result; } static string getProgramInfoLog (const glw::Functions& gl, deUint32 program) { string result; int infoLogLen; vector<char> infoLogBuf; gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLen); infoLogBuf.resize(infoLogLen + 1); gl.getProgramInfoLog(program, infoLogLen + 1, DE_NULL, &infoLogBuf[0]); result = &infoLogBuf[0]; return result; } enum LightType { LIGHT_DIRECTIONAL = 0, LIGHT_POINT, LIGHT_LAST, }; enum LoopType { LOOP_TYPE_STATIC = 0, LOOP_TYPE_UNIFORM, LOOP_TYPE_DYNAMIC, LOOP_LAST }; // For texture lookup cases: which texture lookups are inside a conditional statement. enum ConditionalUsage { CONDITIONAL_USAGE_NONE = 0, // No conditional statements. CONDITIONAL_USAGE_FIRST_HALF, // First numLookUps/2 lookups are inside a conditional statement. CONDITIONAL_USAGE_EVERY_OTHER, // First, third etc. lookups are inside conditional statements. CONDITIONAL_USAGE_LAST }; enum ConditionalType { CONDITIONAL_TYPE_STATIC = 0, CONDITIONAL_TYPE_UNIFORM, CONDITIONAL_TYPE_DYNAMIC, CONDITIONAL_TYPE_LAST }; // For the invalid shader compilation tests; what kind of invalidity a shader shall contain. enum ShaderValidity { SHADER_VALIDITY_VALID = 0, SHADER_VALIDITY_INVALID_CHAR, SHADER_VALIDITY_SEMANTIC_ERROR, SHADER_VALIDITY_LAST }; class ShaderCompilerCase : public TestCase { public: struct AttribSpec { string name; tcu::Vector<float, 16> value; AttribSpec (const string& n, const tcu::Vector<float, 16>& v) : name(n), value(v) {} }; struct UniformSpec { enum Type { TYPE_FLOAT = 0, TYPE_VEC2, TYPE_VEC3, TYPE_VEC4, TYPE_MAT3, TYPE_MAT4, TYPE_TEXTURE_UNIT, TYPE_LAST }; string name; Type type; tcu::Vector<float, 16> value; UniformSpec (const string& n, Type t, float v) : name(n), type(t), value(v) {} UniformSpec (const string& n, Type t, const tcu::Vector<float, 16>& v) : name(n), type(t), value(v) {} }; ShaderCompilerCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments); ~ShaderCompilerCase (void); void init (void); IterateResult iterate (void); protected: struct ProgramContext { string vertShaderSource; string fragShaderSource; vector<AttribSpec> vertexAttributes; vector<UniformSpec> uniforms; }; deUint32 getSpecializationID (int measurementNdx) const; // Return an ID that depends on the case ID, current measurement index and time; used to specialize attribute names etc. (avoid shader caching). virtual ProgramContext generateShaderData (int measurementNdx) const = 0; // Generate shader sources and inputs. Attribute etc. names depend on above name specialization. private: struct Measurement { // \note All times in microseconds. 32-bit integers would probably suffice (would need over an hour of test case runtime to overflow), but better safe than sorry. deInt64 sourceSetTime; deInt64 vertexCompileTime; deInt64 fragmentCompileTime; deInt64 programLinkTime; deInt64 firstInputSetTime; deInt64 firstDrawTime; deInt64 secondInputSetTime; deInt64 secondDrawTime; deInt64 firstPhase (void) const { return sourceSetTime + vertexCompileTime + fragmentCompileTime + programLinkTime + firstInputSetTime + firstDrawTime; } deInt64 secondPhase (void) const { return secondInputSetTime + secondDrawTime; } deInt64 totalTimeWithoutDraw (void) const { return firstPhase() - de::min(secondPhase(), firstInputSetTime + firstDrawTime); } Measurement (deInt64 sourceSetTime_, deInt64 vertexCompileTime_, deInt64 fragmentCompileTime_, deInt64 programLinkTime_, deInt64 firstInputSetTime_, deInt64 firstDrawTime_, deInt64 secondInputSetTime_, deInt64 secondDrawTime_) : sourceSetTime (sourceSetTime_) , vertexCompileTime (vertexCompileTime_) , fragmentCompileTime (fragmentCompileTime_) , programLinkTime (programLinkTime_) , firstInputSetTime (firstInputSetTime_) , firstDrawTime (firstDrawTime_) , secondInputSetTime (secondInputSetTime_) , secondDrawTime (secondDrawTime_) { } }; struct ShadersAndProgram { deUint32 vertShader; deUint32 fragShader; deUint32 program; }; struct Logs { string vert; string frag; string link; }; struct BuildInfo { bool vertCompileSuccess; bool fragCompileSuccess; bool linkSuccess; Logs logs; }; ShadersAndProgram createShadersAndProgram (void) const; void setShaderSources (deUint32 vertShader, deUint32 fragShader, const ProgramContext&) const; bool compileShader (deUint32 shader) const; bool linkAndUseProgram (deUint32 program) const; void setShaderInputs (deUint32 program, const ProgramContext&) const; // Set attribute pointers and uniforms. void draw (void) const; // Clear, draw and finish. void cleanup (const ShadersAndProgram&, const ProgramContext&, bool linkSuccess) const; // Do GL deinitializations. Logs getLogs (const ShadersAndProgram&) const; void logProgramData (const BuildInfo&, const ProgramContext&) const; bool goodEnoughMeasurements (const vector<Measurement>& measurements) const; int m_viewportWidth; int m_viewportHeight; bool m_avoidCache; // If true, avoid caching between measurements as well (and not only between test cases). bool m_addWhitespaceAndComments; // If true, add random whitespace and comments to the source (good caching should ignore those). deUint32 m_startHash; // A hash from case id and time, at the time of construction. int m_minimumMeasurementCount; int m_maximumMeasurementCount; }; class ShaderCompilerLightCase : public ShaderCompilerCase { public: ShaderCompilerLightCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, int numLights, LightType lightType); ~ShaderCompilerLightCase (void); void init (void); void deinit (void); protected: ProgramContext generateShaderData (int measurementNdx) const; private: int m_numLights; bool m_isVertexCase; LightType m_lightType; glu::Texture2D* m_texture; }; class ShaderCompilerTextureCase : public ShaderCompilerCase { public: ShaderCompilerTextureCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType); ~ShaderCompilerTextureCase (void); void init (void); void deinit (void); protected: ProgramContext generateShaderData (int measurementNdx) const; private: int m_numLookups; vector<glu::Texture2D*> m_textures; ConditionalUsage m_conditionalUsage; ConditionalType m_conditionalType; }; class ShaderCompilerLoopCase : public ShaderCompilerCase { public: ShaderCompilerLoopCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, LoopType type, int numLoopIterations, int nestingDepth); ~ShaderCompilerLoopCase (void); protected: ProgramContext generateShaderData (int measurementNdx) const; private: int m_numLoopIterations; int m_nestingDepth; bool m_isVertexCase; LoopType m_type; }; class ShaderCompilerOperCase : public ShaderCompilerCase { public: ShaderCompilerOperCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, const char* oper, int numOperations); ~ShaderCompilerOperCase (void); protected: ProgramContext generateShaderData (int measurementNdx) const; private: string m_oper; int m_numOperations; bool m_isVertexCase; }; class ShaderCompilerMandelbrotCase : public ShaderCompilerCase { public: ShaderCompilerMandelbrotCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, int numFractalIterations); ~ShaderCompilerMandelbrotCase (void); protected: ProgramContext generateShaderData (int measurementNdx) const; private: int m_numFractalIterations; }; class InvalidShaderCompilerCase : public TestCase { public: // \note Similar to the ShaderValidity enum, but doesn't have a VALID type. enum InvalidityType { INVALIDITY_INVALID_CHAR = 0, INVALIDITY_SEMANTIC_ERROR, INVALIDITY_LAST }; InvalidShaderCompilerCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType); ~InvalidShaderCompilerCase (void); IterateResult iterate (void); protected: struct ProgramContext { string vertShaderSource; string fragShaderSource; }; deUint32 getSpecializationID (int measurementNdx) const; // Return an ID that depends on the case ID, current measurement index and time; used to specialize attribute names etc. (avoid shader caching). virtual ProgramContext generateShaderSources (int measurementNdx) const = 0; // Generate shader sources. Attribute etc. names depend on above name specialization. InvalidityType m_invalidityType; private: struct Measurement { // \note All times in microseconds. 32-bit integers would probably suffice (would need over an hour of test case runtime to overflow), but better safe than sorry. deInt64 sourceSetTime; deInt64 vertexCompileTime; deInt64 fragmentCompileTime; deInt64 totalTime (void) const { return sourceSetTime + vertexCompileTime + fragmentCompileTime; } Measurement (deInt64 sourceSetTime_, deInt64 vertexCompileTime_, deInt64 fragmentCompileTime_) : sourceSetTime (sourceSetTime_) , vertexCompileTime (vertexCompileTime_) , fragmentCompileTime (fragmentCompileTime_) { } }; struct Shaders { deUint32 vertShader; deUint32 fragShader; }; struct Logs { string vert; string frag; }; struct BuildInfo { bool vertCompileSuccess; bool fragCompileSuccess; Logs logs; }; Shaders createShaders (void) const; void setShaderSources (const Shaders&, const ProgramContext&) const; bool compileShader (deUint32 shader) const; void cleanup (const Shaders&) const; Logs getLogs (const Shaders&) const; void logProgramData (const BuildInfo&, const ProgramContext&) const; bool goodEnoughMeasurements (const vector<Measurement>& measurements) const; deUint32 m_startHash; // A hash from case id and time, at the time of construction. int m_minimumMeasurementCount; int m_maximumMeasurementCount; }; class InvalidShaderCompilerLightCase : public InvalidShaderCompilerCase { public: InvalidShaderCompilerLightCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, int numLights, LightType lightType); ~InvalidShaderCompilerLightCase (void); protected: ProgramContext generateShaderSources (int measurementNdx) const; private: bool m_isVertexCase; int m_numLights; LightType m_lightType; }; class InvalidShaderCompilerTextureCase : public InvalidShaderCompilerCase { public: InvalidShaderCompilerTextureCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType); ~InvalidShaderCompilerTextureCase (void); protected: ProgramContext generateShaderSources (int measurementNdx) const; private: int m_numLookups; ConditionalUsage m_conditionalUsage; ConditionalType m_conditionalType; }; class InvalidShaderCompilerLoopCase : public InvalidShaderCompilerCase { public: InvalidShaderCompilerLoopCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool , LoopType type, int numLoopIterations, int nestingDepth); ~InvalidShaderCompilerLoopCase (void); protected: ProgramContext generateShaderSources (int measurementNdx) const; private: bool m_isVertexCase; int m_numLoopIterations; int m_nestingDepth; LoopType m_type; }; class InvalidShaderCompilerOperCase : public InvalidShaderCompilerCase { public: InvalidShaderCompilerOperCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, const char* oper, int numOperations); ~InvalidShaderCompilerOperCase (void); protected: ProgramContext generateShaderSources (int measurementNdx) const; private: bool m_isVertexCase; string m_oper; int m_numOperations; }; class InvalidShaderCompilerMandelbrotCase : public InvalidShaderCompilerCase { public: InvalidShaderCompilerMandelbrotCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, int numFractalIterations); ~InvalidShaderCompilerMandelbrotCase (void); protected: ProgramContext generateShaderSources (int measurementNdx) const; private: int m_numFractalIterations; }; static string getNameSpecialization (deUint32 id) { return "_" + toStringWithPadding(id, 10); } // Substitute StringTemplate parameters for attribute/uniform/varying name and constant expression specialization as well as possible shader compilation error causes. static string specializeShaderSource (const string& shaderSourceTemplate, deUint32 cacheAvoidanceID, ShaderValidity validity) { std::map<string, string> params; params["NAME_SPEC"] = getNameSpecialization(cacheAvoidanceID); params["FLOAT01"] = de::floatToString((float)cacheAvoidanceID / (float)(std::numeric_limits<deUint32>::max()), 6); params["SEMANTIC_ERROR"] = validity != SHADER_VALIDITY_SEMANTIC_ERROR ? "" : "\tfloat invalid = sin(1.0, 2.0);\n"; params["INVALID_CHAR"] = validity != SHADER_VALIDITY_INVALID_CHAR ? "" : "@\n"; // \note Some implementations crash when the invalid character is the last character in the source, so use newline. return tcu::StringTemplate(shaderSourceTemplate).specialize(params); } // Function for generating the vertex shader of a (directional or point) light case. static string lightVertexTemplate (int numLights, bool isVertexCase, LightType lightType) { string resultTemplate; resultTemplate += "#version 300 es\n" "in highp vec4 a_position${NAME_SPEC};\n" "in mediump vec3 a_normal${NAME_SPEC};\n" "in mediump vec4 a_texCoord0${NAME_SPEC};\n" "uniform mediump vec3 u_material_ambientColor${NAME_SPEC};\n" "uniform mediump vec4 u_material_diffuseColor${NAME_SPEC};\n" "uniform mediump vec3 u_material_emissiveColor${NAME_SPEC};\n" "uniform mediump vec3 u_material_specularColor${NAME_SPEC};\n" "uniform mediump float u_material_shininess${NAME_SPEC};\n"; for (int lightNdx = 0; lightNdx < numLights; lightNdx++) { string ndxStr = de::toString(lightNdx); resultTemplate += "uniform mediump vec3 u_light" + ndxStr + "_color${NAME_SPEC};\n" "uniform mediump vec3 u_light" + ndxStr + "_direction${NAME_SPEC};\n"; if (lightType == LIGHT_POINT) resultTemplate += "uniform mediump vec4 u_light" + ndxStr + "_position${NAME_SPEC};\n" "uniform mediump float u_light" + ndxStr + "_constantAttenuation${NAME_SPEC};\n" "uniform mediump float u_light" + ndxStr + "_linearAttenuation${NAME_SPEC};\n" "uniform mediump float u_light" + ndxStr + "_quadraticAttenuation${NAME_SPEC};\n"; } resultTemplate += "uniform highp mat4 u_mvpMatrix${NAME_SPEC};\n" "uniform highp mat4 u_modelViewMatrix${NAME_SPEC};\n" "uniform mediump mat3 u_normalMatrix${NAME_SPEC};\n" "uniform mediump mat4 u_texCoordMatrix0${NAME_SPEC};\n" "out mediump vec4 v_color${NAME_SPEC};\n" "out mediump vec2 v_texCoord0${NAME_SPEC};\n"; if (!isVertexCase) { resultTemplate += "out mediump vec3 v_eyeNormal${NAME_SPEC};\n"; if (lightType == LIGHT_POINT) resultTemplate += "out mediump vec3 v_directionToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n" "out mediump float v_distanceToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n"; } resultTemplate += "mediump vec3 direction (mediump vec4 from, mediump vec4 to)\n" "{\n" " return vec3(to.xyz * from.w - from.xyz * to.w);\n" "}\n" "\n" "mediump vec3 computeLighting (\n" " mediump vec3 directionToLight,\n" " mediump vec3 halfVector,\n" " mediump vec3 normal,\n" " mediump vec3 lightColor,\n" " mediump vec3 diffuseColor,\n" " mediump vec3 specularColor,\n" " mediump float shininess)\n" "{\n" " mediump float normalDotDirection = max(dot(normal, directionToLight), 0.0);\n" " mediump vec3 color = normalDotDirection * diffuseColor * lightColor;\n" "\n" " if (normalDotDirection != 0.0)\n" " color += pow(max(dot(normal, halfVector), 0.0), shininess) * specularColor * lightColor;\n" "\n" " return color;\n" "}\n" "\n"; if (lightType == LIGHT_POINT) resultTemplate += "mediump float computeDistanceAttenuation (mediump float distToLight, mediump float constAtt, mediump float linearAtt, mediump float quadraticAtt)\n" "{\n" " return 1.0 / (constAtt + linearAtt * distToLight + quadraticAtt * distToLight * distToLight);\n" "}\n" "\n"; resultTemplate += "void main (void)\n" "{\n" " highp vec4 position = a_position${NAME_SPEC};\n" " highp vec3 normal = a_normal${NAME_SPEC};\n" " gl_Position = u_mvpMatrix${NAME_SPEC} * position * (0.95 + 0.05*${FLOAT01});\n" " v_texCoord0${NAME_SPEC} = (u_texCoordMatrix0${NAME_SPEC} * a_texCoord0${NAME_SPEC}).xy;\n" " mediump vec4 color = vec4(u_material_emissiveColor${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.a);\n" "\n" " highp vec4 eyePosition = u_modelViewMatrix${NAME_SPEC} * position;\n" " mediump vec3 eyeNormal = normalize(u_normalMatrix${NAME_SPEC} * normal);\n"; if (!isVertexCase) resultTemplate += "\tv_eyeNormal${NAME_SPEC} = eyeNormal;\n"; resultTemplate += "\n"; for (int lightNdx = 0; lightNdx < numLights; lightNdx++) { string ndxStr = de::toString(lightNdx); resultTemplate += " /* Light " + ndxStr + " */\n"; if (lightType == LIGHT_POINT) { resultTemplate += " mediump float distanceToLight" + ndxStr + " = distance(eyePosition, u_light" + ndxStr + "_position${NAME_SPEC});\n" " mediump vec3 directionToLight" + ndxStr + " = normalize(direction(eyePosition, u_light" + ndxStr + "_position${NAME_SPEC}));\n"; if (isVertexCase) resultTemplate += " mediump vec3 halfVector" + ndxStr + " = normalize(directionToLight" + ndxStr + " + vec3(0.0, 0.0, 1.0));\n" " color.rgb += computeLighting(directionToLight" + ndxStr + ", halfVector" + ndxStr + ", eyeNormal, u_light" + ndxStr + "_color${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.rgb, " "u_material_specularColor${NAME_SPEC}, u_material_shininess${NAME_SPEC}) * computeDistanceAttenuation(distanceToLight" + ndxStr + ", u_light" + ndxStr + "_constantAttenuation${NAME_SPEC}, " "u_light" + ndxStr + "_linearAttenuation${NAME_SPEC}, u_light" + ndxStr + "_quadraticAttenuation${NAME_SPEC});\n"; else resultTemplate += " v_directionToLight${NAME_SPEC}[" + ndxStr + "] = directionToLight" + ndxStr + ";\n" " v_distanceToLight${NAME_SPEC}[" + ndxStr + "] = distanceToLight" + ndxStr + ";\n"; } else if (lightType == LIGHT_DIRECTIONAL) { if (isVertexCase) resultTemplate += " mediump vec3 directionToLight" + ndxStr + " = -u_light" + ndxStr + "_direction${NAME_SPEC};\n" " mediump vec3 halfVector" + ndxStr + " = normalize(directionToLight" + ndxStr + " + vec3(0.0, 0.0, 1.0));\n" " color.rgb += computeLighting(directionToLight" + ndxStr + ", halfVector" + ndxStr + ", eyeNormal, u_light" + ndxStr + "_color${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.rgb, u_material_specularColor${NAME_SPEC}, u_material_shininess${NAME_SPEC});\n"; } else DE_ASSERT(DE_FALSE); resultTemplate += "\n"; } resultTemplate += " v_color${NAME_SPEC} = color;\n" "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating the fragment shader of a (directional or point) light case. static string lightFragmentTemplate (int numLights, bool isVertexCase, LightType lightType) { string resultTemplate; resultTemplate += "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n"; if (!isVertexCase) { resultTemplate += "uniform mediump vec3 u_material_ambientColor${NAME_SPEC};\n" "uniform mediump vec4 u_material_diffuseColor${NAME_SPEC};\n" "uniform mediump vec3 u_material_emissiveColor${NAME_SPEC};\n" "uniform mediump vec3 u_material_specularColor${NAME_SPEC};\n" "uniform mediump float u_material_shininess${NAME_SPEC};\n"; for (int lightNdx = 0; lightNdx < numLights; lightNdx++) { string ndxStr = de::toString(lightNdx); resultTemplate += "uniform mediump vec3 u_light" + ndxStr + "_color${NAME_SPEC};\n" "uniform mediump vec3 u_light" + ndxStr + "_direction${NAME_SPEC};\n"; if (lightType == LIGHT_POINT) resultTemplate += "uniform mediump vec4 u_light" + ndxStr + "_position${NAME_SPEC};\n" "uniform mediump float u_light" + ndxStr + "_constantAttenuation${NAME_SPEC};\n" "uniform mediump float u_light" + ndxStr + "_linearAttenuation${NAME_SPEC};\n" "uniform mediump float u_light" + ndxStr + "_quadraticAttenuation${NAME_SPEC};\n"; } } resultTemplate += "uniform sampler2D u_sampler0${NAME_SPEC};\n" "in mediump vec4 v_color${NAME_SPEC};\n" "in mediump vec2 v_texCoord0${NAME_SPEC};\n"; if (!isVertexCase) { resultTemplate += "in mediump vec3 v_eyeNormal${NAME_SPEC};\n"; if (lightType == LIGHT_POINT) resultTemplate += "in mediump vec3 v_directionToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n" "in mediump float v_distanceToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n"; resultTemplate += "mediump vec3 direction (mediump vec4 from, mediump vec4 to)\n" "{\n" " return vec3(to.xyz * from.w - from.xyz * to.w);\n" "}\n" "\n"; resultTemplate += "mediump vec3 computeLighting (\n" " mediump vec3 directionToLight,\n" " mediump vec3 halfVector,\n" " mediump vec3 normal,\n" " mediump vec3 lightColor,\n" " mediump vec3 diffuseColor,\n" " mediump vec3 specularColor,\n" " mediump float shininess)\n" "{\n" " mediump float normalDotDirection = max(dot(normal, directionToLight), 0.0);\n" " mediump vec3 color = normalDotDirection * diffuseColor * lightColor;\n" "\n" " if (normalDotDirection != 0.0)\n" " color += pow(max(dot(normal, halfVector), 0.0), shininess) * specularColor * lightColor;\n" "\n" " return color;\n" "}\n" "\n"; if (lightType == LIGHT_POINT) resultTemplate += "mediump float computeDistanceAttenuation (mediump float distToLight, mediump float constAtt, mediump float linearAtt, mediump float quadraticAtt)\n" "{\n" " return 1.0 / (constAtt + linearAtt * distToLight + quadraticAtt * distToLight * distToLight);\n" "}\n" "\n"; } resultTemplate += "void main (void)\n" "{\n" " mediump vec2 texCoord0 = v_texCoord0${NAME_SPEC}.xy;\n" " mediump vec4 color = v_color${NAME_SPEC};\n"; if (!isVertexCase) { resultTemplate += " mediump vec3 eyeNormal = normalize(v_eyeNormal${NAME_SPEC});\n" "\n"; for (int lightNdx = 0; lightNdx < numLights; lightNdx++) { string ndxStr = de::toString(lightNdx); resultTemplate += " /* Light " + ndxStr + " */\n"; if (lightType == LIGHT_POINT) resultTemplate += " mediump vec3 directionToLight" + ndxStr + " = normalize(v_directionToLight${NAME_SPEC}[" + ndxStr + "]);\n" " mediump float distanceToLight" + ndxStr + " = v_distanceToLight${NAME_SPEC}[" + ndxStr + "];\n" " mediump vec3 halfVector" + ndxStr + " = normalize(directionToLight" + ndxStr + " + vec3(0.0, 0.0, 1.0));\n" " color.rgb += computeLighting(directionToLight" + ndxStr + ", halfVector" + ndxStr + ", eyeNormal, u_light" + ndxStr + "_color${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.rgb, " "u_material_specularColor${NAME_SPEC}, u_material_shininess${NAME_SPEC}) * computeDistanceAttenuation(distanceToLight" + ndxStr + ", u_light" + ndxStr + "_constantAttenuation${NAME_SPEC}, " "u_light" + ndxStr + "_linearAttenuation${NAME_SPEC}, u_light" + ndxStr + "_quadraticAttenuation${NAME_SPEC});\n" "\n"; else if (lightType == LIGHT_DIRECTIONAL) resultTemplate += " mediump vec3 directionToLight" + ndxStr + " = -u_light" + ndxStr + "_direction${NAME_SPEC};\n" " mediump vec3 halfVector" + ndxStr + " = normalize(directionToLight" + ndxStr + " + vec3(0.0, 0.0, 1.0));\n" " color.rgb += computeLighting(directionToLight" + ndxStr + ", halfVector" + ndxStr + ", eyeNormal, u_light" + ndxStr + "_color${NAME_SPEC}, u_material_diffuseColor${NAME_SPEC}.rgb, u_material_specularColor${NAME_SPEC}, u_material_shininess${NAME_SPEC});\n" "\n"; else DE_ASSERT(DE_FALSE); } } resultTemplate += " color *= texture(u_sampler0${NAME_SPEC}, texCoord0);\n" " o_color = color + ${FLOAT01};\n" "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating the shader attributes of a (directional or point) light case. static vector<ShaderCompilerCase::AttribSpec> lightShaderAttributes (const string& nameSpecialization) { vector<ShaderCompilerCase::AttribSpec> result; result.push_back(ShaderCompilerCase::AttribSpec("a_position" + nameSpecialization, combineVec4ToVec16(Vec4(-1.0f, -1.0f, 0.0f, 1.0f), Vec4(-1.0f, 1.0f, 0.0f, 1.0f), Vec4( 1.0f, -1.0f, 0.0f, 1.0f), Vec4( 1.0f, 1.0f, 0.0f, 1.0f)))); result.push_back(ShaderCompilerCase::AttribSpec("a_normal" + nameSpecialization, combineVec4ToVec16(Vec4(0.0f, 0.0f, -1.0f, 0.0f), Vec4(0.0f, 0.0f, -1.0f, 0.0f), Vec4(0.0f, 0.0f, -1.0f, 0.0f), Vec4(0.0f, 0.0f, -1.0f, 0.0f)))); result.push_back(ShaderCompilerCase::AttribSpec("a_texCoord0" + nameSpecialization, combineVec4ToVec16(Vec4(0.0f, 0.0f, 0.0f, 0.0f), Vec4(1.0f, 0.0f, 0.0f, 0.0f), Vec4(0.0f, 1.0f, 0.0f, 0.0f), Vec4(1.0f, 1.0f, 0.0f, 0.0f)))); return result; } // Function for generating the shader uniforms of a (directional or point) light case. static vector<ShaderCompilerCase::UniformSpec> lightShaderUniforms (const string& nameSpecialization, int numLights, LightType lightType) { vector<ShaderCompilerCase::UniformSpec> result; result.push_back(ShaderCompilerCase::UniformSpec("u_material_ambientColor" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_VEC3, vecTo16(Vec3(0.5f, 0.7f, 0.9f)))); result.push_back(ShaderCompilerCase::UniformSpec("u_material_diffuseColor" + nameSpecialization, ShaderCompilerCase:: UniformSpec::TYPE_VEC4, vecTo16(Vec4(0.3f, 0.4f, 0.5f, 1.0f)))); result.push_back(ShaderCompilerCase::UniformSpec("u_material_emissiveColor" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_VEC3, vecTo16(Vec3(0.7f, 0.2f, 0.2f)))); result.push_back(ShaderCompilerCase::UniformSpec("u_material_specularColor" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_VEC3, vecTo16(Vec3(0.2f, 0.6f, 1.0f)))); result.push_back(ShaderCompilerCase::UniformSpec("u_material_shininess" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_FLOAT, 0.8f)); for (int lightNdx = 0; lightNdx < numLights; lightNdx++) { string ndxStr = de::toString(lightNdx); result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_color" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_VEC3, vecTo16(Vec3(0.8f, 0.6f, 0.3f)))); result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_direction" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_VEC3, vecTo16(Vec3(0.2f, 0.3f, 0.4f)))); if (lightType == LIGHT_POINT) { result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_position" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_VEC4, vecTo16(Vec4(1.0f, 0.6f, 0.3f, 0.2f)))); result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_constantAttenuation" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_FLOAT, 0.6f)); result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_linearAttenuation" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_FLOAT, 0.5f)); result.push_back(ShaderCompilerCase::UniformSpec("u_light" + ndxStr + "_quadraticAttenuation" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_FLOAT, 0.4f)); } } result.push_back(ShaderCompilerCase::UniformSpec("u_mvpMatrix" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_MAT4, arrTo16(Mat4(1.0f).getColumnMajorData()))); result.push_back(ShaderCompilerCase::UniformSpec("u_modelViewMatrix" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_MAT4, arrTo16(Mat4(1.0f).getColumnMajorData()))); result.push_back(ShaderCompilerCase::UniformSpec("u_normalMatrix" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_MAT3, arrTo16(Mat3(1.0f).getColumnMajorData()))); result.push_back(ShaderCompilerCase::UniformSpec("u_texCoordMatrix0" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_MAT4, arrTo16(Mat4(1.0f).getColumnMajorData()))); result.push_back(ShaderCompilerCase::UniformSpec("u_sampler0" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_TEXTURE_UNIT, 0.0f)); return result; } // Function for generating a vertex shader with a for loop. static string loopVertexTemplate (LoopType type, bool isVertexCase, int numLoopIterations, int nestingDepth) { string resultTemplate; string loopBound = type == LOOP_TYPE_STATIC ? de::toString(numLoopIterations) : type == LOOP_TYPE_UNIFORM ? "int(u_loopBound${NAME_SPEC})" : type == LOOP_TYPE_DYNAMIC ? "int(a_loopBound${NAME_SPEC})" : ""; DE_ASSERT(!loopBound.empty()); resultTemplate += "#version 300 es\n" "in highp vec4 a_position${NAME_SPEC};\n"; if (type == LOOP_TYPE_DYNAMIC) resultTemplate += "in mediump float a_loopBound${NAME_SPEC};\n"; resultTemplate += "in mediump vec4 a_value${NAME_SPEC};\n" "out mediump vec4 v_value${NAME_SPEC};\n"; if (isVertexCase) { if (type == LOOP_TYPE_UNIFORM) resultTemplate += "uniform mediump float u_loopBound${NAME_SPEC};\n"; resultTemplate += "\n" "void main()\n" "{\n" " gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n" " mediump vec4 value = a_value${NAME_SPEC};\n"; for (int i = 0; i < nestingDepth; i++) { string iterName = "i" + de::toString(i); resultTemplate += string(i + 1, '\t') + "for (int " + iterName + " = 0; " + iterName + " < " + loopBound + "; " + iterName + "++)\n"; } resultTemplate += string(nestingDepth + 1, '\t') + "value *= a_value${NAME_SPEC};\n"; resultTemplate += " v_value${NAME_SPEC} = value;\n"; } else { if (type == LOOP_TYPE_DYNAMIC) resultTemplate += "out mediump float v_loopBound${NAME_SPEC};\n"; resultTemplate += "\n" "void main()\n" "{\n" " gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n" " v_value${NAME_SPEC} = a_value${NAME_SPEC};\n"; if (type == LOOP_TYPE_DYNAMIC) resultTemplate += " v_loopBound${NAME_SPEC} = a_loopBound${NAME_SPEC};\n"; } resultTemplate += "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating a fragment shader with a for loop. static string loopFragmentTemplate (LoopType type, bool isVertexCase, int numLoopIterations, int nestingDepth) { string resultTemplate; string loopBound = type == LOOP_TYPE_STATIC ? de::toString(numLoopIterations) : type == LOOP_TYPE_UNIFORM ? "int(u_loopBound${NAME_SPEC})" : type == LOOP_TYPE_DYNAMIC ? "int(v_loopBound${NAME_SPEC})" : ""; DE_ASSERT(!loopBound.empty()); resultTemplate += "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n" "in mediump vec4 v_value${NAME_SPEC};\n"; if (!isVertexCase) { if (type == LOOP_TYPE_DYNAMIC) resultTemplate += "in mediump float v_loopBound${NAME_SPEC};\n"; else if (type == LOOP_TYPE_UNIFORM) resultTemplate += "uniform mediump float u_loopBound${NAME_SPEC};\n"; resultTemplate += "\n" "void main()\n" "{\n" " mediump vec4 value = v_value${NAME_SPEC};\n"; for (int i = 0; i < nestingDepth; i++) { string iterName = "i" + de::toString(i); resultTemplate += string(i + 1, '\t') + "for (int " + iterName + " = 0; " + iterName + " < " + loopBound + "; " + iterName + "++)\n"; } resultTemplate += string(nestingDepth + 1, '\t') + "value *= v_value${NAME_SPEC};\n"; resultTemplate += " o_color = value + ${FLOAT01};\n"; } else resultTemplate += "\n" "void main()\n" "{\n" " o_color = v_value${NAME_SPEC} + ${FLOAT01};\n"; resultTemplate += "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating the shader attributes for a loop case. static vector<ShaderCompilerCase::AttribSpec> loopShaderAttributes (const string& nameSpecialization, LoopType type, int numLoopIterations) { vector<ShaderCompilerCase::AttribSpec> result; result.push_back(ShaderCompilerCase::AttribSpec("a_position" + nameSpecialization, combineVec4ToVec16(Vec4(-1.0f, -1.0f, 0.0f, 1.0f), Vec4(-1.0f, 1.0f, 0.0f, 1.0f), Vec4( 1.0f, -1.0f, 0.0f, 1.0f), Vec4( 1.0f, 1.0f, 0.0f, 1.0f)))); result.push_back(ShaderCompilerCase::AttribSpec("a_value" + nameSpecialization, combineVec4ToVec16(Vec4( 1.0f, 1.0f, 1.0f, 1.0f), Vec4( 1.0f, 1.0f, 1.0f, 1.0f), Vec4( 1.0f, 1.0f, 1.0f, 1.0f), Vec4( 1.0f, 1.0f, 1.0f, 1.0f)))); if (type == LOOP_TYPE_DYNAMIC) result.push_back(ShaderCompilerCase::AttribSpec("a_loopBound" + nameSpecialization, combineVec4ToVec16(Vec4((float)numLoopIterations, 0.0f, 0.0f, 0.0f), Vec4((float)numLoopIterations, 0.0f, 0.0f, 0.0f), Vec4((float)numLoopIterations, 0.0f, 0.0f, 0.0f), Vec4((float)numLoopIterations, 0.0f, 0.0f, 0.0f)))); return result; } static vector<ShaderCompilerCase::UniformSpec> loopShaderUniforms (const string& nameSpecialization, LoopType type, int numLoopIterations) { vector<ShaderCompilerCase::UniformSpec> result; if (type == LOOP_TYPE_UNIFORM) result.push_back(ShaderCompilerCase::UniformSpec("u_loopBound" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_FLOAT, (float)numLoopIterations)); return result; } // Function for generating the shader attributes for a case with only one attribute value in addition to the position attribute. static vector<ShaderCompilerCase::AttribSpec> singleValueShaderAttributes (const string& nameSpecialization) { vector<ShaderCompilerCase::AttribSpec> result; result.push_back(ShaderCompilerCase::AttribSpec("a_position" + nameSpecialization, combineVec4ToVec16(Vec4(-1.0f, -1.0f, 0.0f, 1.0f), Vec4(-1.0f, 1.0f, 0.0f, 1.0f), Vec4( 1.0f, -1.0f, 0.0f, 1.0f), Vec4( 1.0f, 1.0f, 0.0f, 1.0f)))); result.push_back(ShaderCompilerCase::AttribSpec("a_value" + nameSpecialization, combineVec4ToVec16(Vec4( 1.0f, 1.0f, 1.0f, 1.0f), Vec4( 1.0f, 1.0f, 1.0f, 1.0f), Vec4( 1.0f, 1.0f, 1.0f, 1.0f), Vec4( 1.0f, 1.0f, 1.0f, 1.0f)))); return result; } // Function for generating a vertex shader with a binary operation chain. static string binaryOpVertexTemplate (int numOperations, const char* op) { string resultTemplate; resultTemplate += "#version 300 es\n" "in highp vec4 a_position${NAME_SPEC};\n" "in mediump vec4 a_value${NAME_SPEC};\n" "out mediump vec4 v_value${NAME_SPEC};\n" "\n" "void main()\n" "{\n" " gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n" " mediump vec4 value = "; for (int i = 0; i < numOperations; i++) resultTemplate += string(i > 0 ? op : "") + "a_value${NAME_SPEC}"; resultTemplate += ";\n" " v_value${NAME_SPEC} = value;\n" "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating a fragment shader with a binary operation chain. static string binaryOpFragmentTemplate (int numOperations, const char* op) { string resultTemplate; resultTemplate += "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n" "in mediump vec4 v_value${NAME_SPEC};\n" "\n" "void main()\n" "{\n" " mediump vec4 value = "; for (int i = 0; i < numOperations; i++) resultTemplate += string(i > 0 ? op : "") + "v_value${NAME_SPEC}"; resultTemplate += ";\n" " o_color = value + ${FLOAT01};\n" "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating a vertex that takes one attribute in addition to position and just passes it to the fragment shader as a varying. static string singleVaryingVertexTemplate (void) { const char* resultTemplate = "#version 300 es\n" "in highp vec4 a_position${NAME_SPEC};\n" "in mediump vec4 a_value${NAME_SPEC};\n" "out mediump vec4 v_value${NAME_SPEC};\n" "\n" "void main()\n" "{\n" " gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n" " v_value${NAME_SPEC} = a_value${NAME_SPEC};\n" "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating a fragment shader that takes a single varying and uses it as the color. static string singleVaryingFragmentTemplate (void) { const char* resultTemplate = "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n" "in mediump vec4 v_value${NAME_SPEC};\n" "\n" "void main()\n" "{\n" " o_color = v_value${NAME_SPEC} + ${FLOAT01};\n" "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating the vertex shader of a texture lookup case. static string textureLookupVertexTemplate (ConditionalUsage conditionalUsage, ConditionalType conditionalType) { string resultTemplate; bool conditionVaryingNeeded = conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_DYNAMIC; resultTemplate += "#version 300 es\n" "in highp vec4 a_position${NAME_SPEC};\n" "in mediump vec2 a_coords${NAME_SPEC};\n" "out mediump vec2 v_coords${NAME_SPEC};\n"; if (conditionVaryingNeeded) resultTemplate += "in mediump float a_condition${NAME_SPEC};\n" "out mediump float v_condition${NAME_SPEC};\n"; resultTemplate += "\n" "void main()\n" "{\n" " gl_Position = a_position${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n" " v_coords${NAME_SPEC} = a_coords${NAME_SPEC};\n"; if (conditionVaryingNeeded) resultTemplate += " v_condition${NAME_SPEC} = a_condition${NAME_SPEC};\n"; resultTemplate += "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating the fragment shader of a texture lookup case. static string textureLookupFragmentTemplate (int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType) { string resultTemplate; resultTemplate += "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n" "in mediump vec2 v_coords${NAME_SPEC};\n"; if (conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_DYNAMIC) resultTemplate += "in mediump float v_condition${NAME_SPEC};\n"; for (int i = 0; i < numLookups; i++) resultTemplate += "uniform sampler2D u_sampler" + de::toString(i) + "${NAME_SPEC};\n"; if (conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_UNIFORM) resultTemplate += "uniform mediump float u_condition${NAME_SPEC};\n"; resultTemplate += "\n" "void main()\n" "{\n" " mediump vec4 color = vec4(0.0);\n"; const char* conditionalTerm = conditionalType == CONDITIONAL_TYPE_STATIC ? "1.0 > 0.0" : conditionalType == CONDITIONAL_TYPE_UNIFORM ? "u_condition${NAME_SPEC} > 0.0" : conditionalType == CONDITIONAL_TYPE_DYNAMIC ? "v_condition${NAME_SPEC} > 0.0" : DE_NULL; DE_ASSERT(conditionalTerm != DE_NULL); if (conditionalUsage == CONDITIONAL_USAGE_FIRST_HALF) resultTemplate += string("") + " if (" + conditionalTerm + ")\n" " {\n"; for (int i = 0; i < numLookups; i++) { if (conditionalUsage == CONDITIONAL_USAGE_FIRST_HALF) { if (i < (numLookups + 1) / 2) resultTemplate += "\t"; } else if (conditionalUsage == CONDITIONAL_USAGE_EVERY_OTHER) { if (i % 2 == 0) resultTemplate += string("") + " if (" + conditionalTerm + ")\n" "\t"; } resultTemplate += " color += texture(u_sampler" + de::toString(i) + "${NAME_SPEC}, v_coords${NAME_SPEC});\n"; if (conditionalUsage == CONDITIONAL_USAGE_FIRST_HALF && i == (numLookups - 1) / 2) resultTemplate += "\t}\n"; } resultTemplate += " o_color = color/" + de::toString(numLookups) + ".0 + ${FLOAT01};\n" + "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } // Function for generating the shader attributes of a texture lookup case. static vector<ShaderCompilerCase::AttribSpec> textureLookupShaderAttributes (const string& nameSpecialization, ConditionalUsage conditionalUsage, ConditionalType conditionalType) { vector<ShaderCompilerCase::AttribSpec> result; result.push_back(ShaderCompilerCase::AttribSpec("a_position" + nameSpecialization, combineVec4ToVec16(Vec4(-1.0f, -1.0f, 0.0f, 1.0f), Vec4(-1.0f, 1.0f, 0.0f, 1.0f), Vec4( 1.0f, -1.0f, 0.0f, 1.0f), Vec4( 1.0f, 1.0f, 0.0f, 1.0f)))); result.push_back(ShaderCompilerCase::AttribSpec("a_coords" + nameSpecialization, combineVec4ToVec16(Vec4(0.0f, 0.0f, 0.0f, 0.0f), Vec4(0.0f, 1.0f, 0.0f, 0.0f), Vec4(1.0f, 0.0f, 0.0f, 0.0f), Vec4(1.0f, 1.0f, 0.0f, 0.0f)))); if (conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_DYNAMIC) result.push_back(ShaderCompilerCase::AttribSpec("a_condition" + nameSpecialization, combineVec4ToVec16(Vec4(1.0f), Vec4(1.0f), Vec4(1.0f), Vec4(1.0f)))); return result; } // Function for generating the shader uniforms of a texture lookup case. static vector<ShaderCompilerCase::UniformSpec> textureLookupShaderUniforms (const string& nameSpecialization, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType) { vector<ShaderCompilerCase::UniformSpec> result; for (int i = 0; i < numLookups; i++) result.push_back(ShaderCompilerCase::UniformSpec("u_sampler" + de::toString(i) + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_TEXTURE_UNIT, (float)i)); if (conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_UNIFORM) result.push_back(ShaderCompilerCase::UniformSpec("u_condition" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_FLOAT, 1.0f)); return result; } static string mandelbrotVertexTemplate (void) { const char* resultTemplate = "#version 300 es\n" "uniform highp mat4 u_mvp${NAME_SPEC};\n" "\n" "in highp vec4 a_vertex${NAME_SPEC};\n" "in highp vec4 a_coord${NAME_SPEC};\n" "\n" "out mediump vec2 v_coord${NAME_SPEC};\n" "\n" "void main(void)\n" "{\n" " gl_Position = u_mvp${NAME_SPEC} * a_vertex${NAME_SPEC} * (0.95 + 0.05*${FLOAT01});\n" "\n" " float xMin = -2.0;\n" " float xMax = +0.5;\n" " float yMin = -1.5;\n" " float yMax = +1.5;\n" "\n" " v_coord${NAME_SPEC}.x = a_coord${NAME_SPEC}.x * (xMax - xMin) + xMin;\n" " v_coord${NAME_SPEC}.y = a_coord${NAME_SPEC}.y * (yMax - yMin) + yMin;\n" "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } static string mandelbrotFragmentTemplate (int numFractalIterations) { string resultTemplate = "#version 300 es\n" "layout(location = 0) out mediump vec4 o_color;\n" "in mediump vec2 v_coord${NAME_SPEC};\n" "\n" "precision mediump float;\n" "\n" "#define NUM_ITERS " + de::toString(numFractalIterations) + "\n" "\n" "void main (void)\n" "{\n" " vec2 coords = v_coord${NAME_SPEC};\n" " float u_limit = 2.0 * 2.0;\n" " vec2 tmp = vec2(0, 0);\n" " int iter;\n" "\n" " for (iter = 0; iter < NUM_ITERS; iter++)\n" " {\n" " tmp = vec2((tmp.x + tmp.y) * (tmp.x - tmp.y), 2.0 * (tmp.x * tmp.y)) + coords;\n" "\n" " if (dot(tmp, tmp) > u_limit)\n" " break;\n" " }\n" "\n" " vec3 color = vec3(float(iter) * (1.0 / float(NUM_ITERS)));\n" "\n" " o_color = vec4(color, 1.0) + ${FLOAT01};\n" "${SEMANTIC_ERROR}" "}\n" "${INVALID_CHAR}"; return resultTemplate; } static vector<ShaderCompilerCase::AttribSpec> mandelbrotShaderAttributes (const string& nameSpecialization) { vector<ShaderCompilerCase::AttribSpec> result; result.push_back(ShaderCompilerCase::AttribSpec("a_vertex" + nameSpecialization, combineVec4ToVec16(Vec4(-1.0f, -1.0f, 0.0f, 1.0f), Vec4(-1.0f, 1.0f, 0.0f, 1.0f), Vec4( 1.0f, -1.0f, 0.0f, 1.0f), Vec4( 1.0f, 1.0f, 0.0f, 1.0f)))); result.push_back(ShaderCompilerCase::AttribSpec("a_coord" + nameSpecialization, combineVec4ToVec16(Vec4(0.0f, 0.0f, 0.0f, 1.0f), Vec4(0.0f, 1.0f, 0.0f, 1.0f), Vec4(1.0f, 0.0f, 0.0f, 1.0f), Vec4(1.0f, 1.0f, 0.0f, 1.0f)))); return result; } static vector<ShaderCompilerCase::UniformSpec> mandelbrotShaderUniforms (const string& nameSpecialization) { vector<ShaderCompilerCase::UniformSpec> result; result.push_back(ShaderCompilerCase::UniformSpec("u_mvp" + nameSpecialization, ShaderCompilerCase::UniformSpec::TYPE_MAT4, arrTo16(Mat4(1.0f).getColumnMajorData()))); return result; } ShaderCompilerCase::ShaderCompilerCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments) : TestCase (context, tcu::NODETYPE_PERFORMANCE, name, description) , m_viewportWidth (0) , m_viewportHeight (0) , m_avoidCache (avoidCache) , m_addWhitespaceAndComments (addWhitespaceAndComments) , m_startHash ((deUint32)(deUint64Hash(deGetTime()) ^ deUint64Hash(deGetMicroseconds()) ^ deInt32Hash(caseID))) { int cmdLineIterCount = context.getTestContext().getCommandLine().getTestIterationCount(); m_minimumMeasurementCount = cmdLineIterCount > 0 ? cmdLineIterCount : DEFAULT_MINIMUM_MEASUREMENT_COUNT; m_maximumMeasurementCount = m_minimumMeasurementCount*3; } ShaderCompilerCase::~ShaderCompilerCase (void) { } deUint32 ShaderCompilerCase::getSpecializationID (int measurementNdx) const { if (m_avoidCache) return m_startHash ^ (deUint32)deInt32Hash((deInt32)measurementNdx); else return m_startHash; } void ShaderCompilerCase::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget(); m_viewportWidth = deMin32(MAX_VIEWPORT_WIDTH, renderTarget.getWidth()); m_viewportHeight = deMin32(MAX_VIEWPORT_HEIGHT, renderTarget.getHeight()); gl.viewport(0, 0, m_viewportWidth, m_viewportHeight); } ShaderCompilerCase::ShadersAndProgram ShaderCompilerCase::createShadersAndProgram (void) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); ShadersAndProgram result; result.vertShader = gl.createShader(GL_VERTEX_SHADER); result.fragShader = gl.createShader(GL_FRAGMENT_SHADER); result.program = gl.createProgram(); gl.attachShader(result.program, result.vertShader); gl.attachShader(result.program, result.fragShader); return result; } void ShaderCompilerCase::setShaderSources (deUint32 vertShader, deUint32 fragShader, const ProgramContext& progCtx) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const char* vertShaderSourceCStr = progCtx.vertShaderSource.c_str(); const char* fragShaderSourceCStr = progCtx.fragShaderSource.c_str(); gl.shaderSource(vertShader, 1, &vertShaderSourceCStr, DE_NULL); gl.shaderSource(fragShader, 1, &fragShaderSourceCStr, DE_NULL); } bool ShaderCompilerCase::compileShader (deUint32 shader) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); GLint status; gl.compileShader(shader); gl.getShaderiv(shader, GL_COMPILE_STATUS, &status); return status != 0; } bool ShaderCompilerCase::linkAndUseProgram (deUint32 program) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); GLint linkStatus; gl.linkProgram(program); gl.getProgramiv(program, GL_LINK_STATUS, &linkStatus); if (linkStatus != 0) gl.useProgram(program); return linkStatus != 0; } void ShaderCompilerCase::setShaderInputs (deUint32 program, const ProgramContext& progCtx) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); // Setup attributes. for (int attribNdx = 0; attribNdx < (int)progCtx.vertexAttributes.size(); attribNdx++) { int location = gl.getAttribLocation(program, progCtx.vertexAttributes[attribNdx].name.c_str()); if (location >= 0) { gl.enableVertexAttribArray(location); gl.vertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, 0, progCtx.vertexAttributes[attribNdx].value.getPtr()); } } // Setup uniforms. for (int uniformNdx = 0; uniformNdx < (int)progCtx.uniforms.size(); uniformNdx++) { int location = gl.getUniformLocation(program, progCtx.uniforms[uniformNdx].name.c_str()); if (location >= 0) { const float* floatPtr = progCtx.uniforms[uniformNdx].value.getPtr(); switch (progCtx.uniforms[uniformNdx].type) { case UniformSpec::TYPE_FLOAT: gl.uniform1fv(location, 1, floatPtr); break; case UniformSpec::TYPE_VEC2: gl.uniform2fv(location, 1, floatPtr); break; case UniformSpec::TYPE_VEC3: gl.uniform3fv(location, 1, floatPtr); break; case UniformSpec::TYPE_VEC4: gl.uniform4fv(location, 1, floatPtr); break; case UniformSpec::TYPE_MAT3: gl.uniformMatrix3fv(location, 1, GL_FALSE, floatPtr); break; case UniformSpec::TYPE_MAT4: gl.uniformMatrix4fv(location, 1, GL_FALSE, floatPtr); break; case UniformSpec::TYPE_TEXTURE_UNIT: gl.uniform1i(location, (GLint)deRoundFloatToInt32(*floatPtr)); break; default: DE_ASSERT(DE_FALSE); } } } } void ShaderCompilerCase::draw (void) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); static const deUint8 indices[] = { 0, 1, 2, 2, 1, 3 }; gl.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); gl.drawElements(GL_TRIANGLES, DE_LENGTH_OF_ARRAY(indices), GL_UNSIGNED_BYTE, indices); // \note Read one pixel to force compilation. deUint32 pixel; gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); } void ShaderCompilerCase::cleanup (const ShadersAndProgram& shadersAndProgram, const ProgramContext& progCtx, bool linkSuccess) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (linkSuccess) { for (int attribNdx = 0; attribNdx < (int)progCtx.vertexAttributes.size(); attribNdx++) { int location = gl.getAttribLocation(shadersAndProgram.program, progCtx.vertexAttributes[attribNdx].name.c_str()); if (location >= 0) gl.disableVertexAttribArray(location); } } gl.useProgram(0); gl.detachShader(shadersAndProgram.program, shadersAndProgram.vertShader); gl.detachShader(shadersAndProgram.program, shadersAndProgram.fragShader); gl.deleteShader(shadersAndProgram.vertShader); gl.deleteShader(shadersAndProgram.fragShader); gl.deleteProgram(shadersAndProgram.program); } void ShaderCompilerCase::logProgramData (const BuildInfo& buildInfo, const ProgramContext& progCtx) const { m_testCtx.getLog() << TestLog::ShaderProgram(buildInfo.linkSuccess, buildInfo.logs.link) << TestLog::Shader(QP_SHADER_TYPE_VERTEX, progCtx.vertShaderSource, buildInfo.vertCompileSuccess, buildInfo.logs.vert) << TestLog::Shader(QP_SHADER_TYPE_FRAGMENT, progCtx.fragShaderSource, buildInfo.fragCompileSuccess, buildInfo.logs.frag) << TestLog::EndShaderProgram; } ShaderCompilerCase::Logs ShaderCompilerCase::getLogs (const ShadersAndProgram& shadersAndProgram) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); Logs result; result.vert = getShaderInfoLog(gl, shadersAndProgram.vertShader); result.frag = getShaderInfoLog(gl, shadersAndProgram.fragShader); result.link = getProgramInfoLog(gl, shadersAndProgram.program); return result; } bool ShaderCompilerCase::goodEnoughMeasurements (const vector<Measurement>& measurements) const { if ((int)measurements.size() < m_minimumMeasurementCount) return false; else { if ((int)measurements.size() >= m_maximumMeasurementCount) return true; else { vector<deInt64> totalTimesWithoutDraw; for (int i = 0; i < (int)measurements.size(); i++) totalTimesWithoutDraw.push_back(measurements[i].totalTimeWithoutDraw()); return vectorFloatRelativeMedianAbsoluteDeviation(vectorLowestPercentage(totalTimesWithoutDraw, 0.5f)) < RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD; } } } ShaderCompilerCase::IterateResult ShaderCompilerCase::iterate (void) { // Before actual measurements, compile and draw with a dummy shader to avoid possible initial slowdowns in the actual test. { deUint32 specID = getSpecializationID(0); ProgramContext progCtx; progCtx.vertShaderSource = specializeShaderSource(singleVaryingVertexTemplate(), specID, SHADER_VALIDITY_VALID); progCtx.fragShaderSource = specializeShaderSource(singleVaryingFragmentTemplate(), specID, SHADER_VALIDITY_VALID); progCtx.vertexAttributes = singleValueShaderAttributes(getNameSpecialization(specID)); ShadersAndProgram shadersAndProgram = createShadersAndProgram(); setShaderSources(shadersAndProgram.vertShader, shadersAndProgram.fragShader, progCtx); BuildInfo buildInfo; buildInfo.vertCompileSuccess = compileShader(shadersAndProgram.vertShader); buildInfo.fragCompileSuccess = compileShader(shadersAndProgram.fragShader); buildInfo.linkSuccess = linkAndUseProgram(shadersAndProgram.program); if (!(buildInfo.vertCompileSuccess && buildInfo.fragCompileSuccess && buildInfo.linkSuccess)) { buildInfo.logs = getLogs(shadersAndProgram); logProgramData(buildInfo, progCtx); cleanup(shadersAndProgram, progCtx, buildInfo.linkSuccess); m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compilation failed"); return STOP; } setShaderInputs(shadersAndProgram.program, progCtx); draw(); cleanup(shadersAndProgram, progCtx, buildInfo.linkSuccess); } vector<Measurement> measurements; // \note These are logged after measurements are done. ProgramContext latestProgramContext; BuildInfo latestBuildInfo; if (WARMUP_CPU_AT_BEGINNING_OF_CASE) tcu::warmupCPU(); // Actual test measurements. while (!goodEnoughMeasurements(measurements)) { // Create shaders, compile & link, set shader inputs and draw. Time measurement is done at relevant points. // \note Setting inputs and drawing are done twice in order to find out the time for actual compiling. // \note Shader data (sources and inputs) are generated and GL shader and program objects are created before any time measurements. ProgramContext progCtx = generateShaderData((int)measurements.size()); ShadersAndProgram shadersAndProgram = createShadersAndProgram(); BuildInfo buildInfo; if (m_addWhitespaceAndComments) { const deUint32 hash = m_startHash ^ (deUint32)deInt32Hash((deInt32)measurements.size()); progCtx.vertShaderSource = strWithWhiteSpaceAndComments(progCtx.vertShaderSource, hash); progCtx.fragShaderSource = strWithWhiteSpaceAndComments(progCtx.fragShaderSource, hash); } if (WARMUP_CPU_BEFORE_EACH_MEASUREMENT) tcu::warmupCPU(); // \note Do NOT do anything too hefty between the first and last deGetMicroseconds() here (other than the gl calls); it would disturb the measurement. deUint64 startTime = deGetMicroseconds(); setShaderSources(shadersAndProgram.vertShader, shadersAndProgram.fragShader, progCtx); deUint64 shaderSourceSetEndTime = deGetMicroseconds(); buildInfo.vertCompileSuccess = compileShader(shadersAndProgram.vertShader); deUint64 vertexShaderCompileEndTime = deGetMicroseconds(); buildInfo.fragCompileSuccess = compileShader(shadersAndProgram.fragShader); deUint64 fragmentShaderCompileEndTime = deGetMicroseconds(); buildInfo.linkSuccess = linkAndUseProgram(shadersAndProgram.program); deUint64 programLinkEndTime = deGetMicroseconds(); // Check compilation and linking status here, after all compilation and linking gl calls are made. if (!(buildInfo.vertCompileSuccess && buildInfo.fragCompileSuccess && buildInfo.linkSuccess)) { buildInfo.logs = getLogs(shadersAndProgram); logProgramData(buildInfo, progCtx); cleanup(shadersAndProgram, progCtx, buildInfo.linkSuccess); m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compilation failed"); return STOP; } setShaderInputs(shadersAndProgram.program, progCtx); deUint64 firstShaderInputSetEndTime = deGetMicroseconds(); // Draw for the first time. draw(); deUint64 firstDrawEndTime = deGetMicroseconds(); // Set inputs and draw again. setShaderInputs(shadersAndProgram.program, progCtx); deUint64 secondShaderInputSetEndTime = deGetMicroseconds(); draw(); deUint64 secondDrawEndTime = deGetMicroseconds(); // De-initializations (detach shaders etc.). buildInfo.logs = getLogs(shadersAndProgram); cleanup(shadersAndProgram, progCtx, buildInfo.linkSuccess); // Output measurement log later (after last measurement). measurements.push_back(Measurement((deInt64)(shaderSourceSetEndTime - startTime), (deInt64)(vertexShaderCompileEndTime - shaderSourceSetEndTime), (deInt64)(fragmentShaderCompileEndTime - vertexShaderCompileEndTime), (deInt64)(programLinkEndTime - fragmentShaderCompileEndTime), (deInt64)(firstShaderInputSetEndTime - programLinkEndTime), (deInt64)(firstDrawEndTime - firstShaderInputSetEndTime), (deInt64)(secondShaderInputSetEndTime - firstDrawEndTime), (deInt64)(secondDrawEndTime - secondShaderInputSetEndTime))); latestBuildInfo = buildInfo; latestProgramContext = progCtx; m_testCtx.touchWatchdog(); // \note Measurements may take a while in a bad case. } // End of test case, log information about measurements. { TestLog& log = m_testCtx.getLog(); vector<deInt64> sourceSetTimes; vector<deInt64> vertexCompileTimes; vector<deInt64> fragmentCompileTimes; vector<deInt64> programLinkTimes; vector<deInt64> firstInputSetTimes; vector<deInt64> firstDrawTimes; vector<deInt64> secondInputTimes; vector<deInt64> secondDrawTimes; vector<deInt64> firstPhaseTimes; vector<deInt64> secondPhaseTimes; vector<deInt64> totalTimesWithoutDraw; vector<deInt64> specializationTimes; if (!m_avoidCache) log << TestLog::Message << "Note: Testing cache hits, so the medians and averages exclude the first iteration." << TestLog::EndMessage; log << TestLog::Message << "Note: \"Specialization time\" means first draw time minus second draw time." << TestLog::EndMessage << TestLog::Message << "Note: \"Compilation time\" means the time up to (and including) linking, plus specialization time." << TestLog::EndMessage; log << TestLog::Section("IterationMeasurements", "Iteration measurements of compilation and linking times"); DE_ASSERT((int)measurements.size() > (m_avoidCache ? 0 : 1)); for (int ndx = 0; ndx < (int)measurements.size(); ndx++) { const Measurement& curMeas = measurements[ndx]; // Subtract time of second phase (second input setup and draw) from first (from start to end of first draw). // \note Cap if second phase seems unreasonably high (higher than first input set and draw). deInt64 timeWithoutDraw = curMeas.totalTimeWithoutDraw(); // Specialization time = first draw - second draw time. Again, cap at 0 if second draw was longer than first draw. deInt64 specializationTime = de::max<deInt64>(0, curMeas.firstDrawTime - curMeas.secondDrawTime); if (ndx > 0 || m_avoidCache) // \note When allowing cache hits, don't account for the first measurement when calculating median or average. { sourceSetTimes.push_back (curMeas.sourceSetTime); vertexCompileTimes.push_back (curMeas.vertexCompileTime); fragmentCompileTimes.push_back (curMeas.fragmentCompileTime); programLinkTimes.push_back (curMeas.programLinkTime); firstInputSetTimes.push_back (curMeas.firstInputSetTime); firstDrawTimes.push_back (curMeas.firstDrawTime); firstPhaseTimes.push_back (curMeas.firstPhase()); secondDrawTimes.push_back (curMeas.secondDrawTime); secondInputTimes.push_back (curMeas.secondInputSetTime); secondPhaseTimes.push_back (curMeas.secondPhase()); totalTimesWithoutDraw.push_back (timeWithoutDraw); specializationTimes.push_back (specializationTime); } // Log this measurement. log << TestLog::Float("Measurement" + de::toString(ndx) + "CompilationTime", "Measurement " + de::toString(ndx) + " compilation time", "ms", QP_KEY_TAG_TIME, timeWithoutDraw / 1000.0f) << TestLog::Float("Measurement" + de::toString(ndx) + "SpecializationTime", "Measurement " + de::toString(ndx) + " specialization time", "ms", QP_KEY_TAG_TIME, specializationTime / 1000.0f); } // Log some statistics. for (int entireRangeOrLowestHalf = 0; entireRangeOrLowestHalf < 2; entireRangeOrLowestHalf++) { bool isEntireRange = entireRangeOrLowestHalf == 0; string statNamePrefix = isEntireRange ? "" : "LowestHalf"; vector<deInt64> rangeTotalTimes = isEntireRange ? totalTimesWithoutDraw : vectorLowestPercentage(totalTimesWithoutDraw, 0.5f); vector<deInt64> rangeSpecializationTimes = isEntireRange ? specializationTimes : vectorLowestPercentage(specializationTimes, 0.5f); #define LOG_COMPILE_SPECIALIZE_TIME_STAT(NAME, DESC, FUNC) \ log << TestLog::Float(statNamePrefix + "CompilationTime" + (NAME), (DESC) + string(" of compilation time"), "ms", QP_KEY_TAG_TIME, (FUNC)(rangeTotalTimes)/1000.0f) \ << TestLog::Float(statNamePrefix + "SpecializationTime" + (NAME), (DESC) + string(" of specialization time"), "ms", QP_KEY_TAG_TIME, (FUNC)(rangeSpecializationTimes)/1000.0f) #define LOG_COMPILE_SPECIALIZE_RELATIVE_STAT(NAME, DESC, FUNC) \ log << TestLog::Float(statNamePrefix + "CompilationTime" + (NAME), (DESC) + string(" of compilation time"), "", QP_KEY_TAG_NONE, (FUNC)(rangeTotalTimes)) \ << TestLog::Float(statNamePrefix + "SpecializationTime" + (NAME), (DESC) + string(" of specialization time"), "", QP_KEY_TAG_NONE, (FUNC)(rangeSpecializationTimes)) log << TestLog::Message << "\nStatistics computed from " << (isEntireRange ? "all" : "only the lowest 50%") << " of the above measurements:" << TestLog::EndMessage; LOG_COMPILE_SPECIALIZE_TIME_STAT ("Median", "Median", vectorFloatMedian); LOG_COMPILE_SPECIALIZE_TIME_STAT ("Average", "Average", vectorFloatAverage); LOG_COMPILE_SPECIALIZE_TIME_STAT ("Minimum", "Minimum", vectorFloatMinimum); LOG_COMPILE_SPECIALIZE_TIME_STAT ("Maximum", "Maximum", vectorFloatMaximum); LOG_COMPILE_SPECIALIZE_TIME_STAT ("MedianAbsoluteDeviation", "Median absolute deviation", vectorFloatMedianAbsoluteDeviation); LOG_COMPILE_SPECIALIZE_RELATIVE_STAT ("RelativeMedianAbsoluteDeviation", "Relative median absolute deviation", vectorFloatRelativeMedianAbsoluteDeviation); LOG_COMPILE_SPECIALIZE_TIME_STAT ("StandardDeviation", "Standard deviation", vectorFloatStandardDeviation); LOG_COMPILE_SPECIALIZE_RELATIVE_STAT ("RelativeStandardDeviation", "Relative standard deviation", vectorFloatRelativeStandardDeviation); LOG_COMPILE_SPECIALIZE_TIME_STAT ("MaxMinusMin", "Max-min", vectorFloatMaximumMinusMinimum); LOG_COMPILE_SPECIALIZE_RELATIVE_STAT ("RelativeMaxMinusMin", "Relative max-min", vectorFloatRelativeMaximumMinusMinimum); #undef LOG_COMPILE_SPECIALIZE_RELATIVE_STAT #undef LOG_COMPILE_SPECIALIZE_TIME_STAT if (!isEntireRange && vectorFloatRelativeMedianAbsoluteDeviation(rangeTotalTimes) > RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD) log << TestLog::Message << "\nWARNING: couldn't achieve relative median absolute deviation under threshold value " << RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD << " for compilation time of the lowest 50% of measurements" << TestLog::EndMessage; } log << TestLog::EndSection; // End section IterationMeasurements for (int medianOrAverage = 0; medianOrAverage < 2; medianOrAverage++) { typedef float (*VecFunc)(const vector<deInt64>&); bool isMedian = medianOrAverage == 0; string singular = isMedian ? "Median" : "Average"; string plural = singular + "s"; VecFunc func = isMedian ? (VecFunc) vectorFloatMedian<deInt64> : (VecFunc) vectorFloatAverage<deInt64>; log << TestLog::Section(plural + "PerPhase", plural + " per phase"); for (int entireRangeOrLowestHalf = 0; entireRangeOrLowestHalf < 2; entireRangeOrLowestHalf++) { bool isEntireRange = entireRangeOrLowestHalf == 0; string statNamePrefix = isEntireRange ? "" : "LowestHalf"; float rangeSizeRatio = isEntireRange ? 1.0f : 0.5f; #define LOG_TIME(NAME, DESC, DATA) log << TestLog::Float(statNamePrefix + (NAME) + singular, singular + " of " + (DESC), "ms", QP_KEY_TAG_TIME, func(vectorLowestPercentage((DATA), rangeSizeRatio))/1000.0f); log << TestLog::Message << (isEntireRange ? "For all measurements:" : "\nFor only the lowest 50% of the measurements:") << TestLog::EndMessage; LOG_TIME("ShaderSourceSetTime", "shader source set time", sourceSetTimes); LOG_TIME("VertexShaderCompileTime", "vertex shader compile time", vertexCompileTimes); LOG_TIME("FragmentShaderCompileTime", "fragment shader compile time", fragmentCompileTimes); LOG_TIME("ProgramLinkTime", "program link time", programLinkTimes); LOG_TIME("FirstShaderInputSetTime", "first shader input set time", firstInputSetTimes); LOG_TIME("FirstDrawTime", "first draw time", firstDrawTimes); LOG_TIME("SecondShaderInputSetTime", "second shader input set time", secondInputTimes); LOG_TIME("SecondDrawTime", "second draw time", secondDrawTimes); #undef LOG_TIME } log << TestLog::EndSection; } // Set result. { log << TestLog::Message << "Note: test result is the first quartile (i.e. median of the lowest half of measurements) of compilation times" << TestLog::EndMessage; float result = vectorFloatFirstQuartile(totalTimesWithoutDraw) / 1000.0f; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString(result, 2).c_str()); } // Log shaders. if (m_avoidCache || m_addWhitespaceAndComments) { string msg = "Note: the following shaders are the ones from the last iteration; "; if (m_avoidCache) msg += "variables' names and some constant expressions"; if (m_addWhitespaceAndComments) msg += string(m_avoidCache ? " as well as " : "") + "whitespace and comments"; msg += " differ between iterations."; log << TestLog::Message << msg.c_str() << TestLog::EndMessage; } logProgramData(latestBuildInfo, latestProgramContext); return STOP; } } ShaderCompilerLightCase::ShaderCompilerLightCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, int numLights, LightType lightType) : ShaderCompilerCase (context, name, description, caseID, avoidCache, addWhitespaceAndComments) , m_numLights (numLights) , m_isVertexCase (isVertexCase) , m_lightType (lightType) , m_texture (DE_NULL) { } ShaderCompilerLightCase::~ShaderCompilerLightCase (void) { ShaderCompilerLightCase::deinit(); } void ShaderCompilerLightCase::deinit (void) { delete m_texture; m_texture = DE_NULL; } void ShaderCompilerLightCase::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); // Setup texture. DE_ASSERT(m_texture == DE_NULL); m_texture = new glu::Texture2D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, TEXTURE_WIDTH, TEXTURE_HEIGHT); tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_texture->getRefTexture().getFormat()); m_texture->getRefTexture().allocLevel(0); tcu::fillWithComponentGradients(m_texture->getRefTexture().getLevel(0), fmtInfo.valueMin, fmtInfo.valueMax); gl.activeTexture(GL_TEXTURE0); gl.bindTexture(GL_TEXTURE_2D, m_texture->getGLTexture()); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); m_texture->upload(); ShaderCompilerCase::init(); } ShaderCompilerCase::ProgramContext ShaderCompilerLightCase::generateShaderData (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); string nameSpec = getNameSpecialization(specID); ProgramContext result; result.vertShaderSource = specializeShaderSource(lightVertexTemplate(m_numLights, m_isVertexCase, m_lightType), specID, SHADER_VALIDITY_VALID); result.fragShaderSource = specializeShaderSource(lightFragmentTemplate(m_numLights, m_isVertexCase, m_lightType), specID, SHADER_VALIDITY_VALID); result.vertexAttributes = lightShaderAttributes(nameSpec); result.uniforms = lightShaderUniforms(nameSpec, m_numLights, m_lightType); return result; } ShaderCompilerTextureCase::ShaderCompilerTextureCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType) : ShaderCompilerCase (context, name, description, caseID, avoidCache, addWhitespaceAndComments) , m_numLookups (numLookups) , m_conditionalUsage (conditionalUsage) , m_conditionalType (conditionalType) { } ShaderCompilerTextureCase::~ShaderCompilerTextureCase (void) { ShaderCompilerTextureCase::deinit(); } void ShaderCompilerTextureCase::deinit (void) { for (vector<glu::Texture2D*>::iterator i = m_textures.begin(); i != m_textures.end(); i++) delete *i; m_textures.clear(); } void ShaderCompilerTextureCase::init (void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); // Setup texture. DE_ASSERT(m_textures.empty()); m_textures.reserve(m_numLookups); for (int i = 0; i < m_numLookups; i++) { glu::Texture2D* tex = new glu::Texture2D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, TEXTURE_WIDTH, TEXTURE_HEIGHT); tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(tex->getRefTexture().getFormat()); tex->getRefTexture().allocLevel(0); tcu::fillWithComponentGradients(tex->getRefTexture().getLevel(0), fmtInfo.valueMin, fmtInfo.valueMax); gl.activeTexture(GL_TEXTURE0 + i); gl.bindTexture(GL_TEXTURE_2D, tex->getGLTexture()); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); tex->upload(); m_textures.push_back(tex); } ShaderCompilerCase::init(); } ShaderCompilerCase::ProgramContext ShaderCompilerTextureCase::generateShaderData (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); string nameSpec = getNameSpecialization(specID); ProgramContext result; result.vertShaderSource = specializeShaderSource(textureLookupVertexTemplate(m_conditionalUsage, m_conditionalType), specID, SHADER_VALIDITY_VALID); result.fragShaderSource = specializeShaderSource(textureLookupFragmentTemplate(m_numLookups, m_conditionalUsage, m_conditionalType), specID, SHADER_VALIDITY_VALID); result.vertexAttributes = textureLookupShaderAttributes(nameSpec, m_conditionalUsage, m_conditionalType); result.uniforms = textureLookupShaderUniforms(nameSpec, m_numLookups, m_conditionalUsage, m_conditionalType); return result; } ShaderCompilerLoopCase::ShaderCompilerLoopCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, LoopType type, int numLoopIterations, int nestingDepth) : ShaderCompilerCase (context, name, description, caseID, avoidCache, addWhitespaceAndComments) , m_numLoopIterations (numLoopIterations) , m_nestingDepth (nestingDepth) , m_isVertexCase (isVertexCase) , m_type (type) { } ShaderCompilerLoopCase::~ShaderCompilerLoopCase (void) { } ShaderCompilerCase::ProgramContext ShaderCompilerLoopCase::generateShaderData (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); string nameSpec = getNameSpecialization(specID); ProgramContext result; result.vertShaderSource = specializeShaderSource(loopVertexTemplate(m_type, m_isVertexCase, m_numLoopIterations, m_nestingDepth), specID, SHADER_VALIDITY_VALID); result.fragShaderSource = specializeShaderSource(loopFragmentTemplate(m_type, m_isVertexCase, m_numLoopIterations, m_nestingDepth), specID, SHADER_VALIDITY_VALID); result.vertexAttributes = loopShaderAttributes(nameSpec, m_type, m_numLoopIterations); result.uniforms = loopShaderUniforms(nameSpec, m_type, m_numLoopIterations); return result; } ShaderCompilerOperCase::ShaderCompilerOperCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, bool isVertexCase, const char* oper, int numOperations) : ShaderCompilerCase (context, name, description, caseID, avoidCache, addWhitespaceAndComments) , m_oper (oper) , m_numOperations (numOperations) , m_isVertexCase (isVertexCase) { } ShaderCompilerOperCase::~ShaderCompilerOperCase (void) { } ShaderCompilerCase::ProgramContext ShaderCompilerOperCase::generateShaderData (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); string nameSpec = getNameSpecialization(specID); ProgramContext result; if (m_isVertexCase) { result.vertShaderSource = specializeShaderSource(binaryOpVertexTemplate(m_numOperations, m_oper.c_str()), specID, SHADER_VALIDITY_VALID); result.fragShaderSource = specializeShaderSource(singleVaryingFragmentTemplate(), specID, SHADER_VALIDITY_VALID); } else { result.vertShaderSource = specializeShaderSource(singleVaryingVertexTemplate(), specID, SHADER_VALIDITY_VALID); result.fragShaderSource = specializeShaderSource(binaryOpFragmentTemplate(m_numOperations, m_oper.c_str()), specID, SHADER_VALIDITY_VALID); } result.vertexAttributes = singleValueShaderAttributes(nameSpec); result.uniforms.clear(); // No uniforms used. return result; } ShaderCompilerMandelbrotCase::ShaderCompilerMandelbrotCase (Context& context, const char* name, const char* description, int caseID, bool avoidCache, bool addWhitespaceAndComments, int numFractalIterations) : ShaderCompilerCase (context, name, description, caseID, avoidCache, addWhitespaceAndComments) , m_numFractalIterations (numFractalIterations) { } ShaderCompilerMandelbrotCase::~ShaderCompilerMandelbrotCase (void) { } ShaderCompilerCase::ProgramContext ShaderCompilerMandelbrotCase::generateShaderData (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); string nameSpec = getNameSpecialization(specID); ProgramContext result; result.vertShaderSource = specializeShaderSource(mandelbrotVertexTemplate(), specID, SHADER_VALIDITY_VALID); result.fragShaderSource = specializeShaderSource(mandelbrotFragmentTemplate(m_numFractalIterations), specID, SHADER_VALIDITY_VALID); result.vertexAttributes = mandelbrotShaderAttributes(nameSpec); result.uniforms = mandelbrotShaderUniforms(nameSpec); return result; } InvalidShaderCompilerCase::InvalidShaderCompilerCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType) : TestCase (context, tcu::NODETYPE_PERFORMANCE, name, description) , m_invalidityType (invalidityType) , m_startHash ((deUint32)(deUint64Hash(deGetTime()) ^ deUint64Hash(deGetMicroseconds()) ^ deInt32Hash(caseID))) { int cmdLineIterCount = context.getTestContext().getCommandLine().getTestIterationCount(); m_minimumMeasurementCount = cmdLineIterCount > 0 ? cmdLineIterCount : DEFAULT_MINIMUM_MEASUREMENT_COUNT; m_maximumMeasurementCount = 3*m_minimumMeasurementCount; } InvalidShaderCompilerCase::~InvalidShaderCompilerCase (void) { } deUint32 InvalidShaderCompilerCase::getSpecializationID (int measurementNdx) const { return m_startHash ^ (deUint32)deInt32Hash((deInt32)measurementNdx); } InvalidShaderCompilerCase::Shaders InvalidShaderCompilerCase::createShaders (void) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); Shaders result; result.vertShader = gl.createShader(GL_VERTEX_SHADER); result.fragShader = gl.createShader(GL_FRAGMENT_SHADER); return result; } void InvalidShaderCompilerCase::setShaderSources (const Shaders& shaders, const ProgramContext& progCtx) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); const char* vertShaderSourceCStr = progCtx.vertShaderSource.c_str(); const char* fragShaderSourceCStr = progCtx.fragShaderSource.c_str(); gl.shaderSource(shaders.vertShader, 1, &vertShaderSourceCStr, DE_NULL); gl.shaderSource(shaders.fragShader, 1, &fragShaderSourceCStr, DE_NULL); } bool InvalidShaderCompilerCase::compileShader (deUint32 shader) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); GLint status; gl.compileShader(shader); gl.getShaderiv(shader, GL_COMPILE_STATUS, &status); return status != 0; } void InvalidShaderCompilerCase::logProgramData (const BuildInfo& buildInfo, const ProgramContext& progCtx) const { m_testCtx.getLog() << TestLog::ShaderProgram(false, "(No linking done)") << TestLog::Shader(QP_SHADER_TYPE_VERTEX, progCtx.vertShaderSource, buildInfo.vertCompileSuccess, buildInfo.logs.vert) << TestLog::Shader(QP_SHADER_TYPE_FRAGMENT, progCtx.fragShaderSource, buildInfo.fragCompileSuccess, buildInfo.logs.frag) << TestLog::EndShaderProgram; } InvalidShaderCompilerCase::Logs InvalidShaderCompilerCase::getLogs (const Shaders& shaders) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); Logs result; result.vert = getShaderInfoLog(gl, shaders.vertShader); result.frag = getShaderInfoLog(gl, shaders.fragShader); return result; } void InvalidShaderCompilerCase::cleanup (const Shaders& shaders) const { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.deleteShader(shaders.vertShader); gl.deleteShader(shaders.fragShader); } bool InvalidShaderCompilerCase::goodEnoughMeasurements (const vector<Measurement>& measurements) const { if ((int)measurements.size() < m_minimumMeasurementCount) return false; else { if ((int)measurements.size() >= m_maximumMeasurementCount) return true; else { vector<deInt64> totalTimes; for (int i = 0; i < (int)measurements.size(); i++) totalTimes.push_back(measurements[i].totalTime()); return vectorFloatRelativeMedianAbsoluteDeviation(vectorLowestPercentage(totalTimes, 0.5f)) < RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD; } } } InvalidShaderCompilerCase::IterateResult InvalidShaderCompilerCase::iterate (void) { ShaderValidity shaderValidity = m_invalidityType == INVALIDITY_INVALID_CHAR ? SHADER_VALIDITY_INVALID_CHAR : m_invalidityType == INVALIDITY_SEMANTIC_ERROR ? SHADER_VALIDITY_SEMANTIC_ERROR : SHADER_VALIDITY_LAST; DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST); // Before actual measurements, compile a dummy shader to avoid possible initial slowdowns in the actual test. { deUint32 specID = getSpecializationID(0); ProgramContext progCtx; progCtx.vertShaderSource = specializeShaderSource(singleVaryingVertexTemplate(), specID, shaderValidity); progCtx.fragShaderSource = specializeShaderSource(singleVaryingFragmentTemplate(), specID, shaderValidity); Shaders shaders = createShaders(); setShaderSources(shaders, progCtx); BuildInfo buildInfo; buildInfo.vertCompileSuccess = compileShader(shaders.vertShader); buildInfo.fragCompileSuccess = compileShader(shaders.fragShader); if (buildInfo.vertCompileSuccess || buildInfo.fragCompileSuccess) { buildInfo.logs = getLogs(shaders); logProgramData(buildInfo, progCtx); cleanup(shaders); m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compilation of a shader erroneously succeeded"); return STOP; } cleanup(shaders); } vector<Measurement> measurements; // \note These are logged after measurements are done. ProgramContext latestProgramContext; BuildInfo latestBuildInfo; if (WARMUP_CPU_AT_BEGINNING_OF_CASE) tcu::warmupCPU(); // Actual test measurements. while (!goodEnoughMeasurements(measurements)) { // Create shader and compile. Measure time. // \note Shader sources are generated and GL shader objects are created before any time measurements. ProgramContext progCtx = generateShaderSources((int)measurements.size()); Shaders shaders = createShaders(); BuildInfo buildInfo; if (WARMUP_CPU_BEFORE_EACH_MEASUREMENT) tcu::warmupCPU(); // \note Do NOT do anything too hefty between the first and last deGetMicroseconds() here (other than the gl calls); it would disturb the measurement. deUint64 startTime = deGetMicroseconds(); setShaderSources(shaders, progCtx); deUint64 shaderSourceSetEndTime = deGetMicroseconds(); buildInfo.vertCompileSuccess = compileShader(shaders.vertShader); deUint64 vertexShaderCompileEndTime = deGetMicroseconds(); buildInfo.fragCompileSuccess = compileShader(shaders.fragShader); deUint64 fragmentShaderCompileEndTime = deGetMicroseconds(); buildInfo.logs = getLogs(shaders); // Both shader compilations should have failed. if (buildInfo.vertCompileSuccess || buildInfo.fragCompileSuccess) { logProgramData(buildInfo, progCtx); cleanup(shaders); m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compilation of a shader erroneously succeeded"); return STOP; } // De-initializations (delete shaders). cleanup(shaders); // Output measurement log later (after last measurement). measurements.push_back(Measurement((deInt64)(shaderSourceSetEndTime - startTime), (deInt64)(vertexShaderCompileEndTime - shaderSourceSetEndTime), (deInt64)(fragmentShaderCompileEndTime - vertexShaderCompileEndTime))); latestBuildInfo = buildInfo; latestProgramContext = progCtx; m_testCtx.touchWatchdog(); // \note Measurements may take a while in a bad case. } // End of test case, log information about measurements. { TestLog& log = m_testCtx.getLog(); vector<deInt64> sourceSetTimes; vector<deInt64> vertexCompileTimes; vector<deInt64> fragmentCompileTimes; vector<deInt64> totalTimes; log << TestLog::Section("IterationMeasurements", "Iteration measurements of compilation times"); for (int ndx = 0; ndx < (int)measurements.size(); ndx++) { sourceSetTimes.push_back (measurements[ndx].sourceSetTime); vertexCompileTimes.push_back (measurements[ndx].vertexCompileTime); fragmentCompileTimes.push_back (measurements[ndx].fragmentCompileTime); totalTimes.push_back (measurements[ndx].totalTime()); // Log this measurement. log << TestLog::Float("Measurement" + de::toString(ndx) + "Time", "Measurement " + de::toString(ndx) + " time", "ms", QP_KEY_TAG_TIME, measurements[ndx].totalTime()/1000.0f); } // Log some statistics. for (int entireRangeOrLowestHalf = 0; entireRangeOrLowestHalf < 2; entireRangeOrLowestHalf++) { bool isEntireRange = entireRangeOrLowestHalf == 0; string statNamePrefix = isEntireRange ? "" : "LowestHalf"; vector<deInt64> rangeTimes = isEntireRange ? totalTimes : vectorLowestPercentage(totalTimes, 0.5f); log << TestLog::Message << "\nStatistics computed from " << (isEntireRange ? "all" : "only the lowest 50%") << " of the above measurements:" << TestLog::EndMessage; #define LOG_TIME_STAT(NAME, DESC, FUNC) log << TestLog::Float(statNamePrefix + "TotalTime" + (NAME), (DESC) + string(" of total time"), "ms", QP_KEY_TAG_TIME, (FUNC)(rangeTimes)/1000.0f) #define LOG_RELATIVE_STAT(NAME, DESC, FUNC) log << TestLog::Float(statNamePrefix + "TotalTime" + (NAME), (DESC) + string(" of total time"), "", QP_KEY_TAG_NONE, (FUNC)(rangeTimes)) LOG_TIME_STAT ("Median", "Median", vectorFloatMedian); LOG_TIME_STAT ("Average", "Average", vectorFloatAverage); LOG_TIME_STAT ("Minimum", "Minimum", vectorFloatMinimum); LOG_TIME_STAT ("Maximum", "Maximum", vectorFloatMaximum); LOG_TIME_STAT ("MedianAbsoluteDeviation", "Median absolute deviation", vectorFloatMedianAbsoluteDeviation); LOG_RELATIVE_STAT ("RelativeMedianAbsoluteDeviation", "Relative median absolute deviation", vectorFloatRelativeMedianAbsoluteDeviation); LOG_TIME_STAT ("StandardDeviation", "Standard deviation", vectorFloatStandardDeviation); LOG_RELATIVE_STAT ("RelativeStandardDeviation", "Relative standard deviation", vectorFloatRelativeStandardDeviation); LOG_TIME_STAT ("MaxMinusMin", "Max-min", vectorFloatMaximumMinusMinimum); LOG_RELATIVE_STAT ("RelativeMaxMinusMin", "Relative max-min", vectorFloatRelativeMaximumMinusMinimum); #undef LOG_TIME_STAT #undef LOG_RELATIVE_STAT if (!isEntireRange && vectorFloatRelativeMedianAbsoluteDeviation(rangeTimes) > RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD) log << TestLog::Message << "\nWARNING: couldn't achieve relative median absolute deviation under threshold value " << RELATIVE_MEDIAN_ABSOLUTE_DEVIATION_THRESHOLD << TestLog::EndMessage; } log << TestLog::EndSection; // End section IterationMeasurements for (int medianOrAverage = 0; medianOrAverage < 2; medianOrAverage++) { typedef float (*VecFunc)(const vector<deInt64>&); bool isMedian = medianOrAverage == 0; string singular = isMedian ? "Median" : "Average"; string plural = singular + "s"; VecFunc func = isMedian ? (VecFunc) vectorFloatMedian<deInt64> : (VecFunc) vectorFloatAverage<deInt64>; log << TestLog::Section(plural + "PerPhase", plural + " per phase"); for (int entireRangeOrLowestHalf = 0; entireRangeOrLowestHalf < 2; entireRangeOrLowestHalf++) { bool isEntireRange = entireRangeOrLowestHalf == 0; string statNamePrefix = isEntireRange ? "" : "LowestHalf"; float rangeSizeRatio = isEntireRange ? 1.0f : 0.5f; #define LOG_TIME(NAME, DESC, DATA) log << TestLog::Float(statNamePrefix + (NAME) + singular, singular + " of " + (DESC), "ms", QP_KEY_TAG_TIME, func(vectorLowestPercentage((DATA), rangeSizeRatio))/1000.0f); log << TestLog::Message << (isEntireRange ? "For all measurements:" : "\nFor only the lowest 50% of the measurements:") << TestLog::EndMessage; LOG_TIME("ShaderSourceSetTime", "shader source set time", sourceSetTimes); LOG_TIME("VertexShaderCompileTime", "vertex shader compile time", vertexCompileTimes); LOG_TIME("FragmentShaderCompileTime", "fragment shader compile time", fragmentCompileTimes); #undef LOG_TIME } log << TestLog::EndSection; } // Set result. { log << TestLog::Message << "Note: test result is the first quartile (i.e. median of the lowest half of measurements) of total times" << TestLog::EndMessage; float result = vectorFloatFirstQuartile(totalTimes) / 1000.0f; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString(result, 2).c_str()); } // Log shaders. log << TestLog::Message << "Note: the following shaders are the ones from the last iteration; variables' names and some constant expressions differ between iterations." << TestLog::EndMessage; logProgramData(latestBuildInfo, latestProgramContext); return STOP; } } InvalidShaderCompilerLightCase::InvalidShaderCompilerLightCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, int numLights, LightType lightType) : InvalidShaderCompilerCase (context, name, description, caseID, invalidityType) , m_isVertexCase (isVertexCase) , m_numLights (numLights) , m_lightType (lightType) { } InvalidShaderCompilerLightCase::~InvalidShaderCompilerLightCase (void) { } InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerLightCase::generateShaderSources (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); ProgramContext result; ShaderValidity shaderValidity = m_invalidityType == INVALIDITY_INVALID_CHAR ? SHADER_VALIDITY_INVALID_CHAR : m_invalidityType == INVALIDITY_SEMANTIC_ERROR ? SHADER_VALIDITY_SEMANTIC_ERROR : SHADER_VALIDITY_LAST; DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST); result.vertShaderSource = specializeShaderSource(lightVertexTemplate(m_numLights, m_isVertexCase, m_lightType), specID, shaderValidity); result.fragShaderSource = specializeShaderSource(lightFragmentTemplate(m_numLights, m_isVertexCase, m_lightType), specID, shaderValidity); return result; } InvalidShaderCompilerTextureCase::InvalidShaderCompilerTextureCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, int numLookups, ConditionalUsage conditionalUsage, ConditionalType conditionalType) : InvalidShaderCompilerCase (context, name, description, caseID, invalidityType) , m_numLookups (numLookups) , m_conditionalUsage (conditionalUsage) , m_conditionalType (conditionalType) { } InvalidShaderCompilerTextureCase::~InvalidShaderCompilerTextureCase (void) { } InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerTextureCase::generateShaderSources (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); ProgramContext result; ShaderValidity shaderValidity = m_invalidityType == INVALIDITY_INVALID_CHAR ? SHADER_VALIDITY_INVALID_CHAR : m_invalidityType == INVALIDITY_SEMANTIC_ERROR ? SHADER_VALIDITY_SEMANTIC_ERROR : SHADER_VALIDITY_LAST; DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST); result.vertShaderSource = specializeShaderSource(textureLookupVertexTemplate(m_conditionalUsage, m_conditionalType), specID, shaderValidity); result.fragShaderSource = specializeShaderSource(textureLookupFragmentTemplate(m_numLookups, m_conditionalUsage, m_conditionalType), specID, shaderValidity); return result; } InvalidShaderCompilerLoopCase::InvalidShaderCompilerLoopCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, LoopType type, int numLoopIterations, int nestingDepth) : InvalidShaderCompilerCase (context, name, description, caseID, invalidityType) , m_isVertexCase (isVertexCase) , m_numLoopIterations (numLoopIterations) , m_nestingDepth (nestingDepth) , m_type (type) { } InvalidShaderCompilerLoopCase::~InvalidShaderCompilerLoopCase (void) { } InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerLoopCase::generateShaderSources (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); ProgramContext result; ShaderValidity shaderValidity = m_invalidityType == INVALIDITY_INVALID_CHAR ? SHADER_VALIDITY_INVALID_CHAR : m_invalidityType == INVALIDITY_SEMANTIC_ERROR ? SHADER_VALIDITY_SEMANTIC_ERROR : SHADER_VALIDITY_LAST; DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST); result.vertShaderSource = specializeShaderSource(loopVertexTemplate(m_type, m_isVertexCase, m_numLoopIterations, m_nestingDepth), specID, shaderValidity); result.fragShaderSource = specializeShaderSource(loopFragmentTemplate(m_type, m_isVertexCase, m_numLoopIterations, m_nestingDepth), specID, shaderValidity); return result; } InvalidShaderCompilerOperCase::InvalidShaderCompilerOperCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, bool isVertexCase, const char* oper, int numOperations) : InvalidShaderCompilerCase (context, name, description, caseID, invalidityType) , m_isVertexCase (isVertexCase) , m_oper (oper) , m_numOperations (numOperations) { } InvalidShaderCompilerOperCase::~InvalidShaderCompilerOperCase (void) { } InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerOperCase::generateShaderSources (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); ProgramContext result; ShaderValidity shaderValidity = m_invalidityType == INVALIDITY_INVALID_CHAR ? SHADER_VALIDITY_INVALID_CHAR : m_invalidityType == INVALIDITY_SEMANTIC_ERROR ? SHADER_VALIDITY_SEMANTIC_ERROR : SHADER_VALIDITY_LAST; DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST); if (m_isVertexCase) { result.vertShaderSource = specializeShaderSource(binaryOpVertexTemplate(m_numOperations, m_oper.c_str()), specID, shaderValidity); result.fragShaderSource = specializeShaderSource(singleVaryingFragmentTemplate(), specID, shaderValidity); } else { result.vertShaderSource = specializeShaderSource(singleVaryingVertexTemplate(), specID, shaderValidity); result.fragShaderSource = specializeShaderSource(binaryOpFragmentTemplate(m_numOperations, m_oper.c_str()), specID, shaderValidity); } return result; } InvalidShaderCompilerMandelbrotCase::InvalidShaderCompilerMandelbrotCase (Context& context, const char* name, const char* description, int caseID, InvalidityType invalidityType, int numFractalIterations) : InvalidShaderCompilerCase (context, name, description, caseID, invalidityType) , m_numFractalIterations (numFractalIterations) { } InvalidShaderCompilerMandelbrotCase::~InvalidShaderCompilerMandelbrotCase (void) { } InvalidShaderCompilerCase::ProgramContext InvalidShaderCompilerMandelbrotCase::generateShaderSources (int measurementNdx) const { deUint32 specID = getSpecializationID(measurementNdx); ProgramContext result; ShaderValidity shaderValidity = m_invalidityType == INVALIDITY_INVALID_CHAR ? SHADER_VALIDITY_INVALID_CHAR : m_invalidityType == INVALIDITY_SEMANTIC_ERROR ? SHADER_VALIDITY_SEMANTIC_ERROR : SHADER_VALIDITY_LAST; DE_ASSERT(shaderValidity != SHADER_VALIDITY_LAST); result.vertShaderSource = specializeShaderSource(mandelbrotVertexTemplate(), specID, shaderValidity); result.fragShaderSource = specializeShaderSource(mandelbrotFragmentTemplate(m_numFractalIterations), specID, shaderValidity); return result; } void addShaderCompilationPerformanceCases (TestCaseGroup& parentGroup) { Context& context = parentGroup.getContext(); int caseID = 0; // Increment this after adding each case. Used for avoiding cache hits between cases. TestCaseGroup* validGroup = new TestCaseGroup(context, "valid_shader", "Valid Shader Compiler Cases"); TestCaseGroup* invalidGroup = new TestCaseGroup(context, "invalid_shader", "Invalid Shader Compiler Cases"); TestCaseGroup* cacheGroup = new TestCaseGroup(context, "cache", "Allow shader caching"); parentGroup.addChild(validGroup); parentGroup.addChild(invalidGroup); parentGroup.addChild(cacheGroup); TestCaseGroup* invalidCharGroup = new TestCaseGroup(context, "invalid_char", "Invalid Character Shader Compiler Cases"); TestCaseGroup* semanticErrorGroup = new TestCaseGroup(context, "semantic_error", "Semantic Error Shader Compiler Cases"); invalidGroup->addChild(invalidCharGroup); invalidGroup->addChild(semanticErrorGroup); // Lighting shader compilation cases. { static const int lightCounts[] = { 1, 2, 4, 8 }; TestCaseGroup* validLightingGroup = new TestCaseGroup(context, "lighting", "Shader Compiler Lighting Cases"); TestCaseGroup* invalidCharLightingGroup = new TestCaseGroup(context, "lighting", "Invalid Character Shader Compiler Lighting Cases"); TestCaseGroup* semanticErrorLightingGroup = new TestCaseGroup(context, "lighting", "Semantic Error Shader Compiler Lighting Cases"); TestCaseGroup* cacheLightingGroup = new TestCaseGroup(context, "lighting", "Shader Compiler Lighting Cache Cases"); validGroup->addChild(validLightingGroup); invalidCharGroup->addChild(invalidCharLightingGroup); semanticErrorGroup->addChild(semanticErrorLightingGroup); cacheGroup->addChild(cacheLightingGroup); for (int lightType = 0; lightType < (int)LIGHT_LAST; lightType++) { const char* lightTypeName = lightType == (int)LIGHT_DIRECTIONAL ? "directional" : lightType == (int)LIGHT_POINT ? "point" : DE_NULL; DE_ASSERT(lightTypeName != DE_NULL); for (int isFrag = 0; isFrag <= 1; isFrag++) { bool isVertex = isFrag == 0; const char* vertFragStr = isVertex ? "vertex" : "fragment"; for (int lightCountNdx = 0; lightCountNdx < DE_LENGTH_OF_ARRAY(lightCounts); lightCountNdx++) { int numLights = lightCounts[lightCountNdx]; string caseName = string("") + lightTypeName + "_" + de::toString(numLights) + "_lights_" + vertFragStr; // Valid shader case, no-cache and cache versions. validLightingGroup->addChild(new ShaderCompilerLightCase(context, caseName.c_str(), "", caseID++, true /* avoid cache */, false, isVertex, numLights, (LightType)lightType)); cacheLightingGroup->addChild(new ShaderCompilerLightCase(context, caseName.c_str(), "", caseID++, false /* allow cache */, false, isVertex, numLights, (LightType)lightType)); // Invalid shader cases. for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++) { TestCaseGroup* curInvalidGroup = invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR ? invalidCharLightingGroup : invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR ? semanticErrorLightingGroup : DE_NULL; DE_ASSERT(curInvalidGroup != DE_NULL); curInvalidGroup->addChild(new InvalidShaderCompilerLightCase(context, caseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, isVertex, numLights, (LightType)lightType)); } } } } } // Texture lookup shader compilation cases. { static const int texLookupCounts[] = { 1, 2, 4, 8 }; TestCaseGroup* validTexGroup = new TestCaseGroup(context, "texture", "Shader Compiler Texture Lookup Cases"); TestCaseGroup* invalidCharTexGroup = new TestCaseGroup(context, "texture", "Invalid Character Shader Compiler Texture Lookup Cases"); TestCaseGroup* semanticErrorTexGroup = new TestCaseGroup(context, "texture", "Semantic Error Shader Compiler Texture Lookup Cases"); TestCaseGroup* cacheTexGroup = new TestCaseGroup(context, "texture", "Shader Compiler Texture Lookup Cache Cases"); validGroup->addChild(validTexGroup); invalidCharGroup->addChild(invalidCharTexGroup); semanticErrorGroup->addChild(semanticErrorTexGroup); cacheGroup->addChild(cacheTexGroup); for (int conditionalUsage = 0; conditionalUsage < (int)CONDITIONAL_USAGE_LAST; conditionalUsage++) { const char* conditionalUsageName = conditionalUsage == (int)CONDITIONAL_USAGE_NONE ? "no_conditionals" : conditionalUsage == (int)CONDITIONAL_USAGE_FIRST_HALF ? "first_half" : conditionalUsage == (int)CONDITIONAL_USAGE_EVERY_OTHER ? "every_other" : DE_NULL; DE_ASSERT(conditionalUsageName != DE_NULL); int lastConditionalType = conditionalUsage == (int)CONDITIONAL_USAGE_NONE ? 1 : (int)CONDITIONAL_TYPE_LAST; for (int conditionalType = 0; conditionalType < lastConditionalType; conditionalType++) { const char* conditionalTypeName = conditionalType == (int)CONDITIONAL_TYPE_STATIC ? "static_conditionals" : conditionalType == (int)CONDITIONAL_TYPE_UNIFORM ? "uniform_conditionals" : conditionalType == (int)CONDITIONAL_TYPE_DYNAMIC ? "dynamic_conditionals" : DE_NULL; DE_ASSERT(conditionalTypeName != DE_NULL); for (int lookupCountNdx = 0; lookupCountNdx < DE_LENGTH_OF_ARRAY(texLookupCounts); lookupCountNdx++) { int numLookups = texLookupCounts[lookupCountNdx]; string caseName = de::toString(numLookups) + "_lookups_" + conditionalUsageName + (conditionalUsage == (int)CONDITIONAL_USAGE_NONE ? "" : string("_") + conditionalTypeName); // Valid shader case, no-cache and cache versions. validTexGroup->addChild(new ShaderCompilerTextureCase(context, caseName.c_str(), "", caseID++, true /* avoid cache */, false, numLookups, (ConditionalUsage)conditionalUsage, (ConditionalType)conditionalType)); cacheTexGroup->addChild(new ShaderCompilerTextureCase(context, caseName.c_str(), "", caseID++, false /* allow cache */, false, numLookups, (ConditionalUsage)conditionalUsage, (ConditionalType)conditionalType)); // Invalid shader cases. for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++) { TestCaseGroup* curInvalidGroup = invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR ? invalidCharTexGroup : invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR ? semanticErrorTexGroup : DE_NULL; DE_ASSERT(curInvalidGroup != DE_NULL); curInvalidGroup->addChild(new InvalidShaderCompilerTextureCase(context, caseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, numLookups, (ConditionalUsage)conditionalUsage, (ConditionalType)conditionalType)); } } } } } // Loop shader compilation cases. { static const int loopIterCounts[] = { 10, 100, 1000 }; static const int maxLoopNestingDepth = 3; static const int maxTotalLoopIterations = 2000; // If <loop iteration count> ** <loop nesting depth> (where ** is exponentiation) exceeds this, don't generate the case. TestCaseGroup* validLoopGroup = new TestCaseGroup(context, "loop", "Shader Compiler Loop Cases"); TestCaseGroup* invalidCharLoopGroup = new TestCaseGroup(context, "loop", "Invalid Character Shader Compiler Loop Cases"); TestCaseGroup* semanticErrorLoopGroup = new TestCaseGroup(context, "loop", "Semantic Error Shader Compiler Loop Cases"); TestCaseGroup* cacheLoopGroup = new TestCaseGroup(context, "loop", "Shader Compiler Loop Cache Cases"); validGroup->addChild(validLoopGroup); invalidCharGroup->addChild(invalidCharLoopGroup); semanticErrorGroup->addChild(semanticErrorLoopGroup); cacheGroup->addChild(cacheLoopGroup); for (int loopType = 0; loopType < (int)LOOP_LAST; loopType++) { const char* loopTypeName = loopType == (int)LOOP_TYPE_STATIC ? "static" : loopType == (int)LOOP_TYPE_UNIFORM ? "uniform" : loopType == (int)LOOP_TYPE_DYNAMIC ? "dynamic" : DE_NULL; DE_ASSERT(loopTypeName != DE_NULL); TestCaseGroup* validLoopTypeGroup = new TestCaseGroup(context, loopTypeName, ""); TestCaseGroup* invalidCharLoopTypeGroup = new TestCaseGroup(context, loopTypeName, ""); TestCaseGroup* semanticErrorLoopTypeGroup = new TestCaseGroup(context, loopTypeName, ""); TestCaseGroup* cacheLoopTypeGroup = new TestCaseGroup(context, loopTypeName, ""); validLoopGroup->addChild(validLoopTypeGroup); invalidCharLoopGroup->addChild(invalidCharLoopTypeGroup); semanticErrorLoopGroup->addChild(semanticErrorLoopTypeGroup); cacheLoopGroup->addChild(cacheLoopTypeGroup); for (int isFrag = 0; isFrag <= 1; isFrag++) { bool isVertex = isFrag == 0; const char* vertFragStr = isVertex ? "vertex" : "fragment"; // \note Non-static loop cases with different iteration counts have identical shaders, so only make one of each. int loopIterCountMaxNdx = loopType != (int)LOOP_TYPE_STATIC ? 1 : DE_LENGTH_OF_ARRAY(loopIterCounts); for (int nestingDepth = 1; nestingDepth <= maxLoopNestingDepth; nestingDepth++) { for (int loopIterCountNdx = 0; loopIterCountNdx < loopIterCountMaxNdx; loopIterCountNdx++) { int numIterations = loopIterCounts[loopIterCountNdx]; if (deFloatPow((float)numIterations, (float)nestingDepth) > (float)maxTotalLoopIterations) continue; // Don't generate too heavy tasks. string validCaseName = de::toString(numIterations) + "_iterations_" + de::toString(nestingDepth) + "_levels_" + vertFragStr; // Valid shader case, no-cache and cache versions. validLoopTypeGroup->addChild(new ShaderCompilerLoopCase(context, validCaseName.c_str(), "", caseID++, true /* avoid cache */, false, isVertex, (LoopType)loopType, numIterations, nestingDepth)); cacheLoopTypeGroup->addChild(new ShaderCompilerLoopCase(context, validCaseName.c_str(), "", caseID++, false /* allow cache */, false, isVertex, (LoopType)loopType, numIterations, nestingDepth)); // Invalid shader cases. for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++) { TestCaseGroup* curInvalidGroup = invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR ? invalidCharLoopTypeGroup : invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR ? semanticErrorLoopTypeGroup : DE_NULL; DE_ASSERT(curInvalidGroup != DE_NULL); string invalidCaseName = de::toString(nestingDepth) + "_levels_" + vertFragStr; if (loopType == (int)LOOP_TYPE_STATIC) invalidCaseName = de::toString(numIterations) + "_iterations_" + invalidCaseName; // \note For invalid, non-static loop cases the iteration count means nothing (since no uniforms or attributes are set). curInvalidGroup->addChild(new InvalidShaderCompilerLoopCase(context, invalidCaseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, isVertex, (LoopType)loopType, numIterations, nestingDepth)); } } } } } } // Multiplication shader compilation cases. { static const int multiplicationCounts[] = { 10, 100, 1000 }; TestCaseGroup* validMulGroup = new TestCaseGroup(context, "multiplication", "Shader Compiler Multiplication Cases"); TestCaseGroup* invalidCharMulGroup = new TestCaseGroup(context, "multiplication", "Invalid Character Shader Compiler Multiplication Cases"); TestCaseGroup* semanticErrorMulGroup = new TestCaseGroup(context, "multiplication", "Semantic Error Shader Compiler Multiplication Cases"); TestCaseGroup* cacheMulGroup = new TestCaseGroup(context, "multiplication", "Shader Compiler Multiplication Cache Cases"); validGroup->addChild(validMulGroup); invalidCharGroup->addChild(invalidCharMulGroup); semanticErrorGroup->addChild(semanticErrorMulGroup); cacheGroup->addChild(cacheMulGroup); for (int isFrag = 0; isFrag <= 1; isFrag++) { bool isVertex = isFrag == 0; const char* vertFragStr = isVertex ? "vertex" : "fragment"; for (int operCountNdx = 0; operCountNdx < DE_LENGTH_OF_ARRAY(multiplicationCounts); operCountNdx++) { int numOpers = multiplicationCounts[operCountNdx]; string caseName = de::toString(numOpers) + "_operations_" + vertFragStr; // Valid shader case, no-cache and cache versions. validMulGroup->addChild(new ShaderCompilerOperCase(context, caseName.c_str(), "", caseID++, true /* avoid cache */, false, isVertex, "*", numOpers)); cacheMulGroup->addChild(new ShaderCompilerOperCase(context, caseName.c_str(), "", caseID++, false /* allow cache */, false, isVertex, "*", numOpers)); // Invalid shader cases. for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++) { TestCaseGroup* curInvalidGroup = invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR ? invalidCharMulGroup : invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR ? semanticErrorMulGroup : DE_NULL; DE_ASSERT(curInvalidGroup != DE_NULL); curInvalidGroup->addChild(new InvalidShaderCompilerOperCase(context, caseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, isVertex, "*", numOpers)); } } } } // Mandelbrot shader compilation cases. { static const int mandelbrotIterationCounts[] = { 32, 64, 128 }; TestCaseGroup* validMandelbrotGroup = new TestCaseGroup(context, "mandelbrot", "Shader Compiler Mandelbrot Fractal Cases"); TestCaseGroup* invalidCharMandelbrotGroup = new TestCaseGroup(context, "mandelbrot", "Invalid Character Shader Compiler Mandelbrot Fractal Cases"); TestCaseGroup* semanticErrorMandelbrotGroup = new TestCaseGroup(context, "mandelbrot", "Semantic Error Shader Compiler Mandelbrot Fractal Cases"); TestCaseGroup* cacheMandelbrotGroup = new TestCaseGroup(context, "mandelbrot", "Shader Compiler Mandelbrot Fractal Cache Cases"); validGroup->addChild(validMandelbrotGroup); invalidCharGroup->addChild(invalidCharMandelbrotGroup); semanticErrorGroup->addChild(semanticErrorMandelbrotGroup); cacheGroup->addChild(cacheMandelbrotGroup); for (int iterCountNdx = 0; iterCountNdx < DE_LENGTH_OF_ARRAY(mandelbrotIterationCounts); iterCountNdx++) { int numFractalIterations = mandelbrotIterationCounts[iterCountNdx]; string caseName = de::toString(numFractalIterations) + "_iterations"; // Valid shader case, no-cache and cache versions. validMandelbrotGroup->addChild(new ShaderCompilerMandelbrotCase(context, caseName.c_str(), "", caseID++, true /* avoid cache */, false, numFractalIterations)); cacheMandelbrotGroup->addChild(new ShaderCompilerMandelbrotCase(context, caseName.c_str(), "", caseID++, false /* allow cache */, false, numFractalIterations)); // Invalid shader cases. for (int invalidityType = 0; invalidityType < (int)InvalidShaderCompilerCase::INVALIDITY_LAST; invalidityType++) { TestCaseGroup* curInvalidGroup = invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_INVALID_CHAR ? invalidCharMandelbrotGroup : invalidityType == (int)InvalidShaderCompilerCase::INVALIDITY_SEMANTIC_ERROR ? semanticErrorMandelbrotGroup : DE_NULL; DE_ASSERT(curInvalidGroup != DE_NULL); curInvalidGroup->addChild(new InvalidShaderCompilerMandelbrotCase(context, caseName.c_str(), "", caseID++, (InvalidShaderCompilerCase::InvalidityType)invalidityType, numFractalIterations)); } } } // Cases testing cache behaviour when whitespace and comments are added. { TestCaseGroup* whitespaceCommentCacheGroup = new TestCaseGroup(context, "cache_whitespace_comment", "Cases testing the effect of whitespace and comments on caching"); parentGroup.addChild(whitespaceCommentCacheGroup); // \note Add just a small subset of the cases that were added above for the main performance tests. // Cases with both vertex and fragment variants. for (int isFrag = 0; isFrag <= 1; isFrag++) { bool isVertex = isFrag == 0; string vtxFragSuffix = isVertex ? "_vertex" : "_fragment"; string dirLightName = "directional_2_lights" + vtxFragSuffix; string loopName = "static_loop_100_iterations" + vtxFragSuffix; string multCase = "multiplication_100_operations" + vtxFragSuffix; whitespaceCommentCacheGroup->addChild(new ShaderCompilerLightCase(context, dirLightName.c_str(), "", caseID++, false, true, isVertex, 2, LIGHT_DIRECTIONAL)); whitespaceCommentCacheGroup->addChild(new ShaderCompilerLoopCase(context, loopName.c_str(), "", caseID++, false, true, isVertex, LOOP_TYPE_STATIC, 100, 1)); whitespaceCommentCacheGroup->addChild(new ShaderCompilerOperCase(context, multCase.c_str(), "", caseID++, false, true, isVertex, "*", 100)); } // Cases that don't have vertex and fragment variants. whitespaceCommentCacheGroup->addChild(new ShaderCompilerTextureCase(context, "texture_4_lookups", "", caseID++, false, true, 4, CONDITIONAL_USAGE_NONE, CONDITIONAL_TYPE_STATIC)); whitespaceCommentCacheGroup->addChild(new ShaderCompilerMandelbrotCase(context, "mandelbrot_32_operations", "", caseID++, false, true, 32)); } } } // Performance } // gles3 } // deqp