content/canvas/src/WebGLContextAsyncQueries.cpp

changeset 0
6474c204b198
     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 +

mercurial