From 8a7481eaa33bd144c326e51ae2dfef456992e5d3 Mon Sep 17 00:00:00 2001 From: Alex Szpakowski Date: Tue, 26 Aug 2014 09:40:24 -0300 Subject: [PATCH] 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 --- src/modules/graphics/opengl/Image.cpp | 127 ++++++++++++--------- src/modules/graphics/opengl/Image.h | 22 ++-- src/modules/graphics/opengl/wrap_Image.cpp | 8 +- 3 files changed, 90 insertions(+), 67 deletions(-) diff --git a/src/modules/graphics/opengl/Image.cpp b/src/modules/graphics/opengl/Image.cpp index 2adedc9cd..d34edf559 100644 --- a/src/modules/graphics/opengl/Image.cpp +++ b/src/modules/graphics/opengl/Image.cpp @@ -20,6 +20,8 @@ #include "Image.h" +#include "common/int.h" + // STD #include // For memcpy #include // for min/max @@ -218,7 +220,24 @@ void Image::unload() return unloadVolatile(); } -void Image::uploadCompressedData() +void Image::generateMipmaps() +{ + // The GL_GENERATE_MIPMAP texparameter is set in loadVolatile if we don't + // have support for glGenerateMipmap. + if (flags.mipmaps && !isCompressed() && + (GLAD_VERSION_3_0 || GLAD_ARB_framebuffer_object)) + { + // Driver bug: http://www.opengl.org/wiki/Common_Mistakes#Automatic_mipmap_generation +#if defined(LOVE_WINDOWS) || defined(LOVE_LINUX) + if (gl.getVendor() == OpenGL::VENDOR_ATI_AMD) + glEnable(GL_TEXTURE_2D); +#endif + + glGenerateMipmap(GL_TEXTURE_2D); + } +} + +void Image::loadTextureFromCompressedData() { GLenum iformat = getCompressedFormat(cdata->getFormat()); int count = flags.mipmaps ? cdata->getMipmapCount() : 1; @@ -228,62 +247,23 @@ void Image::uploadCompressedData() 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)); + glCompressedTexImage2D(GL_TEXTURE_2D, i, iformat, + cdata->getWidth(i), cdata->getHeight(i), 0, + (GLsizei) cdata->getSize(i), cdata->getData(i)); } } -void Image::uploadImageData() +void Image::loadTextureFromImageData() { 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()); + 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 -#if defined(LOVE_WINDOWS) || defined(LOVE_LINUX) - if (gl.getVendor() == OpenGL::VENDOR_ATI_AMD) - glEnable(GL_TEXTURE_2D); -#endif - - glGenerateMipmap(GL_TEXTURE_2D); - } - else - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); - } -} - -void Image::uploadTexture() -{ - bind(); - - if (isCompressed() && cdata.get()) - uploadCompressedData(); - else if (data.get()) - uploadImageData(); + generateMipmaps(); } bool Image::loadVolatile() @@ -313,21 +293,32 @@ bool Image::loadVolatile() gl.setTextureWrap(wrap); 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. if (width > gl.getMaxTextureSize() || height > gl.getMaxTextureSize()) { - uploadDefaultTexture(); + loadDefaultTexture(); 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. try { - uploadTexture(); + if (isCompressed()) + loadTextureFromCompressedData(); + else + loadTextureFromImageData(); GLenum glerr = glGetError(); 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. if (texture == 0 || usingDefaultTexture) 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; } @@ -389,7 +406,7 @@ const Image::Flags &Image::getFlags() const return flags; } -void Image::uploadDefaultTexture() +void Image::loadDefaultTexture() { usingDefaultTexture = true; diff --git a/src/modules/graphics/opengl/Image.h b/src/modules/graphics/opengl/Image.h index ed837a337..b8ed275da 100644 --- a/src/modules/graphics/opengl/Image.h +++ b/src/modules/graphics/opengl/Image.h @@ -133,7 +133,7 @@ public: * Re-uploads the ImageData or CompressedData associated with this Image to * the GPU. **/ - bool refresh(); + bool refresh(int xoffset, int yoffset, int w, int h); const Flags &getFlags() const; @@ -153,10 +153,18 @@ public: private: - void uploadDefaultTexture(); - 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 // Compressed image data was used to create the texture. Object::StrongRef data; @@ -183,19 +191,11 @@ private: size_t textureMemorySize; - void preload(); - - void uploadCompressedData(); - void uploadImageData(); - void uploadTexture(); - static float maxMipmapSharpness; static FilterMode defaultMipmapFilter; static float defaultMipmapSharpness; - GLenum getCompressedFormat(image::CompressedData::Format cformat) const; - static StringMap::Entry flagNameEntries[]; static StringMap flagNames; diff --git a/src/modules/graphics/opengl/wrap_Image.cpp b/src/modules/graphics/opengl/wrap_Image.cpp index 2f85e06ba..afc722b16 100644 --- a/src/modules/graphics/opengl/wrap_Image.cpp +++ b/src/modules/graphics/opengl/wrap_Image.cpp @@ -79,7 +79,13 @@ int w_Image_isCompressed(lua_State *L) int w_Image_refresh(lua_State *L) { 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; }