Implemented initial support for modular rendering backends. Currently only the existing GL1 renderer implementation is available.

This commit is contained in:
binary1248 2019-05-08 01:05:25 +02:00
parent 80c3bdc23c
commit fcd52afb5c
No known key found for this signature in database
GPG Key ID: E5E52A5D6082224A
43 changed files with 5450 additions and 2524 deletions

View File

@ -41,6 +41,7 @@
#include <SFML/Graphics/PrimitiveType.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
#include <SFML/Graphics/Renderer.hpp>
#include <SFML/Graphics/RenderStates.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/RenderTexture.hpp>

View File

@ -30,13 +30,8 @@
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Export.hpp>
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/View.hpp>
#include <SFML/Graphics/Transform.hpp>
#include <SFML/Graphics/BlendMode.hpp>
#include <SFML/Graphics/RenderStates.hpp>
#include <SFML/Graphics/PrimitiveType.hpp>
#include <SFML/Graphics/Vertex.hpp>
#include <SFML/System/NonCopyable.hpp>
@ -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

View File

@ -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 <SFML/Graphics/Export.hpp>
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

View File

@ -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<int, const Texture*> TextureTable;
typedef std::map<std::string, int> 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

View File

@ -29,23 +29,30 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Export.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Window/GlResource.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/System/Vector2.hpp>
#include <string>
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

View File

@ -31,7 +31,6 @@
#include <SFML/Graphics/Export.hpp>
#include <SFML/Graphics/PrimitiveType.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Window/GlResource.hpp>
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
priv::VertexBufferImpl* m_impl; ///< Platform/hardware specific implementation
PrimitiveType m_primitiveType; ///< Type of primitives to draw
Usage m_usage; ///< How this vertex buffer is to be used
};
} // namespace sf

View File

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

View File

@ -26,7 +26,8 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Graphics/OpenGL/GLCheck.hpp>
#include <SFML/Graphics/Image.hpp>
#ifdef SFML_SYSTEM_ANDROID
#include <SFML/System/Android/ResourceStream.hpp>
#endif

View File

@ -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 <SFML/Graphics/OpenGL/GL1/RenderTargetImplDefault.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Shader.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/OpenGL/GL1/TextureImplDefault.hpp>
#include <SFML/Graphics/VertexArray.hpp>
#include <SFML/Graphics/VertexBuffer.hpp>
#include <SFML/Graphics/OpenGL/GLCheck.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <cassert>
#include <iostream>
#include <algorithm>
#include <map>
// 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<sf::Uint64, sf::Uint64> 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<const char*>(vertices);
// If we pre-transform the vertices, we must use our internal vertex cache
if (useVertexCache)
data = reinterpret_cast<const char*>(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<const char*>(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<const void*>(0)));
glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), reinterpret_cast<const void*>(8)));
glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast<const void*>(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<float>(newSize.x), static_cast<float>(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<const priv::TextureImplDefault*>(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<const priv::TextureImplDefault*>(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<const priv::TextureImplDefault*>(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<GLint>(firstVertex), static_cast<GLsizei>(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<const priv::TextureImplDefault*>(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.
//
////////////////////////////////////////////////////////////

View File

@ -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 <SFML/Graphics/RenderTargetImpl.hpp>
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/View.hpp>
#include <SFML/Graphics/Transform.hpp>
#include <SFML/Graphics/BlendMode.hpp>
#include <SFML/Graphics/RenderStates.hpp>
#include <SFML/Graphics/PrimitiveType.hpp>
#include <SFML/Graphics/Vertex.hpp>
#include <SFML/System/NonCopyable.hpp>
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

View File

@ -25,13 +25,32 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderTextureImplDefault.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Graphics/TextureSaver.hpp>
#include <SFML/Graphics/OpenGL/GL1/RenderTextureImplDefault.hpp>
#include <SFML/Graphics/OpenGL/GLCheck.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/System/Err.hpp>
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));

View File

@ -25,9 +25,9 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
#include <SFML/Graphics/OpenGL/GL1/RenderTextureImplFBO.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Graphics/OpenGL/GLCheck.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
@ -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<GLint*>(&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)

View File

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

View File

@ -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 <SFML/Graphics/OpenGL/GL1/ShaderImplDefault.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/OpenGL/GLCheck.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <SFML/System/NonCopyable.hpp>
#include <vector>
#if defined(SFML_SYSTEM_MACOS) || defined(SFML_SYSTEM_IOS)
#define castToGlHandle(x) reinterpret_cast<GLEXT_GLhandle>(static_cast<ptrdiff_t>(x))
#define castFromGlHandle(x) static_cast<unsigned int>(reinterpret_cast<ptrdiff_t>(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 <typename T>
std::vector<T> flatten(const sf::Vector2<T>* vectorArray, std::size_t length)
{
const std::size_t vectorSize = 2;
std::vector<T> 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 <typename T>
std::vector<T> flatten(const sf::Vector3<T>* vectorArray, std::size_t length)
{
const std::size_t vectorSize = 3;
std::vector<T> 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 <typename T>
std::vector<T> flatten(const sf::priv::Vector4<T>* vectorArray, std::size_t length)
{
const std::size_t vectorSize = 4;
std::vector<T> 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<std::size_t>(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<GLsizei>(length), scalarArray));
}
////////////////////////////////////////////////////////////
void ShaderImplDefault::setUniformArray(const std::string& name, const Glsl::Vec2* vectorArray, std::size_t length)
{
std::vector<float> contiguous = flatten(vectorArray, length);
UniformBinder binder(*this, name);
if (binder.location != -1)
glCheck(GLEXT_glUniform2fv(binder.location, static_cast<GLsizei>(length), &contiguous[0]));
}
////////////////////////////////////////////////////////////
void ShaderImplDefault::setUniformArray(const std::string& name, const Glsl::Vec3* vectorArray, std::size_t length)
{
std::vector<float> contiguous = flatten(vectorArray, length);
UniformBinder binder(*this, name);
if (binder.location != -1)
glCheck(GLEXT_glUniform3fv(binder.location, static_cast<GLsizei>(length), &contiguous[0]));
}
////////////////////////////////////////////////////////////
void ShaderImplDefault::setUniformArray(const std::string& name, const Glsl::Vec4* vectorArray, std::size_t length)
{
std::vector<float> contiguous = flatten(vectorArray, length);
UniformBinder binder(*this, name);
if (binder.location != -1)
glCheck(GLEXT_glUniform4fv(binder.location, static_cast<GLsizei>(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<float> 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<GLsizei>(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<float> 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<GLsizei>(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<GLsizei>(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

View File

@ -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 <SFML/Graphics/Export.hpp>
#include <SFML/Graphics/Shader.hpp>
#include <SFML/Graphics/ShaderImpl.hpp>
#include <SFML/Graphics/Glsl.hpp>
#include <SFML/Window/GlResource.hpp>
#include <map>
#include <string>
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<int, const Texture*> TextureTable;
typedef std::map<std::string, int> 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

View File

@ -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 <SFML/Graphics/OpenGL/GL1/TextureImplDefault.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/OpenGL/GLCheck.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/Window/Window.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <cassert>
#include <cstring>
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<GLuint>(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<unsigned int>(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<int>(image.getSize().x);
int height = static_cast<int>(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<Uint8> 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<Uint8> 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<const TextureImplDefault&>(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<float>(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<unsigned int>(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

View File

@ -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 <SFML/Graphics/TextureImpl.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Export.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Window/GlResource.hpp>
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

View File

@ -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 <SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/Vertex.hpp>
#include <SFML/Graphics/OpenGL/GLCheck.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <cstring>
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<const VertexBufferImplDefault&>(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

View File

@ -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 <SFML/Graphics/VertexBufferImpl.hpp>
#include <SFML/Window/GlResource.hpp>
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

View File

@ -25,7 +25,7 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Graphics/OpenGL/GLCheck.hpp>
#include <SFML/System/Err.hpp>
#include <string>

View File

@ -29,7 +29,7 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Config.hpp>
#include <SFML/Graphics/GLExtensions.hpp>
#include <SFML/Graphics/OpenGL/GLExtensions.hpp>
namespace sf

View File

@ -26,7 +26,7 @@
// Headers
////////////////////////////////////////////////////////////
#define SF_GLAD_GL_IMPLEMENTATION
#include <SFML/Graphics/GLExtensions.hpp>
#include <SFML/Graphics/OpenGL/GLExtensions.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/System/Err.hpp>

View File

@ -26,154 +26,54 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Shader.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/VertexArray.hpp>
#include <SFML/Graphics/OpenGL/GL1/RenderTargetImplDefault.hpp>
#include <SFML/Graphics/Renderer.hpp>
#include <SFML/Graphics/VertexBuffer.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <cassert>
#include <iostream>
#include <algorithm>
#include <map>
// 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<sf::Uint64, sf::Uint64> 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<const char*>(vertices);
// If we pre-transform the vertices, we must use our internal vertex cache
if (useVertexCache)
data = reinterpret_cast<const char*>(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<const char*>(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<const void*>(0)));
glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), reinterpret_cast<const void*>(8)));
glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast<const void*>(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<float>(getSize().x), static_cast<float>(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<GLint>(firstVertex), static_cast<GLsizei>(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.
//
////////////////////////////////////////////////////////////

View File

@ -22,13 +22,11 @@
//
////////////////////////////////////////////////////////////
#ifndef SFML_TEXTURESAVER_HPP
#define SFML_TEXTURESAVER_HPP
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Graphics/RenderTargetImpl.hpp>
#include <SFML/Graphics/Texture.hpp>
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();
RenderTargetImpl::~RenderTargetImpl()
{
// Nothing to do
}
////////////////////////////////////////////////////////////
/// \brief Destructor
///
/// The previous texture binding is restored.
///
////////////////////////////////////////////////////////////
~TextureSaver();
void RenderTargetImpl::pushGLStates()
{
// Nothing to do
}
private:
////////////////////////////////////////////////////////////
// Member data
void RenderTargetImpl::popGLStates()
{
// Nothing to do
}
////////////////////////////////////////////////////////////
GLint m_textureBinding; ///< Texture binding to restore
};
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

View File

@ -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 <SFML/Graphics/PrimitiveType.hpp>
#include <SFML/System/Vector2.hpp>
#include <cstddef>
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

View File

@ -26,8 +26,10 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderTexture.hpp>
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
#include <SFML/Graphics/RenderTextureImplDefault.hpp>
#include <SFML/Graphics/OpenGL/GL1/RenderTextureImplFBO.hpp>
#include <SFML/Graphics/OpenGL/GL1/RenderTextureImplDefault.hpp>
#include <SFML/Graphics/OpenGL/GL1/TextureImplDefault.hpp>
#include <SFML/Graphics/Renderer.hpp>
#include <SFML/System/Err.hpp>
@ -65,6 +67,10 @@ bool RenderTexture::create(unsigned int width, unsigned int height, const Contex
return false;
}
if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1))
{
priv::TextureImplDefault& texture = *static_cast<priv::TextureImplDefault*>(m_texture.m_impl);
// We disable smoothing by default for render textures
setSmooth(false);
@ -76,7 +82,7 @@ bool RenderTexture::create(unsigned int width, unsigned int height, const Contex
m_impl = new priv::RenderTextureImplFBO;
// Mark the texture as being a framebuffer object attachment
m_texture.m_fboAttachment = true;
texture.m_fboAttachment = true;
}
else
{
@ -85,8 +91,9 @@ bool RenderTexture::create(unsigned int width, unsigned int height, const Contex
}
// Initialize the render texture
if (!m_impl->create(width, height, m_texture.m_texture, settings))
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 ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1))
{
if (m_impl && (priv::RenderTextureImplFBO::isAvailable() || setActive(true)))
{
m_impl->updateTexture(m_texture.m_texture);
m_texture.m_pixelsFlipped = true;
m_texture.invalidateMipmap();
priv::TextureImplDefault& texture = *static_cast<priv::TextureImplDefault*>(m_texture.m_impl);
m_impl->updateTexture(texture.m_texture);
texture.m_pixelsFlipped = true;
texture.invalidateMipmap();
}
}
}

View File

@ -27,8 +27,9 @@
////////////////////////////////////////////////////////////
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Graphics/RenderTextureImplFBO.hpp>
#include <SFML/Graphics/OpenGL/GLCheck.hpp>
#include <SFML/Graphics/OpenGL/GL1/RenderTextureImplFBO.hpp>
#include <SFML/Graphics/Renderer.hpp>
namespace sf
@ -82,14 +83,17 @@ bool RenderWindow::setActive(bool active)
if (result)
RenderTarget::setActive(active);
if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1))
{
// 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())
{
glCheck(GLEXT_glBindFramebuffer(GLEXT_GL_FRAMEBUFFER, m_defaultFrameBuffer));
priv::RenderTextureImplFBO::bindFramebuffer(m_defaultFrameBuffer);
return true;
}
}
return result;
}
@ -110,12 +114,15 @@ Image RenderWindow::capture() const
////////////////////////////////////////////////////////////
void RenderWindow::onCreate()
{
if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1))
{
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
glCheck(glGetIntegerv(GLEXT_GL_FRAMEBUFFER_BINDING, reinterpret_cast<GLint*>(&m_defaultFrameBuffer)));
m_defaultFrameBuffer = priv::RenderTextureImplFBO::getFramebuffer();
}
}
// Just initialize the render target part

View File

@ -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 <SFML/Graphics/Renderer.hpp>
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

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@
////////////////////////////////////////////////////////////
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/TextureSaver.hpp>
#include <SFML/Graphics/ShaderImpl.hpp>
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

View File

@ -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 <SFML/Graphics/Export.hpp>
#include <SFML/Graphics/Shader.hpp>
#include <SFML/Graphics/Glsl.hpp>
#include <SFML/System/NonCopyable.hpp>
#include <string>
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

View File

@ -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 <SFML/Graphics/ShaderImplNull.hpp>
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

View File

@ -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 <SFML/Graphics/Export.hpp>
#include <SFML/Graphics/Shader.hpp>
#include <SFML/Graphics/ShaderImpl.hpp>
#include <SFML/Graphics/Glsl.hpp>
#include <string>
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

View File

@ -27,6 +27,8 @@
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Text.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/OpenGL/GL1/TextureImplDefault.hpp>
#include <SFML/Graphics/Renderer.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <cmath>
@ -395,12 +397,19 @@ void Text::ensureGeometryUpdate() const
if (!m_font)
return;
Uint64 cacheId = 0;
if ((sf::getRenderer() == sf::Renderer::Default) || (sf::getRenderer() == sf::Renderer::OpenGL1))
{
cacheId = static_cast<const priv::TextureImplDefault*>(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 && m_font->getTexture(m_characterSize).m_cacheId == m_fontTextureId)
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;

View File

@ -26,191 +26,42 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/OpenGL/GL1/TextureImplDefault.hpp>
#include <SFML/Graphics/Renderer.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/Graphics/TextureSaver.hpp>
#include <SFML/Window/Context.hpp>
#include <SFML/Window/Window.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <cassert>
#include <cstring>
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<const priv::TextureImplDefault*>(copy.m_impl));
}
////////////////////////////////////////////////////////////
Texture::~Texture()
{
// Destroy the OpenGL texture
if (m_texture)
{
TransientContextLock lock;
GLuint texture = static_cast<GLuint>(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<unsigned int>(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<int>(image.getSize().x);
int height = static_cast<int>(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<Uint8> 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<Uint8> 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<float>(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<const priv::TextureImplDefault*>(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<unsigned int>(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

View File

@ -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 <SFML/Graphics/TextureImpl.hpp>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
TextureImpl::~TextureImpl()
{
// Nothing to do
}
} // namespace priv
} // namespace sf

View File

@ -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 <SFML/Graphics/Export.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/System/Vector2.hpp>
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

View File

@ -26,82 +26,60 @@
// Headers
////////////////////////////////////////////////////////////
#include <SFML/Graphics/VertexBuffer.hpp>
#include <SFML/Graphics/OpenGL/GL1/VertexBufferImplDefault.hpp>
#include <SFML/Graphics/Renderer.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/Graphics/Vertex.hpp>
#include <SFML/Graphics/GLCheck.hpp>
#include <SFML/System/Mutex.hpp>
#include <SFML/System/Lock.hpp>
#include <SFML/System/Err.hpp>
#include <cstring>
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<const priv::VertexBufferImplDefault*>(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

View File

@ -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 <SFML/Graphics/VertexBufferImpl.hpp>
namespace sf
{
namespace priv
{
////////////////////////////////////////////////////////////
VertexBufferImpl::~VertexBufferImpl()
{
// Nothing to do
}
} // namespace priv
} // namespace sf

View File

@ -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 <SFML/Graphics/VertexBuffer.hpp>
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