Commit 5aad9673 by Geoff Lang

Update Queries to return Error objects instead of calling gl::error.

BUG=angle:520 Change-Id: If8f2bb1c4de7b9cc30861a06aab1d89c97305b26 Reviewed-on: https://chromium-review.googlesource.com/216699Reviewed-by: 's avatarShannon Woods <shannonwoods@chromium.org> Tested-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 22502d52
...@@ -681,26 +681,35 @@ void Context::bindTransformFeedback(GLuint transformFeedback) ...@@ -681,26 +681,35 @@ void Context::bindTransformFeedback(GLuint transformFeedback)
mState.setTransformFeedbackBinding(getTransformFeedback(transformFeedback)); mState.setTransformFeedbackBinding(getTransformFeedback(transformFeedback));
} }
void Context::beginQuery(GLenum target, GLuint query) Error Context::beginQuery(GLenum target, GLuint query)
{ {
Query *queryObject = getQuery(query, true, target); Query *queryObject = getQuery(query, true, target);
ASSERT(queryObject); ASSERT(queryObject);
// set query as active for specified target // begin query
Error error = queryObject->begin();
if (error.isError())
{
return error;
}
// set query as active for specified target only if begin succeeded
mState.setActiveQuery(target, queryObject); mState.setActiveQuery(target, queryObject);
// begin query return Error(GL_NO_ERROR);
queryObject->begin();
} }
void Context::endQuery(GLenum target) Error Context::endQuery(GLenum target)
{ {
Query *queryObject = mState.getActiveQuery(target); Query *queryObject = mState.getActiveQuery(target);
ASSERT(queryObject); ASSERT(queryObject);
queryObject->end(); gl::Error error = queryObject->end();
// Always unbind the query, even if there was an error. This may delete the query object.
mState.setActiveQuery(target, NULL); mState.setActiveQuery(target, NULL);
return error;
} }
void Context::setFramebufferZero(Framebuffer *buffer) void Context::setFramebufferZero(Framebuffer *buffer)
......
...@@ -137,8 +137,8 @@ class Context ...@@ -137,8 +137,8 @@ class Context
void setProgramBinary(GLuint program, GLenum binaryFormat, const void *binary, GLint length); void setProgramBinary(GLuint program, GLenum binaryFormat, const void *binary, GLint length);
void bindTransformFeedback(GLuint transformFeedback); void bindTransformFeedback(GLuint transformFeedback);
void beginQuery(GLenum target, GLuint query); Error beginQuery(GLenum target, GLuint query);
void endQuery(GLenum target); Error endQuery(GLenum target);
void setFramebufferZero(Framebuffer *framebuffer); void setFramebufferZero(Framebuffer *framebuffer);
......
...@@ -19,31 +19,27 @@ Query::Query(rx::QueryImpl *impl, GLuint id) ...@@ -19,31 +19,27 @@ Query::Query(rx::QueryImpl *impl, GLuint id)
Query::~Query() Query::~Query()
{ {
delete mQuery; SafeDelete(mQuery);
} }
void Query::begin() Error Query::begin()
{ {
// TODO: Rather than keeping track of whether the query was successfully return mQuery->begin();
// created via a boolean in the GL-level Query object, we should probably
// use the error system to track these failed creations at the context level,
// and reset the active query ID for the target to 0 upon failure.
mStarted = mQuery->begin();
} }
void Query::end() Error Query::end()
{ {
mQuery->end(); return mQuery->end();
} }
GLuint Query::getResult() Error Query::getResult(GLuint *params)
{ {
return mQuery->getResult(); return mQuery->getResult(params);
} }
GLboolean Query::isResultAvailable() Error Query::isResultAvailable(GLuint *available)
{ {
return mQuery->isResultAvailable(); return mQuery->isResultAvailable(available);
} }
GLenum Query::getType() const GLenum Query::getType() const
...@@ -51,9 +47,4 @@ GLenum Query::getType() const ...@@ -51,9 +47,4 @@ GLenum Query::getType() const
return mQuery->getType(); return mQuery->getType();
} }
bool Query::isStarted() const
{
return mStarted;
}
} }
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#ifndef LIBGLESV2_QUERY_H_ #ifndef LIBGLESV2_QUERY_H_
#define LIBGLESV2_QUERY_H_ #define LIBGLESV2_QUERY_H_
#include "libGLESv2/Error.h"
#include "common/angleutils.h" #include "common/angleutils.h"
#include "common/RefCountObject.h" #include "common/RefCountObject.h"
...@@ -28,20 +29,17 @@ class Query : public RefCountObject ...@@ -28,20 +29,17 @@ class Query : public RefCountObject
Query(rx::QueryImpl *impl, GLuint id); Query(rx::QueryImpl *impl, GLuint id);
virtual ~Query(); virtual ~Query();
void begin(); Error begin();
void end(); Error end();
GLuint getResult(); Error getResult(GLuint *params);
GLboolean isResultAvailable(); Error isResultAvailable(GLuint *available);
GLenum getType() const; GLenum getType() const;
bool isStarted() const;
private: private:
DISALLOW_COPY_AND_ASSIGN(Query); DISALLOW_COPY_AND_ASSIGN(Query);
bool mStarted;
rx::QueryImpl *mQuery; rx::QueryImpl *mQuery;
}; };
......
...@@ -110,7 +110,12 @@ void __stdcall glBeginQueryEXT(GLenum target, GLuint id) ...@@ -110,7 +110,12 @@ void __stdcall glBeginQueryEXT(GLenum target, GLuint id)
return; return;
} }
context->beginQuery(target, id); gl::Error error = context->beginQuery(target, id);
if (error.isError())
{
context->recordError(error);
return;
}
} }
} }
...@@ -1451,7 +1456,12 @@ void __stdcall glEndQueryEXT(GLenum target) ...@@ -1451,7 +1456,12 @@ void __stdcall glEndQueryEXT(GLenum target)
return; return;
} }
context->endQuery(target); gl::Error error = context->endQuery(target);
if (error.isError())
{
context->recordError(error);
return;
}
} }
} }
...@@ -2604,11 +2614,27 @@ void __stdcall glGetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params) ...@@ -2604,11 +2614,27 @@ void __stdcall glGetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params)
switch(pname) switch(pname)
{ {
case GL_QUERY_RESULT_EXT: case GL_QUERY_RESULT_EXT:
params[0] = queryObject->getResult(); {
gl::Error error = queryObject->getResult(params);
if (error.isError())
{
context->recordError(error);
return;
}
}
break; break;
case GL_QUERY_RESULT_AVAILABLE_EXT: case GL_QUERY_RESULT_AVAILABLE_EXT:
params[0] = queryObject->isResultAvailable(); {
gl::Error error = queryObject->isResultAvailable(params);
if (error.isError())
{
context->recordError(error);
return;
}
}
break; break;
default: default:
context->recordError(gl::Error(GL_INVALID_ENUM)); context->recordError(gl::Error(GL_INVALID_ENUM));
return; return;
...@@ -5301,7 +5327,13 @@ void __stdcall glBeginQuery(GLenum target, GLuint id) ...@@ -5301,7 +5327,13 @@ void __stdcall glBeginQuery(GLenum target, GLuint id)
{ {
return; return;
} }
context->beginQuery(target, id);
gl::Error error = context->beginQuery(target, id);
if (error.isError())
{
context->recordError(error);
return;
}
} }
} }
...@@ -5323,7 +5355,12 @@ void __stdcall glEndQuery(GLenum target) ...@@ -5323,7 +5355,12 @@ void __stdcall glEndQuery(GLenum target)
return; return;
} }
context->endQuery(target); gl::Error error = context->endQuery(target);
if (error.isError())
{
context->recordError(error);
return;
}
} }
} }
...@@ -5388,12 +5425,26 @@ void __stdcall glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) ...@@ -5388,12 +5425,26 @@ void __stdcall glGetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params)
switch(pname) switch(pname)
{ {
case GL_QUERY_RESULT: case GL_QUERY_RESULT_EXT:
params[0] = queryObject->getResult(); {
gl::Error error = queryObject->getResult(params);
if (error.isError())
{
context->recordError(error);
return;
}
}
break; break;
case GL_QUERY_RESULT_AVAILABLE: case GL_QUERY_RESULT_AVAILABLE_EXT:
params[0] = queryObject->isResultAvailable(); {
gl::Error error = queryObject->isResultAvailable(params);
if (error.isError())
{
context->recordError(error);
return;
}
}
break; break;
default: default:
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#ifndef LIBGLESV2_RENDERER_QUERYIMPL_H_ #ifndef LIBGLESV2_RENDERER_QUERYIMPL_H_
#define LIBGLESV2_RENDERER_QUERYIMPL_H_ #define LIBGLESV2_RENDERER_QUERYIMPL_H_
#include "libGLESv2/Error.h"
#include "common/angleutils.h" #include "common/angleutils.h"
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
...@@ -22,10 +24,10 @@ class QueryImpl ...@@ -22,10 +24,10 @@ class QueryImpl
explicit QueryImpl(GLenum type) { mType = type; } explicit QueryImpl(GLenum type) { mType = type; }
virtual ~QueryImpl() { } virtual ~QueryImpl() { }
virtual bool begin() = 0; virtual gl::Error begin() = 0;
virtual void end() = 0; virtual gl::Error end() = 0;
virtual GLuint getResult() = 0; virtual gl::Error getResult(GLuint *params) = 0;
virtual GLboolean isResultAvailable() = 0; virtual gl::Error isResultAvailable(GLuint *available) = 0;
GLenum getType() const { return mType; } GLenum getType() const { return mType; }
......
...@@ -16,24 +16,13 @@ ...@@ -16,24 +16,13 @@
namespace rx namespace rx
{ {
static bool checkOcclusionQuery(ID3D11DeviceContext *context, ID3D11Query *query, UINT64 *numPixels) Query11::Query11(rx::Renderer11 *renderer, GLenum type)
: QueryImpl(type),
mResult(0),
mQueryFinished(false),
mRenderer(renderer),
mQuery(NULL)
{ {
HRESULT result = context->GetData(query, numPixels, sizeof(UINT64), 0);
return (result == S_OK);
}
static bool checkStreamOutPrimitivesWritten(ID3D11DeviceContext *context, ID3D11Query *query, UINT64 *numPrimitives)
{
D3D11_QUERY_DATA_SO_STATISTICS soStats = { 0 };
HRESULT result = context->GetData(query, &soStats, sizeof(D3D11_QUERY_DATA_SO_STATISTICS), 0);
*numPrimitives = soStats.NumPrimitivesWritten;
return (result == S_OK);
}
Query11::Query11(rx::Renderer11 *renderer, GLenum type) : QueryImpl(type), mStatus(GL_FALSE), mResult(0)
{
mRenderer = renderer;
mQuery = NULL;
} }
Query11::~Query11() Query11::~Query11()
...@@ -41,7 +30,7 @@ Query11::~Query11() ...@@ -41,7 +30,7 @@ Query11::~Query11()
SafeRelease(mQuery); SafeRelease(mQuery);
} }
bool Query11::begin() gl::Error Query11::begin()
{ {
if (mQuery == NULL) if (mQuery == NULL)
{ {
...@@ -49,70 +38,85 @@ bool Query11::begin() ...@@ -49,70 +38,85 @@ bool Query11::begin()
queryDesc.Query = gl_d3d11::ConvertQueryType(getType()); queryDesc.Query = gl_d3d11::ConvertQueryType(getType());
queryDesc.MiscFlags = 0; queryDesc.MiscFlags = 0;
if (FAILED(mRenderer->getDevice()->CreateQuery(&queryDesc, &mQuery))) HRESULT result = mRenderer->getDevice()->CreateQuery(&queryDesc, &mQuery);
if (FAILED(result))
{ {
return gl::error(GL_OUT_OF_MEMORY, false); return gl::Error(GL_OUT_OF_MEMORY, "Internal query creation failed, result: 0x%X.", result);
} }
} }
mRenderer->getDeviceContext()->Begin(mQuery); mRenderer->getDeviceContext()->Begin(mQuery);
return true; return gl::Error(GL_NO_ERROR);
} }
void Query11::end() gl::Error Query11::end()
{ {
ASSERT(mQuery); ASSERT(mQuery);
mRenderer->getDeviceContext()->End(mQuery); mRenderer->getDeviceContext()->End(mQuery);
mStatus = GL_FALSE; mQueryFinished = false;
mResult = GL_FALSE; mResult = GL_FALSE;
return gl::Error(GL_NO_ERROR);
} }
GLuint Query11::getResult() gl::Error Query11::getResult(GLuint *params)
{ {
if (mQuery != NULL) while (!mQueryFinished)
{
while (!testQuery())
{ {
Sleep(0); gl::Error error = testQuery();
// explicitly check for device loss, some drivers seem to return S_FALSE if (error.isError())
// if the device is lost
if (mRenderer->testDeviceLost(true))
{ {
return gl::error(GL_OUT_OF_MEMORY, 0); return error;
} }
if (!mQueryFinished)
{
Sleep(0);
} }
} }
return mResult; ASSERT(mQueryFinished);
*params = mResult;
return gl::Error(GL_NO_ERROR);
} }
GLboolean Query11::isResultAvailable() gl::Error Query11::isResultAvailable(GLuint *available)
{ {
if (mQuery != NULL) gl::Error error = testQuery();
if (error.isError())
{ {
testQuery(); return error;
} }
return mStatus; *available = (mQueryFinished ? GL_TRUE : GL_FALSE);
return gl::Error(GL_NO_ERROR);
} }
GLboolean Query11::testQuery() gl::Error Query11::testQuery()
{ {
if (mQuery != NULL && mStatus != GL_TRUE) if (!mQueryFinished)
{ {
ID3D11DeviceContext *context = mRenderer->getDeviceContext(); ASSERT(mQuery);
bool queryFinished = false; ID3D11DeviceContext *context = mRenderer->getDeviceContext();
switch (getType()) switch (getType())
{ {
case GL_ANY_SAMPLES_PASSED_EXT: case GL_ANY_SAMPLES_PASSED_EXT:
case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT: case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT:
{ {
UINT64 numPixels = 0; UINT64 numPixels = 0;
queryFinished = checkOcclusionQuery(context, mQuery, &numPixels); HRESULT result = context->GetData(mQuery, &numPixels, sizeof(numPixels), 0);
if (queryFinished) if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to get the data of an internal query, result: 0x%X.", result);
}
if (result == S_OK)
{ {
mQueryFinished = true;
mResult = (numPixels > 0) ? GL_TRUE : GL_FALSE; mResult = (numPixels > 0) ? GL_TRUE : GL_FALSE;
} }
} }
...@@ -120,11 +124,17 @@ GLboolean Query11::testQuery() ...@@ -120,11 +124,17 @@ GLboolean Query11::testQuery()
case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN: case GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN:
{ {
UINT64 numPrimitives = 0; D3D11_QUERY_DATA_SO_STATISTICS soStats = { 0 };
queryFinished = checkStreamOutPrimitivesWritten(context, mQuery, &numPrimitives); HRESULT result = context->GetData(mQuery, &soStats, sizeof(soStats), 0);
if (queryFinished) if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to get the data of an internal query, result: 0x%X.", result);
}
if (result == S_OK)
{ {
mResult = static_cast<GLuint>(numPrimitives); mQueryFinished = true;
mResult = static_cast<GLuint>(soStats.NumPrimitivesWritten);
} }
} }
break; break;
...@@ -134,19 +144,13 @@ GLboolean Query11::testQuery() ...@@ -134,19 +144,13 @@ GLboolean Query11::testQuery()
break; break;
} }
if (queryFinished) if (!mQueryFinished && mRenderer->testDeviceLost(true))
{ {
mStatus = GL_TRUE; return gl::Error(GL_OUT_OF_MEMORY, "Failed to test get query result, device is lost.");
} }
else if (mRenderer->testDeviceLost(true))
{
return gl::error(GL_OUT_OF_MEMORY, GL_TRUE);
}
return mStatus;
} }
return GL_TRUE; // prevent blocking when query is null return gl::Error(GL_NO_ERROR);
} }
} }
...@@ -21,18 +21,19 @@ class Query11 : public QueryImpl ...@@ -21,18 +21,19 @@ class Query11 : public QueryImpl
Query11(rx::Renderer11 *renderer, GLenum type); Query11(rx::Renderer11 *renderer, GLenum type);
virtual ~Query11(); virtual ~Query11();
virtual bool begin(); virtual gl::Error begin();
virtual void end(); virtual gl::Error end();
virtual GLuint getResult(); virtual gl::Error getResult(GLuint *params);
virtual GLboolean isResultAvailable(); virtual gl::Error isResultAvailable(GLuint *available);
private: private:
DISALLOW_COPY_AND_ASSIGN(Query11); DISALLOW_COPY_AND_ASSIGN(Query11);
GLboolean testQuery(); gl::Error testQuery();
GLuint mResult; GLuint mResult;
GLboolean mStatus;
bool mQueryFinished;
rx::Renderer11 *mRenderer; rx::Renderer11 *mRenderer;
ID3D11Query *mQuery; ID3D11Query *mQuery;
......
...@@ -15,10 +15,13 @@ ...@@ -15,10 +15,13 @@
namespace rx namespace rx
{ {
Query9::Query9(rx::Renderer9 *renderer, GLenum type) : QueryImpl(type), mStatus(GL_FALSE), mResult(0) Query9::Query9(rx::Renderer9 *renderer, GLenum type)
: QueryImpl(type),
mResult(GL_FALSE),
mQueryFinished(false),
mRenderer(renderer),
mQuery(NULL)
{ {
mRenderer = renderer;
mQuery = NULL;
} }
Query9::~Query9() Query9::~Query9()
...@@ -26,74 +29,91 @@ Query9::~Query9() ...@@ -26,74 +29,91 @@ Query9::~Query9()
SafeRelease(mQuery); SafeRelease(mQuery);
} }
bool Query9::begin() gl::Error Query9::begin()
{ {
if (mQuery == NULL) if (mQuery == NULL)
{ {
if (FAILED(mRenderer->getDevice()->CreateQuery(D3DQUERYTYPE_OCCLUSION, &mQuery))) HRESULT result = mRenderer->getDevice()->CreateQuery(D3DQUERYTYPE_OCCLUSION, &mQuery);
if (FAILED(result))
{ {
return gl::error(GL_OUT_OF_MEMORY, false); return gl::Error(GL_OUT_OF_MEMORY, "Internal query creation failed, result: 0x%X.", result);
} }
} }
HRESULT result = mQuery->Issue(D3DISSUE_BEGIN); HRESULT result = mQuery->Issue(D3DISSUE_BEGIN);
UNUSED_ASSERTION_VARIABLE(result);
ASSERT(SUCCEEDED(result)); ASSERT(SUCCEEDED(result));
return true; if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to begin internal query, result: 0x%X.", result);
}
return gl::Error(GL_NO_ERROR);
} }
void Query9::end() gl::Error Query9::end()
{ {
ASSERT(mQuery); ASSERT(mQuery);
HRESULT result = mQuery->Issue(D3DISSUE_END); HRESULT result = mQuery->Issue(D3DISSUE_END);
UNUSED_ASSERTION_VARIABLE(result);
ASSERT(SUCCEEDED(result)); ASSERT(SUCCEEDED(result));
if (FAILED(result))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to end internal query, result: 0x%X.", result);
}
mStatus = GL_FALSE; mQueryFinished = false;
mResult = GL_FALSE; mResult = GL_FALSE;
return gl::Error(GL_NO_ERROR);
} }
GLuint Query9::getResult() gl::Error Query9::getResult(GLuint *params)
{ {
if (mQuery != NULL) while (!mQueryFinished)
{
while (!testQuery())
{ {
Sleep(0); gl::Error error = testQuery();
// explicitly check for device loss if (error.isError())
// some drivers seem to return S_FALSE even if the device is lost
// instead of D3DERR_DEVICELOST like they should
if (mRenderer->testDeviceLost(true))
{ {
return gl::error(GL_OUT_OF_MEMORY, 0); return error;
} }
if (!mQueryFinished)
{
Sleep(0);
} }
} }
return mResult; ASSERT(mQueryFinished);
*params = mResult;
return gl::Error(GL_NO_ERROR);
} }
GLboolean Query9::isResultAvailable() gl::Error Query9::isResultAvailable(GLuint *available)
{ {
if (mQuery != NULL) gl::Error error = testQuery();
if (error.isError())
{ {
testQuery(); return error;
} }
return mStatus; *available = (mQueryFinished ? GL_TRUE : GL_FALSE);
return gl::Error(GL_NO_ERROR);
} }
GLboolean Query9::testQuery() gl::Error Query9::testQuery()
{ {
if (mQuery != NULL && mStatus != GL_TRUE) if (!mQueryFinished)
{ {
ASSERT(mQuery);
DWORD numPixels = 0; DWORD numPixels = 0;
HRESULT hres = mQuery->GetData(&numPixels, sizeof(DWORD), D3DGETDATA_FLUSH); HRESULT hres = mQuery->GetData(&numPixels, sizeof(DWORD), D3DGETDATA_FLUSH);
if (hres == S_OK) if (hres == S_OK)
{ {
mStatus = GL_TRUE; mQueryFinished = true;
switch (getType()) switch (getType())
{ {
...@@ -101,20 +121,24 @@ GLboolean Query9::testQuery() ...@@ -101,20 +121,24 @@ GLboolean Query9::testQuery()
case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT: case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT:
mResult = (numPixels > 0) ? GL_TRUE : GL_FALSE; mResult = (numPixels > 0) ? GL_TRUE : GL_FALSE;
break; break;
default: default:
ASSERT(false); UNREACHABLE();
break;
} }
} }
else if (d3d9::isDeviceLostError(hres)) else if (d3d9::isDeviceLostError(hres))
{ {
mRenderer->notifyDeviceLost(); mRenderer->notifyDeviceLost();
return gl::error(GL_OUT_OF_MEMORY, GL_TRUE); return gl::Error(GL_OUT_OF_MEMORY, "Failed to test get query result, device is lost.");
}
else if (mRenderer->testDeviceLost(true))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to test get query result, device is lost.");
} }
return mStatus;
} }
return GL_TRUE; // prevent blocking when query is null return gl::Error(GL_NO_ERROR);
} }
} }
...@@ -21,18 +21,18 @@ class Query9 : public QueryImpl ...@@ -21,18 +21,18 @@ class Query9 : public QueryImpl
Query9(rx::Renderer9 *renderer, GLenum type); Query9(rx::Renderer9 *renderer, GLenum type);
virtual ~Query9(); virtual ~Query9();
virtual bool begin(); virtual gl::Error begin();
virtual void end(); virtual gl::Error end();
virtual GLuint getResult(); virtual gl::Error getResult(GLuint *params);
virtual GLboolean isResultAvailable(); virtual gl::Error isResultAvailable(GLuint *available);
private: private:
DISALLOW_COPY_AND_ASSIGN(Query9); DISALLOW_COPY_AND_ASSIGN(Query9);
GLboolean testQuery(); gl::Error testQuery();
GLuint mResult; GLuint mResult;
GLboolean mStatus; bool mQueryFinished;
rx::Renderer9 *mRenderer; rx::Renderer9 *mRenderer;
IDirect3DQuery9 *mQuery; IDirect3DQuery9 *mQuery;
......
...@@ -1038,12 +1038,6 @@ bool ValidateEndQuery(gl::Context *context, GLenum target) ...@@ -1038,12 +1038,6 @@ bool ValidateEndQuery(gl::Context *context, GLenum target)
return false; return false;
} }
if (!queryObject->isStarted())
{
context->recordError(Error(GL_INVALID_OPERATION));
return false;
}
return true; return true;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment