Revision control
++ // Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB).
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include "rhibuffer_p.h"
++ #include <submissioncontext_p.h>
++ #include <rhi/qrhi.h>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace Qt3DRender {
++
++ namespace Render {
++
++ namespace Rhi {
++
++ namespace {
++ QRhiBuffer::UsageFlags bufferTypeToRhi(RHIBuffer::Type t)
++ {
++ QRhiBuffer::UsageFlags flag{};
++
++ if (t & RHIBuffer::Type::ArrayBuffer ||
++ t & RHIBuffer::Type::ShaderStorageBuffer) {
++ // We have no easy way to know if a SSBO is used as a VertexBuffer
++ // and vice versa, so we set both flags when either type is requested
++ flag |= QRhiBuffer::StorageBuffer;
++ flag |= QRhiBuffer::VertexBuffer;
++ }
++
++ if (t & RHIBuffer::Type::IndexBuffer)
++ flag |= QRhiBuffer::IndexBuffer;
++
++ if (t & RHIBuffer::Type::UniformBuffer)
++ flag |= QRhiBuffer::UniformBuffer;
++
++ return flag;
++ }
++ }
++
++ // A UBO is created for each ShaderData Shader Pair
++ // That means a UBO is unique to a shader/shaderdata
++
++ RHIBuffer::RHIBuffer()
++ : m_bufferId(Qt3DCore::QNodeId())
++ , m_dynamic(true)
++ { }
++
++ bool RHIBuffer::bind(SubmissionContext *ctx, Type t)
++ {
++ assert(ctx->m_currentUpdates);
++ if (this->m_datasToUpload.empty())
++ return bool(m_rhiBuffer);
++
++ const auto uploadMethod = m_dynamic ? qOverload<QRhiBuffer *, quint32, QByteArray>(
++ &QRhiResourceUpdateBatch::updateDynamicBuffer)
++ : qOverload<QRhiBuffer *, quint32, QByteArray>(
++ &QRhiResourceUpdateBatch::uploadStaticBuffer);
++ if (!m_rhiBuffer) {
++ if (m_allocSize <= 0)
++ return false;
++
++ const auto kind = m_dynamic ? QRhiBuffer::Dynamic : QRhiBuffer::Static;
++ const auto usage = bufferTypeToRhi(t);
++
++ m_rhiBuffer = ctx->rhi()->newBuffer(kind, usage, m_allocSize);
++ assert(m_rhiBuffer);
++
++ if (!m_rhiBuffer->create())
++ return false;
++
++ #if defined(QT_DEBUG)
++ {
++ // for debug: we set the buffer to zero
++ (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, 0, QByteArray(m_allocSize, 0));
++ }
++ #endif
++ }
++ assert(m_rhiBuffer);
++
++ #if defined(QT_DEBUG)
++ // RHI does not seem to support using the same buffer with different types
++ assert(m_rhiBuffer->usage() == bufferTypeToRhi(t));
++ #endif
++
++ for (auto &[data, offset] : m_datasToUpload) {
++ (ctx->m_currentUpdates->*uploadMethod)(m_rhiBuffer, offset, std::move(data));
++ }
++
++ m_datasToUpload.clear();
++ return true;
++ }
++
++ void RHIBuffer::destroy()
++ {
++ if (m_rhiBuffer) {
++ delete m_rhiBuffer;
++ m_rhiBuffer = nullptr;
++ }
++ destroyOrphaned();
++ m_allocSize = 0;
++ }
++
++ void RHIBuffer::destroyOrphaned()
++ {
++ for (QRhiBuffer *oldBuffer : m_buffersToCleanup)
++ delete oldBuffer;
++ m_buffersToCleanup.clear();
++ }
++
++ // Note: when we orphan, we have to keep the previous QRhiBuffer alive until
++ // after frame has been submitted. This is because we might have already stored
++ // updates in the RHI Command Buffers for the buffer that is about to be
++ // destroyed. We therefore have to wait until command buffer has been submitted
++ // until we can destroy the buffer.
++ void RHIBuffer::orphan()
++ {
++ m_datasToUpload.clear();
++ if (m_rhiBuffer) {
++ m_buffersToCleanup.push_back(m_rhiBuffer);
++ m_rhiBuffer = nullptr;
++ }
++ }
++
++ void RHIBuffer::allocate(const QByteArray &data, bool dynamic)
++ {
++ // We orphan only if new size is larger than current size
++ // Otherwise, we can just reuse current buffer
++ if (data.size() > m_allocSize)
++ orphan();
++
++ m_datasToUpload.clear();
++ m_datasToUpload.push_back({ data, 0 });
++ m_allocSize = std::max(m_allocSize, data.size());
++ m_dynamic = dynamic;
++ }
++
++ void RHIBuffer::update(const QByteArray &data, int offset)
++ {
++ m_datasToUpload.push_back({ data, offset });
++ }
++
++ QByteArray RHIBuffer::download(SubmissionContext *ctx, uint size)
++ {
++ Q_UNUSED(ctx);
++ Q_UNUSED(size);
++ RHI_UNIMPLEMENTED;
++ // char *gpu_ptr = ctx->mapBuffer(m_lastTarget, size);
++ // QByteArray data;
++ // if (gpu_ptr != nullptr) {
++ // data.resize(size);
++ // std::copy(gpu_ptr, gpu_ptr+size, data.data());
++ // }
++ // ctx->unmapBuffer(m_lastTarget);
++ // return data;
++ return {};
++ }
++
++ void RHIBuffer::cleanup()
++ {
++ m_bufferId = Qt3DCore::QNodeId();
++ m_dynamic = true;
++ destroy();
++ }
++
++ } // namespace Rhi
++
++ } // namespace Render
++
++ } // namespace Qt3DRender
++
++ QT_END_NAMESPACE