1 /****************************************************************************
2 **
3 ** Copyright (C) 2008-2012 NVIDIA Corporation.
4 ** Copyright (C) 2019 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of Qt Quick 3D.
8 **
9 ** $QT_BEGIN_LICENSE:GPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU
20 ** General Public License version 3 or (at your option) any later version
21 ** approved by the KDE Free Qt Foundation. The licenses are as published by
22 ** the Free Software Foundation and appearing in the file LICENSE.GPL3
23 ** included in the packaging of this file. Please review the following
24 ** information to ensure the GNU General Public License requirements will
25 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
26 **
27 ** $QT_END_LICENSE$
28 **
29 ****************************************************************************/
30 
31 #include <QtQuick3DRender/private/qssgrenderconstantbuffer_p.h>
32 #include <QtQuick3DRender/private/qssgrendercontext_p.h>
33 #include <QtQuick3DRender/private/qssgrendershaderprogram_p.h>
34 
35 #include <QtQuick3DUtils/private/qssgutils_p.h>
36 
37 QT_BEGIN_NAMESPACE
38 
qHash(const QSSGRenderConstantBuffer::ParamHandle & h,uint seed)39 uint qHash(const QSSGRenderConstantBuffer::ParamHandle &h, uint seed) Q_DECL_NOTHROW
40 {
41     return (h.key) ? h.key : QT_PREPEND_NAMESPACE(qHash(h.name, seed));
42 }
43 
44 ///< struct handling a constant buffer entry
45 class ConstantBufferParamEntry
46 {
47 public:
48     QByteArray m_name; ///< parameter Name
49     QSSGRenderShaderDataType m_type; ///< parameter type
50     qint32 m_count; ///< one or array size
51     qint32 m_offset; ///< offset into the memory buffer
52 
ConstantBufferParamEntry(const QByteArray & name,QSSGRenderShaderDataType type,qint32 count,qint32 offset)53     ConstantBufferParamEntry(const QByteArray &name, QSSGRenderShaderDataType type, qint32 count, qint32 offset)
54         : m_name(name), m_type(type), m_count(count), m_offset(offset)
55     {
56     }
57 };
58 
QSSGRenderConstantBuffer(const QSSGRef<QSSGRenderContext> & context,const QByteArray & bufferName,QSSGRenderBufferUsageType usageType,QSSGByteView data)59 QSSGRenderConstantBuffer::QSSGRenderConstantBuffer(const QSSGRef<QSSGRenderContext> &context,
60                                                        const QByteArray &bufferName,
61                                                        QSSGRenderBufferUsageType usageType,
62                                                        QSSGByteView data)
63     : QSSGRenderDataBuffer(context, QSSGRenderBufferType::Constant, usageType, data)
64     , m_name(bufferName)
65     , m_currentOffset(0)
66     , m_currentSize(0)
67     , m_hwBufferInitialized(false)
68     , m_maxBlockSize(0)
69 {
70     Q_ASSERT(context->supportsConstantBuffer());
71 
72     m_backend->getRenderBackendValue(QSSGRenderBackend::QSSGRenderBackendQuery::MaxConstantBufferBlockSize, &m_maxBlockSize);
73 
74     if (data.size()) {
75         Q_ASSERT(data.size() < m_maxBlockSize);
76         m_shadowCopy.resize(data.size());
77         memcpy(m_shadowCopy.begin(), data.begin(), size_t(data.size()));
78     }
79     context->registerConstantBuffer(this);
80 }
81 
~QSSGRenderConstantBuffer()82 QSSGRenderConstantBuffer::~QSSGRenderConstantBuffer()
83 {
84     qDeleteAll(m_constantBufferEntryMap);
85     m_context->bufferDestroyed(this);
86 }
87 
bind()88 void QSSGRenderConstantBuffer::bind()
89 {
90     if (m_mapped) {
91         qCCritical(RENDER_INVALID_OPERATION, "Attempting to Bind a locked buffer");
92         Q_ASSERT(false);
93     }
94 
95     m_backend->bindBuffer(m_handle, m_type);
96 }
97 
bindToShaderProgram(const QSSGRef<QSSGRenderShaderProgram> & inShader,quint32 blockIndex,quint32 binding)98 void QSSGRenderConstantBuffer::bindToShaderProgram(const QSSGRef<QSSGRenderShaderProgram> &inShader, quint32 blockIndex, quint32 binding)
99 {
100     if ((qint32)binding == -1) {
101         binding = m_context->nextConstantBufferUnit();
102         m_backend->programSetConstantBlock(inShader->handle(), blockIndex, binding);
103     }
104 
105     m_backend->programSetConstantBuffer(binding, m_handle);
106 }
107 
setupBuffer(const QSSGRenderShaderProgram * program,qint32 index,qint32 bufSize,qint32 paramCount)108 bool QSSGRenderConstantBuffer::setupBuffer(const QSSGRenderShaderProgram *program, qint32 index, qint32 bufSize, qint32 paramCount)
109 {
110     bool bSuccess = false;
111 
112     if (!m_hwBufferInitialized) {
113         // allocate shadow buffer
114         QByteArray newShadowCopy;
115         newShadowCopy.resize(bufSize);
116         quint8 *newMem = reinterpret_cast<quint8 *>(newShadowCopy.data());
117 
118         // allocate temp buffers to hold constant buffer information
119         qint32 *theIndices = nullptr;
120         QSSGRenderShaderDataType *theTypes = nullptr;
121         qint32 *theSizes = nullptr;
122         qint32 *theOffsets = nullptr;
123 
124         theIndices = static_cast<qint32 *>(::malloc(size_t(paramCount) * sizeof(qint32)));
125         if (!theIndices)
126             goto fail;
127         theTypes = static_cast<QSSGRenderShaderDataType *>(::malloc(size_t(paramCount) * sizeof(QSSGRenderShaderDataType)));
128         if (!theTypes)
129             goto fail;
130         theSizes = static_cast<qint32 *>(::malloc(size_t(paramCount) * sizeof(qint32)));
131         if (!theSizes)
132             goto fail;
133         theOffsets = static_cast<qint32 *>(::malloc(size_t(paramCount) * sizeof(qint32)));
134         if (!theOffsets)
135             goto fail;
136 
137         bSuccess = true;
138 
139         // get indices for the individal constant buffer entries
140         m_backend->getConstantBufferParamIndices(program->handle(), index, theIndices);
141 
142         // get constant buffer uniform information
143         m_backend->getConstantBufferParamInfoByIndices(program->handle(), paramCount, (quint32 *)theIndices, theTypes, theSizes, theOffsets);
144 
145         // get the names of the uniforms
146         char nameBuf[512];
147         qint32 elementCount, binding;
148         QSSGRenderShaderDataType type;
149 
150         for (int idx = 0; idx != paramCount; ++idx) {
151             m_backend->getConstantInfoByID(program->handle(), theIndices[idx], 512, &elementCount, &type, &binding, nameBuf);
152             // check if we already have this entry
153             const QByteArray theName = nameBuf;
154             ParamHandle h = ParamHandle::create(theName);
155             auto entry = m_constantBufferEntryMap.constFind(h);
156             if (entry != m_constantBufferEntryMap.cend()) {
157                 ConstantBufferParamEntry *pParam = entry.value();
158                 // copy content
159                 if (m_shadowCopy.size())
160                     memcpy(newMem + theOffsets[idx],
161                            m_shadowCopy.constData() + entry.value()->m_offset,
162                            entry.value()->m_count * uniformTypeSize(pParam->m_type));
163 
164                 pParam->m_offset = theOffsets[idx];
165                 Q_ASSERT(type == pParam->m_type);
166                 Q_ASSERT(elementCount == pParam->m_count);
167             } else {
168                 // create one
169                 m_constantBufferEntryMap.insert(h,
170                                                 createParamEntry(theName,
171                                                                  theTypes[idx],
172                                                                  theSizes[idx],
173                                                                  theOffsets[idx]));
174             }
175         }
176 
177         m_shadowCopy = newShadowCopy;
178         m_hwBufferInitialized = true;
179 
180     fail:
181         if (theIndices)
182             ::free(theIndices);
183         if (theTypes)
184             ::free(theTypes);
185         if (theSizes)
186             ::free(theSizes);
187         if (theOffsets)
188             ::free(theOffsets);
189 
190     } else {
191         // some sanity checks
192         bSuccess = true;
193         bSuccess &= (m_shadowCopy.size() <= bufSize);
194     }
195 
196     return bSuccess;
197 }
198 
update()199 void QSSGRenderConstantBuffer::update()
200 {
201     // we only update the buffer if the buffer is already on hardware
202     // and if it is dirty
203     if (m_hwBufferInitialized && (m_rangeStart < m_rangeEnd)) {
204         if (m_rangeStart == 0 && m_rangeEnd >= quint32(m_shadowCopy.size())) {
205             m_backend->updateBuffer(m_handle, m_type, m_usageType, toByteView(m_shadowCopy));
206         } else {
207             Q_ASSERT(m_rangeStart < m_rangeEnd && m_rangeEnd <= quint32(m_shadowCopy.size()));
208             m_backend->updateBufferRange(m_handle,
209                                          m_type,
210                                          m_rangeStart,
211                                          QSSGByteView(m_shadowCopy.constBegin() + m_rangeStart, m_rangeEnd - m_rangeStart));
212         }
213 
214         m_rangeStart = std::numeric_limits<quint32>::max();
215         m_rangeEnd = 0;
216     }
217 }
218 
addParam(const ParamHandle & handle,QSSGRenderShaderDataType type,qint32 count)219 void QSSGRenderConstantBuffer::addParam(const ParamHandle &handle, QSSGRenderShaderDataType type, qint32 count)
220 {
221     const auto it = m_constantBufferEntryMap.constFind(handle);
222     const auto end = m_constantBufferEntryMap.cend();
223     if (it != end) // no duplicated entries
224         return;
225 
226     ConstantBufferParamEntry *newEntry = new ConstantBufferParamEntry(handle.name, type, count, m_currentOffset);
227     m_constantBufferEntryMap.insert(handle, newEntry);
228 
229     // compute new current buffer size and offset
230     qint32 constantSize = uniformTypeSize(type) * count;
231     m_currentSize += constantSize;
232     m_currentOffset += constantSize;
233 }
234 
updateParam(const ParamHandle & handle,QSSGByteView value)235 void QSSGRenderConstantBuffer::updateParam(const ParamHandle &handle, QSSGByteView value)
236 {
237     // allocate space if not done yet
238     // NOTE this gets reallocated once we get the real constant buffer size from a program
239     if (!m_shadowCopy.size())
240         m_shadowCopy.resize(m_currentSize);
241 
242     const auto entry = m_constantBufferEntryMap.constFind(handle);
243     if (entry != m_constantBufferEntryMap.cend()) {
244         const qint32 size = entry.value()->m_count * uniformTypeSize(entry.value()->m_type);
245         Q_ASSERT(size == value.size());
246         if (!memcmp(m_shadowCopy.constBegin() + entry.value()->m_offset, value.begin(), size_t(size)))
247             return;
248         memcpy(m_shadowCopy.begin() + entry.value()->m_offset, value.begin(), size_t(size));
249         setDirty(entry.value()->m_offset, size);
250     }
251 }
252 
updateRaw(quint32 offset,QSSGByteView data)253 void QSSGRenderConstantBuffer::updateRaw(quint32 offset, QSSGByteView data)
254 {
255     // allocate space if yet done
256     if (!m_shadowCopy.size()) {
257         Q_ASSERT(offset == 0);
258         m_shadowCopy.resize(data.size());
259     }
260 
261     Q_ASSERT((offset + data.size()) < (quint32)m_maxBlockSize);
262 
263     // we do not initialize anything when this is used
264     m_hwBufferInitialized = true;
265 
266     // we do not allow resize once allocated
267     if ((offset + data.size()) > quint32(m_shadowCopy.size()))
268         return;
269 
270     // copy data
271     if (!memcmp(m_shadowCopy.constBegin() + offset, data.begin(), data.size())) {
272         return;
273     }
274     memcpy(m_shadowCopy.begin() + offset, data.begin(), data.size());
275 
276     setDirty(offset, data.size());
277 }
278 
createParamEntry(const QByteArray & name,QSSGRenderShaderDataType type,qint32 count,qint32 offset)279 ConstantBufferParamEntry *QSSGRenderConstantBuffer::createParamEntry(const QByteArray &name,
280                                                                        QSSGRenderShaderDataType type,
281                                                                        qint32 count,
282                                                                        qint32 offset)
283 {
284     ConstantBufferParamEntry *newEntry = new ConstantBufferParamEntry(name, type, count, offset);
285 
286     return newEntry;
287 }
288 
uniformTypeSize(QSSGRenderShaderDataType type)289 qint32 QSSGRenderConstantBuffer::uniformTypeSize(QSSGRenderShaderDataType type)
290 {
291     switch (type) {
292     case QSSGRenderShaderDataType::Float:
293         return sizeof(float);
294     case QSSGRenderShaderDataType::Integer:
295         return sizeof(qint32);
296     case QSSGRenderShaderDataType::IntegerVec2:
297         return sizeof(qint32) * 2;
298     case QSSGRenderShaderDataType::IntegerVec3:
299         return sizeof(qint32) * 3;
300     case QSSGRenderShaderDataType::IntegerVec4:
301         return sizeof(qint32) * 4;
302     case QSSGRenderShaderDataType::UnsignedInteger:
303         return sizeof(quint32);
304     case QSSGRenderShaderDataType::UnsignedIntegerVec2:
305         return sizeof(quint32) * 2;
306     case QSSGRenderShaderDataType::UnsignedIntegerVec3:
307         return sizeof(quint32) * 3;
308     case QSSGRenderShaderDataType::UnsignedIntegerVec4:
309         return sizeof(quint32) * 4;
310     case QSSGRenderShaderDataType::Vec2:
311         return sizeof(float) * 2;
312     case QSSGRenderShaderDataType::Vec3:
313         return sizeof(float) * 3;
314     case QSSGRenderShaderDataType::Vec4:
315         return sizeof(float) * 4;
316     case QSSGRenderShaderDataType::Matrix3x3:
317         return sizeof(float) * 9;
318     case QSSGRenderShaderDataType::Matrix4x4:
319         return sizeof(float) * 16;
320     default:
321         Q_ASSERT_X(0, "Unhandled type",  "QSSGRenderConstantBuffer::getUniformTypeSize");
322         break;
323     }
324 
325     return 0;
326 }
327 
328 QT_END_NAMESPACE
329