diff -r 000000000000 -r 6474c204b198 content/canvas/src/WebGLContextAsyncQueries.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/content/canvas/src/WebGLContextAsyncQueries.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,351 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WebGLContext.h" +#include "WebGLQuery.h" +#include "GLContext.h" + +using namespace mozilla; + +/* + * We fake ANY_SAMPLES_PASSED and ANY_SAMPLES_PASSED_CONSERVATIVE with + * SAMPLES_PASSED on desktop. + * + * OpenGL ES 3.0 spec 4.1.6 + * If the target of the query is ANY_SAMPLES_PASSED_CONSERVATIVE, an implementation + * may choose to use a less precise version of the test which can additionally set + * the samples-boolean state to TRUE in some other implementation-dependent cases. + */ + +static const char* +GetQueryTargetEnumString(GLenum target) +{ + switch (target) + { + case LOCAL_GL_ANY_SAMPLES_PASSED: + return "ANY_SAMPLES_PASSED"; + case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE: + return "ANY_SAMPLES_PASSED_CONSERVATIVE"; + case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: + return "TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN"; + default: + break; + } + + MOZ_ASSERT(false, "Unknown query `target`."); + return "UNKNOWN_QUERY_TARGET"; +} + +static inline GLenum +SimulateOcclusionQueryTarget(const gl::GLContext* gl, GLenum target) +{ + MOZ_ASSERT(target == LOCAL_GL_ANY_SAMPLES_PASSED || + target == LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE, + "unknown occlusion query target"); + + if (gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) { + return target; + } else if (gl->IsSupported(gl::GLFeature::occlusion_query2)) { + return LOCAL_GL_ANY_SAMPLES_PASSED; + } + + return LOCAL_GL_SAMPLES_PASSED; +} + +already_AddRefed +WebGLContext::CreateQuery() +{ + if (IsContextLost()) + return nullptr; + + if (mActiveOcclusionQuery && !gl->IsGLES()) { + /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt + * Calling either GenQueriesARB or DeleteQueriesARB while any query of + * any target is active causes an INVALID_OPERATION error to be + * generated. + */ + GenerateWarning("createQuery: the WebGL 2 prototype might generate INVALID_OPERATION" + "when creating a query object while one other is active."); + /* + * We *need* to lock webgl2 to GL>=3.0 on desktop, but we don't have a good + * mechanism to do this yet. See bug 898404. + */ + } + + nsRefPtr globj = new WebGLQuery(this); + + return globj.forget(); +} + +void +WebGLContext::DeleteQuery(WebGLQuery *query) +{ + if (IsContextLost()) + return; + + if (!query) + return; + + if (query->IsDeleted()) + return; + + if (query->IsActive()) { + EndQuery(query->mType); + } + + if (mActiveOcclusionQuery && !gl->IsGLES()) { + /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt + * Calling either GenQueriesARB or DeleteQueriesARB while any query of + * any target is active causes an INVALID_OPERATION error to be + * generated. + */ + GenerateWarning("deleteQuery: the WebGL 2 prototype might generate INVALID_OPERATION" + "when deleting a query object while one other is active."); + } + + query->RequestDelete(); +} + +void +WebGLContext::BeginQuery(GLenum target, WebGLQuery *query) +{ + if (IsContextLost()) + return; + + WebGLRefPtr* targetSlot = GetQueryTargetSlot(target, "beginQuery"); + if (!targetSlot) { + return; + } + + if (!query) { + /* SPECS BeginQuery.1 + * http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt + * BeginQueryEXT sets the active query object name for the query type given + * by to . If BeginQueryEXT is called with an of zero, if + * the active query object name for is non-zero (for the targets + * ANY_SAMPLES_PASSED_EXT and ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, if the + * active query for either target is non-zero), if is the name of an + * existing query object whose type does not match , or if is the + * active query object name for any query type, the error INVALID_OPERATION is + * generated. + */ + ErrorInvalidOperation("beginQuery: query should not be null"); + return; + } + + if (query->IsDeleted()) { + /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt + * BeginQueryEXT fails and an INVALID_OPERATION error is generated if + * is not a name returned from a previous call to GenQueriesEXT, or if such + * a name has since been deleted with DeleteQueriesEXT. + */ + ErrorInvalidOperation("beginQuery: query has been deleted"); + return; + } + + if (query->HasEverBeenActive() && + query->mType != target) + { + /* + * See SPECS BeginQuery.1 + */ + ErrorInvalidOperation("beginQuery: target doesn't match with the query type"); + return; + } + + if (*targetSlot) { + /* + * See SPECS BeginQuery.1 + */ + ErrorInvalidOperation("beginQuery: an other query already active"); + return; + } + + if (!query->HasEverBeenActive()) { + query->mType = target; + } + + MakeContextCurrent(); + + if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { + gl->fBeginQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, query->mGLName); + } else { + gl->fBeginQuery(SimulateOcclusionQueryTarget(gl, target), query->mGLName); + } + + *targetSlot = query; +} + +void +WebGLContext::EndQuery(GLenum target) +{ + if (IsContextLost()) + return; + + WebGLRefPtr* targetSlot = GetQueryTargetSlot(target, "endQuery"); + if (!targetSlot) { + return; + } + + if (!*targetSlot || + target != (*targetSlot)->mType) + { + /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt + * marks the end of the sequence of commands to be tracked for the query type + * given by . The active query object for is updated to + * indicate that query results are not available, and the active query object + * name for is reset to zero. When the commands issued prior to + * EndQueryEXT have completed and a final query result is available, the + * query object active when EndQueryEXT is called is updated by the GL. The + * query object is updated to indicate that the query results are available + * and to contain the query result. If the active query object name for + * is zero when EndQueryEXT is called, the error INVALID_OPERATION + * is generated. + */ + ErrorInvalidOperation("endQuery: There is no active query of type %s.", + GetQueryTargetEnumString(target)); + return; + } + + MakeContextCurrent(); + + if (target == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { + gl->fEndQuery(LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); + } else { + gl->fEndQuery(SimulateOcclusionQueryTarget(gl, target)); + } + + *targetSlot = nullptr; +} + +bool +WebGLContext::IsQuery(WebGLQuery *query) +{ + if (IsContextLost()) + return false; + + if (!query) + return false; + + return ValidateObjectAllowDeleted("isQuery", query) && + !query->IsDeleted() && + query->HasEverBeenActive(); +} + +already_AddRefed +WebGLContext::GetQuery(GLenum target, GLenum pname) +{ + if (IsContextLost()) + return nullptr; + + WebGLRefPtr* targetSlot = GetQueryTargetSlot(target, "getQuery"); + if (!targetSlot) { + return nullptr; + } + + if (pname != LOCAL_GL_CURRENT_QUERY) { + /* OpenGL ES 3.0 spec 6.1.7 + * pname must be CURRENT_QUERY. + */ + ErrorInvalidEnum("getQuery: pname must be CURRENT_QUERY"); + return nullptr; + } + + nsRefPtr tmp = targetSlot->get(); + return tmp.forget(); +} + +JS::Value +WebGLContext::GetQueryObject(JSContext* cx, WebGLQuery *query, GLenum pname) +{ + if (IsContextLost()) + return JS::NullValue(); + + if (!query) { + /* OpenGL ES 3.0 spec 6.1.7 (spec getQueryObject 1) + * If id is not the name of a query object, or if the query object named by id is + * currently active, then an INVALID_OPERATION error is generated. pname must be + * QUERY_RESULT or QUERY_RESULT_AVAILABLE. + */ + ErrorInvalidOperation("getQueryObject: query should not be null"); + return JS::NullValue(); + } + + if (query->IsDeleted()) { + // See (spec getQueryObject 1) + ErrorInvalidOperation("getQueryObject: query has been deleted"); + return JS::NullValue(); + } + + if (query->IsActive()) { + // See (spec getQueryObject 1) + ErrorInvalidOperation("getQueryObject: query is active"); + return JS::NullValue(); + } + + if (!query->HasEverBeenActive()) { + /* See (spec getQueryObject 1) + * If this instance of WebGLQuery has never been active before, that mean that + * query->mGLName is not a query object yet. + */ + ErrorInvalidOperation("getQueryObject: query has never been active"); + return JS::NullValue(); + } + + switch (pname) + { + case LOCAL_GL_QUERY_RESULT_AVAILABLE: + { + GLuint returned = 0; + + MakeContextCurrent(); + gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned); + + return JS::BooleanValue(returned != 0); + } + + case LOCAL_GL_QUERY_RESULT: + { + GLuint returned = 0; + + MakeContextCurrent(); + gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT, &returned); + + if (query->mType == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) { + return JS::NumberValue(uint32_t(returned)); + } + + /* + * test (returned != 0) is important because ARB_occlusion_query on desktop drivers + * return the number of samples drawed when the OpenGL ES extension + * ARB_occlusion_query_boolean return only a boolean if a sample has been drawed. + */ + return JS::BooleanValue(returned != 0); + } + + default: + break; + } + + ErrorInvalidEnum("getQueryObject: pname must be QUERY_RESULT{_AVAILABLE}"); + return JS::NullValue(); +} + +WebGLRefPtr* +WebGLContext::GetQueryTargetSlot(GLenum target, const char* infos) +{ + switch (target) { + case LOCAL_GL_ANY_SAMPLES_PASSED: + case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE: + return &mActiveOcclusionQuery; + case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: + return &mActiveTransformFeedbackQuery; + } + + ErrorInvalidEnum("%s: unknown query target", infos); + return nullptr; +} + +