From fcd52afb5c34084559930756d6162bb7eb9cc7d1 Mon Sep 17 00:00:00 2001 From: binary1248 Date: Wed, 8 May 2019 01:05:25 +0200 Subject: [PATCH] Implemented initial support for modular rendering backends. Currently only the existing GL1 renderer implementation is available. --- include/SFML/Graphics.hpp | 1 + include/SFML/Graphics/RenderTarget.hpp | 100 +- include/SFML/Graphics/Renderer.hpp | 117 +++ include/SFML/Graphics/Shader.hpp | 61 +- include/SFML/Graphics/Texture.hpp | 52 +- include/SFML/Graphics/VertexBuffer.hpp | 14 +- src/SFML/Graphics/CMakeLists.txt | 57 +- src/SFML/Graphics/Font.cpp | 3 +- .../OpenGL/GL1/RenderTargetImplDefault.cpp | 704 ++++++++++++++ .../OpenGL/GL1/RenderTargetImplDefault.hpp | 271 ++++++ .../GL1}/RenderTextureImplDefault.cpp | 27 +- .../GL1}/RenderTextureImplDefault.hpp | 0 .../{ => OpenGL/GL1}/RenderTextureImplFBO.cpp | 20 +- .../{ => OpenGL/GL1}/RenderTextureImplFBO.hpp | 12 + .../Graphics/OpenGL/GL1/ShaderImplDefault.cpp | 687 +++++++++++++ .../Graphics/OpenGL/GL1/ShaderImplDefault.hpp | 327 +++++++ .../OpenGL/GL1/TextureImplDefault.cpp | 796 ++++++++++++++++ .../OpenGL/GL1/TextureImplDefault.hpp | 297 ++++++ .../OpenGL/GL1/VertexBufferImplDefault.cpp | 276 ++++++ .../OpenGL/GL1/VertexBufferImplDefault.hpp | 170 ++++ src/SFML/Graphics/{ => OpenGL}/GLCheck.cpp | 2 +- src/SFML/Graphics/{ => OpenGL}/GLCheck.hpp | 2 +- .../Graphics/{ => OpenGL}/GLExtensions.cpp | 2 +- .../Graphics/{ => OpenGL}/GLExtensions.hpp | 0 .../Graphics/{ => OpenGL}/GLExtensions.txt | 0 src/SFML/Graphics/RenderTarget.cpp | 601 +----------- ...{TextureSaver.hpp => RenderTargetImpl.cpp} | 76 +- src/SFML/Graphics/RenderTargetImpl.hpp | 207 ++++ src/SFML/Graphics/RenderTexture.cpp | 62 +- src/SFML/Graphics/RenderWindow.cpp | 29 +- src/SFML/Graphics/Renderer.cpp | 59 ++ src/SFML/Graphics/Shader.cpp | 901 ++---------------- .../{TextureSaver.cpp => ShaderImpl.cpp} | 13 +- src/SFML/Graphics/ShaderImpl.hpp | 251 +++++ src/SFML/Graphics/ShaderImplNull.cpp | 179 ++++ src/SFML/Graphics/ShaderImplNull.hpp | 272 ++++++ src/SFML/Graphics/Text.cpp | 17 +- src/SFML/Graphics/Texture.cpp | 664 +------------ src/SFML/Graphics/TextureImpl.cpp | 43 + src/SFML/Graphics/TextureImpl.hpp | 213 +++++ src/SFML/Graphics/VertexBuffer.cpp | 222 +---- src/SFML/Graphics/VertexBufferImpl.cpp | 43 + src/SFML/Graphics/VertexBufferImpl.hpp | 124 +++ 43 files changed, 5450 insertions(+), 2524 deletions(-) create mode 100644 include/SFML/Graphics/Renderer.hpp create mode 100644 src/SFML/Graphics/OpenGL/GL1/RenderTargetImplDefault.cpp create mode 100644 src/SFML/Graphics/OpenGL/GL1/RenderTargetImplDefault.hpp rename src/SFML/Graphics/{ => OpenGL/GL1}/RenderTextureImplDefault.cpp (85%) rename src/SFML/Graphics/{ => OpenGL/GL1}/RenderTextureImplDefault.hpp (100%) rename src/SFML/Graphics/{ => OpenGL/GL1}/RenderTextureImplFBO.cpp (97%) rename src/SFML/Graphics/{ => OpenGL/GL1}/RenderTextureImplFBO.hpp (92%) create mode 100644 src/SFML/Graphics/OpenGL/GL1/ShaderImplDefault.cpp create mode 100644 src/SFML/Graphics/OpenGL/GL1/ShaderImplDefault.hpp create mode 100644 src/SFML/Graphics/OpenGL/GL1/TextureImplDefault.cpp create mode 100644 src/SFML/Graphics/OpenGL/GL1/TextureImplDefault.hpp create mode 100644 src/SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.cpp create mode 100644 src/SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.hpp rename src/SFML/Graphics/{ => OpenGL}/GLCheck.cpp (98%) rename src/SFML/Graphics/{ => OpenGL}/GLCheck.hpp (98%) rename src/SFML/Graphics/{ => OpenGL}/GLExtensions.cpp (98%) rename src/SFML/Graphics/{ => OpenGL}/GLExtensions.hpp (100%) rename src/SFML/Graphics/{ => OpenGL}/GLExtensions.txt (100%) rename src/SFML/Graphics/{TextureSaver.hpp => RenderTargetImpl.cpp} (60%) create mode 100644 src/SFML/Graphics/RenderTargetImpl.hpp create mode 100644 src/SFML/Graphics/Renderer.cpp rename src/SFML/Graphics/{TextureSaver.cpp => ShaderImpl.cpp} (82%) create mode 100644 src/SFML/Graphics/ShaderImpl.hpp create mode 100644 src/SFML/Graphics/ShaderImplNull.cpp create mode 100644 src/SFML/Graphics/ShaderImplNull.hpp create mode 100644 src/SFML/Graphics/TextureImpl.cpp create mode 100644 src/SFML/Graphics/TextureImpl.hpp create mode 100644 src/SFML/Graphics/VertexBufferImpl.cpp create mode 100644 src/SFML/Graphics/VertexBufferImpl.hpp diff --git a/include/SFML/Graphics.hpp b/include/SFML/Graphics.hpp index d0e0c8ff3..db66c1fa0 100644 --- a/include/SFML/Graphics.hpp +++ b/include/SFML/Graphics.hpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include diff --git a/include/SFML/Graphics/RenderTarget.hpp b/include/SFML/Graphics/RenderTarget.hpp index c35a99eae..0b52320a5 100644 --- a/include/SFML/Graphics/RenderTarget.hpp +++ b/include/SFML/Graphics/RenderTarget.hpp @@ -30,13 +30,8 @@ //////////////////////////////////////////////////////////// #include #include -#include -#include -#include -#include #include #include -#include #include @@ -44,6 +39,13 @@ namespace sf { class Drawable; class VertexBuffer; +class View; +class Vertex; + +namespace priv +{ + class RenderTargetImpl; +} //////////////////////////////////////////////////////////// /// \brief Base class for all render targets (window, texture, ...) @@ -385,96 +387,10 @@ protected: private: - //////////////////////////////////////////////////////////// - /// \brief Apply the current view - /// - //////////////////////////////////////////////////////////// - void applyCurrentView(); - - //////////////////////////////////////////////////////////// - /// \brief Apply a new blending mode - /// - /// \param mode Blending mode to apply - /// - //////////////////////////////////////////////////////////// - void applyBlendMode(const 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); - - //////////////////////////////////////////////////////////// - /// \brief Setup environment for drawing - /// - /// \param useVertexCache Are we going to use the vertex cache? - /// \param states Render states to use for drawing - /// - //////////////////////////////////////////////////////////// - void setupDraw(bool useVertexCache, const RenderStates& states); - - //////////////////////////////////////////////////////////// - /// \brief Draw the primitives - /// - /// \param type Type of primitives to draw - /// \param firstVertex Index of the first vertex to use when drawing - /// \param vertexCount Number of vertices to use when drawing - /// - //////////////////////////////////////////////////////////// - void drawPrimitives(PrimitiveType type, std::size_t firstVertex, std::size_t vertexCount); - - //////////////////////////////////////////////////////////// - /// \brief Clean up environment after drawing - /// - /// \param states Render states used for drawing - /// - //////////////////////////////////////////////////////////// - void cleanupDraw(const RenderStates& states); - - //////////////////////////////////////////////////////////// - /// \brief Render states cache - /// - //////////////////////////////////////////////////////////// - struct StatesCache - { - enum {VertexCacheSize = 4}; - - bool enable; ///< Is the cache enabled? - bool glStatesSet; ///< Are our internal GL states set yet? - bool viewChanged; ///< Has the current view changed since last draw? - BlendMode lastBlendMode; ///< Cached blending mode - Uint64 lastTextureId; ///< Cached texture - bool texCoordsArrayEnabled; ///< Is GL_TEXTURE_COORD_ARRAY client state enabled? - bool useVertexCache; ///< Did we previously use the vertex cache? - Vertex vertexCache[VertexCacheSize]; ///< Pre-transformed vertices cache - }; - //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - View m_defaultView; ///< Default view - View m_view; ///< Current view - StatesCache m_cache; ///< Render states cache - Uint64 m_id; ///< Unique number that identifies the RenderTarget + priv::RenderTargetImpl* m_impl; ///< Platform/hardware specific implementation }; } // namespace sf diff --git a/include/SFML/Graphics/Renderer.hpp b/include/SFML/Graphics/Renderer.hpp new file mode 100644 index 000000000..75010d51b --- /dev/null +++ b/include/SFML/Graphics/Renderer.hpp @@ -0,0 +1,117 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_RENDERER_HPP +#define SFML_RENDERER_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +namespace Renderer +{ + //////////////////////////////////////////////////////////// + /// \ingroup graphics + /// \brief Enumeration of the renderer types + /// + //////////////////////////////////////////////////////////// + enum + { + Legacy = 0, ///< Let SFML choose the renderer for best compatibility + OpenGL1 = 1 << 0, ///< OpenGL 1.x renderer + + Default = Legacy ///< Default renderer + }; +} + +//////////////////////////////////////////////////////////// +/// \ingroup graphics +/// \brief Get the available renderers +/// +/// SFML determines at runtime the renderers that are supported +/// on the target system. These are returned by this function. +/// When calling setRenderers(), only renderers returned by this +/// function are allowed to be specified. +/// +/// \return Renderers supported on the current system +/// +/// \see setRenderers, getRenderer +/// +//////////////////////////////////////////////////////////// +Uint32 SFML_GRAPHICS_API getAvailableRenderers(); + +//////////////////////////////////////////////////////////// +/// \ingroup graphics +/// \brief Set the renderers SFML is allowed to pick from +/// +/// Before anything graphics related is performed, it is possible +/// to specify via this function the renderers that SFML is allowed +/// to choose from to perform any future rendering operations. +/// +/// The renderers available on the target system can be retrieved +/// via getAvailableRenderers(). +/// +/// Once SFML has chosen a renderer, it is final until the next time +/// the application is run. As such, this function must be called as +/// soon as possible if the user decides they want influence the +/// selection process in any way. +/// +/// If the applicable renderers are not explicitly set by calling this +/// function, SFML will automatically choose from everything that is +/// supported on the target system. +/// +/// \param renderers Bit-wise OR of renderers SFML is allowed to pick from +/// +/// \see getAvailableRenderers, getRenderer +/// +//////////////////////////////////////////////////////////// +void SFML_GRAPHICS_API setRenderers(Uint32 renderers); + +//////////////////////////////////////////////////////////// +/// \ingroup graphics +/// \brief Get the renderer SFML has selected to use +/// +/// Calling this function will force SFML to finalize its decision +/// on which renderer it wants to make use of if it has not already +/// done so. The selected renderer is then returned by this function. +/// +/// After this function has been called either from user code or +/// internally within SFML itself, calling setRenderers() will no +/// longer have any effect until the next time the application is run. +/// +/// \return The renderer SFML has selected to use +/// +/// \see setRenderers +/// +//////////////////////////////////////////////////////////// +Uint32 SFML_GRAPHICS_API getRenderer(); + +} // namespace sf + + +#endif // SFML_RENDERER_HPP diff --git a/include/SFML/Graphics/Shader.hpp b/include/SFML/Graphics/Shader.hpp index 6b0026e86..62dfc6129 100644 --- a/include/SFML/Graphics/Shader.hpp +++ b/include/SFML/Graphics/Shader.hpp @@ -45,11 +45,16 @@ class InputStream; class Texture; class Transform; +namespace priv +{ + class ShaderImpl; +} + //////////////////////////////////////////////////////////// /// \brief Shader class (vertex, geometry and fragment) /// //////////////////////////////////////////////////////////// -class SFML_GRAPHICS_API Shader : GlResource, NonCopyable +class SFML_GRAPHICS_API Shader : NonCopyable { public: @@ -693,62 +698,10 @@ public: private: - //////////////////////////////////////////////////////////// - /// \brief Compile the shader(s) and create the program - /// - /// If one of the arguments is NULL, the corresponding shader - /// is not created. - /// - /// \param vertexShaderCode Source code of the vertex shader - /// \param geometryShaderCode Source code of the geometry shader - /// \param fragmentShaderCode Source code of the fragment shader - /// - /// \return True on success, false if any error happened - /// - //////////////////////////////////////////////////////////// - bool compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode); - - //////////////////////////////////////////////////////////// - /// \brief Bind all the textures used by the shader - /// - /// This function each texture to a different unit, and - /// updates the corresponding variables in the shader accordingly. - /// - //////////////////////////////////////////////////////////// - void bindTextures() const; - - //////////////////////////////////////////////////////////// - /// \brief Get the location ID of a shader uniform - /// - /// \param name Name of the uniform variable to search - /// - /// \return Location ID of the uniform, or -1 if not found - /// - //////////////////////////////////////////////////////////// - int getUniformLocation(const std::string& name); - - //////////////////////////////////////////////////////////// - /// \brief RAII object to save and restore the program - /// binding while uniforms are being set - /// - /// Implementation is private in the .cpp file. - /// - //////////////////////////////////////////////////////////// - struct UniformBinder; - - //////////////////////////////////////////////////////////// - // Types - //////////////////////////////////////////////////////////// - typedef std::map TextureTable; - typedef std::map UniformTable; - //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - unsigned int m_shaderProgram; ///< OpenGL identifier for the program - int m_currentTexture; ///< Location of the current texture in the shader - TextureTable m_textures; ///< Texture variables in the shader, mapped to their location - UniformTable m_uniforms; ///< Parameters location cache + priv::ShaderImpl* m_impl; ///< Platform/hardware specific implementation }; } // namespace sf diff --git a/include/SFML/Graphics/Texture.hpp b/include/SFML/Graphics/Texture.hpp index a07b5d706..1f0b79366 100644 --- a/include/SFML/Graphics/Texture.hpp +++ b/include/SFML/Graphics/Texture.hpp @@ -29,23 +29,30 @@ // Headers //////////////////////////////////////////////////////////// #include -#include -#include +#include +#include +#include namespace sf { class InputStream; -class RenderTarget; class RenderTexture; class Text; +class Image; class Window; +namespace priv +{ + class TextureImpl; + class RenderTargetImpl; +} + //////////////////////////////////////////////////////////// /// \brief Image living on the graphics card that can be used for drawing /// //////////////////////////////////////////////////////////// -class SFML_GRAPHICS_API Texture : GlResource +class SFML_GRAPHICS_API Texture { public: @@ -587,45 +594,12 @@ private: friend class Text; friend class RenderTexture; - friend class RenderTarget; - - //////////////////////////////////////////////////////////// - /// \brief Get a valid image size according to hardware support - /// - /// This function checks whether the graphics driver supports - /// non power of two sizes or not, and adjusts the size - /// accordingly. - /// The returned size is greater than or equal to the original size. - /// - /// \param size size to convert - /// - /// \return Valid nearest size (greater than or equal to specified size) - /// - //////////////////////////////////////////////////////////// - static unsigned int getValidSize(unsigned int size); - - //////////////////////////////////////////////////////////// - /// \brief Invalidate the mipmap if one exists - /// - /// This also resets the texture's minifying function. - /// This function is mainly for internal use by RenderTexture. - /// - //////////////////////////////////////////////////////////// - void invalidateMipmap(); + friend class priv::RenderTargetImpl; //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - Vector2u m_size; ///< Public texture size - Vector2u m_actualSize; ///< Actual texture size (can be greater than public size because of padding) - unsigned int m_texture; ///< Internal texture identifier - bool m_isSmooth; ///< Status of the smooth filter - bool m_sRgb; ///< Should the texture source be converted from sRGB? - bool m_isRepeated; ///< Is the texture in repeat mode? - mutable bool m_pixelsFlipped; ///< To work around the inconsistency in Y orientation - bool m_fboAttachment; ///< Is this texture owned by a framebuffer object? - bool m_hasMipmap; ///< Has the mipmap been generated? - Uint64 m_cacheId; ///< Unique number that identifies the texture to the render target's cache + priv::TextureImpl* m_impl; ///< Platform/hardware specific implementation }; } // namespace sf diff --git a/include/SFML/Graphics/VertexBuffer.hpp b/include/SFML/Graphics/VertexBuffer.hpp index cffbd09df..a4e5141a3 100644 --- a/include/SFML/Graphics/VertexBuffer.hpp +++ b/include/SFML/Graphics/VertexBuffer.hpp @@ -31,7 +31,6 @@ #include #include #include -#include namespace sf @@ -39,11 +38,16 @@ namespace sf class RenderTarget; class Vertex; +namespace priv +{ + class VertexBufferImpl; +} + //////////////////////////////////////////////////////////// /// \brief Vertex buffer storage for one or more 2D primitives /// //////////////////////////////////////////////////////////// -class SFML_GRAPHICS_API VertexBuffer : public Drawable, private GlResource +class SFML_GRAPHICS_API VertexBuffer : public Drawable { public: @@ -334,10 +338,8 @@ private: //////////////////////////////////////////////////////////// // Member data //////////////////////////////////////////////////////////// - unsigned int m_buffer; ///< Internal buffer identifier - std::size_t m_size; ///< Size in Vertexes of the currently allocated buffer - PrimitiveType m_primitiveType; ///< Type of primitives to draw - Usage m_usage; ///< How this vertex buffer is to be used + priv::VertexBufferImpl* m_impl; ///< Platform/hardware specific implementation + PrimitiveType m_primitiveType; ///< Type of primitives to draw }; } // namespace sf diff --git a/src/SFML/Graphics/CMakeLists.txt b/src/SFML/Graphics/CMakeLists.txt index 14c48ea9b..f68121f7b 100644 --- a/src/SFML/Graphics/CMakeLists.txt +++ b/src/SFML/Graphics/CMakeLists.txt @@ -15,10 +15,6 @@ set(SRC ${INCROOT}/Glsl.hpp ${INCROOT}/Glsl.inl ${INCROOT}/Glyph.hpp - ${SRCROOT}/GLCheck.cpp - ${SRCROOT}/GLCheck.hpp - ${SRCROOT}/GLExtensions.hpp - ${SRCROOT}/GLExtensions.cpp ${SRCROOT}/Image.cpp ${INCROOT}/Image.hpp ${SRCROOT}/ImageLoader.cpp @@ -26,20 +22,30 @@ set(SRC ${INCROOT}/PrimitiveType.hpp ${INCROOT}/Rect.hpp ${INCROOT}/Rect.inl + ${SRCROOT}/Renderer.cpp + ${INCROOT}/Renderer.hpp ${SRCROOT}/RenderStates.cpp ${INCROOT}/RenderStates.hpp + ${SRCROOT}/RenderTargetImpl.cpp + ${SRCROOT}/RenderTargetImpl.hpp ${SRCROOT}/RenderTexture.cpp ${INCROOT}/RenderTexture.hpp + ${SRCROOT}/RenderTextureImpl.cpp + ${SRCROOT}/RenderTextureImpl.hpp ${SRCROOT}/RenderTarget.cpp ${INCROOT}/RenderTarget.hpp ${SRCROOT}/RenderWindow.cpp ${INCROOT}/RenderWindow.hpp ${SRCROOT}/Shader.cpp ${INCROOT}/Shader.hpp + ${SRCROOT}/ShaderImpl.cpp + ${SRCROOT}/ShaderImpl.hpp + ${SRCROOT}/ShaderImplNull.cpp + ${SRCROOT}/ShaderImplNull.hpp ${SRCROOT}/Texture.cpp ${INCROOT}/Texture.hpp - ${SRCROOT}/TextureSaver.cpp - ${SRCROOT}/TextureSaver.hpp + ${SRCROOT}/TextureImpl.cpp + ${SRCROOT}/TextureImpl.hpp ${SRCROOT}/Transform.cpp ${INCROOT}/Transform.hpp ${SRCROOT}/Transformable.cpp @@ -70,24 +76,43 @@ set(DRAWABLES_SRC ${INCROOT}/VertexArray.hpp ${SRCROOT}/VertexBuffer.cpp ${INCROOT}/VertexBuffer.hpp + ${SRCROOT}/VertexBufferImpl.cpp + ${SRCROOT}/VertexBufferImpl.hpp ) source_group("drawables" FILES ${DRAWABLES_SRC}) -# render-texture sources -set(RENDER_TEXTURE_SRC - ${SRCROOT}/RenderTextureImpl.cpp - ${SRCROOT}/RenderTextureImpl.hpp - ${SRCROOT}/RenderTextureImplFBO.cpp - ${SRCROOT}/RenderTextureImplFBO.hpp - ${SRCROOT}/RenderTextureImplDefault.cpp - ${SRCROOT}/RenderTextureImplDefault.hpp +# OpenGL 1.x sources +set(OPENGL_SRC + ${SRCROOT}/OpenGL/GLCheck.cpp + ${SRCROOT}/OpenGL/GLCheck.hpp + ${SRCROOT}/OpenGL/GLExtensions.hpp + ${SRCROOT}/OpenGL/GLExtensions.cpp ) -source_group("render texture" FILES ${RENDER_TEXTURE_SRC}) +source_group("opengl implementation" FILES ${OPENGL_SRC}) + +# OpenGL 1.x sources +set(OPENGL_1_SRC + ${SRCROOT}/OpenGL/GL1/RenderTargetImplDefault.cpp + ${SRCROOT}/OpenGL/GL1/RenderTargetImplDefault.hpp + ${SRCROOT}/OpenGL/GL1/RenderTextureImplFBO.cpp + ${SRCROOT}/OpenGL/GL1/RenderTextureImplFBO.hpp + ${SRCROOT}/OpenGL/GL1/RenderTextureImplDefault.cpp + ${SRCROOT}/OpenGL/GL1/RenderTextureImplDefault.hpp + ${SRCROOT}/OpenGL/GL1/TextureImplDefault.cpp + ${SRCROOT}/OpenGL/GL1/TextureImplDefault.hpp + ${SRCROOT}/OpenGL/GL1/VertexBufferImplDefault.cpp + ${SRCROOT}/OpenGL/GL1/VertexBufferImplDefault.hpp +) +if(NOT SFML_OPENGL_ES) + list(APPEND OPENGL_1_SRC ${SRCROOT}/OpenGL/GL1/ShaderImplDefault.cpp) + list(APPEND OPENGL_1_SRC ${SRCROOT}/OpenGL/GL1/ShaderImplDefault.hpp) +endif() +source_group("opengl 1.x implementation" FILES ${OPENGL_1_SRC}) # define the sfml-graphics target sfml_add_library(sfml-graphics - SOURCES ${SRC} ${DRAWABLES_SRC} ${RENDER_TEXTURE_SRC} ${STB_SRC}) + SOURCES ${SRC} ${DRAWABLES_SRC} ${OPENGL_1_SRC} ${OPENGL_SRC} ${STB_SRC}) # setup dependencies target_link_libraries(sfml-graphics PUBLIC sfml-window) diff --git a/src/SFML/Graphics/Font.cpp b/src/SFML/Graphics/Font.cpp index 4576987c1..da3c96f3b 100644 --- a/src/SFML/Graphics/Font.cpp +++ b/src/SFML/Graphics/Font.cpp @@ -26,7 +26,8 @@ // Headers //////////////////////////////////////////////////////////// #include -#include +#include +#include #ifdef SFML_SYSTEM_ANDROID #include #endif diff --git a/src/SFML/Graphics/OpenGL/GL1/RenderTargetImplDefault.cpp b/src/SFML/Graphics/OpenGL/GL1/RenderTargetImplDefault.cpp new file mode 100644 index 000000000..f8e7c45e7 --- /dev/null +++ b/src/SFML/Graphics/OpenGL/GL1/RenderTargetImplDefault.cpp @@ -0,0 +1,704 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// GL_QUADS is unavailable on OpenGL ES, thus we need to define GL_QUADS ourselves +#ifdef SFML_OPENGL_ES + + #define GL_QUADS 0 + +#endif // SFML_OPENGL_ES + + +namespace +{ + // Mutex to protect ID generation and our context-RenderTarget-map + sf::Mutex mutex; + + // Unique identifier, used for identifying RenderTargets when + // tracking the currently active RenderTarget within a given context + sf::Uint64 getUniqueId() + { + sf::Lock lock(mutex); + + static sf::Uint64 id = 1; // start at 1, zero is "no RenderTarget" + + return id++; + } + + // Map to help us detect whether a different RenderTarget + // has been activated within a single context + typedef std::map ContextRenderTargetMap; + ContextRenderTargetMap contextRenderTargetMap; + + // Check if a RenderTarget with the given ID is active in the current context + bool isActive(sf::Uint64 id) + { + ContextRenderTargetMap::iterator iter = contextRenderTargetMap.find(sf::Context::getActiveContextId()); + + if ((iter == contextRenderTargetMap.end()) || (iter->second != id)) + return false; + + return true; + } + + // Convert an sf::BlendMode::Factor constant to the corresponding OpenGL constant. + sf::Uint32 factorToGlConstant(sf::BlendMode::Factor blendFactor) + { + switch (blendFactor) + { + case sf::BlendMode::Zero: return GL_ZERO; + case sf::BlendMode::One: return GL_ONE; + case sf::BlendMode::SrcColor: return GL_SRC_COLOR; + case sf::BlendMode::OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR; + case sf::BlendMode::DstColor: return GL_DST_COLOR; + case sf::BlendMode::OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR; + case sf::BlendMode::SrcAlpha: return GL_SRC_ALPHA; + case sf::BlendMode::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; + case sf::BlendMode::DstAlpha: return GL_DST_ALPHA; + case sf::BlendMode::OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA; + } + + sf::err() << "Invalid value for sf::BlendMode::Factor! Fallback to sf::BlendMode::Zero." << std::endl; + assert(false); + return GL_ZERO; + } + + + // Convert an sf::BlendMode::BlendEquation constant to the corresponding OpenGL constant. + sf::Uint32 equationToGlConstant(sf::BlendMode::Equation blendEquation) + { + switch (blendEquation) + { + case sf::BlendMode::Add: return GLEXT_GL_FUNC_ADD; + case sf::BlendMode::Subtract: return GLEXT_GL_FUNC_SUBTRACT; + case sf::BlendMode::ReverseSubtract: return GLEXT_GL_FUNC_REVERSE_SUBTRACT; + } + + sf::err() << "Invalid value for sf::BlendMode::Equation! Fallback to sf::BlendMode::Add." << std::endl; + assert(false); + return GLEXT_GL_FUNC_ADD; + } +} + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +RenderTargetImplDefault::RenderTargetImplDefault(RenderTarget* parent) : +RenderTargetImpl(parent), +m_defaultView (), +m_view (), +m_cache (), +m_id (0) +{ + m_cache.glStatesSet = false; +} + + +//////////////////////////////////////////////////////////// +RenderTargetImplDefault::~RenderTargetImplDefault() +{ +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::clear(const Color& color) +{ + if (isActive(m_id) || getParent()->setActive(true)) + { + // Unbind texture to fix RenderTexture preventing clear + applyTexture(NULL); + + glCheck(glClearColor(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f)); + glCheck(glClear(GL_COLOR_BUFFER_BIT)); + } +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::setView(const View& view) +{ + m_view = view; + m_cache.viewChanged = true; +} + + +//////////////////////////////////////////////////////////// +const View& RenderTargetImplDefault::getView() const +{ + return m_view; +} + + +//////////////////////////////////////////////////////////// +const View& RenderTargetImplDefault::getDefaultView() const +{ + return m_defaultView; +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::draw(const Vertex* vertices, std::size_t vertexCount, + PrimitiveType type, const RenderStates& states) +{ + // Nothing to draw? + if (!vertices || (vertexCount == 0)) + return; + + // GL_QUADS is unavailable on OpenGL ES + #ifdef SFML_OPENGL_ES + if (type == Quads) + { + err() << "sf::Quads primitive type is not supported on OpenGL ES platforms, drawing skipped" << std::endl; + return; + } + #endif + + if (isActive(m_id) || getParent()->setActive(true)) + { + // Check if the vertex count is low enough so that we can pre-transform them + bool useVertexCache = (vertexCount <= StatesCache::VertexCacheSize); + + if (useVertexCache) + { + // Pre-transform the vertices and store them into the vertex cache + for (std::size_t i = 0; i < vertexCount; ++i) + { + Vertex& vertex = m_cache.vertexCache[i]; + vertex.position = states.transform * vertices[i].position; + vertex.color = vertices[i].color; + vertex.texCoords = vertices[i].texCoords; + } + } + + setupDraw(useVertexCache, states); + + // Check if texture coordinates array is needed, and update client state accordingly + bool enableTexCoordsArray = (states.texture || states.shader); + if (!m_cache.enable || (enableTexCoordsArray != m_cache.texCoordsArrayEnabled)) + { + if (enableTexCoordsArray) + glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + else + glCheck(glDisableClientState(GL_TEXTURE_COORD_ARRAY)); + } + + // If we switch between non-cache and cache mode or enable texture + // coordinates we need to set up the pointers to the vertices' components + if (!m_cache.enable || !useVertexCache || !m_cache.useVertexCache) + { + const char* data = reinterpret_cast(vertices); + + // If we pre-transform the vertices, we must use our internal vertex cache + if (useVertexCache) + data = reinterpret_cast(m_cache.vertexCache); + + glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0)); + glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); + if (enableTexCoordsArray) + glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); + } + else if (enableTexCoordsArray && !m_cache.texCoordsArrayEnabled) + { + // If we enter this block, we are already using our internal vertex cache + const char* data = reinterpret_cast(m_cache.vertexCache); + + glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); + } + + drawPrimitives(type, 0, vertexCount); + cleanupDraw(states); + + // Update the cache + m_cache.useVertexCache = useVertexCache; + m_cache.texCoordsArrayEnabled = enableTexCoordsArray; + } +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::draw(const VertexBuffer& vertexBuffer, std::size_t firstVertex, + std::size_t vertexCount, const RenderStates& states) +{ + // VertexBuffer not supported? + if (!VertexBuffer::isAvailable()) + { + err() << "sf::VertexBuffer is not available, drawing skipped" << std::endl; + return; + } + + // Sanity check + if (firstVertex > vertexBuffer.getVertexCount()) + return; + + // Clamp vertexCount to something that makes sense + vertexCount = std::min(vertexCount, vertexBuffer.getVertexCount() - firstVertex); + + // Nothing to draw? + if (!vertexCount || !vertexBuffer.getNativeHandle()) + return; + + // GL_QUADS is unavailable on OpenGL ES + #ifdef SFML_OPENGL_ES + if (vertexBuffer.getPrimitiveType() == Quads) + { + err() << "sf::Quads primitive type is not supported on OpenGL ES platforms, drawing skipped" << std::endl; + return; + } + #endif + + if (isActive(m_id) || getParent()->setActive(true)) + { + setupDraw(false, states); + + // Bind vertex buffer + VertexBuffer::bind(&vertexBuffer); + + // Always enable texture coordinates + if (!m_cache.enable || !m_cache.texCoordsArrayEnabled) + glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + + glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(0))); + glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), reinterpret_cast(8))); + glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(12))); + + drawPrimitives(vertexBuffer.getPrimitiveType(), firstVertex, vertexCount); + + // Unbind vertex buffer + VertexBuffer::bind(NULL); + + cleanupDraw(states); + + // Update the cache + m_cache.useVertexCache = false; + m_cache.texCoordsArrayEnabled = true; + } +} + + +//////////////////////////////////////////////////////////// +bool RenderTargetImplDefault::setActive(bool active) +{ + // Mark this RenderTarget as active or no longer active in the tracking map + { + sf::Lock lock(mutex); + + Uint64 contextId = Context::getActiveContextId(); + + ContextRenderTargetMap::iterator iter = contextRenderTargetMap.find(contextId); + + if (active) + { + if (iter == contextRenderTargetMap.end()) + { + contextRenderTargetMap[contextId] = m_id; + + m_cache.enable = false; + } + else if (iter->second != m_id) + { + iter->second = m_id; + + m_cache.enable = false; + } + } + else + { + if (iter != contextRenderTargetMap.end()) + contextRenderTargetMap.erase(iter); + + m_cache.enable = false; + } + } + + return true; +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::pushGLStates() +{ + if (isActive(m_id) || getParent()->setActive(true)) + { + #ifdef SFML_DEBUG + // make sure that the user didn't leave an unchecked OpenGL error + GLenum error = glGetError(); + if (error != GL_NO_ERROR) + { + err() << "OpenGL error (" << error << ") detected in user code, " + << "you should check for errors with glGetError()" + << std::endl; + } + #endif + + #ifndef SFML_OPENGL_ES + glCheck(glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS)); + glCheck(glPushAttrib(GL_ALL_ATTRIB_BITS)); + #endif + glCheck(glMatrixMode(GL_MODELVIEW)); + glCheck(glPushMatrix()); + glCheck(glMatrixMode(GL_PROJECTION)); + glCheck(glPushMatrix()); + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glPushMatrix()); + } + + resetGLStates(); +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::popGLStates() +{ + if (isActive(m_id) || getParent()->setActive(true)) + { + glCheck(glMatrixMode(GL_PROJECTION)); + glCheck(glPopMatrix()); + glCheck(glMatrixMode(GL_MODELVIEW)); + glCheck(glPopMatrix()); + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glPopMatrix()); + #ifndef SFML_OPENGL_ES + glCheck(glPopClientAttrib()); + glCheck(glPopAttrib()); + #endif + } +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::resetGLStates() +{ + // Check here to make sure a context change does not happen after activate(true) + bool shaderAvailable = Shader::isAvailable(); + bool vertexBufferAvailable = VertexBuffer::isAvailable(); + + // Workaround for states not being properly reset on + // macOS unless a context switch really takes place + #if defined(SFML_SYSTEM_MACOS) + getParent()->setActive(false); + #endif + + if (isActive(m_id) || getParent()->setActive(true)) + { + // Make sure that extensions are initialized + priv::ensureExtensionsInit(); + + // Make sure that the texture unit which is active is the number 0 + if (GLEXT_multitexture) + { + glCheck(GLEXT_glClientActiveTexture(GLEXT_GL_TEXTURE0)); + glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0)); + } + + // Define the default OpenGL states + glCheck(glDisable(GL_CULL_FACE)); + glCheck(glDisable(GL_LIGHTING)); + glCheck(glDisable(GL_DEPTH_TEST)); + glCheck(glDisable(GL_ALPHA_TEST)); + glCheck(glEnable(GL_TEXTURE_2D)); + glCheck(glEnable(GL_BLEND)); + glCheck(glMatrixMode(GL_MODELVIEW)); + glCheck(glLoadIdentity()); + glCheck(glEnableClientState(GL_VERTEX_ARRAY)); + glCheck(glEnableClientState(GL_COLOR_ARRAY)); + glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + m_cache.glStatesSet = true; + + // Apply the default SFML states + applyBlendMode(BlendAlpha); + applyTexture(NULL); + if (shaderAvailable) + applyShader(NULL); + + if (vertexBufferAvailable) + glCheck(VertexBuffer::bind(NULL)); + + m_cache.texCoordsArrayEnabled = true; + + m_cache.useVertexCache = false; + + // Set the default view + setView(getView()); + + m_cache.enable = true; + } +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::initialize(const Vector2u& newSize) +{ + // Setup the default and current views + m_defaultView.reset(FloatRect(0, 0, static_cast(newSize.x), static_cast(newSize.y))); + m_view = m_defaultView; + + // Set GL states only on first draw, so that we don't pollute user's states + m_cache.glStatesSet = false; + + // Generate a unique ID for this RenderTarget to track + // whether it is active within a specific context + m_id = getUniqueId(); +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::applyCurrentView() +{ + // Set the viewport + IntRect viewport = getParent()->getViewport(m_view); + int top = getParent()->getSize().y - (viewport.top + viewport.height); + glCheck(glViewport(viewport.left, top, viewport.width, viewport.height)); + + // Set the projection matrix + glCheck(glMatrixMode(GL_PROJECTION)); + glCheck(glLoadMatrixf(m_view.getTransform().getMatrix())); + + // Go back to model-view mode + glCheck(glMatrixMode(GL_MODELVIEW)); + + m_cache.viewChanged = false; +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::applyBlendMode(const BlendMode& mode) +{ + // Apply the blend mode, falling back to the non-separate versions if necessary + if (GLEXT_blend_func_separate) + { + glCheck(GLEXT_glBlendFuncSeparate( + factorToGlConstant(mode.colorSrcFactor), factorToGlConstant(mode.colorDstFactor), + factorToGlConstant(mode.alphaSrcFactor), factorToGlConstant(mode.alphaDstFactor))); + } + else + { + glCheck(glBlendFunc( + factorToGlConstant(mode.colorSrcFactor), + factorToGlConstant(mode.colorDstFactor))); + } + + if (GLEXT_blend_minmax && GLEXT_blend_subtract) + { + if (GLEXT_blend_equation_separate) + { + glCheck(GLEXT_glBlendEquationSeparate( + equationToGlConstant(mode.colorEquation), + equationToGlConstant(mode.alphaEquation))); + } + else + { + glCheck(GLEXT_glBlendEquation(equationToGlConstant(mode.colorEquation))); + } + } + else if ((mode.colorEquation != BlendMode::Add) || (mode.alphaEquation != BlendMode::Add)) + { + static bool warned = false; + + if (!warned) + { + err() << "OpenGL extension EXT_blend_minmax and/or EXT_blend_subtract unavailable" << std::endl; + err() << "Selecting a blend equation not possible" << std::endl; + err() << "Ensure that hardware acceleration is enabled if available" << std::endl; + + warned = true; + } + } + + m_cache.lastBlendMode = mode; +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::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) + if (transform == Transform::Identity) + glCheck(glLoadIdentity()); + else + glCheck(glLoadMatrixf(transform.getMatrix())); +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::applyTexture(const Texture* texture) +{ + Texture::bind(texture, Texture::Pixels); + + m_cache.lastTextureId = texture ? static_cast(getTextureImpl(*texture))->m_cacheId : 0; +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::applyShader(const Shader* shader) +{ + Shader::bind(shader); +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::setupDraw(bool useVertexCache, const RenderStates& states) +{ + // First set the persistent OpenGL states if it's the very first call + if (!m_cache.glStatesSet) + resetGLStates(); + + if (useVertexCache) + { + // Since vertices are transformed, we must use an identity transform to render them + if (!m_cache.enable || !m_cache.useVertexCache) + glCheck(glLoadIdentity()); + } + else + { + applyTransform(states.transform); + } + + // Apply the view + if (!m_cache.enable || m_cache.viewChanged) + applyCurrentView(); + + // Apply the blend mode + if (!m_cache.enable || (states.blendMode != m_cache.lastBlendMode)) + applyBlendMode(states.blendMode); + + // Apply the texture + if (!m_cache.enable || (states.texture && static_cast(getTextureImpl(*states.texture))->m_fboAttachment)) + { + // If the texture is an FBO attachment, always rebind it + // in order to inform the OpenGL driver that we want changes + // made to it in other contexts to be visible here as well + // This saves us from having to call glFlush() in + // RenderTextureImplFBO which can be quite costly + // See: https://www.khronos.org/opengl/wiki/Memory_Model + applyTexture(states.texture); + } + else + { + Uint64 textureId = states.texture ? static_cast(getTextureImpl(*states.texture))->m_cacheId : 0; + if (textureId != m_cache.lastTextureId) + applyTexture(states.texture); + } + + // Apply the shader + if (states.shader) + applyShader(states.shader); +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::drawPrimitives(PrimitiveType type, std::size_t firstVertex, std::size_t vertexCount) +{ + // 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}; + GLenum mode = modes[type]; + + // Draw the primitives + glCheck(glDrawArrays(mode, static_cast(firstVertex), static_cast(vertexCount))); +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImplDefault::cleanupDraw(const RenderStates& states) +{ + // Unbind the shader, if any + if (states.shader) + applyShader(NULL); + + // If the texture we used to draw belonged to a RenderTexture, then forcibly unbind that texture. + // This prevents a bug where some drivers do not clear RenderTextures properly. + if (states.texture && static_cast(getTextureImpl(*states.texture))->m_fboAttachment) + applyTexture(NULL); + + // Re-enable the cache at the end of the draw if it was disabled + m_cache.enable = true; +} + +} // namespace priv + +} // 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 +// Since it overloads the == operator, we can easily check +// whether any of the 6 blending components changed and, +// thus, whether we need to update the blend mode. +// +// * 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/OpenGL/GL1/RenderTargetImplDefault.hpp b/src/SFML/Graphics/OpenGL/GL1/RenderTargetImplDefault.hpp new file mode 100644 index 000000000..1ef0b7a4c --- /dev/null +++ b/src/SFML/Graphics/OpenGL/GL1/RenderTargetImplDefault.hpp @@ -0,0 +1,271 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_RENDERTARGET_IMPL_DEFAULT_HPP +#define SFML_RENDERTARGET_IMPL_DEFAULT_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace sf +{ +class Drawable; +class VertexBuffer; + +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Base class for all render targets (window, texture, ...) +/// +//////////////////////////////////////////////////////////// +class RenderTargetImplDefault : public RenderTargetImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Constructor + /// + //////////////////////////////////////////////////////////// + RenderTargetImplDefault(RenderTarget* parent); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + virtual ~RenderTargetImplDefault(); + + //////////////////////////////////////////////////////////// + /// \brief Clear the entire target with a single color + /// + /// \param color Fill color to use to clear the render target + /// + //////////////////////////////////////////////////////////// + virtual void clear(const Color& color); + + //////////////////////////////////////////////////////////// + /// \brief Change the current active view + /// + /// \param view New view to use + /// + /// \see getView, getDefaultView + /// + //////////////////////////////////////////////////////////// + virtual void setView(const View& view); + + //////////////////////////////////////////////////////////// + /// \brief Get the view currently in use in the render target + /// + /// \return The view object that is currently used + /// + /// \see setView, getDefaultView + /// + //////////////////////////////////////////////////////////// + virtual const View& getView() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the default view of the render target + /// + /// \return The default view of the render target + /// + /// \see setView, getView + /// + //////////////////////////////////////////////////////////// + virtual const View& getDefaultView() const; + + //////////////////////////////////////////////////////////// + /// \brief Draw primitives defined by an array of vertices + /// + /// \param vertices Pointer to the vertices + /// \param vertexCount Number of vertices in the array + /// \param type Type of primitives to draw + /// \param states Render states to use for drawing + /// + //////////////////////////////////////////////////////////// + virtual void draw(const Vertex* vertices, std::size_t vertexCount, + PrimitiveType type, const RenderStates& states); + + //////////////////////////////////////////////////////////// + /// \brief Draw primitives defined by a vertex buffer + /// + /// \param vertexBuffer Vertex buffer + /// \param firstVertex Index of the first vertex to render + /// \param vertexCount Number of vertices to render + /// \param states Render states to use for drawing + /// + //////////////////////////////////////////////////////////// + virtual void draw(const VertexBuffer& vertexBuffer, std::size_t firstVertex, + std::size_t vertexCount, const RenderStates& states); + + //////////////////////////////////////////////////////////// + /// \brief Activate or deactivate the render target for rendering + /// + /// \param active True to activate, false to deactivate + /// + /// \return True if operation was successful, false otherwise + /// + //////////////////////////////////////////////////////////// + virtual bool setActive(bool active); + + //////////////////////////////////////////////////////////// + /// \brief Save the current OpenGL render states and matrices + /// + /// \see popGLStates + /// + //////////////////////////////////////////////////////////// + virtual void pushGLStates(); + + //////////////////////////////////////////////////////////// + /// \brief Restore the previously saved OpenGL render states and matrices + /// + /// \see pushGLStates + /// + //////////////////////////////////////////////////////////// + virtual void popGLStates(); + + //////////////////////////////////////////////////////////// + /// \brief Reset the internal OpenGL states so that the target is ready for drawing + /// + //////////////////////////////////////////////////////////// + virtual void resetGLStates(); + + //////////////////////////////////////////////////////////// + /// \brief Performs the common initialization step after creation + /// + /// \param newSize New size of the RenderTarget + /// + //////////////////////////////////////////////////////////// + virtual void initialize(const Vector2u& newSize); + +private: + + //////////////////////////////////////////////////////////// + /// \brief Apply the current view + /// + //////////////////////////////////////////////////////////// + void applyCurrentView(); + + //////////////////////////////////////////////////////////// + /// \brief Apply a new blending mode + /// + /// \param mode Blending mode to apply + /// + //////////////////////////////////////////////////////////// + void applyBlendMode(const 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); + + //////////////////////////////////////////////////////////// + /// \brief Setup environment for drawing + /// + /// \param useVertexCache Are we going to use the vertex cache? + /// \param states Render states to use for drawing + /// + //////////////////////////////////////////////////////////// + void setupDraw(bool useVertexCache, const RenderStates& states); + + //////////////////////////////////////////////////////////// + /// \brief Draw the primitives + /// + /// \param type Type of primitives to draw + /// \param firstVertex Index of the first vertex to use when drawing + /// \param vertexCount Number of vertices to use when drawing + /// + //////////////////////////////////////////////////////////// + void drawPrimitives(PrimitiveType type, std::size_t firstVertex, std::size_t vertexCount); + + //////////////////////////////////////////////////////////// + /// \brief Clean up environment after drawing + /// + /// \param states Render states used for drawing + /// + //////////////////////////////////////////////////////////// + void cleanupDraw(const RenderStates& states); + + //////////////////////////////////////////////////////////// + /// \brief Render states cache + /// + //////////////////////////////////////////////////////////// + struct StatesCache + { + enum {VertexCacheSize = 4}; + + bool enable; ///< Is the cache enabled? + bool glStatesSet; ///< Are our internal GL states set yet? + bool viewChanged; ///< Has the current view changed since last draw? + BlendMode lastBlendMode; ///< Cached blending mode + Uint64 lastTextureId; ///< Cached texture + bool texCoordsArrayEnabled; ///< Is GL_TEXTURE_COORD_ARRAY client state enabled? + bool useVertexCache; ///< Did we previously use the vertex cache? + Vertex vertexCache[VertexCacheSize]; ///< Pre-transformed vertices cache + }; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + View m_defaultView; ///< Default view + View m_view; ///< Current view + StatesCache m_cache; ///< Render states cache + Uint64 m_id; ///< Unique number that identifies the RenderTarget +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_RENDERTARGET_IMPL_DEFAULT_HPP diff --git a/src/SFML/Graphics/RenderTextureImplDefault.cpp b/src/SFML/Graphics/OpenGL/GL1/RenderTextureImplDefault.cpp similarity index 85% rename from src/SFML/Graphics/RenderTextureImplDefault.cpp rename to src/SFML/Graphics/OpenGL/GL1/RenderTextureImplDefault.cpp index dabc938ec..813b1ed1a 100644 --- a/src/SFML/Graphics/RenderTextureImplDefault.cpp +++ b/src/SFML/Graphics/OpenGL/GL1/RenderTextureImplDefault.cpp @@ -25,13 +25,32 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include -#include -#include +#include +#include #include #include +namespace +{ + // Automatic wrapper for saving and restoring the current texture binding + struct TextureSaver + { + TextureSaver() + { + glCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding)); + } + + ~TextureSaver() + { + glCheck(glBindTexture(GL_TEXTURE_2D, textureBinding)); + } + + GLint textureBinding; + }; +} + + namespace sf { namespace priv @@ -89,7 +108,7 @@ bool RenderTextureImplDefault::activate(bool active) void RenderTextureImplDefault::updateTexture(unsigned int textureId) { // Make sure that the current texture binding will be preserved - priv::TextureSaver save; + TextureSaver save; // Copy the rendered pixels to the texture glCheck(glBindTexture(GL_TEXTURE_2D, textureId)); diff --git a/src/SFML/Graphics/RenderTextureImplDefault.hpp b/src/SFML/Graphics/OpenGL/GL1/RenderTextureImplDefault.hpp similarity index 100% rename from src/SFML/Graphics/RenderTextureImplDefault.hpp rename to src/SFML/Graphics/OpenGL/GL1/RenderTextureImplDefault.hpp diff --git a/src/SFML/Graphics/RenderTextureImplFBO.cpp b/src/SFML/Graphics/OpenGL/GL1/RenderTextureImplFBO.cpp similarity index 97% rename from src/SFML/Graphics/RenderTextureImplFBO.cpp rename to src/SFML/Graphics/OpenGL/GL1/RenderTextureImplFBO.cpp index c2afc8b04..e763c1371 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.cpp +++ b/src/SFML/Graphics/OpenGL/GL1/RenderTextureImplFBO.cpp @@ -25,9 +25,9 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include +#include #include -#include +#include #include #include #include @@ -208,6 +208,22 @@ void RenderTextureImplFBO::unbind() glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, 0)); } +//////////////////////////////////////////////////////////// +unsigned int RenderTextureImplFBO::getFramebuffer() +{ + unsigned int frameBuffer = 0; + + glCheck(glGetIntegerv(GLEXT_GL_FRAMEBUFFER_BINDING, reinterpret_cast(&frameBuffer))); + + return frameBuffer; +} + +//////////////////////////////////////////////////////////// +void RenderTextureImplFBO::bindFramebuffer(unsigned int frameBuffer) +{ + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer)); +} + //////////////////////////////////////////////////////////// bool RenderTextureImplFBO::create(unsigned int width, unsigned int height, unsigned int textureId, const ContextSettings& settings) diff --git a/src/SFML/Graphics/RenderTextureImplFBO.hpp b/src/SFML/Graphics/OpenGL/GL1/RenderTextureImplFBO.hpp similarity index 92% rename from src/SFML/Graphics/RenderTextureImplFBO.hpp rename to src/SFML/Graphics/OpenGL/GL1/RenderTextureImplFBO.hpp index 4a7839658..ae69e5a16 100644 --- a/src/SFML/Graphics/RenderTextureImplFBO.hpp +++ b/src/SFML/Graphics/OpenGL/GL1/RenderTextureImplFBO.hpp @@ -81,6 +81,18 @@ public: //////////////////////////////////////////////////////////// static void unbind(); + //////////////////////////////////////////////////////////// + /// \brief Get the currently bound frame buffer object + /// + //////////////////////////////////////////////////////////// + static unsigned int getFramebuffer(); + + //////////////////////////////////////////////////////////// + /// \brief Bind a frame buffer object + /// + //////////////////////////////////////////////////////////// + static void bindFramebuffer(unsigned int frameBuffer); + private: //////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/OpenGL/GL1/ShaderImplDefault.cpp b/src/SFML/Graphics/OpenGL/GL1/ShaderImplDefault.cpp new file mode 100644 index 000000000..ec1381804 --- /dev/null +++ b/src/SFML/Graphics/OpenGL/GL1/ShaderImplDefault.cpp @@ -0,0 +1,687 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(SFML_SYSTEM_MACOS) || defined(SFML_SYSTEM_IOS) + + #define castToGlHandle(x) reinterpret_cast(static_cast(x)) + #define castFromGlHandle(x) static_cast(reinterpret_cast(x)) + +#else + + #define castToGlHandle(x) (x) + #define castFromGlHandle(x) (x) + +#endif + +namespace +{ + sf::Mutex maxTextureUnitsMutex; + sf::Mutex isAvailableMutex; + + GLint checkMaxTextureUnits() + { + GLint maxUnits = 0; + glCheck(glGetIntegerv(GLEXT_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxUnits)); + + return maxUnits; + } + + // Retrieve the maximum number of texture units available + GLint getMaxTextureUnits() + { + // TODO: Remove this lock when it becomes unnecessary in C++11 + sf::Lock lock(maxTextureUnitsMutex); + + static GLint maxUnits = checkMaxTextureUnits(); + + return maxUnits; + } + + // Transforms an array of 2D vectors into a contiguous array of scalars + template + std::vector flatten(const sf::Vector2* vectorArray, std::size_t length) + { + const std::size_t vectorSize = 2; + + std::vector contiguous(vectorSize * length); + for (std::size_t i = 0; i < length; ++i) + { + contiguous[vectorSize * i] = vectorArray[i].x; + contiguous[vectorSize * i + 1] = vectorArray[i].y; + } + + return contiguous; + } + + // Transforms an array of 3D vectors into a contiguous array of scalars + template + std::vector flatten(const sf::Vector3* vectorArray, std::size_t length) + { + const std::size_t vectorSize = 3; + + std::vector contiguous(vectorSize * length); + for (std::size_t i = 0; i < length; ++i) + { + contiguous[vectorSize * i] = vectorArray[i].x; + contiguous[vectorSize * i + 1] = vectorArray[i].y; + contiguous[vectorSize * i + 2] = vectorArray[i].z; + } + + return contiguous; + } + + // Transforms an array of 4D vectors into a contiguous array of scalars + template + std::vector flatten(const sf::priv::Vector4* vectorArray, std::size_t length) + { + const std::size_t vectorSize = 4; + + std::vector contiguous(vectorSize * length); + for (std::size_t i = 0; i < length; ++i) + { + contiguous[vectorSize * i] = vectorArray[i].x; + contiguous[vectorSize * i + 1] = vectorArray[i].y; + contiguous[vectorSize * i + 2] = vectorArray[i].z; + contiguous[vectorSize * i + 3] = vectorArray[i].w; + } + + return contiguous; + } +} + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +struct ShaderImplDefault::UniformBinder : private sf::NonCopyable +{ + //////////////////////////////////////////////////////////// + /// \brief Constructor: set up state before uniform is set + /// + //////////////////////////////////////////////////////////// + UniformBinder(ShaderImplDefault& shader, const std::string& name) : + savedProgram(0), + currentProgram(castToGlHandle(shader.m_shaderProgram)), + location(-1) + { + if (currentProgram) + { + // Enable program object + glCheck(savedProgram = GLEXT_glGetHandle(GLEXT_GL_PROGRAM_OBJECT)); + if (currentProgram != savedProgram) + glCheck(GLEXT_glUseProgramObject(currentProgram)); + + // Store uniform location for further use outside constructor + location = shader.getUniformLocation(name); + } + } + + //////////////////////////////////////////////////////////// + /// \brief Destructor: restore state after uniform is set + /// + //////////////////////////////////////////////////////////// + ~UniformBinder() + { + // Disable program object + if (currentProgram && (currentProgram != savedProgram)) + glCheck(GLEXT_glUseProgramObject(savedProgram)); + } + + TransientContextLock lock; ///< Lock to keep context active while uniform is bound + GLEXT_GLhandle savedProgram; ///< Handle to the previously active program object + GLEXT_GLhandle currentProgram; ///< Handle to the program object of the modified sf::Shader instance + GLint location; ///< Uniform location, used by the surrounding sf::Shader code +}; + + +//////////////////////////////////////////////////////////// +ShaderImplDefault::ShaderImplDefault() : +m_shaderProgram (0), +m_currentTexture(-1), +m_textures (), +m_uniforms () +{ +} + + +//////////////////////////////////////////////////////////// +ShaderImplDefault::~ShaderImplDefault() +{ + TransientContextLock lock; + + // Destroy effect program + if (m_shaderProgram) + glCheck(GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram))); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, float x) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform1f(binder.location, x)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, const Glsl::Vec2& v) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform2f(binder.location, v.x, v.y)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, const Glsl::Vec3& v) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform3f(binder.location, v.x, v.y, v.z)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, const Glsl::Vec4& v) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform4f(binder.location, v.x, v.y, v.z, v.w)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, int x) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform1i(binder.location, x)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, const Glsl::Ivec2& v) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform2i(binder.location, v.x, v.y)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, const Glsl::Ivec3& v) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform3i(binder.location, v.x, v.y, v.z)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, const Glsl::Ivec4& v) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform4i(binder.location, v.x, v.y, v.z, v.w)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, const Glsl::Mat3& matrix) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniformMatrix3fv(binder.location, 1, GL_FALSE, matrix.array)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, const Glsl::Mat4& matrix) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniformMatrix4fv(binder.location, 1, GL_FALSE, matrix.array)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, const Texture& texture) +{ + if (m_shaderProgram) + { + TransientContextLock lock; + + // Find the location of the variable in the shader + int location = getUniformLocation(name); + if (location != -1) + { + // Store the location -> texture mapping + TextureTable::iterator it = m_textures.find(location); + if (it == m_textures.end()) + { + // New entry, make sure there are enough texture units + GLint maxUnits = getMaxTextureUnits(); + if (m_textures.size() + 1 >= static_cast(maxUnits)) + { + err() << "Impossible to use texture \"" << name << "\" for shader: all available texture units are used" << std::endl; + return; + } + + m_textures[location] = &texture; + } + else + { + // Location already used, just replace the texture + it->second = &texture; + } + } + } +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniform(const std::string& name, Shader::CurrentTextureType) +{ + if (m_shaderProgram) + { + TransientContextLock lock; + + // Find the location of the variable in the shader + m_currentTexture = getUniformLocation(name); + } +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniformArray(const std::string& name, const float* scalarArray, std::size_t length) +{ + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform1fv(binder.location, static_cast(length), scalarArray)); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniformArray(const std::string& name, const Glsl::Vec2* vectorArray, std::size_t length) +{ + std::vector contiguous = flatten(vectorArray, length); + + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform2fv(binder.location, static_cast(length), &contiguous[0])); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniformArray(const std::string& name, const Glsl::Vec3* vectorArray, std::size_t length) +{ + std::vector contiguous = flatten(vectorArray, length); + + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform3fv(binder.location, static_cast(length), &contiguous[0])); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniformArray(const std::string& name, const Glsl::Vec4* vectorArray, std::size_t length) +{ + std::vector contiguous = flatten(vectorArray, length); + + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniform4fv(binder.location, static_cast(length), &contiguous[0])); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniformArray(const std::string& name, const Glsl::Mat3* matrixArray, std::size_t length) +{ + const std::size_t matrixSize = 3 * 3; + + std::vector contiguous(matrixSize * length); + for (std::size_t i = 0; i < length; ++i) + priv::copyMatrix(matrixArray[i].array, matrixSize, &contiguous[matrixSize * i]); + + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniformMatrix3fv(binder.location, static_cast(length), GL_FALSE, &contiguous[0])); +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::setUniformArray(const std::string& name, const Glsl::Mat4* matrixArray, std::size_t length) +{ + const std::size_t matrixSize = 4 * 4; + + std::vector contiguous(matrixSize * length); + for (std::size_t i = 0; i < length; ++i) + priv::copyMatrix(matrixArray[i].array, matrixSize, &contiguous[matrixSize * i]); + + UniformBinder binder(*this, name); + if (binder.location != -1) + glCheck(GLEXT_glUniformMatrix4fv(binder.location, static_cast(length), GL_FALSE, &contiguous[0])); +} + + +//////////////////////////////////////////////////////////// +unsigned int ShaderImplDefault::getNativeHandle() const +{ + return m_shaderProgram; +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::bind(const ShaderImplDefault* shader) +{ + TransientContextLock lock; + + // Make sure that we can use shaders + if (!isAvailable()) + { + err() << "Failed to bind or unbind shader: your system doesn't support shaders " + << "(you should test ShaderImplDefault::isAvailable() before trying to use the Shader class)" << std::endl; + return; + } + + if (shader && shader->m_shaderProgram) + { + // Enable the program + glCheck(GLEXT_glUseProgramObject(castToGlHandle(shader->m_shaderProgram))); + + // Bind the textures + shader->bindTextures(); + + // Bind the current texture + if (shader->m_currentTexture != -1) + glCheck(GLEXT_glUniform1i(shader->m_currentTexture, 0)); + } + else + { + // Bind no shader + glCheck(GLEXT_glUseProgramObject(0)); + } +} + + +//////////////////////////////////////////////////////////// +bool ShaderImplDefault::isAvailable() +{ + Lock lock(isAvailableMutex); + + static bool checked = false; + static bool available = false; + + if (!checked) + { + checked = true; + + TransientContextLock contextLock; + + // Make sure that extensions are initialized + sf::priv::ensureExtensionsInit(); + + available = GLEXT_multitexture && + GLEXT_shading_language_100 && + GLEXT_shader_objects && + GLEXT_vertex_shader && + GLEXT_fragment_shader; + } + + return available; +} + + +//////////////////////////////////////////////////////////// +bool ShaderImplDefault::isGeometryAvailable() +{ + Lock lock(isAvailableMutex); + + static bool checked = false; + static bool available = false; + + if (!checked) + { + checked = true; + + TransientContextLock contextLock; + + // Make sure that extensions are initialized + sf::priv::ensureExtensionsInit(); + + available = isAvailable() && GLEXT_geometry_shader4; + } + + return available; +} + + +//////////////////////////////////////////////////////////// +bool ShaderImplDefault::compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode) +{ + TransientContextLock lock; + + // First make sure that we can use shaders + if (!isAvailable()) + { + err() << "Failed to create a shader: your system doesn't support shaders " + << "(you should test ShaderImplDefault::isAvailable() before trying to use the Shader class)" << std::endl; + return false; + } + + // Make sure we can use geometry shaders + if (geometryShaderCode && !isGeometryAvailable()) + { + err() << "Failed to create a shader: your system doesn't support geometry shaders " + << "(you should test ShaderImplDefault::isGeometryAvailable() before trying to use geometry shaders)" << std::endl; + return false; + } + + // Destroy the shader if it was already created + if (m_shaderProgram) + { + glCheck(GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram))); + m_shaderProgram = 0; + } + + // Reset the internal state + m_currentTexture = -1; + m_textures.clear(); + m_uniforms.clear(); + + // Create the program + GLEXT_GLhandle shaderProgram; + glCheck(shaderProgram = GLEXT_glCreateProgramObject()); + + // Create the vertex shader if needed + if (vertexShaderCode) + { + // Create and compile the shader + GLEXT_GLhandle vertexShader; + glCheck(vertexShader = GLEXT_glCreateShaderObject(GLEXT_GL_VERTEX_SHADER)); + glCheck(GLEXT_glShaderSource(vertexShader, 1, &vertexShaderCode, NULL)); + glCheck(GLEXT_glCompileShader(vertexShader)); + + // Check the compile log + GLint success; + glCheck(GLEXT_glGetObjectParameteriv(vertexShader, GLEXT_GL_OBJECT_COMPILE_STATUS, &success)); + if (success == GL_FALSE) + { + char log[1024]; + glCheck(GLEXT_glGetInfoLog(vertexShader, sizeof(log), 0, log)); + err() << "Failed to compile vertex shader:" << std::endl + << log << std::endl; + glCheck(GLEXT_glDeleteObject(vertexShader)); + glCheck(GLEXT_glDeleteObject(shaderProgram)); + return false; + } + + // Attach the shader to the program, and delete it (not needed anymore) + glCheck(GLEXT_glAttachObject(shaderProgram, vertexShader)); + glCheck(GLEXT_glDeleteObject(vertexShader)); + } + + // Create the geometry shader if needed + if (geometryShaderCode) + { + // Create and compile the shader + GLEXT_GLhandle geometryShader = GLEXT_glCreateShaderObject(GLEXT_GL_GEOMETRY_SHADER); + glCheck(GLEXT_glShaderSource(geometryShader, 1, &geometryShaderCode, NULL)); + glCheck(GLEXT_glCompileShader(geometryShader)); + + // Check the compile log + GLint success; + glCheck(GLEXT_glGetObjectParameteriv(geometryShader, GLEXT_GL_OBJECT_COMPILE_STATUS, &success)); + if (success == GL_FALSE) + { + char log[1024]; + glCheck(GLEXT_glGetInfoLog(geometryShader, sizeof(log), 0, log)); + err() << "Failed to compile geometry shader:" << std::endl + << log << std::endl; + glCheck(GLEXT_glDeleteObject(geometryShader)); + glCheck(GLEXT_glDeleteObject(shaderProgram)); + return false; + } + + // Attach the shader to the program, and delete it (not needed anymore) + glCheck(GLEXT_glAttachObject(shaderProgram, geometryShader)); + glCheck(GLEXT_glDeleteObject(geometryShader)); + } + + // Create the fragment shader if needed + if (fragmentShaderCode) + { + // Create and compile the shader + GLEXT_GLhandle fragmentShader; + glCheck(fragmentShader = GLEXT_glCreateShaderObject(GLEXT_GL_FRAGMENT_SHADER)); + glCheck(GLEXT_glShaderSource(fragmentShader, 1, &fragmentShaderCode, NULL)); + glCheck(GLEXT_glCompileShader(fragmentShader)); + + // Check the compile log + GLint success; + glCheck(GLEXT_glGetObjectParameteriv(fragmentShader, GLEXT_GL_OBJECT_COMPILE_STATUS, &success)); + if (success == GL_FALSE) + { + char log[1024]; + glCheck(GLEXT_glGetInfoLog(fragmentShader, sizeof(log), 0, log)); + err() << "Failed to compile fragment shader:" << std::endl + << log << std::endl; + glCheck(GLEXT_glDeleteObject(fragmentShader)); + glCheck(GLEXT_glDeleteObject(shaderProgram)); + return false; + } + + // Attach the shader to the program, and delete it (not needed anymore) + glCheck(GLEXT_glAttachObject(shaderProgram, fragmentShader)); + glCheck(GLEXT_glDeleteObject(fragmentShader)); + } + + // Link the program + glCheck(GLEXT_glLinkProgram(shaderProgram)); + + // Check the link log + GLint success; + glCheck(GLEXT_glGetObjectParameteriv(shaderProgram, GLEXT_GL_OBJECT_LINK_STATUS, &success)); + if (success == GL_FALSE) + { + char log[1024]; + glCheck(GLEXT_glGetInfoLog(shaderProgram, sizeof(log), 0, log)); + err() << "Failed to link shader:" << std::endl + << log << std::endl; + glCheck(GLEXT_glDeleteObject(shaderProgram)); + return false; + } + + m_shaderProgram = castFromGlHandle(shaderProgram); + + // Force an OpenGL flush, so that the shader will appear updated + // in all contexts immediately (solves problems in multi-threaded apps) + glCheck(glFlush()); + + return true; +} + + +//////////////////////////////////////////////////////////// +void ShaderImplDefault::bindTextures() const +{ + TextureTable::const_iterator it = m_textures.begin(); + for (std::size_t i = 0; i < m_textures.size(); ++i) + { + GLint index = static_cast(i + 1); + glCheck(GLEXT_glUniform1i(it->first, index)); + glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0 + index)); + Texture::bind(it->second); + ++it; + } + + // Make sure that the texture unit which is left active is the number 0 + glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0)); +} + + +//////////////////////////////////////////////////////////// +int ShaderImplDefault::getUniformLocation(const std::string& name) +{ + // Check the cache + UniformTable::const_iterator it = m_uniforms.find(name); + if (it != m_uniforms.end()) + { + // Already in cache, return it + return it->second; + } + else + { + // Not in cache, request the location from OpenGL + int location = GLEXT_glGetUniformLocation(castToGlHandle(m_shaderProgram), name.c_str()); + m_uniforms.insert(std::make_pair(name, location)); + + if (location == -1) + err() << "Uniform \"" << name << "\" not found in shader" << std::endl; + + return location; + } +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Graphics/OpenGL/GL1/ShaderImplDefault.hpp b/src/SFML/Graphics/OpenGL/GL1/ShaderImplDefault.hpp new file mode 100644 index 000000000..4f6dcf6f6 --- /dev/null +++ b/src/SFML/Graphics/OpenGL/GL1/ShaderImplDefault.hpp @@ -0,0 +1,327 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_SHADER_IMPL_DEFAULT_HPP +#define SFML_SHADER_IMPL_DEFAULT_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include + + +namespace sf +{ +class Texture; + +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Default specialization of ShaderImpl +/// +//////////////////////////////////////////////////////////// +class ShaderImplDefault : public ShaderImpl, GlResource +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + //////////////////////////////////////////////////////////// + ShaderImplDefault(); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~ShaderImplDefault(); + + //////////////////////////////////////////////////////////// + /// \brief Bind a shader for rendering + /// + /// \param shader Shader to bind, can be null to use no shader + /// + //////////////////////////////////////////////////////////// + static void bind(const ShaderImplDefault* shader); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports shaders + /// + /// \return True if shaders are supported, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isAvailable(); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports geometry shaders + /// + /// \return True if geometry shaders are supported, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isGeometryAvailable(); + +private: + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p float uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param x Value of the float scalar + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, float x); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p vec2 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the vec2 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Vec2& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p vec3 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the vec3 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Vec3& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p vec4 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the vec4 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Vec4& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p int uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param x Value of the int scalar + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, int x); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p ivec2 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the ivec2 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Ivec2& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p ivec3 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the ivec3 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Ivec3& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p ivec4 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the ivec4 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Ivec4& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p mat3 matrix + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrix Value of the mat3 matrix + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Mat3& matrix); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p mat4 matrix + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrix Value of the mat4 matrix + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Mat4& matrix); + + //////////////////////////////////////////////////////////// + /// \brief Specify a texture as \p sampler2D uniform + /// + /// \param name Name of the texture in the shader + /// \param texture Texture to assign + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Texture& texture); + + //////////////////////////////////////////////////////////// + /// \brief Specify current texture as \p sampler2D uniform + /// + /// \param name Name of the texture in the shader + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, Shader::CurrentTextureType); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p float[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param scalarArray pointer to array of \p float values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const float* scalarArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p vec2[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vectorArray pointer to array of \p vec2 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Vec2* vectorArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p vec3[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vectorArray pointer to array of \p vec3 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Vec3* vectorArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p vec4[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vectorArray pointer to array of \p vec4 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Vec4* vectorArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p mat3[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrixArray pointer to array of \p mat3 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Mat3* matrixArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p mat4[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrixArray pointer to array of \p mat4 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Mat4* matrixArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Get the underlying OpenGL handle of the shader. + /// + /// \return OpenGL handle of the shader or 0 if not yet loaded + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getNativeHandle() const; + + //////////////////////////////////////////////////////////// + /// \brief Compile the shader(s) and create the program + /// + /// \param vertexShaderCode Source code of the vertex shader + /// \param geometryShaderCode Source code of the geometry shader + /// \param fragmentShaderCode Source code of the fragment shader + /// + /// \return True on success, false if any error happened + /// + //////////////////////////////////////////////////////////// + virtual bool compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode); + + //////////////////////////////////////////////////////////// + /// \brief Bind all the textures used by the shader + /// + /// This function each texture to a different unit, and + /// updates the corresponding variables in the shader accordingly. + /// + //////////////////////////////////////////////////////////// + void bindTextures() const; + + //////////////////////////////////////////////////////////// + /// \brief Get the location ID of a shader uniform + /// + /// \param name Name of the uniform variable to search + /// + /// \return Location ID of the uniform, or -1 if not found + /// + //////////////////////////////////////////////////////////// + int getUniformLocation(const std::string& name); + + //////////////////////////////////////////////////////////// + /// \brief RAII object to save and restore the program + /// binding while uniforms are being set + /// + /// Implementation is private in the .cpp file. + /// + //////////////////////////////////////////////////////////// + struct UniformBinder; + + //////////////////////////////////////////////////////////// + // Types + //////////////////////////////////////////////////////////// + typedef std::map TextureTable; + typedef std::map UniformTable; + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + unsigned int m_shaderProgram; ///< OpenGL identifier for the program + int m_currentTexture; ///< Location of the current texture in the shader + TextureTable m_textures; ///< Texture variables in the shader, mapped to their location + UniformTable m_uniforms; ///< Parameters location cache +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_SHADER_IMPL_DEFAULT_HPP diff --git a/src/SFML/Graphics/OpenGL/GL1/TextureImplDefault.cpp b/src/SFML/Graphics/OpenGL/GL1/TextureImplDefault.cpp new file mode 100644 index 000000000..eecd11360 --- /dev/null +++ b/src/SFML/Graphics/OpenGL/GL1/TextureImplDefault.cpp @@ -0,0 +1,796 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace +{ + sf::Mutex idMutex; + sf::Mutex maximumSizeMutex; + + // Thread-safe unique identifier generator, + // is used for states cache (see RenderTarget) + sf::Uint64 getUniqueId() + { + sf::Lock lock(idMutex); + + static sf::Uint64 id = 1; // start at 1, zero is "no texture" + + return id++; + } + + // Automatic wrapper for saving and restoring the current texture binding + struct TextureSaver + { + TextureSaver() + { + glCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding)); + } + + ~TextureSaver() + { + glCheck(glBindTexture(GL_TEXTURE_2D, textureBinding)); + } + + GLint textureBinding; + }; +} + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +TextureImplDefault::TextureImplDefault() : +m_size (0, 0), +m_actualSize (0, 0), +m_texture (0), +m_isSmooth (false), +m_sRgb (false), +m_isRepeated (false), +m_pixelsFlipped(false), +m_fboAttachment(false), +m_hasMipmap (false), +m_cacheId (getUniqueId()) +{ +} + + +//////////////////////////////////////////////////////////// +TextureImplDefault::TextureImplDefault(const TextureImplDefault& copy) : +m_size (0, 0), +m_actualSize (0, 0), +m_texture (0), +m_isSmooth (copy.m_isSmooth), +m_sRgb (copy.m_sRgb), +m_isRepeated (copy.m_isRepeated), +m_pixelsFlipped(false), +m_fboAttachment(false), +m_hasMipmap (false), +m_cacheId (getUniqueId()) +{ + if (copy.m_texture) + { + if (create(copy.getSize().x, copy.getSize().y)) + { + update(copy, 0, 0); + } + else + { + err() << "Failed to copy texture, failed to create new texture" << std::endl; + } + } +} + + +//////////////////////////////////////////////////////////// +TextureImplDefault::~TextureImplDefault() +{ + // Destroy the OpenGL texture + if (m_texture) + { + TransientContextLock lock; + + GLuint texture = static_cast(m_texture); + glCheck(glDeleteTextures(1, &texture)); + } +} + + +//////////////////////////////////////////////////////////// +bool TextureImplDefault::create(unsigned int width, unsigned int height) +{ + // Check if texture parameters are valid before creating it + if ((width == 0) || (height == 0)) + { + err() << "Failed to create texture, invalid size (" << width << "x" << height << ")" << std::endl; + return false; + } + + TransientContextLock lock; + + // Make sure that extensions are initialized + priv::ensureExtensionsInit(); + + // Compute the internal texture dimensions depending on NPOT textures support + Vector2u actualSize(getValidSize(width), getValidSize(height)); + + // Check the maximum texture size + unsigned int maxSize = getMaximumSize(); + if ((actualSize.x > maxSize) || (actualSize.y > maxSize)) + { + err() << "Failed to create texture, its internal size is too high " + << "(" << actualSize.x << "x" << actualSize.y << ", " + << "maximum is " << maxSize << "x" << maxSize << ")" + << std::endl; + return false; + } + + // All the validity checks passed, we can store the new texture settings + m_size.x = width; + m_size.y = height; + m_actualSize = actualSize; + m_pixelsFlipped = false; + m_fboAttachment = false; + + // Create the OpenGL texture if it doesn't exist yet + if (!m_texture) + { + GLuint texture; + glCheck(glGenTextures(1, &texture)); + m_texture = static_cast(texture); + } + + // Make sure that the current texture binding will be preserved + TextureSaver save; + + static bool textureEdgeClamp = GLEXT_texture_edge_clamp; + + if (!m_isRepeated && !textureEdgeClamp) + { + static bool warned = false; + + if (!warned) + { + err() << "OpenGL extension SGIS_texture_edge_clamp unavailable" << std::endl; + err() << "Artifacts may occur along texture edges" << std::endl; + err() << "Ensure that hardware acceleration is enabled if available" << std::endl; + + warned = true; + } + } + + static bool textureSrgb = GLEXT_texture_sRGB; + + if (m_sRgb && !textureSrgb) + { + static bool warned = false; + + if (!warned) + { +#ifndef SFML_OPENGL_ES + err() << "OpenGL extension EXT_texture_sRGB unavailable" << std::endl; +#else + err() << "OpenGL ES extension EXT_sRGB unavailable" << std::endl; +#endif + err() << "Automatic sRGB to linear conversion disabled" << std::endl; + + warned = true; + } + + m_sRgb = false; + } + + // Initialize the texture + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(glTexImage2D(GL_TEXTURE_2D, 0, (m_sRgb ? GLEXT_GL_SRGB8_ALPHA8 : GL_RGBA), m_actualSize.x, m_actualSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_isRepeated ? GL_REPEAT : (textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP))); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_isRepeated ? GL_REPEAT : (textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP))); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); + m_cacheId = getUniqueId(); + + m_hasMipmap = false; + + return true; +} + + +//////////////////////////////////////////////////////////// +bool TextureImplDefault::loadFromImage(const Image& image, const IntRect& area) +{ + // Retrieve the image size + int width = static_cast(image.getSize().x); + int height = static_cast(image.getSize().y); + + // Load the entire image if the source area is either empty or contains the whole image + if (area.width == 0 || (area.height == 0) || + ((area.left <= 0) && (area.top <= 0) && (area.width >= width) && (area.height >= height))) + { + // Load the entire image + if (create(image.getSize().x, image.getSize().y)) + { + update(image.getPixelsPtr(), image.getSize().x, image.getSize().y, 0, 0); + + return true; + } + else + { + return false; + } + } + else + { + // Load a sub-area of the image + + // Adjust the rectangle to the size of the image + IntRect rectangle = area; + if (rectangle.left < 0) rectangle.left = 0; + if (rectangle.top < 0) rectangle.top = 0; + if (rectangle.left + rectangle.width > width) rectangle.width = width - rectangle.left; + if (rectangle.top + rectangle.height > height) rectangle.height = height - rectangle.top; + + // Create the texture and upload the pixels + if (create(rectangle.width, rectangle.height)) + { + TransientContextLock lock; + + // Make sure that the current texture binding will be preserved + 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, m_texture)); + for (int i = 0; i < rectangle.height; ++i) + { + glCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i, rectangle.width, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); + pixels += 4 * width; + } + + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); + m_hasMipmap = false; + + // Force an OpenGL flush, so that the texture will appear updated + // in all contexts immediately (solves problems in multi-threaded apps) + glCheck(glFlush()); + + return true; + } + else + { + return false; + } + } +} + + +//////////////////////////////////////////////////////////// +Vector2u TextureImplDefault::getSize() const +{ + return m_size; +} + + +//////////////////////////////////////////////////////////// +Image TextureImplDefault::copyToImage() const +{ + // Easy case: empty texture + if (!m_texture) + return Image(); + + TransientContextLock lock; + + // Make sure that the current texture binding will be preserved + TextureSaver save; + + // Create an array of pixels + std::vector pixels(m_size.x * m_size.y * 4); + +#ifdef SFML_OPENGL_ES + + // OpenGL ES doesn't have the glGetTexImage function, the only way to read + // from a texture is to bind it to a FBO and use glReadPixels + GLuint frameBuffer = 0; + glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer)); + if (frameBuffer) + { + GLint previousFrameBuffer; + glCheck(glGetIntegerv(GLEXT_GL_FRAMEBUFFER_BINDING, &previousFrameBuffer)); + + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer)); + glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0)); + glCheck(glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0])); + glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer)); + + glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, previousFrameBuffer)); + } + +#else + + if ((m_size == m_actualSize) && !m_pixelsFlipped) + { + // Texture is not padded nor flipped, we can use a direct copy + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0])); + } + else + { + // Texture is either padded or flipped, we have to use a slower algorithm + + // All the pixels will first be copied to a temporary array + std::vector allPixels(m_actualSize.x * m_actualSize.y * 4); + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &allPixels[0])); + + // Then we copy the useful pixels from the temporary array to the final one + const Uint8* src = &allPixels[0]; + Uint8* dst = &pixels[0]; + int srcPitch = m_actualSize.x * 4; + int dstPitch = m_size.x * 4; + + // Handle the case where source pixels are flipped vertically + if (m_pixelsFlipped) + { + src += srcPitch * (m_size.y - 1); + srcPitch = -srcPitch; + } + + for (unsigned int i = 0; i < m_size.y; ++i) + { + std::memcpy(dst, src, dstPitch); + src += srcPitch; + dst += dstPitch; + } + } + +#endif // SFML_OPENGL_ES + + // Create the image + Image image; + image.create(m_size.x, m_size.y, &pixels[0]); + + return image; +} + + +//////////////////////////////////////////////////////////// +void TextureImplDefault::update(const Uint8* pixels, unsigned int width, unsigned int height, unsigned int x, unsigned int y) +{ + assert(x + width <= m_size.x); + assert(y + height <= m_size.y); + + if (pixels && m_texture) + { + TransientContextLock lock; + + // Make sure that the current texture binding will be preserved + TextureSaver save; + + // Copy pixels from the given array to the texture + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); + m_hasMipmap = false; + m_pixelsFlipped = false; + m_cacheId = getUniqueId(); + + // Force an OpenGL flush, so that the texture data will appear updated + // in all contexts immediately (solves problems in multi-threaded apps) + glCheck(glFlush()); + } +} + + +//////////////////////////////////////////////////////////// +void TextureImplDefault::update(const TextureImpl& texture, unsigned int x, unsigned int y) +{ + const TextureImplDefault& texture_ = static_cast(texture); + + assert(x + texture_.m_size.x <= m_size.x); + assert(y + texture_.m_size.y <= 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, texture_.m_pixelsFlipped ? texture_.m_size.y : 0, texture_.m_size.x, texture_.m_pixelsFlipped ? 0 : texture_.m_size.y, // Source rectangle, flip y if source is flipped + x, y, x + texture_.m_size.x, y + texture_.m_size.y, // Destination rectangle + 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)); + + // Make sure that the current texture binding will be preserved + TextureSaver save; + + // Set the parameters of this texture + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); + m_hasMipmap = false; + m_pixelsFlipped = false; + m_cacheId = getUniqueId(); + + // Force an OpenGL flush, so that the texture data will appear updated + // in all contexts immediately (solves problems in multi-threaded apps) + glCheck(glFlush()); + + return; + } + +#endif // SFML_OPENGL_ES + + update(texture_.copyToImage().getPixelsPtr(), x, y, 0, 0); +} + + +//////////////////////////////////////////////////////////// +void TextureImplDefault::update(const Window& window, unsigned int x, unsigned int y) +{ + assert(x + window.getSize().x <= m_size.x); + assert(y + window.getSize().y <= m_size.y); + + if (m_texture && window.setActive(true)) + { + TransientContextLock lock; + + // Make sure that the current texture binding will be preserved + TextureSaver save; + + // Copy pixels from the back-buffer to the texture + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 0, 0, window.getSize().x, window.getSize().y)); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); + m_hasMipmap = false; + m_pixelsFlipped = true; + m_cacheId = getUniqueId(); + + // Force an OpenGL flush, so that the texture will appear updated + // in all contexts immediately (solves problems in multi-threaded apps) + glCheck(glFlush()); + } +} + + +//////////////////////////////////////////////////////////// +void TextureImplDefault::setSmooth(bool smooth) +{ + if (smooth != m_isSmooth) + { + m_isSmooth = smooth; + + if (m_texture) + { + TransientContextLock lock; + + // Make sure that the current texture binding will be preserved + TextureSaver save; + + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); + + if (m_hasMipmap) + { + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR)); + } + else + { + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); + } + } + } +} + + +//////////////////////////////////////////////////////////// +bool TextureImplDefault::isSmooth() const +{ + return m_isSmooth; +} + + +//////////////////////////////////////////////////////////// +void TextureImplDefault::setSrgb(bool sRgb) +{ + m_sRgb = sRgb; +} + + +//////////////////////////////////////////////////////////// +bool TextureImplDefault::isSrgb() const +{ + return m_sRgb; +} + + +//////////////////////////////////////////////////////////// +void TextureImplDefault::setRepeated(bool repeated) +{ + if (repeated != m_isRepeated) + { + m_isRepeated = repeated; + + if (m_texture) + { + TransientContextLock lock; + + // Make sure that the current texture binding will be preserved + TextureSaver save; + + static bool textureEdgeClamp = GLEXT_texture_edge_clamp; + + if (!m_isRepeated && !textureEdgeClamp) + { + static bool warned = false; + + if (!warned) + { + err() << "OpenGL extension SGIS_texture_edge_clamp unavailable" << std::endl; + err() << "Artifacts may occur along texture edges" << std::endl; + err() << "Ensure that hardware acceleration is enabled if available" << std::endl; + + warned = true; + } + } + + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_isRepeated ? GL_REPEAT : (textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP))); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_isRepeated ? GL_REPEAT : (textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP))); + } + } +} + + +//////////////////////////////////////////////////////////// +bool TextureImplDefault::isRepeated() const +{ + return m_isRepeated; +} + + +//////////////////////////////////////////////////////////// +bool TextureImplDefault::generateMipmap() +{ + if (!m_texture) + return false; + + TransientContextLock lock; + + // Make sure that extensions are initialized + priv::ensureExtensionsInit(); + + if (!GLEXT_framebuffer_object) + return false; + + // Make sure that the current texture binding will be preserved + TextureSaver save; + + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(GLEXT_glGenerateMipmap(GL_TEXTURE_2D)); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR)); + + m_hasMipmap = true; + + return true; +} + + +//////////////////////////////////////////////////////////// +void TextureImplDefault::invalidateMipmap() +{ + if (!m_hasMipmap) + return; + + TransientContextLock lock; + + // Make sure that the current texture binding will be preserved + TextureSaver save; + + glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); + glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); + + m_hasMipmap = false; +} + + +//////////////////////////////////////////////////////////// +void TextureImplDefault::bind(const TextureImplDefault* texture, Texture::CoordinateType coordinateType) +{ + TransientContextLock lock; + + if (texture && texture->m_texture) + { + // Bind the texture + glCheck(glBindTexture(GL_TEXTURE_2D, texture->m_texture)); + + // Check if we need to define a special texture matrix + if ((coordinateType == Texture::Pixels) || texture->m_pixelsFlipped) + { + GLfloat matrix[16] = {1.f, 0.f, 0.f, 0.f, + 0.f, 1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, 0.f, 0.f, 1.f}; + + // If non-normalized coordinates (= pixels) are requested, we need to + // setup scale factors that convert the range [0 .. size] to [0 .. 1] + if (coordinateType == Texture::Pixels) + { + matrix[0] = 1.f / texture->m_actualSize.x; + matrix[5] = 1.f / texture->m_actualSize.y; + } + + // If pixels are flipped we must invert the Y axis + if (texture->m_pixelsFlipped) + { + matrix[5] = -matrix[5]; + matrix[13] = static_cast(texture->m_size.y) / texture->m_actualSize.y; + } + + // 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)); + } + } + else + { + // Bind no texture + glCheck(glBindTexture(GL_TEXTURE_2D, 0)); + + // Reset the texture matrix + glCheck(glMatrixMode(GL_TEXTURE)); + glCheck(glLoadIdentity()); + + // Go back to model-view mode (sf::RenderTarget relies on it) + glCheck(glMatrixMode(GL_MODELVIEW)); + } +} + + +//////////////////////////////////////////////////////////// +unsigned int TextureImplDefault::getMaximumSize() +{ + Lock lock(maximumSizeMutex); + + static bool checked = false; + static GLint size = 0; + + if (!checked) + { + checked = true; + + TransientContextLock lock; + + glCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size)); + } + + return static_cast(size); +} + + +//////////////////////////////////////////////////////////// +unsigned int TextureImplDefault::getNativeHandle() const +{ + return m_texture; +} + + +//////////////////////////////////////////////////////////// +unsigned int TextureImplDefault::getValidSize(unsigned int size) +{ + if (GLEXT_texture_non_power_of_two) + { + // If hardware supports NPOT textures, then just return the unmodified size + return size; + } + else + { + // If hardware doesn't support NPOT textures, we calculate the nearest power of two + unsigned int powerOfTwo = 1; + while (powerOfTwo < size) + powerOfTwo *= 2; + + return powerOfTwo; + } +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Graphics/OpenGL/GL1/TextureImplDefault.hpp b/src/SFML/Graphics/OpenGL/GL1/TextureImplDefault.hpp new file mode 100644 index 000000000..15cec5424 --- /dev/null +++ b/src/SFML/Graphics/OpenGL/GL1/TextureImplDefault.hpp @@ -0,0 +1,297 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_TEXTURE_IMPL_DEFAULT_HPP +#define SFML_TEXTURE_IMPL_DEFAULT_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include + + +namespace sf +{ +class InputStream; +class RenderTarget; +class RenderTexture; +class Text; +class Window; + +namespace priv +{ +class RenderTargetImplDefault; + +//////////////////////////////////////////////////////////// +/// \brief Image living on the graphics card that can be used for drawing +/// +//////////////////////////////////////////////////////////// +class TextureImplDefault : public TextureImpl, private GlResource +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an empty texture. + /// + //////////////////////////////////////////////////////////// + TextureImplDefault(); + + //////////////////////////////////////////////////////////// + /// \brief Copy constructor + /// + /// \param copy instance to copy + /// + //////////////////////////////////////////////////////////// + TextureImplDefault(const TextureImplDefault& copy); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~TextureImplDefault(); + + //////////////////////////////////////////////////////////// + /// \brief Bind a texture for rendering + /// + /// \param texture Pointer to the texture to bind, can be null to use no texture + /// \param coordinateType Type of texture coordinates to use + /// + //////////////////////////////////////////////////////////// + static void bind(const TextureImplDefault* texture, Texture::CoordinateType coordinateType = Texture::Normalized); + + //////////////////////////////////////////////////////////// + /// \brief Get the maximum texture size allowed + /// + /// \return Maximum size allowed for textures, in pixels + /// + //////////////////////////////////////////////////////////// + static unsigned int getMaximumSize(); + +private: + + friend class sf::Text; + friend class sf::RenderTexture; + friend class RenderTargetImplDefault; + + //////////////////////////////////////////////////////////// + /// \brief Create the texture + /// + /// \param width Width of the texture + /// \param height Height of the texture + /// + /// \return True if creation was successful + /// + //////////////////////////////////////////////////////////// + virtual bool create(unsigned int width, unsigned int height); + + //////////////////////////////////////////////////////////// + /// \brief Load the texture from an image + /// + /// \param image Image to load into the texture + /// \param area Area of the image to load + /// + /// \return True if loading was successful + /// + /// \see loadFromFile, loadFromMemory + /// + //////////////////////////////////////////////////////////// + virtual bool loadFromImage(const Image& image, const IntRect& area); + + //////////////////////////////////////////////////////////// + /// \brief Return the size of the texture + /// + /// \return Size in pixels + /// + //////////////////////////////////////////////////////////// + virtual Vector2u getSize() const; + + //////////////////////////////////////////////////////////// + /// \brief Copy the texture pixels to an image + /// + /// \return Image containing the texture's pixels + /// + /// \see loadFromImage + /// + //////////////////////////////////////////////////////////// + virtual Image copyToImage() const; + + //////////////////////////////////////////////////////////// + /// \brief Update a part of the texture from an array of pixels + /// + /// \param pixels Array of pixels to copy to the texture + /// \param width Width of the pixel region contained in \a pixels + /// \param height Height of the pixel region contained in \a pixels + /// \param x X offset in the texture where to copy the source pixels + /// \param y Y offset in the texture where to copy the source pixels + /// + //////////////////////////////////////////////////////////// + virtual 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 + /// + /// \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 + /// + //////////////////////////////////////////////////////////// + virtual void update(const TextureImpl& texture, unsigned int x, unsigned int y); + + //////////////////////////////////////////////////////////// + /// \brief Update a part of the texture from the contents of a window + /// + /// \param window Window to copy to the texture + /// \param x X offset in the texture where to copy the source window + /// \param y Y offset in the texture where to copy the source window + /// + //////////////////////////////////////////////////////////// + virtual void update(const Window& window, unsigned int x, unsigned int y); + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable the smooth filter + /// + /// \param smooth True to enable smoothing, false to disable it + /// + /// \see isSmooth + /// + //////////////////////////////////////////////////////////// + virtual void setSmooth(bool smooth); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether the smooth filter is enabled or not + /// + /// \return True if smoothing is enabled, false if it is disabled + /// + /// \see setSmooth + /// + //////////////////////////////////////////////////////////// + virtual bool isSmooth() const; + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable conversion from sRGB + /// + /// \param sRgb True to enable sRGB conversion, false to disable it + /// + /// \see isSrgb + /// + //////////////////////////////////////////////////////////// + virtual void setSrgb(bool sRgb); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether the texture source is converted from sRGB or not + /// + /// \return True if the texture source is converted from sRGB, false if not + /// + /// \see setSrgb + /// + //////////////////////////////////////////////////////////// + virtual bool isSrgb() const; + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable repeating + /// + /// \param repeated True to repeat the texture, false to disable repeating + /// + /// \see isRepeated + /// + //////////////////////////////////////////////////////////// + virtual void setRepeated(bool repeated); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether the texture is repeated or not + /// + /// \return True if repeat mode is enabled, false if it is disabled + /// + /// \see setRepeated + /// + //////////////////////////////////////////////////////////// + virtual bool isRepeated() const; + + //////////////////////////////////////////////////////////// + /// \brief Generate a mipmap using the current texture data + /// + /// \return True if mipmap generation was successful, false if unsuccessful + /// + //////////////////////////////////////////////////////////// + virtual bool generateMipmap(); + + //////////////////////////////////////////////////////////// + /// \brief Get the underlying OpenGL handle of the texture. + /// + /// \return OpenGL handle of the texture or 0 if not yet created + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getNativeHandle() const; + + //////////////////////////////////////////////////////////// + /// \brief Get a valid image size according to hardware support + /// + /// This function checks whether the graphics driver supports + /// non power of two sizes or not, and adjusts the size + /// accordingly. + /// The returned size is greater than or equal to the original size. + /// + /// \param size size to convert + /// + /// \return Valid nearest size (greater than or equal to specified size) + /// + //////////////////////////////////////////////////////////// + static unsigned int getValidSize(unsigned int size); + + //////////////////////////////////////////////////////////// + /// \brief Invalidate the mipmap if one exists + /// + /// This also resets the texture's minifying function. + /// This function is mainly for internal use by RenderTexture. + /// + //////////////////////////////////////////////////////////// + void invalidateMipmap(); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + Vector2u m_size; ///< Public texture size + Vector2u m_actualSize; ///< Actual texture size (can be greater than public size because of padding) + unsigned int m_texture; ///< Internal texture identifier + bool m_isSmooth; ///< Status of the smooth filter + bool m_sRgb; ///< Should the texture source be converted from sRGB? + bool m_isRepeated; ///< Is the texture in repeat mode? + mutable bool m_pixelsFlipped; ///< To work around the inconsistency in Y orientation + bool m_fboAttachment; ///< Is this texture owned by a framebuffer object? + bool m_hasMipmap; ///< Has the mipmap been generated? + Uint64 m_cacheId; ///< Unique number that identifies the texture to the render target's cache +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_TEXTURE_IMPL_DEFAULT_HPP diff --git a/src/SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.cpp b/src/SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.cpp new file mode 100644 index 000000000..3d6a2e130 --- /dev/null +++ b/src/SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.cpp @@ -0,0 +1,276 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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 +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + sf::Mutex isAvailableMutex; + + GLenum usageToGlEnum(sf::VertexBuffer::Usage usage) + { + switch (usage) + { + case sf::VertexBuffer::Static: return GLEXT_GL_STATIC_DRAW; + case sf::VertexBuffer::Dynamic: return GLEXT_GL_DYNAMIC_DRAW; + default: return GLEXT_GL_STREAM_DRAW; + } + } +} + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +VertexBufferImplDefault::VertexBufferImplDefault() : +m_buffer(0), +m_size (0), +m_usage (VertexBuffer::Stream) +{ +} + + +//////////////////////////////////////////////////////////// +VertexBufferImplDefault::VertexBufferImplDefault(VertexBuffer::Usage usage) : +m_buffer(0), +m_size (0), +m_usage (usage) +{ +} + + +//////////////////////////////////////////////////////////// +VertexBufferImplDefault::~VertexBufferImplDefault() +{ + if (m_buffer) + { + TransientContextLock contextLock; + + glCheck(GLEXT_glDeleteBuffers(1, &m_buffer)); + } +} + + +//////////////////////////////////////////////////////////// +bool VertexBufferImplDefault::create(std::size_t vertexCount) +{ + if (!isAvailable()) + return false; + + TransientContextLock contextLock; + + if (!m_buffer) + glCheck(GLEXT_glGenBuffers(1, &m_buffer)); + + if (!m_buffer) + { + err() << "Could not create vertex buffer, generation failed" << std::endl; + return false; + } + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, m_buffer)); + glCheck(GLEXT_glBufferData(GLEXT_GL_ARRAY_BUFFER, sizeof(Vertex) * vertexCount, 0, usageToGlEnum(m_usage))); + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, 0)); + + m_size = vertexCount; + + return true; +} + + +//////////////////////////////////////////////////////////// +std::size_t VertexBufferImplDefault::getVertexCount() const +{ + return m_size; +} + + +//////////////////////////////////////////////////////////// +bool VertexBufferImplDefault::update(const Vertex* vertices, std::size_t vertexCount, unsigned int offset) +{ + // Sanity checks + if (!m_buffer) + return false; + + if (!vertices) + return false; + + if (offset && (offset + vertexCount > m_size)) + return false; + + TransientContextLock contextLock; + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, m_buffer)); + + // Check if we need to resize or orphan the buffer + if (vertexCount >= m_size) + { + glCheck(GLEXT_glBufferData(GLEXT_GL_ARRAY_BUFFER, sizeof(Vertex) * vertexCount, 0, usageToGlEnum(m_usage))); + + m_size = vertexCount; + } + + glCheck(GLEXT_glBufferSubData(GLEXT_GL_ARRAY_BUFFER, sizeof(Vertex) * offset, sizeof(Vertex) * vertexCount, vertices)); + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, 0)); + + return true; +} + + +//////////////////////////////////////////////////////////// +bool VertexBufferImplDefault::update(const VertexBufferImpl& vertexBuffer) +{ +#ifdef SFML_OPENGL_ES + + return false; + +#else + + const VertexBufferImplDefault& other = static_cast(vertexBuffer); + + if (!m_buffer || !other.m_buffer) + return false; + + TransientContextLock contextLock; + + // Make sure that extensions are initialized + sf::priv::ensureExtensionsInit(); + + if (GLEXT_copy_buffer) + { + glCheck(GLEXT_glBindBuffer(GLEXT_GL_COPY_READ_BUFFER, other.m_buffer)); + glCheck(GLEXT_glBindBuffer(GLEXT_GL_COPY_WRITE_BUFFER, m_buffer)); + + glCheck(GLEXT_glCopyBufferSubData(GLEXT_GL_COPY_READ_BUFFER, GLEXT_GL_COPY_WRITE_BUFFER, 0, 0, sizeof(Vertex) * other.m_size)); + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_COPY_WRITE_BUFFER, 0)); + glCheck(GLEXT_glBindBuffer(GLEXT_GL_COPY_READ_BUFFER, 0)); + + return true; + } + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, m_buffer)); + glCheck(GLEXT_glBufferData(GLEXT_GL_ARRAY_BUFFER, sizeof(Vertex) * other.m_size, 0, usageToGlEnum(m_usage))); + + void* destination = 0; + glCheck(destination = GLEXT_glMapBuffer(GLEXT_GL_ARRAY_BUFFER, GLEXT_GL_WRITE_ONLY)); + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, other.m_buffer)); + + void* source = 0; + glCheck(source = GLEXT_glMapBuffer(GLEXT_GL_ARRAY_BUFFER, GLEXT_GL_READ_ONLY)); + + std::memcpy(destination, source, sizeof(Vertex) * other.m_size); + + GLboolean sourceResult = GL_FALSE; + glCheck(sourceResult = GLEXT_glUnmapBuffer(GLEXT_GL_ARRAY_BUFFER)); + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, m_buffer)); + + GLboolean destinationResult = GL_FALSE; + glCheck(destinationResult = GLEXT_glUnmapBuffer(GLEXT_GL_ARRAY_BUFFER)); + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, 0)); + + if ((sourceResult == GL_FALSE) || (destinationResult == GL_FALSE)) + return false; + + return true; + +#endif // SFML_OPENGL_ES +} + + +//////////////////////////////////////////////////////////// +unsigned int VertexBufferImplDefault::getNativeHandle() const +{ + return m_buffer; +} + + +//////////////////////////////////////////////////////////// +void VertexBufferImplDefault::bind(const VertexBufferImplDefault* vertexBuffer) +{ + if (!isAvailable()) + return; + + TransientContextLock lock; + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, vertexBuffer ? vertexBuffer->m_buffer : 0)); +} + + +//////////////////////////////////////////////////////////// +void VertexBufferImplDefault::setUsage(VertexBuffer::Usage usage) +{ + m_usage = usage; +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::Usage VertexBufferImplDefault::getUsage() const +{ + return m_usage; +} + + +//////////////////////////////////////////////////////////// +bool VertexBufferImplDefault::isAvailable() +{ + Lock lock(isAvailableMutex); + + static bool checked = false; + static bool available = false; + + if (!checked) + { + checked = true; + + TransientContextLock contextLock; + + // Make sure that extensions are initialized + sf::priv::ensureExtensionsInit(); + + available = GLEXT_vertex_buffer_object; + } + + return available; +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.hpp b/src/SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.hpp new file mode 100644 index 000000000..c407d197f --- /dev/null +++ b/src/SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.hpp @@ -0,0 +1,170 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_VERTEXBUFFER_IMPL_DEFAULT_HPP +#define SFML_VERTEXBUFFER_IMPL_DEFAULT_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +class RenderTarget; +class Vertex; + +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Default specialization of VertexBufferImpl, +/// using OpenGL VBOs +/// +//////////////////////////////////////////////////////////// +class VertexBufferImplDefault : public VertexBufferImpl, GlResource +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an empty vertex buffer. + /// + //////////////////////////////////////////////////////////// + VertexBufferImplDefault(); + + //////////////////////////////////////////////////////////// + /// \brief Construct a VertexBuffer with a specific usage specifier + /// + /// Creates an empty vertex buffer and sets its usage to \p usage. + /// + /// \param usage Usage specifier + /// + //////////////////////////////////////////////////////////// + explicit VertexBufferImplDefault(VertexBuffer::Usage usage); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~VertexBufferImplDefault(); + + //////////////////////////////////////////////////////////// + /// \brief Bind a vertex buffer for rendering + /// + /// \param vertexBuffer Pointer to the vertex buffer to bind, can be null to use no vertex buffer + /// + //////////////////////////////////////////////////////////// + static void bind(const VertexBufferImplDefault* vertexBuffer); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports vertex buffers + /// + /// \return True if vertex buffers are supported, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isAvailable(); + + //////////////////////////////////////////////////////////// + /// \brief Create the vertex buffer + /// + /// \param vertexCount Number of vertices worth of memory to allocate + /// + /// \return True if creation was successful + /// + //////////////////////////////////////////////////////////// + virtual bool create(std::size_t vertexCount); + + //////////////////////////////////////////////////////////// + /// \brief Return the vertex count + /// + /// \return Number of vertices in the vertex buffer + /// + //////////////////////////////////////////////////////////// + virtual std::size_t getVertexCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Update a part of the buffer from an array of vertices + /// + /// \param vertices Array of vertices to copy to the buffer + /// \param vertexCount Number of vertices to copy + /// \param offset Offset in the buffer to copy to + /// + /// \return True if the update was successful + /// + //////////////////////////////////////////////////////////// + virtual bool update(const Vertex* vertices, std::size_t vertexCount, unsigned int offset); + + //////////////////////////////////////////////////////////// + /// \brief Copy the contents of another buffer into this buffer + /// + /// \param vertexBuffer Vertex buffer whose contents to copy into this vertex buffer + /// + /// \return True if the copy was successful + /// + //////////////////////////////////////////////////////////// + virtual bool update(const VertexBufferImpl& vertexBuffer); + + //////////////////////////////////////////////////////////// + /// \brief Get the underlying OpenGL handle of the vertex buffer. + /// + /// \return OpenGL handle of the vertex buffer or 0 if not yet created + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getNativeHandle() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the usage specifier of this vertex buffer + /// + /// \param usage Usage specifier + /// + //////////////////////////////////////////////////////////// + virtual void setUsage(VertexBuffer::Usage usage); + + //////////////////////////////////////////////////////////// + /// \brief Get the usage specifier of this vertex buffer + /// + /// \return Usage specifier + /// + //////////////////////////////////////////////////////////// + virtual VertexBuffer::Usage getUsage() const; + +private: + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + unsigned int m_buffer; ///< Internal buffer identifier + std::size_t m_size; ///< Size in Vertexes of the currently allocated buffer + VertexBuffer::Usage m_usage; ///< How this vertex buffer is to be used +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_VERTEXBUFFER_IMPL_DEFAULT_HPP diff --git a/src/SFML/Graphics/GLCheck.cpp b/src/SFML/Graphics/OpenGL/GLCheck.cpp similarity index 98% rename from src/SFML/Graphics/GLCheck.cpp rename to src/SFML/Graphics/OpenGL/GLCheck.cpp index 5bb140c1b..76afe3997 100644 --- a/src/SFML/Graphics/GLCheck.cpp +++ b/src/SFML/Graphics/OpenGL/GLCheck.cpp @@ -25,7 +25,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include +#include #include #include diff --git a/src/SFML/Graphics/GLCheck.hpp b/src/SFML/Graphics/OpenGL/GLCheck.hpp similarity index 98% rename from src/SFML/Graphics/GLCheck.hpp rename to src/SFML/Graphics/OpenGL/GLCheck.hpp index 19ab3b1e9..55ca7d55a 100644 --- a/src/SFML/Graphics/GLCheck.hpp +++ b/src/SFML/Graphics/OpenGL/GLCheck.hpp @@ -29,7 +29,7 @@ // Headers //////////////////////////////////////////////////////////// #include -#include +#include namespace sf diff --git a/src/SFML/Graphics/GLExtensions.cpp b/src/SFML/Graphics/OpenGL/GLExtensions.cpp similarity index 98% rename from src/SFML/Graphics/GLExtensions.cpp rename to src/SFML/Graphics/OpenGL/GLExtensions.cpp index 60d25847e..914219ade 100644 --- a/src/SFML/Graphics/GLExtensions.cpp +++ b/src/SFML/Graphics/OpenGL/GLExtensions.cpp @@ -26,7 +26,7 @@ // Headers //////////////////////////////////////////////////////////// #define SF_GLAD_GL_IMPLEMENTATION -#include +#include #include #include diff --git a/src/SFML/Graphics/GLExtensions.hpp b/src/SFML/Graphics/OpenGL/GLExtensions.hpp similarity index 100% rename from src/SFML/Graphics/GLExtensions.hpp rename to src/SFML/Graphics/OpenGL/GLExtensions.hpp diff --git a/src/SFML/Graphics/GLExtensions.txt b/src/SFML/Graphics/OpenGL/GLExtensions.txt similarity index 100% rename from src/SFML/Graphics/GLExtensions.txt rename to src/SFML/Graphics/OpenGL/GLExtensions.txt diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp index 4ae96a20b..89e048066 100644 --- a/src/SFML/Graphics/RenderTarget.cpp +++ b/src/SFML/Graphics/RenderTarget.cpp @@ -26,154 +26,54 @@ // Headers //////////////////////////////////////////////////////////// #include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -// GL_QUADS is unavailable on OpenGL ES, thus we need to define GL_QUADS ourselves -#ifndef GL_QUADS - - #define GL_QUADS 0 - -#endif // GL_QUADS - - -namespace -{ - // Mutex to protect ID generation and our context-RenderTarget-map - sf::Mutex mutex; - - // Unique identifier, used for identifying RenderTargets when - // tracking the currently active RenderTarget within a given context - sf::Uint64 getUniqueId() - { - sf::Lock lock(mutex); - - static sf::Uint64 id = 1; // start at 1, zero is "no RenderTarget" - - return id++; - } - - // Map to help us detect whether a different RenderTarget - // has been activated within a single context - typedef std::map ContextRenderTargetMap; - ContextRenderTargetMap contextRenderTargetMap; - - // Check if a RenderTarget with the given ID is active in the current context - bool isActive(sf::Uint64 id) - { - ContextRenderTargetMap::iterator iter = contextRenderTargetMap.find(sf::Context::getActiveContextId()); - - if ((iter == contextRenderTargetMap.end()) || (iter->second != id)) - return false; - - return true; - } - - // Convert an sf::BlendMode::Factor constant to the corresponding OpenGL constant. - sf::Uint32 factorToGlConstant(sf::BlendMode::Factor blendFactor) - { - switch (blendFactor) - { - case sf::BlendMode::Zero: return GL_ZERO; - case sf::BlendMode::One: return GL_ONE; - case sf::BlendMode::SrcColor: return GL_SRC_COLOR; - case sf::BlendMode::OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR; - case sf::BlendMode::DstColor: return GL_DST_COLOR; - case sf::BlendMode::OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR; - case sf::BlendMode::SrcAlpha: return GL_SRC_ALPHA; - case sf::BlendMode::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; - case sf::BlendMode::DstAlpha: return GL_DST_ALPHA; - case sf::BlendMode::OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA; - } - - sf::err() << "Invalid value for sf::BlendMode::Factor! Fallback to sf::BlendMode::Zero." << std::endl; - assert(false); - return GL_ZERO; - } - - - // Convert an sf::BlendMode::BlendEquation constant to the corresponding OpenGL constant. - sf::Uint32 equationToGlConstant(sf::BlendMode::Equation blendEquation) - { - switch (blendEquation) - { - case sf::BlendMode::Add: return GLEXT_GL_FUNC_ADD; - case sf::BlendMode::Subtract: return GLEXT_GL_FUNC_SUBTRACT; - case sf::BlendMode::ReverseSubtract: return GLEXT_GL_FUNC_REVERSE_SUBTRACT; - } - - sf::err() << "Invalid value for sf::BlendMode::Equation! Fallback to sf::BlendMode::Add." << std::endl; - assert(false); - return GLEXT_GL_FUNC_ADD; - } -} namespace sf { //////////////////////////////////////////////////////////// RenderTarget::RenderTarget() : -m_defaultView(), -m_view (), -m_cache (), -m_id (0) +m_impl(NULL) { - m_cache.glStatesSet = false; + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + m_impl = new priv::RenderTargetImplDefault(this); } //////////////////////////////////////////////////////////// RenderTarget::~RenderTarget() { + delete m_impl; } //////////////////////////////////////////////////////////// void RenderTarget::clear(const Color& color) { - if (isActive(m_id) || setActive(true)) - { - // Unbind texture to fix RenderTexture preventing clear - applyTexture(NULL); - - glCheck(glClearColor(color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f)); - glCheck(glClear(GL_COLOR_BUFFER_BIT)); - } + m_impl->clear(color); } //////////////////////////////////////////////////////////// void RenderTarget::setView(const View& view) { - m_view = view; - m_cache.viewChanged = true; + m_impl->setView(view); } //////////////////////////////////////////////////////////// const View& RenderTarget::getView() const { - return m_view; + return m_impl->getView(); } //////////////////////////////////////////////////////////// const View& RenderTarget::getDefaultView() const { - return m_defaultView; + return m_impl->getDefaultView(); } @@ -246,78 +146,7 @@ void RenderTarget::draw(const Drawable& drawable, const RenderStates& states) void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, PrimitiveType type, const RenderStates& states) { - // Nothing to draw? - if (!vertices || (vertexCount == 0)) - return; - - // GL_QUADS is unavailable on OpenGL ES - #ifdef SFML_OPENGL_ES - if (type == Quads) - { - err() << "sf::Quads primitive type is not supported on OpenGL ES platforms, drawing skipped" << std::endl; - return; - } - #endif - - if (isActive(m_id) || setActive(true)) - { - // Check if the vertex count is low enough so that we can pre-transform them - bool useVertexCache = (vertexCount <= StatesCache::VertexCacheSize); - - if (useVertexCache) - { - // Pre-transform the vertices and store them into the vertex cache - for (std::size_t i = 0; i < vertexCount; ++i) - { - Vertex& vertex = m_cache.vertexCache[i]; - vertex.position = states.transform * vertices[i].position; - vertex.color = vertices[i].color; - vertex.texCoords = vertices[i].texCoords; - } - } - - setupDraw(useVertexCache, states); - - // Check if texture coordinates array is needed, and update client state accordingly - bool enableTexCoordsArray = (states.texture || states.shader); - if (!m_cache.enable || (enableTexCoordsArray != m_cache.texCoordsArrayEnabled)) - { - if (enableTexCoordsArray) - glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); - else - glCheck(glDisableClientState(GL_TEXTURE_COORD_ARRAY)); - } - - // If we switch between non-cache and cache mode or enable texture - // coordinates we need to set up the pointers to the vertices' components - if (!m_cache.enable || !useVertexCache || !m_cache.useVertexCache) - { - const char* data = reinterpret_cast(vertices); - - // If we pre-transform the vertices, we must use our internal vertex cache - if (useVertexCache) - data = reinterpret_cast(m_cache.vertexCache); - - glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), data + 0)); - glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), data + 8)); - if (enableTexCoordsArray) - glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); - } - else if (enableTexCoordsArray && !m_cache.texCoordsArrayEnabled) - { - // If we enter this block, we are already using our internal vertex cache - const char* data = reinterpret_cast(m_cache.vertexCache); - - glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); - } - - drawPrimitives(type, 0, vertexCount); - cleanupDraw(states); - - // Update the cache - m_cache.useVertexCache = useVertexCache; - m_cache.texCoordsArrayEnabled = enableTexCoordsArray; - } + m_impl->draw(vertices, vertexCount, type, states); } @@ -332,438 +161,42 @@ void RenderTarget::draw(const VertexBuffer& vertexBuffer, const RenderStates& st void RenderTarget::draw(const VertexBuffer& vertexBuffer, std::size_t firstVertex, std::size_t vertexCount, const RenderStates& states) { - // VertexBuffer not supported? - if (!VertexBuffer::isAvailable()) - { - err() << "sf::VertexBuffer is not available, drawing skipped" << std::endl; - return; - } - - // Sanity check - if (firstVertex > vertexBuffer.getVertexCount()) - return; - - // Clamp vertexCount to something that makes sense - vertexCount = std::min(vertexCount, vertexBuffer.getVertexCount() - firstVertex); - - // Nothing to draw? - if (!vertexCount || !vertexBuffer.getNativeHandle()) - return; - - // GL_QUADS is unavailable on OpenGL ES - #ifdef SFML_OPENGL_ES - if (vertexBuffer.getPrimitiveType() == Quads) - { - err() << "sf::Quads primitive type is not supported on OpenGL ES platforms, drawing skipped" << std::endl; - return; - } - #endif - - if (isActive(m_id) || setActive(true)) - { - setupDraw(false, states); - - // Bind vertex buffer - VertexBuffer::bind(&vertexBuffer); - - // Always enable texture coordinates - if (!m_cache.enable || !m_cache.texCoordsArrayEnabled) - glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); - - glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(0))); - glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), reinterpret_cast(8))); - glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(12))); - - drawPrimitives(vertexBuffer.getPrimitiveType(), firstVertex, vertexCount); - - // Unbind vertex buffer - VertexBuffer::bind(NULL); - - cleanupDraw(states); - - // Update the cache - m_cache.useVertexCache = false; - m_cache.texCoordsArrayEnabled = true; - } + m_impl->draw(vertexBuffer, firstVertex, vertexCount, states); } //////////////////////////////////////////////////////////// bool RenderTarget::setActive(bool active) { - // Mark this RenderTarget as active or no longer active in the tracking map - { - sf::Lock lock(mutex); - - Uint64 contextId = Context::getActiveContextId(); - - ContextRenderTargetMap::iterator iter = contextRenderTargetMap.find(contextId); - - if (active) - { - if (iter == contextRenderTargetMap.end()) - { - contextRenderTargetMap[contextId] = m_id; - - m_cache.enable = false; - } - else if (iter->second != m_id) - { - iter->second = m_id; - - m_cache.enable = false; - } - } - else - { - if (iter != contextRenderTargetMap.end()) - contextRenderTargetMap.erase(iter); - - m_cache.enable = false; - } - } - - return true; + return m_impl->setActive(active); } //////////////////////////////////////////////////////////// void RenderTarget::pushGLStates() { - if (isActive(m_id) || setActive(true)) - { - #ifdef SFML_DEBUG - // make sure that the user didn't leave an unchecked OpenGL error - GLenum error = glGetError(); - if (error != GL_NO_ERROR) - { - err() << "OpenGL error (" << error << ") detected in user code, " - << "you should check for errors with glGetError()" - << std::endl; - } - #endif - - #ifndef SFML_OPENGL_ES - glCheck(glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS)); - glCheck(glPushAttrib(GL_ALL_ATTRIB_BITS)); - #endif - glCheck(glMatrixMode(GL_MODELVIEW)); - glCheck(glPushMatrix()); - glCheck(glMatrixMode(GL_PROJECTION)); - glCheck(glPushMatrix()); - glCheck(glMatrixMode(GL_TEXTURE)); - glCheck(glPushMatrix()); - } - - resetGLStates(); + m_impl->pushGLStates(); } //////////////////////////////////////////////////////////// void RenderTarget::popGLStates() { - if (isActive(m_id) || setActive(true)) - { - glCheck(glMatrixMode(GL_PROJECTION)); - glCheck(glPopMatrix()); - glCheck(glMatrixMode(GL_MODELVIEW)); - glCheck(glPopMatrix()); - glCheck(glMatrixMode(GL_TEXTURE)); - glCheck(glPopMatrix()); - #ifndef SFML_OPENGL_ES - glCheck(glPopClientAttrib()); - glCheck(glPopAttrib()); - #endif - } + m_impl->popGLStates(); } //////////////////////////////////////////////////////////// void RenderTarget::resetGLStates() { - // Check here to make sure a context change does not happen after activate(true) - bool shaderAvailable = Shader::isAvailable(); - bool vertexBufferAvailable = VertexBuffer::isAvailable(); - - // Workaround for states not being properly reset on - // macOS unless a context switch really takes place - #if defined(SFML_SYSTEM_MACOS) - setActive(false); - #endif - - if (isActive(m_id) || setActive(true)) - { - // Make sure that extensions are initialized - priv::ensureExtensionsInit(); - - // Make sure that the texture unit which is active is the number 0 - if (GLEXT_multitexture) - { - glCheck(GLEXT_glClientActiveTexture(GLEXT_GL_TEXTURE0)); - glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0)); - } - - // Define the default OpenGL states - glCheck(glDisable(GL_CULL_FACE)); - glCheck(glDisable(GL_LIGHTING)); - glCheck(glDisable(GL_DEPTH_TEST)); - glCheck(glDisable(GL_ALPHA_TEST)); - glCheck(glEnable(GL_TEXTURE_2D)); - glCheck(glEnable(GL_BLEND)); - glCheck(glMatrixMode(GL_MODELVIEW)); - glCheck(glLoadIdentity()); - glCheck(glEnableClientState(GL_VERTEX_ARRAY)); - glCheck(glEnableClientState(GL_COLOR_ARRAY)); - glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); - m_cache.glStatesSet = true; - - // Apply the default SFML states - applyBlendMode(BlendAlpha); - applyTexture(NULL); - if (shaderAvailable) - applyShader(NULL); - - if (vertexBufferAvailable) - glCheck(VertexBuffer::bind(NULL)); - - m_cache.texCoordsArrayEnabled = true; - - m_cache.useVertexCache = false; - - // Set the default view - setView(getView()); - - m_cache.enable = true; - } + m_impl->resetGLStates(); } //////////////////////////////////////////////////////////// void RenderTarget::initialize() { - // Setup the default and current views - m_defaultView.reset(FloatRect(0, 0, static_cast(getSize().x), static_cast(getSize().y))); - m_view = m_defaultView; - - // Set GL states only on first draw, so that we don't pollute user's states - m_cache.glStatesSet = false; - - // Generate a unique ID for this RenderTarget to track - // whether it is active within a specific context - m_id = getUniqueId(); -} - - -//////////////////////////////////////////////////////////// -void RenderTarget::applyCurrentView() -{ - // Set the viewport - IntRect viewport = getViewport(m_view); - int top = getSize().y - (viewport.top + viewport.height); - glCheck(glViewport(viewport.left, top, viewport.width, viewport.height)); - - // Set the projection matrix - glCheck(glMatrixMode(GL_PROJECTION)); - glCheck(glLoadMatrixf(m_view.getTransform().getMatrix())); - - // Go back to model-view mode - glCheck(glMatrixMode(GL_MODELVIEW)); - - m_cache.viewChanged = false; -} - - -//////////////////////////////////////////////////////////// -void RenderTarget::applyBlendMode(const BlendMode& mode) -{ - // Apply the blend mode, falling back to the non-separate versions if necessary - if (GLEXT_blend_func_separate) - { - glCheck(GLEXT_glBlendFuncSeparate( - factorToGlConstant(mode.colorSrcFactor), factorToGlConstant(mode.colorDstFactor), - factorToGlConstant(mode.alphaSrcFactor), factorToGlConstant(mode.alphaDstFactor))); - } - else - { - glCheck(glBlendFunc( - factorToGlConstant(mode.colorSrcFactor), - factorToGlConstant(mode.colorDstFactor))); - } - - if (GLEXT_blend_minmax && GLEXT_blend_subtract) - { - if (GLEXT_blend_equation_separate) - { - glCheck(GLEXT_glBlendEquationSeparate( - equationToGlConstant(mode.colorEquation), - equationToGlConstant(mode.alphaEquation))); - } - else - { - glCheck(GLEXT_glBlendEquation(equationToGlConstant(mode.colorEquation))); - } - } - else if ((mode.colorEquation != BlendMode::Add) || (mode.alphaEquation != BlendMode::Add)) - { - static bool warned = false; - - if (!warned) - { - err() << "OpenGL extension EXT_blend_minmax and/or EXT_blend_subtract unavailable" << std::endl; - err() << "Selecting a blend equation not possible" << std::endl; - err() << "Ensure that hardware acceleration is enabled if available" << std::endl; - - warned = true; - } - } - - m_cache.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) - if (transform == Transform::Identity) - glCheck(glLoadIdentity()); - else - glCheck(glLoadMatrixf(transform.getMatrix())); -} - - -//////////////////////////////////////////////////////////// -void RenderTarget::applyTexture(const Texture* texture) -{ - Texture::bind(texture, Texture::Pixels); - - m_cache.lastTextureId = texture ? texture->m_cacheId : 0; -} - - -//////////////////////////////////////////////////////////// -void RenderTarget::applyShader(const Shader* shader) -{ - Shader::bind(shader); -} - - -//////////////////////////////////////////////////////////// -void RenderTarget::setupDraw(bool useVertexCache, const RenderStates& states) -{ - // First set the persistent OpenGL states if it's the very first call - if (!m_cache.glStatesSet) - resetGLStates(); - - if (useVertexCache) - { - // Since vertices are transformed, we must use an identity transform to render them - if (!m_cache.enable || !m_cache.useVertexCache) - glCheck(glLoadIdentity()); - } - else - { - applyTransform(states.transform); - } - - // Apply the view - if (!m_cache.enable || m_cache.viewChanged) - applyCurrentView(); - - // Apply the blend mode - if (!m_cache.enable || (states.blendMode != m_cache.lastBlendMode)) - applyBlendMode(states.blendMode); - - // Apply the texture - if (!m_cache.enable || (states.texture && states.texture->m_fboAttachment)) - { - // If the texture is an FBO attachment, always rebind it - // in order to inform the OpenGL driver that we want changes - // made to it in other contexts to be visible here as well - // This saves us from having to call glFlush() in - // RenderTextureImplFBO which can be quite costly - // See: https://www.khronos.org/opengl/wiki/Memory_Model - applyTexture(states.texture); - } - else - { - Uint64 textureId = states.texture ? states.texture->m_cacheId : 0; - if (textureId != m_cache.lastTextureId) - applyTexture(states.texture); - } - - // Apply the shader - if (states.shader) - applyShader(states.shader); -} - - -//////////////////////////////////////////////////////////// -void RenderTarget::drawPrimitives(PrimitiveType type, std::size_t firstVertex, std::size_t vertexCount) -{ - // 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}; - GLenum mode = modes[type]; - - // Draw the primitives - glCheck(glDrawArrays(mode, static_cast(firstVertex), static_cast(vertexCount))); -} - - -//////////////////////////////////////////////////////////// -void RenderTarget::cleanupDraw(const RenderStates& states) -{ - // Unbind the shader, if any - if (states.shader) - applyShader(NULL); - - // If the texture we used to draw belonged to a RenderTexture, then forcibly unbind that texture. - // This prevents a bug where some drivers do not clear RenderTextures properly. - if (states.texture && states.texture->m_fboAttachment) - applyTexture(NULL); - - // Re-enable the cache at the end of the draw if it was disabled - m_cache.enable = true; + m_impl->initialize(getSize()); } } // 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 -// Since it overloads the == operator, we can easily check -// whether any of the 6 blending components changed and, -// thus, whether we need to update the blend mode. -// -// * 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/TextureSaver.hpp b/src/SFML/Graphics/RenderTargetImpl.cpp similarity index 60% rename from src/SFML/Graphics/TextureSaver.hpp rename to src/SFML/Graphics/RenderTargetImpl.cpp index 5ee9f0003..d086b5e78 100644 --- a/src/SFML/Graphics/TextureSaver.hpp +++ b/src/SFML/Graphics/RenderTargetImpl.cpp @@ -22,13 +22,11 @@ // //////////////////////////////////////////////////////////// -#ifndef SFML_TEXTURESAVER_HPP -#define SFML_TEXTURESAVER_HPP - //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include +#include +#include namespace sf @@ -36,40 +34,54 @@ namespace sf namespace priv { //////////////////////////////////////////////////////////// -/// \brief Automatic wrapper for saving and restoring the current texture binding -/// -//////////////////////////////////////////////////////////// -class TextureSaver +RenderTargetImpl::RenderTargetImpl(RenderTarget* parent) : +m_parent(parent) { -public: + // Nothing to do +} - //////////////////////////////////////////////////////////// - /// \brief Default constructor - /// - /// The current texture binding is saved. - /// - //////////////////////////////////////////////////////////// - TextureSaver(); - //////////////////////////////////////////////////////////// - /// \brief Destructor - /// - /// The previous texture binding is restored. - /// - //////////////////////////////////////////////////////////// - ~TextureSaver(); +//////////////////////////////////////////////////////////// +RenderTargetImpl::~RenderTargetImpl() +{ + // Nothing to do +} -private: - //////////////////////////////////////////////////////////// - // Member data - //////////////////////////////////////////////////////////// - GLint m_textureBinding; ///< Texture binding to restore -}; +//////////////////////////////////////////////////////////// +void RenderTargetImpl::pushGLStates() +{ + // Nothing to do +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImpl::popGLStates() +{ + // Nothing to do +} + + +//////////////////////////////////////////////////////////// +void RenderTargetImpl::resetGLStates() +{ + // Nothing to do +} + + +//////////////////////////////////////////////////////////// +RenderTarget* RenderTargetImpl::getParent() +{ + return m_parent; +} + + +//////////////////////////////////////////////////////////// +const TextureImpl* RenderTargetImpl::getTextureImpl(const Texture& texture) +{ + return texture.m_impl; +} } // namespace priv } // namespace sf - - -#endif // SFML_TEXTURESAVER_HPP diff --git a/src/SFML/Graphics/RenderTargetImpl.hpp b/src/SFML/Graphics/RenderTargetImpl.hpp new file mode 100644 index 000000000..cc0bba590 --- /dev/null +++ b/src/SFML/Graphics/RenderTargetImpl.hpp @@ -0,0 +1,207 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_RENDERTARGET_IMPL_HPP +#define SFML_RENDERTARGET_IMPL_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + + +namespace sf +{ +class Drawable; +class VertexBuffer; +class Vertex; +class View; +class RenderStates; +class Color; +class RenderTarget; +class Texture; + +namespace priv +{ +class TextureImpl; + +//////////////////////////////////////////////////////////// +/// \brief Base class for all render targets (window, texture, ...) +/// +//////////////////////////////////////////////////////////// +class RenderTargetImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Constructor + /// + //////////////////////////////////////////////////////////// + RenderTargetImpl(RenderTarget* parent); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + virtual ~RenderTargetImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Clear the entire target with a single color + /// + /// \param color Fill color to use to clear the render target + /// + //////////////////////////////////////////////////////////// + virtual void clear(const Color& color) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Change the current active view + /// + /// \param view New view to use + /// + /// \see getView, getDefaultView + /// + //////////////////////////////////////////////////////////// + virtual void setView(const View& view) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get the view currently in use in the render target + /// + /// \return The view object that is currently used + /// + /// \see setView, getDefaultView + /// + //////////////////////////////////////////////////////////// + virtual const View& getView() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get the default view of the render target + /// + /// \return The default view of the render target + /// + /// \see setView, getView + /// + //////////////////////////////////////////////////////////// + virtual const View& getDefaultView() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Draw primitives defined by an array of vertices + /// + /// \param vertices Pointer to the vertices + /// \param vertexCount Number of vertices in the array + /// \param type Type of primitives to draw + /// \param states Render states to use for drawing + /// + //////////////////////////////////////////////////////////// + virtual void draw(const Vertex* vertices, std::size_t vertexCount, + PrimitiveType type, const RenderStates& states) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Draw primitives defined by a vertex buffer + /// + /// \param vertexBuffer Vertex buffer + /// \param firstVertex Index of the first vertex to render + /// \param vertexCount Number of vertices to render + /// \param states Render states to use for drawing + /// + //////////////////////////////////////////////////////////// + virtual void draw(const VertexBuffer& vertexBuffer, std::size_t firstVertex, + std::size_t vertexCount, const RenderStates& states) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Activate or deactivate the render target for rendering + /// + /// \param active True to activate, false to deactivate + /// + /// \return True if operation was successful, false otherwise + /// + //////////////////////////////////////////////////////////// + virtual bool setActive(bool active) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Save the current OpenGL render states and matrices + /// + /// \see popGLStates + /// + //////////////////////////////////////////////////////////// + virtual void pushGLStates(); + + //////////////////////////////////////////////////////////// + /// \brief Restore the previously saved OpenGL render states and matrices + /// + /// \see pushGLStates + /// + //////////////////////////////////////////////////////////// + virtual void popGLStates(); + + //////////////////////////////////////////////////////////// + /// \brief Reset the internal OpenGL states so that the target is ready for drawing + /// + //////////////////////////////////////////////////////////// + virtual void resetGLStates(); + + //////////////////////////////////////////////////////////// + /// \brief Performs the common initialization step after creation + /// + /// \param newSize New size of the RenderTarget + /// + //////////////////////////////////////////////////////////// + virtual void initialize(const Vector2u& newSize) = 0; + +protected: + + //////////////////////////////////////////////////////////// + /// \brief Get the parent RenderTarget + /// + /// \return The parent RenderTarget + /// + //////////////////////////////////////////////////////////// + RenderTarget* getParent(); + + //////////////////////////////////////////////////////////// + /// \brief Get the concrete implementation of a texture + /// + /// \param The texture + /// + /// \return The concrete implementation of a texture + /// + //////////////////////////////////////////////////////////// + static const TextureImpl* getTextureImpl(const Texture& texture); + +private: + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + RenderTarget* m_parent; + +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_RENDERTARGET_IMPL_HPP diff --git a/src/SFML/Graphics/RenderTexture.cpp b/src/SFML/Graphics/RenderTexture.cpp index bd0566a0e..fcf8ec9dd 100644 --- a/src/SFML/Graphics/RenderTexture.cpp +++ b/src/SFML/Graphics/RenderTexture.cpp @@ -26,8 +26,10 @@ // Headers //////////////////////////////////////////////////////////// #include -#include -#include +#include +#include +#include +#include #include @@ -65,28 +67,33 @@ bool RenderTexture::create(unsigned int width, unsigned int height, const Contex return false; } - // We disable smoothing by default for render textures - setSmooth(false); - - // Create the implementation - delete m_impl; - if (priv::RenderTextureImplFBO::isAvailable()) + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) { - // Use frame-buffer object (FBO) - m_impl = new priv::RenderTextureImplFBO; + priv::TextureImplDefault& texture = *static_cast(m_texture.m_impl); - // Mark the texture as being a framebuffer object attachment - m_texture.m_fboAttachment = true; - } - else - { - // Use default implementation - m_impl = new priv::RenderTextureImplDefault; - } + // We disable smoothing by default for render textures + setSmooth(false); - // Initialize the render texture - if (!m_impl->create(width, height, m_texture.m_texture, settings)) - return false; + // Create the implementation + delete m_impl; + if (priv::RenderTextureImplFBO::isAvailable()) + { + // Use frame-buffer object (FBO) + m_impl = new priv::RenderTextureImplFBO; + + // Mark the texture as being a framebuffer object attachment + texture.m_fboAttachment = true; + } + else + { + // Use default implementation + m_impl = new priv::RenderTextureImplDefault; + } + + // Initialize the render texture + if (!m_impl->create(width, height, texture.m_texture, settings)) + return false; + } // We can now initialize the render target part RenderTarget::initialize(); @@ -161,11 +168,16 @@ bool RenderTexture::setActive(bool active) void RenderTexture::display() { // Update the target texture - if (m_impl && (priv::RenderTextureImplFBO::isAvailable() || setActive(true))) + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) { - m_impl->updateTexture(m_texture.m_texture); - m_texture.m_pixelsFlipped = true; - m_texture.invalidateMipmap(); + if (m_impl && (priv::RenderTextureImplFBO::isAvailable() || setActive(true))) + { + priv::TextureImplDefault& texture = *static_cast(m_texture.m_impl); + + m_impl->updateTexture(texture.m_texture); + texture.m_pixelsFlipped = true; + texture.invalidateMipmap(); + } } } diff --git a/src/SFML/Graphics/RenderWindow.cpp b/src/SFML/Graphics/RenderWindow.cpp index 38ec2a7c6..d886902cb 100644 --- a/src/SFML/Graphics/RenderWindow.cpp +++ b/src/SFML/Graphics/RenderWindow.cpp @@ -27,8 +27,9 @@ //////////////////////////////////////////////////////////// #include #include -#include -#include +#include +#include +#include namespace sf @@ -82,13 +83,16 @@ bool RenderWindow::setActive(bool active) if (result) RenderTarget::setActive(active); - // If FBOs are available, make sure none are bound when we - // try to draw to the default framebuffer of the RenderWindow - if (active && result && priv::RenderTextureImplFBO::isAvailable()) + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) { - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, m_defaultFrameBuffer)); + // If FBOs are available, make sure none are bound when we + // try to draw to the default framebuffer of the RenderWindow + if (active && result && priv::RenderTextureImplFBO::isAvailable()) + { + priv::RenderTextureImplFBO::bindFramebuffer(m_defaultFrameBuffer); - return true; + return true; + } } return result; @@ -111,11 +115,14 @@ Image RenderWindow::capture() const //////////////////////////////////////////////////////////// void RenderWindow::onCreate() { - if (priv::RenderTextureImplFBO::isAvailable()) + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) { - // Retrieve the framebuffer ID we have to bind when targeting the window for rendering - // We assume that this window's context is still active at this point - glCheck(glGetIntegerv(GLEXT_GL_FRAMEBUFFER_BINDING, reinterpret_cast(&m_defaultFrameBuffer))); + if (priv::RenderTextureImplFBO::isAvailable()) + { + // Retrieve the framebuffer ID we have to bind when targeting the window for rendering + // We assume that this window's context is still active at this point + m_defaultFrameBuffer = priv::RenderTextureImplFBO::getFramebuffer(); + } } // Just initialize the render target part diff --git a/src/SFML/Graphics/Renderer.cpp b/src/SFML/Graphics/Renderer.cpp new file mode 100644 index 000000000..e2bbb87ca --- /dev/null +++ b/src/SFML/Graphics/Renderer.cpp @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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::Uint32 renderer = sf::Renderer::Default; +} + + +namespace sf +{ +//////////////////////////////////////////////////////////// +Uint32 getAvailableRenderers() +{ + return Renderer::OpenGL1; +} + + +//////////////////////////////////////////////////////////// +void setRenderers(Uint32 renderers) +{ + // TODO: Select a renderer based on the user's preferences +} + + +//////////////////////////////////////////////////////////// +Uint32 getRenderer() +{ + return renderer; +} + +} // namespace sf diff --git a/src/SFML/Graphics/Shader.cpp b/src/SFML/Graphics/Shader.cpp index b7aed77a2..3ef08bde9 100644 --- a/src/SFML/Graphics/Shader.cpp +++ b/src/SFML/Graphics/Shader.cpp @@ -27,57 +27,25 @@ // Headers //////////////////////////////////////////////////////////// #include +#include +#include #include #include #include -#include -#include #include -#include -#include #include #include #include +#if !defined(SFML_OPENGL_ES) -#ifndef SFML_OPENGL_ES - -#if defined(SFML_SYSTEM_MACOS) || defined(SFML_SYSTEM_IOS) - - #define castToGlHandle(x) reinterpret_cast(static_cast(x)) - #define castFromGlHandle(x) static_cast(reinterpret_cast(x)) - -#else - - #define castToGlHandle(x) (x) - #define castFromGlHandle(x) (x) +#include #endif + namespace { - sf::Mutex maxTextureUnitsMutex; - sf::Mutex isAvailableMutex; - - GLint checkMaxTextureUnits() - { - GLint maxUnits = 0; - glCheck(glGetIntegerv(GLEXT_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxUnits)); - - return maxUnits; - } - - // Retrieve the maximum number of texture units available - GLint getMaxTextureUnits() - { - // TODO: Remove this lock when it becomes unnecessary in C++11 - sf::Lock lock(maxTextureUnitsMutex); - - static GLint maxUnits = checkMaxTextureUnits(); - - return maxUnits; - } - // Read the contents of a file into an array of char bool getFileContents(const std::string& filename, std::vector& buffer) { @@ -116,57 +84,6 @@ namespace buffer.push_back('\0'); return success; } - - // Transforms an array of 2D vectors into a contiguous array of scalars - template - std::vector flatten(const sf::Vector2* vectorArray, std::size_t length) - { - const std::size_t vectorSize = 2; - - std::vector contiguous(vectorSize * length); - for (std::size_t i = 0; i < length; ++i) - { - contiguous[vectorSize * i] = vectorArray[i].x; - contiguous[vectorSize * i + 1] = vectorArray[i].y; - } - - return contiguous; - } - - // Transforms an array of 3D vectors into a contiguous array of scalars - template - std::vector flatten(const sf::Vector3* vectorArray, std::size_t length) - { - const std::size_t vectorSize = 3; - - std::vector contiguous(vectorSize * length); - for (std::size_t i = 0; i < length; ++i) - { - contiguous[vectorSize * i] = vectorArray[i].x; - contiguous[vectorSize * i + 1] = vectorArray[i].y; - contiguous[vectorSize * i + 2] = vectorArray[i].z; - } - - return contiguous; - } - - // Transforms an array of 4D vectors into a contiguous array of scalars - template - std::vector flatten(const sf::priv::Vector4* vectorArray, std::size_t length) - { - const std::size_t vectorSize = 4; - - std::vector contiguous(vectorSize * length); - for (std::size_t i = 0; i < length; ++i) - { - contiguous[vectorSize * i] = vectorArray[i].x; - contiguous[vectorSize * i + 1] = vectorArray[i].y; - contiguous[vectorSize * i + 2] = vectorArray[i].z; - contiguous[vectorSize * i + 3] = vectorArray[i].w; - } - - return contiguous; - } } @@ -176,66 +93,29 @@ namespace sf Shader::CurrentTextureType Shader::CurrentTexture; -//////////////////////////////////////////////////////////// -struct Shader::UniformBinder : private NonCopyable -{ - //////////////////////////////////////////////////////////// - /// \brief Constructor: set up state before uniform is set - /// - //////////////////////////////////////////////////////////// - UniformBinder(Shader& shader, const std::string& name) : - savedProgram(0), - currentProgram(castToGlHandle(shader.m_shaderProgram)), - location(-1) - { - if (currentProgram) - { - // Enable program object - glCheck(savedProgram = GLEXT_glGetHandle(GLEXT_GL_PROGRAM_OBJECT)); - if (currentProgram != savedProgram) - glCheck(GLEXT_glUseProgramObject(currentProgram)); - - // Store uniform location for further use outside constructor - location = shader.getUniformLocation(name); - } - } - - //////////////////////////////////////////////////////////// - /// \brief Destructor: restore state after uniform is set - /// - //////////////////////////////////////////////////////////// - ~UniformBinder() - { - // Disable program object - if (currentProgram && (currentProgram != savedProgram)) - glCheck(GLEXT_glUseProgramObject(savedProgram)); - } - - TransientContextLock lock; ///< Lock to keep context active while uniform is bound - GLEXT_GLhandle savedProgram; ///< Handle to the previously active program object - GLEXT_GLhandle currentProgram; ///< Handle to the program object of the modified sf::Shader instance - GLint location; ///< Uniform location, used by the surrounding sf::Shader code -}; - - //////////////////////////////////////////////////////////// Shader::Shader() : -m_shaderProgram (0), -m_currentTexture(-1), -m_textures (), -m_uniforms () +m_impl(NULL) { + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + { +#if !defined(SFML_OPENGL_ES) + + m_impl = new priv::ShaderImplDefault; + +#else + + m_impl = new priv::ShaderImplNull; + +#endif + } } //////////////////////////////////////////////////////////// Shader::~Shader() { - TransientContextLock lock; - - // Destroy effect program - if (m_shaderProgram) - glCheck(GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram))); + delete m_impl; } @@ -252,11 +132,11 @@ bool Shader::loadFromFile(const std::string& filename, Type type) // Compile the shader program if (type == Vertex) - return compile(&shader[0], NULL, NULL); + return m_impl->compile(&shader[0], NULL, NULL); else if (type == Geometry) - return compile(NULL, &shader[0], NULL); + return m_impl->compile(NULL, &shader[0], NULL); else - return compile(NULL, NULL, &shader[0]); + return m_impl->compile(NULL, NULL, &shader[0]); } @@ -280,7 +160,7 @@ bool Shader::loadFromFile(const std::string& vertexShaderFilename, const std::st } // Compile the shader program - return compile(&vertexShader[0], NULL, &fragmentShader[0]); + return m_impl->compile(&vertexShader[0], NULL, &fragmentShader[0]); } @@ -312,7 +192,7 @@ bool Shader::loadFromFile(const std::string& vertexShaderFilename, const std::st } // Compile the shader program - return compile(&vertexShader[0], &geometryShader[0], &fragmentShader[0]); + return m_impl->compile(&vertexShader[0], &geometryShader[0], &fragmentShader[0]); } @@ -321,11 +201,11 @@ bool Shader::loadFromMemory(const std::string& shader, Type type) { // Compile the shader program if (type == Vertex) - return compile(shader.c_str(), NULL, NULL); + return m_impl->compile(shader.c_str(), NULL, NULL); else if (type == Geometry) - return compile(NULL, shader.c_str(), NULL); + return m_impl->compile(NULL, shader.c_str(), NULL); else - return compile(NULL, NULL, shader.c_str()); + return m_impl->compile(NULL, NULL, shader.c_str()); } @@ -333,7 +213,7 @@ bool Shader::loadFromMemory(const std::string& shader, Type type) bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader) { // Compile the shader program - return compile(vertexShader.c_str(), NULL, fragmentShader.c_str()); + return m_impl->compile(vertexShader.c_str(), NULL, fragmentShader.c_str()); } @@ -341,7 +221,7 @@ bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& geometryShader, const std::string& fragmentShader) { // Compile the shader program - return compile(vertexShader.c_str(), geometryShader.c_str(), fragmentShader.c_str()); + return m_impl->compile(vertexShader.c_str(), geometryShader.c_str(), fragmentShader.c_str()); } @@ -358,11 +238,11 @@ bool Shader::loadFromStream(InputStream& stream, Type type) // Compile the shader program if (type == Vertex) - return compile(&shader[0], NULL, NULL); + return m_impl->compile(&shader[0], NULL, NULL); else if (type == Geometry) - return compile(NULL, &shader[0], NULL); + return m_impl->compile(NULL, &shader[0], NULL); else - return compile(NULL, NULL, &shader[0]); + return m_impl->compile(NULL, NULL, &shader[0]); } @@ -386,7 +266,7 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragme } // Compile the shader program - return compile(&vertexShader[0], NULL, &fragmentShader[0]); + return m_impl->compile(&vertexShader[0], NULL, &fragmentShader[0]); } @@ -418,79 +298,63 @@ bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geomet } // Compile the shader program - return compile(&vertexShader[0], &geometryShader[0], &fragmentShader[0]); + return m_impl->compile(&vertexShader[0], &geometryShader[0], &fragmentShader[0]); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, float x) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform1f(binder.location, x)); + m_impl->setUniform(name, x); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Vec2& v) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform2f(binder.location, v.x, v.y)); + m_impl->setUniform(name, v); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Vec3& v) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform3f(binder.location, v.x, v.y, v.z)); + m_impl->setUniform(name, v); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Vec4& v) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform4f(binder.location, v.x, v.y, v.z, v.w)); + m_impl->setUniform(name, v); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, int x) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform1i(binder.location, x)); + m_impl->setUniform(name, x); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Ivec2& v) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform2i(binder.location, v.x, v.y)); + m_impl->setUniform(name, v); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Ivec3& v) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform3i(binder.location, v.x, v.y, v.z)); + m_impl->setUniform(name, v); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Ivec4& v) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform4i(binder.location, v.x, v.y, v.z, v.w)); + m_impl->setUniform(name, v); } @@ -525,138 +389,70 @@ void Shader::setUniform(const std::string& name, const Glsl::Bvec4& v) //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Mat3& matrix) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniformMatrix3fv(binder.location, 1, GL_FALSE, matrix.array)); + m_impl->setUniform(name, matrix); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Glsl::Mat4& matrix) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniformMatrix4fv(binder.location, 1, GL_FALSE, matrix.array)); + m_impl->setUniform(name, matrix); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, const Texture& texture) { - if (m_shaderProgram) - { - TransientContextLock lock; - - // Find the location of the variable in the shader - int location = getUniformLocation(name); - if (location != -1) - { - // Store the location -> texture mapping - TextureTable::iterator it = m_textures.find(location); - if (it == m_textures.end()) - { - // New entry, make sure there are enough texture units - GLint maxUnits = getMaxTextureUnits(); - if (m_textures.size() + 1 >= static_cast(maxUnits)) - { - err() << "Impossible to use texture \"" << name << "\" for shader: all available texture units are used" << std::endl; - return; - } - - m_textures[location] = &texture; - } - else - { - // Location already used, just replace the texture - it->second = &texture; - } - } - } + m_impl->setUniform(name, texture); } //////////////////////////////////////////////////////////// void Shader::setUniform(const std::string& name, CurrentTextureType) { - if (m_shaderProgram) - { - TransientContextLock lock; - - // Find the location of the variable in the shader - m_currentTexture = getUniformLocation(name); - } + m_impl->setUniform(name, CurrentTexture); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const float* scalarArray, std::size_t length) { - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform1fv(binder.location, static_cast(length), scalarArray)); + m_impl->setUniformArray(name, scalarArray, length); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Vec2* vectorArray, std::size_t length) { - std::vector contiguous = flatten(vectorArray, length); - - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform2fv(binder.location, static_cast(length), &contiguous[0])); + m_impl->setUniformArray(name, vectorArray, length); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Vec3* vectorArray, std::size_t length) { - std::vector contiguous = flatten(vectorArray, length); - - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform3fv(binder.location, static_cast(length), &contiguous[0])); + m_impl->setUniformArray(name, vectorArray, length); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Vec4* vectorArray, std::size_t length) { - std::vector contiguous = flatten(vectorArray, length); - - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniform4fv(binder.location, static_cast(length), &contiguous[0])); + m_impl->setUniformArray(name, vectorArray, length); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Mat3* matrixArray, std::size_t length) { - const std::size_t matrixSize = 3 * 3; - - std::vector contiguous(matrixSize * length); - for (std::size_t i = 0; i < length; ++i) - priv::copyMatrix(matrixArray[i].array, matrixSize, &contiguous[matrixSize * i]); - - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniformMatrix3fv(binder.location, static_cast(length), GL_FALSE, &contiguous[0])); + m_impl->setUniformArray(name, matrixArray, length); } //////////////////////////////////////////////////////////// void Shader::setUniformArray(const std::string& name, const Glsl::Mat4* matrixArray, std::size_t length) { - const std::size_t matrixSize = 4 * 4; - - std::vector contiguous(matrixSize * length); - for (std::size_t i = 0; i < length; ++i) - priv::copyMatrix(matrixArray[i].array, matrixSize, &contiguous[matrixSize * i]); - - UniformBinder binder(*this, name); - if (binder.location != -1) - glCheck(GLEXT_glUniformMatrix4fv(binder.location, static_cast(length), GL_FALSE, &contiguous[0])); + m_impl->setUniformArray(name, matrixArray, length); } @@ -733,39 +529,24 @@ void Shader::setParameter(const std::string& name, CurrentTextureType) //////////////////////////////////////////////////////////// unsigned int Shader::getNativeHandle() const { - return m_shaderProgram; + return m_impl->getNativeHandle(); } //////////////////////////////////////////////////////////// void Shader::bind(const Shader* shader) { - TransientContextLock lock; - - // Make sure that we can use shaders - if (!isAvailable()) + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) { - err() << "Failed to bind or unbind shader: your system doesn't support shaders " - << "(you should test Shader::isAvailable() before trying to use the Shader class)" << std::endl; - return; - } +#if !defined(SFML_OPENGL_ES) - if (shader && shader->m_shaderProgram) - { - // Enable the program - glCheck(GLEXT_glUseProgramObject(castToGlHandle(shader->m_shaderProgram))); + priv::ShaderImplDefault::bind(shader ? static_cast(shader->m_impl) : 0); - // Bind the textures - shader->bindTextures(); +#else - // Bind the current texture - if (shader->m_currentTexture != -1) - glCheck(GLEXT_glUniform1i(shader->m_currentTexture, 0)); - } - else - { - // Bind no shader - glCheck(GLEXT_glUseProgramObject(0)); + priv::ShaderImplNull::bind(shader ? static_cast(shader->m_impl) : 0); + +#endif } } @@ -773,539 +554,19 @@ void Shader::bind(const Shader* shader) //////////////////////////////////////////////////////////// bool Shader::isAvailable() { - Lock lock(isAvailableMutex); - - static bool checked = false; - static bool available = false; - - if (!checked) + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) { - checked = true; +#if !defined(SFML_OPENGL_ES) - TransientContextLock contextLock; + return priv::ShaderImplDefault::isAvailable(); - // Make sure that extensions are initialized - sf::priv::ensureExtensionsInit(); +#else - available = GLEXT_multitexture && - GLEXT_shading_language_100 && - GLEXT_shader_objects && - GLEXT_vertex_shader && - GLEXT_fragment_shader; + return priv::ShaderImplNull::isAvailable(); + +#endif } - return available; -} - - -//////////////////////////////////////////////////////////// -bool Shader::isGeometryAvailable() -{ - Lock lock(isAvailableMutex); - - static bool checked = false; - static bool available = false; - - if (!checked) - { - checked = true; - - TransientContextLock contextLock; - - // Make sure that extensions are initialized - sf::priv::ensureExtensionsInit(); - - available = isAvailable() && GLEXT_geometry_shader4; - } - - return available; -} - - -//////////////////////////////////////////////////////////// -bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode) -{ - TransientContextLock lock; - - // First make sure that we can use shaders - if (!isAvailable()) - { - err() << "Failed to create a shader: your system doesn't support shaders " - << "(you should test Shader::isAvailable() before trying to use the Shader class)" << std::endl; - return false; - } - - // Make sure we can use geometry shaders - if (geometryShaderCode && !isGeometryAvailable()) - { - err() << "Failed to create a shader: your system doesn't support geometry shaders " - << "(you should test Shader::isGeometryAvailable() before trying to use geometry shaders)" << std::endl; - return false; - } - - // Destroy the shader if it was already created - if (m_shaderProgram) - { - glCheck(GLEXT_glDeleteObject(castToGlHandle(m_shaderProgram))); - m_shaderProgram = 0; - } - - // Reset the internal state - m_currentTexture = -1; - m_textures.clear(); - m_uniforms.clear(); - - // Create the program - GLEXT_GLhandle shaderProgram; - glCheck(shaderProgram = GLEXT_glCreateProgramObject()); - - // Create the vertex shader if needed - if (vertexShaderCode) - { - // Create and compile the shader - GLEXT_GLhandle vertexShader; - glCheck(vertexShader = GLEXT_glCreateShaderObject(GLEXT_GL_VERTEX_SHADER)); - glCheck(GLEXT_glShaderSource(vertexShader, 1, &vertexShaderCode, NULL)); - glCheck(GLEXT_glCompileShader(vertexShader)); - - // Check the compile log - GLint success; - glCheck(GLEXT_glGetObjectParameteriv(vertexShader, GLEXT_GL_OBJECT_COMPILE_STATUS, &success)); - if (success == GL_FALSE) - { - char log[1024]; - glCheck(GLEXT_glGetInfoLog(vertexShader, sizeof(log), 0, log)); - err() << "Failed to compile vertex shader:" << std::endl - << log << std::endl; - glCheck(GLEXT_glDeleteObject(vertexShader)); - glCheck(GLEXT_glDeleteObject(shaderProgram)); - return false; - } - - // Attach the shader to the program, and delete it (not needed anymore) - glCheck(GLEXT_glAttachObject(shaderProgram, vertexShader)); - glCheck(GLEXT_glDeleteObject(vertexShader)); - } - - // Create the geometry shader if needed - if (geometryShaderCode) - { - // Create and compile the shader - GLEXT_GLhandle geometryShader = GLEXT_glCreateShaderObject(GLEXT_GL_GEOMETRY_SHADER); - glCheck(GLEXT_glShaderSource(geometryShader, 1, &geometryShaderCode, NULL)); - glCheck(GLEXT_glCompileShader(geometryShader)); - - // Check the compile log - GLint success; - glCheck(GLEXT_glGetObjectParameteriv(geometryShader, GLEXT_GL_OBJECT_COMPILE_STATUS, &success)); - if (success == GL_FALSE) - { - char log[1024]; - glCheck(GLEXT_glGetInfoLog(geometryShader, sizeof(log), 0, log)); - err() << "Failed to compile geometry shader:" << std::endl - << log << std::endl; - glCheck(GLEXT_glDeleteObject(geometryShader)); - glCheck(GLEXT_glDeleteObject(shaderProgram)); - return false; - } - - // Attach the shader to the program, and delete it (not needed anymore) - glCheck(GLEXT_glAttachObject(shaderProgram, geometryShader)); - glCheck(GLEXT_glDeleteObject(geometryShader)); - } - - // Create the fragment shader if needed - if (fragmentShaderCode) - { - // Create and compile the shader - GLEXT_GLhandle fragmentShader; - glCheck(fragmentShader = GLEXT_glCreateShaderObject(GLEXT_GL_FRAGMENT_SHADER)); - glCheck(GLEXT_glShaderSource(fragmentShader, 1, &fragmentShaderCode, NULL)); - glCheck(GLEXT_glCompileShader(fragmentShader)); - - // Check the compile log - GLint success; - glCheck(GLEXT_glGetObjectParameteriv(fragmentShader, GLEXT_GL_OBJECT_COMPILE_STATUS, &success)); - if (success == GL_FALSE) - { - char log[1024]; - glCheck(GLEXT_glGetInfoLog(fragmentShader, sizeof(log), 0, log)); - err() << "Failed to compile fragment shader:" << std::endl - << log << std::endl; - glCheck(GLEXT_glDeleteObject(fragmentShader)); - glCheck(GLEXT_glDeleteObject(shaderProgram)); - return false; - } - - // Attach the shader to the program, and delete it (not needed anymore) - glCheck(GLEXT_glAttachObject(shaderProgram, fragmentShader)); - glCheck(GLEXT_glDeleteObject(fragmentShader)); - } - - // Link the program - glCheck(GLEXT_glLinkProgram(shaderProgram)); - - // Check the link log - GLint success; - glCheck(GLEXT_glGetObjectParameteriv(shaderProgram, GLEXT_GL_OBJECT_LINK_STATUS, &success)); - if (success == GL_FALSE) - { - char log[1024]; - glCheck(GLEXT_glGetInfoLog(shaderProgram, sizeof(log), 0, log)); - err() << "Failed to link shader:" << std::endl - << log << std::endl; - glCheck(GLEXT_glDeleteObject(shaderProgram)); - return false; - } - - m_shaderProgram = castFromGlHandle(shaderProgram); - - // Force an OpenGL flush, so that the shader will appear updated - // in all contexts immediately (solves problems in multi-threaded apps) - glCheck(glFlush()); - - return true; -} - - -//////////////////////////////////////////////////////////// -void Shader::bindTextures() const -{ - TextureTable::const_iterator it = m_textures.begin(); - for (std::size_t i = 0; i < m_textures.size(); ++i) - { - GLint index = static_cast(i + 1); - glCheck(GLEXT_glUniform1i(it->first, index)); - glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0 + index)); - Texture::bind(it->second); - ++it; - } - - // Make sure that the texture unit which is left active is the number 0 - glCheck(GLEXT_glActiveTexture(GLEXT_GL_TEXTURE0)); -} - - -//////////////////////////////////////////////////////////// -int Shader::getUniformLocation(const std::string& name) -{ - // Check the cache - UniformTable::const_iterator it = m_uniforms.find(name); - if (it != m_uniforms.end()) - { - // Already in cache, return it - return it->second; - } - else - { - // Not in cache, request the location from OpenGL - int location = GLEXT_glGetUniformLocation(castToGlHandle(m_shaderProgram), name.c_str()); - m_uniforms.insert(std::make_pair(name, location)); - - if (location == -1) - err() << "Uniform \"" << name << "\" not found in shader" << std::endl; - - return location; - } -} - -} // namespace sf - -#else // SFML_OPENGL_ES - -// OpenGL ES 1 doesn't support GLSL shaders at all, we have to provide an empty implementation - -namespace sf -{ -//////////////////////////////////////////////////////////// -Shader::CurrentTextureType Shader::CurrentTexture; - - -//////////////////////////////////////////////////////////// -Shader::Shader() : -m_shaderProgram (0), -m_currentTexture(-1) -{ -} - - -//////////////////////////////////////////////////////////// -Shader::~Shader() -{ -} - - -//////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::string& filename, Type type) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::string& vertexShaderFilename, const std::string& fragmentShaderFilename) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -bool Shader::loadFromFile(const std::string& vertexShaderFilename, const std::string& geometryShaderFilename, const std::string& fragmentShaderFilename) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& shader, Type type) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& fragmentShader) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -bool Shader::loadFromMemory(const std::string& vertexShader, const std::string& geometryShader, const std::string& fragmentShader) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -bool Shader::loadFromStream(InputStream& stream, Type type) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& fragmentShaderStream) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -bool Shader::loadFromStream(InputStream& vertexShaderStream, InputStream& geometryShaderStream, InputStream& fragmentShaderStream) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, float x) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Vec2& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Vec3& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Vec4& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, int x) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Ivec2& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Ivec3& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Ivec4& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, bool x) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Bvec2& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Bvec3& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Bvec4& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Mat3& matrix) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Glsl::Mat4& matrix) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, const Texture& texture) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniform(const std::string& name, CurrentTextureType) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniformArray(const std::string& name, const float* scalarArray, std::size_t length) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniformArray(const std::string& name, const Glsl::Vec2* vectorArray, std::size_t length) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniformArray(const std::string& name, const Glsl::Vec3* vectorArray, std::size_t length) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniformArray(const std::string& name, const Glsl::Vec4* vectorArray, std::size_t length) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniformArray(const std::string& name, const Glsl::Mat3* matrixArray, std::size_t length) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setUniformArray(const std::string& name, const Glsl::Mat4* matrixArray, std::size_t length) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, float x) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, float x, float y) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, float x, float y, float z) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, float x, float y, float z, float w) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const Vector2f& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const Vector3f& v) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const Color& color) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const Transform& transform) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, const Texture& texture) -{ -} - - -//////////////////////////////////////////////////////////// -void Shader::setParameter(const std::string& name, CurrentTextureType) -{ -} - - -//////////////////////////////////////////////////////////// -unsigned int Shader::getNativeHandle() const -{ - return 0; -} - - -//////////////////////////////////////////////////////////// -void Shader::bind(const Shader* shader) -{ -} - - -//////////////////////////////////////////////////////////// -bool Shader::isAvailable() -{ return false; } @@ -1313,22 +574,20 @@ bool Shader::isAvailable() //////////////////////////////////////////////////////////// bool Shader::isGeometryAvailable() { + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + { +#if !defined(SFML_OPENGL_ES) + + return priv::ShaderImplDefault::isGeometryAvailable(); + +#else + + return priv::ShaderImplNull::isGeometryAvailable(); + +#endif + } + return false; } - -//////////////////////////////////////////////////////////// -bool Shader::compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode) -{ - return false; -} - - -//////////////////////////////////////////////////////////// -void Shader::bindTextures() const -{ -} - } // namespace sf - -#endif // SFML_OPENGL_ES diff --git a/src/SFML/Graphics/TextureSaver.cpp b/src/SFML/Graphics/ShaderImpl.cpp similarity index 82% rename from src/SFML/Graphics/TextureSaver.cpp rename to src/SFML/Graphics/ShaderImpl.cpp index 1a71c0893..3ff9c5a53 100644 --- a/src/SFML/Graphics/TextureSaver.cpp +++ b/src/SFML/Graphics/ShaderImpl.cpp @@ -25,7 +25,7 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// -#include +#include namespace sf @@ -33,16 +33,9 @@ namespace sf namespace priv { //////////////////////////////////////////////////////////// -TextureSaver::TextureSaver() +ShaderImpl::~ShaderImpl() { - glCheck(glGetIntegerv(GL_TEXTURE_BINDING_2D, &m_textureBinding)); -} - - -//////////////////////////////////////////////////////////// -TextureSaver::~TextureSaver() -{ - glCheck(glBindTexture(GL_TEXTURE_2D, m_textureBinding)); + // Nothing to do } } // namespace priv diff --git a/src/SFML/Graphics/ShaderImpl.hpp b/src/SFML/Graphics/ShaderImpl.hpp new file mode 100644 index 000000000..104b10ca6 --- /dev/null +++ b/src/SFML/Graphics/ShaderImpl.hpp @@ -0,0 +1,251 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_SHADER_IMPL_HPP +#define SFML_SHADER_IMPL_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include + + +namespace sf +{ +class Texture; + +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Abstract base class for shader implementations +/// +//////////////////////////////////////////////////////////// +class ShaderImpl : NonCopyable +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + virtual ~ShaderImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p float uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param x Value of the float scalar + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, float x) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p vec2 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the vec2 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Vec2& vector) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p vec3 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the vec3 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Vec3& vector) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p vec4 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the vec4 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Vec4& vector) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p int uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param x Value of the int scalar + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, int x) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p ivec2 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the ivec2 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Ivec2& vector) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p ivec3 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the ivec3 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Ivec3& vector) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p ivec4 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the ivec4 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Ivec4& vector) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p mat3 matrix + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrix Value of the mat3 matrix + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Mat3& matrix) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p mat4 matrix + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrix Value of the mat4 matrix + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Mat4& matrix) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify a texture as \p sampler2D uniform + /// + /// \param name Name of the texture in the shader + /// \param texture Texture to assign + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Texture& texture) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify current texture as \p sampler2D uniform + /// + /// \param name Name of the texture in the shader + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, Shader::CurrentTextureType) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p float[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param scalarArray pointer to array of \p float values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const float* scalarArray, std::size_t length) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p vec2[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vectorArray pointer to array of \p vec2 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Vec2* vectorArray, std::size_t length) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p vec3[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vectorArray pointer to array of \p vec3 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Vec3* vectorArray, std::size_t length) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p vec4[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vectorArray pointer to array of \p vec4 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Vec4* vectorArray, std::size_t length) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p mat3[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrixArray pointer to array of \p mat3 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Mat3* matrixArray, std::size_t length) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p mat4[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrixArray pointer to array of \p mat4 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Mat4* matrixArray, std::size_t length) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get the underlying OpenGL handle of the shader. + /// + /// \return OpenGL handle of the shader or 0 if not yet loaded + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getNativeHandle() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Compile the shader(s) and create the program + /// + /// \param vertexShaderCode Source code of the vertex shader + /// \param geometryShaderCode Source code of the geometry shader + /// \param fragmentShaderCode Source code of the fragment shader + /// + /// \return True on success, false if any error happened + /// + //////////////////////////////////////////////////////////// + virtual bool compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode) = 0; +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_SHADER_IMPL_HPP diff --git a/src/SFML/Graphics/ShaderImplNull.cpp b/src/SFML/Graphics/ShaderImplNull.cpp new file mode 100644 index 000000000..b85dfc2c6 --- /dev/null +++ b/src/SFML/Graphics/ShaderImplNull.cpp @@ -0,0 +1,179 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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 +{ +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, float x) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, const Glsl::Vec2& v) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, const Glsl::Vec3& v) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, const Glsl::Vec4& v) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, int x) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, const Glsl::Ivec2& v) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, const Glsl::Ivec3& v) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, const Glsl::Ivec4& v) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, const Glsl::Mat3& matrix) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, const Glsl::Mat4& matrix) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, const Texture& texture) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniform(const std::string& name, Shader::CurrentTextureType) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniformArray(const std::string& name, const float* scalarArray, std::size_t length) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniformArray(const std::string& name, const Glsl::Vec2* vectorArray, std::size_t length) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniformArray(const std::string& name, const Glsl::Vec3* vectorArray, std::size_t length) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniformArray(const std::string& name, const Glsl::Vec4* vectorArray, std::size_t length) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniformArray(const std::string& name, const Glsl::Mat3* matrixArray, std::size_t length) +{ +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::setUniformArray(const std::string& name, const Glsl::Mat4* matrixArray, std::size_t length) +{ +} + + +//////////////////////////////////////////////////////////// +unsigned int ShaderImplNull::getNativeHandle() const +{ + return 0; +} + + +//////////////////////////////////////////////////////////// +void ShaderImplNull::bind(const ShaderImplNull* shader) +{ +} + + +//////////////////////////////////////////////////////////// +bool ShaderImplNull::isAvailable() +{ + return false; +} + + +//////////////////////////////////////////////////////////// +bool ShaderImplNull::isGeometryAvailable() +{ + return false; +} + + +//////////////////////////////////////////////////////////// +bool ShaderImplNull::compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode) +{ + return false; +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Graphics/ShaderImplNull.hpp b/src/SFML/Graphics/ShaderImplNull.hpp new file mode 100644 index 000000000..313de37c0 --- /dev/null +++ b/src/SFML/Graphics/ShaderImplNull.hpp @@ -0,0 +1,272 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_SHADER_IMPL_NULL_HPP +#define SFML_SHADER_IMPL_NULL_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include + + +namespace sf +{ +class Texture; + +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Null specialization of ShaderImpl, +/// for platforms without shader support +/// +//////////////////////////////////////////////////////////// +class ShaderImplNull : public ShaderImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Bind a shader for rendering + /// + /// \param shader Shader to bind, can be null to use no shader + /// + //////////////////////////////////////////////////////////// + static void bind(const ShaderImplNull* shader); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports shaders + /// + /// \return True if shaders are supported, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isAvailable(); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports geometry shaders + /// + /// \return True if geometry shaders are supported, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isGeometryAvailable(); + +private: + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p float uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param x Value of the float scalar + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, float x); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p vec2 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the vec2 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Vec2& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p vec3 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the vec3 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Vec3& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p vec4 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the vec4 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Vec4& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p int uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param x Value of the int scalar + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, int x); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p ivec2 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the ivec2 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Ivec2& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p ivec3 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the ivec3 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Ivec3& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p ivec4 uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vector Value of the ivec4 vector + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Ivec4& vector); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p mat3 matrix + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrix Value of the mat3 matrix + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Mat3& matrix); + + //////////////////////////////////////////////////////////// + /// \brief Specify value for \p mat4 matrix + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrix Value of the mat4 matrix + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Glsl::Mat4& matrix); + + //////////////////////////////////////////////////////////// + /// \brief Specify a texture as \p sampler2D uniform + /// + /// \param name Name of the texture in the shader + /// \param texture Texture to assign + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, const Texture& texture); + + //////////////////////////////////////////////////////////// + /// \brief Specify current texture as \p sampler2D uniform + /// + /// \param name Name of the texture in the shader + /// + //////////////////////////////////////////////////////////// + virtual void setUniform(const std::string& name, Shader::CurrentTextureType); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p float[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param scalarArray pointer to array of \p float values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const float* scalarArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p vec2[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vectorArray pointer to array of \p vec2 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Vec2* vectorArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p vec3[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vectorArray pointer to array of \p vec3 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Vec3* vectorArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p vec4[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param vectorArray pointer to array of \p vec4 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Vec4* vectorArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p mat3[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrixArray pointer to array of \p mat3 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Mat3* matrixArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Specify values for \p mat4[] array uniform + /// + /// \param name Name of the uniform variable in GLSL + /// \param matrixArray pointer to array of \p mat4 values + /// \param length Number of elements in the array + /// + //////////////////////////////////////////////////////////// + virtual void setUniformArray(const std::string& name, const Glsl::Mat4* matrixArray, std::size_t length); + + //////////////////////////////////////////////////////////// + /// \brief Get the underlying OpenGL handle of the shader. + /// + /// \return OpenGL handle of the shader or 0 if not yet loaded + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getNativeHandle() const; + + //////////////////////////////////////////////////////////// + /// \brief Compile the shader(s) and create the program + /// + /// \param vertexShaderCode Source code of the vertex shader + /// \param geometryShaderCode Source code of the geometry shader + /// \param fragmentShaderCode Source code of the fragment shader + /// + /// \return True on success, false if any error happened + /// + //////////////////////////////////////////////////////////// + virtual bool compile(const char* vertexShaderCode, const char* geometryShaderCode, const char* fragmentShaderCode); +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_SHADER_IMPL_NULL_HPP diff --git a/src/SFML/Graphics/Text.cpp b/src/SFML/Graphics/Text.cpp index 7cb4f889b..2e8ffa993 100644 --- a/src/SFML/Graphics/Text.cpp +++ b/src/SFML/Graphics/Text.cpp @@ -27,6 +27,8 @@ //////////////////////////////////////////////////////////// #include #include +#include +#include #include #include @@ -395,12 +397,19 @@ void Text::ensureGeometryUpdate() const if (!m_font) return; - // Do nothing, if geometry has not changed and the font texture has not changed - if (!m_geometryNeedUpdate && m_font->getTexture(m_characterSize).m_cacheId == m_fontTextureId) - return; + Uint64 cacheId = 0; + + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + { + cacheId = static_cast(m_font->getTexture(m_characterSize).m_impl)->m_cacheId; + + // Do nothing, if geometry has not changed and the font texture has not changed + if (!m_geometryNeedUpdate && (cacheId == m_fontTextureId)) + return; + } // Save the current fonts texture id - m_fontTextureId = m_font->getTexture(m_characterSize).m_cacheId; + m_fontTextureId = cacheId; // Mark geometry as updated m_geometryNeedUpdate = false; diff --git a/src/SFML/Graphics/Texture.cpp b/src/SFML/Graphics/Texture.cpp index a139115d6..5f6d390d4 100644 --- a/src/SFML/Graphics/Texture.cpp +++ b/src/SFML/Graphics/Texture.cpp @@ -26,191 +26,42 @@ // Headers //////////////////////////////////////////////////////////// #include +#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace -{ - sf::Mutex idMutex; - sf::Mutex maximumSizeMutex; - - // Thread-safe unique identifier generator, - // is used for states cache (see RenderTarget) - sf::Uint64 getUniqueId() - { - sf::Lock lock(idMutex); - - static sf::Uint64 id = 1; // start at 1, zero is "no texture" - - return id++; - } -} namespace sf { //////////////////////////////////////////////////////////// Texture::Texture() : -m_size (0, 0), -m_actualSize (0, 0), -m_texture (0), -m_isSmooth (false), -m_sRgb (false), -m_isRepeated (false), -m_pixelsFlipped(false), -m_fboAttachment(false), -m_hasMipmap (false), -m_cacheId (getUniqueId()) +m_impl(NULL) { + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + m_impl = new priv::TextureImplDefault; } //////////////////////////////////////////////////////////// Texture::Texture(const Texture& copy) : -m_size (0, 0), -m_actualSize (0, 0), -m_texture (0), -m_isSmooth (copy.m_isSmooth), -m_sRgb (copy.m_sRgb), -m_isRepeated (copy.m_isRepeated), -m_pixelsFlipped(false), -m_fboAttachment(false), -m_hasMipmap (false), -m_cacheId (getUniqueId()) +m_impl(NULL) { - if (copy.m_texture) - { - if (create(copy.getSize().x, copy.getSize().y)) - { - update(copy); - } - else - { - err() << "Failed to copy texture, failed to create new texture" << std::endl; - } - } + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + m_impl = new priv::TextureImplDefault(*static_cast(copy.m_impl)); } //////////////////////////////////////////////////////////// Texture::~Texture() { - // Destroy the OpenGL texture - if (m_texture) - { - TransientContextLock lock; - - GLuint texture = static_cast(m_texture); - glCheck(glDeleteTextures(1, &texture)); - } + delete m_impl; } //////////////////////////////////////////////////////////// bool Texture::create(unsigned int width, unsigned int height) { - // Check if texture parameters are valid before creating it - if ((width == 0) || (height == 0)) - { - err() << "Failed to create texture, invalid size (" << width << "x" << height << ")" << std::endl; - return false; - } - - TransientContextLock lock; - - // Make sure that extensions are initialized - priv::ensureExtensionsInit(); - - // Compute the internal texture dimensions depending on NPOT textures support - Vector2u actualSize(getValidSize(width), getValidSize(height)); - - // Check the maximum texture size - unsigned int maxSize = getMaximumSize(); - if ((actualSize.x > maxSize) || (actualSize.y > maxSize)) - { - err() << "Failed to create texture, its internal size is too high " - << "(" << actualSize.x << "x" << actualSize.y << ", " - << "maximum is " << maxSize << "x" << maxSize << ")" - << std::endl; - return false; - } - - // All the validity checks passed, we can store the new texture settings - m_size.x = width; - m_size.y = height; - m_actualSize = actualSize; - m_pixelsFlipped = false; - m_fboAttachment = false; - - // Create the OpenGL texture if it doesn't exist yet - if (!m_texture) - { - GLuint texture; - glCheck(glGenTextures(1, &texture)); - m_texture = static_cast(texture); - } - - // Make sure that the current texture binding will be preserved - priv::TextureSaver save; - - static bool textureEdgeClamp = GLEXT_texture_edge_clamp; - - if (!m_isRepeated && !textureEdgeClamp) - { - static bool warned = false; - - if (!warned) - { - err() << "OpenGL extension SGIS_texture_edge_clamp unavailable" << std::endl; - err() << "Artifacts may occur along texture edges" << std::endl; - err() << "Ensure that hardware acceleration is enabled if available" << std::endl; - - warned = true; - } - } - - static bool textureSrgb = GLEXT_texture_sRGB; - - if (m_sRgb && !textureSrgb) - { - static bool warned = false; - - if (!warned) - { -#ifndef SFML_OPENGL_ES - err() << "OpenGL extension EXT_texture_sRGB unavailable" << std::endl; -#else - err() << "OpenGL ES extension EXT_sRGB unavailable" << std::endl; -#endif - err() << "Automatic sRGB to linear conversion disabled" << std::endl; - - warned = true; - } - - m_sRgb = false; - } - - // Initialize the texture - glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); - glCheck(glTexImage2D(GL_TEXTURE_2D, 0, (m_sRgb ? GLEXT_GL_SRGB8_ALPHA8 : GL_RGBA), m_actualSize.x, m_actualSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL)); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_isRepeated ? GL_REPEAT : (textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP))); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_isRepeated ? GL_REPEAT : (textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP))); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); - m_cacheId = getUniqueId(); - - m_hasMipmap = false; - - return true; + return m_impl->create(width, height); } @@ -241,157 +92,21 @@ bool Texture::loadFromStream(InputStream& stream, const IntRect& area) //////////////////////////////////////////////////////////// bool Texture::loadFromImage(const Image& image, const IntRect& area) { - // Retrieve the image size - int width = static_cast(image.getSize().x); - int height = static_cast(image.getSize().y); - - // Load the entire image if the source area is either empty or contains the whole image - if (area.width == 0 || (area.height == 0) || - ((area.left <= 0) && (area.top <= 0) && (area.width >= width) && (area.height >= height))) - { - // Load the entire image - if (create(image.getSize().x, image.getSize().y)) - { - update(image); - - return true; - } - else - { - return false; - } - } - else - { - // Load a sub-area of the image - - // Adjust the rectangle to the size of the image - IntRect rectangle = area; - if (rectangle.left < 0) rectangle.left = 0; - if (rectangle.top < 0) rectangle.top = 0; - if (rectangle.left + rectangle.width > width) rectangle.width = width - rectangle.left; - if (rectangle.top + rectangle.height > height) rectangle.height = height - rectangle.top; - - // Create the texture and upload the pixels - if (create(rectangle.width, rectangle.height)) - { - TransientContextLock lock; - - // 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, m_texture)); - for (int i = 0; i < rectangle.height; ++i) - { - glCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i, rectangle.width, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); - pixels += 4 * width; - } - - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); - m_hasMipmap = false; - - // Force an OpenGL flush, so that the texture will appear updated - // in all contexts immediately (solves problems in multi-threaded apps) - glCheck(glFlush()); - - return true; - } - else - { - return false; - } - } + return m_impl->loadFromImage(image, area); } //////////////////////////////////////////////////////////// Vector2u Texture::getSize() const { - return m_size; + return m_impl->getSize(); } //////////////////////////////////////////////////////////// Image Texture::copyToImage() const { - // Easy case: empty texture - if (!m_texture) - return Image(); - - TransientContextLock lock; - - // Make sure that the current texture binding will be preserved - priv::TextureSaver save; - - // Create an array of pixels - std::vector pixels(m_size.x * m_size.y * 4); - -#ifdef SFML_OPENGL_ES - - // OpenGL ES doesn't have the glGetTexImage function, the only way to read - // from a texture is to bind it to a FBO and use glReadPixels - GLuint frameBuffer = 0; - glCheck(GLEXT_glGenFramebuffers(1, &frameBuffer)); - if (frameBuffer) - { - GLint previousFrameBuffer; - glCheck(glGetIntegerv(GLEXT_GL_FRAMEBUFFER_BINDING, &previousFrameBuffer)); - - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, frameBuffer)); - glCheck(GLEXT_glFramebufferTexture2D(GLEXT_GL_FRAMEBUFFER, GLEXT_GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_texture, 0)); - glCheck(glReadPixels(0, 0, m_size.x, m_size.y, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0])); - glCheck(GLEXT_glDeleteFramebuffers(1, &frameBuffer)); - - glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, previousFrameBuffer)); - } - -#else - - if ((m_size == m_actualSize) && !m_pixelsFlipped) - { - // Texture is not padded nor flipped, we can use a direct copy - glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); - glCheck(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0])); - } - else - { - // Texture is either padded or flipped, we have to use a slower algorithm - - // All the pixels will first be copied to a temporary array - std::vector allPixels(m_actualSize.x * m_actualSize.y * 4); - glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); - glCheck(glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, &allPixels[0])); - - // Then we copy the useful pixels from the temporary array to the final one - const Uint8* src = &allPixels[0]; - Uint8* dst = &pixels[0]; - int srcPitch = m_actualSize.x * 4; - int dstPitch = m_size.x * 4; - - // Handle the case where source pixels are flipped vertically - if (m_pixelsFlipped) - { - src += srcPitch * (m_size.y - 1); - srcPitch = -srcPitch; - } - - for (unsigned int i = 0; i < m_size.y; ++i) - { - std::memcpy(dst, src, dstPitch); - src += srcPitch; - dst += dstPitch; - } - } - -#endif // SFML_OPENGL_ES - - // Create the image - Image image; - image.create(m_size.x, m_size.y, &pixels[0]); - - return image; + return m_impl->copyToImage(); } @@ -399,35 +114,14 @@ Image Texture::copyToImage() const void Texture::update(const Uint8* pixels) { // Update the whole texture - update(pixels, m_size.x, m_size.y, 0, 0); + update(pixels, m_impl->getSize().x, m_impl->getSize().y, 0, 0); } //////////////////////////////////////////////////////////// void Texture::update(const Uint8* pixels, unsigned int width, unsigned int height, unsigned int x, unsigned int y) { - assert(x + width <= m_size.x); - assert(y + height <= m_size.y); - - if (pixels && m_texture) - { - TransientContextLock lock; - - // 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, m_texture)); - glCheck(glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); - m_hasMipmap = false; - m_pixelsFlipped = false; - m_cacheId = getUniqueId(); - - // Force an OpenGL flush, so that the texture data will appear updated - // in all contexts immediately (solves problems in multi-threaded apps) - glCheck(glFlush()); - } + m_impl->update(pixels, width, height, x, y); } @@ -442,101 +136,7 @@ void Texture::update(const Texture& texture) //////////////////////////////////////////////////////////// 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.y <= 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, texture.m_pixelsFlipped ? texture.m_size.y : 0, texture.m_size.x, texture.m_pixelsFlipped ? 0 : texture.m_size.y, // Source rectangle, flip y if source is flipped - x, y, x + texture.m_size.x, y + texture.m_size.y, // Destination rectangle - 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)); - - // Make sure that the current texture binding will be preserved - priv::TextureSaver save; - - // Set the parameters of this texture - glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); - m_hasMipmap = false; - m_pixelsFlipped = false; - m_cacheId = getUniqueId(); - - // Force an OpenGL flush, so that the texture data will appear updated - // in all contexts immediately (solves problems in multi-threaded apps) - glCheck(glFlush()); - - return; - } - -#endif // SFML_OPENGL_ES - - update(texture.copyToImage(), x, y); + m_impl->update(*texture.m_impl, x, y); } @@ -565,246 +165,71 @@ void Texture::update(const Window& window) //////////////////////////////////////////////////////////// void Texture::update(const Window& window, unsigned int x, unsigned int y) { - assert(x + window.getSize().x <= m_size.x); - assert(y + window.getSize().y <= m_size.y); - - if (m_texture && window.setActive(true)) - { - TransientContextLock lock; - - // 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, m_texture)); - glCheck(glCopyTexSubImage2D(GL_TEXTURE_2D, 0, x, y, 0, 0, window.getSize().x, window.getSize().y)); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); - m_hasMipmap = false; - m_pixelsFlipped = true; - m_cacheId = getUniqueId(); - - // Force an OpenGL flush, so that the texture will appear updated - // in all contexts immediately (solves problems in multi-threaded apps) - glCheck(glFlush()); - } + m_impl->update(window, x, y); } //////////////////////////////////////////////////////////// void Texture::setSmooth(bool smooth) { - if (smooth != m_isSmooth) - { - m_isSmooth = smooth; - - if (m_texture) - { - TransientContextLock lock; - - // Make sure that the current texture binding will be preserved - priv::TextureSaver save; - - glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); - - if (m_hasMipmap) - { - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR)); - } - else - { - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); - } - } - } + m_impl->setSmooth(smooth); } //////////////////////////////////////////////////////////// bool Texture::isSmooth() const { - return m_isSmooth; + return m_impl->isSmooth(); } //////////////////////////////////////////////////////////// void Texture::setSrgb(bool sRgb) { - m_sRgb = sRgb; + m_impl->setSrgb(sRgb); } //////////////////////////////////////////////////////////// bool Texture::isSrgb() const { - return m_sRgb; + return m_impl->isSrgb(); } //////////////////////////////////////////////////////////// void Texture::setRepeated(bool repeated) { - if (repeated != m_isRepeated) - { - m_isRepeated = repeated; - - if (m_texture) - { - TransientContextLock lock; - - // Make sure that the current texture binding will be preserved - priv::TextureSaver save; - - static bool textureEdgeClamp = GLEXT_texture_edge_clamp; - - if (!m_isRepeated && !textureEdgeClamp) - { - static bool warned = false; - - if (!warned) - { - err() << "OpenGL extension SGIS_texture_edge_clamp unavailable" << std::endl; - err() << "Artifacts may occur along texture edges" << std::endl; - err() << "Ensure that hardware acceleration is enabled if available" << std::endl; - - warned = true; - } - } - - glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_isRepeated ? GL_REPEAT : (textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP))); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_isRepeated ? GL_REPEAT : (textureEdgeClamp ? GLEXT_GL_CLAMP_TO_EDGE : GLEXT_GL_CLAMP))); - } - } + m_impl->setRepeated(repeated); } //////////////////////////////////////////////////////////// bool Texture::isRepeated() const { - return m_isRepeated; + return m_impl->isRepeated(); } //////////////////////////////////////////////////////////// bool Texture::generateMipmap() { - if (!m_texture) - return false; - - TransientContextLock lock; - - // Make sure that extensions are initialized - priv::ensureExtensionsInit(); - - if (!GLEXT_framebuffer_object) - return false; - - // Make sure that the current texture binding will be preserved - priv::TextureSaver save; - - glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); - glCheck(GLEXT_glGenerateMipmap(GL_TEXTURE_2D)); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR)); - - m_hasMipmap = true; - - return true; -} - - -//////////////////////////////////////////////////////////// -void Texture::invalidateMipmap() -{ - if (!m_hasMipmap) - return; - - TransientContextLock lock; - - // Make sure that the current texture binding will be preserved - priv::TextureSaver save; - - glCheck(glBindTexture(GL_TEXTURE_2D, m_texture)); - glCheck(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_isSmooth ? GL_LINEAR : GL_NEAREST)); - - m_hasMipmap = false; + return m_impl->generateMipmap(); } //////////////////////////////////////////////////////////// void Texture::bind(const Texture* texture, CoordinateType coordinateType) { - TransientContextLock lock; - - if (texture && texture->m_texture) - { - // Bind the texture - glCheck(glBindTexture(GL_TEXTURE_2D, texture->m_texture)); - - // Check if we need to define a special texture matrix - if ((coordinateType == Pixels) || texture->m_pixelsFlipped) - { - GLfloat matrix[16] = {1.f, 0.f, 0.f, 0.f, - 0.f, 1.f, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - 0.f, 0.f, 0.f, 1.f}; - - // If non-normalized coordinates (= pixels) are requested, we need to - // setup scale factors that convert the range [0 .. size] to [0 .. 1] - if (coordinateType == Pixels) - { - matrix[0] = 1.f / texture->m_actualSize.x; - matrix[5] = 1.f / texture->m_actualSize.y; - } - - // If pixels are flipped we must invert the Y axis - if (texture->m_pixelsFlipped) - { - matrix[5] = -matrix[5]; - matrix[13] = static_cast(texture->m_size.y) / texture->m_actualSize.y; - } - - // 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)); - } - } - else - { - // Bind no texture - glCheck(glBindTexture(GL_TEXTURE_2D, 0)); - - // Reset the texture matrix - glCheck(glMatrixMode(GL_TEXTURE)); - glCheck(glLoadIdentity()); - - // Go back to model-view mode (sf::RenderTarget relies on it) - glCheck(glMatrixMode(GL_MODELVIEW)); - } + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + priv::TextureImplDefault::bind(texture ? static_cast(texture->m_impl) : 0, coordinateType); } //////////////////////////////////////////////////////////// unsigned int Texture::getMaximumSize() { - Lock lock(maximumSizeMutex); - - static bool checked = false; - static GLint size = 0; - - if (!checked) - { - checked = true; - - TransientContextLock lock; - - glCheck(glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size)); - } - - return static_cast(size); + return priv::TextureImplDefault::getMaximumSize(); } @@ -822,45 +247,14 @@ Texture& Texture::operator =(const Texture& right) //////////////////////////////////////////////////////////// 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(); + std::swap(m_impl, right.m_impl); } //////////////////////////////////////////////////////////// unsigned int Texture::getNativeHandle() const { - return m_texture; -} - - -//////////////////////////////////////////////////////////// -unsigned int Texture::getValidSize(unsigned int size) -{ - if (GLEXT_texture_non_power_of_two) - { - // If hardware supports NPOT textures, then just return the unmodified size - return size; - } - else - { - // If hardware doesn't support NPOT textures, we calculate the nearest power of two - unsigned int powerOfTwo = 1; - while (powerOfTwo < size) - powerOfTwo *= 2; - - return powerOfTwo; - } + return 0; } } // namespace sf diff --git a/src/SFML/Graphics/TextureImpl.cpp b/src/SFML/Graphics/TextureImpl.cpp new file mode 100644 index 000000000..5793a4553 --- /dev/null +++ b/src/SFML/Graphics/TextureImpl.cpp @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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 +{ +//////////////////////////////////////////////////////////// +TextureImpl::~TextureImpl() +{ + // Nothing to do +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Graphics/TextureImpl.hpp b/src/SFML/Graphics/TextureImpl.hpp new file mode 100644 index 000000000..4c094f057 --- /dev/null +++ b/src/SFML/Graphics/TextureImpl.hpp @@ -0,0 +1,213 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_TEXTURE_IMPL_HPP +#define SFML_TEXTURE_IMPL_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + + +namespace sf +{ +class Image; +class Window; + +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Image living on the graphics card that can be used for drawing +/// +//////////////////////////////////////////////////////////// +class TextureImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + virtual ~TextureImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Create the texture implementation + /// + /// \param width Width of the texture + /// \param height Height of the texture + /// + /// \return True if creation was successful + /// + //////////////////////////////////////////////////////////// + virtual bool create(unsigned int width, unsigned int height) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Load the texture from an image + /// + /// \param image Image to load into the texture + /// \param area Area of the image to load + /// + /// \return True if loading was successful + /// + /// \see loadFromFile, loadFromMemory + /// + //////////////////////////////////////////////////////////// + virtual bool loadFromImage(const Image& image, const IntRect& area) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Return the size of the texture + /// + /// \return Size in pixels + /// + //////////////////////////////////////////////////////////// + virtual Vector2u getSize() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Copy the texture pixels to an image + /// + /// \return Image containing the texture's pixels + /// + /// \see loadFromImage + /// + //////////////////////////////////////////////////////////// + virtual Image copyToImage() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Update a part of the texture from an array of pixels + /// + /// \param pixels Array of pixels to copy to the texture + /// \param width Width of the pixel region contained in \a pixels + /// \param height Height of the pixel region contained in \a pixels + /// \param x X offset in the texture where to copy the source pixels + /// \param y Y offset in the texture where to copy the source pixels + /// + //////////////////////////////////////////////////////////// + virtual void update(const Uint8* pixels, unsigned int width, unsigned int height, unsigned int x, unsigned int y) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Update a part of this texture from another texture + /// + /// \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 + /// + //////////////////////////////////////////////////////////// + virtual void update(const TextureImpl& texture, unsigned int x, unsigned int y) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Update a part of the texture from the contents of a window + /// + /// \param window Window to copy to the texture + /// \param x X offset in the texture where to copy the source window + /// \param y Y offset in the texture where to copy the source window + /// + //////////////////////////////////////////////////////////// + virtual void update(const Window& window, unsigned int x, unsigned int y) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable the smooth filter + /// + /// \param smooth True to enable smoothing, false to disable it + /// + /// \see isSmooth + /// + //////////////////////////////////////////////////////////// + virtual void setSmooth(bool smooth) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Tell whether the smooth filter is enabled or not + /// + /// \return True if smoothing is enabled, false if it is disabled + /// + /// \see setSmooth + /// + //////////////////////////////////////////////////////////// + virtual bool isSmooth() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable conversion from sRGB + /// + /// \param sRgb True to enable sRGB conversion, false to disable it + /// + /// \see isSrgb + /// + //////////////////////////////////////////////////////////// + virtual void setSrgb(bool sRgb) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Tell whether the texture source is converted from sRGB or not + /// + /// \return True if the texture source is converted from sRGB, false if not + /// + /// \see setSrgb + /// + //////////////////////////////////////////////////////////// + virtual bool isSrgb() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable repeating + /// + /// \param repeated True to repeat the texture, false to disable repeating + /// + /// \see isRepeated + /// + //////////////////////////////////////////////////////////// + virtual void setRepeated(bool repeated) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Tell whether the texture is repeated or not + /// + /// \return True if repeat mode is enabled, false if it is disabled + /// + /// \see setRepeated + /// + //////////////////////////////////////////////////////////// + virtual bool isRepeated() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Generate a mipmap using the current texture data + /// + /// \return True if mipmap generation was successful, false if unsuccessful + /// + //////////////////////////////////////////////////////////// + virtual bool generateMipmap() = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get the underlying OpenGL handle of the texture. + /// + /// \return OpenGL handle of the texture or 0 if not yet created + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getNativeHandle() const = 0; +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_TEXTURE_IMPL_HPP diff --git a/src/SFML/Graphics/VertexBuffer.cpp b/src/SFML/Graphics/VertexBuffer.cpp index 4d5497c8f..9002f35e4 100644 --- a/src/SFML/Graphics/VertexBuffer.cpp +++ b/src/SFML/Graphics/VertexBuffer.cpp @@ -26,82 +26,60 @@ // Headers //////////////////////////////////////////////////////////// #include +#include +#include #include -#include -#include -#include -#include #include -#include - -namespace -{ - sf::Mutex isAvailableMutex; - - GLenum usageToGlEnum(sf::VertexBuffer::Usage usage) - { - switch (usage) - { - case sf::VertexBuffer::Static: return GLEXT_GL_STATIC_DRAW; - case sf::VertexBuffer::Dynamic: return GLEXT_GL_DYNAMIC_DRAW; - default: return GLEXT_GL_STREAM_DRAW; - } - } -} namespace sf { //////////////////////////////////////////////////////////// VertexBuffer::VertexBuffer() : -m_buffer (0), -m_size (0), -m_primitiveType(Points), -m_usage (Stream) +m_primitiveType(Points) { + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + m_impl = new priv::VertexBufferImplDefault; } //////////////////////////////////////////////////////////// VertexBuffer::VertexBuffer(PrimitiveType type) : -m_buffer (0), -m_size (0), -m_primitiveType(type), -m_usage (Stream) +m_primitiveType(type) { + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + m_impl = new priv::VertexBufferImplDefault; } //////////////////////////////////////////////////////////// VertexBuffer::VertexBuffer(VertexBuffer::Usage usage) : -m_buffer (0), -m_size (0), -m_primitiveType(Points), -m_usage (usage) +m_primitiveType(Points) { + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + m_impl = new priv::VertexBufferImplDefault(usage); } //////////////////////////////////////////////////////////// VertexBuffer::VertexBuffer(PrimitiveType type, VertexBuffer::Usage usage) : -m_buffer (0), -m_size (0), -m_primitiveType(type), -m_usage (usage) +m_primitiveType(type) { + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + m_impl = new priv::VertexBufferImplDefault(usage); } //////////////////////////////////////////////////////////// VertexBuffer::VertexBuffer(const VertexBuffer& copy) : -m_buffer (0), -m_size (0), -m_primitiveType(copy.m_primitiveType), -m_usage (copy.m_usage) +m_primitiveType(copy.m_primitiveType) { - if (copy.m_buffer && copy.m_size) + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + m_impl = new priv::VertexBufferImplDefault(copy.m_impl->getUsage()); + + if (copy.getNativeHandle() && copy.getVertexCount()) { - if (!create(copy.m_size)) + if (!create(copy.getVertexCount())) { err() << "Could not create vertex buffer for copying" << std::endl; return; @@ -116,148 +94,42 @@ m_usage (copy.m_usage) //////////////////////////////////////////////////////////// VertexBuffer::~VertexBuffer() { - if (m_buffer) - { - TransientContextLock contextLock; - - glCheck(GLEXT_glDeleteBuffers(1, &m_buffer)); - } + delete m_impl; } //////////////////////////////////////////////////////////// bool VertexBuffer::create(std::size_t vertexCount) { - if (!isAvailable()) - return false; - - TransientContextLock contextLock; - - if (!m_buffer) - glCheck(GLEXT_glGenBuffers(1, &m_buffer)); - - if (!m_buffer) - { - err() << "Could not create vertex buffer, generation failed" << std::endl; - return false; - } - - glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, m_buffer)); - glCheck(GLEXT_glBufferData(GLEXT_GL_ARRAY_BUFFER, sizeof(Vertex) * vertexCount, 0, usageToGlEnum(m_usage))); - glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, 0)); - - m_size = vertexCount; - - return true; + return m_impl->create(vertexCount); } //////////////////////////////////////////////////////////// std::size_t VertexBuffer::getVertexCount() const { - return m_size; + return m_impl->getVertexCount(); } //////////////////////////////////////////////////////////// bool VertexBuffer::update(const Vertex* vertices) { - return update(vertices, m_size, 0); + return update(vertices, m_impl->getVertexCount(), 0); } //////////////////////////////////////////////////////////// bool VertexBuffer::update(const Vertex* vertices, std::size_t vertexCount, unsigned int offset) { - // Sanity checks - if (!m_buffer) - return false; - - if (!vertices) - return false; - - if (offset && (offset + vertexCount > m_size)) - return false; - - TransientContextLock contextLock; - - glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, m_buffer)); - - // Check if we need to resize or orphan the buffer - if (vertexCount >= m_size) - { - glCheck(GLEXT_glBufferData(GLEXT_GL_ARRAY_BUFFER, sizeof(Vertex) * vertexCount, 0, usageToGlEnum(m_usage))); - - m_size = vertexCount; - } - - glCheck(GLEXT_glBufferSubData(GLEXT_GL_ARRAY_BUFFER, sizeof(Vertex) * offset, sizeof(Vertex) * vertexCount, vertices)); - - glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, 0)); - - return true; + return m_impl->update(vertices, vertexCount, offset); } //////////////////////////////////////////////////////////// bool VertexBuffer::update(const VertexBuffer& vertexBuffer) { -#ifdef SFML_OPENGL_ES - - return false; - -#else - - if (!m_buffer || !vertexBuffer.m_buffer) - return false; - - TransientContextLock contextLock; - - // Make sure that extensions are initialized - sf::priv::ensureExtensionsInit(); - - if (GLEXT_copy_buffer) - { - glCheck(GLEXT_glBindBuffer(GLEXT_GL_COPY_READ_BUFFER, vertexBuffer.m_buffer)); - glCheck(GLEXT_glBindBuffer(GLEXT_GL_COPY_WRITE_BUFFER, m_buffer)); - - glCheck(GLEXT_glCopyBufferSubData(GLEXT_GL_COPY_READ_BUFFER, GLEXT_GL_COPY_WRITE_BUFFER, 0, 0, sizeof(Vertex) * vertexBuffer.m_size)); - - glCheck(GLEXT_glBindBuffer(GLEXT_GL_COPY_WRITE_BUFFER, 0)); - glCheck(GLEXT_glBindBuffer(GLEXT_GL_COPY_READ_BUFFER, 0)); - - return true; - } - - glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, m_buffer)); - glCheck(GLEXT_glBufferData(GLEXT_GL_ARRAY_BUFFER, sizeof(Vertex) * vertexBuffer.m_size, 0, usageToGlEnum(m_usage))); - - void* destination = 0; - glCheck(destination = GLEXT_glMapBuffer(GLEXT_GL_ARRAY_BUFFER, GLEXT_GL_WRITE_ONLY)); - - glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, vertexBuffer.m_buffer)); - - void* source = 0; - glCheck(source = GLEXT_glMapBuffer(GLEXT_GL_ARRAY_BUFFER, GLEXT_GL_READ_ONLY)); - - std::memcpy(destination, source, sizeof(Vertex) * vertexBuffer.m_size); - - GLboolean sourceResult = GL_FALSE; - glCheck(sourceResult = GLEXT_glUnmapBuffer(GLEXT_GL_ARRAY_BUFFER)); - - glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, m_buffer)); - - GLboolean destinationResult = GL_FALSE; - glCheck(destinationResult = GLEXT_glUnmapBuffer(GLEXT_GL_ARRAY_BUFFER)); - - glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, 0)); - - if ((sourceResult == GL_FALSE) || (destinationResult == GL_FALSE)) - return false; - - return true; - -#endif // SFML_OPENGL_ES + return m_impl->update(*vertexBuffer.m_impl); } @@ -275,29 +147,23 @@ VertexBuffer& VertexBuffer::operator =(const VertexBuffer& right) //////////////////////////////////////////////////////////// void VertexBuffer::swap(VertexBuffer& right) { - std::swap(m_size, right.m_size); - std::swap(m_buffer, right.m_buffer); + std::swap(m_impl, right.m_impl); std::swap(m_primitiveType, right.m_primitiveType); - std::swap(m_usage, right.m_usage); } //////////////////////////////////////////////////////////// unsigned int VertexBuffer::getNativeHandle() const { - return m_buffer; + return m_impl->getNativeHandle(); } //////////////////////////////////////////////////////////// void VertexBuffer::bind(const VertexBuffer* vertexBuffer) { - if (!isAvailable()) - return; - - TransientContextLock lock; - - glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, vertexBuffer ? vertexBuffer->m_buffer : 0)); + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + priv::VertexBufferImplDefault::bind(vertexBuffer ? static_cast(vertexBuffer->m_impl) : 0); } @@ -318,46 +184,32 @@ PrimitiveType VertexBuffer::getPrimitiveType() const //////////////////////////////////////////////////////////// void VertexBuffer::setUsage(VertexBuffer::Usage usage) { - m_usage = usage; + m_impl->setUsage(usage); } //////////////////////////////////////////////////////////// VertexBuffer::Usage VertexBuffer::getUsage() const { - return m_usage; + return m_impl->getUsage(); } //////////////////////////////////////////////////////////// bool VertexBuffer::isAvailable() { - Lock lock(isAvailableMutex); + if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1)) + return priv::VertexBufferImplDefault::isAvailable(); - static bool checked = false; - static bool available = false; - - if (!checked) - { - checked = true; - - TransientContextLock contextLock; - - // Make sure that extensions are initialized - sf::priv::ensureExtensionsInit(); - - available = GLEXT_vertex_buffer_object; - } - - return available; + return false; } //////////////////////////////////////////////////////////// void VertexBuffer::draw(RenderTarget& target, RenderStates states) const { - if (m_buffer && m_size) - target.draw(*this, 0, m_size, states); + if (getNativeHandle() && getVertexCount()) + target.draw(*this, 0, getVertexCount(), states); } } // namespace sf diff --git a/src/SFML/Graphics/VertexBufferImpl.cpp b/src/SFML/Graphics/VertexBufferImpl.cpp new file mode 100644 index 000000000..deac578c0 --- /dev/null +++ b/src/SFML/Graphics/VertexBufferImpl.cpp @@ -0,0 +1,43 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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 +{ +//////////////////////////////////////////////////////////// +VertexBufferImpl::~VertexBufferImpl() +{ + // Nothing to do +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Graphics/VertexBufferImpl.hpp b/src/SFML/Graphics/VertexBufferImpl.hpp new file mode 100644 index 000000000..98388c56e --- /dev/null +++ b/src/SFML/Graphics/VertexBufferImpl.hpp @@ -0,0 +1,124 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2019 Laurent Gomila (laurent@sfml-dev.org) +// +// 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_VERTEXBUFFER_IMPL_HPP +#define SFML_VERTEXBUFFER_IMPL_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +class Vertex; + +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Abstract base class for vertex buffer implementations +/// +//////////////////////////////////////////////////////////// +class VertexBufferImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + virtual ~VertexBufferImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Create the vertex buffer + /// + /// \param vertexCount Number of vertices worth of memory to allocate + /// + /// \return True if creation was successful + /// + //////////////////////////////////////////////////////////// + virtual bool create(std::size_t vertexCount) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Return the vertex count + /// + /// \return Number of vertices in the vertex buffer + /// + //////////////////////////////////////////////////////////// + virtual std::size_t getVertexCount() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Update a part of the buffer from an array of vertices + /// + /// \param vertices Array of vertices to copy to the buffer + /// \param vertexCount Number of vertices to copy + /// \param offset Offset in the buffer to copy to + /// + /// \return True if the update was successful + /// + //////////////////////////////////////////////////////////// + virtual bool update(const Vertex* vertices, std::size_t vertexCount, unsigned int offset) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Copy the contents of another buffer into this buffer + /// + /// \param vertexBuffer Vertex buffer whose contents to copy into this vertex buffer + /// + /// \return True if the copy was successful + /// + //////////////////////////////////////////////////////////// + virtual bool update(const VertexBufferImpl& vertexBuffer) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get the underlying OpenGL handle of the vertex buffer. + /// + /// \return OpenGL handle of the vertex buffer or 0 if not yet created + /// + //////////////////////////////////////////////////////////// + virtual unsigned int getNativeHandle() const = 0; + + //////////////////////////////////////////////////////////// + /// \brief Set the usage specifier of this vertex buffer + /// + /// \param usage Usage specifier + /// + //////////////////////////////////////////////////////////// + virtual void setUsage(VertexBuffer::Usage usage) = 0; + + //////////////////////////////////////////////////////////// + /// \brief Get the usage specifier of this vertex buffer + /// + /// \return Usage specifier + /// + //////////////////////////////////////////////////////////// + virtual VertexBuffer::Usage getUsage() const = 0; +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_VERTEXBUFFER_IMPL_HPP