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.

This commit is contained in:
binary1248 2016-07-31 14:08:31 +02:00 committed by Lukas Dürrenberger
parent f053871a6c
commit 6b71456a55
7 changed files with 246 additions and 31 deletions

View File

@ -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.
///

View File

@ -504,7 +504,7 @@ void Font::cleanup()
m_streamRec = NULL;
m_refCount = NULL;
m_pages.clear();
m_pixelBuffer.clear();
std::vector<Uint8>().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<float>(face->glyph->metrics.width) / static_cast<float>(1 << 6) + outlineThickness * 2;
glyph.bounds.height = static_cast<float>(face->glyph->metrics.height) / static_cast<float>(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
{

View File

@ -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

View File

@ -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

View File

@ -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<void (GL_FUNCPTR *)(GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLint, GLbitfield, GLenum)>(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;
}

View File

@ -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);

View File

@ -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
{