aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/gpu
diff options
context:
space:
mode:
authorGravatar csmartdalton <csmartdalton@google.com>2016-10-04 12:33:44 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-10-04 12:33:44 -0700
commitc06720d06faab3b01eba1b8693e0ac791f06dc96 (patch)
tree65e68119ef1d128aeb4bff876c8ae814306c3a81 /tools/gpu
parentb3f543d955637c6e10d7109554b5a46c6e25291a (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.h4
-rw-r--r--tools/gpu/GpuTimer.h77
-rw-r--r--tools/gpu/TestContext.cpp9
-rw-r--r--tools/gpu/TestContext.h9
-rw-r--r--tools/gpu/gl/GLTestContext.cpp130
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() {