Revision control
Copy as Markdown
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "renderextension.h"
#include <QtCore/qpointer.h>
#include <QtCore/qfile.h>
#include <rhi/qshader.h>
#include <rhi/qrhi.h>
#include <ssg/qssgrenderextensions.h>
#include <ssg/qssgrenderhelpers.h>
#include <ssg/qssgrendercontextcore.h>
#include <ssg/qssgrhicontext.h>
#include <ssg/qquick3dextensionhelpers.h>
static constexpr float triangle[] = {
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f,
};
class TextureProvider : public QSSGRenderExtension
{
public:
explicit TextureProvider(RenderExtension *ext);
bool prepareData(QSSGFrameData &data) override;
void prepareRender(QSSGFrameData &data) override;
void render(QSSGFrameData &data) override;
void resetForFrame() override;
RenderMode mode() const override { return QSSGRenderExtension::RenderMode::Standalone; }
RenderStage stage() const override { return QSSGRenderExtension::RenderStage::PreColor; }
private:
static QShader getShader(const QString &name)
{
QFile f(name);
return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader();
}
QPointer<RenderExtension> m_ext;
QSSGExtensionId m_extensionId {};
std::unique_ptr<QRhiTexture> texture;
std::unique_ptr<QRhiTextureRenderTarget> rt;
std::unique_ptr<QRhiRenderPassDescriptor> rpDesc;
std::unique_ptr<QRhiBuffer> vbuf;
std::unique_ptr<QRhiBuffer> ubuf;
std::unique_ptr<QRhiShaderResourceBindings> srb;
std::unique_ptr<QRhiGraphicsPipeline> ps;
float rotation = 0.0f;
};
TextureProvider::TextureProvider(RenderExtension *ext)
: m_ext(ext)
{
}
bool TextureProvider::prepareData(QSSGFrameData &data)
{
Q_UNUSED(data);
// Update the extension ID.
// NOTE: This needs to be done on each call, the extension object may have changed.
// If the extension is destroyed (m_ext == nullptr), we cannot proceed and should return false!
m_extensionId = m_ext ? QQuick3DExtensionHelpers::getExtensionId(*m_ext) : QSSGExtensionId{};
if (QQuick3DExtensionHelpers::isNull(m_extensionId))
return false;
return true;
}
void TextureProvider::prepareRender(QSSGFrameData &data)
{
const auto &ctxIfx = data.contextInterface();
const auto &rhiCtx = ctxIfx->rhiContext();
if (!rhiCtx)
return;
QRhi *rhi = rhiCtx->rhi();
if (!texture) {
texture.reset(rhi->newTexture(QRhiTexture::RGBA8, QSize(512, 512), 1, QRhiTexture::RenderTarget));
texture->create();
rt.reset(rhi->newTextureRenderTarget({ texture.get() }));
rpDesc.reset(rt->newCompatibleRenderPassDescriptor());
rt->setRenderPassDescriptor(rpDesc.get());
rt->create();
QSSGRenderExtensionHelpers::registerRenderResult(data, m_extensionId, texture.get());
}
if (!ps) {
vbuf.reset(rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(triangle)));
vbuf->create();
ubuf.reset(rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64));
ubuf->create();
srb.reset(rhi->newShaderResourceBindings());
srb->setBindings({
QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, ubuf.get()),
});
srb->create();
ps.reset(rhi->newGraphicsPipeline());
ps->setShaderStages({
{ QRhiShaderStage::Vertex, getShader(QLatin1String(":/shaders/color.vert.qsb")) },
{ QRhiShaderStage::Fragment, getShader(QLatin1String(":/shaders/color.frag.qsb")) }
});
QRhiVertexInputLayout inputLayout;
inputLayout.setBindings({
{ 5 * sizeof(float) }
});
inputLayout.setAttributes({
{ 0, 0, QRhiVertexInputAttribute::Float2, 0 },
{ 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) }
});
ps->setVertexInputLayout(inputLayout);
ps->setShaderResourceBindings(srb.get());
ps->setRenderPassDescriptor(rpDesc.get());
ps->create();
QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();
resourceUpdates->uploadStaticBuffer(vbuf.get(), triangle);
cb->resourceUpdate(resourceUpdates);
}
}
void TextureProvider::render(QSSGFrameData &data)
{
const auto &ctxIfx = data.contextInterface();
const auto &rhiCtx = ctxIfx->rhiContext();
if (!rhiCtx)
return;
QRhi *rhi = rhiCtx->rhi();
QRhiResourceUpdateBatch *resourceUpdates = rhi->nextResourceUpdateBatch();
const QSize outputSize = rt->pixelSize();
QMatrix4x4 viewProjection = rhi->clipSpaceCorrMatrix();
viewProjection.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f);
viewProjection.translate(0, 0, -4);
QMatrix4x4 modelViewProjection = viewProjection;
modelViewProjection.rotate(rotation, 0, 1, 0);
resourceUpdates->updateDynamicBuffer(ubuf.get(), 0, 64, modelViewProjection.constData());
QRhiCommandBuffer *cb = rhiCtx->commandBuffer();
const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f);
cb->beginPass(rt.get(), clearColor, { 1.0f, 0}, resourceUpdates);
cb->setGraphicsPipeline(ps.get());
cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height()));
cb->setShaderResources();
const QRhiCommandBuffer::VertexInput vbufBinding(vbuf.get(), 0);
cb->setVertexInput(0, 1, &vbufBinding);
cb->draw(3);
cb->endPass();
}
void TextureProvider::resetForFrame()
{
}
RenderExtension::RenderExtension()
{
}
QSSGRenderGraphObject *RenderExtension::updateSpatialNode(QSSGRenderGraphObject *node)
{
TextureProvider *renderNode = static_cast<TextureProvider *>(node);
if (!renderNode)
renderNode = new TextureProvider(this);
return renderNode;
}