Worked around ATI/AMD driver bugs when creating mipmaps for Images

This commit is contained in:
Alex Szpakowski
2013-02-23 15:52:03 -04:00
parent d9736ab3ab
commit 472915a4bb
2 changed files with 62 additions and 45 deletions
+54 -40
View File
@@ -37,7 +37,8 @@ Image::Image(love::image::ImageData *data)
: width((float)(data->getWidth())) : width((float)(data->getWidth()))
, height((float)(data->getHeight())) , height((float)(data->getHeight()))
, texture(0) , texture(0)
, mipmapsharpness(0.0f) , mipmapSharpness(0.0f)
, mipmapsCreated(false)
{ {
data->retain(); data->retain();
this->data = data; this->data = data;
@@ -143,36 +144,55 @@ void Image::drawq(love::graphics::Quad *quad, float x, float y, float angle, flo
drawv(t, v); drawv(t, v);
} }
void Image::checkMipmapsCreated() const void Image::checkMipmapsCreated()
{ {
if (filter.mipmap != FILTER_NEAREST && filter.mipmap != FILTER_LINEAR) if (mipmapsCreated || (filter.mipmap != FILTER_NEAREST && filter.mipmap != FILTER_LINEAR))
return; return;
if (!hasMipmapSupport()) if (!hasMipmapSupport())
throw love::Exception("Mipmap filtering is not supported on this system!"); throw love::Exception("Mipmap filtering is not supported on this system.");
// some old GPUs/systems claim support for NPOT textures, but fail when generating mipmaps // Some old drivers claim support for NPOT textures, but fail when creating mipmaps.
// we can't detect which systems will do this, so we fail gracefully for all NPOT images // we can't detect which systems will do this, so we fail gracefully for all NPOT images.
int w = int(width), h = int(height); int w = int(width), h = int(height);
if (w != next_p2(w) || h != next_p2(h)) if (w != next_p2(w) || h != next_p2(h))
throw love::Exception("Could not generate mipmaps: image does not have power of two dimensions!"); throw love::Exception("Could not create mipmaps: image does not have power of two dimensions.");
bind(); bind();
GLint mipmapscreated; if (hasNpot() && (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object))
glGetTexParameteriv(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, &mipmapscreated); {
// AMD/ATI drivers have several bugs when generating mipmaps,
// re-uploading the entire base image seems to be required.
glTexImage2D(GL_TEXTURE_2D,
0,
GL_RGBA8,
(GLsizei)width,
(GLsizei)height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
// generate mipmaps for this image if we haven't already // More bugs: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation
if (!mipmapscreated) glEnable(GL_TEXTURE_2D);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{ {
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
glTexSubImage2D(GL_TEXTURE_2D,
if (GLEE_VERSION_3_0 || GLEE_ARB_framebuffer_object) 0,
glGenerateMipmap(GL_TEXTURE_2D); 0,
else 0,
// modify single texel to trigger mipmap chain generation (GLsizei)width,
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, data->getData()); (GLsizei)height,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
} }
mipmapsCreated = true;
} }
void Image::setFilter(const Image::Filter &f) void Image::setFilter(const Image::Filter &f)
@@ -180,8 +200,8 @@ void Image::setFilter(const Image::Filter &f)
filter = f; filter = f;
bind(); bind();
checkMipmapsCreated();
setTextureFilter(f); setTextureFilter(f);
checkMipmapsCreated();
} }
const Image::Filter &Image::getFilter() const const Image::Filter &Image::getFilter() const
@@ -208,15 +228,15 @@ void Image::setMipmapSharpness(float sharpness)
return; return;
// LOD bias has the range (-maxbias, maxbias) // LOD bias has the range (-maxbias, maxbias)
mipmapsharpness = std::min(std::max(sharpness, -maxmipmapsharpness + 0.01f), maxmipmapsharpness - 0.01f); mipmapSharpness = std::min(std::max(sharpness, -maxMipmapSharpness + 0.01f), maxMipmapSharpness - 0.01f);
bind(); bind();
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapsharpness); // negative bias is sharper glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -mipmapSharpness); // negative bias is sharper
} }
float Image::getMipmapSharpness() const float Image::getMipmapSharpness() const
{ {
return mipmapsharpness; return mipmapSharpness;
} }
void Image::bind() const void Image::bind() const
@@ -240,7 +260,7 @@ void Image::unload()
bool Image::loadVolatile() bool Image::loadVolatile()
{ {
if (hasMipmapSharpnessSupport()) if (hasMipmapSharpnessSupport())
glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxmipmapsharpness); glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS, &maxMipmapSharpness);
if (hasNpot()) if (hasNpot())
return loadVolatileNPOT(); return loadVolatileNPOT();
@@ -253,11 +273,8 @@ bool Image::loadVolatilePOT()
glGenTextures(1,(GLuint *)&texture); glGenTextures(1,(GLuint *)&texture);
bindTexture(texture); bindTexture(texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); setTextureFilter(filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); setTextureWrap(wrap);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
float p2width = next_p2(width); float p2width = next_p2(width);
float p2height = next_p2(height); float p2height = next_p2(height);
@@ -289,9 +306,9 @@ bool Image::loadVolatilePOT()
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
data->getData()); data->getData());
setMipmapSharpness(mipmapsharpness); mipmapsCreated = false;
setFilter(filter); checkMipmapsCreated();
setWrap(wrap); setMipmapSharpness(mipmapSharpness);
return true; return true;
} }
@@ -301,11 +318,8 @@ bool Image::loadVolatileNPOT()
glGenTextures(1,(GLuint *)&texture); glGenTextures(1,(GLuint *)&texture);
bindTexture(texture); bindTexture(texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); setTextureFilter(filter);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); setTextureWrap(wrap);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, glTexImage2D(GL_TEXTURE_2D,
0, 0,
@@ -317,9 +331,9 @@ bool Image::loadVolatileNPOT()
GL_UNSIGNED_BYTE, GL_UNSIGNED_BYTE,
data->getData()); data->getData());
setMipmapSharpness(mipmapsharpness); mipmapsCreated = false;
setFilter(filter); checkMipmapsCreated();
setWrap(wrap); setMipmapSharpness(mipmapSharpness);
return true; return true;
} }
@@ -360,12 +374,12 @@ bool Image::hasNpot()
bool Image::hasMipmapSupport() bool Image::hasMipmapSupport()
{ {
return (GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap) != 0; return GLEE_VERSION_1_4 || GLEE_SGIS_generate_mipmap;
} }
bool Image::hasMipmapSharpnessSupport() bool Image::hasMipmapSharpnessSupport()
{ {
return (GLEE_VERSION_1_4 || GLEE_EXT_texture_lod_bias) != 0; return GLEE_VERSION_1_4 || GLEE_EXT_texture_lod_bias;
} }
} // opengl } // opengl
+8 -5
View File
@@ -142,11 +142,14 @@ private:
// The source vertices of the image. // The source vertices of the image.
vertex vertices[4]; vertex vertices[4];
// Mipmap texture LOD bias value // Mipmap texture LOD bias (sharpness) value.
float mipmapsharpness; float mipmapSharpness;
// Implementation-dependent maximum/minimum mipmap sharpness values // Implementation-dependent min/max mipmap sharpness values.
float maxmipmapsharpness; float maxMipmapSharpness;
// True if mipmaps have been created for this Image.
bool mipmapsCreated;
// The image's filter mode // The image's filter mode
Image::Filter filter; Image::Filter filter;
@@ -157,7 +160,7 @@ private:
bool loadVolatilePOT(); bool loadVolatilePOT();
bool loadVolatileNPOT(); bool loadVolatileNPOT();
void checkMipmapsCreated() const; void checkMipmapsCreated();
}; // Image }; // Image