diff options
Diffstat (limited to 'dom/canvas/WebGLQuery.cpp')
-rw-r--r-- | dom/canvas/WebGLQuery.cpp | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/dom/canvas/WebGLQuery.cpp b/dom/canvas/WebGLQuery.cpp new file mode 100644 index 000000000..ea71243c9 --- /dev/null +++ b/dom/canvas/WebGLQuery.cpp @@ -0,0 +1,270 @@ +/* -*- Mode: C++; tab-width: 20; 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 "WebGLQuery.h" + +#include "GLContext.h" +#include "mozilla/dom/WebGL2RenderingContextBinding.h" +#include "nsContentUtils.h" +#include "WebGLContext.h" + +namespace mozilla { + +class AvailableRunnable final : public Runnable +{ + const RefPtr<WebGLQuery> mQuery; + +public: + explicit AvailableRunnable(WebGLQuery* query) + : mQuery(query) + { } + + NS_IMETHOD Run() override { + mQuery->mCanBeAvailable = true; + return NS_OK; + } +}; + +//// + +static GLuint +GenQuery(gl::GLContext* gl) +{ + gl->MakeCurrent(); + + GLuint ret = 0; + gl->fGenQueries(1, &ret); + return ret; +} + +WebGLQuery::WebGLQuery(WebGLContext* webgl) + : WebGLRefCountedObject(webgl) + , mGLName(GenQuery(mContext->gl)) + , mTarget(0) + , mActiveSlot(nullptr) + , mCanBeAvailable(false) +{ + mContext->mQueries.insertBack(this); +} + +void +WebGLQuery::Delete() +{ + mContext->MakeContextCurrent(); + mContext->gl->fDeleteQueries(1, &mGLName); + LinkedListElement<WebGLQuery>::removeFrom(mContext->mQueries); +} + +//// + +static GLenum +TargetForDriver(const gl::GLContext* gl, GLenum target) +{ + switch (target) { + case LOCAL_GL_ANY_SAMPLES_PASSED: + case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE: + break; + + default: + return target; + } + + if (gl->IsSupported(gl::GLFeature::occlusion_query_boolean)) + return target; + + if (gl->IsSupported(gl::GLFeature::occlusion_query2)) + return LOCAL_GL_ANY_SAMPLES_PASSED; + + return LOCAL_GL_SAMPLES_PASSED; +} + +void +WebGLQuery::BeginQuery(GLenum target, WebGLRefPtr<WebGLQuery>& slot) +{ + const char funcName[] = "beginQuery"; + + if (mTarget && target != mTarget) { + mContext->ErrorInvalidOperation("%s: Queries cannot change targets.", funcName); + return; + } + + //// + + mTarget = target; + mActiveSlot = &slot; + *mActiveSlot = this; + + //// + + const auto& gl = mContext->gl; + gl->MakeCurrent(); + + const auto driverTarget = TargetForDriver(gl, mTarget); + gl->fBeginQuery(driverTarget, mGLName); +} + +void +WebGLQuery::EndQuery() +{ + *mActiveSlot = nullptr; + mActiveSlot = nullptr; + mCanBeAvailable = false; + + //// + + const auto& gl = mContext->gl; + gl->MakeCurrent(); + + const auto driverTarget = TargetForDriver(gl, mTarget); + gl->fEndQuery(driverTarget); + + //// + + NS_DispatchToCurrentThread(new AvailableRunnable(this)); +} + +void +WebGLQuery::GetQueryParameter(GLenum pname, JS::MutableHandleValue retval) const +{ + const char funcName[] = "getQueryParameter"; + + switch (pname) { + case LOCAL_GL_QUERY_RESULT_AVAILABLE: + case LOCAL_GL_QUERY_RESULT: + break; + + default: + mContext->ErrorInvalidEnumArg(funcName, "pname", pname); + return; + } + + if (!mTarget) { + mContext->ErrorInvalidOperation("%s: Query has never been active.", funcName); + return; + } + + if (mActiveSlot) + return mContext->ErrorInvalidOperation("%s: Query is still active.", funcName); + + // End of validation + //// + + // We must usually wait for an event loop before the query can be available. + const bool canBeAvailable = (mCanBeAvailable || gfxPrefs::WebGLImmediateQueries()); + if (!canBeAvailable) { + if (pname == LOCAL_GL_QUERY_RESULT_AVAILABLE) { + retval.set(JS::BooleanValue(false)); + } + return; + } + + const auto& gl = mContext->gl; + gl->MakeCurrent(); + + uint64_t val = 0; + switch (pname) { + case LOCAL_GL_QUERY_RESULT_AVAILABLE: + gl->fGetQueryObjectuiv(mGLName, pname, (GLuint*)&val); + retval.set(JS::BooleanValue(bool(val))); + return; + + case LOCAL_GL_QUERY_RESULT: + switch (mTarget) { + case LOCAL_GL_TIME_ELAPSED_EXT: + case LOCAL_GL_TIMESTAMP_EXT: + if (mContext->Has64BitTimestamps()) { + gl->fGetQueryObjectui64v(mGLName, pname, &val); + break; + } + MOZ_FALLTHROUGH; + + default: + gl->fGetQueryObjectuiv(mGLName, LOCAL_GL_QUERY_RESULT, (GLuint*)&val); + break; + } + + switch (mTarget) { + case LOCAL_GL_ANY_SAMPLES_PASSED: + case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE: + retval.set(JS::BooleanValue(bool(val))); + break; + + case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: + case LOCAL_GL_TIME_ELAPSED_EXT: + case LOCAL_GL_TIMESTAMP_EXT: + retval.set(JS::NumberValue(val)); + break; + + default: + MOZ_CRASH("Bad `mTarget`."); + } + return; + + default: + MOZ_CRASH("Bad `pname`."); + } +} + +bool +WebGLQuery::IsQuery() const +{ + MOZ_ASSERT(!IsDeleted()); + + if (!mTarget) + return false; + + return true; +} + +void +WebGLQuery::DeleteQuery() +{ + MOZ_ASSERT(!IsDeleteRequested()); + + if (mActiveSlot) { + EndQuery(); + } + + RequestDelete(); +} + +void +WebGLQuery::QueryCounter(const char* funcName, GLenum target) +{ + if (target != LOCAL_GL_TIMESTAMP_EXT) { + mContext->ErrorInvalidEnum("%s: `target` must be TIMESTAMP_EXT.", funcName, + target); + return; + } + + if (mTarget && target != mTarget) { + mContext->ErrorInvalidOperation("%s: Queries cannot change targets.", funcName); + return; + } + + mTarget = target; + mCanBeAvailable = false; + + const auto& gl = mContext->gl; + gl->MakeCurrent(); + gl->fQueryCounter(mGLName, mTarget); + + NS_DispatchToCurrentThread(new AvailableRunnable(this)); +} + +//// + +JSObject* +WebGLQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) +{ + return dom::WebGLQueryBinding::Wrap(cx, this, givenProto); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLQuery) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLQuery, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLQuery, Release) + +} // namespace mozilla |