diff --git a/include/pymol/span.h b/include/pymol/span.h new file mode 100644 index 000000000..f4d09a298 --- /dev/null +++ b/include/pymol/span.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +namespace pymol +{ +template class span +{ +public: + span() = default; + + span(T* data, std::size_t size) + : data_(data) + , size_(size) + { + } + + template + span(RangeT& range) + : data_(range.data()) + , size_(range.size()) + { + } + + T& operator[](std::size_t index) { return data_[index]; } + + std::size_t size() const { return size_; } + + T* data() { return data_; } + + const T* data() const { return data_; } + + using value_type = T; + +private: + T* data_{}; + std::size_t size_{}; +}; + +template span(RangeT&) -> span; + +template +span(const RangeT&) -> span; + +template span as_bytes(RangeT& range) +{ + return span(reinterpret_cast(range.data()), + range.size() * sizeof(typename RangeT::value_type)); +} + +} // namespace pymol diff --git a/layer0/GenericBuffer.cpp b/layer0/GenericBuffer.cpp index fcb081cfc..ab021a48a 100644 --- a/layer0/GenericBuffer.cpp +++ b/layer0/GenericBuffer.cpp @@ -197,286 +197,6 @@ std::size_t GetSizeOfVertexFormat(VertexFormat format) } } -GenericBuffer::GenericBuffer(buffer_layout layout, MemoryProperty memProperty) - : m_memProperty(memProperty) - , m_layout(layout) -{ -} - -GenericBuffer::~GenericBuffer() -{ - for (auto i = 0; i < m_desc.size(); ++i) { - auto& glID = desc_glIDs[i]; - if (glID) { - glDeleteBuffers(1, &glID); - } - } - if (m_interleavedID) { - glDeleteBuffers(1, &m_interleavedID); - } -} - -bool GenericBuffer::bufferData(BufferDataDesc&& desc) -{ - m_desc = std::move(desc); - desc_glIDs = std::vector(m_desc.size()); - return evaluate(); -} - -bool GenericBuffer::bufferData( - BufferDataDesc&& desc, const void* data, size_t len, size_t stride) -{ - bool ok = true; - m_desc = std::move(desc); - desc_glIDs = std::vector(m_desc.size()); - m_interleaved = true; - m_stride = stride; - ok = genBuffer(m_interleavedID, len, data); - return ok; -} - -void GenericBuffer::bufferSubData(size_t offset, size_t size, void* data, size_t index) -{ - assert("Invalid Desc index" && index < m_desc.size()); - assert("Invalid GLDesc index" && index < desc_glIDs.size()); - auto glID = m_interleaved ? m_interleavedID : desc_glIDs[index]; - glBindBuffer(bufferType(), glID); - glBufferSubData(bufferType(), offset, size, data); -} - -void GenericBuffer::bufferReplaceData(size_t offset, size_t len, const void* data) -{ - glBindBuffer(bufferType(), m_interleavedID); - glBufferSubData(bufferType(), offset, len, data); -} - -const BufferDataDesc& GenericBuffer::getVertDesc() const -{ - return m_desc; -} - -bool GenericBuffer::evaluate() -{ - if (bufferType() == GL_ELEMENT_ARRAY_BUFFER) { - return seqBufferData(); - } else { - switch (m_layout) { - case buffer_layout::SEPARATE: - return sepBufferData(); - break; - case buffer_layout::SEQUENTIAL: - return seqBufferData(); - break; - case buffer_layout::INTERLEAVED: - return interleaveBufferData(); - break; - } - } - return true; // unreacheable/Should be false? -} - -bool GenericBuffer::sepBufferData() -{ - for (auto i = 0; i < m_desc.size(); ++i) { - // If the specified size is 0 but we have a valid pointer - // then we are going to glVertexAttribXfv X in {1,2,3,4} - const auto& d = m_desc[i]; - auto& glID = desc_glIDs[i]; - if (d.data_ptr && (m_memProperty == MemoryProperty::DeviceLocal)) { - if (d.data_size) { - if (!genBuffer(glID, d.data_size, d.data_ptr)) { - return false; - } - } - } - } - return true; -} - -bool GenericBuffer::seqBufferData() { - // this is only going to use a single opengl vbo - m_interleaved = true; - - size_t buffer_size { 0 }; - for ( auto & d : m_desc ) { - buffer_size += d.data_size; - } - - std::vector buffer_data(buffer_size); - auto data_ptr = buffer_data.data(); - size_t offset = 0; - - for ( auto & d : m_desc ) { - d.offset = offset; - if (d.data_ptr) - memcpy(data_ptr, d.data_ptr, d.data_size); - else - memset(data_ptr, 0, d.data_size); - data_ptr += d.data_size; - offset += d.data_size; - } - - return genBuffer(m_interleavedID, buffer_size, buffer_data.data()); -} - -bool GenericBuffer::interleaveBufferData() -{ - const std::size_t bufferCount = m_desc.size(); - std::size_t stride = 0; - std::vector data_table(bufferCount); - std::vector ptr_table(bufferCount); - std::vector size_table(bufferCount); - std::size_t count = - m_desc[0].data_size / GetSizeOfVertexFormat(m_desc[0].m_format); - - // Maybe assert that all pointers in d_desc are valid? - for (size_t i = 0; i < bufferCount; ++i) { - auto& d = m_desc[i]; - // offset is the current stride - d.offset = stride; - - // These must come after so that offset starts at 0 - // Size of 3 normals or whatever the current type is - size_table[i] = GetSizeOfVertexFormat(d.m_format); - - // Increase our current estimate of the stride by this amount - stride += size_table[i]; - - // Does the addition of that previous stride leave us on a word boundry? - int m = stride % 4; - stride = (m ? (stride + (4 - m)) : stride); - - // data_table a pointer to the begining of each array - data_table[i] = static_cast(d.data_ptr); - - // We will move these pointers along by the values in the size table - ptr_table[i] = data_table[i]; - } - - m_stride = stride; - - std::size_t interleavedSize = count * stride; - - std::vector interleavedData(interleavedSize); - auto iPtr = interleavedData.data(); - - while (iPtr != (interleavedData.data() + interleavedSize)) { - for (size_t i = 0; i < bufferCount; ++i) { - if (ptr_table[i]) { - memcpy(iPtr, ptr_table[i], size_table[i]); - ptr_table[i] += size_table[i]; - } - iPtr += size_table[i]; - } - } - - m_interleaved = true; - return genBuffer(m_interleavedID, interleavedSize, interleavedData.data()); -} - -bool GenericBuffer::genBuffer(GLuint& id, size_t size, const void* ptr) -{ - glGenBuffers(1, &id); - if (!CheckGLErrorOK(nullptr, "GenericBuffer::genBuffer failed\n")) - return false; - glBindBuffer(bufferType(), id); - if (!CheckGLErrorOK(nullptr, "GenericBuffer::bindBuffer failed\n")) - return false; - glBufferData(bufferType(), size, ptr, GL_STATIC_DRAW); - if (!CheckGLErrorOK(nullptr, "GenericBuffer::bufferData failed\n")) - return false; - return true; -} - -void VertexBufferGL::bind_attrib(GLuint prg, const BufferDesc& d, GLuint glID) -{ - GLint loc = glGetAttribLocation(prg, d.attr_name); - auto type_dim = VertexFormatToGLSize(d.m_format); - auto type = VertexFormatToGLType(d.m_format); - auto data_norm = VertexFormatToGLNormalized(d.m_format); - bool masked = false; - for (GLint lid : m_attribmask) - if (lid == loc) - masked = true; - if (loc >= 0) - m_locs.push_back(loc); - if (loc >= 0 && !masked) { - if (!m_interleaved && glID) - glBindBuffer(bufferType(), glID); - glEnableVertexAttribArray(loc); - glVertexAttribPointer(loc, type_dim, type, data_norm, m_stride, - reinterpret_cast(d.offset)); - } -}; - -VertexBufferGL::VertexBufferGL(buffer_layout layout, MemoryProperty memProperty) - : GenericBuffer(layout, memProperty) -{ -} - -void VertexBufferGL::bind() const -{ - // we shouldn't use this one - if (m_interleaved) - glBindBuffer(bufferType(), m_interleavedID); -} - -void VertexBufferGL::bind(GLuint prg, int index) -{ - if (index >= 0) { - glBindBuffer(bufferType(), m_interleavedID); - bind_attrib(prg, m_desc[index], desc_glIDs[index]); - } else { - if (m_interleaved && m_interleavedID) - glBindBuffer(bufferType(), m_interleavedID); - for (auto i = 0; i < m_desc.size(); ++i) { - const auto& d = m_desc[i]; - auto glID = desc_glIDs[i]; - bind_attrib(prg, d, glID); - } - m_attribmask.clear(); - } -} - -void VertexBufferGL::unbind() -{ - for (auto& d : m_locs) { - glDisableVertexAttribArray(d); - } - m_locs.clear(); - glBindBuffer(bufferType(), 0); -} - -void VertexBufferGL::maskAttributes(std::vector attrib_locs) -{ - m_attribmask = std::move(attrib_locs); -} - -void VertexBufferGL::maskAttribute(GLint attrib_loc) -{ - m_attribmask.push_back(attrib_loc); -} - -std::uint32_t VertexBufferGL::bufferType() const -{ - return GL_ARRAY_BUFFER; -} - -void IndexBufferGL::bind() const -{ - glBindBuffer(bufferType(), m_interleavedID); -} - -void IndexBufferGL::unbind() -{ - glBindBuffer(bufferType(), 0); -} - -std::uint32_t IndexBufferGL::bufferType() const -{ - return GL_ELEMENT_ARRAY_BUFFER; -} - /*********************************************************************** * RENDERBUFFER ***********************************************************************/ diff --git a/layer0/GenericBuffer.h b/layer0/GenericBuffer.h index 46b2008e2..a0115d2b6 100644 --- a/layer0/GenericBuffer.h +++ b/layer0/GenericBuffer.h @@ -1,59 +1,15 @@ #pragma once // ----------------------------------------------------------------------------- +#include "GPUEnums.h" +#include "GPUVertexDesc.h" #include "Vector.h" #include #include -#include #include #include #include -#include #include -enum class VertexFormat { - // 8 bit - Byte, - Byte2, - Byte3, - Byte4, - ByteNorm, - Byte2Norm, - Byte3Norm, - Byte4Norm, - UByte, - UByte2, - UByte3, - UByte4, - UByteNorm, - UByte2Norm, - UByte3Norm, - UByte4Norm, - - //Single Precision - Float, - Float2, - Float3, - Float4, - - // 32 bit - Int, - Int2, - Int3, - Int4, - UInt, - UInt2, - UInt3, - UInt4, -}; - -enum class VertexFormatBaseType { Byte, UByte, Float, Int, UInt }; - -/** - * @param format Vertex format - * @return The base type of the vertex format -*/ -VertexFormatBaseType GetVertexFormatBaseType(VertexFormat format); - /** * @param format Vertex format * @return The GL type of the vertex format @@ -66,50 +22,12 @@ GLenum VertexFormatToGLType(VertexFormat format); */ GLint VertexFormatToGLSize(VertexFormat format); -/** - * @param format Vertex format - * @return Whether the vertex format is a normalized type -*/ -bool VertexFormatIsNormalized(VertexFormat format); - /** * @param format Vertex format * @return Whether the vertex format is a normalized type as a GLboolean */ GLboolean VertexFormatToGLNormalized(VertexFormat format); -/** - * @param format Vertex format - * @return The size of the vertex format in bytes -*/ -std::size_t GetSizeOfVertexFormat(VertexFormat format); - - -// ----------------------------------------------------------------------------- -// DESCRIPTORS -// ----------------------------------------------------------------------------- -// Describes a single array held in the vbo -struct BufferDesc { - BufferDesc(const char* _attr_name, VertexFormat _format, - std::size_t _data_size = 0, const void* _data_ptr = nullptr, - std::uint32_t offset = 0) - : attr_name(_attr_name) - , m_format(_format) - , data_size(_data_size) - , data_ptr(_data_ptr) - , offset(offset) - { - } - - const char* attr_name{nullptr}; - VertexFormat m_format{VertexFormat::Float}; - std::size_t data_size{}; - const void* data_ptr{nullptr}; - std::uint32_t offset{}; -}; - -using BufferDataDesc = std::vector< BufferDesc >; - /* different types of AttribOp */ enum attrib_op_type { NO_COPY = 0, @@ -244,186 +162,6 @@ private: size_t _hashid { 0 }; }; -enum class buffer_layout { - SEPARATE, // multiple vbos - SEQUENTIAL, // single vbo - INTERLEAVED // single vbo -}; - -enum class MemoryProperty { - DeviceLocal, - HostVisible, -}; - -// ----------------------------------------------------------------------------- -/* Vertexbuffer rules: - * ----------------------------------------------------------------------------- - * - If the buffer data is interleaved then buffer sub data functionality cannot - * be used. - * - The same order of buffer data must be maintained when uploading and binding - * - *----------------------------------------------------------------------- - * USAGE_PATTERN: - * SEPARATE: - * vbo1 [ data1 ] - * vbo2 [ data2 ] - * ... - * vboN [ dataN ] - * SEQUENTIAL: - * vbo [ data1 | data2 | ... | dataN ] - * INTERLEAVED: - * vbo [ data1[0], data2[0], ..., dataN[0] | ... | data1[M], data2[M], ..., dataN[M] ] - */ -class GenericBuffer : public GPUBuffer -{ - friend class CShaderMgr; - -public: - GenericBuffer(buffer_layout layout = buffer_layout::SEPARATE, - MemoryProperty properties = MemoryProperty::DeviceLocal); - GenericBuffer(const GenericBuffer&) = delete; - GenericBuffer& operator=(const GenericBuffer&) = delete; - GenericBuffer(GenericBuffer&&) = delete; - GenericBuffer& operator=(GenericBuffer&&) = delete; - ~GenericBuffer(); - - /** - * Conditionally generates a GPU buffer for the given data descriptor - * @param desc The buffer data descriptor - * @return Whether the buffer data was successfully buffered - * @note The supplied data ptr in the struct can - * be zero, in which case if the default usage is STATIC_DRAW then no - * opengl buffer will be generated for that, else it is assumed that the - * data will be supplied at a later point because it's dynamic draw. - */ - bool bufferData(BufferDataDesc&& desc); - - /** - * Generates a GPU buffer for the given data descriptor - * @param desc The buffer data descriptor - * @param data The data to buffer - * @param len The length of the data - * @param stride The stride of the data - * @note assumes the data is interleaved - */ - bool bufferData( - BufferDataDesc&& desc, const void* data, size_t len, size_t stride); - - // ----------------------------------------------------------------------------- - - /** - * Updates (a portion of) the buffer data - * @param offset The offset to start updating the buffer data - * @param size The size of the data to update - * @param data The data to update - * @param index The index of the buffer data to update - * @note This function assumes that the data layout hasn't changed - */ - void bufferSubData(size_t offset, size_t size, void* data, size_t index = 0); - - /** - * Replaces the whole interleaved buffer data - * @param data The data to replace the buffer data with - * @param len The length of the data - * @param data The data to replace the buffer data with - * @note This function assumes that the data layout hasn't changed - */ - void bufferReplaceData(size_t offset, size_t len, const void* data); - - const BufferDataDesc& getVertDesc() const; - -protected: - - /** - * Generates GPU buffer(s) for the given data descriptor - * @return Whether the buffer data was successfully buffered - */ - bool evaluate(); - - // USAGE PATTERNS - - /** - * Generates a separate buffer for each data descriptor - * @return Whether the buffer data was successfully buffered - */ - bool sepBufferData(); - - /** - * Generates a single sequential buffer for all data descriptors - * @return Whether the buffer data was successfully buffered - */ - bool seqBufferData(); - - /** - * Generates a single interleaved buffer for all data descriptors - * @return Whether the buffer data was successfully buffered - */ - bool interleaveBufferData(); - - /** - * Generates an OpenGL buffer with given size and data - * @param id The OpenGL buffer ID - * @param size The size of the buffer - * @param ptr The data to buffer - * @return Whether the buffer was successfully generated - */ - bool genBuffer(GLuint& id, size_t size, const void* ptr); - - /** - * @return OpenGL buffer type - */ - virtual GLenum bufferType() const = 0; - -protected: - bool m_status{false}; - bool m_interleaved{false}; - GLuint m_interleavedID{0}; - MemoryProperty m_memProperty{MemoryProperty::DeviceLocal}; - const buffer_layout m_layout{ buffer_layout::SEPARATE }; - size_t m_stride{0}; - BufferDataDesc m_desc; - std::vector desc_glIDs; // m_desc's gl buffer IDs -}; - -/** - * Vertex buffer specialization - */ -class VertexBufferGL : public GenericBuffer { - void bind_attrib(GLuint prg, const BufferDesc& d, GLuint glID); - -public: - VertexBufferGL(buffer_layout layout = buffer_layout::SEPARATE, - MemoryProperty property = MemoryProperty::DeviceLocal); - - void bind() const override; - - void bind(GLuint prg, int index = -1); - - void unbind(); - - void maskAttributes(std::vector attrib_locs); - void maskAttribute(GLint attrib_loc); - - GLenum bufferType() const override; - -private: - // m_locs is only for interleaved data - std::vector m_locs; - std::vector m_attribmask; -}; - -/** - * Index buffer specialization - */ -class IndexBufferGL : public GenericBuffer { -public: - using GenericBuffer::GenericBuffer; - - void bind() const override; - void unbind(); - GLenum bufferType() const override; -}; - // Forward Decls class FramebufferGL; class RenderbufferGL; diff --git a/layer1/CGO.cpp b/layer1/CGO.cpp index 145f8336b..49ea5f486 100644 --- a/layer1/CGO.cpp +++ b/layer1/CGO.cpp @@ -47,6 +47,7 @@ Z* ------------------------------------------------------------------- #include "VFont.h" #include "Vector.h" #include "GraphicsUtil.h" +#include "GLVertexBuffer.h" #include "pymol/algorithm.h" @@ -2229,6 +2230,69 @@ static NormalColorFormatSize GetNormalColorFormatSize(PyMOLGlobals* G) return fmt; } +// TODO: The following functions are temporary until OpenGL is a backend + +/** + * @brief Create a vertex buffer + * @param layout Vertex buffer layout + * @param memProperty Usage property for Vertex buffer + * @return Reference to created Vertex Buffer (no mem allocated) + */ +static VertexBuffer& CreateVertexBuffer(PyMOLGlobals* G, + VertexBufferLayout layout = VertexBufferLayout::Separate, + MemoryUsageProperty memProperty = MemoryUsageProperty::GpuOnly) +{ + return *G->ShaderMgr->newGPUBuffer(layout, memProperty); +} + +/** + * @brief Allocates GPU memory for vertex buffer and sets data + * @param dataDesc Buffer data description + * @param dataOverride Optional data override + */ +static void SetVertexBufferData(PyMOLGlobals* G, VertexBuffer& vertexBuffer, + BufferDataDesc&& dataDesc, pymol::span dataOverride = {}) +{ + auto* vbo = static_cast(&vertexBuffer); + vbo->bufferData(std::move(dataDesc)); +} + +/** + * @brief Allocates GPU memory for vertex buffer and sets data + * @param vertexBuffer Vertex buffer + * @param dataDesc Buffer data description + * @param bufferSize Size of the buffer + * @param dataOverride Optional data override + */ +static void SetVertexBufferDataSized(PyMOLGlobals* G, + VertexBuffer& vertexBuffer, BufferDataDesc&& dataDesc, + std::size_t bufferSize, std::byte* dataOverride = nullptr) +{ + auto* vbo = static_cast(&vertexBuffer); + vbo->bufferData(std::move(dataDesc), dataOverride, bufferSize); +} + +/** + * @brief Create an index buffer + * @return Reference to created Index Buffer (no mem allocated) + */ +static IndexBuffer& CreateIndexBuffer(PyMOLGlobals* G) +{ + return *G->ShaderMgr->newGPUBuffer(); +} + +/** + * @brief Allocates GPU memory for index buffer and sets data + * @param indexBuffer Index buffer + * @param data Index data + */ +static void SetIndexBufferData(PyMOLGlobals* G, IndexBuffer& indexBuffer, + pymol::span data) +{ + auto* ibo = static_cast(&indexBuffer); + ibo->copyFrom(data); +} + static int OptimizePointsToVBO(const CGO* I, CGO* cgo, int num_total_vertices_points, float* min, float* max, short* has_draw_buffer, bool addshaders) @@ -2368,26 +2432,26 @@ static int OptimizePointsToVBO(const CGO* I, CGO* cgo, short arrays = CGO_VERTEX_ARRAY | CGO_PICK_COLOR_ARRAY; auto fmt = GetNormalColorFormatSize(I->G); - auto* vbo = I->G->ShaderMgr->newGPUBuffer(); + auto& vbo = CreateVertexBuffer(I->G); - BufferDataDesc bufData = {{"a_Vertex", VertexFormat::Float3, - sizeof(float) * num_total_vertices_points * 3, vertexVals}}; + BufferDataDesc bufData{{{"a_Vertex", VertexFormat::Float3, + sizeof(float) * num_total_vertices_points * 3, vertexVals}}}; if (has_normals) { - bufData.push_back({"a_Normal", fmt.normalFormat, + bufData.descs.push_back({"a_Normal", fmt.normalFormat, num_total_vertices_points * fmt.normalSize, normalVals}); } if (has_colors) { - bufData.push_back({"a_Color", fmt.colorFormat, + bufData.descs.push_back({"a_Color", fmt.colorFormat, num_total_vertices_points * fmt.colorSize, colorVals}); } - ok = vbo->bufferData(std::move(bufData)); + SetVertexBufferData(I->G, vbo, std::move(bufData)); if (ok && has_colors) { arrays |= CGO_COLOR_ARRAY; } - size_t vboid = vbo->get_hash_id(); + auto vboid = vbo.get_hash_id(); if (ok) { float* newPickColorVals; @@ -2915,34 +2979,36 @@ static bool OptimizeVertsToVBONotIndexed(const CGO* I, CGO* cgo, if (ok) { auto fmt = GetNormalColorFormatSize(G); - auto* vbo = - G->ShaderMgr->newGPUBuffer(buffer_layout::SEQUENTIAL); - BufferDataDesc bufData = {{"a_Vertex", VertexFormat::Float3, - sizeof(float) * count.num_total_indexes * 3, vertexVals}}; + auto& vbo = CreateVertexBuffer( + G, VertexBufferLayout::Sequential); + BufferDataDesc bufData{{{"a_Vertex", VertexFormat::Float3, + sizeof(float) * count.num_total_indexes * 3, vertexVals}}}; if (has_normals) { - bufData.push_back({"a_Normal", fmt.normalFormat, + bufData.descs.push_back({"a_Normal", fmt.normalFormat, count.num_total_indexes * fmt.normalSize, normalVals}); } if (has_colors) { - bufData.push_back({"a_Color", fmt.colorFormat, + bufData.descs.push_back({"a_Color", fmt.colorFormat, count.num_total_indexes * fmt.colorSize, colorVals}); } if (has_accessibility) { - bufData.push_back({"a_Accessibility", VertexFormat::Float, + bufData.descs.push_back({"a_Accessibility", VertexFormat::Float, sizeof(float) * count.num_total_indexes, accessibilityVals}); } - ok = vbo->bufferData(std::move(bufData)); + SetVertexBufferData(I->G, vbo, std::move(bufData)); - size_t vboid = vbo->get_hash_id(); + auto vboid = vbo.get_hash_id(); // picking VBO: generate a buffer twice the size needed, for each picking // pass - auto* pickvbo = G->ShaderMgr->newGPUBuffer( - buffer_layout::SEQUENTIAL, MemoryProperty::HostVisible); - ok = pickvbo->bufferData({BufferDesc{"a_Color", VertexFormat::UByte4Norm, - sizeof(float) * count.num_total_indexes}, + auto& pickvbo = CreateVertexBuffer( + G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu); + BufferDataDesc pickDataDesc{{// BufferDesc{"a_Color", VertexFormat::UByte4Norm, - sizeof(float) * count.num_total_indexes}}); - size_t pickvboid = pickvbo->get_hash_id(); + sizeof(float) * count.num_total_indexes}, + BufferDesc{"a_Color", VertexFormat::UByte4Norm, + sizeof(float) * count.num_total_indexes}}}; + SetVertexBufferData(G, pickvbo, std::move(pickDataDesc)); + auto pickvboid = pickvbo.get_hash_id(); if (ok) { float* newPickColorVals; @@ -3179,31 +3245,32 @@ static bool OptimizeLinesToVBONotIndexed(const CGO* I, CGO* cgo, } auto fmt = GetNormalColorFormatSize(G); - auto* vbo = - G->ShaderMgr->newGPUBuffer(buffer_layout::SEQUENTIAL); - BufferDataDesc bufData = {{"a_Vertex", VertexFormat::Float3, - sizeof(float) * count.num_total_indexes_lines * 3, vertexVals}}; + auto& vbo = CreateVertexBuffer(G, VertexBufferLayout::Sequential); + BufferDataDesc bufData{{{"a_Vertex", VertexFormat::Float3, + sizeof(float) * count.num_total_indexes_lines * 3, vertexVals}}}; if (has_normals) { - bufData.push_back({"a_Normal", fmt.normalFormat, + bufData.descs.push_back({"a_Normal", fmt.normalFormat, count.num_total_indexes_lines * fmt.normalSize, normalVals}); } if (has_color) { - bufData.push_back({"a_Color", fmt.colorFormat, + bufData.descs.push_back({"a_Color", fmt.colorFormat, count.num_total_indexes_lines * fmt.colorSize, colorVals}); } - ok = vbo->bufferData(std::move(bufData)); - size_t vboid = vbo->get_hash_id(); + SetVertexBufferData(I->G, vbo, std::move(bufData)); + auto vboid = vbo.get_hash_id(); // picking VBO: generate a buffer twice the size needed, for each picking // pass - auto* pickvbo = G->ShaderMgr->newGPUBuffer( - buffer_layout::SEQUENTIAL, MemoryProperty::HostVisible); - ok &= pickvbo->bufferData({BufferDesc("a_Color", VertexFormat::UByte4Norm, - sizeof(float) * count.num_total_indexes_lines), + auto& pickvbo = CreateVertexBuffer( + G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu); + BufferDataDesc pickDataDesc{{// BufferDesc("a_Color", VertexFormat::UByte4Norm, - sizeof(float) * count.num_total_indexes_lines)}); - size_t pickvboid = pickvbo->get_hash_id(); + sizeof(float) * count.num_total_indexes_lines), + BufferDesc("a_Color", VertexFormat::UByte4Norm, + sizeof(float) * count.num_total_indexes_lines)}}; + SetVertexBufferData(G, pickvbo, std::move(pickDataDesc)); + auto pickvboid = pickvbo.get_hash_id(); if (ok) { float* newPickColorVals; @@ -3573,32 +3640,33 @@ static bool OptimizeVertsToVBOIndexed(const CGO* I, CGO* cgo, if (ok) { auto fmt = GetNormalColorFormatSize(I->G); - auto* vbo = I->G->ShaderMgr->newGPUBuffer(); - ok &= vbo->bufferData( - {BufferDesc{"a_Vertex", VertexFormat::Float3, - sizeof(float) * count.num_total_vertices * 3, vertexVals}, - BufferDesc{"a_Normal", fmt.normalFormat, - count.num_total_vertices * fmt.normalSize, normalVals}, - BufferDesc{"a_Color", fmt.colorFormat, - count.num_total_vertices * fmt.colorSize, colorVals}, - BufferDesc{"a_Accessibility", VertexFormat::Float, - sizeof(float) * count.num_total_vertices, accessibilityVals}}); + auto& vbo = CreateVertexBuffer(I->G); + BufferDataDesc dataDesc{{// + BufferDesc{"a_Vertex", VertexFormat::Float3, + sizeof(float) * count.num_total_vertices * 3, vertexVals}, + BufferDesc{"a_Normal", fmt.normalFormat, + count.num_total_vertices * fmt.normalSize, normalVals}, + BufferDesc{"a_Color", fmt.colorFormat, + count.num_total_vertices * fmt.colorSize, colorVals}, + BufferDesc{"a_Accessibility", VertexFormat::Float, + sizeof(float) * count.num_total_vertices, accessibilityVals}}}; + SetVertexBufferData(I->G, vbo, std::move(dataDesc)); - auto* ibo = I->G->ShaderMgr->newGPUBuffer(); - ok &= ibo->bufferData({BufferDesc{nullptr, VertexFormat::UInt, - sizeof(VertexIndex_t) * count.num_total_indexes, vertexIndices.data()}}); + auto& ibo = CreateIndexBuffer(I->G); + SetIndexBufferData(I->G, ibo, vertexIndices); - size_t vboid = vbo->get_hash_id(); - size_t iboid = ibo->get_hash_id(); + auto vboid = vbo.get_hash_id(); + auto iboid = ibo.get_hash_id(); - - auto* pickvbo = I->G->ShaderMgr->newGPUBuffer( - buffer_layout::SEQUENTIAL, MemoryProperty::HostVisible); - ok &= pickvbo->bufferData({BufferDesc{"a_Color", VertexFormat::UByte4Norm, - sizeof(float) * count.num_total_indexes}, + auto& pickvbo = CreateVertexBuffer( + I->G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu); + BufferDataDesc pickDataDesc{{// BufferDesc{"a_Color", VertexFormat::UByte4Norm, - sizeof(float) * count.num_total_indexes}}); - size_t pickvboid = pickvbo->get_hash_id(); + sizeof(float) * count.num_total_indexes}, + BufferDesc{"a_Color", VertexFormat::UByte4Norm, + sizeof(float) * count.num_total_indexes}}}; + SetVertexBufferData(I->G, pickvbo, std::move(pickDataDesc)); + auto pickvboid = pickvbo.get_hash_id(); if (ok) { float* newPickColorVals; @@ -3859,31 +3927,32 @@ static bool OptimizeLinesToVBOIndexed(const CGO* I, CGO* cgo, if (ok) { auto fmt = GetNormalColorFormatSize(I->G); - auto* vbo = I->G->ShaderMgr->newGPUBuffer(); - ok &= vbo->bufferData({ + auto& vbo = CreateVertexBuffer(I->G); + BufferDataDesc dataDesc{{// BufferDesc{"a_Vertex", VertexFormat::Float3, sizeof(float) * count.num_total_vertices_lines * 3, vertexVals}, BufferDesc{"a_Normal", fmt.normalFormat, count.num_total_vertices_lines * fmt.normalSize, normalVals}, BufferDesc{"a_Color", fmt.colorFormat, count.num_total_vertices_lines * fmt.colorSize, colorVals}, - }); + }}; + SetVertexBufferData(I->G, vbo, std::move(dataDesc)); - auto* ibo = I->G->ShaderMgr->newGPUBuffer(); - ok &= ibo->bufferData({BufferDesc(nullptr, VertexFormat::UInt, - sizeof(VertexIndex_t) * count.num_total_indexes_lines, - vertexIndexes.data())}); + auto& ibo = CreateIndexBuffer(I->G); + SetIndexBufferData(I->G, ibo, vertexIndexes); - size_t vboid = vbo->get_hash_id(); - size_t iboid = ibo->get_hash_id(); + auto vboid = vbo.get_hash_id(); + auto iboid = ibo.get_hash_id(); - auto* pickvbo = I->G->ShaderMgr->newGPUBuffer( - buffer_layout::SEQUENTIAL, MemoryProperty::HostVisible); - ok &= pickvbo->bufferData({BufferDesc("a_Color", VertexFormat::UByte4Norm, - sizeof(float) * count.num_total_indexes), + auto& pickvbo = CreateVertexBuffer( + I->G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu); + BufferDataDesc pickDataDesc{{// BufferDesc("a_Color", VertexFormat::UByte4Norm, - sizeof(float) * count.num_total_indexes)}); - size_t pickvboid = pickvbo->get_hash_id(); + sizeof(float) * count.num_total_indexes), + BufferDesc("a_Color", VertexFormat::UByte4Norm, + sizeof(float) * count.num_total_indexes)}}; + SetVertexBufferData(I->G, pickvbo, std::move(pickDataDesc)); + auto pickvboid = pickvbo.get_hash_id(); if (ok) { if (addshaders) { @@ -4204,7 +4273,7 @@ static OptimizeSphereData GetOptimizeSphereData( * @pre cgo must not be nullptr */ -static bool PopulateGLBufferOptimizedSphereData(const CGO* I, CGO* cgo, +static bool PopulateBufferOptimizedSphereData(const CGO* I, CGO* cgo, bool addshaders, int num_total_spheres, const OptimizeSphereData& sphereData) { @@ -4220,8 +4289,8 @@ static bool PopulateGLBufferOptimizedSphereData(const CGO* I, CGO* cgo, radiusptr = sphereData.rightUpFlagsUB.data(); } - auto* vbo = I->G->ShaderMgr->newGPUBuffer(); - ok &= vbo->bufferData({ + auto& vbo = CreateVertexBuffer(I->G); + BufferDataDesc vertDescs{{// BufferDesc("a_vertex_radius", VertexFormat::Float4, sizeof(float) * sphereData.total_vert * 4, sphereData.vert.data()), BufferDesc("a_Color", VertexFormat::UByte4Norm, @@ -4229,16 +4298,20 @@ static bool PopulateGLBufferOptimizedSphereData(const CGO* I, CGO* cgo, BufferDesc("a_rightUpFlags", rtp, rsz * sphereData.total_vert * VALUES_PER_IMPOSTER_SPACE_COORD, radiusptr), - }); - size_t vboid = vbo->get_hash_id(); + }}; + SetVertexBufferData(I->G, vbo, std::move(vertDescs)); + auto vboid = vbo.get_hash_id(); - auto* pickvbo = I->G->ShaderMgr->newGPUBuffer( - buffer_layout::SEQUENTIAL, MemoryProperty::HostVisible); - ok &= pickvbo->bufferData({BufferDesc("a_Color", VertexFormat::UByte4Norm, 0), - BufferDesc("a_Color", VertexFormat::UByte4Norm, - sizeof(float) * sphereData.total_vert)}, - 0, sizeof(float) * sphereData.total_vert * 2, 0); - size_t pickvboid = pickvbo->get_hash_id(); + auto& pickvbo = CreateVertexBuffer( + I->G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu); + BufferDataDesc pickDataDesc{{// + BufferDesc("a_Color", VertexFormat::UByte4Norm, 0), + BufferDesc("a_Color", VertexFormat::UByte4Norm, + sizeof(float) * sphereData.total_vert)}}; + auto size = sizeof(float) * sphereData.total_vert * 2; + SetVertexBufferDataSized(I->G, pickvbo, std::move(pickDataDesc), size); + + auto pickvboid = pickvbo.get_hash_id(); cgo->has_draw_buffers = true; cgo->has_draw_sphere_buffers = true; @@ -4295,7 +4368,7 @@ CGO* CGOOptimizeSpheresToVBONonIndexed( GetOptimizeSphereData(I, cgo, num_total_spheres, leftOverCGO); if (ok && sphereData.total_spheres > 0) { - ok = PopulateGLBufferOptimizedSphereData( + ok = PopulateBufferOptimizedSphereData( I, cgo, addshaders, num_total_spheres, sphereData); } @@ -4324,7 +4397,7 @@ CGO* CGOOptimizeBezier(const CGO* I) { auto cgo = std::make_unique(I->G); int num_splines = CGOCountNumberOfOperationsOfType(I, CGO_BEZIER); - auto vbo = I->G->ShaderMgr->newGPUBuffer(); + auto& vbo = CreateVertexBuffer(I->G); std::vector vertData; vertData.reserve(num_splines * CGO_BEZIER_SZ); for (auto it = I->begin(); !it.is_stop(); ++it) { @@ -4341,11 +4414,12 @@ CGO* CGOOptimizeBezier(const CGO* I) std::size_t numDimensions = 3; std::size_t numVerts = 4; - vbo->bufferData({ + BufferDataDesc dataDesc{{ BufferDesc("position", VertexFormat::Float3, sizeof(float) * numVerts * numDimensions, vertData.data()), - }); - size_t vboid = vbo->get_hash_id(); + }}; + SetVertexBufferData(I->G, vbo, std::move(dataDesc)); + auto vboid = vbo.get_hash_id(); CGOEnable(cgo.get(), GL_BEZIER_SHADER); cgo->add(vboid); @@ -4904,16 +4978,16 @@ CGO* CGOOptimizeTextures(const CGO* I, int est) ok &= !I->G->Interrupt; } if (ok) { - auto* vbo = I->G->ShaderMgr->newGPUBuffer( - buffer_layout::SEQUENTIAL); - ok &= vbo->bufferData( - {BufferDesc("attr_worldpos", VertexFormat::Float3, - sizeof(float) * num_total_textures * 18, worldPos.data()), - BufferDesc("attr_screenoffset", VertexFormat::Float3, - sizeof(float) * num_total_textures * 18, screenValues.data()), - BufferDesc("attr_texcoords", VertexFormat::Float3, - sizeof(float) * num_total_textures * 18, textExtents.data())}); - size_t vboid = vbo->get_hash_id(); + auto& vbo = CreateVertexBuffer(I->G, VertexBufferLayout::Sequential); + BufferDataDesc dataDesc{{ + BufferDesc("attr_worldpos", VertexFormat::Float3, + sizeof(float) * num_total_textures * 18, worldPos.data()), + BufferDesc("attr_screenoffset", VertexFormat::Float3, + sizeof(float) * num_total_textures * 18, screenValues.data()), + BufferDesc("attr_texcoords", VertexFormat::Float3, + sizeof(float) * num_total_textures * 18, textExtents.data())}}; + SetVertexBufferData(I->G, vbo, std::move(dataDesc)); + auto vboid = vbo.get_hash_id(); if (ok) { float* pickArray = @@ -5030,7 +5104,7 @@ CGO* CGOConvertToLabelShader(const CGO* I, CGO* addTo) AttribDataDesc pickDesc = { {"attr_pickcolor", VertexFormat::UByte4Norm, pickOp}}; return CGOConvertToShader( - I, attrDesc, pickDesc, GL_TRIANGLES, buffer_layout::INTERLEAVED, true); + I, attrDesc, pickDesc, GL_TRIANGLES, VertexBufferLayout::Interleaved, true); } CGO* CGOOptimizeLabels(const CGO* I, int est, bool addshaders) @@ -5141,31 +5215,33 @@ CGO* CGOOptimizeLabels(const CGO* I, int est, bool addshaders) } if (ok) { // Static Vertex Data - auto* vbo = I->G->ShaderMgr->newGPUBuffer( - buffer_layout::SEQUENTIAL); - ok &= - vbo->bufferData({BufferDesc("attr_worldpos", VertexFormat::Float3, - sizeof(float) * num_total_labels * 18, worldPos.data()), - BufferDesc("attr_targetpos", VertexFormat::Float3, - sizeof(float) * num_total_labels * 18, targetPos), - BufferDesc("attr_screenoffset", VertexFormat::Float3, - sizeof(float) * num_total_labels * 18, screenValues), - BufferDesc("attr_texcoords", VertexFormat::Float2, - sizeof(float) * num_total_labels * 12, textExtents), - BufferDesc("attr_screenworldoffset", VertexFormat::Float3, - sizeof(float) * num_total_labels * 18, screenWorldValues), - BufferDesc("attr_relative_mode", VertexFormat::Float, - sizeof(float) * num_total_labels * 6, relativeMode)}); - size_t vboid = vbo->get_hash_id(); + auto& vbo = CreateVertexBuffer(I->G, VertexBufferLayout::Sequential); + BufferDataDesc dataDesc{{ + BufferDesc("attr_worldpos", VertexFormat::Float3, + sizeof(float) * num_total_labels * 18, worldPos.data()), + BufferDesc("attr_targetpos", VertexFormat::Float3, + sizeof(float) * num_total_labels * 18, targetPos), + BufferDesc("attr_screenoffset", VertexFormat::Float3, + sizeof(float) * num_total_labels * 18, screenValues), + BufferDesc("attr_texcoords", VertexFormat::Float2, + sizeof(float) * num_total_labels * 12, textExtents), + BufferDesc("attr_screenworldoffset", VertexFormat::Float3, + sizeof(float) * num_total_labels * 18, screenWorldValues), + BufferDesc("attr_relative_mode", VertexFormat::Float, + sizeof(float) * num_total_labels * 6, relativeMode)}}; + SetVertexBufferData(I->G, vbo, std::move(dataDesc)); + auto vboid = vbo.get_hash_id(); - auto* pickvbo = I->G->ShaderMgr->newGPUBuffer( - buffer_layout::SEQUENTIAL, MemoryProperty::HostVisible); - ok &= pickvbo->bufferData( - {BufferDesc("attr_pickcolor", VertexFormat::UByteNorm, 0), - BufferDesc("attr_pickcolor", VertexFormat::UByteNorm, - sizeof(float) * num_total_labels * 6)}, - 0, sizeof(float) * num_total_labels * 12, 0); - size_t pickvboid = pickvbo->get_hash_id(); + auto& pickvbo = CreateVertexBuffer( + I->G, VertexBufferLayout::Sequential, MemoryUsageProperty::CpuToGpu); + BufferDataDesc pickDataDesc{{ + BufferDesc("attr_pickcolor", VertexFormat::UByteNorm, 0), + BufferDesc("attr_pickcolor", VertexFormat::UByteNorm, + sizeof(float) * num_total_labels * 6)}}; + std::size_t bufferSize = sizeof(float) * num_total_labels * 12; + SetVertexBufferDataSized( + I->G, pickvbo, std::move(pickDataDesc), bufferSize); + auto pickvboid = pickvbo.get_hash_id(); auto freebuffers = [vboid, pickvboid, I]() { I->G->ShaderMgr->freeGPUBuffer(vboid); @@ -5312,9 +5388,10 @@ CGO* CGOOptimizeConnectors(const CGO* I, int est) } if (ok) { const size_t quant = factor * num_total_connectors; - auto* vbo = I->G->ShaderMgr->newGPUBuffer(); - ok = vbo->bufferData({BufferDesc("a_target_pt3d", VertexFormat::Float3, - sizeof(float) * 3 * quant, targetPt3d.data()), + auto& vbo = CreateVertexBuffer(I->G); + BufferDataDesc bufferDataDesc{{// + BufferDesc("a_target_pt3d", VertexFormat::Float3, + sizeof(float) * 3 * quant, targetPt3d.data()), BufferDesc("a_center_pt3d", VertexFormat::Float3, sizeof(float) * 3 * quant, labelCenterPt3d), BufferDesc("a_indentFactor", VertexFormat::Float2, @@ -5336,8 +5413,9 @@ CGO* CGOOptimizeConnectors(const CGO* I, int est) BufferDesc("a_con_width", VertexFormat::Float, sizeof(float) * quant, connectorWidth), BufferDesc("a_isCenterPt", VertexFormat::UByte, - sizeof(std::uint8_t) * quant, isCenterPt)}); - size_t vboid = vbo->get_hash_id(); + sizeof(std::uint8_t) * quant, isCenterPt)}}; + SetVertexBufferData(I->G, vbo, std::move(bufferDataDesc)); + auto vboid = vbo.get_hash_id(); if (ok) { cgo->add(num_total_connectors, vboid); if (ok) @@ -7500,15 +7578,16 @@ CGO* CGOOptimizeScreenTexturesAndPolygons(CGO* I, int est) G, I, vertexVals, texcoordVals, colorVals, colorValsUC); RETURN_VAL_IF_FAIL(ok && !G->Interrupt, nullptr); if (ok) { - auto* vbo = I->G->ShaderMgr->newGPUBuffer(); - ok = vbo->bufferData( - {BufferDesc("attr_screenoffset", VertexFormat::Float3, - sizeof(float) * num_total_indices * 3, vertexVals), - BufferDesc("attr_texcoords", VertexFormat::Float2, - sizeof(float) * num_total_indices * 2, texcoordVals), - BufferDesc("attr_backgroundcolor", VertexFormat::UByte4Norm, - sizeof(std::uint8_t) * num_total_indices * 4, colorValsUC)}); - size_t vboid = vbo->get_hash_id(); + auto& vbo = CreateVertexBuffer(I->G); + BufferDataDesc dataDesc{{ + BufferDesc("attr_screenoffset", VertexFormat::Float3, + sizeof(float) * num_total_indices * 3, vertexVals), + BufferDesc("attr_texcoords", VertexFormat::Float2, + sizeof(float) * num_total_indices * 2, texcoordVals), + BufferDesc("attr_backgroundcolor", VertexFormat::UByte4Norm, + sizeof(std::uint8_t) * num_total_indices * 4, colorValsUC)}}; + SetVertexBufferData(I->G, vbo, std::move(dataDesc)); + auto vboid = vbo.get_hash_id(); if (ok) { CGOEnable(cgo, GL_SCREEN_SHADER); cgo->add(num_total_indices, vboid); @@ -8971,13 +9050,13 @@ static void PopulateOpsIntoStructuresForConversion( * */ CGO* CGOConvertToShader(const CGO* I, AttribDataDesc& attrData, - AttribDataDesc& pickData, int mode, const buffer_layout layout, + AttribDataDesc& pickData, int mode, const VertexBufferLayout layout, bool check_attr_for_data, int* idx_array, int nvertsperfrag, int nfragspergroup) { CGO* cgo; int ok = true; - bool isInterleaved = (layout == buffer_layout::INTERLEAVED); + bool isInterleaved = (layout == VertexBufferLayout::Interleaved); bool has_picking = true; std::map attrToDesc; @@ -9057,11 +9136,11 @@ CGO* CGOConvertToShader(const CGO* I, AttribDataDesc& attrData, // - pickvbo is interleaved so that for multiple channels, pick data for each // vertex // is contiguous - auto* vbo = I->G->ShaderMgr->newGPUBuffer(layout); - VertexBufferGL* pickvbo = nullptr; + auto& vbo = CreateVertexBuffer(I->G, layout); + VertexBuffer* pickvbo = nullptr; if (pickDataSize) { - pickvbo = I->G->ShaderMgr->newGPUBuffer( - buffer_layout::SEQUENTIAL, MemoryProperty::HostVisible); + pickvbo = &CreateVertexBuffer(I->G, VertexBufferLayout::Sequential, + MemoryUsageProperty::CpuToGpu); pickvbohash = pickvbo->get_hash_id(); } @@ -9089,7 +9168,7 @@ CGO* CGOConvertToShader(const CGO* I, AttribDataDesc& attrData, for (auto& pickDesc : pickData) { cgo->add( I->G->ShaderMgr->GetAttributeUID(pickDesc.attr_name), - vbo->get_hash_id()); + vbo.get_hash_id()); if (has_picking) { cgo->add(pickvbohash, pl++, npickattr); } else { @@ -9115,15 +9194,14 @@ CGO* CGOConvertToShader(const CGO* I, AttribDataDesc& attrData, } vpl += nvertsperindivfrag; } - auto* ibo = I->G->ShaderMgr->newGPUBuffer(); - ok &= ibo->bufferData({BufferDesc(nullptr, VertexFormat::UInt, - sizeof(VertexIndex_t) * num_total_indexes, vertexIndices.data())}); - iboid = ibo->get_hash_id(); + auto& ibo = CreateIndexBuffer(I->G); + SetIndexBufferData(I->G, ibo, vertexIndices); + iboid = ibo.get_hash_id(); } // pick_data is interleaved if more than one attribute float* pick_data = cgo->add(mode, ntotalverts, - vbo->get_hash_id(), pickvbohash, vertsperpickinfo, pickDataSize, iboid, + vbo.get_hash_id(), pickvbohash, vertsperpickinfo, pickDataSize, iboid, num_total_indexes); std::vector allData(ntotalverts * vertexDataSize); std::vector dataPtrs; @@ -9332,19 +9410,19 @@ CGO* CGOConvertToShader(const CGO* I, AttribDataDesc& attrData, for (int i = 0; i < npickcolattr; i++) { for (auto& pickDesc : pickData) { auto pickSize = GetSizeOfVertexFormat(pickDesc.m_format); - pickBufferData.push_back(BufferDesc( + pickBufferData.descs.push_back(BufferDesc( pickDesc.attr_name, pickDesc.m_format, pickSize * nvert)); } } - pickvbo->bufferData(std::move(pickBufferData)); + SetVertexBufferData(I->G, *pickvbo, std::move(pickBufferData)); } /* Generate VBO Buffers with all pick attributes based on the VertexBuffer * type SEPARATE/SEQUENTIAL/INTERLEAVED*/ BufferDataDesc bufferData; switch (layout) { - case buffer_layout::SEPARATE: - case buffer_layout::SEQUENTIAL: { + case VertexBufferLayout::Separate: + case VertexBufferLayout::Sequential: { auto attrDataIt = attrData.begin(); auto dataPtrIt = dataPtrs.begin(); auto attrSizeIt = attrSizes.begin(); @@ -9354,24 +9432,25 @@ CGO* CGOConvertToShader(const CGO* I, AttribDataDesc& attrData, auto attrDesc = &(*attrDataIt); auto dataPtr = *dataPtrIt; auto attrSize = *attrSizeIt; - bufferData.push_back(BufferDesc( + bufferData.descs.push_back(BufferDesc( attrDesc->attr_name, attrDesc->m_format, nvert * attrSize, dataPtr)); } - vbo->bufferData(std::move(bufferData)); + SetVertexBufferData(I->G, vbo, std::move(bufferData)); break; } break; - case buffer_layout::INTERLEAVED: { + case VertexBufferLayout::Interleaved: { auto attrDataIt = attrData.begin(); auto attrOffsetIt = attrOffset.begin(); for (; attrDataIt != attrData.end() && attrOffsetIt != attrOffset.end(); ++attrDataIt, ++attrOffsetIt) { auto attrDesc = &(*attrDataIt); auto offset = *attrOffsetIt; - bufferData.push_back(BufferDesc{attrDesc->attr_name, attrDesc->m_format, + bufferData.descs.push_back(BufferDesc{attrDesc->attr_name, attrDesc->m_format, 0, nullptr, (std::uint32_t) offset}); } - vbo->bufferData(std::move(bufferData), allData.data(), - (size_t) (nvert * vertexDataSize), (size_t) vertexDataSize); + bufferData.stride = static_cast(vertexDataSize); + SetVertexBufferDataSized(I->G, vbo, std::move(bufferData), + nvert * vertexDataSize, reinterpret_cast(allData.data())); break; } } @@ -9523,7 +9602,7 @@ CGO* CGOConvertToTrilinesShader(const CGO* I, CGO* addTo, bool add_color) } return CGOConvertToShader( - I, attrDesc, pickDesc, GL_TRIANGLES, buffer_layout::INTERLEAVED); + I, attrDesc, pickDesc, GL_TRIANGLES, VertexBufferLayout::Interleaved); } CGO* CGOConvertToLinesShader(const CGO* I, CGO* addTo, bool add_color) @@ -9579,7 +9658,7 @@ CGO* CGOConvertToLinesShader(const CGO* I, CGO* addTo, bool add_color) } return CGOConvertToShader( - I, attrDesc, pickDesc, GL_LINES, buffer_layout::INTERLEAVED); + I, attrDesc, pickDesc, GL_LINES, VertexBufferLayout::Interleaved); } CGO* CGOConvertLinesToCylinderShader(const CGO* I, CGO* addTo, bool add_color) @@ -9664,7 +9743,7 @@ CGO* CGOConvertLinesToCylinderShader(const CGO* I, CGO* addTo, bool add_color) {"a_Color", VertexFormat::UByte4Norm, extraPickColorOps}, {"a_Color2", VertexFormat::UByte4Norm, extraPickColor2Ops}}; return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES, - buffer_layout::INTERLEAVED, true, box_indices_ptr, 36); + VertexBufferLayout::Interleaved, true, box_indices_ptr, 36); } struct CrossSizeData { @@ -9748,7 +9827,7 @@ CGO* CGOConvertCrossesToCylinderShader( {"a_Color", VertexFormat::UByte4Norm, extraPickColorOps}, {"a_Color2", VertexFormat::UByte4Norm, extraPickColor2Ops}}; return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES, - buffer_layout::INTERLEAVED, true, box_indices_ptr, 36, 3); + VertexBufferLayout::Interleaved, true, box_indices_ptr, 36, 3); } struct CrossSizeDataLines { @@ -9814,7 +9893,7 @@ CGO* CGOConvertCrossesToLinesShader( } #endif return CGOConvertToShader( - I, attrDesc, pickDesc, GL_LINES, buffer_layout::INTERLEAVED); + I, attrDesc, pickDesc, GL_LINES, VertexBufferLayout::Interleaved); } static void CrossVertexConversionTrilines( @@ -9872,7 +9951,7 @@ CGO* CGOConvertCrossesToTrilinesShader( G->ShaderMgr->GetAttributeUID("a_interpolate"), 0.f); return CGOConvertToShader( - I, attrDesc, pickDesc, GL_TRIANGLES, buffer_layout::INTERLEAVED); + I, attrDesc, pickDesc, GL_TRIANGLES, VertexBufferLayout::Interleaved); } cgo::draw::shadercylinder2ndcolor::shadercylinder2ndcolor(CGO* I, @@ -10041,7 +10120,7 @@ CGO* CGOConvertShaderCylindersToCylinderShader(const CGO* I, CGO* addTo) {"a_Color", VertexFormat::UByte4Norm, extraPickColorOps}, {"a_Color2", VertexFormat::UByte4Norm, extraPickColor2Ops}}; return CGOConvertToShader(I, attrDesc, pickDesc, GL_TRIANGLES, - buffer_layout::INTERLEAVED, true, box_indices_ptr, 36); + VertexBufferLayout::Interleaved, true, box_indices_ptr, 36); } /** diff --git a/layer1/CGO.h b/layer1/CGO.h index f6fa61104..faacde823 100644 --- a/layer1/CGO.h +++ b/layer1/CGO.h @@ -17,22 +17,26 @@ Z* ------------------------------------------------------------------- #ifndef _H_CGO #define _H_CGO -#include"Base.h" -#include"Ray.h" -#include"Setting.h" -#include"os_gl_cgo.h" -#include"Rep.h" -#include"MemoryDebug.h" -#include -#include -#include -#include -#include +#include "Base.h" +#include "GPUEnums.h" #include "GenericBuffer.h" -#include +#include "MemoryDebug.h" +#include "Ray.h" +#include "Rep.h" +#include "Setting.h" +#include "os_gl_cgo.h" + #include #include + #include +#include +#include +#include +#include +#include +#include +#include /* Compiled Graphics Library for simple graphics objects in floating point three-space, with the goal of achieving @@ -1185,7 +1189,7 @@ CGO *CGOConvertSpheresToPoints(const CGO *I); CGO* CGOConvertToShader(const CGO* I, AttribDataDesc& attrData, AttribDataDesc& pickData, int mode, - const buffer_layout layout = buffer_layout::INTERLEAVED, + const VertexBufferLayout layout = VertexBufferLayout::Interleaved, bool check_attr_for_data = true, int* idx_array = nullptr, int nindicesperfrag = 0, int nfragspergroup = 1); diff --git a/layer1/CGOGL.cpp b/layer1/CGOGL.cpp index 71c077cb4..ceb2a84ba 100644 --- a/layer1/CGOGL.cpp +++ b/layer1/CGOGL.cpp @@ -5,6 +5,7 @@ #include "CGORenderer.h" #include "CoordSet.h" #include "Feedback.h" +#include "GLVertexBuffer.h" #include "GraphicsUtil.h" #include "Scene.h" #include "SceneDef.h" @@ -472,7 +473,7 @@ static void CGOReorderIndicesWithTransparentInfo(PyMOLGlobals* G, int nindices, vertexIndices[pl++] = vertexIndicesOriginal[idx + 1]; vertexIndices[pl++] = vertexIndicesOriginal[idx + 2]; } - ibo->bufferSubData(0, sizeof(VertexIndex_t) * nindices, vertexIndices); + ibo->copyFrom(pymol::span{vertexIndices, static_cast(nindices)}); } static void CGO_gl_draw_buffers_indexed(CCGORenderer* I, CGO_op_data pc) @@ -2108,8 +2109,11 @@ void CGORenderGLPicking(CGO* I, RenderInfo* info, PickContext* context, // reload entire vbo auto* vbo = I->G->ShaderMgr->getGPUBuffer(pickvbo); - vbo->bufferReplaceData(destOffset, - sizeof(float) * nverts * bufsizemult, pickColorDestUC); + auto pickPtrBytes = + reinterpret_cast(pickColorDestUC); + auto dataSpan = + pymol::span{pickPtrBytes, sizeof(float) * nverts * bufsizemult}; + vbo->bufferReplaceData(destOffset, dataSpan); (*pickcolors_are_set_ptr) = true; } diff --git a/layerGraphics/GPUEnums.h b/layerGraphics/GPUEnums.h new file mode 100644 index 000000000..30543a4bc --- /dev/null +++ b/layerGraphics/GPUEnums.h @@ -0,0 +1,25 @@ +#pragma once + +enum class MemoryUsageProperty { + GpuOnly, ///< Keep in GPU Memory + CpuToGpu, ///< Usual Memcpy from CPU to GPU + GpuToCpu, ///< Readback from GPU to CPU +}; + +enum class VertexBufferLayout { + Separate, // multiple vbos + Sequential, // single vbo + Interleaved // single vbo +}; + +enum class GPUPrimitiveTopology +{ + Triangles, + TriangleStrip, + TriangleFan, + Lines, + LinesStrip, + LineLoop, + Points, + Quads // depreated +}; diff --git a/layerGraphics/GPUVertexDesc.h b/layerGraphics/GPUVertexDesc.h new file mode 100644 index 000000000..d36fe51f9 --- /dev/null +++ b/layerGraphics/GPUVertexDesc.h @@ -0,0 +1,61 @@ +#pragma once + +#include "GPUEnums.h" +#include "VertexFormat.h" + +#include +#include +// #include +#include "pymol/span.h" +#include +#include +#include + +// ----------------------------------------------------------------------------- +// DESCRIPTORS +// ----------------------------------------------------------------------------- +// Describes a single array held in the vbo +struct BufferDesc { + BufferDesc(std::string_view _attr_name, VertexFormat _format, + std::size_t _data_size = 0, const void* _data_ptr = nullptr, + std::uint32_t offset = 0) + : attr_name(_attr_name) + , m_format(_format) + , data_size(_data_size) + , data_ptr(_data_ptr) + , offset(offset) + { + } + + std::string_view attr_name; + VertexFormat m_format{VertexFormat::Float}; + std::size_t data_size{}; + const void* data_ptr{nullptr}; + std::uint32_t offset{}; +}; + +/** + * @brief Describes the vertex buffer data + */ +struct BufferDataDesc { + + /** + * @brief Default constructor + */ + BufferDataDesc() = default; + + /** + * @brief Constructor + * @param _descs The buffer descriptors + */ + BufferDataDesc(std::vector _descs) + : descs(std::move(_descs)) + { + } + std::vector descs; + GPUPrimitiveTopology topology{GPUPrimitiveTopology::Triangles}; + std::optional stride; +}; + +using VertexBufferDataDesc = BufferDataDesc; +using VertexBufferDesc = BufferDesc; diff --git a/layerGraphics/IndexBuffer.h b/layerGraphics/IndexBuffer.h new file mode 100644 index 000000000..d76b3ef7f --- /dev/null +++ b/layerGraphics/IndexBuffer.h @@ -0,0 +1,13 @@ +#pragma once + +#include "GenericBuffer.h" + +#include + +class IndexBuffer : public GPUBuffer +{ +public: + [[nodiscard]] virtual std::uint64_t getBufferID() const = 0; + + virtual void copyFrom(pymol::span data) = 0; +}; diff --git a/layerGraphics/VertexBuffer.h b/layerGraphics/VertexBuffer.h new file mode 100644 index 000000000..c0e1e2065 --- /dev/null +++ b/layerGraphics/VertexBuffer.h @@ -0,0 +1,36 @@ +#pragma once + +#include +// #include +#include "pymol/span.h" +#include + +#include "GenericBuffer.h" + +struct BufferAndOffsets { + std::size_t bufferIdx{}; + std::size_t offset{}; +}; + +class VertexBuffer : public GPUBuffer +{ +public: + + /** + * @return Underlying buffers Image IDs + */ + virtual std::vector getBufferIDs() const = 0; + + /** + * @return Underlying buffers and offsets + */ + virtual std::vector getBufferOffsets() const = 0; + + /** + * @brief Copies data from external source to this buffer + * @param bufferAndOffets Buffer index and offset + * @param data The data to copy to the buffer + */ + virtual void copyFrom(const BufferAndOffsets& bufferAndOffsets, + pymol::span data) = 0; +}; diff --git a/layerGraphics/VertexFormat.h b/layerGraphics/VertexFormat.h new file mode 100644 index 000000000..da20b0e0e --- /dev/null +++ b/layerGraphics/VertexFormat.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +enum class VertexFormat { + // 8 bit + Byte, + Byte2, + Byte3, + Byte4, + ByteNorm, + Byte2Norm, + Byte3Norm, + Byte4Norm, + UByte, + UByte2, + UByte3, + UByte4, + UByteNorm, + UByte2Norm, + UByte3Norm, + UByte4Norm, + + // Single Precision + Float, + Float2, + Float3, + Float4, + + // Double Precision + Float64, + Float64_2, + Float64_3, + Float64_4, + + // 32 bit + Int, + Int2, + Int3, + Int4, + UInt, + UInt2, + UInt3, + UInt4, +}; + +enum class VertexFormatBaseType { Byte, UByte, Float, Int, UInt }; + +/** + * @param format Vertex format + * @return The base type of the vertex format +*/ +VertexFormatBaseType GetVertexFormatBaseType(VertexFormat format); + + +/** + * @param format Vertex format + * @return Whether the vertex format is a normalized type +*/ +bool VertexFormatIsNormalized(VertexFormat format); + +/** + * @param format Vertex format + * @return The size of the vertex format in bytes +*/ +std::size_t GetSizeOfVertexFormat(VertexFormat format); diff --git a/layerGraphics/gl/GLVertexBuffer.cpp b/layerGraphics/gl/GLVertexBuffer.cpp new file mode 100644 index 000000000..92ebe27f3 --- /dev/null +++ b/layerGraphics/gl/GLVertexBuffer.cpp @@ -0,0 +1,323 @@ +#include "GLVertexBuffer.h" + +#include "GraphicsUtil.h" + +#include "pymol/algorithm.h" + +static bool GLGenBuffer(GLuint& id, GLuint bufferType, pymol::span data) +{ + glGenBuffers(1, &id); + if (!CheckGLErrorOK(nullptr, "GenericBuffer::genBuffer failed\n")) + return false; + glBindBuffer(bufferType, id); + if (!CheckGLErrorOK(nullptr, "GenericBuffer::bindBuffer failed\n")) + return false; + glBufferData(bufferType, data.size(), data.data(), GL_STATIC_DRAW); + return CheckGLErrorOK(nullptr, "GenericBuffer::bufferData failed\n"); +} + +/** + * @brief Converts raw data (void*) to a byte span + * @param data The data + * @param size The size of the data in bytes + * @return The byte span + */ +static pymol::span RawDataToByteSpan( + const void* data, std::size_t size) +{ + return {static_cast(data), size}; +} + +std::vector VertexBufferGL::getBufferIDs() const +{ + std::vector bufferIDs; + for (const auto& glID : desc_glIDs) { + bufferIDs.push_back(glID); + } + return bufferIDs; +} + +std::vector VertexBufferGL::getBufferOffsets() const +{ + std::vector bufferOffsets; + + return bufferOffsets; +} + +void VertexBufferGL::copyFrom( + const BufferAndOffsets& bufferAndOffsets, pymol::span data) +{ +} + +void VertexBufferGL::bufferReplaceData(std::size_t offset, pymol::span data) +{ + glBindBuffer(bufferType(), m_interleavedID); + glBufferSubData(bufferType(), offset, data.size(), data.data()); +} + +void VertexBufferGL::bind_attrib(GLuint prg, const BufferDesc& d, GLuint glID) +{ + GLint loc = glGetAttribLocation(prg, d.attr_name.data()); + auto type_dim = VertexFormatToGLSize(d.m_format); + auto type = VertexFormatToGLType(d.m_format); + auto data_norm = VertexFormatToGLNormalized(d.m_format); + bool masked = false; + for (GLint lid : m_attribmask) + if (lid == loc) + masked = true; + if (loc >= 0) + m_locs.push_back(loc); + if (loc >= 0 && !masked) { + if (!isInterleaved() && glID) + glBindBuffer(bufferType(), glID); + glEnableVertexAttribArray(loc); + glVertexAttribPointer(loc, type_dim, type, data_norm, m_stride, + reinterpret_cast(d.offset)); + } +}; + +VertexBufferGL::VertexBufferGL( + VertexBufferLayout layout, MemoryUsageProperty memProperty) + : m_layout{layout} + , m_memProperty{memProperty} +{ +} + +void VertexBufferGL::bind() const +{ + // we shouldn't use this one + if (isInterleaved()) + glBindBuffer(bufferType(), m_interleavedID); +} + +void VertexBufferGL::bind(GLuint prg, int index) +{ + auto& descs = m_desc.descs; + if (index >= 0) { + glBindBuffer(bufferType(), m_interleavedID); + bind_attrib(prg, descs[index], desc_glIDs[index]); + } else { + if (isInterleaved() && m_interleavedID) + glBindBuffer(bufferType(), m_interleavedID); + for (auto i = 0; i < descs.size(); ++i) { + const auto& d = descs[i]; + auto glID = desc_glIDs[i]; + bind_attrib(prg, d, glID); + } + m_attribmask.clear(); + } +} + +void VertexBufferGL::unbind() +{ + for (auto& d : m_locs) { + glDisableVertexAttribArray(d); + } + m_locs.clear(); + glBindBuffer(bufferType(), 0); +} + +bool VertexBufferGL::isInterleaved() const noexcept +{ + return m_layout == VertexBufferLayout::Interleaved; +} + +void VertexBufferGL::maskAttributes(std::vector attrib_locs) +{ + m_attribmask = std::move(attrib_locs); +} + +void VertexBufferGL::maskAttribute(GLint attrib_loc) +{ + m_attribmask.push_back(attrib_loc); +} + +std::uint32_t VertexBufferGL::bufferType() const +{ + return GL_ARRAY_BUFFER; +} + +bool VertexBufferGL::sepBufferData() +{ + auto& descs = m_desc.descs; + for (auto i = 0; i < descs.size(); ++i) { + // If the specified size is 0 but we have a valid pointer + // then we are going to glVertexAttribXfv X in {1,2,3,4} + const auto& d = descs[i]; + auto& glID = desc_glIDs[i]; + if (d.data_ptr && (m_memProperty == MemoryUsageProperty::GpuOnly)) { + if (d.data_size) { + auto data = RawDataToByteSpan(d.data_ptr, d.data_size); + if (!GLGenBuffer(glID, bufferType(), data)) { + return false; + } + } + } + } + return true; +} + +bool VertexBufferGL::seqBufferData() { + // this is only going to use a single opengl vbo + m_layout = VertexBufferLayout::Interleaved; + + auto& descs = m_desc.descs; + size_t buffer_size { 0 }; + for ( auto & d : descs ) { + buffer_size += d.data_size; + } + + std::vector buffer_data(buffer_size); + auto data_ptr = buffer_data.data(); + size_t offset = 0; + + for ( auto & d : descs ) { + d.offset = offset; + if (d.data_ptr) + memcpy(data_ptr, d.data_ptr, d.data_size); + else + memset(data_ptr, 0, d.data_size); + data_ptr += d.data_size; + offset += d.data_size; + } + + return GLGenBuffer(m_interleavedID, bufferType(), pymol::span{buffer_data}); +} + +bool VertexBufferGL::interleaveBufferData() +{ + auto& descs = m_desc.descs; + const std::size_t bufferCount = descs.size(); + std::size_t stride = 0; + std::vector data_table(bufferCount); + std::vector ptr_table(bufferCount); + std::vector size_table(bufferCount); + std::size_t count = + descs[0].data_size / GetSizeOfVertexFormat(descs[0].m_format); + + // Maybe assert that all pointers in d_desc are valid? + for (size_t i = 0; i < bufferCount; ++i) { + auto& d = descs[i]; + // offset is the current stride + d.offset = stride; + + // These must come after so that offset starts at 0 + // Size of 3 normals or whatever the current type is + size_table[i] = GetSizeOfVertexFormat(d.m_format); + + // Increase our current estimate of the stride by this amount + stride += size_table[i]; + + // Does the addition of that previous stride leave us on a word boundry? + int m = stride % 4; + stride = (m ? (stride + (4 - m)) : stride); + + // data_table a pointer to the begining of each array + data_table[i] = static_cast(d.data_ptr); + + // We will move these pointers along by the values in the size table + ptr_table[i] = data_table[i]; + } + + m_stride = stride; + + std::size_t interleavedSize = count * stride; + + std::vector interleavedData(interleavedSize); + auto iPtr = interleavedData.data(); + + while (iPtr != (interleavedData.data() + interleavedSize)) { + for (size_t i = 0; i < bufferCount; ++i) { + if (ptr_table[i]) { + memcpy(iPtr, ptr_table[i], size_table[i]); + ptr_table[i] += size_table[i]; + } + iPtr += size_table[i]; + } + } + + m_layout = VertexBufferLayout::Interleaved; + return GLGenBuffer(m_interleavedID, bufferType(), pymol::span{interleavedData}); +} + +bool VertexBufferGL::evaluate() +{ + switch (m_layout) { + case VertexBufferLayout::Separate: + return sepBufferData(); + break; + case VertexBufferLayout::Sequential: + return seqBufferData(); + break; + case VertexBufferLayout::Interleaved: + return interleaveBufferData(); + default: + assert("Invalid VertexBufferLayout" && false); + break; + } + return false; +} + +bool VertexBufferGL::bufferData(BufferDataDesc&& desc) +{ + m_desc = std::move(desc); + desc_glIDs = std::vector(m_desc.descs.size()); + return evaluate(); +} + +bool VertexBufferGL::bufferData( + BufferDataDesc&& desc, const void* data, size_t len) +{ + m_desc = std::move(desc); + desc_glIDs = std::vector(m_desc.descs.size()); + m_layout = VertexBufferLayout::Interleaved; + m_stride = desc.stride.value_or(0); + auto span = RawDataToByteSpan(data, len); + return GLGenBuffer(m_interleavedID, bufferType(), span); +} + +void VertexBufferGL::bufferSubData(size_t offset, size_t size, void* data, size_t index) +{ + assert("Invalid Desc index" && index < m_desc.size()); + assert("Invalid GLDesc index" && index < desc_glIDs.size()); + auto glID = isInterleaved() ? m_interleavedID : desc_glIDs[index]; + glBindBuffer(bufferType(), glID); + glBufferSubData(bufferType(), offset, size, data); +} + +std::uint64_t IndexBufferGL::getBufferID() const +{ + return static_cast(m_bufferID); +} + +void IndexBufferGL::bind() const +{ + glBindBuffer(bufferType(), m_bufferID); +} + +void IndexBufferGL::unbind() +{ + glBindBuffer(bufferType(), 0); +} + +std::uint32_t IndexBufferGL::bufferType() const +{ + return GL_ELEMENT_ARRAY_BUFFER; +} + +void IndexBufferGL::bufferSubData( + std::size_t offset, pymol::span data) +{ + glBindBuffer(bufferType(), m_bufferID); + glBufferSubData(bufferType(), offset, data.size(), data.data()); +} + +void IndexBufferGL::copyFrom(pymol::span data) +{ + auto bytesSpan = pymol::as_bytes(data); + if (!m_bufferID) { + GLGenBuffer(m_bufferID, bufferType(), bytesSpan); + } else { + bufferSubData(0, bytesSpan); + } +} diff --git a/layerGraphics/gl/GLVertexBuffer.h b/layerGraphics/gl/GLVertexBuffer.h new file mode 100644 index 000000000..37b0d2de0 --- /dev/null +++ b/layerGraphics/gl/GLVertexBuffer.h @@ -0,0 +1,221 @@ +#pragma once + +#include "GPUEnums.h" +#include "IndexBuffer.h" +#include "VertexBuffer.h" + +// #include +#include "pymol/span.h" +#include + +// ----------------------------------------------------------------------------- +/* Vertexbuffer rules: + * ----------------------------------------------------------------------------- + * - If the buffer data is interleaved then buffer sub data functionality cannot + * be used. + * - The same order of buffer data must be maintained when uploading and binding + * + *----------------------------------------------------------------------- + * USAGE_PATTERN: + * SEPARATE: + * vbo1 [ data1 ] + * vbo2 [ data2 ] + * ... + * vboN [ dataN ] + * SEQUENTIAL: + * vbo [ data1 | data2 | ... | dataN ] + * INTERLEAVED: + * vbo [ data1[0], data2[0], ..., dataN[0] | ... | data1[M], data2[M], ..., dataN[M] ] + */ +class VertexBufferGL : public VertexBuffer +{ +public: + + /** + * @brief Constructs a Vertex Buffer + * @param layout The vertex buffer layout + * @param memProperty The memory usage property + */ + VertexBufferGL(VertexBufferLayout layout = VertexBufferLayout::Separate, + MemoryUsageProperty property = MemoryUsageProperty::GpuOnly); + + /** + * @brief Binds this Vertex Buffer + */ + void bind() const override; + + /** + * @brief Binds a specific buffer in the Vertex Buffer + */ + void bind(GLuint prg, int index = -1); + + /** + * @brief Unbinds this Vertex Buffer + */ + void unbind(); + + /** + * @brief Masks the attributes to be used in the shader + * @param attrib_locs The attribute locations + */ + void maskAttributes(std::vector attrib_locs); + + /** + * @brief Masks a specific attribute to be used in the shader + * @param attrib_loc The attribute location + */ + void maskAttribute(GLint attrib_loc); + + /** + * @return Underlying buffers Image IDs + */ + std::vector getBufferIDs() const override; + + /** + * @return Underlying buffers and offsets + */ + std::vector getBufferOffsets() const override; + + /** + * @brief Copies data from external source to this buffer + * @param bufferAndOffets Buffer index and offset + * @param data The data to copy to the buffer + */ + void copyFrom(const BufferAndOffsets& bufferAndOffsets, + pymol::span data) override; + + /** + * Conditionally generates a GPU buffer for the given data descriptor + * @param desc The buffer data descriptor + * @return Whether the buffer data was successfully buffered + * @note The supplied data ptr in the struct can + * be zero, in which case if the default usage is STATIC_DRAW then no + * opengl buffer will be generated for that, else it is assumed that the + * data will be supplied at a later point because it's dynamic draw. + */ + bool bufferData(BufferDataDesc&& desc); + + /** + * Generates a GPU buffer for the given data descriptor + * @param desc The buffer data descriptor + * @param data The data to buffer + * @param len The length of the data + * @note assumes the data is interleaved + */ + bool bufferData( + BufferDataDesc&& desc, const void* data, size_t len); + + // ----------------------------------------------------------------------------- + + /** + * Updates (a portion of) the buffer data + * @param offset The offset to start updating the buffer data + * @param size The size of the data to update + * @param data The data to update + * @param index The index of the buffer data to update + * @note This function assumes that the data layout hasn't changed + */ + void bufferSubData(size_t offset, size_t size, void* data, size_t index = 0); + + /** + * Replaces the whole interleaved buffer data + * @param data The data to replace the buffer data with + * @param len The length of the data + * @param data The data to replace the buffer data with + * @note This function assumes that the data layout hasn't changed + */ + void bufferReplaceData(std::size_t offset, pymol::span data); + +private: + + /** + * @return Vertex buffer type (GL_ARRAY_BUFFER) + */ + GLenum bufferType() const; + + /** + * @brief Binds an attribute to the shader program + * @param prg The shader program + * @param d The buffer descriptor + * @param glID The OpenGL buffer ID + */ + void bind_attrib(GLuint prg, const BufferDesc& d, GLuint glID); + + /** + * @return true if the data is interleaved + */ + bool isInterleaved() const noexcept; + + /** + * Generates a separate buffer for each data descriptor + * @return Whether the buffer data was successfully buffered + */ + bool sepBufferData(); + + /** + * Generates a single sequential buffer for all data descriptors + * @return Whether the buffer data was successfully buffered + */ + bool seqBufferData(); + + /** + * Generates a single interleaved buffer for all data descriptors + * @return Whether the buffer data was successfully buffered + */ + bool interleaveBufferData(); + + /** + * Generates GPU buffer(s) for the given data descriptor + * @return Whether the buffer data was successfully buffered + */ + bool evaluate(); + + bool m_status{false}; + GLuint m_interleavedID{}; + MemoryUsageProperty m_memProperty{MemoryUsageProperty::GpuOnly}; + VertexBufferLayout m_layout{ VertexBufferLayout::Separate }; + std::size_t m_stride{}; + BufferDataDesc m_desc; + std::vector desc_glIDs; // m_desc's gl buffer IDs + + // m_locs is only for interleaved data + std::vector m_locs; + std::vector m_attribmask; +}; + +/** + * Index buffer specialization + */ +class IndexBufferGL : public IndexBuffer +{ +public: + + /** + * @brief Binds this Index Buffer + */ + void bind() const override; + + /** + * @brief Unbinds this Index Buffer + */ + void unbind(); + + /** + * @return Index buffer type (GL_ELEMENT_ARRAY_BUFFER) + */ + GLenum bufferType() const; + + /** + * @brief Copies index data from external source to this buffer + */ + void copyFrom(pymol::span data); + + /** + * @return The OpenGL buffer ID + */ + std::uint64_t getBufferID() const override; + +private: + void bufferSubData(std::size_t offset, pymol::span data); + GLuint m_bufferID{}; +}; diff --git a/setup.py b/setup.py index b948cb52a..ec7237a86 100644 --- a/setup.py +++ b/setup.py @@ -548,6 +548,8 @@ pymol_src_dirs = [ "layer3", "layer4", "layer5", + "layerGraphics", + "layerGraphics/gl", generated_dir, ]