From 75d238f50bd377caf56d1f7a9170b8921d9a7ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 13 Sep 2020 12:05:14 +0200 Subject: [PATCH] GL: implemented ARB_buffer_storage. --- doc/changelog.dox | 3 + doc/opengl-mapping.dox | 2 +- doc/opengl-support.dox | 2 +- src/Magnum/GL/Buffer.cpp | 17 +++ src/Magnum/GL/Buffer.h | 130 +++++++++++++++++-- src/Magnum/GL/Implementation/BufferState.cpp | 6 + src/Magnum/GL/Implementation/BufferState.h | 3 + src/Magnum/GL/Test/BufferGLTest.cpp | 23 ++++ 8 files changed, 175 insertions(+), 11 deletions(-) diff --git a/doc/changelog.dox b/doc/changelog.dox index 92d6c19ac..502ea4978 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -62,6 +62,9 @@ See also: means these can'be implemented right now. - Added a @ref GL::AbstractTexture::target() getter to simplify interaction with raw GL code +- Exposed @gl_extension{ARB,buffer_storage} as + @ref GL::Buffer::setStorage() together with additions to + @ref GL::Buffer::MapFlag @subsubsection changelog-latest-new-math Math library diff --git a/doc/opengl-mapping.dox b/doc/opengl-mapping.dox index 8e29c865c..41535d7a6 100644 --- a/doc/opengl-mapping.dox +++ b/doc/opengl-mapping.dox @@ -85,7 +85,7 @@ OpenGL function | Matching API @fn_gl{BlitFramebuffer}, \n `glBlitNamedFramebuffer()` | @ref GL::AbstractFramebuffer::blit() @fn_gl{BufferData}, \n `glNamedBufferData()` | @ref GL::Buffer::setData() @fn_gl_extension{BufferPageCommitment,ARB,sparse_buffer}, \n `glNamedBufferPageCommitmentEXT()`, \n `glNamedBufferPageCommitmentARB()` | | -@fn_gl{BufferStorage}, \n `glNamedBufferStorage()` | | +@fn_gl{BufferStorage}, \n `glNamedBufferStorage()` | @ref GL::Buffer::setStorage() @fn_gl{BufferSubData}, \n `glNamedBufferSubData()` | @ref GL::Buffer::setSubData() @subsection opengl-mapping-functions-c C diff --git a/doc/opengl-support.dox b/doc/opengl-support.dox index 5ef3d6fa5..7c626fe58 100644 --- a/doc/opengl-support.dox +++ b/doc/opengl-support.dox @@ -229,7 +229,7 @@ Extension | Status ------------------------------------------- | ------ GLSL 4.40 | done @def_gl{MAX_VERTEX_ATTRIB_STRIDE} | | -@gl_extension{ARB,buffer_storage} | | +@gl_extension{ARB,buffer_storage} | done @gl_extension{ARB,clear_texture} | | @gl_extension{ARB,enhanced_layouts} | done (shading language only) @gl_extension{ARB,multi_bind} | missing sampler and vertex buffer binding diff --git a/src/Magnum/GL/Buffer.cpp b/src/Magnum/GL/Buffer.cpp index 5f0e58771..4214b3e77 100644 --- a/src/Magnum/GL/Buffer.cpp +++ b/src/Magnum/GL/Buffer.cpp @@ -305,6 +305,13 @@ Buffer& Buffer::bind(const Target target, const UnsignedInt index) { } #endif +#ifndef MAGNUM_TARGET_GLES +Buffer& Buffer::setStorage(const Containers::ArrayView data, const StorageFlags flags) { + (this->*Context::current().state().buffer->storageImplementation)(data, flags); + return *this; +} +#endif + Int Buffer::size() { /** * @todo there is something like glGetBufferParameteri64v in 3.2 (I @@ -434,6 +441,16 @@ void Buffer::copyImplementationDSA(Buffer& read, Buffer& write, const GLintptr r #endif #endif +#ifndef MAGNUM_TARGET_GLES +void Buffer::storageImplementationDefault(Containers::ArrayView data, StorageFlags flags) { + glBufferStorage(GLenum(bindSomewhereInternal(_targetHint)), data.size(), data.data(), GLbitfield(flags)); +} + +void Buffer::storageImplementationDSA(Containers::ArrayView data, const StorageFlags flags) { + glNamedBufferStorage(_id, data.size(), data.data(), GLbitfield(flags)); +} +#endif + void Buffer::getParameterImplementationDefault(const GLenum value, GLint* const data) { glGetBufferParameteriv(GLenum(bindSomewhereInternal(_targetHint)), value, data); } diff --git a/src/Magnum/GL/Buffer.h b/src/Magnum/GL/Buffer.h index bb73b7b4e..3d486548a 100644 --- a/src/Magnum/GL/Buffer.h +++ b/src/Magnum/GL/Buffer.h @@ -196,19 +196,20 @@ and index buffers in WebGL. The engine tracks currently bound buffers to avoid unnecessary calls to @fn_gl_keyword{BindBuffer}. If the buffer is already bound to some target, -functions @ref copy(), @ref setData(), @ref setSubData(), @ref map(), -@ref mapRead(), @ref flushMappedRange() and @ref unmap() use that target -instead of binding the buffer to some specific target. You can also use +functions @ref copy(), @ref setStorage(), @ref setData(), @ref setSubData(), +@ref map(), @ref mapRead(), @ref flushMappedRange() and @ref unmap() use that +target instead of binding the buffer to some specific target. You can also use @ref setTargetHint() to possibly reduce unnecessary rebinding. Buffer limits and implementation-defined values (such as @ref maxUniformBindings()) are cached, so repeated queries don't result in repeated @fn_gl{Get} calls. See also @ref Context::resetState() and @ref Context::State::Buffers. If @gl_extension{ARB,direct_state_access} (part of OpenGL 4.5) is available, -functions @ref copy(), @ref size(), @ref data(), @ref subData(), @ref setData(), -@ref setSubData(), @ref map(), @ref mapRead(), @ref flushMappedRange() and -@ref unmap() use DSA functions to avoid unnecessary calls to @fn_gl{BindBuffer}. -See their respective documentation for more information. +functions @ref copy(), @ref setStorage(), @ref size(), @ref data(), +@ref subData(), @ref setData(), @ref setSubData(), @ref map(), @ref mapRead(), +@ref flushMappedRange() and @ref unmap() use DSA functions to avoid unnecessary +calls to @fn_gl{BindBuffer}. See their respective documentation for more +information. You can use functions @ref invalidateData() and @ref invalidateSubData() if you don't need buffer data anymore to avoid unnecessary memory operations performed @@ -489,9 +490,30 @@ class MAGNUM_GL_EXPORT Buffer: public AbstractObject { * before mapping. */ #ifndef MAGNUM_TARGET_GLES2 - Unsynchronized = GL_MAP_UNSYNCHRONIZED_BIT + Unsynchronized = GL_MAP_UNSYNCHRONIZED_BIT, #else - Unsynchronized = GL_MAP_UNSYNCHRONIZED_BIT_EXT + Unsynchronized = GL_MAP_UNSYNCHRONIZED_BIT_EXT, + #endif + + #ifndef MAGNUM_TARGET_GLES + /** + * Allow reading from or writing to the buffer while it is mapped. + * @m_since_latest + * @requires_gl44 Extension @gl_extension{ARB,buffer_storage} + * @requires_gl Buffer storage is not available in OpenGL ES and + * WebGL, use @ref setData() instead. + */ + Persistent = GL_MAP_PERSISTENT_BIT, + + /** + * Shared access to buffer that's both mapped and used will be + * coherent. + * @m_since_latest + * @requires_gl44 Extension @gl_extension{ARB,buffer_storage} + * @requires_gl Buffer storage is not available in OpenGL ES and + * WebGL, use @ref setData() instead. + */ + Coherent = GL_MAP_COHERENT_BIT #endif }; @@ -507,6 +529,53 @@ class MAGNUM_GL_EXPORT Buffer: public AbstractObject { typedef Containers::EnumSet MapFlags; #endif + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Buffer storage flag + * @m_since_latest + * + * @see @ref StorageFlags, @ref setStorage() + * @m_enum_values_as_keywords + * @requires_gl44 Extension @gl_extension{ARB,buffer_storage} + * @requires_gl Buffer storage is not available in OpenGL ES and WebGL, + * use @ref setData() instead. + */ + enum class StorageFlag: GLbitfield { + /** Allow the buffer to be mapped with @ref MapFlag::Read. */ + MapRead = GL_MAP_READ_BIT, + + /** Allow the buffer to be mapped with @ref MapFlag::Write. */ + MapWrite = GL_MAP_WRITE_BIT, + + /** Allow the buffer to be mapped with @ref MapFlag::Persistent. */ + MapPersistent = GL_MAP_PERSISTENT_BIT, + + /** Allow the buffer to be mapped with @ref MapFlag::Coherent. */ + MapCoherent = GL_MAP_COHERENT_BIT, + + /** + * Allow the buffer to be updated with @ref setSubData(). Note that + * the buffer can still be updated through @ref copy() even without + * this flag present. + */ + DynamicStorage = GL_DYNAMIC_STORAGE_BIT, + + /** Prefer to allocate the memory in client memory space. */ + ClientStorage = GL_CLIENT_STORAGE_BIT + }; + + /** + * @brief Buffer storage flags + * @m_since_latest + * + * @see @ref setStorage() + * @requires_gl44 Extension @gl_extension{ARB,buffer_storage} + * @requires_gl Buffer storage is not available in OpenGL ES and WebGL, + * use @ref setData() instead. + */ + typedef Containers::EnumSet StorageFlags; + #endif + #ifndef MAGNUM_TARGET_GLES /** * @brief Minimal supported mapping alignment @@ -963,6 +1032,40 @@ class MAGNUM_GL_EXPORT Buffer: public AbstractObject { Buffer& bind(Target target, UnsignedInt index); #endif + #ifndef MAGNUM_TARGET_GLES + /** + * @brief Set storage + * @param data Data + * @param flags Storage flags + * @m_since_latest + * + * If @gl_extension{ARB,direct_state_access} (part of OpenGL 4.5) is + * not available, the buffer is bound to hinted target before the + * operation (if not already). + * @see @ref setTargetHint(), + * @fn_gl2_keyword{NamedBufferStorage,BufferStorage}, eventually + * @fn_gl{BindBuffer} and @fn_gl_keyword{BufferStorage} + * @requires_gl44 Extension @gl_extension{ARB,buffer_storage} + * @requires_gl Buffer storage is not available in OpenGL ES and WebGL, + * use @ref setData() instead. + */ + Buffer& setStorage(Containers::ArrayView data, StorageFlags flags); + + /** + * @brief Set storage + * @param size Size in bytes + * @param flags Storage flags + * @m_since_latest + * + * Equivalent to calling @ref setStorage(Containers::ArrayView, StorageFlags) + * with a @cpp nullptr @ce view of @p size bytes. + * @requires_gl44 Extension @gl_extension{ARB,buffer_storage} + * @requires_gl Buffer storage is not available in OpenGL ES and WebGL, + * use @ref setData() instead. + */ + Buffer& setStorage(std::size_t size, StorageFlags flags); + #endif + /** * @brief Buffer size in bytes * @@ -1236,6 +1339,11 @@ class MAGNUM_GL_EXPORT Buffer: public AbstractObject { Buffer& setLabelInternal(Containers::ArrayView label); #endif + #ifndef MAGNUM_TARGET_GLES + void MAGNUM_GL_LOCAL storageImplementationDefault(Containers::ArrayView data, StorageFlags flags); + void MAGNUM_GL_LOCAL storageImplementationDSA(Containers::ArrayView data, StorageFlags flags); + #endif + void MAGNUM_GL_LOCAL getParameterImplementationDefault(GLenum value, GLint* data); #ifndef MAGNUM_TARGET_GLES void MAGNUM_GL_LOCAL getParameterImplementationDSA(GLenum value, GLint* data); @@ -1323,6 +1431,10 @@ class MAGNUM_GL_EXPORT Buffer: public AbstractObject { CORRADE_ENUMSET_OPERATORS(Buffer::MapFlags) #endif +#ifndef MAGNUM_TARGET_GLES +CORRADE_ENUMSET_OPERATORS(Buffer::StorageFlags) +#endif + /** @debugoperatorclassenum{Buffer,Buffer::TargetHint} */ MAGNUM_GL_EXPORT Debug& operator<<(Debug& debug, Buffer::TargetHint value); diff --git a/src/Magnum/GL/Implementation/BufferState.cpp b/src/Magnum/GL/Implementation/BufferState.cpp index 25231e861..99a68a859 100644 --- a/src/Magnum/GL/Implementation/BufferState.cpp +++ b/src/Magnum/GL/Implementation/BufferState.cpp @@ -100,6 +100,9 @@ BufferState::BufferState(Context& context, std::vector& extensions) createImplementation = &Buffer::createImplementationDSA; copyImplementation = &Buffer::copyImplementationDSA; + #ifndef MAGNUM_TARGET_GLES + storageImplementation = &Buffer::storageImplementationDSA; + #endif getParameterImplementation = &Buffer::getParameterImplementationDSA; getSubDataImplementation = &Buffer::getSubDataImplementationDSA; dataImplementation = &Buffer::dataImplementationDSA; @@ -115,6 +118,9 @@ BufferState::BufferState(Context& context, std::vector& extensions) #ifndef MAGNUM_TARGET_GLES2 copyImplementation = &Buffer::copyImplementationDefault; #endif + #ifndef MAGNUM_TARGET_GLES + storageImplementation = &Buffer::storageImplementationDefault; + #endif getParameterImplementation = &Buffer::getParameterImplementationDefault; #ifndef MAGNUM_TARGET_GLES getSubDataImplementation = &Buffer::getSubDataImplementationDefault; diff --git a/src/Magnum/GL/Implementation/BufferState.h b/src/Magnum/GL/Implementation/BufferState.h index a3ef4ec55..fcde643c4 100644 --- a/src/Magnum/GL/Implementation/BufferState.h +++ b/src/Magnum/GL/Implementation/BufferState.h @@ -57,6 +57,9 @@ struct BufferState { #endif void(Buffer::*createImplementation)(); void(Buffer::*setTargetHintImplementation)(Buffer::TargetHint); + #ifndef MAGNUM_TARGET_GLES + void(Buffer::*storageImplementation)(Containers::ArrayView, Buffer::StorageFlags); + #endif void(Buffer::*getParameterImplementation)(GLenum, GLint*); #ifndef MAGNUM_TARGET_GLES2 void(Buffer::*getSubDataImplementation)(GLintptr, GLsizeiptr, GLvoid*); diff --git a/src/Magnum/GL/Test/BufferGLTest.cpp b/src/Magnum/GL/Test/BufferGLTest.cpp index 6dba01e78..43f104f70 100644 --- a/src/Magnum/GL/Test/BufferGLTest.cpp +++ b/src/Magnum/GL/Test/BufferGLTest.cpp @@ -54,6 +54,10 @@ struct BufferGLTest: OpenGLTester { void bindRange(); #endif + #ifndef MAGNUM_TARGET_GLES + void storage(); + #endif + void data(); #ifndef MAGNUM_TARGET_WEBGL void map(); @@ -81,6 +85,10 @@ BufferGLTest::BufferGLTest() { &BufferGLTest::bindRange, #endif + #ifndef MAGNUM_TARGET_GLES + &BufferGLTest::storage, + #endif + &BufferGLTest::data, #ifndef MAGNUM_TARGET_WEBGL &BufferGLTest::map, @@ -241,6 +249,21 @@ void BufferGLTest::bindRange() { } #endif +#ifndef MAGNUM_TARGET_GLES +void BufferGLTest::storage() { + Buffer buffer; + + constexpr Int data[] = {2, 7, 5, 13, 25}; + + buffer.setStorage(data, Buffer::StorageFlag::MapRead|Buffer::StorageFlag::ClientStorage); + MAGNUM_VERIFY_NO_GL_ERROR(); + + CORRADE_COMPARE_AS(Containers::arrayCast(buffer.data()), + Containers::arrayView(data), + TestSuite::Compare::Container); +} +#endif + void BufferGLTest::data() { Buffer buffer;