| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL (ES) 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 State change performance tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "glsStateChangePerfTestCases.hpp" |
| |
| #include "tcuTestLog.hpp" |
| |
| #include "gluDefs.hpp" |
| #include "gluRenderContext.hpp" |
| #include "gluShaderProgram.hpp" |
| |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| |
| #include "deStringUtil.hpp" |
| |
| #include "deClock.h" |
| |
| #include <vector> |
| #include <algorithm> |
| |
| using std::string; |
| using std::vector; |
| using tcu::TestLog; |
| using namespace glw; |
| |
| namespace deqp |
| { |
| namespace gls |
| { |
| |
| namespace |
| { |
| |
| struct ResultStats |
| { |
| double median; |
| double mean; |
| double variance; |
| |
| uint64_t min; |
| uint64_t max; |
| }; |
| |
| ResultStats calculateStats(const vector<uint64_t> &values) |
| { |
| ResultStats result = {0.0, 0.0, 0.0, 0xFFFFFFFFFFFFFFFFu, 0}; |
| |
| uint64_t sum = 0; |
| |
| for (int i = 0; i < (int)values.size(); i++) |
| sum += values[i]; |
| |
| result.mean = ((double)sum) / (double)values.size(); |
| |
| for (int i = 0; i < (int)values.size(); i++) |
| { |
| const double val = (double)values[i]; |
| result.variance += (val - result.mean) * (val - result.mean); |
| } |
| |
| result.variance /= (double)values.size(); |
| |
| { |
| const int n = (int)(values.size() / 2); |
| |
| vector<uint64_t> sortedValues = values; |
| |
| std::sort(sortedValues.begin(), sortedValues.end()); |
| |
| result.median = (double)sortedValues[n]; |
| } |
| |
| for (int i = 0; i < (int)values.size(); i++) |
| { |
| result.min = std::min(result.min, values[i]); |
| result.max = std::max(result.max, values[i]); |
| } |
| |
| return result; |
| } |
| |
| void genIndices(vector<GLushort> &indices, int triangleCount) |
| { |
| indices.reserve(triangleCount * 3); |
| |
| for (int triangleNdx = 0; triangleNdx < triangleCount; triangleNdx++) |
| { |
| indices.push_back((GLushort)(triangleNdx * 3)); |
| indices.push_back((GLushort)(triangleNdx * 3 + 1)); |
| indices.push_back((GLushort)(triangleNdx * 3 + 2)); |
| } |
| } |
| |
| void genCoords(vector<GLfloat> &coords, int triangleCount) |
| { |
| coords.reserve(triangleCount * 3 * 2); |
| |
| for (int triangleNdx = 0; triangleNdx < triangleCount; triangleNdx++) |
| { |
| if ((triangleNdx % 2) == 0) |
| { |
| // CW |
| coords.push_back(-1.0f); |
| coords.push_back(-1.0f); |
| |
| coords.push_back(1.0f); |
| coords.push_back(-1.0f); |
| |
| coords.push_back(1.0f); |
| coords.push_back(1.0f); |
| } |
| else |
| { |
| // CCW |
| coords.push_back(-1.0f); |
| coords.push_back(-1.0f); |
| |
| coords.push_back(-1.0f); |
| coords.push_back(1.0f); |
| |
| coords.push_back(1.0f); |
| coords.push_back(1.0f); |
| } |
| } |
| } |
| |
| void genTextureData(vector<uint8_t> &data, int width, int height) |
| { |
| data.clear(); |
| data.reserve(width * height * 4); |
| |
| for (int x = 0; x < width; x++) |
| { |
| for (int y = 0; y < height; y++) |
| { |
| data.push_back((uint8_t)((255 * x) / width)); |
| data.push_back((uint8_t)((255 * y) / width)); |
| data.push_back((uint8_t)((255 * x * y) / (width * height))); |
| data.push_back(255); |
| } |
| } |
| } |
| |
| double calculateVariance(const vector<uint64_t> &values, double avg) |
| { |
| double sum = 0.0; |
| |
| for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++) |
| { |
| double value = (double)values[valueNdx]; |
| sum += (value - avg) * (value - avg); |
| } |
| |
| return sum / (double)values.size(); |
| } |
| |
| uint64_t findMin(const vector<uint64_t> &values) |
| { |
| uint64_t min = ~0ull; |
| |
| for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++) |
| min = std::min(values[valueNdx], min); |
| |
| return min; |
| } |
| |
| uint64_t findMax(const vector<uint64_t> &values) |
| { |
| uint64_t max = 0; |
| |
| for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++) |
| max = std::max(values[valueNdx], max); |
| |
| return max; |
| } |
| |
| uint64_t findMedian(const vector<uint64_t> &v) |
| { |
| vector<uint64_t> values = v; |
| size_t n = values.size() / 2; |
| |
| std::nth_element(values.begin(), values.begin() + n, values.end()); |
| |
| return values[n]; |
| } |
| |
| } // namespace |
| |
| StateChangePerformanceCase::StateChangePerformanceCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, |
| const char *name, const char *description, DrawType drawType, |
| int drawCallCount, int triangleCount) |
| : tcu::TestCase(testCtx, tcu::NODETYPE_PERFORMANCE, name, description) |
| , m_renderCtx(renderCtx) |
| , m_drawType(drawType) |
| , m_iterationCount(100) |
| , m_callCount(drawCallCount) |
| , m_triangleCount(triangleCount) |
| { |
| } |
| |
| StateChangePerformanceCase::~StateChangePerformanceCase(void) |
| { |
| StateChangePerformanceCase::deinit(); |
| } |
| |
| void StateChangePerformanceCase::init(void) |
| { |
| if (m_drawType == DRAWTYPE_INDEXED_USER_PTR) |
| genIndices(m_indices, m_triangleCount); |
| } |
| |
| void StateChangePerformanceCase::requireIndexBuffers(int count) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| |
| if ((int)m_indexBuffers.size() >= count) |
| return; |
| |
| m_indexBuffers.reserve(count); |
| |
| vector<GLushort> indices; |
| genIndices(indices, m_triangleCount); |
| |
| while ((int)m_indexBuffers.size() < count) |
| { |
| GLuint buffer; |
| |
| gl.genBuffers(1, &buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers()"); |
| |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()"); |
| gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(indices.size() * sizeof(GLushort)), &(indices[0]), |
| GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData()"); |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()"); |
| |
| m_indexBuffers.push_back(buffer); |
| } |
| } |
| |
| void StateChangePerformanceCase::requireCoordBuffers(int count) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| |
| if ((int)m_coordBuffers.size() >= count) |
| return; |
| |
| m_coordBuffers.reserve(count); |
| |
| vector<GLfloat> coords; |
| genCoords(coords, m_triangleCount); |
| |
| while ((int)m_coordBuffers.size() < count) |
| { |
| GLuint buffer; |
| |
| gl.genBuffers(1, &buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers()"); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, buffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()"); |
| gl.bufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(coords.size() * sizeof(GLfloat)), &(coords[0]), GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData()"); |
| gl.bindBuffer(GL_ARRAY_BUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer()"); |
| |
| m_coordBuffers.push_back(buffer); |
| } |
| } |
| |
| void StateChangePerformanceCase::requirePrograms(int count) |
| { |
| if ((int)m_programs.size() >= count) |
| return; |
| |
| m_programs.reserve(count); |
| |
| while ((int)m_programs.size() < count) |
| { |
| string vertexShaderSource = "attribute mediump vec2 a_coord;\n" |
| "varying mediump vec2 v_texCoord;\n" |
| "void main (void)\n" |
| "{\n" |
| "\tv_texCoord = vec2(0.5) + 0.5" + |
| de::toString(m_programs.size()) + |
| " * a_coord.xy;\n" |
| "\tgl_Position = vec4(a_coord, 0.5, 1.0);\n" |
| "}"; |
| |
| string fragmentShaderSource = "uniform sampler2D u_sampler;\n" |
| "varying mediump vec2 v_texCoord;\n" |
| "void main (void)\n" |
| "{\n" |
| "\tgl_FragColor = vec4(1.0" + |
| de::toString(m_programs.size()) + |
| " * texture2D(u_sampler, v_texCoord).xyz, 1.0);\n" |
| "}"; |
| |
| glu::ShaderProgram *program = |
| new glu::ShaderProgram(m_renderCtx, glu::ProgramSources() << glu::VertexSource(vertexShaderSource) |
| << glu::FragmentSource(fragmentShaderSource)); |
| |
| if (!program->isOk()) |
| { |
| m_testCtx.getLog() << *program; |
| delete program; |
| TCU_FAIL("Compile failed"); |
| } |
| |
| m_programs.push_back(program); |
| } |
| } |
| |
| void StateChangePerformanceCase::requireTextures(int count) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| |
| const int textureWidth = 64; |
| const int textureHeight = 64; |
| |
| if ((int)m_textures.size() >= count) |
| return; |
| |
| m_textures.reserve(count); |
| |
| vector<uint8_t> textureData; |
| genTextureData(textureData, textureWidth, textureHeight); |
| |
| DE_ASSERT(textureData.size() == textureWidth * textureHeight * 4); |
| |
| while ((int)m_textures.size() < count) |
| { |
| GLuint texture; |
| |
| gl.genTextures(1, &texture); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures()"); |
| |
| gl.bindTexture(GL_TEXTURE_2D, texture); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture()"); |
| |
| gl.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, |
| &(textureData[0])); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexImage2D()"); |
| |
| gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri()"); |
| gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri()"); |
| gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri()"); |
| gl.texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexParameteri()"); |
| |
| gl.bindTexture(GL_TEXTURE_2D, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture()"); |
| |
| m_textures.push_back(texture); |
| } |
| } |
| |
| void StateChangePerformanceCase::requireFramebuffers(int count) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| |
| if ((int)m_framebuffers.size() >= count) |
| return; |
| |
| m_framebuffers.reserve(count); |
| |
| requireRenderbuffers(count); |
| |
| while ((int)m_framebuffers.size() < count) |
| { |
| GLuint framebuffer; |
| |
| gl.genFramebuffers(1, &framebuffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers()"); |
| |
| gl.bindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer()"); |
| |
| gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffers[m_framebuffers.size()]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer()"); |
| |
| gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, |
| m_renderbuffers[m_framebuffers.size()]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer()"); |
| |
| gl.bindRenderbuffer(GL_RENDERBUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer()"); |
| |
| gl.bindFramebuffer(GL_FRAMEBUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer()"); |
| |
| m_framebuffers.push_back(framebuffer); |
| } |
| } |
| |
| void StateChangePerformanceCase::requireRenderbuffers(int count) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| |
| if ((int)m_renderbuffers.size() >= count) |
| return; |
| |
| m_renderbuffers.reserve(count); |
| |
| while ((int)m_renderbuffers.size() < count) |
| { |
| GLuint renderbuffer; |
| |
| gl.genRenderbuffers(1, &renderbuffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers()"); |
| |
| gl.bindRenderbuffer(GL_RENDERBUFFER, renderbuffer); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer()"); |
| |
| gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGB565, 24, 24); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage()"); |
| |
| gl.bindRenderbuffer(GL_RENDERBUFFER, 0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer()"); |
| |
| m_renderbuffers.push_back(renderbuffer); |
| } |
| } |
| |
| void StateChangePerformanceCase::requireSamplers(int count) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| |
| if ((int)m_samplers.size() >= count) |
| return; |
| |
| m_samplers.reserve(count); |
| |
| while ((int)m_samplers.size() < count) |
| { |
| GLuint sampler; |
| gl.genSamplers(1, &sampler); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenSamplers()"); |
| m_samplers.push_back(sampler); |
| } |
| } |
| |
| void StateChangePerformanceCase::requireVertexArrays(int count) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| |
| if ((int)m_vertexArrays.size() >= count) |
| return; |
| |
| m_vertexArrays.reserve(count); |
| |
| while ((int)m_vertexArrays.size() < count) |
| { |
| GLuint vertexArray; |
| gl.genVertexArrays(1, &vertexArray); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays()"); |
| m_vertexArrays.push_back(vertexArray); |
| } |
| } |
| |
| void StateChangePerformanceCase::deinit(void) |
| { |
| m_indices.clear(); |
| m_interleavedResults.clear(); |
| m_batchedResults.clear(); |
| |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| |
| if (!m_indexBuffers.empty()) |
| { |
| gl.deleteBuffers((GLsizei)m_indexBuffers.size(), &(m_indexBuffers[0])); |
| m_indexBuffers.clear(); |
| } |
| |
| if (!m_coordBuffers.empty()) |
| { |
| gl.deleteBuffers((GLsizei)m_coordBuffers.size(), &(m_coordBuffers[0])); |
| m_coordBuffers.clear(); |
| } |
| |
| if (!m_textures.empty()) |
| { |
| gl.deleteTextures((GLsizei)m_textures.size(), &(m_textures[0])); |
| m_textures.clear(); |
| } |
| |
| if (!m_framebuffers.empty()) |
| { |
| gl.deleteFramebuffers((GLsizei)m_framebuffers.size(), &(m_framebuffers[0])); |
| m_framebuffers.clear(); |
| } |
| |
| if (!m_renderbuffers.empty()) |
| { |
| gl.deleteRenderbuffers((GLsizei)m_renderbuffers.size(), &(m_renderbuffers[0])); |
| m_renderbuffers.clear(); |
| } |
| |
| if (!m_samplers.empty()) |
| { |
| gl.deleteSamplers((GLsizei)m_samplers.size(), &m_samplers[0]); |
| m_samplers.clear(); |
| } |
| |
| if (!m_vertexArrays.empty()) |
| { |
| gl.deleteVertexArrays((GLsizei)m_vertexArrays.size(), &m_vertexArrays[0]); |
| m_vertexArrays.clear(); |
| } |
| |
| for (int programNdx = 0; programNdx < (int)m_programs.size(); programNdx++) |
| { |
| delete m_programs[programNdx]; |
| m_programs[programNdx] = NULL; |
| } |
| m_programs.clear(); |
| } |
| } |
| |
| void StateChangePerformanceCase::logAndSetTestResult(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| |
| ResultStats interleaved = calculateStats(m_interleavedResults); |
| ResultStats batched = calculateStats(m_batchedResults); |
| |
| log << TestLog::Message << "Interleaved mean: " << interleaved.mean << TestLog::EndMessage; |
| log << TestLog::Message << "Interleaved median: " << interleaved.median << TestLog::EndMessage; |
| log << TestLog::Message << "Interleaved variance: " << interleaved.variance << TestLog::EndMessage; |
| log << TestLog::Message << "Interleaved min: " << interleaved.min << TestLog::EndMessage; |
| log << TestLog::Message << "Interleaved max: " << interleaved.max << TestLog::EndMessage; |
| |
| log << TestLog::Message << "Batched mean: " << batched.mean << TestLog::EndMessage; |
| log << TestLog::Message << "Batched median: " << batched.median << TestLog::EndMessage; |
| log << TestLog::Message << "Batched variance: " << batched.variance << TestLog::EndMessage; |
| log << TestLog::Message << "Batched min: " << batched.min << TestLog::EndMessage; |
| log << TestLog::Message << "Batched max: " << batched.max << TestLog::EndMessage; |
| |
| log << TestLog::Message << "Batched/Interleaved mean ratio: " << (interleaved.mean / batched.mean) |
| << TestLog::EndMessage; |
| log << TestLog::Message << "Batched/Interleaved median ratio: " << (interleaved.median / batched.median) |
| << TestLog::EndMessage; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, |
| de::floatToString((float)(((double)interleaved.median) / batched.median), 2).c_str()); |
| } |
| |
| tcu::TestCase::IterateResult StateChangePerformanceCase::iterate(void) |
| { |
| if (m_interleavedResults.empty() && m_batchedResults.empty()) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| |
| log << TestLog::Message << "Draw call count: " << m_callCount << TestLog::EndMessage; |
| log << TestLog::Message << "Per call triangle count: " << m_triangleCount << TestLog::EndMessage; |
| } |
| |
| // \note [mika] Interleave sampling to balance effects of powerstate etc. |
| if ((int)m_interleavedResults.size() < m_iterationCount && m_batchedResults.size() >= m_interleavedResults.size()) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| uint64_t resBeginUs = 0; |
| uint64_t resEndUs = 0; |
| |
| setupInitialState(gl); |
| gl.finish(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()"); |
| |
| // Render result |
| resBeginUs = deGetMicroseconds(); |
| |
| renderTest(gl); |
| |
| gl.finish(); |
| resEndUs = deGetMicroseconds(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()"); |
| |
| m_interleavedResults.push_back(resEndUs - resBeginUs); |
| |
| return CONTINUE; |
| } |
| else if ((int)m_batchedResults.size() < m_iterationCount) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| uint64_t refBeginUs = 0; |
| uint64_t refEndUs = 0; |
| |
| setupInitialState(gl); |
| gl.finish(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()"); |
| |
| // Render reference |
| refBeginUs = deGetMicroseconds(); |
| |
| renderReference(gl); |
| |
| gl.finish(); |
| refEndUs = deGetMicroseconds(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFinish()"); |
| |
| m_batchedResults.push_back(refEndUs - refBeginUs); |
| |
| return CONTINUE; |
| } |
| else |
| { |
| logAndSetTestResult(); |
| return STOP; |
| } |
| } |
| |
| void StateChangePerformanceCase::callDraw(const glw::Functions &gl) |
| { |
| switch (m_drawType) |
| { |
| case DRAWTYPE_NOT_INDEXED: |
| gl.drawArrays(GL_TRIANGLES, 0, m_triangleCount * 3); |
| break; |
| case DRAWTYPE_INDEXED_USER_PTR: |
| gl.drawElements(GL_TRIANGLES, m_triangleCount * 3, GL_UNSIGNED_SHORT, &m_indices[0]); |
| break; |
| case DRAWTYPE_INDEXED_BUFFER: |
| gl.drawElements(GL_TRIANGLES, m_triangleCount * 3, GL_UNSIGNED_SHORT, NULL); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| |
| // StateChangeCallPerformanceCase |
| |
| StateChangeCallPerformanceCase::StateChangeCallPerformanceCase(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, |
| const char *name, const char *description) |
| : tcu::TestCase(testCtx, tcu::NODETYPE_PERFORMANCE, name, description) |
| , m_renderCtx(renderCtx) |
| , m_iterationCount(100) |
| , m_callCount(1000) |
| { |
| } |
| |
| StateChangeCallPerformanceCase::~StateChangeCallPerformanceCase(void) |
| { |
| } |
| |
| void StateChangeCallPerformanceCase::executeTest(void) |
| { |
| const glw::Functions &gl = m_renderCtx.getFunctions(); |
| uint64_t beginTimeUs = 0; |
| uint64_t endTimeUs = 0; |
| |
| beginTimeUs = deGetMicroseconds(); |
| |
| execCalls(gl, (int)m_results.size(), m_callCount); |
| |
| endTimeUs = deGetMicroseconds(); |
| |
| m_results.push_back(endTimeUs - beginTimeUs); |
| } |
| |
| void StateChangeCallPerformanceCase::logTestCase(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| |
| log << TestLog::Message << "Iteration count: " << m_iterationCount << TestLog::EndMessage; |
| log << TestLog::Message << "Per iteration call count: " << m_callCount << TestLog::EndMessage; |
| } |
| |
| double calculateAverage(const vector<uint64_t> &values) |
| { |
| uint64_t sum = 0; |
| |
| for (int valueNdx = 0; valueNdx < (int)values.size(); valueNdx++) |
| sum += values[valueNdx]; |
| |
| return ((double)sum) / (double)values.size(); |
| } |
| |
| void StateChangeCallPerformanceCase::logAndSetTestResult(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| |
| uint64_t minUs = findMin(m_results); |
| uint64_t maxUs = findMax(m_results); |
| uint64_t medianUs = findMedian(m_results); |
| double avgIterationUs = calculateAverage(m_results); |
| double avgCallUs = avgIterationUs / m_callCount; |
| double varIteration = calculateVariance(m_results, avgIterationUs); |
| double avgMedianCallUs = ((double)medianUs) / m_callCount; |
| |
| log << TestLog::Message << "Min iteration time: " << minUs << "us" << TestLog::EndMessage; |
| log << TestLog::Message << "Max iteration time: " << maxUs << "us" << TestLog::EndMessage; |
| log << TestLog::Message << "Average iteration time: " << avgIterationUs << "us" << TestLog::EndMessage; |
| log << TestLog::Message << "Iteration variance time: " << varIteration << TestLog::EndMessage; |
| log << TestLog::Message << "Median iteration time: " << medianUs << "us" << TestLog::EndMessage; |
| log << TestLog::Message << "Average call time: " << avgCallUs << "us" << TestLog::EndMessage; |
| log << TestLog::Message << "Average call time for median iteration: " << avgMedianCallUs << "us" |
| << TestLog::EndMessage; |
| |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString((float)avgMedianCallUs, 3).c_str()); |
| } |
| |
| tcu::TestCase::IterateResult StateChangeCallPerformanceCase::iterate(void) |
| { |
| if (m_results.empty()) |
| logTestCase(); |
| |
| if ((int)m_results.size() < m_iterationCount) |
| { |
| executeTest(); |
| GLU_EXPECT_NO_ERROR(m_renderCtx.getFunctions().getError(), "Unexpected error"); |
| return CONTINUE; |
| } |
| else |
| { |
| logAndSetTestResult(); |
| return STOP; |
| } |
| } |
| |
| } // namespace gls |
| } // namespace deqp |