File indexing completed on 2024-12-22 04:12:47

0001 /*
0002  *  SPDX-FileCopyrightText: 2021 Dmitry Kazakov <dimula73@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisOpenGLBufferCircularStorage.h"
0008 
0009 #include <QtMath>
0010 
0011 #include "kis_assert.h"
0012 #include "kis_opengl.h"
0013 
0014 KisOpenGLBufferCircularStorage::BufferBinder::BufferBinder(KisOpenGLBufferCircularStorage *bufferStorage, const void **dataPtr, int dataSize) {
0015     if (bufferStorage) {
0016         m_buffer = bufferStorage->getNextBuffer();
0017         m_buffer->bind();
0018         m_buffer->write(0, *dataPtr, dataSize);
0019         *dataPtr = nullptr;
0020     }
0021 
0022 }
0023 
0024 KisOpenGLBufferCircularStorage::BufferBinder::~BufferBinder() {
0025     if (m_buffer) {
0026         m_buffer->release();
0027 
0028         if (KisOpenGL::useTextureBufferInvalidation()) {
0029             KisOpenGL::glInvalidateBufferData(m_buffer->bufferId());
0030         }
0031     }
0032 }
0033 
0034 struct Q_DECL_HIDDEN KisOpenGLBufferCircularStorage::Private
0035 {
0036     std::vector<QOpenGLBuffer> buffers;
0037     decltype(buffers)::size_type nextBuffer = 0;
0038     int bufferSize = 0;
0039     QOpenGLBuffer::Type type = QOpenGLBuffer::QOpenGLBuffer::VertexBuffer;
0040 };
0041 
0042 
0043 KisOpenGLBufferCircularStorage::KisOpenGLBufferCircularStorage()
0044     : KisOpenGLBufferCircularStorage(QOpenGLBuffer::VertexBuffer)
0045 {
0046 }
0047 
0048 KisOpenGLBufferCircularStorage::KisOpenGLBufferCircularStorage(QOpenGLBuffer::Type type)
0049     : m_d(new Private)
0050 {
0051     m_d->type = type;
0052 }
0053 
0054 KisOpenGLBufferCircularStorage::~KisOpenGLBufferCircularStorage() = default;
0055 
0056 void KisOpenGLBufferCircularStorage::allocate(int numBuffers, int bufferSize)
0057 {
0058     reset();
0059     KIS_ASSERT(numBuffers > 0);
0060     KIS_ASSERT(bufferSize > 0);
0061     addBuffersImpl(static_cast<size_t>(numBuffers), bufferSize);
0062 }
0063 
0064 QOpenGLBuffer *KisOpenGLBufferCircularStorage::getNextBuffer()
0065 {
0066     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(isValid(), 0);
0067 
0068     QOpenGLBuffer *buffer = &m_d->buffers[m_d->nextBuffer];
0069     m_d->nextBuffer = (m_d->nextBuffer + 1) % m_d->buffers.size();
0070     return buffer;
0071 }
0072 
0073 bool KisOpenGLBufferCircularStorage::isValid() const
0074 {
0075     return !m_d->buffers.empty();
0076 }
0077 
0078 int KisOpenGLBufferCircularStorage::size() const
0079 {
0080     return static_cast<int>(m_d->buffers.size());
0081 }
0082 
0083 void KisOpenGLBufferCircularStorage::reset()
0084 {
0085     m_d->buffers.clear();
0086     m_d->nextBuffer = 0;
0087     m_d->bufferSize = 0;
0088 }
0089 
0090 void KisOpenGLBufferCircularStorage::allocateMoreBuffers()
0091 {
0092     const size_t numBuffers = nextPowerOfTwo(m_d->buffers.size());
0093 
0094     KIS_SAFE_ASSERT_RECOVER_RETURN(!m_d->buffers.empty());
0095 
0096     auto begin = m_d->buffers.begin();
0097     auto middle = [&]() {
0098         using value_type = typename decltype(m_d->buffers)::difference_type;
0099         const value_type maxIndex = std::numeric_limits<value_type>::max();
0100 
0101         if (m_d->nextBuffer <= std::numeric_limits<value_type>::max()) {
0102             return std::next(begin, value_type(m_d->nextBuffer));
0103         } else {
0104             auto midpoint = std::next(begin, std::numeric_limits<value_type>::max());
0105             return std::next(midpoint, value_type(m_d->nextBuffer - maxIndex));
0106         }
0107     }();
0108     auto end = m_d->buffers.end();
0109 
0110     std::rotate(begin, middle, end);
0111 
0112     m_d->nextBuffer = m_d->buffers.size();
0113 
0114     const size_t buffersToAdd = numBuffers - m_d->buffers.size();
0115 
0116     addBuffersImpl(buffersToAdd, m_d->bufferSize);
0117 }
0118 
0119 void KisOpenGLBufferCircularStorage::addBuffersImpl(size_t buffersToAdd, int bufferSize)
0120 {
0121     m_d->bufferSize = bufferSize;
0122 
0123     const size_t newSize = qMax(m_d->buffers.size() + buffersToAdd, nextPowerOfTwo(m_d->buffers.size()));
0124 
0125     if (m_d->buffers.capacity() < newSize)
0126         m_d->buffers.reserve(newSize);
0127 
0128     // overflow check for size()
0129     KIS_ASSERT(m_d->buffers.size() <= std::numeric_limits<int>::max());
0130 
0131     for (size_t i = 0; i < buffersToAdd; i++) {
0132         m_d->buffers.emplace_back(m_d->type);
0133 
0134         QOpenGLBuffer &buf = m_d->buffers.back();
0135 
0136         buf.create();
0137         buf.setUsagePattern(QOpenGLBuffer::DynamicDraw);
0138         buf.bind();
0139         buf.allocate(m_d->bufferSize);
0140         buf.release();
0141     }
0142 }