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