diff options
Diffstat (limited to 'backends/graphics')
-rw-r--r-- | backends/graphics/opengl/context.cpp | 9 | ||||
-rw-r--r-- | backends/graphics/opengl/opengl-defs.h | 13 | ||||
-rw-r--r-- | backends/graphics/opengl/opengl-func.h | 11 | ||||
-rw-r--r-- | backends/graphics/opengl/opengl-graphics.cpp | 6 | ||||
-rw-r--r-- | backends/graphics/opengl/opengl-sys.h | 9 | ||||
-rw-r--r-- | backends/graphics/opengl/shader.cpp | 10 | ||||
-rw-r--r-- | backends/graphics/opengl/shader.h | 18 | ||||
-rw-r--r-- | backends/graphics/opengl/texture.cpp | 270 | ||||
-rw-r--r-- | backends/graphics/opengl/texture.h | 73 |
9 files changed, 416 insertions, 3 deletions
diff --git a/backends/graphics/opengl/context.cpp b/backends/graphics/opengl/context.cpp index 209c38e5f3..10468f3700 100644 --- a/backends/graphics/opengl/context.cpp +++ b/backends/graphics/opengl/context.cpp @@ -45,6 +45,9 @@ void Context::reset() { NPOTSupported = false; #if !USE_FORCED_GLES && !USE_FORCED_GLES2 shadersSupported = false; + multitextureSupported = false; + framebufferObjectSupported = false; + textureRGSupported = false; #endif #define GL_FUNC_DEF(ret, name, param) name = nullptr; @@ -216,6 +219,12 @@ void OpenGLGraphicsManager::initializeGLContext() { ARBVertexShader = true; } else if (token == "GL_ARB_fragment_shader") { ARBFragmentShader = true; + } else if (token == "GL_ARB_multitexture") { + g_context.multitextureSupported = true; + } else if (token == "GL_ARB_texture_rg") { + g_context.textureRGSupported = true; + } else if (token == "GL_EXT_framebuffer_object") { + g_context.framebufferObjectSupported = true; #endif } } diff --git a/backends/graphics/opengl/opengl-defs.h b/backends/graphics/opengl/opengl-defs.h index 4de73d3476..733fc2933c 100644 --- a/backends/graphics/opengl/opengl-defs.h +++ b/backends/graphics/opengl/opengl-defs.h @@ -183,6 +183,9 @@ typedef GLhandleARB GLshader; #define GL_BGR 0x80E0 #define GL_BGRA 0x80E1 +#define GL_RED 0x1903 +#define GL_R8 0x8229 + /* PixelStoreParameter */ #define GL_UNPACK_ALIGNMENT 0x0CF5 #define GL_PACK_ALIGNMENT 0x0D05 @@ -242,8 +245,18 @@ typedef GLhandleARB GLshader; #define GL_COMPILE_STATUS 0x8B81 #define GL_LINK_STATUS 0x8B82 #define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_CURRENT_PROGRAM 0x8B8D /* Textures */ #define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 + +/* GetPName */ +#define GL_VIEWPORT 0x0BA2 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 + +/* Framebuffer objects */ +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_FRAMEBUFFER 0x8D40 #endif diff --git a/backends/graphics/opengl/opengl-func.h b/backends/graphics/opengl/opengl-func.h index fb0877913e..763d5e9143 100644 --- a/backends/graphics/opengl/opengl-func.h +++ b/backends/graphics/opengl/opengl-func.h @@ -77,6 +77,7 @@ GL_FUNC_DEF(void, glEnable, (GLenum cap)); GL_FUNC_DEF(void, glDisable, (GLenum cap)); +GL_FUNC_DEF(GLboolean, glIsEnabled, (GLenum cap)); GL_FUNC_DEF(void, glClear, (GLbitfield mask)); GL_FUNC_DEF(void, glColor4f, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)); GL_FUNC_DEF(void, glViewport, (GLint x, GLint y, GLsizei width, GLsizei height)); @@ -128,10 +129,16 @@ GL_FUNC_2_DEF(void, glGetShaderiv, glGetObjectParameterivARB, (GLshader shader, GL_FUNC_2_DEF(void, glGetShaderInfoLog, glGetInfoLogARB, (GLshader shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog)); GL_FUNC_2_DEF(void, glShaderSource, glShaderSourceARB, (GLshader shader, GLsizei count, const GLchar *const *string, const GLint *length)); GL_FUNC_2_DEF(void, glCompileShader, glCompileShaderARB, (GLshader shader)); + +#if !USE_FORCED_GLES2 +GL_FUNC_2_DEF(void, glBindFramebuffer, glBindFramebufferEXT, (GLenum target, GLuint renderbuffer)); +GL_FUNC_2_DEF(void, glDeleteFramebuffers, glDeleteFramebuffersEXT, (GLsizei n, const GLuint *framebuffers)); +GL_FUNC_2_DEF(void, glGenFramebuffers, glGenFramebuffersEXT, (GLsizei n, GLuint *renderbuffers)); +GL_FUNC_2_DEF(void, glFramebufferTexture2D, glFramebufferTexture2DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)); +GL_FUNC_2_DEF(GLenum, glCheckFramebufferStatus, glCheckFramebufferStatusEXT, (GLenum target)); #endif -#if !USE_FORCED_GL && !USE_FORCED_GLES -GL_FUNC_DEF(void, glActiveTexture, (GLenum texture)); +GL_FUNC_2_DEF(void, glActiveTexture, glActiveTextureARB, (GLenum texture)); #endif #ifdef DEFINED_GL_EXT_FUNC_DEF diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp index 6784bfe6ab..df939fb3d8 100644 --- a/backends/graphics/opengl/opengl-graphics.cpp +++ b/backends/graphics/opengl/opengl-graphics.cpp @@ -1031,6 +1031,12 @@ void OpenGLGraphicsManager::setMousePosition(int x, int y) { Surface *OpenGLGraphicsManager::createSurface(const Graphics::PixelFormat &format, bool wantAlpha) { GLenum glIntFormat, glFormat, glType; if (format.bytesPerPixel == 1) { +#if !USE_FORCED_GLES && !USE_FORCED_GLES2 + if (TextureCLUT8GPU::isSupportedByContext()) { + return new TextureCLUT8GPU(); + } +#endif + const Graphics::PixelFormat &virtFormat = wantAlpha ? _defaultFormatAlpha : _defaultFormat; const bool supported = getGLPixelFormat(virtFormat, glIntFormat, glFormat, glType); if (!supported) { diff --git a/backends/graphics/opengl/opengl-sys.h b/backends/graphics/opengl/opengl-sys.h index 4d143485b6..9df5efc38c 100644 --- a/backends/graphics/opengl/opengl-sys.h +++ b/backends/graphics/opengl/opengl-sys.h @@ -111,6 +111,15 @@ struct Context { #if !USE_FORCED_GLES && !USE_FORCED_GLES2 /** Whether shader support is available or not. */ bool shadersSupported; + + /** Whether multi texture support is available or not. */ + bool multitextureSupported; + + /** Whether (GLES2) RG texture formats are supported. */ + bool textureRGSupported; + + /** Whether FBO support is available or not. */ + bool framebufferObjectSupported; #endif #define GL_FUNC_DEF(ret, name, param) ret (GL_CALL_CONV *name)param diff --git a/backends/graphics/opengl/shader.cpp b/backends/graphics/opengl/shader.cpp index 9e106a81b8..c251ec1ed9 100644 --- a/backends/graphics/opengl/shader.cpp +++ b/backends/graphics/opengl/shader.cpp @@ -171,6 +171,16 @@ void Shader::activate(const GLfloat *projectionMatrix) { GL_CALL(glUniform1i(_textureLocation, 0)); } +GLint Shader::getUniformLocation(const char *name) const { + GLint result = -1; + GL_ASSIGN(result, glGetUniformLocation(_program, name)); + return result; +} + +void Shader::setUniformI(GLint location, GLint value) { + GL_CALL(glUniform1i(location, value)); +} + GLshader Shader::compileShader(const char *source, GLenum shaderType) { GLshader handle; GL_ASSIGN(handle, glCreateShader(shaderType)); diff --git a/backends/graphics/opengl/shader.h b/backends/graphics/opengl/shader.h index f9dcbb0c61..458ecb9f16 100644 --- a/backends/graphics/opengl/shader.h +++ b/backends/graphics/opengl/shader.h @@ -71,6 +71,24 @@ public: * @param projectionMatrix Projection matrix to use. */ void activate(const GLfloat *projectionMatrix); + + /** + * Return location for uniform with given name. + * + * @param name Name of the uniform to look up in the shader. + * @return The location or -1 if uniform was not found. + */ + GLint getUniformLocation(const char *name) const; + + /** + * Bind value to uniform. + * + * Note: this only works when the shader is actived by activate. + * + * @param location Location of the uniform. + * @param value The value to be set. + */ + void setUniformI(GLint location, GLint value); protected: /** * Vertex shader sources. diff --git a/backends/graphics/opengl/texture.cpp b/backends/graphics/opengl/texture.cpp index e9713eeb40..5ae4987874 100644 --- a/backends/graphics/opengl/texture.cpp +++ b/backends/graphics/opengl/texture.cpp @@ -504,4 +504,274 @@ void TextureRGB555::updateTexture() { } #endif // !USE_FORCED_GL +#if !USE_FORCED_GLES && !USE_FORCED_GLES2 +namespace { +const char *const g_lookUpFragmentShaderGL = + "varying vec2 texCoord;\n" + "varying vec4 blendColor;\n" + "\n" + "uniform sampler2D texture;\n" + "uniform sampler2D palette;\n" + "\n" + "const float adjustFactor = 255.0 / 256.0 + 1.0 / (2.0 * 256.0);" + "\n" + "void main(void) {\n" + "\tvec4 index = texture2D(texture, texCoord);\n" + "\tgl_FragColor = blendColor * texture2D(palette, vec2(index.x * adjustFactor, 0.0));\n" + "}\n"; +} // End of anonymous namespace + +TextureCLUT8GPU::TextureCLUT8GPU() + : _clut8Texture(GL_R8, GL_RED, GL_UNSIGNED_BYTE), + _paletteTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE), + _glTexture(GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE), + _glFBO(0), _clut8Vertices(), _projectionMatrix(), + _lookUpShader(nullptr), _paletteLocation(-1), + _clut8Data(), _userPixelData(), _palette(), _paletteDirty(false) { + // Allocate space for 256 colors. + _paletteTexture.setSize(256, 1); + + _lookUpShader = new Shader(g_defaultVertexShader, g_lookUpFragmentShaderGL); + _lookUpShader->recreate(); + _paletteLocation = _lookUpShader->getUniformLocation("palette"); + + setupFBO(); +} + +TextureCLUT8GPU::~TextureCLUT8GPU() { + delete _lookUpShader; + GL_CALL_SAFE(glDeleteFramebuffers, (1, &_glFBO)); + _clut8Data.free(); +} + +void TextureCLUT8GPU::destroy() { + _clut8Texture.destroy(); + _paletteTexture.destroy(); + _glTexture.destroy(); + _lookUpShader->destroy(); + + GL_CALL(glDeleteFramebuffers(1, &_glFBO)); + _glFBO = 0; +} + +void TextureCLUT8GPU::recreate() { + _clut8Texture.create(); + _paletteTexture.create(); + _glTexture.create(); + _lookUpShader->recreate(); + _paletteLocation = _lookUpShader->getUniformLocation("palette"); + setupFBO(); + + // In case image date exists assure it will be completely refreshed next + // time. + if (_clut8Data.getPixels()) { + flagDirty(); + _paletteDirty = true; + } +} + +void TextureCLUT8GPU::enableLinearFiltering(bool enable) { + _glTexture.enableLinearFiltering(enable); +} + +void TextureCLUT8GPU::allocate(uint width, uint height) { + // Assure the texture can contain our user data. + _clut8Texture.setSize(width, height); + _glTexture.setSize(width, height); + + // In case the needed texture dimension changed we will reinitialize the + // texture data buffer. + if (_clut8Texture.getWidth() != _clut8Data.w || _clut8Texture.getHeight() != _clut8Data.h) { + // Create a buffer for the texture data. + _clut8Data.create(_clut8Texture.getWidth(), _clut8Texture.getHeight(), Graphics::PixelFormat::createFormatCLUT8()); + } + + // Create a sub-buffer for raw access. + _userPixelData = _clut8Data.getSubArea(Common::Rect(width, height)); + + // Setup structures for internal rendering to _glTexture. + _clut8Vertices[0] = 0; + _clut8Vertices[1] = 0; + + _clut8Vertices[2] = width; + _clut8Vertices[3] = 0; + + _clut8Vertices[4] = 0; + _clut8Vertices[5] = height; + + _clut8Vertices[6] = width; + _clut8Vertices[7] = height; + + _projectionMatrix[ 0] = 2.0f / _glTexture.getWidth(); + _projectionMatrix[ 1] = 0.0f; + _projectionMatrix[ 2] = 0.0f; + _projectionMatrix[ 3] = 0.0f; + + _projectionMatrix[ 4] = 0.0f; + _projectionMatrix[ 5] = 2.0f / _glTexture.getHeight(); + _projectionMatrix[ 6] = 0.0f; + _projectionMatrix[ 7] = 0.0f; + + _projectionMatrix[ 8] = 0.0f; + _projectionMatrix[ 9] = 0.0f; + _projectionMatrix[10] = 0.0f; + _projectionMatrix[11] = 0.0f; + + _projectionMatrix[12] = -1.0f; + _projectionMatrix[13] = -1.0f; + _projectionMatrix[14] = 0.0f; + _projectionMatrix[15] = 1.0f; +} + +void TextureCLUT8GPU::draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h) { + // Only do any processing when the Texture is initialized. + if (!_clut8Data.getPixels()) { + return; + } + + // First update any potentional changes. + updateTextures(); + + // Set the texture. + _glTexture.bind(); + + // Calculate the screen rect where the texture will be drawn. + const GLfloat vertices[4*2] = { + x, y, + x + w, y, + x, y + h, + x + w, y + h + }; + + // Setup coordinates for drawing. + g_context.setDrawCoordinates(vertices, _glTexture.getTexCoords()); + + // Draw the texture to the screen buffer. + GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); +} + +Graphics::PixelFormat TextureCLUT8GPU::getFormat() const { + return Graphics::PixelFormat::createFormatCLUT8(); +} + +void TextureCLUT8GPU::setColorKey(uint colorKey) { + _palette[colorKey * 4 + 3] = 0x00; + + _paletteDirty = true; +} + +void TextureCLUT8GPU::setPalette(uint start, uint colors, const byte *palData) { + byte *dst = _palette + start * 4; + + while (colors-- > 0) { + memcpy(dst, palData, 3); + dst[3] = 0xFF; + + dst += 4; + palData += 3; + } + + _paletteDirty = true; +} + +void TextureCLUT8GPU::updateTextures() { + const bool needLookUp = Surface::isDirty() || _paletteDirty; + + // Update CLUT8 texture if necessary. + if (Surface::isDirty()) { + _clut8Texture.updateArea(getDirtyArea(), _clut8Data); + clearDirty(); + } + + // Update palette if necessary. + if (_paletteDirty) { + Graphics::Surface palSurface; + palSurface.init(256, 1, 256, _palette, +#ifdef SCUMM_LITTLE_ENDIAN + Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24) // ABGR8888 +#else + Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0) // RGBA8888 +#endif + ); + + _paletteTexture.updateArea(Common::Rect(256, 1), palSurface); + _paletteDirty = false; + } + + // In case any data changed, do color look up and save result in _glTexture. + if (needLookUp) { + lookUpColors(); + } +} + +void TextureCLUT8GPU::lookUpColors() { + // Save old state. + GLint oldProgram = 0; + GL_CALL(glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgram)); + + GLint viewport[4]; + GL_CALL(glGetIntegerv(GL_VIEWPORT, viewport)); + + GLboolean scissorState; + GL_ASSIGN(scissorState, glIsEnabled(GL_SCISSOR_TEST)); + GLboolean blendState; + GL_ASSIGN(blendState, glIsEnabled(GL_BLEND)); + + GLint oldFBO = 0; + GL_CALL(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO)); + + // Update state. + GL_CALL(glViewport(0, 0, _glTexture.getWidth(), _glTexture.getHeight())); + GL_CALL(glDisable(GL_SCISSOR_TEST)); + GL_CALL(glDisable(GL_BLEND)); + + // Bind framebuffer. + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, _glFBO)); + + // Set the palette texture. + GL_CALL(glActiveTexture(GL_TEXTURE1)); + _paletteTexture.bind(); + + // Set the clut8 texture. + GL_CALL(glActiveTexture(GL_TEXTURE0)); + _clut8Texture.bind(); + + // Do color look up. + _lookUpShader->activate(_projectionMatrix); + _lookUpShader->setUniformI(_paletteLocation, 1); + g_context.setDrawCoordinates(_clut8Vertices, _clut8Texture.getTexCoords()); + GL_CALL(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)); + + // Restore old state. + GL_CALL(glUseProgram(oldProgram)); + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO)); + + if (blendState) { + GL_CALL(glEnable(GL_BLEND)); + } + if (scissorState) { + GL_CALL(glEnable(GL_SCISSOR_TEST)); + } + GL_CALL(glViewport(viewport[0], viewport[1], viewport[2], viewport[3])); +} + +void TextureCLUT8GPU::setupFBO() { + // Allocate framebuffer object if necessary. + if (!_glFBO) { + GL_CALL(glGenFramebuffers(1, &_glFBO)); + } + + // Save old FBO. + GLint oldFBO = 0; + GL_CALL(glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFBO)); + + // Attach destination texture to FBO. + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, _glFBO)); + GL_CALL(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _glTexture.getGLTexture(), 0)); + + // Restore old FBO. + GL_CALL(glBindFramebuffer(GL_FRAMEBUFFER, oldFBO)); +} +#endif // !USE_FORCED_GLES && !USE_FORCED_GLES2 + } // End of namespace OpenGL diff --git a/backends/graphics/opengl/texture.h b/backends/graphics/opengl/texture.h index 343a32b341..06fe47a241 100644 --- a/backends/graphics/opengl/texture.h +++ b/backends/graphics/opengl/texture.h @@ -32,6 +32,8 @@ namespace OpenGL { +class Shader; + /** * A simple GL texture object abstraction. * @@ -104,6 +106,14 @@ public: * Obtain texture coordinates for rectangular drawing. */ const GLfloat *getTexCoords() const { return _texCoords; } + + /** + * Obtain texture name. + * + * Beware that the texture name changes whenever create is used. + * destroy will invalidate the texture name. + */ + GLuint getGLTexture() const { return _glTexture; } private: const GLenum _glIntFormat; const GLenum _glFormat; @@ -175,7 +185,7 @@ public: virtual void draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h) = 0; void flagDirty() { _allDirty = true; } - bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); } + virtual bool isDirty() const { return _allDirty || !_dirtyArea.isEmpty(); } virtual uint getWidth() const = 0; virtual uint getHeight() const = 0; @@ -307,6 +317,67 @@ private: }; #endif // !USE_FORCED_GL +#if !USE_FORCED_GLES && !USE_FORCED_GLES2 +class TextureCLUT8GPU : public Surface { +public: + TextureCLUT8GPU(); + virtual ~TextureCLUT8GPU(); + + virtual void destroy(); + + virtual void recreate(); + + virtual void enableLinearFiltering(bool enable); + + virtual void allocate(uint width, uint height); + + virtual void draw(GLfloat x, GLfloat y, GLfloat w, GLfloat h); + + virtual bool isDirty() const { return _paletteDirty || Surface::isDirty(); } + + virtual uint getWidth() const { return _userPixelData.w; } + virtual uint getHeight() const { return _userPixelData.h; } + + virtual Graphics::PixelFormat getFormat() const; + + virtual bool hasPalette() const { return true; } + + virtual void setColorKey(uint colorKey); + virtual void setPalette(uint start, uint colors, const byte *palData); + + virtual Graphics::Surface *getSurface() { return &_userPixelData; } + virtual const Graphics::Surface *getSurface() const { return &_userPixelData; } + + static bool isSupportedByContext() { + return g_context.shadersSupported + && g_context.multitextureSupported + && g_context.textureRGSupported + && g_context.framebufferObjectSupported; + } +private: + void updateTextures(); + void lookUpColors(); + + GLTexture _clut8Texture; + GLTexture _paletteTexture; + GLTexture _glTexture; + + void setupFBO(); + GLuint _glFBO; + GLfloat _clut8Vertices[4*2]; + GLfloat _projectionMatrix[4*4]; + + Shader *_lookUpShader; + GLint _paletteLocation; + + Graphics::Surface _clut8Data; + Graphics::Surface _userPixelData; + + byte _palette[4 * 256]; + bool _paletteDirty; +}; +#endif // !USE_FORCED_GLES && !USE_FORCED_GLES2 + } // End of namespace OpenGL #endif |