diff options
author | 2016-10-04 12:33:44 -0700 | |
---|---|---|
committer | 2016-10-04 12:33:44 -0700 | |
commit | c06720d06faab3b01eba1b8693e0ac791f06dc96 (patch) | |
tree | 65e68119ef1d128aeb4bff876c8ae814306c3a81 /tools/gpu | |
parent | b3f543d955637c6e10d7109554b5a46c6e25291a (diff) |
skpbench: add option for gpu timing
Adds a gpu timing option with a GL implementation.
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2388433003
Review-Url: https://codereview.chromium.org/2388433003
Diffstat (limited to 'tools/gpu')
-rw-r--r-- | tools/gpu/FenceSync.h | 4 | ||||
-rw-r--r-- | tools/gpu/GpuTimer.h | 77 | ||||
-rw-r--r-- | tools/gpu/TestContext.cpp | 9 | ||||
-rw-r--r-- | tools/gpu/TestContext.h | 9 | ||||
-rw-r--r-- | tools/gpu/gl/GLTestContext.cpp | 130 |
5 files changed, 225 insertions, 4 deletions
diff --git a/tools/gpu/FenceSync.h b/tools/gpu/FenceSync.h index c3f1182621..90ef1daec7 100644 --- a/tools/gpu/FenceSync.h +++ b/tools/gpu/FenceSync.h @@ -13,7 +13,7 @@ namespace sk_gpu_test { using PlatformFence = intptr_t; -static constexpr PlatformFence kInvalidPlatformFence = 0; +static constexpr PlatformFence kInvalidFence = 0; /* * This class provides an interface to interact with fence syncs. A fence sync is an object that the @@ -29,6 +29,6 @@ public: virtual ~FenceSync() {} }; -} +} // namespace sk_gpu_test #endif diff --git a/tools/gpu/GpuTimer.h b/tools/gpu/GpuTimer.h new file mode 100644 index 0000000000..d9e320e0a0 --- /dev/null +++ b/tools/gpu/GpuTimer.h @@ -0,0 +1,77 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GpuTimer_DEFINED +#define GpuTimer_DEFINED + +#include "SkTypes.h" +#include "SkExchange.h" +#include <chrono> + +namespace sk_gpu_test { + +using PlatformTimerQuery = intptr_t; +static constexpr PlatformTimerQuery kInvalidTimerQuery = 0; + +/** + * Platform-independent interface for timing operations on the GPU. + */ +class GpuTimer { +public: + GpuTimer(bool disjointSupport) + : fDisjointSupport(disjointSupport) + , fActiveTimer(kInvalidTimerQuery) { + } + virtual ~GpuTimer() { SkASSERT(!fActiveTimer); } + + /** + * Returns whether this timer can detect disjoint GPU operations while timing. If false, a query + * has less confidence when it completes with QueryStatus::kAccurate. + */ + bool disjointSupport() const { return fDisjointSupport; } + + /** + * Inserts a "start timing" command in the GPU command stream. + */ + void queueStart() { + SkASSERT(!fActiveTimer); + fActiveTimer = this->onQueueTimerStart(); + } + + /** + * Inserts a "stop timing" command in the GPU command stream. + * + * @return a query object that can retrieve the time elapsed once the timer has completed. + */ + PlatformTimerQuery SK_WARN_UNUSED_RESULT queueStop() { + SkASSERT(fActiveTimer); + this->onQueueTimerStop(fActiveTimer); + return skstd::exchange(fActiveTimer, kInvalidTimerQuery); + } + + enum class QueryStatus { + kInvalid, //<! the timer query is invalid. + kPending, //<! the timer is still running on the GPU. + kDisjoint, //<! the query is complete, but dubious due to disjoint GPU operations. + kAccurate //<! the query is complete and reliable. + }; + + virtual QueryStatus checkQueryStatus(PlatformTimerQuery) = 0; + virtual std::chrono::nanoseconds getTimeElapsed(PlatformTimerQuery) = 0; + virtual void deleteQuery(PlatformTimerQuery) = 0; + +private: + virtual PlatformTimerQuery onQueueTimerStart() const = 0; + virtual void onQueueTimerStop(PlatformTimerQuery) const = 0; + + bool const fDisjointSupport; + PlatformTimerQuery fActiveTimer; +}; + +} // namespace sk_gpu_test + +#endif diff --git a/tools/gpu/TestContext.cpp b/tools/gpu/TestContext.cpp index 8a78b903b1..0857024d06 100644 --- a/tools/gpu/TestContext.cpp +++ b/tools/gpu/TestContext.cpp @@ -8,8 +8,13 @@ #include "TestContext.h" +#include "GpuTimer.h" + namespace sk_gpu_test { -TestContext::TestContext() : fFenceSync(nullptr), fCurrentFenceIdx(0) { +TestContext::TestContext() + : fFenceSync(nullptr) + , fGpuTimer(nullptr) + , fCurrentFenceIdx(0) { memset(fFrameFences, 0, sizeof(fFrameFences)); } @@ -21,6 +26,7 @@ TestContext::~TestContext() { } #endif SkASSERT(!fFenceSync); + SkASSERT(!fGpuTimer); } void TestContext::makeCurrent() const { this->onPlatformMakeCurrent(); } @@ -63,6 +69,7 @@ void TestContext::teardown() { delete fFenceSync; fFenceSync = nullptr; } + delete fGpuTimer; } } diff --git a/tools/gpu/TestContext.h b/tools/gpu/TestContext.h index d01cb02af2..d39b7446c6 100644 --- a/tools/gpu/TestContext.h +++ b/tools/gpu/TestContext.h @@ -14,6 +14,9 @@ #include "../private/SkTemplates.h" namespace sk_gpu_test { + +class GpuTimer; + /** * An offscreen 3D context. This class is intended for Skia's internal testing needs and not * for general use. @@ -27,6 +30,9 @@ public: bool fenceSyncSupport() const { return fFenceSync != nullptr; } FenceSync* fenceSync() { SkASSERT(fFenceSync); return fFenceSync; } + bool gpuTimingSupport() const { return fGpuTimer != nullptr; } + GpuTimer* gpuTimer() const { SkASSERT(fGpuTimer); return fGpuTimer; } + bool getMaxGpuFrameLag(int *maxFrameLag) const { if (!fFenceSync) { return false; @@ -75,7 +81,8 @@ public: virtual void finish() = 0; protected: - FenceSync* fFenceSync; + FenceSync* fFenceSync; + GpuTimer* fGpuTimer; TestContext(); diff --git a/tools/gpu/gl/GLTestContext.cpp b/tools/gpu/gl/GLTestContext.cpp index 1d53bbcd83..dc8966b296 100644 --- a/tools/gpu/gl/GLTestContext.cpp +++ b/tools/gpu/gl/GLTestContext.cpp @@ -6,6 +6,8 @@ */ #include "GLTestContext.h" + +#include "GpuTimer.h" #include "gl/GrGLUtil.h" namespace { @@ -77,6 +79,133 @@ void GLFenceSync::deleteFence(sk_gpu_test::PlatformFence fence) const { fGLDeleteSync(glsync); } +class GLGpuTimer : public sk_gpu_test::GpuTimer { +public: + static GLGpuTimer* CreateIfSupported(const sk_gpu_test::GLTestContext*); + + QueryStatus checkQueryStatus(sk_gpu_test::PlatformTimerQuery) override; + std::chrono::nanoseconds getTimeElapsed(sk_gpu_test::PlatformTimerQuery) override; + void deleteQuery(sk_gpu_test::PlatformTimerQuery) override; + +private: + GLGpuTimer(bool disjointSupport, const sk_gpu_test::GLTestContext*, const char* ext = ""); + + bool validate() const; + + sk_gpu_test::PlatformTimerQuery onQueueTimerStart() const override; + void onQueueTimerStop(sk_gpu_test::PlatformTimerQuery) const override; + + static constexpr GrGLenum GL_QUERY_RESULT = 0x8866; + static constexpr GrGLenum GL_QUERY_RESULT_AVAILABLE = 0x8867; + static constexpr GrGLenum GL_TIME_ELAPSED = 0x88bf; + static constexpr GrGLenum GL_GPU_DISJOINT = 0x8fbb; + + typedef void (GR_GL_FUNCTION_TYPE* GLGetIntegervProc) (GrGLenum, GrGLint*); + typedef void (GR_GL_FUNCTION_TYPE* GLGenQueriesProc) (GrGLsizei, GrGLuint*); + typedef void (GR_GL_FUNCTION_TYPE* GLDeleteQueriesProc) (GrGLsizei, const GrGLuint*); + typedef void (GR_GL_FUNCTION_TYPE* GLBeginQueryProc) (GrGLenum, GrGLuint); + typedef void (GR_GL_FUNCTION_TYPE* GLEndQueryProc) (GrGLenum); + typedef void (GR_GL_FUNCTION_TYPE* GLGetQueryObjectuivProc) (GrGLuint, GrGLenum, GrGLuint*); + typedef void (GR_GL_FUNCTION_TYPE* GLGetQueryObjectui64vProc) (GrGLuint, GrGLenum, GrGLuint64*); + + GLGetIntegervProc fGLGetIntegerv; + GLGenQueriesProc fGLGenQueries; + GLDeleteQueriesProc fGLDeleteQueries; + GLBeginQueryProc fGLBeginQuery; + GLEndQueryProc fGLEndQuery; + GLGetQueryObjectuivProc fGLGetQueryObjectuiv; + GLGetQueryObjectui64vProc fGLGetQueryObjectui64v; + + + typedef sk_gpu_test::GpuTimer INHERITED; +}; + +GLGpuTimer* GLGpuTimer::CreateIfSupported(const sk_gpu_test::GLTestContext* ctx) { + SkAutoTDelete<GLGpuTimer> ret; + const GrGLInterface* gl = ctx->gl(); + if (gl->fExtensions.has("GL_EXT_disjoint_timer_query")) { + ret.reset(new GLGpuTimer(true, ctx, "EXT")); + } else if (kGL_GrGLStandard == gl->fStandard && + (GrGLGetVersion(gl) > GR_GL_VER(3,3) || gl->fExtensions.has("GL_ARB_timer_query"))) { + ret.reset(new GLGpuTimer(false, ctx)); + } else if (gl->fExtensions.has("GL_EXT_timer_query")) { + ret.reset(new GLGpuTimer(false, ctx, "EXT")); + } + return ret && ret->validate() ? ret.release() : nullptr; +} + +GLGpuTimer::GLGpuTimer(bool disjointSupport, const sk_gpu_test::GLTestContext* ctx, const char* ext) + : INHERITED(disjointSupport) { + ctx->getGLProcAddress(&fGLGetIntegerv, "glGetIntegerv"); + ctx->getGLProcAddress(&fGLGenQueries, "glGenQueries", ext); + ctx->getGLProcAddress(&fGLDeleteQueries, "glDeleteQueries", ext); + ctx->getGLProcAddress(&fGLBeginQuery, "glBeginQuery", ext); + ctx->getGLProcAddress(&fGLEndQuery, "glEndQuery", ext); + ctx->getGLProcAddress(&fGLGetQueryObjectuiv, "glGetQueryObjectuiv", ext); + ctx->getGLProcAddress(&fGLGetQueryObjectui64v, "glGetQueryObjectui64v", ext); +} + +bool GLGpuTimer::validate() const { + return fGLGetIntegerv && fGLGenQueries && fGLDeleteQueries && fGLBeginQuery && fGLEndQuery && + fGLGetQueryObjectuiv && fGLGetQueryObjectui64v; +} + +sk_gpu_test::PlatformTimerQuery GLGpuTimer::onQueueTimerStart() const { + GrGLuint queryID; + fGLGenQueries(1, &queryID); + if (!queryID) { + return sk_gpu_test::kInvalidTimerQuery; + } + if (this->disjointSupport()) { + // Clear the disjoint flag. + GrGLint disjoint; + fGLGetIntegerv(GL_GPU_DISJOINT, &disjoint); + } + fGLBeginQuery(GL_TIME_ELAPSED, queryID); + return static_cast<sk_gpu_test::PlatformTimerQuery>(queryID); +} + +void GLGpuTimer::onQueueTimerStop(sk_gpu_test::PlatformTimerQuery platformTimer) const { + if (sk_gpu_test::kInvalidTimerQuery == platformTimer) { + return; + } + fGLEndQuery(GL_TIME_ELAPSED); +} + +sk_gpu_test::GpuTimer::QueryStatus +GLGpuTimer::checkQueryStatus(sk_gpu_test::PlatformTimerQuery platformTimer) { + const GrGLuint queryID = static_cast<GrGLuint>(platformTimer); + if (!queryID) { + return QueryStatus::kInvalid; + } + GrGLuint available = 0; + fGLGetQueryObjectuiv(queryID, GL_QUERY_RESULT_AVAILABLE, &available); + if (!available) { + return QueryStatus::kPending; + } + if (this->disjointSupport()) { + GrGLint disjoint = 1; + fGLGetIntegerv(GL_GPU_DISJOINT, &disjoint); + if (disjoint) { + return QueryStatus::kDisjoint; + } + } + return QueryStatus::kAccurate; +} + +std::chrono::nanoseconds GLGpuTimer::getTimeElapsed(sk_gpu_test::PlatformTimerQuery platformTimer) { + SkASSERT(this->checkQueryStatus(platformTimer) >= QueryStatus::kDisjoint); + const GrGLuint queryID = static_cast<GrGLuint>(platformTimer); + GrGLuint64 nanoseconds; + fGLGetQueryObjectui64v(queryID, GL_QUERY_RESULT, &nanoseconds); + return std::chrono::nanoseconds(nanoseconds); +} + +void GLGpuTimer::deleteQuery(sk_gpu_test::PlatformTimerQuery platformTimer) { + const GrGLuint queryID = static_cast<GrGLuint>(platformTimer); + fGLDeleteQueries(1, &queryID); +} + } // anonymous namespace namespace sk_gpu_test { @@ -91,6 +220,7 @@ void GLTestContext::init(const GrGLInterface* gl, FenceSync* fenceSync) { SkASSERT(!fGL.get()); fGL.reset(gl); fFenceSync = fenceSync ? fenceSync : GLFenceSync::CreateIfSupported(this); + fGpuTimer = GLGpuTimer::CreateIfSupported(this); } void GLTestContext::teardown() { |