aboutsummaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
authorJohannes Schickel2016-01-02 02:15:20 +0100
committerJohannes Schickel2016-03-16 20:29:26 +0100
commitb081fe63e866883b424dfefb694c9b6518efa3b4 (patch)
treecd3f9ad5e3f06bcc2b50d8e5886e4104fc3696f6 /backends
parent5eb0ac0c9e82b419d996784adddc5f94d1fa5be3 (diff)
downloadscummvm-rg350-b081fe63e866883b424dfefb694c9b6518efa3b4.tar.gz
scummvm-rg350-b081fe63e866883b424dfefb694c9b6518efa3b4.tar.bz2
scummvm-rg350-b081fe63e866883b424dfefb694c9b6518efa3b4.zip
OPENGL: Create new abstraction for GL texture objects.
Diffstat (limited to 'backends')
-rw-r--r--backends/graphics/opengl/texture.cpp212
-rw-r--r--backends/graphics/opengl/texture.h91
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;