1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/canvas/src/WebGLContextAsyncQueries.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,351 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "WebGLContext.h" 1.10 +#include "WebGLQuery.h" 1.11 +#include "GLContext.h" 1.12 + 1.13 +using namespace mozilla; 1.14 + 1.15 +/* 1.16 + * We fake ANY_SAMPLES_PASSED and ANY_SAMPLES_PASSED_CONSERVATIVE with 1.17 + * SAMPLES_PASSED on desktop. 1.18 + * 1.19 + * OpenGL ES 3.0 spec 4.1.6 1.20 + * If the target of the query is ANY_SAMPLES_PASSED_CONSERVATIVE, an implementation 1.21 + * may choose to use a less precise version of the test which can additionally set 1.22 + * the samples-boolean state to TRUE in some other implementation-dependent cases. 1.23 + */ 1.24 + 1.25 +static const char* 1.26 +GetQueryTargetEnumString(GLenum target) 1.27 +{ 1.28 + switch (target) 1.29 + { 1.30 + case LOCAL_GL_ANY_SAMPLES_PASSED: 1.31 + return "ANY_SAMPLES_PASSED"; 1.32 + case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE: 1.33 + return "ANY_SAMPLES_PASSED_CONSERVATIVE"; 1.34 + case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 1.35 + return "TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN"; 1.36 + default: 1.37 + break; 1.38 + } 1.39 + 1.40 + MOZ_ASSERT(false, "Unknown query `target`."); 1.41 + return "UNKNOWN_QUERY_TARGET"; 1.42 +} 1.43 + 1.44 +static inline GLenum 1.45 +SimulateOcclusionQueryTarget(const gl::GLContext* gl, GLenum target) 1.46 +{ 1.47 + MOZ_ASSERT(target == LOCAL_GL_ANY_SAMPLES_PASSED || 1.48 + target == LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE, 1.49 + "unknown occlusion query target"); 1.50 + 1.51 + if (gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) { 1.52 + return target; 1.53 + } else if (gl->IsSupported(gl::GLFeature::occlusion_query2)) { 1.54 + return LOCAL_GL_ANY_SAMPLES_PASSED; 1.55 + } 1.56 + 1.57 + return LOCAL_GL_SAMPLES_PASSED; 1.58 +} 1.59 + 1.60 +already_AddRefed<WebGLQuery> 1.61 +WebGLContext::CreateQuery() 1.62 +{ 1.63 + if (IsContextLost()) 1.64 + return nullptr; 1.65 + 1.66 + if (mActiveOcclusionQuery && !gl->IsGLES()) { 1.67 + /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt 1.68 + * Calling either GenQueriesARB or DeleteQueriesARB while any query of 1.69 + * any target is active causes an INVALID_OPERATION error to be 1.70 + * generated. 1.71 + */ 1.72 + GenerateWarning("createQuery: the WebGL 2 prototype might generate INVALID_OPERATION" 1.73 + "when creating a query object while one other is active."); 1.74 + /* 1.75 + * We *need* to lock webgl2 to GL>=3.0 on desktop, but we don't have a good 1.76 + * mechanism to do this yet. See bug 898404. 1.77 + */ 1.78 + } 1.79 + 1.80 + nsRefPtr<WebGLQuery> globj = new WebGLQuery(this); 1.81 + 1.82 + return globj.forget(); 1.83 +} 1.84 + 1.85 +void 1.86 +WebGLContext::DeleteQuery(WebGLQuery *query) 1.87 +{ 1.88 + if (IsContextLost()) 1.89 + return; 1.90 + 1.91 + if (!query) 1.92 + return; 1.93 + 1.94 + if (query->IsDeleted()) 1.95 + return; 1.96 + 1.97 + if (query->IsActive()) { 1.98 + EndQuery(query->mType); 1.99 + } 1.100 + 1.101 + if (mActiveOcclusionQuery && !gl->IsGLES()) { 1.102 + /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt 1.103 + * Calling either GenQueriesARB or DeleteQueriesARB while any query of 1.104 + * any target is active causes an INVALID_OPERATION error to be 1.105 + * generated. 1.106 + */ 1.107 + GenerateWarning("deleteQuery: the WebGL 2 prototype might generate INVALID_OPERATION" 1.108 + "when deleting a query object while one other is active."); 1.109 + } 1.110 + 1.111 + query->RequestDelete(); 1.112 +} 1.113 + 1.114 +void 1.115 +WebGLContext::BeginQuery(GLenum target, WebGLQuery *query) 1.116 +{ 1.117 + if (IsContextLost()) 1.118 + return; 1.119 + 1.120 + WebGLRefPtr<WebGLQuery>* targetSlot = GetQueryTargetSlot(target, "beginQuery"); 1.121 + if (!targetSlot) { 1.122 + return; 1.123 + } 1.124 + 1.125 + if (!query) { 1.126 + /* SPECS BeginQuery.1 1.127 + * http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt 1.128 + * BeginQueryEXT sets the active query object name for the query type given 1.129 + * by <target> to <id>. If BeginQueryEXT is called with an <id> of zero, if 1.130 + * the active query object name for <target> is non-zero (for the targets 1.131 + * ANY_SAMPLES_PASSED_EXT and ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, if the 1.132 + * active query for either target is non-zero), if <id> is the name of an 1.133 + * existing query object whose type does not match <target>, or if <id> is the 1.134 + * active query object name for any query type, the error INVALID_OPERATION is 1.135 + * generated. 1.136 + */ 1.137 + ErrorInvalidOperation("beginQuery: query should not be null"); 1.138 + return; 1.139 + } 1.140 + 1.141 + if (query->IsDeleted()) { 1.142 + /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt 1.143 + * BeginQueryEXT fails and an INVALID_OPERATION error is generated if <id> 1.144 + * is not a name returned from a previous call to GenQueriesEXT, or if such 1.145 + * a name has since been deleted with DeleteQueriesEXT. 1.146 + */ 1.147 + ErrorInvalidOperation("beginQuery: query has been deleted"); 1.148 + return; 1.149 + } 1.150 + 1.151 + if (query->HasEverBeenActive() && 1.152 + query->mType != target) 1.153 + { 1.154 + /* 1.155 + * See SPECS BeginQuery.1 1.156 + */ 1.157 + ErrorInvalidOperation("beginQuery: target doesn't match with the query type"); 1.158 + return; 1.159 + } 1.160 + 1.161 + if (*targetSlot) { 1.162 + /* 1.163 + * See SPECS BeginQuery.1 1.164 + */ 1.165 + ErrorInvalidOperation("beginQuery: an other query already active"); 1.166 + return; 1.167 + } 1.168 + 1.169 + if (!query->HasEverBeenActive()) { 1.170 + query->mType = target; 1.171 + } 1.172 + 1.173 + MakeContextCurrent(); 1.174 + 1.175 + if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { 1.176 + gl->fBeginQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query->mGLName); 1.177 + } else { 1.178 + gl->fBeginQuery(SimulateOcclusionQueryTarget(gl, target), query->mGLName); 1.179 + } 1.180 + 1.181 + *targetSlot = query; 1.182 +} 1.183 + 1.184 +void 1.185 +WebGLContext::EndQuery(GLenum target) 1.186 +{ 1.187 + if (IsContextLost()) 1.188 + return; 1.189 + 1.190 + WebGLRefPtr<WebGLQuery>* targetSlot = GetQueryTargetSlot(target, "endQuery"); 1.191 + if (!targetSlot) { 1.192 + return; 1.193 + } 1.194 + 1.195 + if (!*targetSlot || 1.196 + target != (*targetSlot)->mType) 1.197 + { 1.198 + /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt 1.199 + * marks the end of the sequence of commands to be tracked for the query type 1.200 + * given by <target>. The active query object for <target> is updated to 1.201 + * indicate that query results are not available, and the active query object 1.202 + * name for <target> is reset to zero. When the commands issued prior to 1.203 + * EndQueryEXT have completed and a final query result is available, the 1.204 + * query object active when EndQueryEXT is called is updated by the GL. The 1.205 + * query object is updated to indicate that the query results are available 1.206 + * and to contain the query result. If the active query object name for 1.207 + * <target> is zero when EndQueryEXT is called, the error INVALID_OPERATION 1.208 + * is generated. 1.209 + */ 1.210 + ErrorInvalidOperation("endQuery: There is no active query of type %s.", 1.211 + GetQueryTargetEnumString(target)); 1.212 + return; 1.213 + } 1.214 + 1.215 + MakeContextCurrent(); 1.216 + 1.217 + if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { 1.218 + gl->fEndQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); 1.219 + } else { 1.220 + gl->fEndQuery(SimulateOcclusionQueryTarget(gl, target)); 1.221 + } 1.222 + 1.223 + *targetSlot = nullptr; 1.224 +} 1.225 + 1.226 +bool 1.227 +WebGLContext::IsQuery(WebGLQuery *query) 1.228 +{ 1.229 + if (IsContextLost()) 1.230 + return false; 1.231 + 1.232 + if (!query) 1.233 + return false; 1.234 + 1.235 + return ValidateObjectAllowDeleted("isQuery", query) && 1.236 + !query->IsDeleted() && 1.237 + query->HasEverBeenActive(); 1.238 +} 1.239 + 1.240 +already_AddRefed<WebGLQuery> 1.241 +WebGLContext::GetQuery(GLenum target, GLenum pname) 1.242 +{ 1.243 + if (IsContextLost()) 1.244 + return nullptr; 1.245 + 1.246 + WebGLRefPtr<WebGLQuery>* targetSlot = GetQueryTargetSlot(target, "getQuery"); 1.247 + if (!targetSlot) { 1.248 + return nullptr; 1.249 + } 1.250 + 1.251 + if (pname != LOCAL_GL_CURRENT_QUERY) { 1.252 + /* OpenGL ES 3.0 spec 6.1.7 1.253 + * pname must be CURRENT_QUERY. 1.254 + */ 1.255 + ErrorInvalidEnum("getQuery: pname must be CURRENT_QUERY"); 1.256 + return nullptr; 1.257 + } 1.258 + 1.259 + nsRefPtr<WebGLQuery> tmp = targetSlot->get(); 1.260 + return tmp.forget(); 1.261 +} 1.262 + 1.263 +JS::Value 1.264 +WebGLContext::GetQueryObject(JSContext* cx, WebGLQuery *query, GLenum pname) 1.265 +{ 1.266 + if (IsContextLost()) 1.267 + return JS::NullValue(); 1.268 + 1.269 + if (!query) { 1.270 + /* OpenGL ES 3.0 spec 6.1.7 (spec getQueryObject 1) 1.271 + * If id is not the name of a query object, or if the query object named by id is 1.272 + * currently active, then an INVALID_OPERATION error is generated. pname must be 1.273 + * QUERY_RESULT or QUERY_RESULT_AVAILABLE. 1.274 + */ 1.275 + ErrorInvalidOperation("getQueryObject: query should not be null"); 1.276 + return JS::NullValue(); 1.277 + } 1.278 + 1.279 + if (query->IsDeleted()) { 1.280 + // See (spec getQueryObject 1) 1.281 + ErrorInvalidOperation("getQueryObject: query has been deleted"); 1.282 + return JS::NullValue(); 1.283 + } 1.284 + 1.285 + if (query->IsActive()) { 1.286 + // See (spec getQueryObject 1) 1.287 + ErrorInvalidOperation("getQueryObject: query is active"); 1.288 + return JS::NullValue(); 1.289 + } 1.290 + 1.291 + if (!query->HasEverBeenActive()) { 1.292 + /* See (spec getQueryObject 1) 1.293 + * If this instance of WebGLQuery has never been active before, that mean that 1.294 + * query->mGLName is not a query object yet. 1.295 + */ 1.296 + ErrorInvalidOperation("getQueryObject: query has never been active"); 1.297 + return JS::NullValue(); 1.298 + } 1.299 + 1.300 + switch (pname) 1.301 + { 1.302 + case LOCAL_GL_QUERY_RESULT_AVAILABLE: 1.303 + { 1.304 + GLuint returned = 0; 1.305 + 1.306 + MakeContextCurrent(); 1.307 + gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned); 1.308 + 1.309 + return JS::BooleanValue(returned != 0); 1.310 + } 1.311 + 1.312 + case LOCAL_GL_QUERY_RESULT: 1.313 + { 1.314 + GLuint returned = 0; 1.315 + 1.316 + MakeContextCurrent(); 1.317 + gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT, &returned); 1.318 + 1.319 + if (query->mType == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { 1.320 + return JS::NumberValue(uint32_t(returned)); 1.321 + } 1.322 + 1.323 + /* 1.324 + * test (returned != 0) is important because ARB_occlusion_query on desktop drivers 1.325 + * return the number of samples drawed when the OpenGL ES extension 1.326 + * ARB_occlusion_query_boolean return only a boolean if a sample has been drawed. 1.327 + */ 1.328 + return JS::BooleanValue(returned != 0); 1.329 + } 1.330 + 1.331 + default: 1.332 + break; 1.333 + } 1.334 + 1.335 + ErrorInvalidEnum("getQueryObject: pname must be QUERY_RESULT{_AVAILABLE}"); 1.336 + return JS::NullValue(); 1.337 +} 1.338 + 1.339 +WebGLRefPtr<WebGLQuery>* 1.340 +WebGLContext::GetQueryTargetSlot(GLenum target, const char* infos) 1.341 +{ 1.342 + switch (target) { 1.343 + case LOCAL_GL_ANY_SAMPLES_PASSED: 1.344 + case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE: 1.345 + return &mActiveOcclusionQuery; 1.346 + case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: 1.347 + return &mActiveTransformFeedbackQuery; 1.348 + } 1.349 + 1.350 + ErrorInvalidEnum("%s: unknown query target", infos); 1.351 + return nullptr; 1.352 +} 1.353 + 1.354 +