From 61cdcd47ca88e0cedb10ac3ef0d3b0907267bb7d Mon Sep 17 00:00:00 2001 From: binary1248 Date: Tue, 24 Oct 2017 18:57:26 +0200 Subject: [PATCH] Added sf::VertexBuffer class. --- include/SFML/Graphics.hpp | 1 + include/SFML/Graphics/RenderTarget.hpp | 48 +++ include/SFML/Graphics/VertexBuffer.hpp | 408 +++++++++++++++++++++++++ src/SFML/Graphics/CMakeLists.txt | 2 + src/SFML/Graphics/GLExtensions.hpp | 38 +++ src/SFML/Graphics/GLExtensions.txt | 2 + src/SFML/Graphics/GLLoader.cpp | 86 +++++- src/SFML/Graphics/GLLoader.hpp | 69 +++++ src/SFML/Graphics/RenderTarget.cpp | 195 +++++++++--- src/SFML/Graphics/VertexBuffer.cpp | 363 ++++++++++++++++++++++ 10 files changed, 1165 insertions(+), 47 deletions(-) create mode 100644 include/SFML/Graphics/VertexBuffer.hpp create mode 100644 src/SFML/Graphics/VertexBuffer.cpp diff --git a/include/SFML/Graphics.hpp b/include/SFML/Graphics.hpp index 5bb622aa..81923520 100644 --- a/include/SFML/Graphics.hpp +++ b/include/SFML/Graphics.hpp @@ -54,6 +54,7 @@ #include #include #include +#include #include diff --git a/include/SFML/Graphics/RenderTarget.hpp b/include/SFML/Graphics/RenderTarget.hpp index 50bf7a6f..a6c7c836 100644 --- a/include/SFML/Graphics/RenderTarget.hpp +++ b/include/SFML/Graphics/RenderTarget.hpp @@ -43,6 +43,7 @@ namespace sf { class Drawable; +class VertexBuffer; //////////////////////////////////////////////////////////// /// \brief Base class for all render targets (window, texture, ...) @@ -247,6 +248,26 @@ public: void draw(const Vertex* vertices, std::size_t vertexCount, PrimitiveType type, const RenderStates& states = RenderStates::Default); + //////////////////////////////////////////////////////////// + /// \brief Draw primitives defined by a vertex buffer + /// + /// \param vertexBuffer Vertex buffer + /// \param states Render states to use for drawing + /// + //////////////////////////////////////////////////////////// + void draw(const VertexBuffer& vertexBuffer, const RenderStates& states = RenderStates::Default); + + //////////////////////////////////////////////////////////// + /// \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 + /// + //////////////////////////////////////////////////////////// + void draw(const VertexBuffer& vertexBuffer, std::size_t firstVertex, std::size_t vertexCount, const RenderStates& states = RenderStates::Default); + //////////////////////////////////////////////////////////// /// \brief Return the size of the rendering region of the target /// @@ -402,6 +423,33 @@ private: //////////////////////////////////////////////////////////// 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 /// diff --git a/include/SFML/Graphics/VertexBuffer.hpp b/include/SFML/Graphics/VertexBuffer.hpp new file mode 100644 index 00000000..321e875e --- /dev/null +++ b/include/SFML/Graphics/VertexBuffer.hpp @@ -0,0 +1,408 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2017 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_HPP +#define SFML_VERTEXBUFFER_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include + + +namespace sf +{ +class RenderTarget; +class Vertex; + +//////////////////////////////////////////////////////////// +/// \brief Vertex buffer storage for one or more 2D primitives +/// +//////////////////////////////////////////////////////////// +class SFML_GRAPHICS_API VertexBuffer : public Drawable, private GlResource +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Usage specifiers + /// + /// If data is going to be updated once or more every frame, + /// set the usage to Stream. If data is going to be set once + /// and used for a long time without being modified, set the + /// usage to Static. For everything else Dynamic should be a + /// good compromise. + /// + //////////////////////////////////////////////////////////// + enum Usage + { + Stream, ///< Constantly changing data + Dynamic, ///< Occasionally changing data + Static ///< Rarely changing data + }; + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates an empty vertex buffer. + /// + //////////////////////////////////////////////////////////// + VertexBuffer(); + + //////////////////////////////////////////////////////////// + /// \brief Construct a VertexBuffer with a specific PrimitiveType + /// + /// Creates an empty vertex buffer and sets its primitive type to \p type. + /// + /// \param type Type of primitive + /// + //////////////////////////////////////////////////////////// + explicit VertexBuffer(PrimitiveType type); + + //////////////////////////////////////////////////////////// + /// \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 VertexBuffer(Usage usage); + + //////////////////////////////////////////////////////////// + /// \brief Construct a VertexBuffer with a specific PrimitiveType and usage specifier + /// + /// Creates an empty vertex buffer and sets its primitive type + /// to \p type and usage to \p usage. + /// + /// \param type Type of primitive + /// \param usage Usage specifier + /// + //////////////////////////////////////////////////////////// + VertexBuffer(PrimitiveType type, Usage usage); + + //////////////////////////////////////////////////////////// + /// \brief Copy constructor + /// + /// \param copy instance to copy + /// + //////////////////////////////////////////////////////////// + VertexBuffer(const VertexBuffer& copy); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~VertexBuffer(); + + //////////////////////////////////////////////////////////// + /// \brief Create the vertex buffer + /// + /// Creates the vertex buffer and allocates enough graphics + /// memory to hold \p vertexCount vertices. Any previously + /// allocated memory is freed in the process. + /// + /// In order to deallocate previously allocated memory pass 0 + /// as \p vertexCount. Don't forget to recreate with a non-zero + /// value when graphics memory should be allocated again. + /// + /// \param vertexCount Number of vertices worth of memory to allocate + /// + /// \return True if creation was successful + /// + //////////////////////////////////////////////////////////// + bool create(std::size_t vertexCount); + + //////////////////////////////////////////////////////////// + /// \brief Return the vertex count + /// + /// \return Number of vertices in the vertex buffer + /// + //////////////////////////////////////////////////////////// + std::size_t getVertexCount() const; + + //////////////////////////////////////////////////////////// + /// \brief Update the whole buffer from an array of vertices + /// + /// The \a vertex array is assumed to have the same size as + /// the \a created buffer. + /// + /// No additional check is performed on the size of the vertex + /// array, passing invalid arguments will lead to undefined + /// behavior. + /// + /// This function does nothing if \a vertices is null or if the + /// buffer was not previously created. + /// + /// \param vertices Array of vertices to copy to the buffer + /// + /// \return True if the update was successful + /// + //////////////////////////////////////////////////////////// + bool update(const Vertex* vertices); + + //////////////////////////////////////////////////////////// + /// \brief Update a part of the buffer from an array of vertices + /// + /// \p offset is specified as the number of vertices to skip + /// from the beginning of the buffer. + /// + /// If \p offset is 0 and \p vertexCount is equal to the size of + /// the currently created buffer, its whole contents are replaced. + /// + /// If \p offset is 0 and \p vertexCount is greater than the + /// size of the currently created buffer, a new buffer is created + /// containing the vertex data. + /// + /// If \p offset is 0 and \p vertexCount is less than the size of + /// the currently created buffer, only the corresponding region + /// is updated. + /// + /// If \p offset is not 0 and \p offset + \p vertexCount is greater + /// than the size of the currently created buffer, the update fails. + /// + /// No additional check is performed on the size of the vertex + /// array, passing invalid arguments will lead to undefined + /// behavior. + /// + /// \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 + /// + //////////////////////////////////////////////////////////// + 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 + /// + //////////////////////////////////////////////////////////// + bool update(const VertexBuffer& vertexBuffer); + + //////////////////////////////////////////////////////////// + /// \brief Overload of assignment operator + /// + /// \param right Instance to assign + /// + /// \return Reference to self + /// + //////////////////////////////////////////////////////////// + VertexBuffer& operator =(const VertexBuffer& right); + + //////////////////////////////////////////////////////////// + /// \brief Swap the contents of this vertex buffer with those of another + /// + /// \param right Instance to swap with + /// + //////////////////////////////////////////////////////////// + void swap(VertexBuffer& right); + + //////////////////////////////////////////////////////////// + /// \brief Get the underlying OpenGL handle of the vertex buffer. + /// + /// You shouldn't need to use this function, unless you have + /// very specific stuff to implement that SFML doesn't support, + /// or implement a temporary workaround until a bug is fixed. + /// + /// \return OpenGL handle of the vertex buffer or 0 if not yet created + /// + //////////////////////////////////////////////////////////// + unsigned int getNativeHandle() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the type of primitives to draw + /// + /// This function defines how the vertices must be interpreted + /// when it's time to draw them. + /// + /// The default primitive type is sf::Points. + /// + /// \param type Type of primitive + /// + //////////////////////////////////////////////////////////// + void setPrimitiveType(PrimitiveType type); + + //////////////////////////////////////////////////////////// + /// \brief Get the type of primitives drawn by the vertex buffer + /// + /// \return Primitive type + /// + //////////////////////////////////////////////////////////// + PrimitiveType getPrimitiveType() const; + + //////////////////////////////////////////////////////////// + /// \brief Set the usage specifier of this vertex buffer + /// + /// This function provides a hint about how this vertex buffer is + /// going to be used in terms of data update frequency. + /// + /// After changing the usage specifier, the vertex buffer has + /// to be updated with new data for the usage specifier to + /// take effect. + /// + /// The default primitive type is sf::VertexBuffer::Stream. + /// + /// \param usage Usage specifier + /// + //////////////////////////////////////////////////////////// + void setUsage(Usage usage); + + //////////////////////////////////////////////////////////// + /// \brief Get the usage specifier of this vertex buffer + /// + /// \return Usage specifier + /// + //////////////////////////////////////////////////////////// + Usage getUsage() const; + + //////////////////////////////////////////////////////////// + /// \brief Bind a vertex buffer for rendering + /// + /// This function is not part of the graphics API, it mustn't be + /// used when drawing SFML entities. It must be used only if you + /// mix sf::VertexBuffer with OpenGL code. + /// + /// \code + /// sf::VertexBuffer vb1, vb2; + /// ... + /// sf::VertexBuffer::bind(&vb1); + /// // draw OpenGL stuff that use vb1... + /// sf::VertexBuffer::bind(&vb2); + /// // draw OpenGL stuff that use vb2... + /// sf::VertexBuffer::bind(NULL); + /// // draw OpenGL stuff that use no vertex buffer... + /// \endcode + /// + /// \param vertexBuffer Pointer to the vertex buffer to bind, can be null to use no vertex buffer + /// + //////////////////////////////////////////////////////////// + static void bind(const VertexBuffer* vertexBuffer); + + //////////////////////////////////////////////////////////// + /// \brief Tell whether or not the system supports vertex buffers + /// + /// This function should always be called before using + /// the vertex buffer features. If it returns false, then + /// any attempt to use sf::VertexBuffer will fail. + /// + /// \return True if vertex buffers are supported, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isAvailable(); + +private: + + //////////////////////////////////////////////////////////// + /// \brief Draw the vertex buffer to a render target + /// + /// \param target Render target to draw to + /// \param states Current render states + /// + //////////////////////////////////////////////////////////// + virtual void draw(RenderTarget& target, RenderStates states) const; + +private: + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + unsigned int m_buffer; ///< Internal buffer identifier + std::size_t m_size; ///< Size in Vertexes of the currently allocated buffer + PrimitiveType m_primitiveType; ///< Type of primitives to draw + Usage m_usage; ///< How this vertex buffer is to be used +}; + +} // namespace sf + + +#endif // SFML_VERTEXBUFFER_HPP + + +//////////////////////////////////////////////////////////// +/// \class sf::VertexBuffer +/// \ingroup graphics +/// +/// sf::VertexBuffer is a simple wrapper around a dynamic +/// buffer of vertices and a primitives type. +/// +/// Unlike sf::VertexArray, the vertex data is stored in +/// graphics memory. +/// +/// In situations where a large amount of vertex data would +/// have to be transferred from system memory to graphics memory +/// every frame, using sf::VertexBuffer can help. By using a +/// sf::VertexBuffer, data that has not been changed between frames +/// does not have to be re-transferred from system to graphics +/// memory as would be the case with sf::VertexArray. If data transfer +/// is a bottleneck, this can lead to performance gains. +/// +/// Using sf::VertexBuffer, the user also has the ability to only modify +/// a portion of the buffer in graphics memory. This way, a large buffer +/// can be allocated at the start of the application and only the +/// applicable portions of it need to be updated during the course of +/// the application. This allows the user to take full control of data +/// transfers between system and graphics memory if they need to. +/// +/// In special cases, the user can make use of multiple threads to update +/// vertex data in multiple distinct regions of the buffer simultaneously. +/// This might make sense when e.g. the position of multiple objects has to +/// be recalculated very frequently. The computation load can be spread +/// across multiple threads as long as there are no other data dependencies. +/// +/// Simultaneous updates to the vertex buffer are not guaranteed to be +/// carried out by the driver in any specific order. Updating the same +/// region of the buffer from multiple threads will not cause undefined +/// behaviour, however the final state of the buffer will be unpredictable. +/// +/// Simultaneous updates of distinct non-overlapping regions of the buffer +/// are also not guaranteed to complete in a specific order. However, in +/// this case the user can make sure to synchronize the writer threads at +/// well-defined points in their code. The driver will make sure that all +/// pending data transfers complete before the vertex buffer is sourced +/// by the rendering pipeline. +/// +/// It inherits sf::Drawable, but unlike other drawables it +/// is not transformable. +/// +/// Example: +/// \code +/// sf::Vertex vertices[15]; +/// ... +/// sf::VertexBuffer triangles(sf::Triangles); +/// triangles.create(15); +/// triangles.update(vertices); +/// ... +/// window.draw(triangles); +/// \endcode +/// +/// \see sf::Vertex, sf::VertexArray +/// +//////////////////////////////////////////////////////////// diff --git a/src/SFML/Graphics/CMakeLists.txt b/src/SFML/Graphics/CMakeLists.txt index d2eab8b2..3ad9b8d4 100644 --- a/src/SFML/Graphics/CMakeLists.txt +++ b/src/SFML/Graphics/CMakeLists.txt @@ -72,6 +72,8 @@ set(DRAWABLES_SRC ${INCROOT}/Text.hpp ${SRCROOT}/VertexArray.cpp ${INCROOT}/VertexArray.hpp + ${SRCROOT}/VertexBuffer.cpp + ${INCROOT}/VertexBuffer.hpp ) source_group("drawables" FILES ${DRAWABLES_SRC}) diff --git a/src/SFML/Graphics/GLExtensions.hpp b/src/SFML/Graphics/GLExtensions.hpp index 76c97512..cc5da64b 100644 --- a/src/SFML/Graphics/GLExtensions.hpp +++ b/src/SFML/Graphics/GLExtensions.hpp @@ -53,6 +53,19 @@ #define GLEXT_GL_CLAMP GL_CLAMP_TO_EDGE #define GLEXT_GL_CLAMP_TO_EDGE GL_CLAMP_TO_EDGE + // Core since 1.1 + // 1.1 does not support GL_STREAM_DRAW so we just define it to GL_DYNAMIC_DRAW + #define GLEXT_vertex_buffer_object true + #define GLEXT_GL_ARRAY_BUFFER GL_ARRAY_BUFFER + #define GLEXT_GL_DYNAMIC_DRAW GL_DYNAMIC_DRAW + #define GLEXT_GL_STATIC_DRAW GL_STATIC_DRAW + #define GLEXT_GL_STREAM_DRAW GL_DYNAMIC_DRAW + #define GLEXT_glBindBuffer glBindBuffer + #define GLEXT_glBufferData glBufferData + #define GLEXT_glBufferSubData glBufferSubData + #define GLEXT_glDeleteBuffers glDeleteBuffers + #define GLEXT_glGenBuffers glGenBuffers + // The following extensions are listed chronologically // Extension macro first, followed by tokens then // functions according to the corresponding specification @@ -114,6 +127,9 @@ // Core since 3.0 #define GLEXT_framebuffer_blit false + // Core since 3.0 - NV_copy_buffer + #define GLEXT_copy_buffer false + // Core since 3.0 - EXT_sRGB #ifdef GL_EXT_sRGB #define GLEXT_texture_sRGB GL_EXT_sRGB @@ -168,6 +184,22 @@ #define GLEXT_blend_func_separate sfogl_ext_EXT_blend_func_separate #define GLEXT_glBlendFuncSeparate glBlendFuncSeparateEXT + // Core since 1.5 - ARB_vertex_buffer_object + #define GLEXT_vertex_buffer_object sfogl_ext_ARB_vertex_buffer_object + #define GLEXT_GL_ARRAY_BUFFER GL_ARRAY_BUFFER_ARB + #define GLEXT_GL_DYNAMIC_DRAW GL_DYNAMIC_DRAW_ARB + #define GLEXT_GL_READ_ONLY GL_READ_ONLY_ARB + #define GLEXT_GL_STATIC_DRAW GL_STATIC_DRAW_ARB + #define GLEXT_GL_STREAM_DRAW GL_STREAM_DRAW_ARB + #define GLEXT_GL_WRITE_ONLY GL_WRITE_ONLY_ARB + #define GLEXT_glBindBuffer glBindBufferARB + #define GLEXT_glBufferData glBufferDataARB + #define GLEXT_glBufferSubData glBufferSubDataARB + #define GLEXT_glDeleteBuffers glDeleteBuffersARB + #define GLEXT_glGenBuffers glGenBuffersARB + #define GLEXT_glMapBuffer glMapBufferARB + #define GLEXT_glUnmapBuffer glUnmapBufferARB + // Core since 2.0 - ARB_shading_language_100 #define GLEXT_shading_language_100 sfogl_ext_ARB_shading_language_100 @@ -254,6 +286,12 @@ #define GLEXT_GL_DRAW_FRAMEBUFFER_BINDING GL_DRAW_FRAMEBUFFER_BINDING_EXT #define GLEXT_GL_READ_FRAMEBUFFER_BINDING GL_READ_FRAMEBUFFER_BINDING_EXT + // Core since 3.1 - ARB_copy_buffer + #define GLEXT_copy_buffer sfogl_ext_ARB_copy_buffer + #define GLEXT_GL_COPY_READ_BUFFER GL_COPY_READ_BUFFER + #define GLEXT_GL_COPY_WRITE_BUFFER GL_COPY_WRITE_BUFFER + #define GLEXT_glCopyBufferSubData glCopyBufferSubData + // Core since 3.2 - ARB_geometry_shader4 #define GLEXT_geometry_shader4 sfogl_ext_ARB_geometry_shader4 #define GLEXT_GL_GEOMETRY_SHADER GL_GEOMETRY_SHADER_ARB diff --git a/src/SFML/Graphics/GLExtensions.txt b/src/SFML/Graphics/GLExtensions.txt index 4b3ac662..fe65158c 100644 --- a/src/SFML/Graphics/GLExtensions.txt +++ b/src/SFML/Graphics/GLExtensions.txt @@ -7,6 +7,7 @@ EXT_blend_minmax EXT_blend_subtract ARB_multitexture EXT_blend_func_separate +ARB_vertex_buffer_object ARB_shading_language_100 ARB_shader_objects ARB_vertex_shader @@ -16,4 +17,5 @@ EXT_blend_equation_separate EXT_texture_sRGB EXT_framebuffer_object EXT_framebuffer_blit +ARB_copy_buffer ARB_geometry_shader4 diff --git a/src/SFML/Graphics/GLLoader.cpp b/src/SFML/Graphics/GLLoader.cpp index ec1531fb..2b459072 100644 --- a/src/SFML/Graphics/GLLoader.cpp +++ b/src/SFML/Graphics/GLLoader.cpp @@ -39,6 +39,7 @@ int sfogl_ext_EXT_blend_minmax = sfogl_LOAD_FAILED; int sfogl_ext_EXT_blend_subtract = sfogl_LOAD_FAILED; int sfogl_ext_ARB_multitexture = sfogl_LOAD_FAILED; int sfogl_ext_EXT_blend_func_separate = sfogl_LOAD_FAILED; +int sfogl_ext_ARB_vertex_buffer_object = sfogl_LOAD_FAILED; int sfogl_ext_ARB_shading_language_100 = sfogl_LOAD_FAILED; int sfogl_ext_ARB_shader_objects = sfogl_LOAD_FAILED; int sfogl_ext_ARB_vertex_shader = sfogl_LOAD_FAILED; @@ -48,6 +49,7 @@ int sfogl_ext_EXT_blend_equation_separate = sfogl_LOAD_FAILED; int sfogl_ext_EXT_texture_sRGB = sfogl_LOAD_FAILED; int sfogl_ext_EXT_framebuffer_object = sfogl_LOAD_FAILED; int sfogl_ext_EXT_framebuffer_blit = sfogl_LOAD_FAILED; +int sfogl_ext_ARB_copy_buffer = sfogl_LOAD_FAILED; int sfogl_ext_ARB_geometry_shader4 = sfogl_LOAD_FAILED; void (GL_FUNCPTR *sf_ptrc_glBlendEquationEXT)(GLenum) = NULL; @@ -254,6 +256,69 @@ static int Load_EXT_blend_func_separate() return numFailed; } +void (GL_FUNCPTR *sf_ptrc_glBindBufferARB)(GLenum, GLuint) = NULL; +void (GL_FUNCPTR *sf_ptrc_glBufferDataARB)(GLenum, GLsizeiptrARB, const void*, GLenum) = NULL; +void (GL_FUNCPTR *sf_ptrc_glBufferSubDataARB)(GLenum, GLintptrARB, GLsizeiptrARB, const void*) = NULL; +void (GL_FUNCPTR *sf_ptrc_glDeleteBuffersARB)(GLsizei, const GLuint*) = NULL; +void (GL_FUNCPTR *sf_ptrc_glGenBuffersARB)(GLsizei, GLuint*) = NULL; +void (GL_FUNCPTR *sf_ptrc_glGetBufferParameterivARB)(GLenum, GLenum, GLint*) = NULL; +void (GL_FUNCPTR *sf_ptrc_glGetBufferPointervARB)(GLenum, GLenum, void**) = NULL; +void (GL_FUNCPTR *sf_ptrc_glGetBufferSubDataARB)(GLenum, GLintptrARB, GLsizeiptrARB, void*) = NULL; +GLboolean (GL_FUNCPTR *sf_ptrc_glIsBufferARB)(GLuint) = NULL; +void* (GL_FUNCPTR *sf_ptrc_glMapBufferARB)(GLenum, GLenum) = NULL; +GLboolean (GL_FUNCPTR *sf_ptrc_glUnmapBufferARB)(GLenum) = NULL; + +static int Load_ARB_vertex_buffer_object() +{ + int numFailed = 0; + + sf_ptrc_glBindBufferARB = reinterpret_cast(glLoaderGetProcAddress("glBindBufferARB")); + if (!sf_ptrc_glBindBufferARB) + numFailed++; + + sf_ptrc_glBufferDataARB = reinterpret_cast(glLoaderGetProcAddress("glBufferDataARB")); + if (!sf_ptrc_glBufferDataARB) + numFailed++; + + sf_ptrc_glBufferSubDataARB = reinterpret_cast(glLoaderGetProcAddress("glBufferSubDataARB")); + if (!sf_ptrc_glBufferSubDataARB) + numFailed++; + + sf_ptrc_glDeleteBuffersARB = reinterpret_cast(glLoaderGetProcAddress("glDeleteBuffersARB")); + if (!sf_ptrc_glDeleteBuffersARB) + numFailed++; + + sf_ptrc_glGenBuffersARB = reinterpret_cast(glLoaderGetProcAddress("glGenBuffersARB")); + if (!sf_ptrc_glGenBuffersARB) + numFailed++; + + sf_ptrc_glGetBufferParameterivARB = reinterpret_cast(glLoaderGetProcAddress("glGetBufferParameterivARB")); + if (!sf_ptrc_glGetBufferParameterivARB) + numFailed++; + + sf_ptrc_glGetBufferPointervARB = reinterpret_cast(glLoaderGetProcAddress("glGetBufferPointervARB")); + if (!sf_ptrc_glGetBufferPointervARB) + numFailed++; + + sf_ptrc_glGetBufferSubDataARB = reinterpret_cast(glLoaderGetProcAddress("glGetBufferSubDataARB")); + if (!sf_ptrc_glGetBufferSubDataARB) + numFailed++; + + sf_ptrc_glIsBufferARB = reinterpret_cast(glLoaderGetProcAddress("glIsBufferARB")); + if (!sf_ptrc_glIsBufferARB) + numFailed++; + + sf_ptrc_glMapBufferARB = reinterpret_cast(glLoaderGetProcAddress("glMapBufferARB")); + if (!sf_ptrc_glMapBufferARB) + numFailed++; + + sf_ptrc_glUnmapBufferARB = reinterpret_cast(glLoaderGetProcAddress("glUnmapBufferARB")); + if (!sf_ptrc_glUnmapBufferARB) + numFailed++; + + return numFailed; +} + void (GL_FUNCPTR *sf_ptrc_glAttachObjectARB)(GLhandleARB, GLhandleARB) = NULL; void (GL_FUNCPTR *sf_ptrc_glCompileShaderARB)(GLhandleARB) = NULL; GLhandleARB (GL_FUNCPTR *sf_ptrc_glCreateProgramObjectARB)() = NULL; @@ -814,6 +879,19 @@ static int Load_EXT_framebuffer_blit() return numFailed; } +void (GL_FUNCPTR *sf_ptrc_glCopyBufferSubData)(GLenum, GLenum, GLintptr, GLintptr, GLsizeiptr) = NULL; + +static int Load_ARB_copy_buffer() +{ + int numFailed = 0; + + sf_ptrc_glCopyBufferSubData = reinterpret_cast(glLoaderGetProcAddress("glCopyBufferSubData")); + if (!sf_ptrc_glCopyBufferSubData) + numFailed++; + + return numFailed; +} + void (GL_FUNCPTR *sf_ptrc_glFramebufferTextureARB)(GLenum, GLenum, GLuint, GLint) = NULL; void (GL_FUNCPTR *sf_ptrc_glFramebufferTextureFaceARB)(GLenum, GLenum, GLuint, GLint, GLenum) = NULL; void (GL_FUNCPTR *sf_ptrc_glFramebufferTextureLayerARB)(GLenum, GLenum, GLuint, GLint, GLint) = NULL; @@ -850,13 +928,14 @@ typedef struct sfogl_StrToExtMap_s PFN_LOADFUNCPOINTERS LoadExtension; } sfogl_StrToExtMap; -static sfogl_StrToExtMap ExtensionMap[16] = { +static sfogl_StrToExtMap ExtensionMap[18] = { {"GL_SGIS_texture_edge_clamp", &sfogl_ext_SGIS_texture_edge_clamp, NULL}, {"GL_EXT_texture_edge_clamp", &sfogl_ext_EXT_texture_edge_clamp, NULL}, {"GL_EXT_blend_minmax", &sfogl_ext_EXT_blend_minmax, Load_EXT_blend_minmax}, {"GL_EXT_blend_subtract", &sfogl_ext_EXT_blend_subtract, NULL}, {"GL_ARB_multitexture", &sfogl_ext_ARB_multitexture, Load_ARB_multitexture}, {"GL_EXT_blend_func_separate", &sfogl_ext_EXT_blend_func_separate, Load_EXT_blend_func_separate}, + {"GL_ARB_vertex_buffer_object", &sfogl_ext_ARB_vertex_buffer_object, Load_ARB_vertex_buffer_object}, {"GL_ARB_shading_language_100", &sfogl_ext_ARB_shading_language_100, NULL}, {"GL_ARB_shader_objects", &sfogl_ext_ARB_shader_objects, Load_ARB_shader_objects}, {"GL_ARB_vertex_shader", &sfogl_ext_ARB_vertex_shader, Load_ARB_vertex_shader}, @@ -866,10 +945,11 @@ static sfogl_StrToExtMap ExtensionMap[16] = { {"GL_EXT_texture_sRGB", &sfogl_ext_EXT_texture_sRGB, NULL}, {"GL_EXT_framebuffer_object", &sfogl_ext_EXT_framebuffer_object, Load_EXT_framebuffer_object}, {"GL_EXT_framebuffer_blit", &sfogl_ext_EXT_framebuffer_blit, Load_EXT_framebuffer_blit}, + {"GL_ARB_copy_buffer", &sfogl_ext_ARB_copy_buffer, Load_ARB_copy_buffer}, {"GL_ARB_geometry_shader4", &sfogl_ext_ARB_geometry_shader4, Load_ARB_geometry_shader4} }; -static int g_extensionMapSize = 16; +static int g_extensionMapSize = 18; static void ClearExtensionVars() @@ -880,6 +960,7 @@ static void ClearExtensionVars() sfogl_ext_EXT_blend_subtract = sfogl_LOAD_FAILED; sfogl_ext_ARB_multitexture = sfogl_LOAD_FAILED; sfogl_ext_EXT_blend_func_separate = sfogl_LOAD_FAILED; + sfogl_ext_ARB_vertex_buffer_object = sfogl_LOAD_FAILED; sfogl_ext_ARB_shading_language_100 = sfogl_LOAD_FAILED; sfogl_ext_ARB_shader_objects = sfogl_LOAD_FAILED; sfogl_ext_ARB_vertex_shader = sfogl_LOAD_FAILED; @@ -889,6 +970,7 @@ static void ClearExtensionVars() sfogl_ext_EXT_texture_sRGB = sfogl_LOAD_FAILED; sfogl_ext_EXT_framebuffer_object = sfogl_LOAD_FAILED; sfogl_ext_EXT_framebuffer_blit = sfogl_LOAD_FAILED; + sfogl_ext_ARB_copy_buffer = sfogl_LOAD_FAILED; sfogl_ext_ARB_geometry_shader4 = sfogl_LOAD_FAILED; } diff --git a/src/SFML/Graphics/GLLoader.hpp b/src/SFML/Graphics/GLLoader.hpp index a802808c..4990d3d8 100644 --- a/src/SFML/Graphics/GLLoader.hpp +++ b/src/SFML/Graphics/GLLoader.hpp @@ -176,6 +176,7 @@ extern int sfogl_ext_EXT_blend_minmax; extern int sfogl_ext_EXT_blend_subtract; extern int sfogl_ext_ARB_multitexture; extern int sfogl_ext_EXT_blend_func_separate; +extern int sfogl_ext_ARB_vertex_buffer_object; extern int sfogl_ext_ARB_shading_language_100; extern int sfogl_ext_ARB_shader_objects; extern int sfogl_ext_ARB_vertex_shader; @@ -185,6 +186,7 @@ extern int sfogl_ext_EXT_blend_equation_separate; extern int sfogl_ext_EXT_texture_sRGB; extern int sfogl_ext_EXT_framebuffer_object; extern int sfogl_ext_EXT_framebuffer_blit; +extern int sfogl_ext_ARB_copy_buffer; extern int sfogl_ext_ARB_geometry_shader4; #define GL_CLAMP_TO_EDGE_SGIS 0x812F @@ -240,6 +242,38 @@ extern int sfogl_ext_ARB_geometry_shader4; #define GL_BLEND_SRC_ALPHA_EXT 0x80CB #define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_DYNAMIC_COPY_ARB 0x88EA +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_WRITE_ONLY_ARB 0x88B9 + #define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C #define GL_BOOL_ARB 0x8B56 @@ -385,6 +419,9 @@ extern int sfogl_ext_ARB_geometry_shader4; #define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA #define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 + #define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 #define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 #define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 @@ -1022,6 +1059,32 @@ extern void (GL_FUNCPTR *sf_ptrc_glBlendFuncSeparateEXT)(GLenum, GLenum, GLenum, #define glBlendFuncSeparateEXT sf_ptrc_glBlendFuncSeparateEXT #endif // GL_EXT_blend_func_separate +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +extern void (GL_FUNCPTR *sf_ptrc_glBindBufferARB)(GLenum, GLuint); +#define glBindBufferARB sf_ptrc_glBindBufferARB +extern void (GL_FUNCPTR *sf_ptrc_glBufferDataARB)(GLenum, GLsizeiptrARB, const void*, GLenum); +#define glBufferDataARB sf_ptrc_glBufferDataARB +extern void (GL_FUNCPTR *sf_ptrc_glBufferSubDataARB)(GLenum, GLintptrARB, GLsizeiptrARB, const void*); +#define glBufferSubDataARB sf_ptrc_glBufferSubDataARB +extern void (GL_FUNCPTR *sf_ptrc_glDeleteBuffersARB)(GLsizei, const GLuint*); +#define glDeleteBuffersARB sf_ptrc_glDeleteBuffersARB +extern void (GL_FUNCPTR *sf_ptrc_glGenBuffersARB)(GLsizei, GLuint*); +#define glGenBuffersARB sf_ptrc_glGenBuffersARB +extern void (GL_FUNCPTR *sf_ptrc_glGetBufferParameterivARB)(GLenum, GLenum, GLint*); +#define glGetBufferParameterivARB sf_ptrc_glGetBufferParameterivARB +extern void (GL_FUNCPTR *sf_ptrc_glGetBufferPointervARB)(GLenum, GLenum, void**); +#define glGetBufferPointervARB sf_ptrc_glGetBufferPointervARB +extern void (GL_FUNCPTR *sf_ptrc_glGetBufferSubDataARB)(GLenum, GLintptrARB, GLsizeiptrARB, void*); +#define glGetBufferSubDataARB sf_ptrc_glGetBufferSubDataARB +extern GLboolean (GL_FUNCPTR *sf_ptrc_glIsBufferARB)(GLuint); +#define glIsBufferARB sf_ptrc_glIsBufferARB +extern void* (GL_FUNCPTR *sf_ptrc_glMapBufferARB)(GLenum, GLenum); +#define glMapBufferARB sf_ptrc_glMapBufferARB +extern GLboolean (GL_FUNCPTR *sf_ptrc_glUnmapBufferARB)(GLenum); +#define glUnmapBufferARB sf_ptrc_glUnmapBufferARB +#endif // GL_ARB_vertex_buffer_object + #ifndef GL_ARB_shader_objects #define GL_ARB_shader_objects 1 @@ -1254,6 +1317,12 @@ extern void (GL_FUNCPTR *sf_ptrc_glBlitFramebufferEXT)(GLint, GLint, GLint, GLin #define glBlitFramebufferEXT sf_ptrc_glBlitFramebufferEXT #endif // GL_EXT_framebuffer_blit +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +extern void (GL_FUNCPTR *sf_ptrc_glCopyBufferSubData)(GLenum, GLenum, GLintptr, GLintptr, GLsizeiptr); +#define glCopyBufferSubData sf_ptrc_glCopyBufferSubData +#endif // GL_ARB_copy_buffer + #ifndef GL_ARB_geometry_shader4 #define GL_ARB_geometry_shader4 1 extern void (GL_FUNCPTR *sf_ptrc_glFramebufferTextureARB)(GLenum, GLenum, GLuint, GLint); diff --git a/src/SFML/Graphics/RenderTarget.cpp b/src/SFML/Graphics/RenderTarget.cpp index cba622cd..be2b2b6c 100644 --- a/src/SFML/Graphics/RenderTarget.cpp +++ b/src/SFML/Graphics/RenderTarget.cpp @@ -30,10 +30,21 @@ #include #include #include +#include #include #include #include #include +#include + + +// GL_QUADS is unavailable on OpenGL ES, thus we need to define GL_QUADS ourselves +#ifdef SFML_OPENGL_ES + + #define GL_QUADS 0 + +#endif // SFML_OPENGL_ES + namespace { @@ -211,17 +222,13 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, err() << "sf::Quads primitive type is not supported on OpenGL ES platforms, drawing skipped" << std::endl; return; } - #define GL_QUADS 0 #endif if (setActive(true)) { - // First set the persistent OpenGL states if it's the very first call - if (!m_cache.glStatesSet) - resetGLStates(); - // 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 @@ -232,32 +239,9 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, vertex.color = vertices[i].color; vertex.texCoords = vertices[i].texCoords; } - - // Since vertices are transformed, we must use an identity transform to render them - if (!m_cache.useVertexCache) - glCheck(glLoadIdentity()); - } - else - { - applyTransform(states.transform); } - // Apply the view - if (m_cache.viewChanged) - applyCurrentView(); - - // Apply the blend mode - if (states.blendMode != m_cache.lastBlendMode) - applyBlendMode(states.blendMode); - - // Apply the texture - 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); + setupDraw(useVertexCache, states); // Check if texture coordinates array is needed, and update client state accordingly bool enableTexCoordsArray = (states.texture || states.shader); @@ -292,22 +276,8 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), data + 12)); } - // Find the OpenGL primitive type - static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, - GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS}; - GLenum mode = modes[type]; - - // Draw the primitives - glCheck(glDrawArrays(mode, 0, static_cast(vertexCount))); - - // 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); + drawPrimitives(type, 0, vertexCount); + cleanupDraw(states); // Update the cache m_cache.useVertexCache = useVertexCache; @@ -316,6 +286,73 @@ void RenderTarget::draw(const Vertex* vertices, std::size_t vertexCount, } +//////////////////////////////////////////////////////////// +void RenderTarget::draw(const VertexBuffer& vertexBuffer, const RenderStates& states) +{ + draw(vertexBuffer, 0, vertexBuffer.getVertexCount(), states); +} + + +//////////////////////////////////////////////////////////// +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 (setActive(true)) + { + setupDraw(false, states); + + // Bind vertex buffer + VertexBuffer::bind(&vertexBuffer); + + // Always enable texture coordinates + if (!m_cache.texCoordsArrayEnabled) + glCheck(glEnableClientState(GL_TEXTURE_COORD_ARRAY)); + + glCheck(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(0))); + glCheck(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), reinterpret_cast(8))); + glCheck(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast(12))); + + drawPrimitives(vertexBuffer.getPrimitiveType(), firstVertex, vertexCount); + + // Unbind vertex buffer + VertexBuffer::bind(NULL); + + cleanupDraw(states); + + // Update the cache + m_cache.useVertexCache = false; + m_cache.texCoordsArrayEnabled = true; + } +} + + //////////////////////////////////////////////////////////// void RenderTarget::pushGLStates() { @@ -372,6 +409,7 @@ 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 @@ -411,6 +449,9 @@ void RenderTarget::resetGLStates() if (shaderAvailable) applyShader(NULL); + if (vertexBufferAvailable) + glCheck(VertexBuffer::bind(NULL)); + m_cache.texCoordsArrayEnabled = true; m_cache.useVertexCache = false; @@ -527,6 +568,70 @@ 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.useVertexCache) + glCheck(glLoadIdentity()); + } + else + { + applyTransform(states.transform); + } + + // Apply the view + if (m_cache.viewChanged) + applyCurrentView(); + + // Apply the blend mode + if (states.blendMode != m_cache.lastBlendMode) + applyBlendMode(states.blendMode); + + // Apply the texture + 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, firstVertex, static_cast(vertexCount))); +} + + +//////////////////////////////////////////////////////////// +void RenderTarget::cleanupDraw(const RenderStates& states) +{ + // Unbind the shader, if any + if (states.shader) + applyShader(NULL); + + // If the texture we used to draw belonged to a RenderTexture, then forcibly unbind that texture. + // This prevents a bug where some drivers do not clear RenderTextures properly. + if (states.texture && states.texture->m_fboAttachment) + applyTexture(NULL); +} + } // namespace sf diff --git a/src/SFML/Graphics/VertexBuffer.cpp b/src/SFML/Graphics/VertexBuffer.cpp new file mode 100644 index 00000000..c1cc11e9 --- /dev/null +++ b/src/SFML/Graphics/VertexBuffer.cpp @@ -0,0 +1,363 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2017 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + sf::Mutex isAvailableMutex; + + GLenum usageToGlEnum(sf::VertexBuffer::Usage usage) + { + switch (usage) + { + case sf::VertexBuffer::Static: return GLEXT_GL_STATIC_DRAW; + case sf::VertexBuffer::Dynamic: return GLEXT_GL_DYNAMIC_DRAW; + default: return GLEXT_GL_STREAM_DRAW; + } + } +} + + +namespace sf +{ +//////////////////////////////////////////////////////////// +VertexBuffer::VertexBuffer() : +m_buffer (0), +m_size (0), +m_primitiveType(Points), +m_usage (Stream) +{ +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::VertexBuffer(PrimitiveType type) : +m_buffer (0), +m_size (0), +m_primitiveType(type), +m_usage (Stream) +{ +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::VertexBuffer(VertexBuffer::Usage usage) : +m_buffer (0), +m_size (0), +m_primitiveType(Points), +m_usage (usage) +{ +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::VertexBuffer(PrimitiveType type, VertexBuffer::Usage usage) : +m_buffer (0), +m_size (0), +m_primitiveType(type), +m_usage (usage) +{ +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::VertexBuffer(const VertexBuffer& copy) : +m_buffer (0), +m_size (0), +m_primitiveType(copy.m_primitiveType), +m_usage (copy.m_usage) +{ + if (copy.m_buffer && copy.m_size) + { + if (!create(copy.m_size)) + { + err() << "Could not create vertex buffer for copying" << std::endl; + return; + } + + if (!update(copy)) + err() << "Could not copy vertex buffer" << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::~VertexBuffer() +{ + if (m_buffer) + { + TransientContextLock contextLock; + + glCheck(GLEXT_glDeleteBuffers(1, &m_buffer)); + } +} + + +//////////////////////////////////////////////////////////// +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; +} + + +//////////////////////////////////////////////////////////// +std::size_t VertexBuffer::getVertexCount() const +{ + return m_size; +} + + +//////////////////////////////////////////////////////////// +bool VertexBuffer::update(const Vertex* vertices) +{ + return update(vertices, m_size, 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; +} + + +//////////////////////////////////////////////////////////// +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 +} + + +//////////////////////////////////////////////////////////// +VertexBuffer& VertexBuffer::operator =(const VertexBuffer& right) +{ + VertexBuffer temp(right); + + swap(temp); + + return *this; +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::swap(VertexBuffer& right) +{ + std::swap(m_size, right.m_size); + std::swap(m_buffer, right.m_buffer); + std::swap(m_primitiveType, right.m_primitiveType); + std::swap(m_usage, right.m_usage); +} + + +//////////////////////////////////////////////////////////// +unsigned int VertexBuffer::getNativeHandle() const +{ + return m_buffer; +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::bind(const VertexBuffer* vertexBuffer) +{ + if (!isAvailable()) + return; + + TransientContextLock lock; + + glCheck(GLEXT_glBindBuffer(GLEXT_GL_ARRAY_BUFFER, vertexBuffer ? vertexBuffer->m_buffer : 0)); +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::setPrimitiveType(PrimitiveType type) +{ + m_primitiveType = type; +} + + +//////////////////////////////////////////////////////////// +PrimitiveType VertexBuffer::getPrimitiveType() const +{ + return m_primitiveType; +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::setUsage(VertexBuffer::Usage usage) +{ + m_usage = usage; +} + + +//////////////////////////////////////////////////////////// +VertexBuffer::Usage VertexBuffer::getUsage() const +{ + return m_usage; +} + + +//////////////////////////////////////////////////////////// +bool VertexBuffer::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; +} + + +//////////////////////////////////////////////////////////// +void VertexBuffer::draw(RenderTarget& target, RenderStates states) const +{ + if (m_buffer && m_size) + target.draw(*this, 0, m_size, states); +} + +} // namespace sf