| /*------------------------------------------------------------------------- |
| * 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 control statement performance tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es2pShaderControlStatementTests.hpp" |
| #include "glsShaderPerformanceCase.hpp" |
| #include "tcuTestLog.hpp" |
| |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| |
| #include <string> |
| #include <vector> |
| |
| namespace deqp |
| { |
| namespace gles2 |
| { |
| namespace Performance |
| { |
| |
| using namespace gls; |
| using namespace glw; // GL types |
| using tcu::Vec4; |
| using tcu::TestLog; |
| using std::string; |
| using std::vector; |
| |
| // Writes the workload expression used in conditional tests. |
| static void writeConditionalWorkload (std::ostringstream& stream, const char* resultName, const char* operandName) |
| { |
| const int numMultiplications = 64; |
| |
| stream << resultName << " = "; |
| |
| for (int i = 0; i < numMultiplications; i++) |
| { |
| if (i > 0) |
| stream << "*"; |
| |
| stream << operandName; |
| } |
| |
| stream << ";"; |
| } |
| |
| // Writes the workload expression used in loop tests (one iteration). |
| static void writeLoopWorkload (std::ostringstream& stream, const char* resultName, const char* operandName) |
| { |
| const int numMultiplications = 8; |
| |
| stream << resultName << " = "; |
| |
| for (int i = 0; i < numMultiplications; i++) |
| { |
| if (i > 0) |
| stream << " * "; |
| |
| stream << "(" << resultName << " + " << operandName << ")"; |
| } |
| |
| stream << ";"; |
| } |
| |
| // The type of decision to be made in a conditional expression. |
| // \note In fragment cases with DECISION_ATTRIBUTE, the value in the expression will actually be a varying. |
| enum DecisionType |
| { |
| DECISION_STATIC = 0, |
| DECISION_UNIFORM, |
| DECISION_ATTRIBUTE, |
| |
| DECISION_LAST |
| }; |
| |
| class ControlStatementCase : public ShaderPerformanceCase |
| { |
| public: |
| ControlStatementCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, gls::PerfCaseType caseType) |
| : ShaderPerformanceCase(testCtx, renderCtx, name, description, caseType) |
| { |
| } |
| |
| void init (void) |
| { |
| m_testCtx.getLog() << TestLog::Message << "Using additive blending." << TestLog::EndMessage; |
| ShaderPerformanceCase::init(); |
| } |
| |
| void setupRenderState (void) |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| |
| gl.enable(GL_BLEND); |
| gl.blendEquation(GL_FUNC_ADD); |
| gl.blendFunc(GL_ONE, GL_ONE); |
| } |
| }; |
| |
| class ConditionalCase : public ControlStatementCase |
| { |
| public: |
| enum BranchResult |
| { |
| BRANCH_TRUE = 0, |
| BRANCH_FALSE, |
| BRANCH_MIXED, |
| |
| BRANCH_LAST |
| }; |
| |
| enum WorkloadDivision |
| { |
| WORKLOAD_DIVISION_EVEN = 0, //! Both true and false branches contain same amount of computation. |
| WORKLOAD_DIVISION_TRUE_HEAVY, //! True branch contains more computation. |
| WORKLOAD_DIVISION_FALSE_HEAVY, //! False branch contains more computation. |
| |
| WORKLOAD_DIVISION_LAST |
| }; |
| |
| ConditionalCase (Context& context, const char* name, const char* description, DecisionType decisionType, BranchResult branchType, WorkloadDivision workloadDivision, bool isVertex); |
| ~ConditionalCase (void); |
| |
| void init (void); |
| void deinit (void); |
| void setupProgram (deUint32 program); |
| |
| private: |
| DecisionType m_decisionType; |
| BranchResult m_branchType; |
| WorkloadDivision m_workloadDivision; |
| |
| vector<float> m_comparisonValueArray; // Will contain per-vertex comparison values if using mixed branch type in vertex case. |
| deUint32 m_arrayBuffer; |
| }; |
| |
| ConditionalCase::ConditionalCase (Context& context, const char* name, const char* description, DecisionType decisionType, BranchResult branchType, WorkloadDivision workloadDivision, bool isVertex) |
| : ControlStatementCase (context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT) |
| , m_decisionType (decisionType) |
| , m_branchType (branchType) |
| , m_workloadDivision (workloadDivision) |
| , m_arrayBuffer (0) |
| { |
| } |
| |
| void ConditionalCase::init (void) |
| { |
| bool isVertexCase = m_caseType == CASETYPE_VERTEX; |
| |
| bool isStaticCase = m_decisionType == DECISION_STATIC; |
| bool isUniformCase = m_decisionType == DECISION_UNIFORM; |
| bool isAttributeCase = m_decisionType == DECISION_ATTRIBUTE; |
| |
| DE_ASSERT(isStaticCase || isUniformCase || isAttributeCase); |
| |
| bool isConditionTrue = m_branchType == BRANCH_TRUE; |
| bool isConditionFalse = m_branchType == BRANCH_FALSE; |
| bool isConditionMixed = m_branchType == BRANCH_MIXED; |
| |
| DE_ASSERT(isConditionTrue || isConditionFalse || isConditionMixed); |
| DE_UNREF(isConditionFalse); |
| |
| DE_ASSERT(isAttributeCase || !isConditionMixed); // The branch taken can vary between executions only if using attribute input. |
| |
| const char* staticCompareValueStr = isConditionTrue ? "1.0" : "-1.0"; |
| const char* compareValueStr = isStaticCase ? staticCompareValueStr : |
| isUniformCase ? "u_compareValue" : |
| isVertexCase ? "a_compareValue" : |
| "v_compareValue"; |
| |
| std::ostringstream vtx; |
| std::ostringstream frag; |
| std::ostringstream& op = isVertexCase ? vtx : frag; |
| |
| vtx << "attribute highp vec4 a_position;\n"; // Position attribute. |
| vtx << "attribute mediump vec4 a_value0;\n"; // Input for workload calculations of "true" branch. |
| vtx << "attribute mediump vec4 a_value1;\n"; // Input for workload calculations of "false" branch. |
| |
| // Value to be used in the conditional expression. |
| if (isAttributeCase) |
| vtx << "attribute mediump float a_compareValue;\n"; |
| else if (isUniformCase) |
| op << "uniform mediump float u_compareValue;\n"; |
| |
| // Varyings. |
| if (isVertexCase) |
| { |
| vtx << "varying mediump vec4 v_color;\n"; |
| frag << "varying mediump vec4 v_color;\n"; |
| } |
| else |
| { |
| vtx << "varying mediump vec4 v_value0;\n"; |
| vtx << "varying mediump vec4 v_value1;\n"; |
| frag << "varying mediump vec4 v_value0;\n"; |
| frag << "varying mediump vec4 v_value1;\n"; |
| |
| if (isAttributeCase) |
| { |
| vtx << "varying mediump float v_compareValue;\n"; |
| frag << "varying mediump float v_compareValue;\n"; |
| } |
| } |
| |
| vtx << "\n"; |
| vtx << "void main()\n"; |
| vtx << "{\n"; |
| vtx << " gl_Position = a_position;\n"; |
| |
| frag << "\n"; |
| frag << "void main()\n"; |
| frag << "{\n"; |
| |
| op << " mediump vec4 res;\n"; |
| |
| string condition; |
| |
| if (isConditionMixed && !isVertexCase) |
| condition = string("") + "fract(" + compareValueStr + ") < 0.5"; // Comparison result varies with high frequency. |
| else |
| condition = string("") + compareValueStr + " > 0.0"; |
| |
| op << " if (" << condition << ")\n"; |
| op << " {\n"; |
| |
| op << "\t\t"; |
| if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_TRUE_HEAVY) |
| writeConditionalWorkload(op, "res", isVertexCase ? "a_value0" : "v_value0"); // Workload calculation for the "true" branch. |
| else |
| op << "res = " << (isVertexCase ? "a_value0" : "v_value0") << ";"; |
| op << "\n"; |
| |
| op << " }\n"; |
| op << " else\n"; |
| op << " {\n"; |
| |
| op << "\t\t"; |
| if (m_workloadDivision == WORKLOAD_DIVISION_EVEN || m_workloadDivision == WORKLOAD_DIVISION_FALSE_HEAVY) |
| writeConditionalWorkload(op, "res", isVertexCase ? "a_value1" : "v_value1"); // Workload calculations for the "false" branch. |
| else |
| op << "res = " << (isVertexCase ? "a_value1" : "v_value1") << ";"; |
| op << "\n"; |
| |
| op << " }\n"; |
| |
| if (isVertexCase) |
| { |
| // Put result to color variable. |
| vtx << " v_color = res;\n"; |
| frag << " gl_FragColor = v_color;\n"; |
| } |
| else |
| { |
| // Transfer inputs to fragment shader through varyings. |
| if (isAttributeCase) |
| vtx << " v_compareValue = a_compareValue;\n"; |
| vtx << " v_value0 = a_value0;\n"; |
| vtx << " v_value1 = a_value1;\n"; |
| |
| frag << " gl_FragColor = res;\n"; // Put result to color variable. |
| } |
| |
| vtx << "}\n"; |
| frag << "}\n"; |
| |
| m_vertShaderSource = vtx.str(); |
| m_fragShaderSource = frag.str(); |
| |
| if (isAttributeCase) |
| { |
| if (!isConditionMixed) |
| { |
| // Every execution takes the same branch. |
| |
| float value = isConditionTrue ? +1.0f : -1.0f; |
| m_attributes.push_back(AttribSpec("a_compareValue", Vec4(value, 0.0f, 0.0f, 0.0f), |
| Vec4(value, 0.0f, 0.0f, 0.0f), |
| Vec4(value, 0.0f, 0.0f, 0.0f), |
| Vec4(value, 0.0f, 0.0f, 0.0f))); |
| } |
| else if (isVertexCase) |
| { |
| // Vertex case, not every execution takes the same branch. |
| |
| const int numComponents = 4; |
| int numVertices = (getGridWidth() + 1) * (getGridHeight() + 1); |
| |
| // setupProgram() will later bind this array as an attribute. |
| m_comparisonValueArray.resize(numVertices * numComponents); |
| |
| // Make every second vertex take the true branch, and every second the false branch. |
| for (int i = 0; i < (int)m_comparisonValueArray.size(); i++) |
| { |
| if (i % numComponents == 0) |
| m_comparisonValueArray[i] = (i / numComponents) % 2 == 0 ? +1.0f : -1.0f; |
| else |
| m_comparisonValueArray[i] = 0.0f; |
| } |
| } |
| else // isConditionMixed && !isVertexCase |
| { |
| // Fragment case, not every execution takes the same branch. |
| // \note fract(a_compareValue) < 0.5 will be true for every second column of fragments. |
| |
| float minValue = 0.0f; |
| float maxValue = (float)getViewportWidth()*0.5f; |
| m_attributes.push_back(AttribSpec("a_compareValue", Vec4(minValue, 0.0f, 0.0f, 0.0f), |
| Vec4(maxValue, 0.0f, 0.0f, 0.0f), |
| Vec4(minValue, 0.0f, 0.0f, 0.0f), |
| Vec4(maxValue, 0.0f, 0.0f, 0.0f))); |
| } |
| } |
| |
| m_attributes.push_back(AttribSpec("a_value0", Vec4(0.0f, 0.1f, 0.2f, 0.3f), |
| Vec4(0.4f, 0.5f, 0.6f, 0.7f), |
| Vec4(0.8f, 0.9f, 1.0f, 1.1f), |
| Vec4(1.2f, 1.3f, 1.4f, 1.5f))); |
| |
| m_attributes.push_back(AttribSpec("a_value1", Vec4(0.0f, 0.1f, 0.2f, 0.3f), |
| Vec4(0.4f, 0.5f, 0.6f, 0.7f), |
| Vec4(0.8f, 0.9f, 1.0f, 1.1f), |
| Vec4(1.2f, 1.3f, 1.4f, 1.5f))); |
| |
| ControlStatementCase::init(); |
| } |
| |
| void ConditionalCase::setupProgram (deUint32 program) |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| |
| if (m_decisionType == DECISION_UNIFORM) |
| { |
| int location = gl.getUniformLocation(program, "u_compareValue"); |
| gl.uniform1f(location, m_branchType == BRANCH_TRUE ? +1.0f : -1.0f); |
| } |
| else if (m_decisionType == DECISION_ATTRIBUTE && m_branchType == BRANCH_MIXED && m_caseType == CASETYPE_VERTEX) |
| { |
| // Setup per-vertex comparison values calculated in init(). |
| |
| const int numComponents = 4; |
| int compareAttribLocation = gl.getAttribLocation(program, "a_compareValue"); |
| |
| DE_ASSERT((int)m_comparisonValueArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1)); |
| |
| gl.genBuffers(1, &m_arrayBuffer); |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer); |
| gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_comparisonValueArray.size()*sizeof(float)), &m_comparisonValueArray[0], GL_STATIC_DRAW); |
| gl.enableVertexAttribArray(compareAttribLocation); |
| gl.vertexAttribPointer(compareAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state"); |
| } |
| |
| ConditionalCase::~ConditionalCase (void) |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| |
| if (m_arrayBuffer != 0) |
| { |
| gl.deleteBuffers(1, &m_arrayBuffer); |
| m_arrayBuffer = 0; |
| } |
| } |
| |
| void ConditionalCase::deinit (void) |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| |
| m_comparisonValueArray.clear(); |
| |
| if (m_arrayBuffer != 0) |
| { |
| gl.deleteBuffers(1, &m_arrayBuffer); |
| m_arrayBuffer = 0; |
| } |
| |
| ShaderPerformanceCase::deinit(); |
| } |
| |
| class LoopCase : public ControlStatementCase |
| { |
| public: |
| enum LoopType |
| { |
| LOOP_FOR = 0, |
| LOOP_WHILE, |
| LOOP_DO_WHILE, |
| |
| LOOP_LAST |
| }; |
| LoopCase (Context& context, const char* name, const char* description, LoopType type, DecisionType decisionType, bool isLoopBoundStable, bool isVertex); |
| ~LoopCase (void); |
| |
| void init (void); |
| void deinit (void); |
| void setupProgram (deUint32 program); |
| |
| private: |
| DecisionType m_decisionType; |
| LoopType m_type; |
| |
| bool m_isLoopBoundStable; // Whether loop bound is same in all executions. |
| vector<float> m_boundArray; // Will contain per-vertex loop bounds if using non-stable attribute in vertex case. |
| deUint32 m_arrayBuffer; |
| }; |
| |
| LoopCase::LoopCase (Context& context, const char* name, const char* description, LoopType type, DecisionType decisionType, bool isLoopBoundStable, bool isVertex) |
| : ControlStatementCase (context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT) |
| , m_decisionType (decisionType) |
| , m_type (type) |
| , m_isLoopBoundStable (isLoopBoundStable) |
| , m_arrayBuffer (0) |
| { |
| } |
| |
| void LoopCase::init (void) |
| { |
| bool isVertexCase = m_caseType == CASETYPE_VERTEX; |
| |
| bool isStaticCase = m_decisionType == DECISION_STATIC; |
| bool isUniformCase = m_decisionType == DECISION_UNIFORM; |
| bool isAttributeCase = m_decisionType == DECISION_ATTRIBUTE; |
| |
| DE_ASSERT(isStaticCase || isUniformCase || isAttributeCase); |
| |
| DE_ASSERT(m_type == LOOP_FOR || |
| m_type == LOOP_WHILE || |
| m_type == LOOP_DO_WHILE); |
| |
| DE_ASSERT(isAttributeCase || m_isLoopBoundStable); // The loop bound count can vary between executions only if using attribute input. |
| |
| // \note The fractional part is .5 (instead of .0) so that these can be safely used as loop bounds. |
| const float loopBound = 10.5f; |
| const float unstableBoundLow = 5.5f; |
| const float unstableBoundHigh = 15.5f; |
| static const char* loopBoundStr = "10.5"; |
| static const char* unstableBoundLowStr = "5.5"; |
| static const char* unstableBoundHighStr = "15.5"; |
| |
| const char* boundValueStr = isStaticCase ? loopBoundStr : |
| isUniformCase ? "u_bound" : |
| isVertexCase ? "a_bound" : |
| m_isLoopBoundStable ? "v_bound" : |
| "loopBound"; |
| |
| std::ostringstream vtx; |
| std::ostringstream frag; |
| std::ostringstream& op = isVertexCase ? vtx : frag; |
| |
| vtx << "attribute highp vec4 a_position;\n"; // Position attribute. |
| vtx << "attribute mediump vec4 a_value;\n"; // Input for workload calculations. |
| |
| // Value to be used as the loop iteration count. |
| if (isAttributeCase) |
| vtx << "attribute mediump float a_bound;\n"; |
| else if (isUniformCase) |
| op << "uniform mediump float u_bound;\n"; |
| |
| // Varyings. |
| if (isVertexCase) |
| { |
| vtx << "varying mediump vec4 v_color;\n"; |
| frag << "varying mediump vec4 v_color;\n"; |
| } |
| else |
| { |
| vtx << "varying mediump vec4 v_value;\n"; |
| frag << "varying mediump vec4 v_value;\n"; |
| |
| if (isAttributeCase) |
| { |
| vtx << "varying mediump float v_bound;\n"; |
| frag << "varying mediump float v_bound;\n"; |
| } |
| } |
| |
| vtx << "\n"; |
| vtx << "void main()\n"; |
| vtx << "{\n"; |
| vtx << " gl_Position = a_position;\n"; |
| |
| frag << "\n"; |
| frag << "void main()\n"; |
| frag << "{\n"; |
| |
| op << " mediump vec4 res = vec4(0.0);\n"; |
| |
| if (!m_isLoopBoundStable && !isVertexCase) |
| { |
| // Choose the actual loop bound based on v_bound. |
| // \note Loop bound will vary with high frequency between fragment columns, given appropriate range for v_bound. |
| op << " mediump float loopBound = fract(v_bound) < 0.5 ? " << unstableBoundLowStr << " : " << unstableBoundHighStr << ";\n"; |
| } |
| |
| // Start a for, while or do-while loop. |
| if (m_type == LOOP_FOR) |
| op << " for (mediump float i = 0.0; i < " << boundValueStr << "; i++)\n"; |
| else |
| { |
| op << " mediump float i = 0.0;\n"; |
| if (m_type == LOOP_WHILE) |
| op << " while (i < " << boundValueStr << ")\n"; |
| else // LOOP_DO_WHILE |
| op << " do\n"; |
| } |
| op << " {\n"; |
| |
| // Workload calculations inside the loop. |
| op << "\t\t"; |
| writeLoopWorkload(op, "res", isVertexCase ? "a_value" : "v_value"); |
| op << "\n"; |
| |
| // Only "for" has counter increment in the loop head. |
| if (m_type != LOOP_FOR) |
| op << " i++;\n"; |
| |
| // End the loop. |
| if (m_type == LOOP_DO_WHILE) |
| op << " } while (i < " << boundValueStr << ");\n"; |
| else |
| op << " }\n"; |
| |
| if (isVertexCase) |
| { |
| // Put result to color variable. |
| vtx << " v_color = res;\n"; |
| frag << " gl_FragColor = v_color;\n"; |
| } |
| else |
| { |
| // Transfer inputs to fragment shader through varyings. |
| if (isAttributeCase) |
| vtx << " v_bound = a_bound;\n"; |
| vtx << " v_value = a_value;\n"; |
| |
| frag << " gl_FragColor = res;\n"; // Put result to color variable. |
| } |
| |
| vtx << "}\n"; |
| frag << "}\n"; |
| |
| m_vertShaderSource = vtx.str(); |
| m_fragShaderSource = frag.str(); |
| |
| if (isAttributeCase) |
| { |
| if (m_isLoopBoundStable) |
| { |
| // Every execution has same number of iterations. |
| |
| m_attributes.push_back(AttribSpec("a_bound", Vec4(loopBound, 0.0f, 0.0f, 0.0f), |
| Vec4(loopBound, 0.0f, 0.0f, 0.0f), |
| Vec4(loopBound, 0.0f, 0.0f, 0.0f), |
| Vec4(loopBound, 0.0f, 0.0f, 0.0f))); |
| } |
| else if (isVertexCase) |
| { |
| // Vertex case, with non-constant number of iterations. |
| |
| const int numComponents = 4; |
| int numVertices = (getGridWidth() + 1) * (getGridHeight() + 1); |
| |
| // setupProgram() will later bind this array as an attribute. |
| m_boundArray.resize(numVertices * numComponents); |
| |
| // Vary between low and high loop bounds; they should average to loopBound however. |
| for (int i = 0; i < (int)m_boundArray.size(); i++) |
| { |
| if (i % numComponents == 0) |
| m_boundArray[i] = (i / numComponents) % 2 == 0 ? unstableBoundLow : unstableBoundHigh; |
| else |
| m_boundArray[i] = 0.0f; |
| } |
| } |
| else // !m_isLoopBoundStable && !isVertexCase |
| { |
| // Fragment case, with non-constant number of iterations. |
| // \note fract(a_bound) < 0.5 will be true for every second fragment. |
| |
| float minValue = 0.0f; |
| float maxValue = (float)getViewportWidth()*0.5f; |
| m_attributes.push_back(AttribSpec("a_bound", Vec4(minValue, 0.0f, 0.0f, 0.0f), |
| Vec4(maxValue, 0.0f, 0.0f, 0.0f), |
| Vec4(minValue, 0.0f, 0.0f, 0.0f), |
| Vec4(maxValue, 0.0f, 0.0f, 0.0f))); |
| } |
| } |
| |
| m_attributes.push_back(AttribSpec("a_value", Vec4(0.0f, 0.1f, 0.2f, 0.3f), |
| Vec4(0.4f, 0.5f, 0.6f, 0.7f), |
| Vec4(0.8f, 0.9f, 1.0f, 1.1f), |
| Vec4(1.2f, 1.3f, 1.4f, 1.5f))); |
| |
| ControlStatementCase::init(); |
| } |
| |
| void LoopCase::setupProgram (deUint32 program) |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| |
| if (m_decisionType == DECISION_UNIFORM) |
| { |
| const float loopBound = 10.5f; |
| |
| int location = gl.getUniformLocation(program, "u_bound"); |
| gl.uniform1f(location, loopBound); |
| } |
| else if (m_decisionType == DECISION_ATTRIBUTE && !m_isLoopBoundStable && m_caseType == CASETYPE_VERTEX) |
| { |
| // Setup per-vertex loop bounds calculated in init(). |
| |
| const int numComponents = 4; |
| int boundAttribLocation = gl.getAttribLocation(program, "a_bound"); |
| |
| DE_ASSERT((int)m_boundArray.size() == numComponents * (getGridWidth() + 1) * (getGridHeight() + 1)); |
| |
| gl.genBuffers(1, &m_arrayBuffer); |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuffer); |
| gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(m_boundArray.size()*sizeof(float)), &m_boundArray[0], GL_STATIC_DRAW); |
| gl.enableVertexAttribArray(boundAttribLocation); |
| gl.vertexAttribPointer(boundAttribLocation, (GLint)numComponents, GL_FLOAT, GL_FALSE, 0, DE_NULL); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Setup program state"); |
| } |
| |
| LoopCase::~LoopCase (void) |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| |
| if (m_arrayBuffer) |
| { |
| gl.deleteBuffers(1, &m_arrayBuffer); |
| m_arrayBuffer = 0; |
| } |
| } |
| |
| void LoopCase::deinit (void) |
| { |
| const glw::Functions& gl = m_renderCtx.getFunctions(); |
| |
| m_boundArray.clear(); |
| |
| if (m_arrayBuffer) |
| { |
| gl.deleteBuffers(1, &m_arrayBuffer); |
| m_arrayBuffer = 0; |
| } |
| |
| ShaderPerformanceCase::deinit(); |
| } |
| |
| // A reference case, same calculations as the actual tests but without control statements. |
| class WorkloadReferenceCase : public ControlStatementCase |
| { |
| public: |
| WorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex); |
| |
| void init (void); |
| |
| protected: |
| virtual void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const = 0; |
| }; |
| |
| WorkloadReferenceCase::WorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex) |
| : ControlStatementCase(context.getTestContext(), context.getRenderContext(), name, description, isVertex ? CASETYPE_VERTEX : CASETYPE_FRAGMENT) |
| { |
| } |
| |
| void WorkloadReferenceCase::init (void) |
| { |
| bool isVertexCase = m_caseType == CASETYPE_VERTEX; |
| |
| std::ostringstream vtx; |
| std::ostringstream frag; |
| std::ostringstream& op = isVertexCase ? vtx : frag; |
| |
| vtx << "attribute highp vec4 a_position;\n"; // Position attribute. |
| vtx << "attribute mediump vec4 a_value;\n"; // Value for workload calculations. |
| |
| // Varyings. |
| if (isVertexCase) |
| { |
| vtx << "varying mediump vec4 v_color;\n"; |
| frag << "varying mediump vec4 v_color;\n"; |
| } |
| else |
| { |
| vtx << "varying mediump vec4 v_value;\n"; |
| frag << "varying mediump vec4 v_value;\n"; |
| } |
| |
| vtx << "\n"; |
| vtx << "void main()\n"; |
| vtx << "{\n"; |
| vtx << " gl_Position = a_position;\n"; |
| |
| frag << "\n"; |
| frag << "void main()\n"; |
| frag << "{\n"; |
| |
| op << "\tmediump vec4 res;\n"; |
| writeWorkload(op, "res", isVertexCase ? "a_value" : "v_value"); |
| |
| if (isVertexCase) |
| { |
| // Put result to color variable. |
| vtx << " v_color = res;\n"; |
| frag << " gl_FragColor = v_color;\n"; |
| } |
| else |
| { |
| vtx << " v_value = a_value;\n"; // Transfer input to fragment shader through varying. |
| frag << " gl_FragColor = res;\n"; // Put result to color variable. |
| } |
| |
| vtx << "}\n"; |
| frag << "}\n"; |
| |
| m_vertShaderSource = vtx.str(); |
| m_fragShaderSource = frag.str(); |
| |
| m_attributes.push_back(AttribSpec("a_value", Vec4(0.0f, 0.1f, 0.2f, 0.3f), |
| Vec4(0.4f, 0.5f, 0.6f, 0.7f), |
| Vec4(0.8f, 0.9f, 1.0f, 1.1f), |
| Vec4(1.2f, 1.3f, 1.4f, 1.5f))); |
| |
| ControlStatementCase::init(); |
| } |
| |
| class LoopWorkloadReferenceCase : public WorkloadReferenceCase |
| { |
| public: |
| LoopWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isAttributeStable, bool isVertex) |
| : WorkloadReferenceCase (context, name, description, isVertex) |
| , m_isAttributeStable (isAttributeStable) |
| { |
| } |
| |
| protected: |
| void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const; |
| |
| private: |
| bool m_isAttributeStable; |
| }; |
| |
| void LoopWorkloadReferenceCase::writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const |
| { |
| const int loopIterations = 11; |
| bool isVertexCase = m_caseType == CASETYPE_VERTEX; |
| |
| dst << "\t" << resultVariableName << " = vec4(0.0);\n"; |
| |
| for (int i = 0; i < loopIterations; i++) |
| { |
| dst << "\t"; |
| writeLoopWorkload(dst, resultVariableName, inputVariableName); |
| dst << "\n"; |
| } |
| |
| if (!isVertexCase && !m_isAttributeStable) |
| { |
| // Corresponds to the fract() done in a real test's fragment case with non-stable attribute. |
| dst << " res.x = fract(res.x);\n"; |
| } |
| } |
| |
| class ConditionalWorkloadReferenceCase : public WorkloadReferenceCase |
| { |
| public: |
| ConditionalWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isAttributeStable, bool isVertex) |
| : WorkloadReferenceCase (context, name, description, isVertex) |
| , m_isAttributeStable (isAttributeStable) |
| { |
| } |
| |
| protected: |
| void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const; |
| |
| private: |
| bool m_isAttributeStable; |
| }; |
| |
| void ConditionalWorkloadReferenceCase::writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const |
| { |
| bool isVertexCase = m_caseType == CASETYPE_VERTEX; |
| |
| dst << "\t"; |
| writeConditionalWorkload(dst, resultVariableName, inputVariableName); |
| dst << "\n"; |
| |
| if (!isVertexCase && !m_isAttributeStable) |
| { |
| // Corresponds to the fract() done in a real test's fragment case with non-stable attribute. |
| dst << " res.x = fract(res.x);\n"; |
| } |
| } |
| |
| // A workload reference case for e.g. a conditional case with a branch with no computation. |
| class EmptyWorkloadReferenceCase : public WorkloadReferenceCase |
| { |
| public: |
| EmptyWorkloadReferenceCase (Context& context, const char* name, const char* description, bool isVertex) |
| : WorkloadReferenceCase (context, name, description, isVertex) |
| { |
| } |
| |
| protected: |
| void writeWorkload (std::ostringstream& dst, const char* resultVariableName, const char* inputVariableName) const |
| { |
| dst << "\t" << resultVariableName << " = " << inputVariableName << ";\n"; |
| } |
| }; |
| |
| ShaderControlStatementTests::ShaderControlStatementTests (Context& context) |
| : TestCaseGroup(context, "control_statement", "Control Statement Performance Tests") |
| { |
| } |
| |
| ShaderControlStatementTests::~ShaderControlStatementTests (void) |
| { |
| } |
| |
| void ShaderControlStatementTests::init (void) |
| { |
| // Conditional cases (if-else). |
| |
| tcu::TestCaseGroup* ifElseGroup = new tcu::TestCaseGroup(m_testCtx, "if_else", "if-else Conditional Performance Tests"); |
| addChild(ifElseGroup); |
| |
| for (int isFrag = 0; isFrag <= 1; isFrag++) |
| { |
| bool isVertex = isFrag == 0; |
| ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", ""); |
| ifElseGroup->addChild(vertexOrFragmentGroup); |
| |
| DE_STATIC_ASSERT(DECISION_STATIC == 0); |
| for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++) |
| { |
| const char* decisionName = decisionType == (int)DECISION_STATIC ? "static" : |
| decisionType == (int)DECISION_UNIFORM ? "uniform" : |
| decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") : |
| DE_NULL; |
| DE_ASSERT(decisionName != DE_NULL); |
| |
| for (int workloadDivision = 0; workloadDivision < ConditionalCase::WORKLOAD_DIVISION_LAST; workloadDivision++) |
| { |
| const char* workloadDivisionSuffix = workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_EVEN ? "" : |
| workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_TRUE_HEAVY ? "_with_heavier_true" : |
| workloadDivision == (int)ConditionalCase::WORKLOAD_DIVISION_FALSE_HEAVY ? "_with_heavier_false" : |
| DE_NULL; |
| DE_ASSERT(workloadDivisionSuffix != DE_NULL); |
| |
| DE_STATIC_ASSERT(ConditionalCase::BRANCH_TRUE == 0); |
| for (int branchResult = (int)ConditionalCase::BRANCH_TRUE; branchResult < (int)ConditionalCase::BRANCH_LAST; branchResult++) |
| { |
| if (decisionType != (int)DECISION_ATTRIBUTE && branchResult == (int)ConditionalCase::BRANCH_MIXED) |
| continue; |
| |
| const char* branchResultName = branchResult == (int)ConditionalCase::BRANCH_TRUE ? "true" : |
| branchResult == (int)ConditionalCase::BRANCH_FALSE ? "false" : |
| branchResult == (int)ConditionalCase::BRANCH_MIXED ? "mixed" : |
| DE_NULL; |
| DE_ASSERT(branchResultName != DE_NULL); |
| |
| string caseName = string("") + decisionName + "_" + branchResultName + workloadDivisionSuffix; |
| |
| vertexOrFragmentGroup->addChild(new ConditionalCase(m_context, caseName.c_str(), "", |
| (DecisionType)decisionType, (ConditionalCase::BranchResult)branchResult, |
| (ConditionalCase::WorkloadDivision)workloadDivision, isVertex)); |
| } |
| } |
| } |
| |
| if (isVertex) |
| vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference", "", true, isVertex)); |
| else |
| { |
| // Only fragment case with BRANCH_MIXED has an additional fract() call. |
| vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_unmixed", "", true, isVertex)); |
| vertexOrFragmentGroup->addChild(new ConditionalWorkloadReferenceCase(m_context, "reference_mixed", "", false, isVertex)); |
| } |
| |
| vertexOrFragmentGroup->addChild(new EmptyWorkloadReferenceCase(m_context, "reference_empty", "", isVertex)); |
| } |
| |
| // Loop cases. |
| |
| static const struct |
| { |
| LoopCase::LoopType type; |
| const char* name; |
| const char* description; |
| } loopGroups[] = |
| { |
| {LoopCase::LOOP_FOR, "for", "for Loop Performance Tests"}, |
| {LoopCase::LOOP_WHILE, "while", "while Loop Performance Tests"}, |
| {LoopCase::LOOP_DO_WHILE, "do_while", "do-while Loop Performance Tests"} |
| }; |
| |
| for (int groupNdx = 0; groupNdx < DE_LENGTH_OF_ARRAY(loopGroups); groupNdx++) |
| { |
| tcu::TestCaseGroup* currentLoopGroup = new tcu::TestCaseGroup(m_testCtx, loopGroups[groupNdx].name, loopGroups[groupNdx].description); |
| addChild(currentLoopGroup); |
| |
| for (int isFrag = 0; isFrag <= 1; isFrag++) |
| { |
| bool isVertex = isFrag == 0; |
| ShaderPerformanceCaseGroup* vertexOrFragmentGroup = new ShaderPerformanceCaseGroup(m_testCtx, isVertex ? "vertex" : "fragment", ""); |
| currentLoopGroup->addChild(vertexOrFragmentGroup); |
| |
| DE_STATIC_ASSERT(DECISION_STATIC == 0); |
| for (int decisionType = (int)DECISION_STATIC; decisionType < (int)DECISION_LAST; decisionType++) |
| { |
| const char* decisionName = decisionType == (int)DECISION_STATIC ? "static" : |
| decisionType == (int)DECISION_UNIFORM ? "uniform" : |
| decisionType == (int)DECISION_ATTRIBUTE ? (isVertex ? "attribute" : "varying") : |
| DE_NULL; |
| DE_ASSERT(decisionName != DE_NULL); |
| |
| if (decisionType == (int)DECISION_ATTRIBUTE) |
| { |
| vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_stable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex)); |
| vertexOrFragmentGroup->addChild(new LoopCase(m_context, (string(decisionName) + "_unstable").c_str(), "", loopGroups[groupNdx].type, (DecisionType)decisionType, false, isVertex)); |
| } |
| else |
| vertexOrFragmentGroup->addChild(new LoopCase(m_context, decisionName, "", loopGroups[groupNdx].type, (DecisionType)decisionType, true, isVertex)); |
| |
| } |
| |
| if (isVertex) |
| vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference", "", true, isVertex)); |
| else |
| { |
| // Only fragment case with unstable attribute has an additional fract() call. |
| vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_stable", "", true, isVertex)); |
| vertexOrFragmentGroup->addChild(new LoopWorkloadReferenceCase(m_context, "reference_unstable", "", false, isVertex)); |
| } |
| } |
| } |
| } |
| |
| } // Performance |
| } // gles2 |
| } // deqp |