From 6b71456a55c9cce3eae12ee9cdacbf96fe1c1a71 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Sun, 31 Jul 2016 14:08:31 +0200 Subject: [PATCH] Add methods to make use of GPU local texture data copying bypassing a roundtrip to the CPU and back, add sf::Texture::swap to enable swapping texture contents, fixed sf::Font::cleanup not shrinking its allocated pixel buffer storage when the user loads a new font using the same sf::Font object. --- include/SFML/Graphics/Texture.hpp | 45 ++++++++++ src/SFML/Graphics/Font.cpp | 54 ++++++++---- src/SFML/Graphics/GLExtensions.hpp | 11 +++ src/SFML/Graphics/GLExtensions.txt | 1 + src/SFML/Graphics/GLLoader.cpp | 20 ++++- src/SFML/Graphics/GLLoader.hpp | 12 +++ src/SFML/Graphics/Texture.cpp | 134 ++++++++++++++++++++++++++--- 7 files changed, 246 insertions(+), 31 deletions(-) diff --git a/include/SFML/Graphics/Texture.hpp b/include/SFML/Graphics/Texture.hpp index f88c58241..20a8ede80 100644 --- a/include/SFML/Graphics/Texture.hpp +++ b/include/SFML/Graphics/Texture.hpp @@ -276,6 +276,43 @@ public: //////////////////////////////////////////////////////////// void update(const Uint8* pixels, unsigned int width, unsigned int height, unsigned int x, unsigned int y); + //////////////////////////////////////////////////////////// + /// \brief Update a part of this texture from another texture + /// + /// Although the source texture can be smaller than this texture, + /// this function is usually used for updating the whole texture. + /// The other overload, which has (x, y) additional arguments, + /// is more convenient for updating a sub-area of this texture. + /// + /// No additional check is performed on the size of the passed + /// texture, passing a texture bigger than this texture + /// will lead to an undefined behavior. + /// + /// This function does nothing if either texture was not + /// previously created. + /// + /// \param texture Source texture to copy to this texture + /// + //////////////////////////////////////////////////////////// + void update(const Texture& texture); + + //////////////////////////////////////////////////////////// + /// \brief Update a part of this texture from another texture + /// + /// No additional check is performed on the size of the texture, + /// passing an invalid combination of texture size and offset + /// will lead to an undefined behavior. + /// + /// This function does nothing if either texture was not + /// previously created. + /// + /// \param texture Source texture to copy to this texture + /// \param x X offset in this texture where to copy the source texture + /// \param y Y offset in this texture where to copy the source texture + /// + //////////////////////////////////////////////////////////// + void update(const Texture& texture, unsigned int x, unsigned int y); + //////////////////////////////////////////////////////////// /// \brief Update the texture from an image /// @@ -480,6 +517,14 @@ public: //////////////////////////////////////////////////////////// Texture& operator =(const Texture& right); + //////////////////////////////////////////////////////////// + /// \brief Swap the contents of this texture with those of another + /// + /// \param right Instance to swap with + /// + //////////////////////////////////////////////////////////// + void swap(Texture& right); + //////////////////////////////////////////////////////////// /// \brief Get the underlying OpenGL handle of the texture. /// diff --git a/src/SFML/Graphics/Font.cpp b/src/SFML/Graphics/Font.cpp index 53df89fa6..bf1823333 100644 --- a/src/SFML/Graphics/Font.cpp +++ b/src/SFML/Graphics/Font.cpp @@ -504,7 +504,7 @@ void Font::cleanup() m_streamRec = NULL; m_refCount = NULL; m_pages.clear(); - m_pixelBuffer.clear(); + std::vector().swap(m_pixelBuffer); } @@ -583,11 +583,14 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, f // pollute them with pixels from neighbors const unsigned int padding = 1; + width += 2 * padding; + height += 2 * padding; + // Get the glyphs page corresponding to the character size Page& page = m_pages[characterSize]; // Find a good position for the new glyph into the texture - glyph.textureRect = findGlyphRect(page, width + 2 * padding, height + 2 * padding); + glyph.textureRect = findGlyphRect(page, width, height); // Make sure the texture data is positioned in the center // of the allocated texture rectangle @@ -602,19 +605,32 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, f glyph.bounds.width = static_cast(face->glyph->metrics.width) / static_cast(1 << 6) + outlineThickness * 2; glyph.bounds.height = static_cast(face->glyph->metrics.height) / static_cast(1 << 6) + outlineThickness * 2; + // Resize the pixel buffer to the new size and fill it with transparent white pixels + m_pixelBuffer.resize(width * height * 4); + + Uint8* current = &m_pixelBuffer[0]; + Uint8* end = current + width * height * 4; + + while (current != end) + { + (*current++) = 255; + (*current++) = 255; + (*current++) = 255; + (*current++) = 0; + } + // Extract the glyph's pixels from the bitmap - m_pixelBuffer.resize(width * height * 4, 255); const Uint8* pixels = bitmap.buffer; if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO) { // Pixels are 1 bit monochrome values - for (int y = 0; y < height; ++y) + for (int y = padding; y < height - padding; ++y) { - for (int x = 0; x < width; ++x) + for (int x = padding; x < width - padding; ++x) { // The color channels remain white, just fill the alpha channel - std::size_t index = (x + y * width) * 4 + 3; - m_pixelBuffer[index] = ((pixels[x / 8]) & (1 << (7 - (x % 8)))) ? 255 : 0; + std::size_t index = x + y * width; + m_pixelBuffer[index * 4 + 3] = ((pixels[(x - padding) / 8]) & (1 << (7 - ((x - padding) % 8)))) ? 255 : 0; } pixels += bitmap.pitch; } @@ -622,23 +638,23 @@ Glyph Font::loadGlyph(Uint32 codePoint, unsigned int characterSize, bool bold, f else { // Pixels are 8 bits gray levels - for (int y = 0; y < height; ++y) + for (int y = padding; y < height - padding; ++y) { - for (int x = 0; x < width; ++x) + for (int x = padding; x < width - padding; ++x) { // The color channels remain white, just fill the alpha channel - std::size_t index = (x + y * width) * 4 + 3; - m_pixelBuffer[index] = pixels[x]; + std::size_t index = x + y * width; + m_pixelBuffer[index * 4 + 3] = pixels[x - padding]; } pixels += bitmap.pitch; } } // Write the pixels to the texture - unsigned int x = glyph.textureRect.left; - unsigned int y = glyph.textureRect.top; - unsigned int w = glyph.textureRect.width; - unsigned int h = glyph.textureRect.height; + unsigned int x = glyph.textureRect.left - padding; + unsigned int y = glyph.textureRect.top - padding; + unsigned int w = glyph.textureRect.width + 2 * padding; + unsigned int h = glyph.textureRect.height + 2 * padding; page.texture.update(&m_pixelBuffer[0], w, h, x, y); } @@ -689,10 +705,10 @@ IntRect Font::findGlyphRect(Page& page, unsigned int width, unsigned int height) if ((textureWidth * 2 <= Texture::getMaximumSize()) && (textureHeight * 2 <= Texture::getMaximumSize())) { // Make the texture 2 times bigger - Image newImage; - newImage.create(textureWidth * 2, textureHeight * 2, Color(255, 255, 255, 0)); - newImage.copy(page.texture.copyToImage(), 0, 0); - page.texture.loadFromImage(newImage); + Texture newTexture; + newTexture.create(textureWidth * 2, textureHeight * 2); + newTexture.update(page.texture); + page.texture.swap(newTexture); } else { diff --git a/src/SFML/Graphics/GLExtensions.hpp b/src/SFML/Graphics/GLExtensions.hpp index 2110bd959..76c97512b 100644 --- a/src/SFML/Graphics/GLExtensions.hpp +++ b/src/SFML/Graphics/GLExtensions.hpp @@ -111,6 +111,9 @@ #define GLEXT_GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING_OES #define GLEXT_GL_INVALID_FRAMEBUFFER_OPERATION GL_INVALID_FRAMEBUFFER_OPERATION_OES + // Core since 3.0 + #define GLEXT_framebuffer_blit false + // Core since 3.0 - EXT_sRGB #ifdef GL_EXT_sRGB #define GLEXT_texture_sRGB GL_EXT_sRGB @@ -243,6 +246,14 @@ #define GLEXT_GL_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING_EXT #define GLEXT_GL_INVALID_FRAMEBUFFER_OPERATION GL_INVALID_FRAMEBUFFER_OPERATION_EXT + // Core since 3.0 - EXT_framebuffer_blit + #define GLEXT_framebuffer_blit sfogl_ext_EXT_framebuffer_blit + #define GLEXT_glBlitFramebuffer glBlitFramebufferEXT + #define GLEXT_GL_READ_FRAMEBUFFER GL_READ_FRAMEBUFFER_EXT + #define GLEXT_GL_DRAW_FRAMEBUFFER GL_DRAW_FRAMEBUFFER_EXT + #define GLEXT_GL_DRAW_FRAMEBUFFER_BINDING GL_DRAW_FRAMEBUFFER_BINDING_EXT + #define GLEXT_GL_READ_FRAMEBUFFER_BINDING GL_READ_FRAMEBUFFER_BINDING_EXT + // Core since 3.2 - ARB_geometry_shader4 #define GLEXT_geometry_shader4 sfogl_ext_ARB_geometry_shader4 #define GLEXT_GL_GEOMETRY_SHADER GL_GEOMETRY_SHADER_ARB diff --git a/src/SFML/Graphics/GLExtensions.txt b/src/SFML/Graphics/GLExtensions.txt index 5b620278f..4b3ac6622 100644 --- a/src/SFML/Graphics/GLExtensions.txt +++ b/src/SFML/Graphics/GLExtensions.txt @@ -15,4 +15,5 @@ ARB_texture_non_power_of_two EXT_blend_equation_separate EXT_texture_sRGB EXT_framebuffer_object +EXT_framebuffer_blit ARB_geometry_shader4 diff --git a/src/SFML/Graphics/GLLoader.cpp b/src/SFML/Graphics/GLLoader.cpp index 40bc84c32..ec1531fb2 100644 --- a/src/SFML/Graphics/GLLoader.cpp +++ b/src/SFML/Graphics/GLLoader.cpp @@ -47,6 +47,7 @@ int sfogl_ext_ARB_texture_non_power_of_two = sfogl_LOAD_FAILED; int sfogl_ext_EXT_blend_equation_separate = sfogl_LOAD_FAILED; int sfogl_ext_EXT_texture_sRGB = sfogl_LOAD_FAILED; int sfogl_ext_EXT_framebuffer_object = sfogl_LOAD_FAILED; +int sfogl_ext_EXT_framebuffer_blit = sfogl_LOAD_FAILED; int sfogl_ext_ARB_geometry_shader4 = sfogl_LOAD_FAILED; void (GL_FUNCPTR *sf_ptrc_glBlendEquationEXT)(GLenum) = NULL; @@ -800,6 +801,19 @@ static int Load_EXT_framebuffer_object() return numFailed; } +void (GL_FUNCPTR *sf_ptrc_glBlitFramebufferEXT)(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum) = NULL; + +static int Load_EXT_framebuffer_blit() +{ + int numFailed = 0; + + sf_ptrc_glBlitFramebufferEXT = reinterpret_cast(glLoaderGetProcAddress("glBlitFramebufferEXT")); + if (!sf_ptrc_glBlitFramebufferEXT) + numFailed++; + + return numFailed; +} + void (GL_FUNCPTR *sf_ptrc_glFramebufferTextureARB)(GLenum, GLenum, GLuint, GLint) = NULL; void (GL_FUNCPTR *sf_ptrc_glFramebufferTextureFaceARB)(GLenum, GLenum, GLuint, GLint, GLenum) = NULL; void (GL_FUNCPTR *sf_ptrc_glFramebufferTextureLayerARB)(GLenum, GLenum, GLuint, GLint, GLint) = NULL; @@ -836,7 +850,7 @@ typedef struct sfogl_StrToExtMap_s PFN_LOADFUNCPOINTERS LoadExtension; } sfogl_StrToExtMap; -static sfogl_StrToExtMap ExtensionMap[15] = { +static sfogl_StrToExtMap ExtensionMap[16] = { {"GL_SGIS_texture_edge_clamp", &sfogl_ext_SGIS_texture_edge_clamp, NULL}, {"GL_EXT_texture_edge_clamp", &sfogl_ext_EXT_texture_edge_clamp, NULL}, {"GL_EXT_blend_minmax", &sfogl_ext_EXT_blend_minmax, Load_EXT_blend_minmax}, @@ -851,10 +865,11 @@ static sfogl_StrToExtMap ExtensionMap[15] = { {"GL_EXT_blend_equation_separate", &sfogl_ext_EXT_blend_equation_separate, Load_EXT_blend_equation_separate}, {"GL_EXT_texture_sRGB", &sfogl_ext_EXT_texture_sRGB, NULL}, {"GL_EXT_framebuffer_object", &sfogl_ext_EXT_framebuffer_object, Load_EXT_framebuffer_object}, + {"GL_EXT_framebuffer_blit", &sfogl_ext_EXT_framebuffer_blit, Load_EXT_framebuffer_blit}, {"GL_ARB_geometry_shader4", &sfogl_ext_ARB_geometry_shader4, Load_ARB_geometry_shader4} }; -static int g_extensionMapSize = 15; +static int g_extensionMapSize = 16; static void ClearExtensionVars() @@ -873,6 +888,7 @@ static void ClearExtensionVars() sfogl_ext_EXT_blend_equation_separate = sfogl_LOAD_FAILED; sfogl_ext_EXT_texture_sRGB = sfogl_LOAD_FAILED; sfogl_ext_EXT_framebuffer_object = sfogl_LOAD_FAILED; + sfogl_ext_EXT_framebuffer_blit = sfogl_LOAD_FAILED; sfogl_ext_ARB_geometry_shader4 = sfogl_LOAD_FAILED; } diff --git a/src/SFML/Graphics/GLLoader.hpp b/src/SFML/Graphics/GLLoader.hpp index fb3f9b378..a802808c3 100644 --- a/src/SFML/Graphics/GLLoader.hpp +++ b/src/SFML/Graphics/GLLoader.hpp @@ -184,6 +184,7 @@ extern int sfogl_ext_ARB_texture_non_power_of_two; extern int sfogl_ext_EXT_blend_equation_separate; extern int sfogl_ext_EXT_texture_sRGB; extern int sfogl_ext_EXT_framebuffer_object; +extern int sfogl_ext_EXT_framebuffer_blit; extern int sfogl_ext_ARB_geometry_shader4; #define GL_CLAMP_TO_EDGE_SGIS 0x812F @@ -379,6 +380,11 @@ extern int sfogl_ext_ARB_geometry_shader4; #define GL_STENCIL_INDEX4_EXT 0x8D47 #define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 + #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 @@ -1242,6 +1248,12 @@ extern void (GL_FUNCPTR *sf_ptrc_glRenderbufferStorageEXT)(GLenum, GLenum, GLsiz #define glRenderbufferStorageEXT sf_ptrc_glRenderbufferStorageEXT #endif // GL_EXT_framebuffer_object +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +extern void (GL_FUNCPTR *sf_ptrc_glBlitFramebufferEXT)(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum); +#define glBlitFramebufferEXT sf_ptrc_glBlitFramebufferEXT +#endif // GL_EXT_framebuffer_blit + #ifndef GL_ARB_geometry_shader4 #define GL_ARB_geometry_shader4 1 extern void (GL_FUNCPTR *sf_ptrc_glFramebufferTextureARB)(GLenum, GLenum, GLuint, GLint); diff --git a/src/SFML/Graphics/Texture.cpp b/src/SFML/Graphics/Texture.cpp index 091055994..2caba1656 100644 --- a/src/SFML/Graphics/Texture.cpp +++ b/src/SFML/Graphics/Texture.cpp @@ -88,7 +88,20 @@ m_hasMipmap (false), m_cacheId (getUniqueId()) { if (copy.m_texture) - loadFromImage(copy.copyToImage()); + { + if (create(copy.getSize().x, copy.getSize().y)) + { + update(copy); + + // Force an OpenGL flush, so that the texture will appear updated + // in all contexts immediately (solves problems in multi-threaded apps) + glCheck(glFlush()); + } + else + { + err() << "Failed to copy texture, failed to create new texture" << std::endl; + } + } } @@ -422,6 +435,97 @@ void Texture::update(const Uint8* pixels, unsigned int width, unsigned int heigh } +//////////////////////////////////////////////////////////// +void Texture::update(const Texture& texture) +{ + // Update the whole texture + update(texture, 0, 0); +} + + +//////////////////////////////////////////////////////////// +void Texture::update(const Texture& texture, unsigned int x, unsigned int y) +{ + assert(x + texture.m_size.x <= m_size.x); + assert(y + texture.m_size.x <= m_size.y); + + if (!m_texture || !texture.m_texture) + return; + +#ifndef SFML_OPENGL_ES + + { + TransientContextLock lock; + + // Make sure that extensions are initialized + priv::ensureExtensionsInit(); + } + + if (GLEXT_framebuffer_object && GLEXT_framebuffer_blit) + { + TransientContextLock lock; + + // Save the current bindings so we can restore them after we are done + GLint readFramebuffer = 0; + GLint drawFramebuffer = 0; + + glCheck(glGetIntegerv(GLEXT_GL_READ_FRAMEBUFFER_BINDING, &readFramebuffer)); + glCheck(glGetIntegerv(GLEXT_GL_DRAW_FRAMEBUFFER_BINDING, &drawFramebuffer)); + + // Create the framebuffers + GLuint sourceFrameBuffer = 0; + GLuint destFrameBuffer = 0; + glCheck(GLEXT_glGenFramebuffers(1, &sourceFrameBuffer)); + glCheck(GLEXT_glGenFramebuffers(1, &destFrameBuffer)); + + if (!sourceFrameBuffer || !destFrameBuffer) + { + err() << "Cannot copy texture, failed to create a frame buffer object" << std::endl; + return; + } + + // Link the source texture to the source frame buffer + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_READ_FRAMEBUFFER, sourceFrameBuffer)); + glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_READ_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.m_texture, 0)); + + // Link the destination texture to the destination frame buffer + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, destFrameBuffer)); + glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_DRAW_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0)); + + // A final check, just to be sure... + GLenum sourceStatus; + glCheck(sourceStatus = GLEXT_glCheckFramebufferStatus(GLEXT_GL_READ_FRAMEBUFFER)); + + GLenum destStatus; + glCheck(destStatus = GLEXT_glCheckFramebufferStatus(GLEXT_GL_DRAW_FRAMEBUFFER)); + + if ((sourceStatus == GLEXT_GL_FRAMEBUFFER_COMPLETE) && (destStatus == GLEXT_GL_FRAMEBUFFER_COMPLETE)) + { + // Blit the texture contents from the source to the destination texture + glCheck(GLEXT_glBlitFramebuffer(0, 0, texture.m_size.x, texture.m_size.y, x, y, x + texture.m_size.x, y + texture.m_size.y, GL_COLOR_BUFFER_BIT, GL_NEAREST)); + } + else + { + err() << "Cannot copy texture, failed to link texture to frame buffer" << std::endl; + } + + // Restore previously bound framebuffers + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_READ_FRAMEBUFFER, readFramebuffer)); + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_DRAW_FRAMEBUFFER, drawFramebuffer)); + + // Delete the framebuffers + glCheck(GLEXT_glDeleteFramebuffers(1, &sourceFrameBuffer)); + glCheck(GLEXT_glDeleteFramebuffers(1, &destFrameBuffer)); + + return; + } + +#endif // SFML_OPENGL_ES + + update(texture.copyToImage(), x, y); +} + + //////////////////////////////////////////////////////////// void Texture::update(const Image& image) { @@ -695,20 +799,30 @@ Texture& Texture::operator =(const Texture& right) { Texture temp(right); - std::swap(m_size, temp.m_size); - std::swap(m_actualSize, temp.m_actualSize); - std::swap(m_texture, temp.m_texture); - std::swap(m_isSmooth, temp.m_isSmooth); - std::swap(m_isRepeated, temp.m_isRepeated); - std::swap(m_pixelsFlipped, temp.m_pixelsFlipped); - std::swap(m_fboAttachment, temp.m_fboAttachment); - std::swap(m_hasMipmap, temp.m_hasMipmap); - m_cacheId = getUniqueId(); + swap(temp); return *this; } +//////////////////////////////////////////////////////////// +void Texture::swap(Texture& right) +{ + std::swap(m_size, right.m_size); + std::swap(m_actualSize, right.m_actualSize); + std::swap(m_texture, right.m_texture); + std::swap(m_isSmooth, right.m_isSmooth); + std::swap(m_sRgb, right.m_sRgb); + std::swap(m_isRepeated, right.m_isRepeated); + std::swap(m_pixelsFlipped, right.m_pixelsFlipped); + std::swap(m_fboAttachment, right.m_fboAttachment); + std::swap(m_hasMipmap, right.m_hasMipmap); + + m_cacheId = getUniqueId(); + right.m_cacheId = getUniqueId(); +} + + //////////////////////////////////////////////////////////// unsigned int Texture::getNativeHandle() const {