diff options
author | Johannes Schickel | 2013-10-05 02:38:13 +0200 |
---|---|---|
committer | Johannes Schickel | 2013-10-19 22:17:42 +0200 |
commit | decc013e53ebf2a7c83e599ed676bfec27ade14f (patch) | |
tree | a7a9ae19f6c1f67e605b20ef923365214f08f761 /backends/graphics | |
parent | 50a86463c1caf689922a72cfa6cece06c2ed3455 (diff) | |
download | scummvm-rg350-decc013e53ebf2a7c83e599ed676bfec27ade14f.tar.gz scummvm-rg350-decc013e53ebf2a7c83e599ed676bfec27ade14f.tar.bz2 scummvm-rg350-decc013e53ebf2a7c83e599ed676bfec27ade14f.zip |
OPENGL: Implement dirty rect handling.
Diffstat (limited to 'backends/graphics')
-rw-r--r-- | backends/graphics/opengl/texture.cpp | 85 | ||||
-rw-r--r-- | backends/graphics/opengl/texture.h | 8 |
2 files changed, 78 insertions, 15 deletions
diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp index 3067aa7d1c..d89bd00ac4 100644 --- a/backends/graphics/opengl/texture.cpp +++ b/backends/graphics/opengl/texture.cpp @@ -71,6 +71,10 @@ void Texture::recreateInternalTexture() { // In case there is an actual texture setup we reinitialize it. if (_textureData.getPixels()) { + // Allocate storage for OpenGL texture. + GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w, + _textureData.h, 0, _glFormat, _glType, NULL)); + // Mark dirts such that it will be completely refreshed the next time. flagDirty(); } @@ -101,6 +105,13 @@ void Texture::allocate(uint width, uint height) { if (texWidth != _textureData.w || texHeight != _textureData.h) { // Create a buffer for the texture data. _textureData.create(texWidth, texHeight, _format); + + // Set the texture. + GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); + + // Allocate storage for OpenGL texture. + GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w, + _textureData.h, 0, _glFormat, _glType, NULL)); } // Create a sub-buffer for raw access. @@ -112,6 +123,16 @@ void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcP assert(x + w <= dstSurf->w); assert(y + h <= dstSurf->h); + // *sigh* Common::Rect::extend behaves unexpected whenever one of the two + // parameters is an empty rect. Thus, we check whether the current dirty + // area is valid. In case it is not we simply use the parameters as new + // dirty area. Otherwise, we simply call extend. + if (_dirtyArea.isEmpty()) { + _dirtyArea = Common::Rect(x, y, x + w, y + h); + } else { + _dirtyArea.extend(Common::Rect(x, y, x + w, y + h)); + } + const byte *src = (const byte *)srcPtr; byte *dst = (byte *)dstSurf->getBasePtr(x, y); const uint pitch = dstSurf->pitch; @@ -126,8 +147,6 @@ void Texture::copyRectToTexture(uint x, uint y, uint w, uint h, const void *srcP src += srcPitch; } } - - flagDirty(); } void Texture::fill(uint32 color) { @@ -178,26 +197,34 @@ void Texture::updateTexture() { return; } + Common::Rect dirtyArea = getDirtyArea(); + // In case we use linear filtering we might need to duplicate the last // pixel row/column to avoid glitches with filtering. if (_glFilter == GL_LINEAR) { - if (_textureData.w != _userPixelData.w) { - uint height = _userPixelData.h; + if (dirtyArea.right == _userPixelData.w && _userPixelData.w != _textureData.w) { + uint height = dirtyArea.height(); - const byte *src = (const byte *)_textureData.getBasePtr(_userPixelData.w - 1, 0); - byte *dst = (byte *)_textureData.getBasePtr(_userPixelData.w, 0); + const byte *src = (const byte *)_textureData.getBasePtr(_userPixelData.w - 1, dirtyArea.top); + byte *dst = (byte *)_textureData.getBasePtr(_userPixelData.w, dirtyArea.top); while (height-- > 0) { memcpy(dst, src, _textureData.format.bytesPerPixel); dst += _textureData.pitch; src += _textureData.pitch; } + + // Extend the dirty area. + ++dirtyArea.right; } - if (_textureData.h != _userPixelData.h) { - const byte *src = (const byte *)_textureData.getBasePtr(0, _userPixelData.h - 1); - byte *dst = (byte *)_textureData.getBasePtr(0, _userPixelData.h); - memcpy(dst, src, _userPixelData.w * _textureData.format.bytesPerPixel); + if (dirtyArea.bottom == _userPixelData.h && _userPixelData.h != _textureData.h) { + const byte *src = (const byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h - 1); + byte *dst = (byte *)_textureData.getBasePtr(dirtyArea.left, _userPixelData.h); + memcpy(dst, src, dirtyArea.width() * _textureData.format.bytesPerPixel); + + // Extend the dirty area. + ++dirtyArea.bottom; } } @@ -205,12 +232,38 @@ void Texture::updateTexture() { GLCALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); // Update the actual texture. - GLCALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w, _textureData.h, 0, _glFormat, _glType, _textureData.getPixels())); + // Although we keep track of the dirty part of the texture buffer we + // cannot take advantage of the left/right boundries here because it is + // not possible to specify a pitch to glTexSubImage2D. To be precise, with + // plain OpenGL we could set GL_UNPACK_ROW_LENGTH to achieve this. However, + // OpenGL ES 1.0 does not support GL_UNPACK_ROW_LENGTH. Thus, we are left + // with the following options: + // + // 1) (As we do right now) Simply always update the whole texture lines of + // rect changed. This is simplest to implement. In case performance is + // really an issue we can think of switching to another method. + // + // 2) Copy the dirty rect to a temporary buffer and upload that by using + // glTexSubImage2D. This is what the Android backend does. It is more + // complicated though. + // + // 3) Use glTexSubImage2D per line changed. This is what the old OpenGL + // graphics manager did but it is much slower! Thus, we do not use it. + GLCALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, dirtyArea.top, _textureData.w, dirtyArea.height(), + _glFormat, _glType, _textureData.getBasePtr(0, dirtyArea.top))); // We should have handled everything, thus not dirty anymore. clearDirty(); } +Common::Rect Texture::getDirtyArea() const { + if (_allDirty) { + return Common::Rect(_userPixelData.w, _userPixelData.h); + } else { + return _dirtyArea; + } +} + TextureCLUT8::TextureCLUT8(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format) : Texture(glIntFormat, glFormat, glType, format), _clut8Data(), _palette(new byte[256 * format.bytesPerPixel]) { memset(_palette, 0, sizeof(byte) * format.bytesPerPixel); @@ -288,11 +341,17 @@ void TextureCLUT8::updateTexture() { // Do the palette look up Graphics::Surface *outSurf = Texture::getSurface(); + Common::Rect dirtyArea = getDirtyArea(); + if (outSurf->format.bytesPerPixel == 2) { - doPaletteLookUp<uint16>((uint16 *)outSurf->getPixels(), (const byte *)_clut8Data.getPixels(), _clut8Data.w, _clut8Data.h, + doPaletteLookUp<uint16>((uint16 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top), + (const byte *)_clut8Data.getBasePtr(dirtyArea.left, dirtyArea.top), + dirtyArea.width(), dirtyArea.height(), outSurf->pitch, _clut8Data.pitch, (const uint16 *)_palette); } else if (outSurf->format.bytesPerPixel == 4) { - doPaletteLookUp<uint32>((uint32 *)outSurf->getPixels(), (const byte *)_clut8Data.getPixels(), _clut8Data.w, _clut8Data.h, + doPaletteLookUp<uint32>((uint32 *)outSurf->getBasePtr(dirtyArea.left, dirtyArea.top), + (const byte *)_clut8Data.getBasePtr(dirtyArea.left, dirtyArea.top), + dirtyArea.width(), dirtyArea.height(), outSurf->pitch, _clut8Data.pitch, (const uint32 *)_palette); } else { warning("TextureCLUT8::updateTexture: Unsupported pixel depth: %d", outSurf->format.bytesPerPixel); diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h index 1127aed350..273e7b7319 100644 --- a/backends/graphics/opengl/texture.h +++ b/backends/graphics/opengl/texture.h @@ -28,6 +28,8 @@ #include "graphics/pixelformat.h" #include "graphics/surface.h" +#include "common/rect.h" + namespace OpenGL { /** @@ -80,7 +82,7 @@ public: void draw(GLuint x, GLuint y, GLuint w, GLuint h); void flagDirty() { _allDirty = true; } - bool isDirty() const { return _allDirty; } + bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); } uint getWidth() const { return _userPixelData.w; } uint getHeight() const { return _userPixelData.h; } @@ -111,6 +113,7 @@ public: protected: virtual void updateTexture(); + Common::Rect getDirtyArea() const; private: const GLenum _glIntFormat; const GLenum _glFormat; @@ -124,7 +127,8 @@ private: Graphics::Surface _userPixelData; bool _allDirty; - void clearDirty() { _allDirty = false; } + Common::Rect _dirtyArea; + void clearDirty() { _allDirty = false; _dirtyArea = Common::Rect(); } }; class TextureCLUT8 : public Texture { |