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