1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL (ES) Module 3 * ----------------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 *//*! 20 * \file 21 * \brief Shader performance measurer; handles calibration and measurement 22 *//*--------------------------------------------------------------------*/ 23 24 #include "glsShaderPerformanceMeasurer.hpp" 25 #include "gluDefs.hpp" 26 #include "tcuTestLog.hpp" 27 #include "tcuRenderTarget.hpp" 28 #include "deStringUtil.hpp" 29 #include "deMath.h" 30 #include "deClock.h" 31 32 #include "glwFunctions.hpp" 33 #include "glwEnums.hpp" 34 35 #include <algorithm> 36 37 using tcu::Vec4; 38 using std::string; 39 using std::vector; 40 using tcu::TestLog; 41 using namespace glw; // GL types 42 43 namespace deqp 44 { 45 namespace gls 46 { 47 48 static inline float triangleInterpolate (float v0, float v1, float v2, float x, float y) 49 { 50 return v0 + (v2-v0)*x + (v1-v0)*y; 51 } 52 53 static inline float triQuadInterpolate (float x, float y, const tcu::Vec4& quad) 54 { 55 // \note Top left fill rule. 56 if (x + y < 1.0f) 57 return triangleInterpolate(quad.x(), quad.y(), quad.z(), x, y); 58 else 59 return triangleInterpolate(quad.w(), quad.z(), quad.y(), 1.0f-x, 1.0f-y); 60 } 61 62 static inline int getNumVertices (int gridSizeX, int gridSizeY) 63 { 64 return (gridSizeX + 1) * (gridSizeY + 1); 65 } 66 67 static inline int getNumIndices (int gridSizeX, int gridSizeY) 68 { 69 return gridSizeX*gridSizeY*6; 70 } 71 72 static inline deUint16 getVtxIndex (int x, int y, int gridSizeX) 73 { 74 return (deUint16)(y*(gridSizeX+1) + x); 75 } 76 77 static void generateVertices (std::vector<float>& dst, int gridSizeX, int gridSizeY, const AttribSpec& spec) 78 { 79 const int numComponents = 4; 80 81 DE_ASSERT((gridSizeX + 1)*(gridSizeY + 1) <= (1<<16)); // Must fit into 16-bit indices. 82 DE_ASSERT(gridSizeX >= 1 && gridSizeY >= 1); 83 dst.resize((gridSizeX + 1) * (gridSizeY + 1) * 4); 84 85 for (int y = 0; y <= gridSizeY; y++) 86 { 87 for (int x = 0; x <= gridSizeX; x++) 88 { 89 float xf = (float)x / (float)gridSizeX; 90 float yf = (float)y / (float)gridSizeY; 91 92 for (int compNdx = 0; compNdx < numComponents; compNdx++) 93 dst[getVtxIndex(x, y, gridSizeX)*numComponents + compNdx] = triQuadInterpolate(xf, yf, tcu::Vec4(spec.p00[compNdx], spec.p01[compNdx], spec.p10[compNdx], spec.p11[compNdx])); 94 } 95 } 96 } 97 98 static void generateIndices (std::vector<deUint16>& dst, int gridSizeX, int gridSizeY) 99 { 100 const int numIndicesPerQuad = 6; 101 int numIndices = gridSizeX * gridSizeY * numIndicesPerQuad; 102 dst.resize(numIndices); 103 104 for (int y = 0; y < gridSizeY; y++) 105 { 106 for (int x = 0; x < gridSizeX; x++) 107 { 108 int quadNdx = y*gridSizeX + x; 109 110 dst[quadNdx*numIndicesPerQuad + 0] = getVtxIndex(x+0, y+0, gridSizeX); 111 dst[quadNdx*numIndicesPerQuad + 1] = getVtxIndex(x+1, y+0, gridSizeX); 112 dst[quadNdx*numIndicesPerQuad + 2] = getVtxIndex(x+0, y+1, gridSizeX); 113 114 dst[quadNdx*numIndicesPerQuad + 3] = getVtxIndex(x+0, y+1, gridSizeX); 115 dst[quadNdx*numIndicesPerQuad + 4] = getVtxIndex(x+1, y+0, gridSizeX); 116 dst[quadNdx*numIndicesPerQuad + 5] = getVtxIndex(x+1, y+1, gridSizeX); 117 } 118 } 119 } 120 121 ShaderPerformanceMeasurer::ShaderPerformanceMeasurer (const glu::RenderContext& renderCtx, PerfCaseType measureType) 122 : m_renderCtx (renderCtx) 123 , m_gridSizeX (measureType == CASETYPE_FRAGMENT ? 1 : 255) 124 , m_gridSizeY (measureType == CASETYPE_FRAGMENT ? 1 : 255) 125 , m_viewportWidth (measureType == CASETYPE_VERTEX ? 32 : renderCtx.getRenderTarget().getWidth()) 126 , m_viewportHeight (measureType == CASETYPE_VERTEX ? 32 : renderCtx.getRenderTarget().getHeight()) 127 , m_state (STATE_UNINITIALIZED) 128 , m_result (-1.0f, -1.0f) 129 , m_indexBuffer (0) 130 , m_vao (0) 131 { 132 } 133 134 void ShaderPerformanceMeasurer::logParameters (TestLog& log) const 135 { 136 log << TestLog::Message << "Grid size: " << m_gridSizeX << "x" << m_gridSizeY << TestLog::EndMessage 137 << TestLog::Message << "Viewport: " << m_viewportWidth << "x" << m_viewportHeight << TestLog::EndMessage; 138 } 139 140 void ShaderPerformanceMeasurer::init (deUint32 program, const vector<AttribSpec>& attributes, int calibratorInitialNumCalls) 141 { 142 DE_ASSERT(m_state == STATE_UNINITIALIZED); 143 144 const glw::Functions& gl = m_renderCtx.getFunctions(); 145 const bool useVAO = glu::isContextTypeGLCore(m_renderCtx.getType()); 146 147 if (useVAO) 148 { 149 DE_ASSERT(!m_vao); 150 gl.genVertexArrays(1, &m_vao); 151 gl.bindVertexArray(m_vao); 152 GLU_EXPECT_NO_ERROR(gl.getError(), "Create VAO"); 153 } 154 155 // Validate that we have sane grid and viewport setup. 156 157 DE_ASSERT(de::inBounds(m_gridSizeX, 1, 256) && de::inBounds(m_gridSizeY, 1, 256)); 158 159 { 160 bool widthTooSmall = m_renderCtx.getRenderTarget().getWidth() < m_viewportWidth; 161 bool heightTooSmall = m_renderCtx.getRenderTarget().getHeight() < m_viewportHeight; 162 163 if (widthTooSmall || heightTooSmall) 164 throw tcu::NotSupportedError("Render target too small (" + 165 (widthTooSmall ? "width must be at least " + de::toString(m_viewportWidth) : "") + 166 (heightTooSmall ? string(widthTooSmall ? ", " : "") + "height must be at least " + de::toString(m_viewportHeight) : "") + 167 ")"); 168 } 169 170 TCU_CHECK_INTERNAL(de::inRange(m_viewportWidth, 1, m_renderCtx.getRenderTarget().getWidth()) && 171 de::inRange(m_viewportHeight, 1, m_renderCtx.getRenderTarget().getHeight())); 172 173 // Insert a_position to attributes. 174 m_attributes = attributes; 175 m_attributes.push_back(AttribSpec("a_position", 176 Vec4(-1.0f, -1.0f, 0.0f, 1.0f), 177 Vec4( 1.0f, -1.0f, 0.0f, 1.0f), 178 Vec4(-1.0f, 1.0f, 0.0f, 1.0f), 179 Vec4( 1.0f, 1.0f, 0.0f, 1.0f))); 180 181 // Generate indices. 182 { 183 std::vector<deUint16> indices; 184 generateIndices(indices, m_gridSizeX, m_gridSizeY); 185 186 gl.genBuffers(1, &m_indexBuffer); 187 gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer); 188 gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(indices.size()*sizeof(deUint16)), &indices[0], GL_STATIC_DRAW); 189 190 GLU_EXPECT_NO_ERROR(gl.getError(), "Upload index data"); 191 } 192 193 // Generate vertices. 194 m_attribBuffers.resize(m_attributes.size(), 0); 195 gl.genBuffers((GLsizei)m_attribBuffers.size(), &m_attribBuffers[0]); 196 197 for (int attribNdx = 0; attribNdx < (int)m_attributes.size(); attribNdx++) 198 { 199 std::vector<float> vertices; 200 generateVertices(vertices, m_gridSizeX, m_gridSizeY, m_attributes[attribNdx]); 201 202 gl.bindBuffer(GL_ARRAY_BUFFER, m_attribBuffers[attribNdx]); 203 gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(vertices.size()*sizeof(float)), &vertices[0], GL_STATIC_DRAW); 204 } 205 206 GLU_EXPECT_NO_ERROR(gl.getError(), "Upload vertex data"); 207 208 // Setup attribute bindings. 209 for (int attribNdx = 0; attribNdx < (int)m_attributes.size(); attribNdx++) 210 { 211 int location = gl.getAttribLocation(program, m_attributes[attribNdx].name.c_str()); 212 213 if (location >= 0) 214 { 215 gl.enableVertexAttribArray(location); 216 gl.bindBuffer(GL_ARRAY_BUFFER, m_attribBuffers[attribNdx]); 217 gl.vertexAttribPointer(location, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); 218 } 219 220 GLU_EXPECT_NO_ERROR(gl.getError(), "Setup vertex attribute state"); 221 } 222 223 gl.useProgram(program); 224 GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram()"); 225 226 m_state = STATE_MEASURING; 227 m_isFirstIteration = true; 228 229 m_calibrator.clear(CalibratorParameters(calibratorInitialNumCalls, 10 /* calibrate iteration frames */, 2000.0f /* calibrate iteration shortcut threshold (ms) */, 16 /* max calibrate iterations */, 230 1000.0f/30.0f /* frame time (ms) */, 1000.0f/60.0f /* frame time cap (ms) */, 1000.0f /* target measure duration (ms) */)); 231 } 232 233 void ShaderPerformanceMeasurer::deinit (void) 234 { 235 const glw::Functions& gl = m_renderCtx.getFunctions(); 236 237 if (m_indexBuffer) 238 { 239 gl.deleteBuffers(1, &m_indexBuffer); 240 m_indexBuffer = 0; 241 } 242 243 if (m_vao) 244 { 245 gl.deleteVertexArrays(1, &m_vao); 246 m_vao = 0; 247 } 248 249 if (!m_attribBuffers.empty()) 250 { 251 gl.deleteBuffers((GLsizei)m_attribBuffers.size(), &m_attribBuffers[0]); 252 m_attribBuffers.clear(); 253 } 254 255 m_state = STATE_UNINITIALIZED; 256 } 257 258 void ShaderPerformanceMeasurer::render (int numDrawCalls) 259 { 260 const glw::Functions& gl = m_renderCtx.getFunctions(); 261 GLsizei numIndices = (GLsizei)getNumIndices(m_gridSizeX, m_gridSizeY); 262 263 gl.viewport(0, 0, m_viewportWidth, m_viewportHeight); 264 265 for (int callNdx = 0; callNdx < numDrawCalls; callNdx++) 266 gl.drawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, DE_NULL); 267 } 268 269 void ShaderPerformanceMeasurer::iterate (void) 270 { 271 DE_ASSERT(m_state == STATE_MEASURING); 272 273 deUint64 renderStartTime = deGetMicroseconds(); 274 render(m_calibrator.getCallCount()); // Always render. This gives more stable performance behavior. 275 276 TheilSenCalibrator::State calibratorState = m_calibrator.getState(); 277 278 if (calibratorState == TheilSenCalibrator::STATE_RECOMPUTE_PARAMS) 279 { 280 m_calibrator.recomputeParameters(); 281 282 m_isFirstIteration = true; 283 m_prevRenderStartTime = renderStartTime; 284 } 285 else if (calibratorState == TheilSenCalibrator::STATE_MEASURE) 286 { 287 if (!m_isFirstIteration) 288 m_calibrator.recordIteration(renderStartTime - m_prevRenderStartTime); 289 290 m_isFirstIteration = false; 291 m_prevRenderStartTime = renderStartTime; 292 } 293 else 294 { 295 DE_ASSERT(calibratorState == TheilSenCalibrator::STATE_FINISHED); 296 297 GLU_EXPECT_NO_ERROR(m_renderCtx.getFunctions().getError(), "End of rendering"); 298 299 const MeasureState& measureState = m_calibrator.getMeasureState(); 300 301 // Compute result. 302 deUint64 totalTime = measureState.getTotalTime(); 303 int numFrames = (int)measureState.frameTimes.size(); 304 deInt64 numQuadGrids = measureState.numDrawCalls * numFrames; 305 deInt64 numPixels = (deInt64)m_viewportWidth * (deInt64)m_viewportHeight * numQuadGrids; 306 deInt64 numVertices = (deInt64)getNumVertices(m_gridSizeX, m_gridSizeY) * numQuadGrids; 307 double mfragPerSecond = (double)numPixels / (double)totalTime; 308 double mvertPerSecond = (double)numVertices / (double)totalTime; 309 310 m_result = Result((float)mvertPerSecond, (float)mfragPerSecond); 311 m_state = STATE_FINISHED; 312 } 313 } 314 315 void ShaderPerformanceMeasurer::logMeasurementInfo (TestLog& log) const 316 { 317 DE_ASSERT(m_state == STATE_FINISHED); 318 319 const MeasureState& measureState(m_calibrator.getMeasureState()); 320 321 // Compute totals. 322 deUint64 totalTime = measureState.getTotalTime(); 323 int numFrames = (int)measureState.frameTimes.size(); 324 deInt64 numQuadGrids = measureState.numDrawCalls * numFrames; 325 deInt64 numPixels = (deInt64)m_viewportWidth * (deInt64)m_viewportHeight * numQuadGrids; 326 deInt64 numVertices = (deInt64)getNumVertices(m_gridSizeX, m_gridSizeY) * numQuadGrids; 327 double mfragPerSecond = (double)numPixels / (double)totalTime; 328 double mvertPerSecond = (double)numVertices / (double)totalTime; 329 double framesPerSecond = (double)numFrames / ((double)totalTime / 1000000.0); 330 331 logCalibrationInfo(log, m_calibrator); 332 333 log << TestLog::Float("FramesPerSecond", "Frames per second in measurement", "Frames/s", QP_KEY_TAG_PERFORMANCE, (float)framesPerSecond) 334 << TestLog::Float("FragmentsPerVertices", "Vertex-fragment ratio", "Fragments/Vertices", QP_KEY_TAG_NONE, (float)numPixels / (float)numVertices) 335 << TestLog::Float("FragmentPerf", "Fragment performance", "MPix/s", QP_KEY_TAG_PERFORMANCE, (float)mfragPerSecond) 336 << TestLog::Float("VertexPerf", "Vertex performance", "MVert/s", QP_KEY_TAG_PERFORMANCE, (float)mvertPerSecond); 337 } 338 339 void ShaderPerformanceMeasurer::setGridSize (int gridW, int gridH) 340 { 341 DE_ASSERT(m_state == STATE_UNINITIALIZED); 342 DE_ASSERT(de::inBounds(gridW, 1, 256) && de::inBounds(gridH, 1, 256)); 343 m_gridSizeX = gridW; 344 m_gridSizeY = gridH; 345 } 346 347 void ShaderPerformanceMeasurer::setViewportSize (int width, int height) 348 { 349 DE_ASSERT(m_state == STATE_UNINITIALIZED); 350 DE_ASSERT(de::inRange(width, 1, m_renderCtx.getRenderTarget().getWidth()) && 351 de::inRange(height, 1, m_renderCtx.getRenderTarget().getHeight())); 352 m_viewportWidth = width; 353 m_viewportHeight = height; 354 } 355 356 } // gls 357 } // deqp 358