Better error checking in the GL texture backend

This commit is contained in:
Alex Szpakowski
2020-02-12 22:57:13 -04:00
parent 5c06b2a860
commit 4ec9cafdea
3 changed files with 40 additions and 32 deletions
+2 -2
View File
@@ -369,7 +369,7 @@ void Texture::drawLayer(Graphics *gfx, int layer, Quad *q, const Matrix4 &m)
throw love::Exception("Cannot render a Texture to itself."); throw love::Exception("Cannot render a Texture to itself.");
if (texType != TEXTURE_2D_ARRAY) if (texType != TEXTURE_2D_ARRAY)
throw love::Exception("drawLayer can only be used with Array Textures!"); throw love::Exception("drawLayer can only be used with Array Textures.");
if (layer < 0 || layer >= layers) if (layer < 0 || layer >= layers)
throw love::Exception("Invalid layer: %d (Texture has %d layers)", layer + 1, layers); throw love::Exception("Invalid layer: %d (Texture has %d layers)", layer + 1, layers);
@@ -811,7 +811,7 @@ bool Texture::Slices::validate() const
int mipcount = getMipmapCount(0); int mipcount = getMipmapCount(0);
if (slicecount == 0 || mipcount == 0) if (slicecount == 0 || mipcount == 0)
throw love::Exception("At least one ImageData or CompressedImageData is required!"); throw love::Exception("At least one ImageData or CompressedImageData is required.");
if (textureType == TEXTURE_CUBE && slicecount != 6) if (textureType == TEXTURE_CUBE && slicecount != 6)
throw love::Exception("Cube textures must have exactly 6 sides."); throw love::Exception("Cube textures must have exactly 6 sides.");
+36 -28
View File
@@ -106,9 +106,8 @@ static GLenum createFBO(GLuint &framebuffer, TextureType texType, PixelFormat fo
return status; return status;
} }
static bool newRenderbuffer(int width, int height, int &samples, PixelFormat pixelformat, GLuint &buffer) static GLenum newRenderbuffer(int width, int height, int &samples, PixelFormat pixelformat, GLuint &buffer)
{ {
int reqsamples = samples;
bool unusedSRGB = false; bool unusedSRGB = false;
OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, true, unusedSRGB); OpenGL::TextureFormat fmt = OpenGL::convertPixelFormat(pixelformat, true, unusedSRGB);
@@ -145,13 +144,16 @@ static bool newRenderbuffer(int width, int height, int &samples, PixelFormat pix
} }
if (samples > 1) if (samples > 1)
{
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples); glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, &samples);
samples = std::max(1, samples);
}
glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindRenderbuffer(GL_RENDERBUFFER, 0);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status == GL_FRAMEBUFFER_COMPLETE && (reqsamples <= 1 || samples > 1)) if (status == GL_FRAMEBUFFER_COMPLETE)
{ {
if (isPixelFormatDepthStencil(pixelformat)) if (isPixelFormatDepthStencil(pixelformat))
{ {
@@ -183,7 +185,7 @@ static bool newRenderbuffer(int width, int height, int &samples, PixelFormat pix
gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo); gl.bindFramebuffer(OpenGL::FRAMEBUFFER_ALL, current_fbo);
gl.deleteFramebuffer(fbo); gl.deleteFramebuffer(fbo);
return status == GL_FRAMEBUFFER_COMPLETE; return status;
} }
Texture::Texture(const Settings &settings, const Slices *data) Texture::Texture(const Settings &settings, const Slices *data)
@@ -193,11 +195,19 @@ Texture::Texture(const Settings &settings, const Slices *data)
, texture(0) , texture(0)
, renderbuffer(0) , renderbuffer(0)
, framebufferStatus(GL_FRAMEBUFFER_COMPLETE) , framebufferStatus(GL_FRAMEBUFFER_COMPLETE)
, textureGLError(GL_NO_ERROR)
, actualSamples(1) , actualSamples(1)
{ {
if (data != nullptr) if (data != nullptr)
slices = *data; slices = *data;
loadVolatile();
if (!loadVolatile())
{
if (framebufferStatus != GL_FRAMEBUFFER_COMPLETE)
throw love::Exception("Cannot create Texture (OpenGL framebuffer error: %s)", OpenGL::framebufferStatusString(framebufferStatus));
if (textureGLError != GL_NO_ERROR)
throw love::Exception("Cannot create Texture (OpenGL error: %s)", OpenGL::errorString(textureGLError));
}
} }
Texture::~Texture() Texture::~Texture()
@@ -205,7 +215,7 @@ Texture::~Texture()
unloadVolatile(); unloadVolatile();
} }
bool Texture::createTexture() void Texture::createTexture()
{ {
// The base class handles some validation. For example, if ImageData is // The base class handles some validation. For example, if ImageData is
// given then it must exist for all mip levels, a render target can't use // given then it must exist for all mip levels, a render target can't use
@@ -235,7 +245,7 @@ bool Texture::createTexture()
for (int slice = 0; slice < slices; slice++) for (int slice = 0; slice < slices; slice++)
uploadByteData(PIXELFORMAT_RGBA8_UNORM, px, sizeof(px), 0, slice, rect, nullptr); uploadByteData(PIXELFORMAT_RGBA8_UNORM, px, sizeof(px), 0, slice, rect, nullptr);
return true; return;
} }
GLenum gltype = OpenGL::getGLTextureType(texType); GLenum gltype = OpenGL::getGLTextureType(texType);
@@ -254,6 +264,11 @@ bool Texture::createTexture()
else if (texType == TEXTURE_CUBE) else if (texType == TEXTURE_CUBE)
slicecount = 6; slicecount = 6;
// For a couple flimsy reasons, we don't initialize the texture here if it's
// compressed. I need to verify that getPixelFormatSliceSize will return the
// correct value for all compressed texture formats, and I also vaguely
// remember some driver issues on some old Android systems, maybe...
// For now, the base class enforces data on init for compressed textures.
if (!isCompressed()) if (!isCompressed())
gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers); gl.rawTexStorage(texType, mipcount, format, sRGB, pixelWidth, pixelHeight, texType == TEXTURE_VOLUME ? depth : layers);
@@ -322,16 +337,6 @@ bool Texture::createTexture()
// so generateMipmaps here is fine - when they aren't already initialized. // so generateMipmaps here is fine - when they aren't already initialized.
if (getMipmapCount() > 1 && slices.getMipmapCount() <= 1) if (getMipmapCount() > 1 && slices.getMipmapCount() <= 1)
generateMipmaps(); generateMipmaps();
return true;
}
bool Texture::createRenderbuffer()
{
if (isReadable() && actualSamples <= 1)
return true;
return newRenderbuffer(pixelWidth, pixelHeight, actualSamples, format, renderbuffer);
} }
bool Texture::loadVolatile() bool Texture::loadVolatile()
@@ -353,21 +358,24 @@ bool Texture::loadVolatile()
while (glGetError() != GL_NO_ERROR); // Clear errors. while (glGetError() != GL_NO_ERROR); // Clear errors.
try framebufferStatus = GL_FRAMEBUFFER_COMPLETE;
{ textureGLError = GL_NO_ERROR;
if (isReadable())
createTexture();
if (!isReadable() || actualSamples > 1)
createRenderbuffer();
GLenum glerr = glGetError(); if (isReadable())
if (glerr != GL_NO_ERROR) createTexture();
throw love::Exception("Cannot create texture (OpenGL error: %s)", OpenGL::errorString(glerr));
if (!usingDefaultTexture && framebufferStatus == GL_FRAMEBUFFER_COMPLETE
&& (!isReadable() || actualSamples > 1))
{
framebufferStatus = newRenderbuffer(pixelWidth, pixelHeight, actualSamples, format, renderbuffer);
} }
catch (love::Exception &)
textureGLError = glGetError();
if (framebufferStatus != GL_FRAMEBUFFER_COMPLETE || textureGLError != GL_NO_ERROR)
{ {
unloadVolatile(); unloadVolatile();
throw; return false;
} }
int64 memsize = 0; int64 memsize = 0;
+2 -2
View File
@@ -58,8 +58,7 @@ public:
private: private:
bool createTexture(); void createTexture();
bool createRenderbuffer();
void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override; void uploadByteData(PixelFormat pixelformat, const void *data, size_t size, int level, int slice, const Rect &r, love::image::ImageDataBase *imgd = nullptr) override;
@@ -71,6 +70,7 @@ private:
GLuint renderbuffer; GLuint renderbuffer;
GLenum framebufferStatus; GLenum framebufferStatus;
GLenum textureGLError;
int actualSamples; int actualSamples;