1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "tools/gpu/gl/GLTestContext.h"
9
10#include "include/gpu/GrDirectContext.h"
11#include "src/gpu/gl/GrGLUtil.h"
12#include "tools/gpu/GpuTimer.h"
13
14namespace {
15
16class GLGpuTimer : public sk_gpu_test::GpuTimer {
17public:
18    static std::unique_ptr<GLGpuTimer> MakeIfSupported(const sk_gpu_test::GLTestContext*);
19
20    QueryStatus checkQueryStatus(sk_gpu_test::PlatformTimerQuery) override;
21    std::chrono::nanoseconds getTimeElapsed(sk_gpu_test::PlatformTimerQuery) override;
22    void deleteQuery(sk_gpu_test::PlatformTimerQuery) override;
23
24private:
25#ifdef SK_GL
26    GLGpuTimer(bool disjointSupport, const sk_gpu_test::GLTestContext*, const char* ext = "");
27    bool validate() const;
28#endif
29
30    sk_gpu_test::PlatformTimerQuery onQueueTimerStart() const override;
31    void onQueueTimerStop(sk_gpu_test::PlatformTimerQuery) const override;
32
33    inline static constexpr GrGLenum GL_QUERY_RESULT            = 0x8866;
34    inline static constexpr GrGLenum GL_QUERY_RESULT_AVAILABLE  = 0x8867;
35    inline static constexpr GrGLenum GL_TIME_ELAPSED            = 0x88bf;
36    inline static constexpr GrGLenum GL_GPU_DISJOINT            = 0x8fbb;
37
38    typedef void (GR_GL_FUNCTION_TYPE* GLGetIntegervProc) (GrGLenum, GrGLint*);
39    typedef void (GR_GL_FUNCTION_TYPE* GLGenQueriesProc) (GrGLsizei, GrGLuint*);
40    typedef void (GR_GL_FUNCTION_TYPE* GLDeleteQueriesProc) (GrGLsizei, const GrGLuint*);
41    typedef void (GR_GL_FUNCTION_TYPE* GLBeginQueryProc) (GrGLenum, GrGLuint);
42    typedef void (GR_GL_FUNCTION_TYPE* GLEndQueryProc) (GrGLenum);
43    typedef void (GR_GL_FUNCTION_TYPE* GLGetQueryObjectuivProc) (GrGLuint, GrGLenum, GrGLuint*);
44    typedef void (GR_GL_FUNCTION_TYPE* GLGetQueryObjectui64vProc) (GrGLuint, GrGLenum, GrGLuint64*);
45
46    GLGetIntegervProc           fGLGetIntegerv;
47    GLGenQueriesProc            fGLGenQueries;
48    GLDeleteQueriesProc         fGLDeleteQueries;
49    GLBeginQueryProc            fGLBeginQuery;
50    GLEndQueryProc              fGLEndQuery;
51    GLGetQueryObjectuivProc     fGLGetQueryObjectuiv;
52    GLGetQueryObjectui64vProc   fGLGetQueryObjectui64v;
53
54
55    using INHERITED = sk_gpu_test::GpuTimer;
56};
57
58std::unique_ptr<GLGpuTimer> GLGpuTimer::MakeIfSupported(const sk_gpu_test::GLTestContext* ctx) {
59#ifdef SK_GL
60    std::unique_ptr<GLGpuTimer> ret;
61    const GrGLInterface* gl = ctx->gl();
62    if (gl->fExtensions.has("GL_EXT_disjoint_timer_query")) {
63        ret.reset(new GLGpuTimer(true, ctx, "EXT"));
64    } else if (kGL_GrGLStandard == gl->fStandard &&
65               (GrGLGetVersion(gl) > GR_GL_VER(3,3) || gl->fExtensions.has("GL_ARB_timer_query"))) {
66        ret.reset(new GLGpuTimer(false, ctx));
67    } else if (gl->fExtensions.has("GL_EXT_timer_query")) {
68        ret.reset(new GLGpuTimer(false, ctx, "EXT"));
69    }
70    if (ret && !ret->validate()) {
71        ret = nullptr;
72    }
73    return ret;
74#else
75    return nullptr;
76#endif
77}
78
79#ifdef SK_GL
80GLGpuTimer::GLGpuTimer(bool disjointSupport, const sk_gpu_test::GLTestContext* ctx, const char* ext)
81    : INHERITED(disjointSupport) {
82    ctx->getGLProcAddress(&fGLGetIntegerv, "glGetIntegerv");
83    ctx->getGLProcAddress(&fGLGenQueries, "glGenQueries", ext);
84    ctx->getGLProcAddress(&fGLDeleteQueries, "glDeleteQueries", ext);
85    ctx->getGLProcAddress(&fGLBeginQuery, "glBeginQuery", ext);
86    ctx->getGLProcAddress(&fGLEndQuery, "glEndQuery", ext);
87    ctx->getGLProcAddress(&fGLGetQueryObjectuiv, "glGetQueryObjectuiv", ext);
88    ctx->getGLProcAddress(&fGLGetQueryObjectui64v, "glGetQueryObjectui64v", ext);
89}
90
91bool GLGpuTimer::validate() const {
92    return fGLGetIntegerv && fGLGenQueries && fGLDeleteQueries && fGLBeginQuery && fGLEndQuery &&
93           fGLGetQueryObjectuiv && fGLGetQueryObjectui64v;
94}
95#endif
96
97sk_gpu_test::PlatformTimerQuery GLGpuTimer::onQueueTimerStart() const {
98    GrGLuint queryID;
99    fGLGenQueries(1, &queryID);
100    if (!queryID) {
101        return sk_gpu_test::kInvalidTimerQuery;
102    }
103    if (this->disjointSupport()) {
104        // Clear the disjoint flag.
105        GrGLint disjoint;
106        fGLGetIntegerv(GL_GPU_DISJOINT, &disjoint);
107    }
108    fGLBeginQuery(GL_TIME_ELAPSED, queryID);
109    return static_cast<sk_gpu_test::PlatformTimerQuery>(queryID);
110}
111
112void GLGpuTimer::onQueueTimerStop(sk_gpu_test::PlatformTimerQuery platformTimer) const {
113    if (sk_gpu_test::kInvalidTimerQuery == platformTimer) {
114        return;
115    }
116    fGLEndQuery(GL_TIME_ELAPSED);
117}
118
119sk_gpu_test::GpuTimer::QueryStatus
120GLGpuTimer::checkQueryStatus(sk_gpu_test::PlatformTimerQuery platformTimer) {
121    const GrGLuint queryID = static_cast<GrGLuint>(platformTimer);
122    if (!queryID) {
123        return QueryStatus::kInvalid;
124    }
125    GrGLuint available = 0;
126    fGLGetQueryObjectuiv(queryID, GL_QUERY_RESULT_AVAILABLE, &available);
127    if (!available) {
128        return QueryStatus::kPending;
129    }
130    if (this->disjointSupport()) {
131        GrGLint disjoint = 1;
132        fGLGetIntegerv(GL_GPU_DISJOINT, &disjoint);
133        if (disjoint) {
134            return QueryStatus::kDisjoint;
135        }
136    }
137    return QueryStatus::kAccurate;
138}
139
140std::chrono::nanoseconds GLGpuTimer::getTimeElapsed(sk_gpu_test::PlatformTimerQuery platformTimer) {
141    SkASSERT(this->checkQueryStatus(platformTimer) >= QueryStatus::kDisjoint);
142    const GrGLuint queryID = static_cast<GrGLuint>(platformTimer);
143    GrGLuint64 nanoseconds;
144    fGLGetQueryObjectui64v(queryID, GL_QUERY_RESULT, &nanoseconds);
145    return std::chrono::nanoseconds(nanoseconds);
146}
147
148void GLGpuTimer::deleteQuery(sk_gpu_test::PlatformTimerQuery platformTimer) {
149    const GrGLuint queryID = static_cast<GrGLuint>(platformTimer);
150    fGLDeleteQueries(1, &queryID);
151}
152
153static_assert(sizeof(GrGLuint) <= sizeof(sk_gpu_test::PlatformTimerQuery));
154
155}  // anonymous namespace
156
157namespace sk_gpu_test {
158
159GLTestContext::GLTestContext() : TestContext() {}
160
161GLTestContext::~GLTestContext() {
162    SkASSERT(!fGLInterface);
163    SkASSERT(!fOriginalGLInterface);
164}
165
166bool GLTestContext::isValid() const {
167#ifdef SK_GL
168    return SkToBool(this->gl());
169#else
170    return fWasInitialized;
171#endif
172}
173
174static bool fence_is_supported(const GLTestContext* ctx) {
175#ifdef SK_GL
176    if (kGL_GrGLStandard == ctx->gl()->fStandard) {
177        if (GrGLGetVersion(ctx->gl()) < GR_GL_VER(3, 2) &&
178            !ctx->gl()->hasExtension("GL_ARB_sync")) {
179            return false;
180        }
181        return true;
182    } else {
183        if (ctx->gl()->hasExtension("GL_APPLE_sync")) {
184            return true;
185        } else if (ctx->gl()->hasExtension("GL_NV_fence")) {
186            return true;
187        } else if (GrGLGetVersion(ctx->gl()) >= GR_GL_VER(3, 0)) {
188            return true;
189        } else {
190            return false;
191        }
192    }
193#else
194    return false;
195#endif
196}
197
198void GLTestContext::init(sk_sp<const GrGLInterface> gl) {
199    fGLInterface = std::move(gl);
200    fOriginalGLInterface = fGLInterface;
201    fFenceSupport = fence_is_supported(this);
202    fGpuTimer = GLGpuTimer::MakeIfSupported(this);
203#ifndef SK_GL
204    fWasInitialized = true;
205#endif
206}
207
208void GLTestContext::teardown() {
209    fGLInterface.reset();
210    fOriginalGLInterface.reset();
211    INHERITED::teardown();
212}
213
214void GLTestContext::testAbandon() {
215    INHERITED::testAbandon();
216#ifdef SK_GL
217    if (fGLInterface) {
218        fGLInterface->abandon();
219        fOriginalGLInterface->abandon();
220    }
221#endif
222}
223
224void GLTestContext::finish() {
225#ifdef SK_GL
226    if (fGLInterface) {
227        GR_GL_CALL(fGLInterface.get(), Finish());
228    }
229#endif
230}
231
232void GLTestContext::overrideVersion(const char* version, const char* shadingLanguageVersion) {
233#ifdef SK_GL
234    // GrGLFunction has both a limited capture size and doesn't call a destructor when it is
235    // initialized with a lambda. So here we're trusting fOriginalGLInterface will be kept alive.
236    auto getString = [wrapped = &fOriginalGLInterface->fFunctions.fGetString,
237                      version,
238                      shadingLanguageVersion](GrGLenum name) {
239        if (name == GR_GL_VERSION) {
240            return reinterpret_cast<const GrGLubyte*>(version);
241        } else if (name == GR_GL_SHADING_LANGUAGE_VERSION) {
242            return reinterpret_cast<const GrGLubyte*>(shadingLanguageVersion);
243        }
244        return (*wrapped)(name);
245    };
246    auto newInterface = sk_make_sp<GrGLInterface>(*fOriginalGLInterface);
247    newInterface->fFunctions.fGetString = getString;
248    fGLInterface = std::move(newInterface);
249#endif
250};
251
252sk_sp<GrDirectContext> GLTestContext::makeContext(const GrContextOptions& options) {
253#ifdef SK_GL
254    return GrDirectContext::MakeGL(fGLInterface, options);
255#else
256    return nullptr;
257#endif
258}
259
260}  // namespace sk_gpu_test
261