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 | |
8 | QT_BEGIN_NAMESPACE |
9 | |
10 | namespace Qt3DRender { |
11 | |
12 | namespace Render { |
13 | |
14 | namespace Rhi { |
15 | |
16 | namespace { |
17 | QRhiBuffer::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 | |
42 | RHIBuffer::RHIBuffer() |
43 | : m_bufferId(Qt3DCore::QNodeId()) |
44 | , m_dynamic(true) |
45 | { } |
46 | |
47 | bool 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 | |
95 | void RHIBuffer::destroy() |
96 | { |
97 | if (m_rhiBuffer) { |
98 | delete m_rhiBuffer; |
99 | m_rhiBuffer = nullptr; |
100 | } |
101 | destroyOrphaned(); |
102 | m_allocSize = 0; |
103 | } |
104 | |
105 | void 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. |
117 | void 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 | |
126 | void 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 | |
139 | void RHIBuffer::update(const QByteArray &data, int offset) |
140 | { |
141 | m_datasToUpload.push_back(x: { data, offset }); |
142 | } |
143 | |
144 | QByteArray 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 | |
160 | void 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 | |
173 | QT_END_NAMESPACE |
174 | |