1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "WebGLQuery.h"
7 
8 #include "GLContext.h"
9 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
10 #include "mozilla/StaticPrefs_webgl.h"
11 #include "nsContentUtils.h"
12 #include "WebGLContext.h"
13 
14 namespace mozilla {
15 
16 ////
17 
GenQuery(gl::GLContext * gl)18 static GLuint GenQuery(gl::GLContext* gl) {
19   GLuint ret = 0;
20   gl->fGenQueries(1, &ret);
21   return ret;
22 }
23 
WebGLQuery(WebGLContext * webgl)24 WebGLQuery::WebGLQuery(WebGLContext* webgl)
25     : WebGLContextBoundObject(webgl),
26       mGLName(GenQuery(mContext->gl)),
27       mTarget(0),
28       mActiveSlot(nullptr) {}
29 
~WebGLQuery()30 WebGLQuery::~WebGLQuery() {
31   if (!mContext) return;
32   mContext->gl->fDeleteQueries(1, &mGLName);
33 }
34 
35 ////
36 
TargetForDriver(const gl::GLContext * gl,GLenum target)37 static GLenum TargetForDriver(const gl::GLContext* gl, GLenum target) {
38   switch (target) {
39     case LOCAL_GL_ANY_SAMPLES_PASSED:
40     case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
41       break;
42 
43     default:
44       return target;
45   }
46 
47   if (gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) return target;
48 
49   if (gl->IsSupported(gl::GLFeature::occlusion_query2))
50     return LOCAL_GL_ANY_SAMPLES_PASSED;
51 
52   return LOCAL_GL_SAMPLES_PASSED;
53 }
54 
BeginQuery(GLenum target,RefPtr<WebGLQuery> & slot)55 void WebGLQuery::BeginQuery(GLenum target, RefPtr<WebGLQuery>& slot) {
56   mTarget = target;
57   mActiveSlot = &slot;
58   *mActiveSlot = this;
59 
60   ////
61 
62   const auto& gl = mContext->gl;
63 
64   const auto driverTarget = TargetForDriver(gl, mTarget);
65   gl->fBeginQuery(driverTarget, mGLName);
66 }
67 
EndQuery()68 void WebGLQuery::EndQuery() {
69   *mActiveSlot = nullptr;
70   mActiveSlot = nullptr;
71   mCanBeAvailable = false;
72 
73   ////
74 
75   const auto& gl = mContext->gl;
76 
77   const auto driverTarget = TargetForDriver(gl, mTarget);
78   gl->fEndQuery(driverTarget);
79 
80   ////
81 
82   const auto& availRunnable = mContext->EnsureAvailabilityRunnable();
83   availRunnable->mQueries.push_back(this);
84 }
85 
GetQueryParameter(GLenum pname) const86 Maybe<double> WebGLQuery::GetQueryParameter(GLenum pname) const {
87   switch (pname) {
88     case LOCAL_GL_QUERY_RESULT_AVAILABLE:
89     case LOCAL_GL_QUERY_RESULT:
90       break;
91 
92     default:
93       mContext->ErrorInvalidEnumInfo("pname", pname);
94       return Nothing();
95   }
96 
97   if (!mTarget) {
98     mContext->ErrorInvalidOperation("Query has never been active.");
99     return Nothing();
100   }
101 
102   if (mActiveSlot) {
103     mContext->ErrorInvalidOperation("Query is still active.");
104     return Nothing();
105   }
106 
107   // End of validation
108   ////
109 
110   // We must usually wait for an event loop before the query can be available.
111   const bool canBeAvailable =
112       (mCanBeAvailable || StaticPrefs::webgl_allow_immediate_queries());
113   if (!canBeAvailable) {
114     if (pname == LOCAL_GL_QUERY_RESULT_AVAILABLE) {
115       return Some(false);
116     }
117     return Nothing();
118   }
119 
120   const auto& gl = mContext->gl;
121 
122   uint64_t val = 0;
123   switch (pname) {
124     case LOCAL_GL_QUERY_RESULT_AVAILABLE:
125       gl->fGetQueryObjectuiv(mGLName, pname, (GLuint*)&val);
126       return Some(static_cast<bool>(val));
127 
128     case LOCAL_GL_QUERY_RESULT:
129       switch (mTarget) {
130         case LOCAL_GL_TIME_ELAPSED_EXT:
131         case LOCAL_GL_TIMESTAMP_EXT:
132           if (mContext->Has64BitTimestamps()) {
133             gl->fGetQueryObjectui64v(mGLName, pname, &val);
134             break;
135           }
136           [[fallthrough]];
137 
138         default:
139           gl->fGetQueryObjectuiv(mGLName, LOCAL_GL_QUERY_RESULT, (GLuint*)&val);
140           break;
141       }
142 
143       switch (mTarget) {
144         case LOCAL_GL_ANY_SAMPLES_PASSED:
145         case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
146         case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
147         case LOCAL_GL_TIME_ELAPSED_EXT:
148         case LOCAL_GL_TIMESTAMP_EXT:
149           return Some(val);
150       }
151       MOZ_CRASH("Bad `mTarget`.");
152 
153     default:
154       MOZ_CRASH("Bad `pname`.");
155   }
156 }
157 
QueryCounter()158 void WebGLQuery::QueryCounter() {
159   const GLenum target = LOCAL_GL_TIMESTAMP_EXT;
160   if (mTarget && target != mTarget) {
161     mContext->ErrorInvalidOperation("Queries cannot change targets.");
162     return;
163   }
164 
165   mTarget = target;
166   mCanBeAvailable = false;
167 
168   const auto& gl = mContext->gl;
169   gl->fQueryCounter(mGLName, mTarget);
170 
171   const auto& availRunnable = mContext->EnsureAvailabilityRunnable();
172   availRunnable->mQueries.push_back(this);
173 }
174 
175 }  // namespace mozilla
176