LCOV - code coverage report
Current view: top level - ezlibs/ezGL - bufferBlock.hpp (source / functions) Coverage Total Hit
Test: Coverage (llvm-cov → lcov → genhtml) Lines: 0.0 % 98 0
Test Date: 2025-09-16 22:55:37 Functions: 0.0 % 26 0
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : #pragma once
       2                 :             : 
       3                 :             : /*
       4                 :             : MIT License
       5                 :             : 
       6                 :             : Copyright (c) 2014-2024 Stephane Cuillerdier (aka aiekick)
       7                 :             : 
       8                 :             : Permission is hereby granted, free of charge, to any person obtaining a copy
       9                 :             : of this software and associated documentation files (the "Software"), to deal
      10                 :             : in the Software without restriction, including without limitation the rights
      11                 :             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      12                 :             : copies of the Software, and to permit persons to whom the Software is
      13                 :             : furnished to do so, subject to the following conditions:
      14                 :             : 
      15                 :             : The above copyright notice and this permission notice shall be included in all
      16                 :             : copies or substantial portions of the Software.
      17                 :             : 
      18                 :             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      19                 :             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      20                 :             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      21                 :             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      22                 :             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      23                 :             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      24                 :             : SOFTWARE.
      25                 :             : */
      26                 :             : 
      27                 :             : // ezGL is part of the ezLibs project : https://github.com/aiekick/ezLibs.git
      28                 :             : 
      29                 :             : #include "ezGL.hpp"
      30                 :             : #include <unordered_map>
      31                 :             : #include <vector>
      32                 :             : #include <string>
      33                 :             : #include <memory>
      34                 :             : #include <cstring>  // memset, memcpy
      35                 :             : #include <cmath>
      36                 :             : 
      37                 :             : #define PRINT_BLOCK_DATAS
      38                 :             : 
      39                 :             : namespace ez {
      40                 :             : namespace gl {
      41                 :             : 
      42                 :             : class BufferBlock;
      43                 :             : using BufferBlockPtr = std::unique_ptr<BufferBlock>;
      44                 :             : 
      45                 :             : class BufferBlock {
      46                 :             : public:
      47                 :           0 :     static BufferBlockPtr create(const std::string& vName, GLenum vTarget) {
      48                 :           0 :         return BufferBlockPtr(new BufferBlock(vName, vTarget));  // cpp11
      49                 :           0 :     }
      50                 :             : 
      51                 :             : protected:
      52                 :             :     GLuint m_buffer = 0;  // buffer id
      53                 :             :     GLenum m_target = 0;  // ex : GL_UNIFORM_BUFFER;
      54                 :             :     std::string m_name;
      55                 :             : 
      56                 :             : public:
      57                 :             :     BufferBlock() = default;
      58                 :           0 :     explicit BufferBlock(const std::string& vName, GLenum vTarget) : m_name(vName), m_target(vTarget) { m_create(); }
      59                 :           0 :     virtual ~BufferBlock() { m_destroy(); }
      60                 :           0 :     const std::string& getName() const { return m_name; }
      61                 :           0 :     void upload(GLenum vUsage, const void* vData, size_t vSize) {
      62                 :           0 : #ifdef PROFILER_SCOPED_PTR
      63                 :           0 :         PROFILER_SCOPED_PTR(this, "upload BufferBlock ", "%s", m_name.c_str());
      64                 :           0 : #endif
      65                 :           0 :         glBindBuffer(m_target, m_buffer);
      66                 :           0 :         glBufferData(m_target, vSize, vData, vUsage);
      67                 :           0 :         CheckGLErrors;
      68                 :           0 :     }
      69                 :           0 :     void bind(GLuint vBinding) {
      70                 :           0 :         glBindBufferBase(m_target, vBinding, m_buffer);
      71                 :           0 :         CheckGLErrors;
      72                 :           0 :     }
      73                 :           0 :     GLuint id() const { return m_buffer; }
      74                 :             : 
      75                 :             : private:
      76                 :           0 :     void m_create() {
      77                 :           0 :         glGenBuffers(1, &m_buffer);
      78                 :           0 :         CheckGLErrors;
      79                 :           0 :     }
      80                 :           0 :     void m_destroy() {
      81                 :           0 :         if (m_buffer != 0) {
      82                 :           0 :             glDeleteBuffers(1, &m_buffer);
      83                 :           0 :             CheckGLErrors;
      84                 :           0 :             m_buffer = 0;
      85                 :           0 :         }
      86                 :           0 :     }
      87                 :             : };
      88                 :             : 
      89                 :             : /*
      90                 :             : usage :
      91                 :             :     struct Datas {
      92                 :             :         fvec2 p;
      93                 :             :         fvec2 t
      94                 :             :     };
      95                 :             :     BufferBlockAuto<GL_SHADER_STORAGE_BUFFER, Datas> ssbo;
      96                 :             :     ssbo.getDatasRef().push_back(Datas{}};
      97                 :             :     ssbo.upload(GL_STATIC_DRAW);
      98                 :             : */
      99                 :             : template <GLenum TTARGET, typename TDATAS>
     100                 :             : class BufferBlockAuto;
     101                 :             : template <GLenum TTARGET, typename TDATAS>
     102                 :             : using BufferBlockAutoPtr = std::unique_ptr<BufferBlockAuto<TTARGET, TDATAS>>;
     103                 :             : 
     104                 :             : template <GLenum TTARGET, typename TDATAS>
     105                 :             : class BufferBlockAuto {
     106                 :             : public:
     107                 :             :     static BufferBlockAutoPtr<TTARGET, TDATAS> create(const std::string& vName) {
     108                 :             :         return BufferBlockAutoPtr<TTARGET, TDATAS>(new BufferBlockAuto<TTARGET, TDATAS>(vName));  // cpp11
     109                 :             :     }
     110                 :             : 
     111                 :             : private:
     112                 :             :     BufferBlockPtr mp_buffer;
     113                 :             :     std::vector<TDATAS> m_datas;
     114                 :             : 
     115                 :             : public:
     116                 :             :     BufferBlockAuto() = default;
     117                 :             :     explicit BufferBlockAuto(const std::string& vName) { mp_buffer = BufferBlock::create(vName, TTARGET); }
     118                 :             :     virtual ~BufferBlockAuto() = default;
     119                 :             :     const std::vector<TDATAS>& getDatas() const { return m_datas; }
     120                 :             :     std::vector<TDATAS>& getDatasRef() { return m_datas; }
     121                 :             :     GLuint getId() const { return mp_buffer->id(); }
     122                 :             :     void bind(GLuint vBinding) { mp_buffer->bind(vBinding); }
     123                 :             :     void upload(GLenum vUsage) { mp_buffer->upload(vUsage, m_datas.data(), sizeof(TDATAS) * m_datas.size()); }
     124                 :             :     BufferBlock* getBufferBlockPtr() { return mp_buffer.get(); }  // for adding in es::gl::program
     125                 :             : };
     126                 :             : template <typename TDATAS>
     127                 :             : using SSBO = BufferBlockAuto<GL_SHADER_STORAGE_BUFFER, TDATAS>;
     128                 :             : template <typename TDATAS>
     129                 :             : using SSBOPtr = std::unique_ptr<SSBO<TDATAS>>;
     130                 :             : 
     131                 :             : // UBOAuto : layout std140
     132                 :             : // this ubo can be udpated dynamically by code and will manage automatically resources and layout std140
     133                 :             : 
     134                 :             : class UBOAuto {
     135                 :             : private:
     136                 :             :     BufferBlockPtr mp_buffer;
     137                 :             :     std::string m_name;
     138                 :             :     // key = uniform name, offset in buffer for have op on uniform data
     139                 :             :     std::unordered_map<std::string, uint32_t> m_offsets;
     140                 :             :     // uniforms datas buffer
     141                 :             :     std::vector<uint8_t> m_datas;
     142                 :             :     // if he is dirty, value has been changed and mut be uploaded in gpu memory
     143                 :             :     // dirty at first for init in gpu memory
     144                 :             :     bool m_isDirty = true;
     145                 :             : 
     146                 :             : public:
     147                 :           0 :     UBOAuto(const std::string& vName) : m_name(vName) { create(); }
     148                 :           0 :     ~UBOAuto() { unit(); }
     149                 :           0 :     bool init() { return true; }
     150                 :           0 :     void unit() { clear(); }
     151                 :           0 :     void clear() {
     152                 :           0 :         m_datas.clear();
     153                 :           0 :         m_offsets.clear();
     154                 :           0 :         m_isDirty = false;
     155                 :           0 :     }
     156                 :           0 :     void setDirty() { m_isDirty = true; }
     157                 :           0 :     bool isDirty() const { return m_isDirty; }
     158                 :           0 :     void create() {
     159                 :           0 :         mp_buffer = BufferBlock::create(m_name, GL_UNIFORM_BUFFER);
     160                 :           0 :         mp_buffer->upload(GL_DYNAMIC_DRAW, m_datas.data(), m_datas.size());
     161                 :           0 :     }
     162                 :           0 :     void destroy() { mp_buffer.reset(); }
     163                 :           0 :     bool recreate() {
     164                 :           0 :         bool res = false;
     165                 :           0 :         if (!m_datas.empty() && mp_buffer->id() != 0U) {
     166                 :           0 :             destroy();
     167                 :           0 :             create();
     168                 :           0 :             res = true;
     169                 :           0 :         }
     170                 :           0 :         return res;
     171                 :           0 :     }
     172                 :           0 :     void uploadIfDirty() {
     173                 :           0 :         if (m_isDirty && !m_datas.empty()) {
     174                 :           0 :             mp_buffer->upload(GL_DYNAMIC_DRAW, m_datas.data(), m_datas.size());
     175                 :           0 :             m_isDirty = false;
     176                 :           0 :         }
     177                 :           0 :     }
     178                 :           0 :     void bind(const uint32_t vBinding) { mp_buffer->bind(vBinding); }
     179                 :           0 :     GLuint id() const { return mp_buffer->id(); }
     180                 :             :     // add size to uniform block, return startOffset
     181                 :           0 :     bool registerByteSize(const std::string& vKey, uint32_t vSizeInBytes, uint32_t* vStartOffset) {
     182                 :           0 :         if (offsetExist(vKey)) {
     183                 :           0 :             LogVarDebugWarning("Debug : key %s is already defined in buffer. %s fail.", vKey.c_str(), __FUNCTION__);
     184                 :           0 :         } else if (vSizeInBytes > 0) {
     185                 :           0 :             uint32_t newSize = vSizeInBytes;
     186                 :           0 :             uint32_t lastOffset = (uint32_t)m_datas.size();
     187                 :           0 :             auto baseAlign = getStd140Alignment(newSize);
     188                 :           0 :             // il faut trouver le prochain offset qui est multiple de baseAlign
     189                 :           0 :             auto startOffset = baseAlign * static_cast<uint32_t>(std::ceil(static_cast<double>(lastOffset) / static_cast<double>(baseAlign)));
     190                 :           0 :             auto newSizeToAllocate = startOffset - lastOffset + newSize;
     191                 :           0 : #ifdef PRINT_BLOCK_DATAS
     192                 :           0 :             auto endOffset = startOffset + newSize;
     193                 :           0 :             LogVarDebugInfo(
     194                 :           0 :                 "key %s, size %u, align %u, Offsets : %u => %u, size to alloc %u", vKey.c_str(), newSize, baseAlign, startOffset, endOffset, newSizeToAllocate);
     195                 :           0 : #endif
     196                 :           0 :             m_datas.resize(lastOffset + newSizeToAllocate);
     197                 :           0 :             // on set de "lastOffset" a "lastOffset + newSizeToAllocate"
     198                 :           0 :             std::memset(m_datas.data() + lastOffset, 0, newSizeToAllocate);
     199                 :           0 :             addOffsetForKey(vKey, startOffset);
     200                 :           0 :             if (vStartOffset) {
     201                 :           0 :                 *vStartOffset = startOffset;
     202                 :           0 :             }
     203                 :           0 :             return true;
     204                 :           0 :         }
     205                 :           0 :         return false;
     206                 :           0 :     }
     207                 :             : 
     208                 :             :     // templates (defined under class)
     209                 :             :     // add a variable
     210                 :             :     template <typename T>
     211                 :             :     void registerVar(const std::string& vKey, T vValue);  // add var to uniform block
     212                 :             :     template <typename T>
     213                 :             :     void registerVar(const std::string& vKey, T* vValue, uint32_t vSizeInBytes);  // add var to uniform block
     214                 :             : 
     215                 :             :     // Get / set + op on variables
     216                 :             :     template <typename T>
     217                 :             :     bool getVar(const std::string& vKey, T& vValue);  // Get
     218                 :             :     template <typename T>
     219                 :             :     bool setVar(const std::string& vKey, T vValue);  // set
     220                 :             :     template <typename T>
     221                 :             :     bool setVar(const std::string& vKey, T* vValue, uint32_t vSizeInBytes);  // set
     222                 :             :     template <typename T>
     223                 :             :     bool setAddVar(const std::string&, T vValue);  // add and set like +=
     224                 :             : 
     225                 :             : private:
     226                 :           0 :     void addOffsetForKey(const std::string& vKey, uint32_t vOffset) { m_offsets[vKey] = vOffset; }
     227                 :           0 :     bool offsetExist(const std::string& vKey) { return m_offsets.find(vKey) != m_offsets.end(); }
     228                 :             : 
     229                 :             : protected:
     230                 :             : #if 1
     231                 :             :     // tested
     232                 :           0 :     uint32_t getStd140Alignment(uint32_t vSize) {
     233                 :           0 :         auto goodAlign = static_cast<uint32_t>(std::pow(2, std::ceil(std::log(vSize) / std::log(2))));
     234                 :           0 :         if (goodAlign > 16U) {
     235                 :           0 :             return 16U;
     236                 :           0 :         }
     237                 :           0 :         return goodAlign;
     238                 :           0 :     }
     239                 :             : #else
     240                 :             :     // maybe closer to the std140, to test
     241                 :             :     uint32_t getStd140Alignment(uint32_t vSize) {
     242                 :             :         if (vSize <= 4) {
     243                 :             :             return 4;
     244                 :             :         }
     245                 :             :         if (vSize <= 8) {
     246                 :             :             return 8;
     247                 :             :         }
     248                 :             :         return 16;
     249                 :             :     }
     250                 :             :     // ou un mix des deux ?
     251                 :             :     uint32_t getStd140Alignment(uint32_t vSize) {
     252                 :             :         if (vSize <= 16) {
     253                 :             :             if (vSize <= 4)
     254                 :             :                 return 4;  // float, int, uint
     255                 :             :             if (vSize <= 8)
     256                 :             :                 return 8;  // vec2, ivec2
     257                 :             :             return 16;     // vec3 (12), vec4 (16), mat3 row (12)
     258                 :             :         }
     259                 :             : 
     260                 :             :         // vSize > 16 → struct, array, mat*, etc.
     261                 :             :         uint32_t pow2 = 1 << (uint32_t)std::ceil(std::log2(vSize));
     262                 :             :         return std::min(pow2, 16u);  // clip max align à 16
     263                 :             :     }
     264                 :             : #endif
     265                 :             : };
     266                 :             : 
     267                 :             : ////////////////////////////////////////////////////////////////////////////////////////////////////////////
     268                 :             : //// PUBLIC TEMPLATES //////////////////////////////////////////////////////////////////////////////////////
     269                 :             : ////////////////////////////////////////////////////////////////////////////////////////////////////////////
     270                 :             : 
     271                 :             : template <typename T>
     272                 :             : void UBOAuto::registerVar(const std::string& vKey, T* vValue, uint32_t vSizeInBytes) {
     273                 :             :     uint32_t startOffset;
     274                 :             :     if (registerByteSize(vKey, vSizeInBytes, &startOffset)) {
     275                 :             :         // on copy de "startOffset" a "startOffset + vSizeInBytes"
     276                 :             :         std::memcpy(m_datas.data() + startOffset, vValue, vSizeInBytes);
     277                 :             :     }
     278                 :             : }
     279                 :             : 
     280                 :             : template <typename T>
     281                 :             : void UBOAuto::registerVar(const std::string& vKey, T vValue) {
     282                 :             :     registerVar(vKey, &vValue, sizeof(vValue));
     283                 :             : }
     284                 :             : 
     285                 :             : template <typename T>
     286                 :             : bool UBOAuto::getVar(const std::string& vKey, T& vValue) {
     287                 :             :     if (offsetExist(vKey)) {
     288                 :             :         uint32_t offset = m_offsets[vKey];
     289                 :             :         uint32_t size = sizeof(vValue);
     290                 :             :         std::memcpy(&vValue, m_datas.data() + offset, size);
     291                 :             :         return true;
     292                 :             :     }
     293                 :             :     LogVarDebugInfo("Debug : key %s not exist in buffer. %s fail.", vKey.c_str(), __FUNCTION__);
     294                 :             :     return false;
     295                 :             : }
     296                 :             : 
     297                 :             : template <typename T>
     298                 :             : bool UBOAuto::setVar(const std::string& vKey, T* vValue, uint32_t vSizeInBytes) {
     299                 :             :     if (offsetExist(vKey) && vSizeInBytes > 0) {
     300                 :             :         uint32_t newSize = vSizeInBytes;
     301                 :             :         uint32_t offset = m_offsets[vKey];
     302                 :             :         std::memcpy(m_datas.data() + offset, vValue, newSize);
     303                 :             :         m_isDirty = true;
     304                 :             :         return true;
     305                 :             :     }
     306                 :             :     LogVarDebugInfo("Debug : key %s not exist in buffer. %s fail.", vKey.c_str(), __FUNCTION__);
     307                 :             :     return false;
     308                 :             : }
     309                 :             : 
     310                 :             : template <typename T>
     311                 :             : bool UBOAuto::setVar(const std::string& vKey, T vValue) {
     312                 :             :     return setVar(vKey, &vValue, sizeof(vValue));
     313                 :             : }
     314                 :             : 
     315                 :             : template <typename T>
     316                 :             : bool UBOAuto::setAddVar(const std::string& vKey, T vValue) {
     317                 :             :     T v;
     318                 :             :     if (getVar(vKey, v)) {
     319                 :             :         v += vValue;
     320                 :             :         return setVar(vKey, v);
     321                 :             :     }
     322                 :             :     LogVarDebugInfo("Debug : key %s not exist in buffer. %s fail.", vKey.c_str(), __FUNCTION__);
     323                 :             :     return false;
     324                 :             : }
     325                 :             : 
     326                 :             : }  // namespace gl
     327                 :             : }  // namespace ez
        

Generated by: LCOV version 2.0-1