From 191730ac0d639cfdef8fae560cdbc7d7e8c67db1 Mon Sep 17 00:00:00 2001 From: Laurent Gomila Date: Sun, 25 Dec 2011 22:30:38 +0100 Subject: [PATCH] Added a render states cache to improve performances --- include/SFML/Graphics/RenderTarget.hpp | 61 ++++- include/SFML/Graphics/Texture.hpp | 3 + src/SFML/Graphics/CMakeLists.txt | 2 + src/SFML/Graphics/RenderTarget.cpp | 250 +++++++++++++----- .../Graphics/RenderTextureImplDefault.cpp | 4 + src/SFML/Graphics/RenderTextureImplFBO.cpp | 2 +- src/SFML/Graphics/Texture.cpp | 55 +++- src/SFML/Graphics/TextureSaver.cpp | 50 ++++ src/SFML/Graphics/TextureSaver.hpp | 75 ++++++ 9 files changed, 435 insertions(+), 67 deletions(-) create mode 100644 src/SFML/Graphics/TextureSaver.cpp create mode 100644 src/SFML/Graphics/TextureSaver.hpp diff --git a/include/SFML/Graphics/RenderTarget.hpp b/include/SFML/Graphics/RenderTarget.hpp index 548c09df..e87ffc3b 100644 --- a/include/SFML/Graphics/RenderTarget.hpp +++ b/include/SFML/Graphics/RenderTarget.hpp @@ -36,12 +36,12 @@ #include #include #include +#include namespace sf { class Drawable; -class Vertex; //////////////////////////////////////////////////////////// /// \brief Base class for all render targets (window, texture, ...) @@ -303,6 +303,44 @@ protected : //////////////////////////////////////////////////////////// void Initialize(); + //////////////////////////////////////////////////////////// + /// \brief Apply the current view + /// + //////////////////////////////////////////////////////////// + void ApplyCurrentView(); + + //////////////////////////////////////////////////////////// + /// \brief Apply a new blending mode + /// + /// \param mode Blending mode to apply + /// + //////////////////////////////////////////////////////////// + void ApplyBlendMode(BlendMode mode); + + //////////////////////////////////////////////////////////// + /// \brief Apply a new transform + /// + /// \param transform Transform to apply + /// + //////////////////////////////////////////////////////////// + void ApplyTransform(const Transform& transform); + + //////////////////////////////////////////////////////////// + /// \brief Apply a new texture + /// + /// \param texture Texture to apply + /// + //////////////////////////////////////////////////////////// + void ApplyTexture(const Texture* texture); + + //////////////////////////////////////////////////////////// + /// \brief Apply a new shader + /// + /// \param shader Shader to apply + /// + //////////////////////////////////////////////////////////// + void ApplyShader(const Shader* shader); + private : //////////////////////////////////////////////////////////// @@ -319,12 +357,27 @@ private : //////////////////////////////////////////////////////////// virtual bool Activate(bool active) = 0; + //////////////////////////////////////////////////////////// + /// \brief Render states cache + /// + //////////////////////////////////////////////////////////// + struct StatesCache + { + enum {VertexCacheSize = 4}; + + bool ViewChanged; ///< Has the current view changed since last draw? + BlendMode LastBlendMode; ///< Cached blending mode + Uint64 LastTextureId; ///< Cached texture + bool UseVertexCache; ///< Did we previously use the vertex cache? + Vertex VertexCache[VertexCacheSize]; ///< Pre-transformed vertices cache + }; + //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - View myDefaultView; ///< Default view - View myView; ///< Current view - bool myViewChanged; ///< Has the current view changed since last Draw? + View myDefaultView; ///< Default view + View myView; ///< Current view + StatesCache myCache; ///< Render states cache }; } // namespace sf diff --git a/include/SFML/Graphics/Texture.hpp b/include/SFML/Graphics/Texture.hpp index c500ab5f..7a2ee5e4 100644 --- a/include/SFML/Graphics/Texture.hpp +++ b/include/SFML/Graphics/Texture.hpp @@ -35,6 +35,7 @@ namespace sf { class Window; +class RenderTarget; class RenderTexture; class InputStream; @@ -467,6 +468,7 @@ public : private : friend class RenderTexture; + friend class RenderTarget; //////////////////////////////////////////////////////////// /// \brief Get a valid image size according to hardware support @@ -494,6 +496,7 @@ private : bool myIsSmooth; ///< Status of the smooth filter bool myIsRepeated; ///< Is the texture in repeat mode? mutable bool myPixelsFlipped; ///< To work around the inconsistency in Y orientation + Uint64 myCacheId; ///< Unique number that identifies the texture to the render target's cache }; } // namespace sf diff --git a/src/SFML/Graphics/CMakeLists.txt b/src/SFML/Graphics/CMakeLists.txt index 1adabaed..8856fb12 100644 --- a/src/SFML/Graphics/CMakeLists.txt +++ b/src/SFML/Graphics/CMakeLists.txt @@ -51,6 +51,8 @@ set(SRC ${INCROOT}/Text.hpp ${SRCROOT}/Texture.cpp ${INCROOT}/Texture.hpp + ${SRCROOT}/TextureSaver.cpp + ${SRCROOT}/TextureSaver.hpp ${SRCROOT}/Transform.cpp ${INCROOT}/Transform.hpp ${SRCROOT}/Transformable.cpp diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp index 3fbf730c..cea7386b 100644 --- a/src/SFML/Graphics/RenderTarget.cpp +++ b/src/SFML/Graphics/RenderTarget.cpp @@ -40,7 +40,7 @@ namespace sf RenderTarget::RenderTarget() : myDefaultView(), myView (), -myViewChanged(false) +myCache () { } @@ -66,7 +66,7 @@ void RenderTarget::Clear(const Color& color) void RenderTarget::SetView(const View& view) { myView = view; - myViewChanged = true; + myCache.ViewChanged = true; } @@ -136,81 +136,78 @@ void RenderTarget::Draw(const Vertex* vertices, unsigned int verticesCount, if (Activate(true)) { - // Apply the new view if needed - if (myViewChanged) + // Check if the vertex count is low enough so that we can pre-transform them + bool useVertexCache = (verticesCount <= StatesCache::VertexCacheSize); + if (useVertexCache) { - // Set the viewport - IntRect viewport = GetViewport(myView); - int top = GetHeight() - (viewport.Top + viewport.Height); - GLCheck(glViewport(viewport.Left, top, viewport.Width, viewport.Height)); + // Pre-transform the vertices and store them into the vertex cache + for (unsigned int i = 0; i < verticesCount; ++i) + { + Vertex& vertex = myCache.VertexCache[i]; + vertex.Position = states.Transform * vertices[i].Position; + vertex.Color = vertices[i].Color; + vertex.TexCoords = vertices[i].TexCoords; + } - // Set the projection matrix - GLCheck(glMatrixMode(GL_PROJECTION)); - GLCheck(glLoadMatrixf(myView.GetTransform().GetMatrix())); - - myViewChanged = false; + // Since vertices are transformed, we must use an identity transform to render them + if (!myCache.UseVertexCache) + ApplyTransform(Transform::Identity); + } + else + { + ApplyTransform(states.Transform); } - // Apply the transform - GLCheck(glMatrixMode(GL_MODELVIEW)); - GLCheck(glLoadMatrixf(states.Transform.GetMatrix())); + // Apply the view + if (myCache.ViewChanged) + ApplyCurrentView(); // Apply the blend mode - switch (states.BlendMode) - { - // Alpha blending - // glBlendFuncSeparateEXT is used when available to avoid an incorrect alpha value when the target - // is a RenderTexture -- in this case the alpha value must be written directly to the target buffer - default : - case BlendAlpha : - if (GLEW_EXT_blend_func_separate) - GLCheck(glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); - else - GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); - break; - - // Additive blending - case BlendAdd : - GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE)); - break; - - // Multiplicative blending - case BlendMultiply : - GLCheck(glBlendFunc(GL_DST_COLOR, GL_ZERO)); - break; - - // No blending - case BlendNone : - GLCheck(glBlendFunc(GL_ONE, GL_ZERO)); - break; - } + if (states.BlendMode != myCache.LastBlendMode) + ApplyBlendMode(states.BlendMode); // Apply the texture - if (states.Texture) - states.Texture->Bind(Texture::Pixels); - else - GLCheck(glBindTexture(GL_TEXTURE_2D, 0)); + Uint64 textureId = states.Texture ? states.Texture->myCacheId : 0; + if (textureId != myCache.LastTextureId) + ApplyTexture(states.Texture); // Apply the shader if (states.Shader) - states.Shader->Bind(); - else - GLCheck(glUseProgramObjectARB(0)); + ApplyShader(states.Shader); + + // If we pre-transform the vertices, we must use our internal vertex cache + if (useVertexCache) + { + // ... and if we already used it previously, we don't need to set the pointers again + if (!myCache.UseVertexCache) + vertices = myCache.VertexCache; + else + vertices = NULL; + } // Setup the pointers to the vertices' components - const char* data = reinterpret_cast(vertices); - GLCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0)); - GLCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); - GLCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); + if (vertices) + { + const char* data = reinterpret_cast(vertices); + GLCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0)); + GLCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); + GLCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); + } // Find the OpenGL primitive type - static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, - GL_TRIANGLES, GL_TRIANGLE_STRIP, - GL_TRIANGLE_FAN, GL_QUADS}; + static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, + GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS}; GLenum mode = modes[type]; // Draw the primitives GLCheck(glDrawArrays(mode, 0, verticesCount)); + + // Unbind the shader, if any + if (states.Shader) + ApplyShader(NULL); + + // Update the cache + myCache.UseVertexCache = useVertexCache; } } @@ -257,16 +254,26 @@ void RenderTarget::ResetGLStates() // Make sure that GLEW is initialized priv::EnsureGlewInit(); + // Define the default OpenGL states GLCheck(glDisable(GL_LIGHTING)); GLCheck(glDisable(GL_DEPTH_TEST)); GLCheck(glEnable(GL_TEXTURE_2D)); GLCheck(glEnable(GL_ALPHA_TEST)); GLCheck(glEnable(GL_BLEND)); GLCheck(glAlphaFunc(GL_GREATER, 0)); + GLCheck(glMatrixMode(GL_MODELVIEW)); GLCheck(glEnableClientState(GL_VERTEX_ARRAY)); GLCheck(glEnableClientState(GL_COLOR_ARRAY)); GLCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + // Apply the default SFML states + ApplyBlendMode(BlendAlpha); + ApplyTransform(Transform::Identity); + ApplyTexture(NULL); + ApplyShader(NULL); + myCache.UseVertexCache = false; + + // Set the default view SetView(GetView()); } } @@ -275,12 +282,135 @@ void RenderTarget::ResetGLStates() //////////////////////////////////////////////////////////// void RenderTarget::Initialize() { - // Setup the default view + // Setup the default and current views myDefaultView.Reset(FloatRect(0, 0, static_cast(GetWidth()), static_cast(GetHeight()))); - SetView(myDefaultView); + myView = myDefaultView; // Initialize the default OpenGL render-states ResetGLStates(); } + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyCurrentView() +{ + // Set the viewport + IntRect viewport = GetViewport(myView); + int top = GetHeight() - (viewport.Top + viewport.Height); + GLCheck(glViewport(viewport.Left, top, viewport.Width, viewport.Height)); + + // Set the projection matrix + GLCheck(glMatrixMode(GL_PROJECTION)); + GLCheck(glLoadMatrixf(myView.GetTransform().GetMatrix())); + + // Go back to model-view mode + GLCheck(glMatrixMode(GL_MODELVIEW)); + + myCache.ViewChanged = false; +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyBlendMode(BlendMode mode) +{ + switch (mode) + { + // Alpha blending + // glBlendFuncSeparateEXT is used when available to avoid an incorrect alpha value when the target + // is a RenderTexture -- in this case the alpha value must be written directly to the target buffer + default : + case BlendAlpha : + if (GLEW_EXT_blend_func_separate) + GLCheck(glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + else + GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + break; + + // Additive blending + case BlendAdd : + GLCheck(glBlendFunc(GL_SRC_ALPHA, GL_ONE)); + break; + + // Multiplicative blending + case BlendMultiply : + GLCheck(glBlendFunc(GL_DST_COLOR, GL_ZERO)); + break; + + // No blending + case BlendNone : + GLCheck(glBlendFunc(GL_ONE, GL_ZERO)); + break; + } + + myCache.LastBlendMode = mode; +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyTransform(const Transform& transform) +{ + // No need to call glMatrixMode(GL_MODELVIEW), it is always the + // current mode (for optimization purpose, since it's the most used) + GLCheck(glLoadMatrixf(transform.GetMatrix())); +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyTexture(const Texture* texture) +{ + if (texture) + texture->Bind(Texture::Pixels); + else + GLCheck(glBindTexture(GL_TEXTURE_2D, 0)); + + myCache.LastTextureId = texture ? texture->myCacheId : 0; +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::ApplyShader(const Shader* shader) +{ + if (shader) + shader->Bind(); + else + GLCheck(glUseProgramObjectARB(0)); +} + } // namespace sf + + +//////////////////////////////////////////////////////////// +// Render states caching strategies +// +// * View +// If SetView was called since last draw, the projection +// matrix is updated. We don't need more, the view doesn't +// change frequently. +// +// * Transform +// The transform matrix is usually expensive because each +// entity will most likely use a different transform. This can +// lead, in worst case, to changing it every 4 vertices. +// To avoid that, when the vertex count is low enough, we +// pre-transform them and therefore use an identity transform +// to render them. +// +// * Blending mode +// It's a simple integral value, so we can easily check +// whether the value to apply is the same as before or not. +// +// * Texture +// Storing the pointer or OpenGL ID of the last used texture +// is not enough; if the sf::Texture instance is destroyed, +// both the pointer and the OpenGL ID might be recycled in +// a new texture instance. We need to use our own unique +// identifier system to ensure consistent caching. +// +// * Shader +// Shaders are very hard to optimize, because they have +// parameters that can be hard (if not impossible) to track, +// like matrices or textures. The only optimization that we +// do is that we avoid setting a null shader if there was +// already none for the previous draw. +// +//////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/RenderTextureImplDefault.cpp b/src/SFML/Graphics/RenderTextureImplDefault.cpp index 7cffc63c..481eed64 100644 --- a/src/SFML/Graphics/RenderTextureImplDefault.cpp +++ b/src/SFML/Graphics/RenderTextureImplDefault.cpp @@ -27,6 +27,7 @@ //////////////////////////////////////////////////////////// #include #include +#include #include #include @@ -77,6 +78,9 @@ bool RenderTextureImplDefault::Activate(bool active) //////////////////////////////////////////////////////////// void RenderTextureImplDefault::UpdateTexture(unsigned int textureId) { + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Copy the rendered pixels to the texture GLCheck(glBindTexture(GL_TEXTURE_2D, textureId)); GLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, myWidth, myHeight)); diff --git a/src/SFML/Graphics/RenderTextureImplFBO.cpp b/src/SFML/Graphics/RenderTextureImplFBO.cpp index 326452ae..d7ded0de 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.cpp +++ b/src/SFML/Graphics/RenderTextureImplFBO.cpp @@ -83,7 +83,7 @@ bool RenderTextureImplFBO::IsAvailable() //////////////////////////////////////////////////////////// bool RenderTextureImplFBO::Create(unsigned int width, unsigned int height, unsigned int textureId, bool depthBuffer) { - //Create the context + // Create the context myContext = new Context; // Create the framebuffer object diff --git a/src/SFML/Graphics/Texture.cpp b/src/SFML/Graphics/Texture.cpp index 197baf33..22207fe7 100644 --- a/src/SFML/Graphics/Texture.cpp +++ b/src/SFML/Graphics/Texture.cpp @@ -28,12 +28,33 @@ #include #include #include +#include #include +#include +#include #include #include #include +//////////////////////////////////////////////////////////// +// Private data +//////////////////////////////////////////////////////////// +namespace +{ + // Thread-safe unique identifier generator, + // is used for states cache (see RenderTarget) + sf::Uint64 GetUniqueId() + { + static sf::Uint64 id = 1; // start at 1, zero is "no texture" + static sf::Mutex mutex; + + sf::Lock lock(mutex); + return id++; + } +} + + namespace sf { //////////////////////////////////////////////////////////// @@ -45,7 +66,8 @@ myTextureHeight(0), myTexture (0), myIsSmooth (false), myIsRepeated (false), -myPixelsFlipped(false) +myPixelsFlipped(false), +myCacheId (GetUniqueId()) { } @@ -60,7 +82,8 @@ myTextureHeight(0), myTexture (0), myIsSmooth (copy.myIsSmooth), myIsRepeated (copy.myIsRepeated), -myPixelsFlipped(false) +myPixelsFlipped(false), +myCacheId (GetUniqueId()) { if (copy.myTexture) LoadFromImage(copy.CopyToImage()); @@ -123,6 +146,9 @@ bool Texture::Create(unsigned int width, unsigned int height) myTexture = static_cast(texture); } + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Initialize the texture GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, myTextureWidth, myTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); @@ -130,6 +156,7 @@ bool Texture::Create(unsigned int width, unsigned int height) GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); + myCacheId = GetUniqueId(); return true; } @@ -195,6 +222,9 @@ bool Texture::LoadFromImage(const Image& image, const IntRect& area) // Create the texture and upload the pixels if (Create(rectangle.Width, rectangle.Height)) { + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Copy the pixels to the texture, row by row const Uint8* pixels = image.GetPixelsPtr() + 4 * (rectangle.Left + (width * rectangle.Top)); GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); @@ -237,6 +267,9 @@ Image Texture::CopyToImage() const EnsureGlContext(); + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Create an array of pixels std::vector pixels(myWidth * myHeight * 4); @@ -302,10 +335,14 @@ void Texture::Update(const Uint8* pixels, unsigned int width, unsigned int heigh { EnsureGlContext(); + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Copy pixels from the given array to the texture GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); myPixelsFlipped = false; + myCacheId = GetUniqueId(); } } @@ -340,10 +377,14 @@ void Texture::Update(const Window& window, unsigned int x, unsigned int y) if (myTexture && window.SetActive(true)) { + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + // Copy pixels from the back-buffer to the texture GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 0, 0, window.GetWidth(), window.GetHeight())); myPixelsFlipped = true; + myCacheId = GetUniqueId(); } } @@ -380,6 +421,9 @@ void Texture::Bind(CoordinateType coordinateType) const // Load the matrix GLCheck(glMatrixMode(GL_TEXTURE)); GLCheck(glLoadMatrixf(matrix)); + + // Go back to model-view mode (sf::RenderTarget relies on it) + GLCheck(glMatrixMode(GL_MODELVIEW)); } } @@ -395,6 +439,9 @@ void Texture::SetSmooth(bool smooth) { EnsureGlContext(); + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, myIsSmooth ? GL_LINEAR : GL_NEAREST)); @@ -421,6 +468,9 @@ void Texture::SetRepeated(bool repeated) { EnsureGlContext(); + // Make sure that the current texture binding will be preserved + priv::TextureSaver save; + GLCheck(glBindTexture(GL_TEXTURE_2D, myTexture)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); GLCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, myIsRepeated ? GL_REPEAT : GL_CLAMP_TO_EDGE)); @@ -461,6 +511,7 @@ Texture& Texture::operator =(const Texture& right) std::swap(myIsSmooth, temp.myIsSmooth); std::swap(myIsRepeated, temp.myIsRepeated); std::swap(myPixelsFlipped, temp.myPixelsFlipped); + myCacheId = GetUniqueId(); return *this; } diff --git a/src/SFML/Graphics/TextureSaver.cpp b/src/SFML/Graphics/TextureSaver.cpp new file mode 100644 index 00000000..e4bbecd3 --- /dev/null +++ b/src/SFML/Graphics/TextureSaver.cpp @@ -0,0 +1,50 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +TextureSaver::TextureSaver() +{ + GLCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &myTextureBinding)); +} + + +//////////////////////////////////////////////////////////// +TextureSaver::~TextureSaver() +{ + GLCheck(glBindTexture(GL_TEXTURE_2D, myTextureBinding)); +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Graphics/TextureSaver.hpp b/src/SFML/Graphics/TextureSaver.hpp new file mode 100644 index 00000000..845cffb7 --- /dev/null +++ b/src/SFML/Graphics/TextureSaver.hpp @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2009 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_TEXTURESAVER_HPP +#define SFML_TEXTURESAVER_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Automatic wrapper for saving and restoring the current texture binding +/// +//////////////////////////////////////////////////////////// +class TextureSaver +{ +public : + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// The current texture binding is saved. + /// + //////////////////////////////////////////////////////////// + TextureSaver(); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + /// The previous texture binding is restored. + /// + //////////////////////////////////////////////////////////// + ~TextureSaver(); + +private : + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + GLint myTextureBinding; ///< Texture binding to restore +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_TEXTURESAVER_HPP