| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 2.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 "es2pShaderCompilationCases.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 gles2 |
| { |
| |
| 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 = 0; |
| 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 = 0; |
| 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 ? "" : "\tmediump float 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 += |
| "attribute highp vec4 a_position${NAME_SPEC};\n" |
| "attribute mediump vec3 a_normal${NAME_SPEC};\n" |
| "attribute 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" |
| "varying mediump vec4 v_color${NAME_SPEC};\n" |
| "varying mediump vec2 v_texCoord0${NAME_SPEC};\n"; |
| |
| if (!isVertexCase) |
| { |
| resultTemplate += "varying mediump vec3 v_eyeNormal${NAME_SPEC};\n"; |
| |
| if (lightType == LIGHT_POINT) |
| resultTemplate += |
| "varying mediump vec3 v_directionToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n" |
| "varying 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; |
| |
| 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" |
| "varying mediump vec4 v_color${NAME_SPEC};\n" |
| "varying mediump vec2 v_texCoord0${NAME_SPEC};\n"; |
| |
| if (!isVertexCase) |
| { |
| resultTemplate += |
| "varying mediump vec3 v_eyeNormal${NAME_SPEC};\n"; |
| |
| if (lightType == LIGHT_POINT) |
| resultTemplate += |
| "varying mediump vec3 v_directionToLight${NAME_SPEC}[" + de::toString(numLights) + "];\n" |
| "varying 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 *= texture2D(u_sampler0${NAME_SPEC}, texCoord0);\n" |
| " gl_FragColor = 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 += |
| "attribute highp vec4 a_position${NAME_SPEC};\n"; |
| |
| if (type == LOOP_TYPE_DYNAMIC) |
| resultTemplate += |
| "attribute mediump float a_loopBound${NAME_SPEC};\n"; |
| |
| resultTemplate += |
| "attribute mediump vec4 a_value${NAME_SPEC};\n" |
| "varying 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 += |
| "varying 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 += |
| "varying mediump vec4 v_value${NAME_SPEC};\n"; |
| |
| if (!isVertexCase) |
| { |
| if (type == LOOP_TYPE_DYNAMIC) |
| resultTemplate += |
| "varying 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 += |
| " gl_FragColor = value + ${FLOAT01};\n"; |
| } |
| else |
| resultTemplate += |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = 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 += |
| "attribute highp vec4 a_position${NAME_SPEC};\n" |
| "attribute mediump vec4 a_value${NAME_SPEC};\n" |
| "varying 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 += |
| "varying 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" |
| " gl_FragColor = 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 = |
| "attribute highp vec4 a_position${NAME_SPEC};\n" |
| "attribute mediump vec4 a_value${NAME_SPEC};\n" |
| "varying 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 = |
| "varying mediump vec4 v_value${NAME_SPEC};\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = 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 += |
| "attribute highp vec4 a_position${NAME_SPEC};\n" |
| "attribute mediump vec2 a_coords${NAME_SPEC};\n" |
| "varying mediump vec2 v_coords${NAME_SPEC};\n"; |
| |
| if (conditionVaryingNeeded) |
| resultTemplate += |
| "attribute mediump float a_condition${NAME_SPEC};\n" |
| "varying 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 += |
| "varying mediump vec2 v_coords${NAME_SPEC};\n"; |
| |
| if (conditionalUsage != CONDITIONAL_USAGE_NONE && conditionalType == CONDITIONAL_TYPE_DYNAMIC) |
| resultTemplate += |
| "varying 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 += texture2D(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 += |
| " gl_FragColor = 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 = |
| "uniform highp mat4 u_mvp${NAME_SPEC};\n" |
| "\n" |
| "attribute highp vec4 a_vertex${NAME_SPEC};\n" |
| "attribute highp vec4 a_coord${NAME_SPEC};\n" |
| "\n" |
| "varying 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 = |
| "varying 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" |
| " gl_FragColor = 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 = 0; |
| 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 = 0; |
| |
| 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 minimal 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, (float)timeWithoutDraw / 1000.0f) |
| << TestLog::Float("Measurement" + de::toString(ndx) + "SpecializationTime", |
| "Measurement " + de::toString(ndx) + " specialization time", |
| "ms", QP_KEY_TAG_TIME, (float)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 minimal 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, (float)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(cont
|