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 ? qOverload<QRhiBuffer *, quint32, QByteArray>(
54 &QRhiResourceUpdateBatch::updateDynamicBuffer)
55 : qOverload<QRhiBuffer *, quint32, QByteArray>(
56 &QRhiResourceUpdateBatch::uploadStaticBuffer);
57 if (!m_rhiBuffer) {
58 if (m_allocSize <= 0)
59 return false;
60
61 const auto kind = m_dynamic ? QRhiBuffer::Dynamic : QRhiBuffer::Static;
62 const auto usage = bufferTypeToRhi(t);
63
64 m_rhiBuffer = ctx->rhi()->newBuffer(type: kind, usage, size: m_allocSize);
65 assert(m_rhiBuffer);
66
67 if (!m_rhiBuffer->create())
68 return false;
69
70#if defined(QT_DEBUG)
71 {
72 // for debug: we set the buffer to zero
73 (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, 0, QByteArray(m_allocSize, 0));
74 }
75#endif
76 }
77 assert(m_rhiBuffer);
78
79#if defined(QT_DEBUG)
80 // RHI does not seem to support using the same buffer with different types
81 assert(m_rhiBuffer->usage() == bufferTypeToRhi(t));
82#endif
83
84 for (auto &[data, offset] : m_datasToUpload) {
85 (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, offset, std::move(data));
86 }
87
88 m_datasToUpload.clear();
89 return true;
90}
91
92void RHIBuffer::destroy()
93{
94 if (m_rhiBuffer) {
95 delete m_rhiBuffer;
96 m_rhiBuffer = nullptr;
97 }
98 destroyOrphaned();
99 m_allocSize = 0;
100}
101
102void RHIBuffer::destroyOrphaned()
103{
104 for (QRhiBuffer *oldBuffer : m_buffersToCleanup)
105 delete oldBuffer;
106 m_buffersToCleanup.clear();
107}
108
109// Note: when we orphan, we have to keep the previous QRhiBuffer alive until
110// after frame has been submitted. This is because we might have already stored
111// updates in the RHI Command Buffers for the buffer that is about to be
112// destroyed. We therefore have to wait until command buffer has been submitted
113// until we can destroy the buffer.
114void RHIBuffer::orphan()
115{
116 m_datasToUpload.clear();
117 if (m_rhiBuffer) {
118 m_buffersToCleanup.push_back(x: m_rhiBuffer);
119 m_rhiBuffer = nullptr;
120 }
121}
122
123void RHIBuffer::allocate(const QByteArray &data, bool dynamic)
124{
125 // We orphan only if new size is larger than current size
126 // Otherwise, we can just reuse current buffer
127 if (data.size() > m_allocSize)
128 orphan();
129
130 m_datasToUpload.clear();
131 m_datasToUpload.push_back(x: { data, 0 });
132 m_allocSize = std::max(a: m_allocSize, b: data.size());
133 m_dynamic = dynamic;
134}
135
136void RHIBuffer::update(const QByteArray &data, int offset)
137{
138 m_datasToUpload.push_back(x: { data, offset });
139}
140
141QByteArray RHIBuffer::download(SubmissionContext *ctx, uint size)
142{
143 Q_UNUSED(ctx);
144 Q_UNUSED(size);
145 RHI_UNIMPLEMENTED;
146 // char *gpu_ptr = ctx->mapBuffer(m_lastTarget, size);
147 // QByteArray data;
148 // if (gpu_ptr != nullptr) {
149 // data.resize(size);
150 // std::copy(gpu_ptr, gpu_ptr+size, data.data());
151 // }
152 // ctx->unmapBuffer(m_lastTarget);
153 // return data;
154 return {};
155}
156
157void RHIBuffer::cleanup()
158{
159 m_bufferId = Qt3DCore::QNodeId();
160 m_dynamic = true;
161 destroy();
162}
163
164} // namespace Rhi
165
166} // namespace Render
167
168} // namespace Qt3DRender
169
170QT_END_NAMESPACE
171

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