diff options
author | Johannes Schickel | 2016-01-02 02:15:20 +0100 |
---|---|---|
committer | Johannes Schickel | 2016-03-16 20:29:26 +0100 |
commit | b081fe63e866883b424dfefb694c9b6518efa3b4 (patch) | |
tree | cd3f9ad5e3f06bcc2b50d8e5886e4104fc3696f6 | |
parent | 5eb0ac0c9e82b419d996784adddc5f94d1fa5be3 (diff) | |
download | scummvm-rg350-b081fe63e866883b424dfefb694c9b6518efa3b4.tar.gz scummvm-rg350-b081fe63e866883b424dfefb694c9b6518efa3b4.tar.bz2 scummvm-rg350-b081fe63e866883b424dfefb694c9b6518efa3b4.zip |
OPENGL: Create new abstraction for GL texture objects.
-rw-r--r-- | backends/graphics/opengl/texture.cpp | 212 | ||||
-rw-r--r-- | backends/graphics/opengl/texture.h | 91 |
2 files changed, 218 insertions, 85 deletions
diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp index 08f5e69f03..656de20f25 100644 --- a/backends/graphics/opengl/texture.cpp +++ b/backends/graphics/opengl/texture.cpp @@ -40,87 +40,171 @@ static GLuint nextHigher2(GLuint v) { return ++v; } -GLint Texture::_maxTextureSize = 0; -void Texture::queryTextureInformation() { - GL_CALL(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxTextureSize)); - debug(5, "OpenGL maximum texture size: %d", _maxTextureSize); +GLTexture::GLTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType) + : _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType), + _width(0), _height(0), _texCoords(), _glFilter(GL_NEAREST), + _glTexture(0) { + create(); } -Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format) - : _glIntFormat(glIntFormat), _glFormat(glFormat), _glType(glType), _format(format), _glFilter(GL_NEAREST), - _glTexture(0), _textureData(), _userPixelData(), _allDirty(false) { - recreateInternalTexture(); +GLTexture::~GLTexture() { + GL_CALL_SAFE(glDeleteTextures, (1, &_glTexture)); } -Texture::~Texture() { - GL_CALL_SAFE(glDeleteTextures, (1, &_glTexture)); - _textureData.free(); +void GLTexture::enableLinearFiltering(bool enable) { + if (enable) { + _glFilter = GL_LINEAR; + } else { + _glFilter = GL_NEAREST; + } + + bind(); + + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter)); } -void Texture::releaseInternalTexture() { +void GLTexture::destroy() { GL_CALL(glDeleteTextures(1, &_glTexture)); _glTexture = 0; } -void Texture::recreateInternalTexture() { +void GLTexture::create() { // Release old texture name in case it exists. - releaseInternalTexture(); + destroy(); // Get a new texture name. GL_CALL(glGenTextures(1, &_glTexture)); // Set up all texture parameters. - GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); + bind(); GL_CALL(glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - // In case there is an actual texture setup we reinitialize it. - if (_textureData.getPixels()) { + // If a size is specified, allocate memory for it. + if (_width != 0 && _height != 0) { // Allocate storage for OpenGL texture. - GL_CALL(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(); + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width, _height, + 0, _glFormat, _glType, NULL)); } } -void Texture::enableLinearFiltering(bool enable) { - if (enable) { - _glFilter = GL_LINEAR; +void GLTexture::bind() { + GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); +} + +void GLTexture::setSize(uint width, uint height) { + const uint oldWidth = _width; + const uint oldHeight = _height; + + if (!g_context.NPOTSupported) { + _width = nextHigher2(width); + _height = nextHigher2(height); } else { - _glFilter = GL_NEAREST; + _width = width; + _height = height; } - GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); + // If a size is specified, allocate memory for it. + if (width != 0 && height != 0) { + const GLfloat texWidth = (GLfloat)width / _width; + const GLfloat texHeight = (GLfloat)height / _height; - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, _glFilter)); - GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _glFilter)); + _texCoords[0] = 0; + _texCoords[1] = 0; + + _texCoords[2] = texWidth; + _texCoords[3] = 0; + + _texCoords[4] = 0; + _texCoords[5] = texHeight; + + _texCoords[6] = texWidth; + _texCoords[7] = texHeight; + + // Allocate storage for OpenGL texture if necessary. + if (oldWidth != _width || oldHeight != _height) { + bind(); + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _width, + _height, 0, _glFormat, _glType, NULL)); + } + } } -void Texture::allocate(uint width, uint height) { - uint texWidth = width, texHeight = height; - if (!g_context.NPOTSupported) { - texWidth = nextHigher2(texWidth); - texHeight = nextHigher2(texHeight); +void GLTexture::updateArea(const Common::Rect &area, const Graphics::Surface &src) { + // Set the texture on the active texture unit. + bind(); + + // Update the actual texture. + // Although we have the area of the texture buffer we want to update 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. + GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, area.top, src.w, area.height(), + _glFormat, _glType, src.getBasePtr(0, area.top))); +} + +GLint Texture::_maxTextureSize = 0; + +void Texture::queryTextureInformation() { + GL_CALL(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &_maxTextureSize)); + debug(5, "OpenGL maximum texture size: %d", _maxTextureSize); +} + +Texture::Texture(GLenum glIntFormat, GLenum glFormat, GLenum glType, const Graphics::PixelFormat &format) + : _format(format), _glTexture(glIntFormat, glFormat, glType), + _textureData(), _userPixelData(), _allDirty(false) { + recreateInternalTexture(); +} + +Texture::~Texture() { + _textureData.free(); +} + +void Texture::releaseInternalTexture() { + _glTexture.destroy(); +} + +void Texture::recreateInternalTexture() { + _glTexture.create(); + + // In case image date exists assure it will be completely refreshed next + // time. + if (_textureData.getPixels()) { + flagDirty(); } +} - // In case the needed texture dimension changed we will reinitialize the - // texture. - if (texWidth != _textureData.w || texHeight != _textureData.h) { - // Create a buffer for the texture data. - _textureData.create(texWidth, texHeight, _format); +void Texture::enableLinearFiltering(bool enable) { + _glTexture.enableLinearFiltering(enable); +} - // Set the texture. - GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); +void Texture::allocate(uint width, uint height) { + // Assure the texture can contain our user data. + _glTexture.setSize(width, height); - // Allocate storage for OpenGL texture. - GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, _glIntFormat, _textureData.w, - _textureData.h, 0, _glFormat, _glType, NULL)); + // In case the needed texture dimension changed we will reinitialize the + // texture data buffer. + if (_glTexture.getWidth() != _textureData.w || _glTexture.getHeight() != _textureData.h) { + // Create a buffer for the texture data. + _textureData.create(_glTexture.getWidth(), _glTexture.getHeight(), _format); } // Create a sub-buffer for raw access. @@ -175,17 +259,7 @@ void Texture::draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h) { updateTexture(); // Set the texture. - GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); - - // Calculate the texture rect that will be drawn. - const GLfloat texWidth = (GLfloat)_userPixelData.w / _textureData.w; - const GLfloat texHeight = (GLfloat)_userPixelData.h / _textureData.h; - const GLfloat texcoords[4*2] = { - 0, 0, - texWidth, 0, - 0, texHeight, - texWidth, texHeight - }; + _glTexture.bind(); // Calculate the screen rect where the texture will be drawn. const GLfloat vertices[4*2] = { @@ -196,7 +270,7 @@ void Texture::draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h) { }; // Setup coordinates for drawing. - g_context.setDrawCoordinates(vertices, texcoords); + g_context.setDrawCoordinates(vertices, _glTexture.getTexCoords()); // Draw the texture to the screen buffer. GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); @@ -211,7 +285,7 @@ void Texture::updateTexture() { // 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 (_glTexture.isLinearFilteringEnabled()) { if (dirtyArea.right == _userPixelData.w && _userPixelData.w != _textureData.w) { uint height = dirtyArea.height(); @@ -238,29 +312,7 @@ void Texture::updateTexture() { } } - // Set the texture. - GL_CALL(glBindTexture(GL_TEXTURE_2D, _glTexture)); - - // Update the actual texture. - // 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. - GL_CALL(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, dirtyArea.top, _textureData.w, dirtyArea.height(), - _glFormat, _glType, _textureData.getBasePtr(0, dirtyArea.top))); + _glTexture.updateArea(dirtyArea, _textureData); // We should have handled everything, thus not dirty anymore. clearDirty(); diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h index 4c2126741c..b16faabd3f 100644 --- a/backends/graphics/opengl/texture.h +++ b/backends/graphics/opengl/texture.h @@ -33,6 +33,91 @@ namespace OpenGL { /** + * A simple GL texture object abstraction. + * + * This is used for low-level GL texture handling. + */ +class GLTexture { +public: + /** + * Constrcut a new GL texture object. + * + * @param glIntFormat The internal format to use. + * @param glFormat The input format. + * @param glType The input type. + */ + GLTexture(GLenum glIntFormat, GLenum glFormat, GLenum glType); + ~GLTexture(); + + /** + * Enable or disable linear texture filtering. + * + * @param enable true to enable and false to disable. + */ + void enableLinearFiltering(bool enable); + + /** + * Test whether linear filtering is enabled. + */ + bool isLinearFilteringEnabled() const { return (_glFilter == GL_LINEAR); } + + /** + * Destroy the OpenGL texture name. + */ + void destroy(); + + /** + * Create the OpenGL texture name. + */ + void create(); + + /** + * Bind the texture to the active texture unit. + */ + void bind(); + + /** + * Sets the size of the texture in pixels. + * + * The internal OpenGL texture might have a different size. To query the + * actual size use getWidth()/getHeight(). + * + * @param width The desired logical width. + * @param height The desired logical height. + */ + void setSize(uint width, uint height); + + /** + * Copy image data to the texture. + * + * @param area The area to update. + * @param src Surface for the whole texture containing the pixel data + * to upload. Only the area described by area will be + * uploaded. + */ + void updateArea(const Common::Rect &area, const Graphics::Surface &src); + + uint getWidth() const { return _width; } + uint getHeight() const { return _height; } + + /** + * Obtain texture coordinates for rectangular drawing. + */ + const GLfloat *getTexCoords() const { return _texCoords; } +private: + const GLenum _glIntFormat; + const GLenum _glFormat; + const GLenum _glType; + + uint _width, _height; + GLfloat _texCoords[4*2]; + + GLint _glFilter; + + GLuint _glTexture; +}; + +/** * An OpenGL texture wrapper. It automatically takes care of all OpenGL * texture handling issues and also provides access to the texture data. */ @@ -125,13 +210,9 @@ protected: Common::Rect getDirtyArea() const; private: - const GLenum _glIntFormat; - const GLenum _glFormat; - const GLenum _glType; const Graphics::PixelFormat _format; - GLint _glFilter; - GLuint _glTexture; + GLTexture _glTexture; Graphics::Surface _textureData; Graphics::Surface _userPixelData; |