/*------------------------------------------------------------------------- * drawElements Quality Program EGL 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 eglMakeCurrent performance tests. *//*--------------------------------------------------------------------*/ #include "teglMakeCurrentPerfTests.hpp" #include "egluNativeWindow.hpp" #include "egluNativePixmap.hpp" #include "egluUtil.hpp" #include "eglwLibrary.hpp" #include "eglwEnums.hpp" #include "tcuTestLog.hpp" #include "deRandom.hpp" #include "deStringUtil.hpp" #include "deClock.h" #include "deString.h" #include <algorithm> #include <cmath> #include <limits> #include <sstream> #include <string> #include <vector> using std::ostringstream; using std::string; using std::vector; using tcu::TestLog; using namespace eglw; namespace deqp { namespace egl { class MakeCurrentPerfCase : public TestCase { public: enum SurfaceType { SURFACETYPE_PBUFFER = (1<<0), SURFACETYPE_WINDOW = (1<<1), SURFACETYPE_PIXMAP = (1<<2) }; struct Spec { SurfaceType surfaceTypes; int contextCount; int surfaceCount; bool release; int iterationCount; int sampleCount; string toName (void) const; string toDescription (void) const; }; MakeCurrentPerfCase (EglTestContext& eglTestCtx, const Spec& spec, const char* name, const char* description); ~MakeCurrentPerfCase (void); void init (void); void deinit (void); IterateResult iterate (void); private: Spec m_spec; de::Random m_rnd; EGLDisplay m_display; EGLConfig m_config; vector<EGLContext> m_contexts; vector<EGLSurface> m_surfaces; vector<eglu::NativeWindow*> m_windows; vector<eglu::NativePixmap*> m_pixmaps; vector<deUint64> m_samples; void chooseConfig (void); void createSurfaces (void); void createContexts (void); void destroySurfaces (void); void destroyContexts (void); void createPBuffer (void); void createWindow (void); void createPixmap (void); void logTestInfo (void); void logResults (void); // Disabled MakeCurrentPerfCase (const MakeCurrentPerfCase&); MakeCurrentPerfCase& operator= (const MakeCurrentPerfCase&); }; string MakeCurrentPerfCase::Spec::toName (void) const { ostringstream name; name << "context"; if (contextCount > 1) name << "s_" << contextCount; if ((surfaceTypes & SURFACETYPE_WINDOW) != 0) name << "_window" << (surfaceCount > 1 ? "s" : ""); if ((surfaceTypes & SURFACETYPE_PIXMAP) != 0) name << "_pixmap" << (surfaceCount > 1 ? "s" : ""); if ((surfaceTypes & SURFACETYPE_PBUFFER) != 0) name << "_pbuffer" << (surfaceCount > 1 ? "s" : ""); if (surfaceCount > 1) name << "_" << surfaceCount; if (release) name << "_release"; return name.str(); } string MakeCurrentPerfCase::Spec::toDescription (void) const { // \todo [mika] Generate descrpition return toName(); } MakeCurrentPerfCase::MakeCurrentPerfCase (EglTestContext& eglTestCtx, const Spec& spec, const char* name, const char* description) : TestCase (eglTestCtx, tcu::NODETYPE_PERFORMANCE, name, description) , m_spec (spec) , m_rnd (deStringHash(name)) , m_display (EGL_NO_DISPLAY) , m_config (DE_NULL) { } MakeCurrentPerfCase::~MakeCurrentPerfCase (void) { deinit(); } void MakeCurrentPerfCase::init (void) { m_display = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay()); chooseConfig(); createContexts(); createSurfaces(); } void MakeCurrentPerfCase::deinit (void) { destroyContexts(); destroySurfaces(); if (m_display != EGL_NO_DISPLAY) { m_eglTestCtx.getLibrary().terminate(m_display); m_display = EGL_NO_DISPLAY; } } void MakeCurrentPerfCase::chooseConfig (void) { const EGLint surfaceBits = ((m_spec.surfaceTypes & SURFACETYPE_WINDOW) != 0 ? EGL_WINDOW_BIT : 0) | ((m_spec.surfaceTypes & SURFACETYPE_PIXMAP) != 0 ? EGL_PIXMAP_BIT : 0) | ((m_spec.surfaceTypes & SURFACETYPE_PBUFFER) != 0 ? EGL_PBUFFER_BIT : 0); const EGLint attribList[] = { EGL_SURFACE_TYPE, surfaceBits, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; const Library& egl = m_eglTestCtx.getLibrary(); EGLint configCount = 0; EGLU_CHECK_CALL(egl, chooseConfig(m_display, attribList, &m_config, 1, &configCount)); if (configCount <= 0) throw tcu::NotSupportedError("No compatible configs found"); } void MakeCurrentPerfCase::createSurfaces (void) { vector<SurfaceType> types; if ((m_spec.surfaceTypes & SURFACETYPE_WINDOW) != 0) types.push_back(SURFACETYPE_WINDOW); if ((m_spec.surfaceTypes & SURFACETYPE_PIXMAP) != 0) types.push_back(SURFACETYPE_PIXMAP); if ((m_spec.surfaceTypes & SURFACETYPE_PBUFFER) != 0) types.push_back(SURFACETYPE_PBUFFER); DE_ASSERT((int)types.size() <= m_spec.surfaceCount); // Create surfaces for (int surfaceNdx = 0; surfaceNdx < m_spec.surfaceCount; surfaceNdx++) { SurfaceType type = types[surfaceNdx % types.size()]; switch (type) { case SURFACETYPE_PBUFFER: createPBuffer(); break; case SURFACETYPE_WINDOW: createWindow(); break; case SURFACETYPE_PIXMAP: createPixmap(); break; default: DE_ASSERT(false); }; } } void MakeCurrentPerfCase::createPBuffer (void) { const Library& egl = m_eglTestCtx.getLibrary(); const EGLint width = 256; const EGLint height = 256; const EGLint attribList[] = { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE }; EGLSurface surface = egl.createPbufferSurface(m_display, m_config, attribList); EGLU_CHECK_MSG(egl, "eglCreatePbufferSurface()"); m_surfaces.push_back(surface); } void MakeCurrentPerfCase::createWindow (void) { const Library& egl = m_eglTestCtx.getLibrary(); const EGLint width = 256; const EGLint height = 256; const eglu::NativeWindowFactory& windowFactory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine()); eglu::NativeWindow* window = DE_NULL; EGLSurface surface = EGL_NO_SURFACE; try { window = windowFactory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, eglu::WindowParams(width, height, eglu::parseWindowVisibility(m_eglTestCtx.getTestContext().getCommandLine()))); surface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, m_display, m_config, DE_NULL); } catch (...) { if (surface != EGL_NO_SURFACE) egl.destroySurface(m_display, surface); delete window; throw; } m_windows.push_back(window); m_surfaces.push_back(surface); } void MakeCurrentPerfCase::createPixmap (void) { const Library& egl = m_eglTestCtx.getLibrary(); const EGLint width = 256; const EGLint height = 256; const eglu::NativePixmapFactory& pixmapFactory = eglu::selectNativePixmapFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine()); eglu::NativePixmap* pixmap = DE_NULL; EGLSurface surface = EGL_NO_SURFACE; try { pixmap = pixmapFactory.createPixmap(&m_eglTestCtx.getNativeDisplay(), m_display, m_config, DE_NULL, width, height); surface = eglu::createPixmapSurface(m_eglTestCtx.getNativeDisplay(), *pixmap, m_display, m_config, DE_NULL); } catch (...) { if (surface != EGL_NO_SURFACE) egl.destroySurface(m_display, surface); delete pixmap; throw; } m_pixmaps.push_back(pixmap); m_surfaces.push_back(surface); } void MakeCurrentPerfCase::destroySurfaces (void) { const Library& egl = m_eglTestCtx.getLibrary(); if (m_surfaces.size() > 0) { EGLDisplay display = m_display; // Destroy surfaces for (vector<EGLSurface>::iterator iter = m_surfaces.begin(); iter != m_surfaces.end(); ++iter) { if (*iter != EGL_NO_SURFACE) EGLU_CHECK_CALL(egl, destroySurface(display, *iter)); *iter = EGL_NO_SURFACE; } m_surfaces.clear(); // Destroy pixmaps for (vector<eglu::NativePixmap*>::iterator iter = m_pixmaps.begin(); iter != m_pixmaps.end(); ++iter) { delete *iter; *iter = NULL; } m_pixmaps.clear(); // Destroy windows for (vector<eglu::NativeWindow*>::iterator iter = m_windows.begin(); iter != m_windows.end(); ++iter) { delete *iter; *iter = NULL; } m_windows.clear(); // Clear all surface handles m_surfaces.clear(); } } void MakeCurrentPerfCase::createContexts (void) { const Library& egl = m_eglTestCtx.getLibrary(); for (int contextNdx = 0; contextNdx < m_spec.contextCount; contextNdx++) { const EGLint attribList[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; EGLU_CHECK_CALL(egl, bindAPI(EGL_OPENGL_ES_API)); EGLContext context = egl.createContext(m_display, m_config, EGL_NO_CONTEXT, attribList); EGLU_CHECK_MSG(egl, "eglCreateContext()"); m_contexts.push_back(context); } } void MakeCurrentPerfCase::destroyContexts (void) { const Library& egl = m_eglTestCtx.getLibrary(); if (m_contexts.size() > 0) { EGLDisplay display = m_display; for (vector<EGLContext>::iterator iter = m_contexts.begin(); iter != m_contexts.end(); ++iter) { if (*iter != EGL_NO_CONTEXT) EGLU_CHECK_CALL(egl, destroyContext(display, *iter)); *iter = EGL_NO_CONTEXT; } m_contexts.clear(); } } void MakeCurrentPerfCase::logTestInfo (void) { TestLog& log = m_testCtx.getLog(); { tcu::ScopedLogSection section(log, "Test Info", "Test case information."); log << TestLog::Message << "Context count: " << m_contexts.size() << TestLog::EndMessage; log << TestLog::Message << "Surfaces count: " << m_surfaces.size() << TestLog::EndMessage; log << TestLog::Message << "Sample count: " << m_spec.sampleCount << TestLog::EndMessage; log << TestLog::Message << "Iteration count: " << m_spec.iterationCount << TestLog::EndMessage; log << TestLog::Message << "Window count: " << m_windows.size() << TestLog::EndMessage; log << TestLog::Message << "Pixmap count: " << m_pixmaps.size() << TestLog::EndMessage; log << TestLog::Message << "PBuffer count: " << (m_surfaces.size() - m_windows.size() - m_pixmaps.size()) << TestLog::EndMessage; if (m_spec.release) log << TestLog::Message << "Context is released after each use. Both binding and releasing context are included in result time." << TestLog::EndMessage; } } void MakeCurrentPerfCase::logResults (void) { TestLog& log = m_testCtx.getLog(); log << TestLog::SampleList("Result", "Result") << TestLog::SampleInfo << TestLog::ValueInfo("Time", "Time", "us", QP_SAMPLE_VALUE_TAG_RESPONSE) << TestLog::EndSampleInfo; for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++) log << TestLog::Sample << deInt64(m_samples[sampleNdx]) << TestLog::EndSample; log << TestLog::EndSampleList; // Log stats { deUint64 totalTimeUs = 0; deUint64 totalIterationCount = 0; float iterationTimeMeanUs = 0.0f; float iterationTimeMedianUs = 0.0f; float iterationTimeVarianceUs = 0.0f; float iterationTimeSkewnessUs = 0.0f; float iterationTimeMinUs = std::numeric_limits<float>::max(); float iterationTimeMaxUs = 0.0f; std::sort(m_samples.begin(), m_samples.end()); // Calculate totals for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++) { totalTimeUs += m_samples[sampleNdx]; totalIterationCount += m_spec.iterationCount; } // Calculate mean and median iterationTimeMeanUs = ((float)(((double)totalTimeUs) / (double)totalIterationCount)); iterationTimeMedianUs = ((float)(((double)m_samples[m_samples.size() / 2]) / (double)m_spec.iterationCount)); // Calculate variance for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++) { float iterationTimeUs = (float)(((double)m_samples[sampleNdx]) / m_spec.iterationCount); iterationTimeVarianceUs += std::pow(iterationTimeUs - iterationTimeMedianUs, 2.0f); } // Calculate min and max for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++) { float iterationTimeUs = (float)(((double)m_samples[sampleNdx]) / m_spec.iterationCount); iterationTimeMinUs = std::min<float>(iterationTimeMinUs, iterationTimeUs); iterationTimeMaxUs = std::max<float>(iterationTimeMaxUs, iterationTimeUs); } iterationTimeVarianceUs /= (float)m_samples.size(); // Calculate skewness for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++) { float iterationTimeUs = (float)(((double)m_samples[sampleNdx]) / m_spec.iterationCount); iterationTimeSkewnessUs = std::pow((iterationTimeUs - iterationTimeMedianUs) / iterationTimeVarianceUs, 2.0f); } iterationTimeSkewnessUs /= (float)m_samples.size(); { tcu::ScopedLogSection section(log, "Result", "Statistics from results."); log << TestLog::Message << "Total time: " << totalTimeUs << "us" << TestLog::EndMessage; log << TestLog::Message << "Mean: " << iterationTimeMeanUs << "us" << TestLog::EndMessage; log << TestLog::Message << "Median: " << iterationTimeMedianUs << "us" << TestLog::EndMessage; log << TestLog::Message << "Variance: " << iterationTimeVarianceUs << "us" << TestLog::EndMessage; log << TestLog::Message << "Skewness: " << iterationTimeSkewnessUs << "us" << TestLog::EndMessage; log << TestLog::Message << "Min: " << iterationTimeMinUs << "us" << TestLog::EndMessage; log << TestLog::Message << "Max: " << iterationTimeMaxUs << "us" << TestLog::EndMessage; } m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString((float)(((double)totalTimeUs)/(double)totalIterationCount), 2).c_str()); } } TestCase::IterateResult MakeCurrentPerfCase::iterate (void) { const Library& egl = m_eglTestCtx.getLibrary(); if (m_samples.size() == 0) logTestInfo(); { EGLDisplay display = m_display; deUint64 beginTimeUs = deGetMicroseconds(); for (int iteration = 0; iteration < m_spec.iterationCount; iteration++) { EGLContext context = m_contexts[m_rnd.getUint32() % m_contexts.size()]; EGLSurface surface = m_surfaces[m_rnd.getUint32() % m_surfaces.size()]; egl.makeCurrent(display, surface, surface, context); if (m_spec.release) egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } m_samples.push_back(deGetMicroseconds() - beginTimeUs); egl.makeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); EGLU_CHECK_MSG(egl, "eglMakeCurrent()"); } if ((int)m_samples.size() == m_spec.sampleCount) { logResults(); return STOP; } else return CONTINUE; } MakeCurrentPerfTests::MakeCurrentPerfTests (EglTestContext& eglTestCtx) : TestCaseGroup(eglTestCtx, "make_current", "eglMakeCurrent performance tests") { } void MakeCurrentPerfTests::init (void) { const int iterationCount = 100; const int sampleCount = 100; // Add simple test group { TestCaseGroup* simple = new TestCaseGroup(m_eglTestCtx, "simple", "Simple eglMakeCurrent performance tests using single context and surface"); const MakeCurrentPerfCase::SurfaceType types[] = { MakeCurrentPerfCase::SURFACETYPE_PBUFFER, MakeCurrentPerfCase::SURFACETYPE_PIXMAP, MakeCurrentPerfCase::SURFACETYPE_WINDOW }; for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++) { for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++) { MakeCurrentPerfCase::Spec spec; spec.surfaceTypes = types[typeNdx]; spec.contextCount = 1; spec.surfaceCount = 1; spec.release = (releaseNdx == 1); spec.iterationCount = iterationCount; spec.sampleCount = sampleCount; simple->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(), spec.toDescription().c_str())); } } addChild(simple); } // Add multi context test group { TestCaseGroup* multiContext = new TestCaseGroup(m_eglTestCtx, "multi_context", "eglMakeCurrent performance tests using multiple contexts and single surface"); const MakeCurrentPerfCase::SurfaceType types[] = { MakeCurrentPerfCase::SURFACETYPE_PBUFFER, MakeCurrentPerfCase::SURFACETYPE_PIXMAP, MakeCurrentPerfCase::SURFACETYPE_WINDOW }; const int contextCounts[] = { 10, 100 }; for (int contextCountNdx = 0; contextCountNdx < DE_LENGTH_OF_ARRAY(contextCounts); contextCountNdx++) { for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++) { for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++) { MakeCurrentPerfCase::Spec spec; spec.surfaceTypes = types[typeNdx]; spec.contextCount = contextCounts[contextCountNdx]; spec.surfaceCount = 1; spec.release = (releaseNdx == 1); spec.iterationCount = iterationCount; spec.sampleCount = sampleCount; multiContext->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(), spec.toDescription().c_str())); } } } addChild(multiContext); } // Add multi surface test group { TestCaseGroup* multiSurface = new TestCaseGroup(m_eglTestCtx, "multi_surface", "eglMakeCurrent performance tests using single context and multiple surfaces"); const MakeCurrentPerfCase::SurfaceType types[] = { MakeCurrentPerfCase::SURFACETYPE_PBUFFER, MakeCurrentPerfCase::SURFACETYPE_PIXMAP, MakeCurrentPerfCase::SURFACETYPE_WINDOW, (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |MakeCurrentPerfCase::SURFACETYPE_PIXMAP), (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |MakeCurrentPerfCase::SURFACETYPE_WINDOW), (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PIXMAP |MakeCurrentPerfCase::SURFACETYPE_WINDOW), (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER|MakeCurrentPerfCase::SURFACETYPE_PIXMAP|MakeCurrentPerfCase::SURFACETYPE_WINDOW) }; const int surfaceCounts[] = { 10, 100 }; for (int surfaceCountNdx = 0; surfaceCountNdx < DE_LENGTH_OF_ARRAY(surfaceCounts); surfaceCountNdx++) { for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++) { for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++) { MakeCurrentPerfCase::Spec spec; spec.surfaceTypes = types[typeNdx]; spec.surfaceCount = surfaceCounts[surfaceCountNdx]; spec.contextCount = 1; spec.release = (releaseNdx == 1); spec.iterationCount = iterationCount; spec.sampleCount = sampleCount; multiSurface->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(), spec.toDescription().c_str())); } } } addChild(multiSurface); } // Add Complex? test group { TestCaseGroup* multi = new TestCaseGroup(m_eglTestCtx, "complex", "eglMakeCurrent performance tests using multiple contexts and multiple surfaces"); const MakeCurrentPerfCase::SurfaceType types[] = { MakeCurrentPerfCase::SURFACETYPE_PBUFFER, MakeCurrentPerfCase::SURFACETYPE_PIXMAP, MakeCurrentPerfCase::SURFACETYPE_WINDOW, (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |MakeCurrentPerfCase::SURFACETYPE_PIXMAP), (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER |MakeCurrentPerfCase::SURFACETYPE_WINDOW), (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PIXMAP |MakeCurrentPerfCase::SURFACETYPE_WINDOW), (MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER|MakeCurrentPerfCase::SURFACETYPE_PIXMAP|MakeCurrentPerfCase::SURFACETYPE_WINDOW) }; const int surfaceCounts[] = { 10, 100 }; const int contextCounts[] = { 10, 100 }; for (int surfaceCountNdx = 0; surfaceCountNdx < DE_LENGTH_OF_ARRAY(surfaceCounts); surfaceCountNdx++) { for (int contextCountNdx = 0; contextCountNdx < DE_LENGTH_OF_ARRAY(contextCounts); contextCountNdx++) { for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++) { for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++) { MakeCurrentPerfCase::Spec spec; spec.surfaceTypes = types[typeNdx]; spec.contextCount = contextCounts[contextCountNdx]; spec.surfaceCount = surfaceCounts[surfaceCountNdx]; spec.release = (releaseNdx == 1); spec.iterationCount = iterationCount; spec.sampleCount = sampleCount; multi->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(), spec.toDescription().c_str())); } } } } addChild(multi); } } } // egl } // deqp