Bug 996266 - Support testing for errors at the GLContext level. - r=kamidphish

This commit is contained in:
Jeff Gilbert 2014-08-27 16:18:43 -07:00
parent 9451a4fa01
commit 323f57a297
6 changed files with 152 additions and 54 deletions

View File

@ -38,7 +38,6 @@ RenderbufferStorageBySamples(GLContext* aGL, GLsizei aSamples,
}
}
GLuint
CreateTexture(GLContext* aGL, GLenum aInternalFormat, GLenum aFormat,
GLenum aType, const gfx::IntSize& aSize, bool linear)
@ -47,10 +46,16 @@ CreateTexture(GLContext* aGL, GLenum aInternalFormat, GLenum aFormat,
aGL->fGenTextures(1, &tex);
ScopedBindTexture autoTex(aGL, tex);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, linear ? LOCAL_GL_LINEAR : LOCAL_GL_NEAREST);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, linear ? LOCAL_GL_LINEAR : LOCAL_GL_NEAREST);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
LOCAL_GL_TEXTURE_MIN_FILTER, linear ? LOCAL_GL_LINEAR
: LOCAL_GL_NEAREST);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D,
LOCAL_GL_TEXTURE_MAG_FILTER, linear ? LOCAL_GL_LINEAR
: LOCAL_GL_NEAREST);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S,
LOCAL_GL_CLAMP_TO_EDGE);
aGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T,
LOCAL_GL_CLAMP_TO_EDGE);
aGL->fTexImage2D(LOCAL_GL_TEXTURE_2D,
0,

View File

@ -277,7 +277,7 @@ GLContext::GLContext(const SurfaceCaps& caps,
mRenderer(GLRenderer::Other),
mHasRobustness(false),
#ifdef DEBUG
mGLError(LOCAL_GL_NO_ERROR),
mIsInLocalErrorCheck(false),
#endif
mSharedContext(sharedContext),
mCaps(caps),

View File

@ -12,6 +12,7 @@
#include <ctype.h>
#include <map>
#include <bitset>
#include <queue>
#ifdef DEBUG
#include <string.h>
@ -505,7 +506,6 @@ private:
// -----------------------------------------------------------------------------
// Robustness handling
public:
bool HasRobustness() const {
return mHasRobustness;
}
@ -516,17 +516,13 @@ public:
*/
virtual bool SupportsRobustness() const = 0;
private:
bool mHasRobustness;
// -----------------------------------------------------------------------------
// Error handling
public:
static const char* GLErrorToString(GLenum aError)
{
static const char* GLErrorToString(GLenum aError) {
switch (aError) {
case LOCAL_GL_INVALID_ENUM:
return "GL_INVALID_ENUM";
@ -549,12 +545,10 @@ public:
}
}
/** \returns the first GL error, and guarantees that all GL error flags are cleared,
* i.e. that a subsequent GetError call will return NO_ERROR
*/
GLenum GetAndClearError()
{
GLenum GetAndClearError() {
// the first error is what we want to return
GLenum error = fGetError();
@ -566,30 +560,99 @@ public:
return error;
}
/*** In GL debug mode, we completely override glGetError ***/
GLenum fGetError()
{
#ifdef DEBUG
// debug mode ends up eating the error in AFTER_GL_CALL
if (DebugMode()) {
GLenum err = mGLError;
mGLError = LOCAL_GL_NO_ERROR;
return err;
}
#endif // DEBUG
private:
GLenum raw_fGetError() {
return mSymbols.fGetError();
}
std::queue<GLenum> mGLErrorQueue;
public:
GLenum fGetError() {
if (!mGLErrorQueue.empty()) {
GLenum err = mGLErrorQueue.front();
mGLErrorQueue.pop();
return err;
}
return GetUnpushedError();
}
#ifdef DEBUG
private:
GLenum GetUnpushedError() {
return raw_fGetError();
}
GLenum mGLError;
#endif // DEBUG
void ClearUnpushedErrors() {
while (GetUnpushedError()) {
// Discard errors.
}
}
GLenum GetAndClearUnpushedErrors() {
GLenum err = GetUnpushedError();
if (err) {
ClearUnpushedErrors();
}
return err;
}
void PushError(GLenum err) {
mGLErrorQueue.push(err);
}
void GetAndPushAllErrors() {
while (true) {
GLenum err = GetUnpushedError();
if (!err)
break;
PushError(err);
}
}
////////////////////////////////////
// Use this safer option.
private:
#ifdef DEBUG
bool mIsInLocalErrorCheck;
#endif
public:
class ScopedLocalErrorCheck {
GLContext* const mGL;
bool mHasBeenChecked;
public:
ScopedLocalErrorCheck(GLContext* gl)
: mGL(gl)
, mHasBeenChecked(false)
{
#ifdef DEBUG
MOZ_ASSERT(!mGL->mIsInLocalErrorCheck);
mGL->mIsInLocalErrorCheck = true;
#endif
mGL->GetAndPushAllErrors();
}
GLenum GetLocalError() {
#ifdef DEBUG
MOZ_ASSERT(mGL->mIsInLocalErrorCheck);
mGL->mIsInLocalErrorCheck = false;
#endif
MOZ_ASSERT(!mHasBeenChecked);
mHasBeenChecked = true;
return mGL->GetAndClearUnpushedErrors();
}
~ScopedLocalErrorCheck() {
MOZ_ASSERT(mHasBeenChecked);
}
};
private:
static void GLAPIENTRY StaticDebugCallback(GLenum source,
GLenum type,
GLuint id,
@ -624,8 +687,7 @@ private:
# endif
#endif
void BeforeGLCall(const char* glFunction)
{
void BeforeGLCall(const char* glFunction) {
MOZ_ASSERT(IsCurrent());
if (DebugMode()) {
GLContext *currentGLContext = nullptr;
@ -643,21 +705,23 @@ private:
}
}
void AfterGLCall(const char* glFunction)
{
void AfterGLCall(const char* glFunction) {
if (DebugMode()) {
// calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
// the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces
// tend to be meaningless
mSymbols.fFinish();
mGLError = mSymbols.fGetError();
GLenum err = GetUnpushedError();
PushError(err);
if (DebugMode() & DebugTrace)
printf_stderr("[gl:%p] < %s [0x%04x]\n", this, glFunction, mGLError);
if (mGLError != LOCAL_GL_NO_ERROR) {
printf_stderr("[gl:%p] < %s [0x%04x]\n", this, glFunction, err);
if (err != LOCAL_GL_NO_ERROR) {
printf_stderr("GL ERROR: %s generated GL error %s(0x%04x)\n",
glFunction,
GLErrorToString(mGLError),
mGLError);
GLErrorToString(err),
err);
if (DebugMode() & DebugAbortOnError)
NS_ABORT();
}

View File

@ -568,6 +568,8 @@ DrawBuffer::Create(GLContext* const gl,
pStencilRB = nullptr;
}
GLContext::ScopedLocalErrorCheck localError(gl);
CreateRenderbuffersForOffscreen(gl, formats, size, caps.antialias,
pColorMSRB, pDepthRB, pStencilRB);
@ -578,7 +580,8 @@ DrawBuffer::Create(GLContext* const gl,
UniquePtr<DrawBuffer> ret( new DrawBuffer(gl, size, fb, colorMSRB,
depthRB, stencilRB) );
if (!gl->IsFramebufferComplete(fb))
GLenum err = localError.GetLocalError();
if (err || !gl->IsFramebufferComplete(fb))
return false;
*out_buffer = Move(ret);
@ -624,6 +627,8 @@ ReadBuffer::Create(GLContext* gl,
GLuint* pDepthRB = caps.depth ? &depthRB : nullptr;
GLuint* pStencilRB = caps.stencil ? &stencilRB : nullptr;
GLContext::ScopedLocalErrorCheck localError(gl);
CreateRenderbuffersForOffscreen(gl, formats, surf->mSize, caps.antialias,
nullptr, pDepthRB, pStencilRB);
@ -651,7 +656,9 @@ ReadBuffer::Create(GLContext* gl,
UniquePtr<ReadBuffer> ret( new ReadBuffer(gl, fb, depthRB,
stencilRB, surf) );
if (!gl->IsFramebufferComplete(fb)) {
GLenum err = localError.GetLocalError();
if (err || !gl->IsFramebufferComplete(fb)) {
ret = nullptr;
}

View File

@ -144,20 +144,28 @@ ChooseConfig(GLContext* gl,
return config;
}
// Returns EGL_NO_SURFACE on error.
// Returns `EGL_NO_SURFACE` (`0`) on error.
static EGLSurface
CreatePBufferSurface(GLLibraryEGL* egl,
EGLDisplay display,
EGLConfig config,
const gfx::IntSize& size)
{
auto width = size.width;
auto height = size.height;
EGLint attribs[] = {
LOCAL_EGL_WIDTH, size.width,
LOCAL_EGL_HEIGHT, size.height,
LOCAL_EGL_WIDTH, width,
LOCAL_EGL_HEIGHT, height,
LOCAL_EGL_NONE
};
DebugOnly<EGLint> preCallErr = egl->fGetError();
MOZ_ASSERT(preCallErr == LOCAL_EGL_SUCCESS);
EGLSurface surface = egl->fCreatePbufferSurface(display, config, attribs);
EGLint err = egl->fGetError();
if (err != LOCAL_EGL_SUCCESS)
return 0;
return surface;
}

View File

@ -24,10 +24,17 @@ SharedSurface_Basic::Create(GLContext* gl,
bool hasAlpha)
{
gl->MakeCurrent();
GLContext::ScopedLocalErrorCheck localError(gl);
GLuint tex = CreateTexture(gl, formats.color_texInternalFormat,
formats.color_texFormat,
formats.color_texType,
size);
GLenum err = localError.GetLocalError();
if (err) {
gl->fDeleteTextures(1, &tex);
return nullptr;
}
SurfaceFormat format = SurfaceFormat::B8G8R8X8;
switch (formats.color_texInternalFormat) {
@ -74,11 +81,8 @@ SharedSurface_Basic::SharedSurface_Basic(GLContext* gl,
mTex,
0);
GLenum status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
mGL->fDeleteFramebuffers(1, &mFB);
mFB = 0;
}
DebugOnly<GLenum> status = mGL->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
int32_t stride = gfx::GetAlignedStride<4>(size.width * BytesPerPixel(format));
mData = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
@ -133,14 +137,24 @@ SharedSurface_GLTexture::Create(GLContext* prodGL,
bool ownsTex = false;
UniquePtr<SharedSurface_GLTexture> ret;
if (!tex) {
GLContext::ScopedLocalErrorCheck localError(prodGL);
tex = CreateTextureForOffscreen(prodGL, formats, size);
GLenum err = localError.GetLocalError();
if (err) {
prodGL->fDeleteTextures(1, &tex);
return Move(ret);
}
ownsTex = true;
}
typedef SharedSurface_GLTexture ptrT;
UniquePtr<ptrT> ret( new ptrT(prodGL, consGL, size, hasAlpha, tex,
ownsTex) );
ret.reset( new SharedSurface_GLTexture(prodGL, consGL, size,
hasAlpha, tex, ownsTex) );
return Move(ret);
}