|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
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 "WebGLContext.h" |
|
7 #include "WebGLQuery.h" |
|
8 #include "GLContext.h" |
|
9 |
|
10 using namespace mozilla; |
|
11 |
|
12 /* |
|
13 * We fake ANY_SAMPLES_PASSED and ANY_SAMPLES_PASSED_CONSERVATIVE with |
|
14 * SAMPLES_PASSED on desktop. |
|
15 * |
|
16 * OpenGL ES 3.0 spec 4.1.6 |
|
17 * If the target of the query is ANY_SAMPLES_PASSED_CONSERVATIVE, an implementation |
|
18 * may choose to use a less precise version of the test which can additionally set |
|
19 * the samples-boolean state to TRUE in some other implementation-dependent cases. |
|
20 */ |
|
21 |
|
22 static const char* |
|
23 GetQueryTargetEnumString(GLenum target) |
|
24 { |
|
25 switch (target) |
|
26 { |
|
27 case LOCAL_GL_ANY_SAMPLES_PASSED: |
|
28 return "ANY_SAMPLES_PASSED"; |
|
29 case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE: |
|
30 return "ANY_SAMPLES_PASSED_CONSERVATIVE"; |
|
31 case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: |
|
32 return "TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN"; |
|
33 default: |
|
34 break; |
|
35 } |
|
36 |
|
37 MOZ_ASSERT(false, "Unknown query `target`."); |
|
38 return "UNKNOWN_QUERY_TARGET"; |
|
39 } |
|
40 |
|
41 static inline GLenum |
|
42 SimulateOcclusionQueryTarget(const gl::GLContext* gl, GLenum target) |
|
43 { |
|
44 MOZ_ASSERT(target == LOCAL_GL_ANY_SAMPLES_PASSED || |
|
45 target == LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE, |
|
46 "unknown occlusion query target"); |
|
47 |
|
48 if (gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) { |
|
49 return target; |
|
50 } else if (gl->IsSupported(gl::GLFeature::occlusion_query2)) { |
|
51 return LOCAL_GL_ANY_SAMPLES_PASSED; |
|
52 } |
|
53 |
|
54 return LOCAL_GL_SAMPLES_PASSED; |
|
55 } |
|
56 |
|
57 already_AddRefed<WebGLQuery> |
|
58 WebGLContext::CreateQuery() |
|
59 { |
|
60 if (IsContextLost()) |
|
61 return nullptr; |
|
62 |
|
63 if (mActiveOcclusionQuery && !gl->IsGLES()) { |
|
64 /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt |
|
65 * Calling either GenQueriesARB or DeleteQueriesARB while any query of |
|
66 * any target is active causes an INVALID_OPERATION error to be |
|
67 * generated. |
|
68 */ |
|
69 GenerateWarning("createQuery: the WebGL 2 prototype might generate INVALID_OPERATION" |
|
70 "when creating a query object while one other is active."); |
|
71 /* |
|
72 * We *need* to lock webgl2 to GL>=3.0 on desktop, but we don't have a good |
|
73 * mechanism to do this yet. See bug 898404. |
|
74 */ |
|
75 } |
|
76 |
|
77 nsRefPtr<WebGLQuery> globj = new WebGLQuery(this); |
|
78 |
|
79 return globj.forget(); |
|
80 } |
|
81 |
|
82 void |
|
83 WebGLContext::DeleteQuery(WebGLQuery *query) |
|
84 { |
|
85 if (IsContextLost()) |
|
86 return; |
|
87 |
|
88 if (!query) |
|
89 return; |
|
90 |
|
91 if (query->IsDeleted()) |
|
92 return; |
|
93 |
|
94 if (query->IsActive()) { |
|
95 EndQuery(query->mType); |
|
96 } |
|
97 |
|
98 if (mActiveOcclusionQuery && !gl->IsGLES()) { |
|
99 /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt |
|
100 * Calling either GenQueriesARB or DeleteQueriesARB while any query of |
|
101 * any target is active causes an INVALID_OPERATION error to be |
|
102 * generated. |
|
103 */ |
|
104 GenerateWarning("deleteQuery: the WebGL 2 prototype might generate INVALID_OPERATION" |
|
105 "when deleting a query object while one other is active."); |
|
106 } |
|
107 |
|
108 query->RequestDelete(); |
|
109 } |
|
110 |
|
111 void |
|
112 WebGLContext::BeginQuery(GLenum target, WebGLQuery *query) |
|
113 { |
|
114 if (IsContextLost()) |
|
115 return; |
|
116 |
|
117 WebGLRefPtr<WebGLQuery>* targetSlot = GetQueryTargetSlot(target, "beginQuery"); |
|
118 if (!targetSlot) { |
|
119 return; |
|
120 } |
|
121 |
|
122 if (!query) { |
|
123 /* SPECS BeginQuery.1 |
|
124 * http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt |
|
125 * BeginQueryEXT sets the active query object name for the query type given |
|
126 * by <target> to <id>. If BeginQueryEXT is called with an <id> of zero, if |
|
127 * the active query object name for <target> is non-zero (for the targets |
|
128 * ANY_SAMPLES_PASSED_EXT and ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, if the |
|
129 * active query for either target is non-zero), if <id> is the name of an |
|
130 * existing query object whose type does not match <target>, or if <id> is the |
|
131 * active query object name for any query type, the error INVALID_OPERATION is |
|
132 * generated. |
|
133 */ |
|
134 ErrorInvalidOperation("beginQuery: query should not be null"); |
|
135 return; |
|
136 } |
|
137 |
|
138 if (query->IsDeleted()) { |
|
139 /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt |
|
140 * BeginQueryEXT fails and an INVALID_OPERATION error is generated if <id> |
|
141 * is not a name returned from a previous call to GenQueriesEXT, or if such |
|
142 * a name has since been deleted with DeleteQueriesEXT. |
|
143 */ |
|
144 ErrorInvalidOperation("beginQuery: query has been deleted"); |
|
145 return; |
|
146 } |
|
147 |
|
148 if (query->HasEverBeenActive() && |
|
149 query->mType != target) |
|
150 { |
|
151 /* |
|
152 * See SPECS BeginQuery.1 |
|
153 */ |
|
154 ErrorInvalidOperation("beginQuery: target doesn't match with the query type"); |
|
155 return; |
|
156 } |
|
157 |
|
158 if (*targetSlot) { |
|
159 /* |
|
160 * See SPECS BeginQuery.1 |
|
161 */ |
|
162 ErrorInvalidOperation("beginQuery: an other query already active"); |
|
163 return; |
|
164 } |
|
165 |
|
166 if (!query->HasEverBeenActive()) { |
|
167 query->mType = target; |
|
168 } |
|
169 |
|
170 MakeContextCurrent(); |
|
171 |
|
172 if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { |
|
173 gl->fBeginQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query->mGLName); |
|
174 } else { |
|
175 gl->fBeginQuery(SimulateOcclusionQueryTarget(gl, target), query->mGLName); |
|
176 } |
|
177 |
|
178 *targetSlot = query; |
|
179 } |
|
180 |
|
181 void |
|
182 WebGLContext::EndQuery(GLenum target) |
|
183 { |
|
184 if (IsContextLost()) |
|
185 return; |
|
186 |
|
187 WebGLRefPtr<WebGLQuery>* targetSlot = GetQueryTargetSlot(target, "endQuery"); |
|
188 if (!targetSlot) { |
|
189 return; |
|
190 } |
|
191 |
|
192 if (!*targetSlot || |
|
193 target != (*targetSlot)->mType) |
|
194 { |
|
195 /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt |
|
196 * marks the end of the sequence of commands to be tracked for the query type |
|
197 * given by <target>. The active query object for <target> is updated to |
|
198 * indicate that query results are not available, and the active query object |
|
199 * name for <target> is reset to zero. When the commands issued prior to |
|
200 * EndQueryEXT have completed and a final query result is available, the |
|
201 * query object active when EndQueryEXT is called is updated by the GL. The |
|
202 * query object is updated to indicate that the query results are available |
|
203 * and to contain the query result. If the active query object name for |
|
204 * <target> is zero when EndQueryEXT is called, the error INVALID_OPERATION |
|
205 * is generated. |
|
206 */ |
|
207 ErrorInvalidOperation("endQuery: There is no active query of type %s.", |
|
208 GetQueryTargetEnumString(target)); |
|
209 return; |
|
210 } |
|
211 |
|
212 MakeContextCurrent(); |
|
213 |
|
214 if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { |
|
215 gl->fEndQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); |
|
216 } else { |
|
217 gl->fEndQuery(SimulateOcclusionQueryTarget(gl, target)); |
|
218 } |
|
219 |
|
220 *targetSlot = nullptr; |
|
221 } |
|
222 |
|
223 bool |
|
224 WebGLContext::IsQuery(WebGLQuery *query) |
|
225 { |
|
226 if (IsContextLost()) |
|
227 return false; |
|
228 |
|
229 if (!query) |
|
230 return false; |
|
231 |
|
232 return ValidateObjectAllowDeleted("isQuery", query) && |
|
233 !query->IsDeleted() && |
|
234 query->HasEverBeenActive(); |
|
235 } |
|
236 |
|
237 already_AddRefed<WebGLQuery> |
|
238 WebGLContext::GetQuery(GLenum target, GLenum pname) |
|
239 { |
|
240 if (IsContextLost()) |
|
241 return nullptr; |
|
242 |
|
243 WebGLRefPtr<WebGLQuery>* targetSlot = GetQueryTargetSlot(target, "getQuery"); |
|
244 if (!targetSlot) { |
|
245 return nullptr; |
|
246 } |
|
247 |
|
248 if (pname != LOCAL_GL_CURRENT_QUERY) { |
|
249 /* OpenGL ES 3.0 spec 6.1.7 |
|
250 * pname must be CURRENT_QUERY. |
|
251 */ |
|
252 ErrorInvalidEnum("getQuery: pname must be CURRENT_QUERY"); |
|
253 return nullptr; |
|
254 } |
|
255 |
|
256 nsRefPtr<WebGLQuery> tmp = targetSlot->get(); |
|
257 return tmp.forget(); |
|
258 } |
|
259 |
|
260 JS::Value |
|
261 WebGLContext::GetQueryObject(JSContext* cx, WebGLQuery *query, GLenum pname) |
|
262 { |
|
263 if (IsContextLost()) |
|
264 return JS::NullValue(); |
|
265 |
|
266 if (!query) { |
|
267 /* OpenGL ES 3.0 spec 6.1.7 (spec getQueryObject 1) |
|
268 * If id is not the name of a query object, or if the query object named by id is |
|
269 * currently active, then an INVALID_OPERATION error is generated. pname must be |
|
270 * QUERY_RESULT or QUERY_RESULT_AVAILABLE. |
|
271 */ |
|
272 ErrorInvalidOperation("getQueryObject: query should not be null"); |
|
273 return JS::NullValue(); |
|
274 } |
|
275 |
|
276 if (query->IsDeleted()) { |
|
277 // See (spec getQueryObject 1) |
|
278 ErrorInvalidOperation("getQueryObject: query has been deleted"); |
|
279 return JS::NullValue(); |
|
280 } |
|
281 |
|
282 if (query->IsActive()) { |
|
283 // See (spec getQueryObject 1) |
|
284 ErrorInvalidOperation("getQueryObject: query is active"); |
|
285 return JS::NullValue(); |
|
286 } |
|
287 |
|
288 if (!query->HasEverBeenActive()) { |
|
289 /* See (spec getQueryObject 1) |
|
290 * If this instance of WebGLQuery has never been active before, that mean that |
|
291 * query->mGLName is not a query object yet. |
|
292 */ |
|
293 ErrorInvalidOperation("getQueryObject: query has never been active"); |
|
294 return JS::NullValue(); |
|
295 } |
|
296 |
|
297 switch (pname) |
|
298 { |
|
299 case LOCAL_GL_QUERY_RESULT_AVAILABLE: |
|
300 { |
|
301 GLuint returned = 0; |
|
302 |
|
303 MakeContextCurrent(); |
|
304 gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned); |
|
305 |
|
306 return JS::BooleanValue(returned != 0); |
|
307 } |
|
308 |
|
309 case LOCAL_GL_QUERY_RESULT: |
|
310 { |
|
311 GLuint returned = 0; |
|
312 |
|
313 MakeContextCurrent(); |
|
314 gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT, &returned); |
|
315 |
|
316 if (query->mType == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { |
|
317 return JS::NumberValue(uint32_t(returned)); |
|
318 } |
|
319 |
|
320 /* |
|
321 * test (returned != 0) is important because ARB_occlusion_query on desktop drivers |
|
322 * return the number of samples drawed when the OpenGL ES extension |
|
323 * ARB_occlusion_query_boolean return only a boolean if a sample has been drawed. |
|
324 */ |
|
325 return JS::BooleanValue(returned != 0); |
|
326 } |
|
327 |
|
328 default: |
|
329 break; |
|
330 } |
|
331 |
|
332 ErrorInvalidEnum("getQueryObject: pname must be QUERY_RESULT{_AVAILABLE}"); |
|
333 return JS::NullValue(); |
|
334 } |
|
335 |
|
336 WebGLRefPtr<WebGLQuery>* |
|
337 WebGLContext::GetQueryTargetSlot(GLenum target, const char* infos) |
|
338 { |
|
339 switch (target) { |
|
340 case LOCAL_GL_ANY_SAMPLES_PASSED: |
|
341 case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE: |
|
342 return &mActiveOcclusionQuery; |
|
343 case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: |
|
344 return &mActiveTransformFeedbackQuery; |
|
345 } |
|
346 |
|
347 ErrorInvalidEnum("%s: unknown query target", infos); |
|
348 return nullptr; |
|
349 } |
|
350 |
|
351 |