Added optional xoffset/yoffset/width/height arguments to Image:refresh, to specify a sub-rectangle. Specifying a small sub-rectangle can improve the performance of Image:refresh.

--HG--
branch : minor
This commit is contained in:
Alex Szpakowski
2014-08-26 09:40:24 -03:00
parent 760304e023
commit 8a7481eaa3
3 changed files with 90 additions and 67 deletions
+77 -60
View File
@@ -20,6 +20,8 @@
#include "Image.h" #include "Image.h"
#include "common/int.h"
// STD // STD
#include <cstring> // For memcpy #include <cstring> // For memcpy
#include <algorithm> // for min/max #include <algorithm> // for min/max
@@ -218,50 +220,12 @@ void Image::unload()
return unloadVolatile(); return unloadVolatile();
} }
void Image::uploadCompressedData() void Image::generateMipmaps()
{ {
GLenum iformat = getCompressedFormat(cdata->getFormat()); // The GL_GENERATE_MIPMAP texparameter is set in loadVolatile if we don't
int count = flags.mipmaps ? cdata->getMipmapCount() : 1; // have support for glGenerateMipmap.
if (flags.mipmaps && !isCompressed() &&
// We have to inform OpenGL if the image doesn't have all mipmap levels. (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
for (int i = 0; i < count; i++)
{
glCompressedTexImage2D(GL_TEXTURE_2D,
i,
iformat,
cdata->getWidth(i),
cdata->getHeight(i),
0,
(GLsizei) cdata->getSize(i),
cdata->getData(i));
}
}
void Image::uploadImageData()
{
GLenum iformat = flags.sRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8;
if (flags.mipmaps && !(GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
{
love::thread::Lock lock(data->getMutex());
glTexImage2D(GL_TEXTURE_2D,
0,
iformat,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
data->getData());
}
if (flags.mipmaps)
{
if (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)
{ {
// Driver bug: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation // Driver bug: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation
#if defined(LOVE_WINDOWS) || defined(LOVE_LINUX) #if defined(LOVE_WINDOWS) || defined(LOVE_LINUX)
@@ -271,19 +235,35 @@ void Image::uploadImageData()
glGenerateMipmap(GL_TEXTURE_2D); glGenerateMipmap(GL_TEXTURE_2D);
} }
else
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE);
}
} }
void Image::uploadTexture() void Image::loadTextureFromCompressedData()
{ {
bind(); GLenum iformat = getCompressedFormat(cdata->getFormat());
int count = flags.mipmaps ? cdata->getMipmapCount() : 1;
if (isCompressed() && cdata.get()) // We have to inform OpenGL if the image doesn't have all mipmap levels.
uploadCompressedData(); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, count - 1);
else if (data.get())
uploadImageData(); for (int i = 0; i < count; i++)
{
glCompressedTexImage2D(GL_TEXTURE_2D, i, iformat,
cdata->getWidth(i), cdata->getHeight(i), 0,
(GLsizei) cdata->getSize(i), cdata->getData(i));
}
}
void Image::loadTextureFromImageData()
{
GLenum iformat = flags.sRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8;
{
love::thread::Lock lock(data->getMutex());
glTexImage2D(GL_TEXTURE_2D, 0, iformat, width, height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, data->getData());
}
generateMipmaps();
} }
bool Image::loadVolatile() bool Image::loadVolatile()
@@ -313,21 +293,32 @@ bool Image::loadVolatile()
gl.setTextureWrap(wrap); gl.setTextureWrap(wrap);
setMipmapSharpness(mipmapSharpness); setMipmapSharpness(mipmapSharpness);
if (!flags.mipmaps)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
// Use a default texture if the size is too big for the system. // Use a default texture if the size is too big for the system.
if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize()) if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize())
{ {
uploadDefaultTexture(); loadDefaultTexture();
return true; return true;
} }
if (!flags.mipmaps)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
if (flags.mipmaps && !isCompressed() &&
!(GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object))
{
// Auto-generate mipmaps every time the texture is modified, if
// glGenerateMipmap isn't supported.
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
}
while (glGetError() != GL_NO_ERROR); // Clear errors. while (glGetError() != GL_NO_ERROR); // Clear errors.
try try
{ {
uploadTexture(); if (isCompressed())
loadTextureFromCompressedData();
else
loadTextureFromImageData();
GLenum glerr = glGetError(); GLenum glerr = glGetError();
if (glerr != GL_NO_ERROR) if (glerr != GL_NO_ERROR)
@@ -374,13 +365,39 @@ void Image::unloadVolatile()
} }
} }
bool Image::refresh() void Image::uploadImageData(int xoffset, int yoffset, int w, int h)
{
const image::pixel *pdata = (const image::pixel *) data->getData();
pdata += yoffset * data->getWidth() + xoffset;
{
thread::Lock lock(data->getMutex());
glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, w, h, GL_RGBA,
GL_UNSIGNED_BYTE, pdata);
}
generateMipmaps();
}
bool Image::refresh(int xoffset, int yoffset, int w, int h)
{ {
// No effect if the texture hasn't been created yet. // No effect if the texture hasn't been created yet.
if (texture == 0 || usingDefaultTexture) if (texture == 0 || usingDefaultTexture)
return false; return false;
uploadTexture(); if (xoffset < 0 || yoffset < 0 || w <= 0 || h <= 0 ||
(xoffset + w) > width || (yoffset + h) > height)
{
throw love::Exception("Invalid rectangle dimensions.");
}
bind();
if (isCompressed())
loadTextureFromCompressedData();
else
uploadImageData(xoffset, yoffset, w, h);
return true; return true;
} }
@@ -389,7 +406,7 @@ const Image::Flags &Image::getFlags() const
return flags; return flags;
} }
void Image::uploadDefaultTexture() void Image::loadDefaultTexture()
{ {
usingDefaultTexture = true; usingDefaultTexture = true;
+11 -11
View File
@@ -133,7 +133,7 @@ public:
* Re-uploads the ImageData or CompressedData associated with this Image to * Re-uploads the ImageData or CompressedData associated with this Image to
* the GPU. * the GPU.
**/ **/
bool refresh(); bool refresh(int xoffset, int yoffset, int w, int h);
const Flags &getFlags() const; const Flags &getFlags() const;
@@ -153,10 +153,18 @@ public:
private: private:
void uploadDefaultTexture();
void drawv(const Matrix &t, const Vertex *v); void drawv(const Matrix &t, const Vertex *v);
void preload();
void generateMipmaps();
void loadDefaultTexture();
void loadTextureFromCompressedData();
void loadTextureFromImageData();
void uploadImageData(int xoffset, int yoffset, int w, int h);
GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
// The ImageData from which the texture is created. May be null if // The ImageData from which the texture is created. May be null if
// Compressed image data was used to create the texture. // Compressed image data was used to create the texture.
Object::StrongRef<love::image::ImageData> data; Object::StrongRef<love::image::ImageData> data;
@@ -183,19 +191,11 @@ private:
size_t textureMemorySize; size_t textureMemorySize;
void preload();
void uploadCompressedData();
void uploadImageData();
void uploadTexture();
static float maxMipmapSharpness; static float maxMipmapSharpness;
static FilterMode defaultMipmapFilter; static FilterMode defaultMipmapFilter;
static float defaultMipmapSharpness; static float defaultMipmapSharpness;
GLenum getCompressedFormat(image::CompressedData::Format cformat) const;
static StringMap<FlagType, FLAG_TYPE_MAX_ENUM>::Entry flagNameEntries[]; static StringMap<FlagType, FLAG_TYPE_MAX_ENUM>::Entry flagNameEntries[];
static StringMap<FlagType, FLAG_TYPE_MAX_ENUM> flagNames; static StringMap<FlagType, FLAG_TYPE_MAX_ENUM> flagNames;
+7 -1
View File
@@ -79,7 +79,13 @@ int w_Image_isCompressed(lua_State *L)
int w_Image_refresh(lua_State *L) int w_Image_refresh(lua_State *L)
{ {
Image *i = luax_checkimage(L, 1); Image *i = luax_checkimage(L, 1);
luax_catchexcept(L, [&](){ i->refresh(); });
int xoffset = luaL_optint(L, 2, 0);
int yoffset = luaL_optint(L, 3, 0);
int w = luaL_optint(L, 4, i->getWidth());
int h = luaL_optint(L, 5, i->getHeight());
luax_catchexcept(L, [&](){ i->refresh(xoffset, yoffset, w, h); });
return 0; return 0;
} }