mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-24 21:58:06 +00:00
Bug 1308057 - Merge WebGLTimerQuery into WebGLQuery. - r=ethlin
MozReview-Commit-ID: 88b8DLd2uJ6
This commit is contained in:
parent
bd6c33a960
commit
b054199751
@ -38,12 +38,6 @@ WebGL1Context::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
|
||||
return dom::WebGLRenderingContextBinding::Wrap(cx, this, givenProto);
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL1Context::ValidateQueryTarget(GLenum target, const char* info)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
nsresult
|
||||
|
@ -36,7 +36,6 @@ private:
|
||||
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type,
|
||||
uint32_t* alignment,
|
||||
const char* info) override;
|
||||
virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
|
||||
};
|
||||
|
||||
|
@ -302,18 +302,6 @@ public:
|
||||
|
||||
void ClearBufferfi(GLenum buffer, GLint drawBuffer, GLfloat depth, GLint stencil);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Query Objects - WebGL2ContextQueries.cpp
|
||||
|
||||
already_AddRefed<WebGLQuery> CreateQuery();
|
||||
void DeleteQuery(WebGLQuery* query);
|
||||
bool IsQuery(WebGLQuery* query);
|
||||
void BeginQuery(GLenum target, WebGLQuery* query);
|
||||
void EndQuery(GLenum target);
|
||||
already_AddRefed<WebGLQuery> GetQuery(GLenum target, GLenum pname);
|
||||
void GetQueryParameter(JSContext*, WebGLQuery* query, GLenum pname, JS::MutableHandleValue retval);
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Sampler Objects - WebGL2ContextSamplers.cpp
|
||||
|
||||
@ -410,7 +398,6 @@ private:
|
||||
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type,
|
||||
uint32_t* alignment,
|
||||
const char* info) override;
|
||||
virtual bool ValidateQueryTarget(GLenum target, const char* info) override;
|
||||
virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
|
||||
};
|
||||
|
||||
|
@ -22,58 +22,35 @@ namespace mozilla {
|
||||
* implementation-dependent cases.
|
||||
*/
|
||||
|
||||
static const char*
|
||||
GetQueryTargetEnumString(GLenum target)
|
||||
WebGLRefPtr<WebGLQuery>*
|
||||
WebGLContext::ValidateQuerySlotByTarget(const char* funcName, 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;
|
||||
if (IsWebGL2()) {
|
||||
switch (target) {
|
||||
case LOCAL_GL_ANY_SAMPLES_PASSED:
|
||||
case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
|
||||
return &mQuerySlot_SamplesPassed;
|
||||
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
|
||||
return &mQuerySlot_TFPrimsWritten;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "Unknown query `target`.");
|
||||
return "UNKNOWN_QUERY_TARGET";
|
||||
}
|
||||
if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
|
||||
switch (target) {
|
||||
case LOCAL_GL_TIME_ELAPSED_EXT:
|
||||
return &mQuerySlot_TimeElapsed;
|
||||
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return LOCAL_GL_SAMPLES_PASSED;
|
||||
}
|
||||
|
||||
WebGLRefPtr<WebGLQuery>&
|
||||
WebGLContext::GetQuerySlotByTarget(GLenum target)
|
||||
{
|
||||
/* This function assumes that target has been validated for either
|
||||
* WebGL1 or WebGL2.
|
||||
*/
|
||||
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;
|
||||
|
||||
default:
|
||||
MOZ_CRASH("GFX: Should not get here.");
|
||||
}
|
||||
ErrorInvalidEnum("%s: Bad `target`.", funcName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -81,329 +58,179 @@ WebGLContext::GetQuerySlotByTarget(GLenum target)
|
||||
// Query Objects
|
||||
|
||||
already_AddRefed<WebGLQuery>
|
||||
WebGL2Context::CreateQuery()
|
||||
WebGLContext::CreateQuery(const char* funcName)
|
||||
{
|
||||
if (!funcName) {
|
||||
funcName = "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.
|
||||
*/
|
||||
}
|
||||
|
||||
RefPtr<WebGLQuery> globj = new WebGLQuery(this);
|
||||
|
||||
return globj.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::DeleteQuery(WebGLQuery* query)
|
||||
WebGLContext::DeleteQuery(WebGLQuery* query, const char* funcName)
|
||||
{
|
||||
if (!funcName) {
|
||||
funcName = "deleteQuery";
|
||||
}
|
||||
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!query)
|
||||
return;
|
||||
|
||||
if (query->IsDeleted())
|
||||
if (!ValidateObjectAllowDeleted(funcName, query))
|
||||
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();
|
||||
query->DeleteQuery();
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL2Context::IsQuery(WebGLQuery* query)
|
||||
WebGLContext::IsQuery(const WebGLQuery* query, const char* funcName)
|
||||
{
|
||||
if (!funcName) {
|
||||
funcName = "isQuery";
|
||||
}
|
||||
|
||||
if (IsContextLost())
|
||||
return false;
|
||||
|
||||
if (!query)
|
||||
return false;
|
||||
|
||||
return (ValidateObjectAllowDeleted("isQuery", query) &&
|
||||
!query->IsDeleted() &&
|
||||
query->HasEverBeenActive());
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::BeginQuery(GLenum target, WebGLQuery* query)
|
||||
{
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateQueryTarget(target, "beginQuery"))
|
||||
return;
|
||||
|
||||
if (!query) {
|
||||
/* From GLES's EXT_occlusion_query_boolean:
|
||||
* BeginQueryEXT sets the active query object name for the query
|
||||
* type given by <target> to <id>. If BeginQueryEXT is called with
|
||||
* an <id> of zero, if the active query object name for <target> 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 <id> is the name of an existing
|
||||
* query object whose type does not match <target>, or if <id> 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()) {
|
||||
/* From GLES's EXT_occlusion_query_boolean:
|
||||
* BeginQueryEXT fails and an INVALID_OPERATION error is generated
|
||||
* if <id> 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)
|
||||
{
|
||||
ErrorInvalidOperation("beginQuery: Target doesn't match with the query"
|
||||
" type.");
|
||||
return;
|
||||
}
|
||||
|
||||
WebGLRefPtr<WebGLQuery>& querySlot = GetQuerySlotByTarget(target);
|
||||
WebGLQuery* activeQuery = querySlot.get();
|
||||
if (activeQuery)
|
||||
return ErrorInvalidOperation("beginQuery: An other query already active.");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
UpdateBoundQuery(target, query);
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::EndQuery(GLenum target)
|
||||
{
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateQueryTarget(target, "endQuery"))
|
||||
return;
|
||||
|
||||
WebGLRefPtr<WebGLQuery>& querySlot = GetQuerySlotByTarget(target);
|
||||
WebGLQuery* activeQuery = querySlot.get();
|
||||
|
||||
if (!activeQuery || target != activeQuery->mType)
|
||||
{
|
||||
/* From GLES's EXT_occlusion_query_boolean:
|
||||
* marks the end of the sequence of commands to be tracked for the
|
||||
* query type given by <target>. The active query object for
|
||||
* <target> is updated to indicate that query results are not
|
||||
* available, and the active query object name for <target> 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 <target> 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));
|
||||
}
|
||||
|
||||
UpdateBoundQuery(target, nullptr);
|
||||
NS_DispatchToCurrentThread(new WebGLQuery::AvailableRunnable(activeQuery));
|
||||
}
|
||||
|
||||
already_AddRefed<WebGLQuery>
|
||||
WebGL2Context::GetQuery(GLenum target, GLenum pname)
|
||||
{
|
||||
if (IsContextLost())
|
||||
return nullptr;
|
||||
|
||||
if (!ValidateQueryTarget(target, "getQuery"))
|
||||
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;
|
||||
}
|
||||
|
||||
WebGLRefPtr<WebGLQuery>& targetSlot = GetQuerySlotByTarget(target);
|
||||
RefPtr<WebGLQuery> tmp = targetSlot.get();
|
||||
if (tmp && tmp->mType != target) {
|
||||
// Query in slot doesn't match target
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return tmp.forget();
|
||||
}
|
||||
|
||||
static bool
|
||||
ValidateQueryEnum(WebGLContext* webgl, GLenum pname, const char* info)
|
||||
{
|
||||
switch (pname) {
|
||||
case LOCAL_GL_QUERY_RESULT_AVAILABLE:
|
||||
case LOCAL_GL_QUERY_RESULT:
|
||||
return true;
|
||||
|
||||
default:
|
||||
webgl->ErrorInvalidEnum("%s: invalid pname: %s", info, webgl->EnumName(pname));
|
||||
if (!ValidateObjectAllowDeleted("isQuery", query))
|
||||
return false;
|
||||
}
|
||||
|
||||
return query->IsQuery();
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::GetQueryParameter(JSContext*, WebGLQuery* query, GLenum pname,
|
||||
JS::MutableHandleValue retval)
|
||||
WebGLContext::BeginQuery(GLenum target, WebGLQuery* query, const char* funcName)
|
||||
{
|
||||
retval.set(JS::NullValue());
|
||||
if (!funcName) {
|
||||
funcName = "beginQuery";
|
||||
}
|
||||
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateQueryEnum(this, pname, "getQueryParameter"))
|
||||
if (!ValidateObject(funcName, query))
|
||||
return;
|
||||
|
||||
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.");
|
||||
const auto& slot = ValidateQuerySlotByTarget(funcName, target);
|
||||
if (!slot)
|
||||
return;
|
||||
|
||||
if (*slot)
|
||||
return ErrorInvalidOperation("%s: Query target already active.", funcName);
|
||||
|
||||
////
|
||||
|
||||
if (!query->BeginQuery(target))
|
||||
return;
|
||||
|
||||
*slot = query;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::EndQuery(GLenum target, const char* funcName)
|
||||
{
|
||||
if (!funcName) {
|
||||
funcName = "endQuery";
|
||||
}
|
||||
|
||||
if (query->IsDeleted()) {
|
||||
// See (spec getQueryObject 1)
|
||||
ErrorInvalidOperation("getQueryObject: `query` has been deleted.");
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
const auto& slot = ValidateQuerySlotByTarget(funcName, target);
|
||||
if (!slot)
|
||||
return;
|
||||
|
||||
const auto& query = *slot;
|
||||
if (!query)
|
||||
return ErrorInvalidOperation("%s: Query target not active.", funcName);
|
||||
|
||||
query->EndQuery();
|
||||
|
||||
*slot = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::GetQuery(JSContext* cx, GLenum target, GLenum pname,
|
||||
JS::MutableHandleValue retval, const char* funcName)
|
||||
{
|
||||
if (!funcName) {
|
||||
funcName = "getQuery";
|
||||
}
|
||||
|
||||
if (query->IsActive()) {
|
||||
// See (spec getQueryObject 1)
|
||||
ErrorInvalidOperation("getQueryObject: `query` is active.");
|
||||
retval.setNull();
|
||||
if (IsContextLost())
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
switch (pname) {
|
||||
case LOCAL_GL_CURRENT_QUERY_EXT:
|
||||
{
|
||||
const auto& slot = ValidateQuerySlotByTarget(funcName, target);
|
||||
if (!slot || !*slot)
|
||||
return;
|
||||
|
||||
// We must wait for an event loop before the query can be available
|
||||
if (!query->mCanBeAvailable && !gfxPrefs::WebGLImmediateQueries()) {
|
||||
if (pname == LOCAL_GL_QUERY_RESULT_AVAILABLE) {
|
||||
retval.set(JS::BooleanValue(false));
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
dom::GetOrCreateDOMReflector(cx, slot->get(), &v);
|
||||
retval.set(v);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
MakeContextCurrent();
|
||||
GLuint returned = 0;
|
||||
switch (pname) {
|
||||
case LOCAL_GL_QUERY_RESULT_AVAILABLE:
|
||||
gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned);
|
||||
retval.set(JS::BooleanValue(returned != 0));
|
||||
return;
|
||||
case LOCAL_GL_QUERY_COUNTER_BITS_EXT:
|
||||
if (!IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query))
|
||||
break;
|
||||
|
||||
case LOCAL_GL_QUERY_RESULT:
|
||||
gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT, &returned);
|
||||
|
||||
if (query->mType == LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN) {
|
||||
retval.set(JS::NumberValue(returned));
|
||||
if (target != LOCAL_GL_TIME_ELAPSED_EXT &&
|
||||
target != LOCAL_GL_TIMESTAMP_EXT)
|
||||
{
|
||||
ErrorInvalidEnum("%s: Bad pname for target.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
retval.set(JS::BooleanValue(returned != 0));
|
||||
{
|
||||
GLint bits = 0;
|
||||
gl->fGetQueryiv(target, pname, &bits);
|
||||
|
||||
if (!Has64BitTimestamps() && bits > 32) {
|
||||
bits = 32;
|
||||
}
|
||||
retval.set(JS::Int32Value(bits));
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ErrorInvalidEnum("getQueryObject: `pname` must be QUERY_RESULT{_AVAILABLE}.");
|
||||
ErrorInvalidEnum("%s: Bad pname.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
WebGL2Context::UpdateBoundQuery(GLenum target, WebGLQuery* query)
|
||||
WebGLContext::GetQueryParameter(JSContext*, const WebGLQuery* query, GLenum pname,
|
||||
JS::MutableHandleValue retval, const char* funcName)
|
||||
{
|
||||
WebGLRefPtr<WebGLQuery>& querySlot = GetQuerySlotByTarget(target);
|
||||
querySlot = query;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGL2Context::ValidateQueryTarget(GLenum target, const char* info)
|
||||
{
|
||||
switch (target) {
|
||||
case LOCAL_GL_ANY_SAMPLES_PASSED:
|
||||
case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
|
||||
case LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
|
||||
return true;
|
||||
|
||||
default:
|
||||
ErrorInvalidEnumInfo(info, target);
|
||||
return false;
|
||||
if (!funcName) {
|
||||
funcName = "getQueryParameter";
|
||||
}
|
||||
|
||||
retval.setNull();
|
||||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (!ValidateObject(funcName, query))
|
||||
return;
|
||||
|
||||
query->GetQueryParameter(pname, retval);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -71,7 +71,6 @@
|
||||
#include "WebGLSampler.h"
|
||||
#include "WebGLShader.h"
|
||||
#include "WebGLSync.h"
|
||||
#include "WebGLTimerQuery.h"
|
||||
#include "WebGLTransformFeedback.h"
|
||||
#include "WebGLVertexArray.h"
|
||||
#include "WebGLVertexAttribData.h"
|
||||
@ -252,13 +251,16 @@ WebGLContext::DestroyResourcesAndContext()
|
||||
mActiveProgramLinkInfo = nullptr;
|
||||
mBoundDrawFramebuffer = nullptr;
|
||||
mBoundReadFramebuffer = nullptr;
|
||||
mActiveOcclusionQuery = nullptr;
|
||||
mBoundRenderbuffer = nullptr;
|
||||
mBoundVertexArray = nullptr;
|
||||
mDefaultVertexArray = nullptr;
|
||||
mBoundTransformFeedback = nullptr;
|
||||
mDefaultTransformFeedback = nullptr;
|
||||
|
||||
mQuerySlot_SamplesPassed = nullptr;
|
||||
mQuerySlot_TFPrimsWritten = nullptr;
|
||||
mQuerySlot_TimeElapsed = nullptr;
|
||||
|
||||
mIndexedUniformBufferBindings.clear();
|
||||
|
||||
//////
|
||||
@ -272,7 +274,6 @@ WebGLContext::DestroyResourcesAndContext()
|
||||
ClearLinkedList(mShaders);
|
||||
ClearLinkedList(mSyncs);
|
||||
ClearLinkedList(mTextures);
|
||||
ClearLinkedList(mTimerQueries);
|
||||
ClearLinkedList(mTransformFeedbacks);
|
||||
ClearLinkedList(mVertexArrays);
|
||||
|
||||
@ -1647,7 +1648,7 @@ WebGLContext::DummyReadFramebufferOperation(const char* funcName)
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContext::HasTimestampBits() const
|
||||
WebGLContext::Has64BitTimestamps() const
|
||||
{
|
||||
// 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or GLES3+.
|
||||
return gl->IsSupported(GLFeature::sync);
|
||||
@ -2601,8 +2602,9 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
|
||||
mBoundRenderbuffer,
|
||||
mBoundVertexArray,
|
||||
mDefaultVertexArray,
|
||||
mActiveOcclusionQuery,
|
||||
mActiveTransformFeedbackQuery)
|
||||
mQuerySlot_SamplesPassed,
|
||||
mQuerySlot_TFPrimsWritten,
|
||||
mQuerySlot_TimeElapsed)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
|
@ -103,7 +103,6 @@ class WebGLShader;
|
||||
class WebGLShaderPrecisionFormat;
|
||||
class WebGLSync;
|
||||
class WebGLTexture;
|
||||
class WebGLTimerQuery;
|
||||
class WebGLTransformFeedback;
|
||||
class WebGLUniformLocation;
|
||||
class WebGLVertexArray;
|
||||
@ -446,7 +445,7 @@ public:
|
||||
|
||||
// a number that increments every time we have an event that causes
|
||||
// all context resources to be lost.
|
||||
uint32_t Generation() { return mGeneration.value(); }
|
||||
uint32_t Generation() const { return mGeneration.value(); }
|
||||
|
||||
// This is similar to GLContext::ClearSafely, but tries to minimize the
|
||||
// amount of work it does.
|
||||
@ -934,10 +933,24 @@ protected:
|
||||
// -----------------------------------------------------------------------------
|
||||
// Queries (WebGL2ContextQueries.cpp)
|
||||
protected:
|
||||
WebGLRefPtr<WebGLQuery>& GetQuerySlotByTarget(GLenum target);
|
||||
WebGLRefPtr<WebGLQuery> mQuerySlot_SamplesPassed;
|
||||
WebGLRefPtr<WebGLQuery> mQuerySlot_TFPrimsWritten;
|
||||
WebGLRefPtr<WebGLQuery> mQuerySlot_TimeElapsed;
|
||||
|
||||
WebGLRefPtr<WebGLQuery>*
|
||||
ValidateQuerySlotByTarget(const char* funcName, GLenum target);
|
||||
|
||||
public:
|
||||
already_AddRefed<WebGLQuery> CreateQuery(const char* funcName = nullptr);
|
||||
void DeleteQuery(WebGLQuery* query, const char* funcName = nullptr);
|
||||
bool IsQuery(const WebGLQuery* query, const char* funcName = nullptr);
|
||||
void BeginQuery(GLenum target, WebGLQuery* query, const char* funcName = nullptr);
|
||||
void EndQuery(GLenum target, const char* funcName = nullptr);
|
||||
void GetQuery(JSContext* cx, GLenum target, GLenum pname,
|
||||
JS::MutableHandleValue retval, const char* funcName = nullptr);
|
||||
void GetQueryParameter(JSContext* cx, const WebGLQuery* query, GLenum pname,
|
||||
JS::MutableHandleValue retval, const char* funcName = nullptr);
|
||||
|
||||
WebGLRefPtr<WebGLQuery> mActiveOcclusionQuery;
|
||||
WebGLRefPtr<WebGLQuery> mActiveTransformFeedbackQuery;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// State and State Requests (WebGLContextState.cpp)
|
||||
@ -1610,27 +1623,27 @@ protected:
|
||||
|
||||
// Returns false if `object` is null or not valid.
|
||||
template<class ObjectType>
|
||||
bool ValidateObject(const char* info, ObjectType* object);
|
||||
bool ValidateObject(const char* info, const ObjectType* object);
|
||||
|
||||
// Returns false if `object` is not valid. Considers null to be valid.
|
||||
template<class ObjectType>
|
||||
bool ValidateObjectAllowNull(const char* info, ObjectType* object);
|
||||
bool ValidateObjectAllowNull(const char* info, const ObjectType* object);
|
||||
|
||||
// Returns false if `object` is not valid, but considers deleted objects and
|
||||
// null objects valid.
|
||||
template<class ObjectType>
|
||||
bool ValidateObjectAllowDeletedOrNull(const char* info, ObjectType* object);
|
||||
bool ValidateObjectAllowDeletedOrNull(const char* info, const ObjectType* object);
|
||||
|
||||
// Returns false if `object` is null or not valid, but considers deleted
|
||||
// objects valid.
|
||||
template<class ObjectType>
|
||||
bool ValidateObjectAllowDeleted(const char* info, ObjectType* object);
|
||||
bool ValidateObjectAllowDeleted(const char* info, const ObjectType* object);
|
||||
|
||||
private:
|
||||
// Like ValidateObject, but only for cases when `object` is known to not be
|
||||
// null already.
|
||||
template<class ObjectType>
|
||||
bool ValidateObjectAssumeNonNull(const char* info, ObjectType* object);
|
||||
bool ValidateObjectAssumeNonNull(const char* info, const ObjectType* object);
|
||||
|
||||
private:
|
||||
// -------------------------------------------------------------------------
|
||||
@ -1638,7 +1651,6 @@ private:
|
||||
virtual WebGLVertexArray* CreateVertexArrayImpl();
|
||||
|
||||
virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, uint32_t* alignment, const char* info) = 0;
|
||||
virtual bool ValidateQueryTarget(GLenum usage, const char* info) = 0;
|
||||
virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
|
||||
|
||||
public:
|
||||
@ -1675,7 +1687,6 @@ protected:
|
||||
LinkedList<WebGLShader> mShaders;
|
||||
LinkedList<WebGLSync> mSyncs;
|
||||
LinkedList<WebGLTexture> mTextures;
|
||||
LinkedList<WebGLTimerQuery> mTimerQueries;
|
||||
LinkedList<WebGLTransformFeedback> mTransformFeedbacks;
|
||||
LinkedList<WebGLVertexArray> mVertexArrays;
|
||||
|
||||
@ -1790,7 +1801,7 @@ protected:
|
||||
bool mNeedsFakeNoStencil;
|
||||
bool mNeedsEmulatedLoneDepthStencil;
|
||||
|
||||
bool HasTimestampBits() const;
|
||||
bool Has64BitTimestamps() const;
|
||||
|
||||
struct ScopedMaskWorkaround {
|
||||
WebGLContext& mWebGL;
|
||||
@ -1898,7 +1909,6 @@ public:
|
||||
friend class WebGLSampler;
|
||||
friend class WebGLShader;
|
||||
friend class WebGLSync;
|
||||
friend class WebGLTimerQuery;
|
||||
friend class WebGLTransformFeedback;
|
||||
friend class WebGLUniformLocation;
|
||||
friend class WebGLVertexArray;
|
||||
@ -1920,7 +1930,7 @@ ToSupports(WebGLContext* webgl)
|
||||
template<class ObjectType>
|
||||
inline bool
|
||||
WebGLContext::ValidateObjectAllowDeletedOrNull(const char* info,
|
||||
ObjectType* object)
|
||||
const ObjectType* object)
|
||||
{
|
||||
if (object && !object->IsCompatibleWithContext(this)) {
|
||||
ErrorInvalidOperation("%s: object from different WebGL context "
|
||||
@ -1934,7 +1944,7 @@ WebGLContext::ValidateObjectAllowDeletedOrNull(const char* info,
|
||||
|
||||
template<class ObjectType>
|
||||
inline bool
|
||||
WebGLContext::ValidateObjectAssumeNonNull(const char* info, ObjectType* object)
|
||||
WebGLContext::ValidateObjectAssumeNonNull(const char* info, const ObjectType* object)
|
||||
{
|
||||
MOZ_ASSERT(object);
|
||||
|
||||
@ -1951,7 +1961,7 @@ WebGLContext::ValidateObjectAssumeNonNull(const char* info, ObjectType* object)
|
||||
|
||||
template<class ObjectType>
|
||||
inline bool
|
||||
WebGLContext::ValidateObjectAllowNull(const char* info, ObjectType* object)
|
||||
WebGLContext::ValidateObjectAllowNull(const char* info, const ObjectType* object)
|
||||
{
|
||||
if (!object)
|
||||
return true;
|
||||
@ -1961,7 +1971,7 @@ WebGLContext::ValidateObjectAllowNull(const char* info, ObjectType* object)
|
||||
|
||||
template<class ObjectType>
|
||||
inline bool
|
||||
WebGLContext::ValidateObjectAllowDeleted(const char* info, ObjectType* object)
|
||||
WebGLContext::ValidateObjectAllowDeleted(const char* info, const ObjectType* object)
|
||||
{
|
||||
if (!object) {
|
||||
ErrorInvalidValue("%s: null object passed as argument", info);
|
||||
@ -1973,7 +1983,7 @@ WebGLContext::ValidateObjectAllowDeleted(const char* info, ObjectType* object)
|
||||
|
||||
template<class ObjectType>
|
||||
inline bool
|
||||
WebGLContext::ValidateObject(const char* info, ObjectType* object)
|
||||
WebGLContext::ValidateObject(const char* info, const ObjectType* object)
|
||||
{
|
||||
if (!object) {
|
||||
ErrorInvalidValue("%s: null object passed as argument", info);
|
||||
|
@ -264,24 +264,31 @@ WebGLContext::GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv)
|
||||
}
|
||||
}
|
||||
|
||||
if (IsWebGL2() || IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
|
||||
if (pname == LOCAL_GL_TIMESTAMP_EXT) {
|
||||
GLuint64 iv = 0;
|
||||
if (HasTimestampBits()) {
|
||||
gl->fGetInteger64v(pname, (GLint64*)&iv);
|
||||
} else {
|
||||
GenerateWarning("QUERY_COUNTER_BITS_EXT for TIMESTAMP_EXT is 0.");
|
||||
if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
|
||||
switch (pname) {
|
||||
case LOCAL_GL_TIMESTAMP_EXT:
|
||||
{
|
||||
uint64_t val = 0;
|
||||
if (Has64BitTimestamps()) {
|
||||
gl->fGetInteger64v(pname, (GLint64*)&val);
|
||||
} else {
|
||||
gl->fGetIntegerv(pname, (GLint*)&val);
|
||||
}
|
||||
// TODO: JS doesn't support 64-bit integers. Be lossy and
|
||||
// cast to double (53 bits)
|
||||
return JS::NumberValue(val);
|
||||
}
|
||||
// TODO: JS doesn't support 64-bit integers. Be lossy and
|
||||
// cast to double (53 bits)
|
||||
return JS::NumberValue(static_cast<double>(iv));
|
||||
} else if (pname == LOCAL_GL_GPU_DISJOINT_EXT) {
|
||||
// When disjoint isn't supported, leave as false.
|
||||
realGLboolean disjoint = LOCAL_GL_FALSE;
|
||||
if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
|
||||
gl->fGetBooleanv(pname, &disjoint);
|
||||
|
||||
case LOCAL_GL_GPU_DISJOINT_EXT:
|
||||
{
|
||||
MOZ_ASSERT(gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query));
|
||||
realGLboolean val = false;
|
||||
gl->fGetBooleanv(pname, &val);
|
||||
return JS::BooleanValue(val);
|
||||
}
|
||||
return JS::BooleanValue(bool(disjoint));
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,13 +12,12 @@
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "WebGLTimerQuery.h"
|
||||
#include "WebGLQuery.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
WebGLExtensionDisjointTimerQuery::WebGLExtensionDisjointTimerQuery(WebGLContext* webgl)
|
||||
: WebGLExtensionBase(webgl)
|
||||
, mActiveQuery(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
|
||||
}
|
||||
@ -27,225 +26,92 @@ WebGLExtensionDisjointTimerQuery::~WebGLExtensionDisjointTimerQuery()
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<WebGLTimerQuery>
|
||||
WebGLExtensionDisjointTimerQuery::CreateQueryEXT()
|
||||
already_AddRefed<WebGLQuery>
|
||||
WebGLExtensionDisjointTimerQuery::CreateQueryEXT() const
|
||||
{
|
||||
const char funcName[] = "createQueryEXT";
|
||||
if (mIsLost)
|
||||
return nullptr;
|
||||
|
||||
RefPtr<WebGLTimerQuery> query = WebGLTimerQuery::Create(mContext);
|
||||
return query.forget();
|
||||
return mContext->CreateQuery(funcName);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLExtensionDisjointTimerQuery::DeleteQueryEXT(WebGLTimerQuery* query)
|
||||
WebGLExtensionDisjointTimerQuery::DeleteQueryEXT(WebGLQuery* query) const
|
||||
{
|
||||
const char funcName[] = "deleteQueryEXT";
|
||||
if (mIsLost)
|
||||
return;
|
||||
|
||||
if (!mContext->ValidateObject("deleteQueryEXT", query))
|
||||
return;
|
||||
|
||||
query->RequestDelete();
|
||||
mContext->DeleteQuery(query, funcName);
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLExtensionDisjointTimerQuery::IsQueryEXT(WebGLTimerQuery* query)
|
||||
WebGLExtensionDisjointTimerQuery::IsQueryEXT(const WebGLQuery* query) const
|
||||
{
|
||||
if (!query)
|
||||
const char funcName[] = "isQueryEXT";
|
||||
if (mIsLost)
|
||||
return false;
|
||||
|
||||
if (!mContext->ValidateObjectAllowDeleted("isQueryEXT", query))
|
||||
return false;
|
||||
|
||||
if (query->IsDeleted())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return mContext->IsQuery(query, funcName);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target,
|
||||
WebGLTimerQuery* query)
|
||||
WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target, WebGLQuery* query) const
|
||||
{
|
||||
const char funcName[] = "beginQueryEXT";
|
||||
if (mIsLost)
|
||||
return;
|
||||
|
||||
if (!mContext->ValidateObject("beginQueryEXT", query))
|
||||
return;
|
||||
|
||||
if (query->HasEverBeenBound() && query->Target() != target) {
|
||||
mContext->ErrorInvalidOperation("beginQueryEXT: Query is already bound"
|
||||
" to a different target.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
|
||||
mContext->ErrorInvalidEnumInfo("beginQueryEXT: Can only begin on target"
|
||||
" TIME_ELAPSED_EXT.", target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mActiveQuery) {
|
||||
mContext->ErrorInvalidOperation("beginQueryEXT: A query is already"
|
||||
" active.");
|
||||
return;
|
||||
}
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
gl::GLContext* gl = mContext->GL();
|
||||
gl->fBeginQuery(target, query->mGLName);
|
||||
query->mTarget = LOCAL_GL_TIME_ELAPSED_EXT;
|
||||
mActiveQuery = query;
|
||||
mContext->BeginQuery(target, query, funcName);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLExtensionDisjointTimerQuery::EndQueryEXT(GLenum target)
|
||||
WebGLExtensionDisjointTimerQuery::EndQueryEXT(GLenum target) const
|
||||
{
|
||||
const char funcName[] = "endQueryEXT";
|
||||
if (mIsLost)
|
||||
return;
|
||||
|
||||
if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
|
||||
mContext->ErrorInvalidEnumInfo("endQueryEXT: Can only end on"
|
||||
" TIME_ELAPSED_EXT.", target);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mActiveQuery) {
|
||||
mContext->ErrorInvalidOperation("endQueryEXT: A query is not active.");
|
||||
return;
|
||||
}
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
mContext->GL()->fEndQuery(target);
|
||||
mActiveQuery->QueueAvailablity();
|
||||
mActiveQuery = nullptr;
|
||||
mContext->EndQuery(target, funcName);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLTimerQuery* query,
|
||||
GLenum target)
|
||||
WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLQuery* query, GLenum target) const
|
||||
{
|
||||
const char funcName[] = "queryCounterEXT";
|
||||
if (mIsLost)
|
||||
return;
|
||||
|
||||
if (!mContext->ValidateObject("queryCounterEXT", query))
|
||||
if (!mContext->ValidateObject(funcName, query))
|
||||
return;
|
||||
|
||||
if (target != LOCAL_GL_TIMESTAMP_EXT) {
|
||||
mContext->ErrorInvalidEnumInfo("queryCounterEXT: requires"
|
||||
" TIMESTAMP_EXT.", target);
|
||||
return;
|
||||
}
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
mContext->GL()->fQueryCounter(query->mGLName, target);
|
||||
query->mTarget = LOCAL_GL_TIMESTAMP_EXT;
|
||||
query->QueueAvailablity();
|
||||
query->QueryCounter(funcName, target);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target,
|
||||
GLenum pname,
|
||||
JS::MutableHandle<JS::Value> retval)
|
||||
WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target, GLenum pname,
|
||||
JS::MutableHandleValue retval) const
|
||||
{
|
||||
const char funcName[] = "getQueryEXT";
|
||||
retval.setNull();
|
||||
if (mIsLost)
|
||||
return;
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
switch (pname) {
|
||||
case LOCAL_GL_CURRENT_QUERY_EXT:
|
||||
if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
|
||||
mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query target.",
|
||||
target);
|
||||
return;
|
||||
}
|
||||
if (mActiveQuery) {
|
||||
JS::Rooted<JS::Value> v(cx);
|
||||
dom::GetOrCreateDOMReflector(cx, mActiveQuery.get(), &v);
|
||||
retval.set(v);
|
||||
} else {
|
||||
retval.set(JS::NullValue());
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_QUERY_COUNTER_BITS_EXT:
|
||||
if (target != LOCAL_GL_TIME_ELAPSED_EXT &&
|
||||
target != LOCAL_GL_TIMESTAMP_EXT)
|
||||
{
|
||||
mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query target.",
|
||||
target);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
GLint bits = 0;
|
||||
if (mContext->HasTimestampBits()) {
|
||||
mContext->GL()->fGetQueryiv(target, pname, &bits);
|
||||
}
|
||||
retval.set(JS::Int32Value(int32_t(bits)));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query property.",
|
||||
pname);
|
||||
break;
|
||||
}
|
||||
mContext->GetQuery(cx, target, pname, retval, funcName);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLExtensionDisjointTimerQuery::GetQueryObjectEXT(JSContext* cx,
|
||||
WebGLTimerQuery* query,
|
||||
GLenum pname,
|
||||
JS::MutableHandle<JS::Value> retval)
|
||||
const WebGLQuery* query, GLenum pname,
|
||||
JS::MutableHandleValue retval) const
|
||||
{
|
||||
const char funcName[] = "getQueryObjectEXT";
|
||||
retval.setNull();
|
||||
if (mIsLost)
|
||||
return;
|
||||
|
||||
if (!mContext->ValidateObject("getQueryObjectEXT", query))
|
||||
return;
|
||||
|
||||
if (query == mActiveQuery.get()) {
|
||||
mContext->ErrorInvalidOperation("getQueryObjectEXT: Query must not be"
|
||||
" active.");
|
||||
return;
|
||||
}
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
// XXX: Note that the query result *may change* within the same task!
|
||||
// This does not follow the specification, which states that all calls
|
||||
// checking query results must return the same value until the event loop
|
||||
// is empty.
|
||||
switch (pname) {
|
||||
case LOCAL_GL_QUERY_RESULT_EXT:
|
||||
{
|
||||
GLuint64 result = 0;
|
||||
mContext->GL()->fGetQueryObjectui64v(query->mGLName,
|
||||
LOCAL_GL_QUERY_RESULT_EXT,
|
||||
&result);
|
||||
retval.set(JS::NumberValue(result));
|
||||
}
|
||||
break;
|
||||
|
||||
case LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT:
|
||||
{
|
||||
GLuint avail = 0;
|
||||
mContext->GL()->fGetQueryObjectuiv(query->mGLName,
|
||||
LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT,
|
||||
&avail);
|
||||
bool canBeAvailable = query->CanBeAvailable() || gfxPrefs::WebGLImmediateQueries();
|
||||
retval.set(JS::BooleanValue(bool(avail) && canBeAvailable));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
mContext->ErrorInvalidEnumInfo("getQueryObjectEXT: Invalid query"
|
||||
" property.", pname);
|
||||
break;
|
||||
}
|
||||
mContext->GetQueryParameter(cx, query, pname, retval, funcName);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -258,12 +124,6 @@ WebGLExtensionDisjointTimerQuery::IsSupported(const WebGLContext* webgl)
|
||||
gl->IsSupported(gl::GLFeature::query_counter); // provides GL_TIMESTAMP
|
||||
}
|
||||
|
||||
void
|
||||
WebGLExtensionDisjointTimerQuery::OnMarkLost()
|
||||
{
|
||||
mActiveQuery = nullptr;
|
||||
}
|
||||
|
||||
IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDisjointTimerQuery, EXT_disjoint_timer_query)
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -25,7 +25,6 @@ class FormatUsageAuthority;
|
||||
class WebGLContext;
|
||||
class WebGLShader;
|
||||
class WebGLQuery;
|
||||
class WebGLTimerQuery;
|
||||
class WebGLVertexArray;
|
||||
|
||||
class WebGLExtensionBase
|
||||
@ -370,29 +369,20 @@ public:
|
||||
explicit WebGLExtensionDisjointTimerQuery(WebGLContext* webgl);
|
||||
virtual ~WebGLExtensionDisjointTimerQuery();
|
||||
|
||||
already_AddRefed<WebGLTimerQuery> CreateQueryEXT();
|
||||
void DeleteQueryEXT(WebGLTimerQuery* query);
|
||||
bool IsQueryEXT(WebGLTimerQuery* query);
|
||||
void BeginQueryEXT(GLenum target, WebGLTimerQuery* query);
|
||||
void EndQueryEXT(GLenum target);
|
||||
void QueryCounterEXT(WebGLTimerQuery* query, GLenum target);
|
||||
void GetQueryEXT(JSContext *cx, GLenum target, GLenum pname,
|
||||
JS::MutableHandle<JS::Value> retval);
|
||||
void GetQueryObjectEXT(JSContext *cx, WebGLTimerQuery* query,
|
||||
GLenum pname,
|
||||
JS::MutableHandle<JS::Value> retval);
|
||||
already_AddRefed<WebGLQuery> CreateQueryEXT() const;
|
||||
void DeleteQueryEXT(WebGLQuery* query) const;
|
||||
bool IsQueryEXT(const WebGLQuery* query) const;
|
||||
void BeginQueryEXT(GLenum target, WebGLQuery* query) const;
|
||||
void EndQueryEXT(GLenum target) const;
|
||||
void QueryCounterEXT(WebGLQuery* query, GLenum target) const;
|
||||
void GetQueryEXT(JSContext* cx, GLenum target, GLenum pname,
|
||||
JS::MutableHandleValue retval) const;
|
||||
void GetQueryObjectEXT(JSContext* cx, const WebGLQuery* query,
|
||||
GLenum pname, JS::MutableHandleValue retval) const;
|
||||
|
||||
static bool IsSupported(const WebGLContext*);
|
||||
|
||||
DECL_WEBGL_EXTENSION_GOOP
|
||||
|
||||
private:
|
||||
virtual void OnMarkLost() override;
|
||||
|
||||
/**
|
||||
* An active TIME_ELAPSED query participating in a begin/end block.
|
||||
*/
|
||||
WebGLRefPtr<WebGLTimerQuery> mActiveQuery;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -16,7 +16,7 @@ WebGLContextBoundObject::WebGLContextBoundObject(WebGLContext* webgl)
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContextBoundObject::IsCompatibleWithContext(WebGLContext* other)
|
||||
WebGLContextBoundObject::IsCompatibleWithContext(const WebGLContext* other) const
|
||||
{
|
||||
return (mContext == other &&
|
||||
mContextGeneration == other->Generation());
|
||||
|
@ -267,7 +267,7 @@ class WebGLContextBoundObject
|
||||
public:
|
||||
explicit WebGLContextBoundObject(WebGLContext* webgl);
|
||||
|
||||
bool IsCompatibleWithContext(WebGLContext* other);
|
||||
bool IsCompatibleWithContext(const WebGLContext* other) const;
|
||||
|
||||
WebGLContext* const mContext;
|
||||
protected:
|
||||
|
@ -12,22 +12,41 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
JSObject*
|
||||
WebGLQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
|
||||
class AvailableRunnable final : public Runnable
|
||||
{
|
||||
return dom::WebGLQueryBinding::Wrap(cx, this, givenProto);
|
||||
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)
|
||||
: WebGLContextBoundObject(webgl)
|
||||
, mGLName(GenQuery(mContext->gl))
|
||||
, mTarget(0)
|
||||
, mIsActive(false)
|
||||
, mCanBeAvailable(false)
|
||||
, mGLName(0)
|
||||
, mType(0)
|
||||
{
|
||||
mContext->mQueries.insertBack(this);
|
||||
|
||||
mContext->MakeContextCurrent();
|
||||
mContext->gl->fGenQueries(1, &mGLName);
|
||||
}
|
||||
|
||||
void
|
||||
@ -38,15 +57,212 @@ WebGLQuery::Delete()
|
||||
LinkedListElement<WebGLQuery>::removeFrom(mContext->mQueries);
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLQuery::IsActive() const
|
||||
////
|
||||
|
||||
static GLenum
|
||||
TargetForDriver(const gl::GLContext* gl, GLenum target)
|
||||
{
|
||||
if (!HasEverBeenActive())
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLQuery::BeginQuery(GLenum target)
|
||||
{
|
||||
const char funcName[] = "beginQuery";
|
||||
|
||||
if (mTarget && target != mTarget) {
|
||||
mContext->ErrorInvalidOperation("%s: Queries cannot change targets.", funcName);
|
||||
return false;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
mTarget = target;
|
||||
mIsActive = true;
|
||||
|
||||
////
|
||||
|
||||
const auto& gl = mContext->gl;
|
||||
gl->MakeCurrent();
|
||||
|
||||
const auto driverTarget = TargetForDriver(gl, mTarget);
|
||||
gl->fBeginQuery(driverTarget, mGLName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLQuery::EndQuery()
|
||||
{
|
||||
mIsActive = false;
|
||||
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->ErrorInvalidEnum("%s: Invalid pname: %s", funcName,
|
||||
mContext->EnumName(pname));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mTarget) {
|
||||
mContext->ErrorInvalidOperation("%s: Query has never been active.", funcName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsActive)
|
||||
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
|
||||
{
|
||||
if (IsDeleted())
|
||||
return false;
|
||||
|
||||
WebGLRefPtr<WebGLQuery>& targetSlot = mContext->GetQuerySlotByTarget(mType);
|
||||
if (!mTarget)
|
||||
return false;
|
||||
|
||||
return targetSlot.get() == this;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLQuery::DeleteQuery()
|
||||
{
|
||||
if (IsDeleted())
|
||||
return;
|
||||
|
||||
if (mIsActive) {
|
||||
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)
|
||||
|
@ -20,54 +20,48 @@ class WebGLQuery final
|
||||
, public LinkedListElement<WebGLQuery>
|
||||
, public WebGLContextBoundObject
|
||||
{
|
||||
friend class AvailableRunnable;
|
||||
friend class WebGLRefCountedObject<WebGLQuery>;
|
||||
|
||||
public:
|
||||
explicit WebGLQuery(WebGLContext* webgl);
|
||||
const GLuint mGLName;
|
||||
private:
|
||||
GLenum mTarget;
|
||||
bool mIsActive;
|
||||
|
||||
class AvailableRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit AvailableRunnable(WebGLQuery* query) : mQuery(query) { }
|
||||
bool mCanBeAvailable; // Track whether the event loop has spun
|
||||
|
||||
NS_IMETHOD Run() override {
|
||||
mQuery->mCanBeAvailable = true;
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
const RefPtr<WebGLQuery> mQuery;
|
||||
};
|
||||
////
|
||||
public:
|
||||
bool IsActive() const { return mIsActive; }
|
||||
|
||||
bool IsActive() const;
|
||||
|
||||
bool HasEverBeenActive() const {
|
||||
return mType != 0;
|
||||
}
|
||||
|
||||
// WebGLRefCountedObject
|
||||
void Delete();
|
||||
|
||||
// nsWrapperCache
|
||||
WebGLContext* GetParentObject() const {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
// NS
|
||||
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
|
||||
////
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLQuery)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLQuery)
|
||||
|
||||
// Track whether the event loop has spun
|
||||
bool mCanBeAvailable;
|
||||
explicit WebGLQuery(WebGLContext* webgl);
|
||||
|
||||
private:
|
||||
~WebGLQuery() {
|
||||
DeleteOnce();
|
||||
};
|
||||
|
||||
GLuint mGLName;
|
||||
GLenum mType;
|
||||
// WebGLRefCountedObject
|
||||
void Delete();
|
||||
|
||||
friend class WebGL2Context;
|
||||
public:
|
||||
WebGLContext* GetParentObject() const { return mContext; }
|
||||
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
|
||||
|
||||
////
|
||||
|
||||
bool BeginQuery(GLenum target);
|
||||
void DeleteQuery();
|
||||
void EndQuery();
|
||||
void GetQueryParameter(GLenum pname, JS::MutableHandleValue retval) const;
|
||||
bool IsQuery() const;
|
||||
void QueryCounter(const char* funcName, GLenum target);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -1,75 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "WebGLTimerQuery.h"
|
||||
|
||||
#include "GLContext.h"
|
||||
#include "mozilla/dom/WebGLRenderingContextBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
JSObject*
|
||||
WebGLTimerQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
|
||||
{
|
||||
return dom::WebGLTimerQueryEXTBinding::Wrap(cx, this, givenProto);
|
||||
}
|
||||
|
||||
WebGLTimerQuery::WebGLTimerQuery(WebGLContext* webgl, GLuint name)
|
||||
: WebGLContextBoundObject(webgl)
|
||||
, mGLName(name)
|
||||
, mTarget(LOCAL_GL_NONE)
|
||||
, mCanBeAvailable(false)
|
||||
{
|
||||
mContext->mTimerQueries.insertBack(this);
|
||||
}
|
||||
|
||||
WebGLTimerQuery::~WebGLTimerQuery()
|
||||
{
|
||||
DeleteOnce();
|
||||
}
|
||||
|
||||
WebGLTimerQuery*
|
||||
WebGLTimerQuery::Create(WebGLContext* webgl)
|
||||
{
|
||||
GLuint name = 0;
|
||||
webgl->MakeContextCurrent();
|
||||
webgl->gl->fGenQueries(1, &name);
|
||||
return new WebGLTimerQuery(webgl, name);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLTimerQuery::Delete()
|
||||
{
|
||||
gl::GLContext* gl = mContext->GL();
|
||||
|
||||
gl->MakeCurrent();
|
||||
gl->fDeleteQueries(1, &mGLName);
|
||||
|
||||
LinkedListElement<WebGLTimerQuery>::removeFrom(mContext->mTimerQueries);
|
||||
}
|
||||
|
||||
WebGLContext*
|
||||
WebGLTimerQuery::GetParentObject() const
|
||||
{
|
||||
return mContext;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLTimerQuery::QueueAvailablity()
|
||||
{
|
||||
RefPtr<WebGLTimerQuery> self = this;
|
||||
NS_DispatchToCurrentThread(NS_NewRunnableFunction([self] { self->mCanBeAvailable = true; }));
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTimerQuery)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTimerQuery, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTimerQuery, Release)
|
||||
|
||||
} // namespace mozilla
|
@ -1,54 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef WEBGL_TIMER_QUERY_H_
|
||||
#define WEBGL_TIMER_QUERY_H_
|
||||
|
||||
#include "GLConsts.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "WebGLObjectModel.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WebGLTimerQuery final
|
||||
: public nsWrapperCache
|
||||
, public WebGLRefCountedObject<WebGLTimerQuery>
|
||||
, public LinkedListElement<WebGLTimerQuery>
|
||||
, public WebGLContextBoundObject
|
||||
{
|
||||
public:
|
||||
static WebGLTimerQuery* Create(WebGLContext* webgl);
|
||||
|
||||
void Delete();
|
||||
|
||||
bool HasEverBeenBound() const { return mTarget != LOCAL_GL_NONE; }
|
||||
bool CanBeAvailable() const { return mCanBeAvailable; }
|
||||
void QueueAvailablity();
|
||||
GLenum Target() const { return mTarget; }
|
||||
|
||||
WebGLContext* GetParentObject() const;
|
||||
|
||||
// NS
|
||||
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;
|
||||
|
||||
const GLenum mGLName;
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTimerQuery)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTimerQuery)
|
||||
|
||||
private:
|
||||
WebGLTimerQuery(WebGLContext* webgl, GLuint name);
|
||||
~WebGLTimerQuery();
|
||||
|
||||
GLenum mTarget;
|
||||
bool mCanBeAvailable;
|
||||
|
||||
friend class WebGLExtensionDisjointTimerQuery;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // WEBGL_TIMER_QUERY_H_
|
@ -148,7 +148,6 @@ UNIFIED_SOURCES += [
|
||||
'WebGLTexelConversions.cpp',
|
||||
'WebGLTexture.cpp',
|
||||
'WebGLTextureUpload.cpp',
|
||||
'WebGLTimerQuery.cpp',
|
||||
'WebGLTransformFeedback.cpp',
|
||||
'WebGLUniformLocation.cpp',
|
||||
'WebGLValidateStrings.cpp',
|
||||
|
Loading…
x
Reference in New Issue
Block a user