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 }