1// Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "rhibuffer_p.h"
5#include <submissioncontext_p.h>
6#include <rhi/qrhi.h>
7
8QT_BEGIN_NAMESPACE
9
10namespace Qt3DRender {
11
12namespace Render {
13
14namespace Rhi {
15
16namespace {
17QRhiBuffer::UsageFlags bufferTypeToRhi(RHIBuffer::Type t)
18{
19 QRhiBuffer::UsageFlags flag{};
20
21 if (t & RHIBuffer::Type::ArrayBuffer ||
22 t & RHIBuffer::Type::ShaderStorageBuffer) {
23 // We have no easy way to know if a SSBO is used as a VertexBuffer
24 // and vice versa, so we set both flags when either type is requested
25 flag |= QRhiBuffer::StorageBuffer;
26 flag |= QRhiBuffer::VertexBuffer;
27 }
28
29 if (t & RHIBuffer::Type::IndexBuffer)
30 flag |= QRhiBuffer::IndexBuffer;
31
32 if (t & RHIBuffer::Type::UniformBuffer)
33 flag |= QRhiBuffer::UniformBuffer;
34
35 return flag;
36}
37}
38
39// A UBO is created for each ShaderData Shader Pair
40// That means a UBO is unique to a shader/shaderdata
41
42RHIBuffer::RHIBuffer()
43 : m_bufferId(Qt3DCore::QNodeId())
44 , m_dynamic(true)
45{ }
46
47bool RHIBuffer::bind(SubmissionContext *ctx, Type t)
48{
49 assert(ctx->m_currentUpdates);
50 if (this->m_datasToUpload.empty())
51 return bool(m_rhiBuffer);
52
53 const auto uploadMethod = m_dynamic ? &QRhiResourceUpdateBatch::updateDynamicBuffer
54 : qOverload<QRhiBuffer *, quint32, quint32, const void *>(
55 &QRhiResourceUpdateBatch::uploadStaticBuffer);
56 if (!m_rhiBuffer) {
57 if (m_allocSize <= 0)
58 return false;
59
60 const auto kind = m_dynamic ? QRhiBuffer::Dynamic : QRhiBuffer::Static;
61 const auto usage = bufferTypeToRhi(t);
62
63 m_rhiBuffer = ctx->rhi()->newBuffer(type: kind, usage, size: m_allocSize);
64 assert(m_rhiBuffer);
65
66 if (!m_rhiBuffer->create())
67 return false;
68
69#if defined(QT_DEBUG)
70 {
71 // for debug: we set the buffer to zero
72 auto ptr = new char[m_allocSize] {};
73 (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, 0, m_allocSize, ptr);
74 delete[] ptr;
75 }
76#endif
77 }
78 assert(m_rhiBuffer);
79
80#if defined(QT_DEBUG)
81 // RHI does not seem to support using the same buffer with different types
82 assert(m_rhiBuffer->usage() == bufferTypeToRhi(t));
83#endif
84
85 for (const std::pair<QByteArray, int> &pair : this->m_datasToUpload) {
86 const QByteArray &data = pair.first;
87 int offset = pair.second;
88 (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, offset, data.size(), data.constData());
89 }
90
91 m_datasToUpload.clear();
92 return true;
93}
94
95void RHIBuffer::destroy()
96{
97 if (m_rhiBuffer) {
98 delete m_rhiBuffer;
99 m_rhiBuffer = nullptr;
100 }
101 destroyOrphaned();
102 m_allocSize = 0;
103}
104
105void RHIBuffer::destroyOrphaned()
106{
107 for (QRhiBuffer *oldBuffer : m_buffersToCleanup)
108 delete oldBuffer;
109 m_buffersToCleanup.clear();
110}
111
112// Note: when we orphan, we have to keep the previous QRhiBuffer alive until
113// after frame has been submitted. This is because we might have already stored
114// updates in the RHI Command Buffers for the buffer that is about to be
115// destroyed. We therefore have to wait until command buffer has been submitted
116// until we can destroy the buffer.
117void RHIBuffer::orphan()
118{
119 m_datasToUpload.clear();
120 if (m_rhiBuffer) {
121 m_buffersToCleanup.push_back(x: m_rhiBuffer);
122 m_rhiBuffer = nullptr;
123 }
124}
125
126void RHIBuffer::allocate(const QByteArray &data, bool dynamic)
127{
128 // We orphan only if new size is larger than current size
129 // Otherwise, we can just reuse current buffer
130 if (data.size() > m_allocSize)
131 orphan();
132
133 m_datasToUpload.clear();
134 m_datasToUpload.push_back(x: { data, 0 });
135 m_allocSize = std::max(a: m_allocSize, b: data.size());
136 m_dynamic = dynamic;
137}
138
139void RHIBuffer::update(const QByteArray &data, int offset)
140{
141 m_datasToUpload.push_back(x: { data, offset });
142}
143
144QByteArray RHIBuffer::download(SubmissionContext *ctx, uint size)
145{
146 Q_UNUSED(ctx);
147 Q_UNUSED(size);
148 RHI_UNIMPLEMENTED;
149 // char *gpu_ptr = ctx->mapBuffer(m_lastTarget, size);
150 // QByteArray data;
151 // if (gpu_ptr != nullptr) {
152 // data.resize(size);
153 // std::copy(gpu_ptr, gpu_ptr+size, data.data());
154 // }
155 // ctx->unmapBuffer(m_lastTarget);
156 // return data;
157 return {};
158}
159
160void RHIBuffer::cleanup()
161{
162 m_bufferId = Qt3DCore::QNodeId();
163 m_dynamic = true;
164 destroy();
165}
166
167} // namespace Rhi
168
169} // namespace Render
170
171} // namespace Qt3DRender
172
173QT_END_NAMESPACE
174

source code of qt3d/src/plugins/renderers/rhi/io/rhibuffer.cpp