Revision control
++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++
++ #include "qquick3drenderoutputprovider_p.h"
++
++ #include "private/qquick3dobject_p.h"
++
++ #include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
++
++ #include <ssg/qssgrenderextensions.h>
++ #include <ssg/qssgrenderhelpers.h>
++ #include <ssg/qssgrendercontextcore.h>
++ #include <ssg/qssgrhicontext.h>
++ #include <ssg/qquick3dextensionhelpers.h>
++
++ QT_BEGIN_NAMESPACE
++
++ class QSSGRenderOutputProviderExtension : public QSSGRenderTextureProviderExtension
++ {
++ public:
++ enum class SourceType {
++ None,
++ BuiltInPass,
++ UserPass,
++ };
++
++ explicit QSSGRenderOutputProviderExtension(QQuick3DRenderOutputProvider *ext);
++ ~QSSGRenderOutputProviderExtension() override;
++ bool prepareData(QSSGFrameData &data) override;
++ void prepareRender(QSSGFrameData &data) override;
++ void render(QSSGFrameData &data) override;
++ void resetForFrame() override;
++
++ SourceType m_sourceType = SourceType::BuiltInPass;
++ QSSGFrameData::RenderResult m_builtInPass = QSSGFrameData::RenderResult::DepthTexture;
++ QSSGResourceId userPassId = QSSGResourceId::Invalid;
++ QSSGFrameData::AttachmentSelector attachmentSelector = QSSGFrameData::AttachmentSelector::Attachment0;
++ bool m_isDirty = true;
++
++ QPointer<QQuick3DRenderOutputProvider> m_ext;
++ QSSGExtensionId m_extensionId {};
++ };
++
++ QSSGRenderOutputProviderExtension::QSSGRenderOutputProviderExtension(QQuick3DRenderOutputProvider *ext)
++ : m_ext(ext)
++ {
++
++ }
++
++ QSSGRenderOutputProviderExtension::~QSSGRenderOutputProviderExtension()
++ {
++
++ }
++
++ bool QSSGRenderOutputProviderExtension::prepareData(QSSGFrameData &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::Invalid;
++ if (QQuick3DExtensionHelpers::isNull(m_extensionId))
++ return false;
++
++ const bool wasDirty = m_isDirty;
++
++ switch (m_sourceType) {
++ case SourceType::None:
++ // Nothing to do
++ m_isDirty = false;
++ break;
++ case SourceType::BuiltInPass:
++ {
++ // Make sure we schedule the pass to run this frame
++ data.scheduleRenderResults(QSSGFrameData::RenderResults(m_builtInPass));
++
++ // Check if a texture exists for the result already
++ QSSGFrameData::Result extResult = data.getRenderResult(m_builtInPass);
++ if (extResult.texture) {
++ QSSGRenderExtensionHelpers::registerRenderResult(data, m_extensionId, extResult.texture);
++ m_isDirty = false;
++
++ }
++ // No "else" in this case since its likely the texture for the pass doesn't exist yet, try again in prepareRender.
++ }
++ break;
++ case SourceType::UserPass:
++ {
++ if (userPassId != QSSGResourceId::Invalid) {
++ // Make sure we schedule the pass to run this frame
++ data.scheduleRenderResults(userPassId);
++
++ // Check if a texture exists for the result already
++ QSSGFrameData::Result extResult = data.getRenderResult(userPassId, attachmentSelector);
++ if (extResult.texture) {
++ // Associate the texture with this extension and expose it so it can be used.
++ QSSGRenderExtensionHelpers::registerRenderResult(data, m_extensionId, extResult.texture);
++ m_isDirty = false;
++
++ }
++ }
++ // No "else" in this case since its likely the texture for the pass doesn't exist yet, try again in prepareRender.
++ }
++ break;
++ }
++
++ return wasDirty;
++ }
++
++ void QSSGRenderOutputProviderExtension::prepareRender(QSSGFrameData &data)
++ {
++ // If no id is set prepareData() should return false and this should not be called!
++ Q_ASSERT(!QQuick3DExtensionHelpers::isNull(m_extensionId));
++
++ QSSGFrameData::Result extResult;
++ if (m_sourceType == SourceType::BuiltInPass)
++ extResult = data.getRenderResult(m_builtInPass);
++ else if (m_sourceType == SourceType::UserPass && userPassId != QSSGResourceId::Invalid)
++ extResult = data.getRenderResult(userPassId, attachmentSelector);
++
++ if (userPassId != QSSGResourceId::Invalid) {
++ if (extResult.texture) {
++ QSSGRenderExtensionHelpers::registerRenderResult(data, m_extensionId, extResult.texture);
++ m_isDirty = false;
++ }
++ }
++ }
++
++ void QSSGRenderOutputProviderExtension::render(QSSGFrameData &)
++ {
++ // Nothing to render
++ }
++
++ void QSSGRenderOutputProviderExtension::resetForFrame()
++ {
++ // Nothing to reset
++ }
++
++
++ /*!
++ \qmltype RenderOutputProvider
++ \nativetype QQuick3DRenderOutputProvider
++ \inqmlmodule QtQuick3D.Helpers
++ \inherits TextureProviderExtension
++ \since 6.11
++ \brief Used as a bridge to access textures provided in passes.
++
++ This type is used to provide textures that are generated by passes in the rendering pipeline.
++
++ Some example of when this is useful is when you want to access the depth texture generated by the ZPrePass,
++ or the ambient occlusion texture generated by the SSAO pass. This type can be used to access these generated
++ textures and use them in materials or effects.
++
++ \qml
++ Texture {
++ textureProvider: RenderOutputProvider {
++ // Specify the pass buffer texture you want to access
++ textureSource: RenderOutputProvider.DepthTexture
++ }
++ }
++ \endqml
++
++ \sa QQuick3DTextureProviderExtension, QSSGRenderExtension
++ */
++
++ QQuick3DRenderOutputProvider::QQuick3DRenderOutputProvider(QQuick3DObject *parent)
++ : QQuick3DTextureProviderExtension(parent)
++ {
++ update();
++ }
++
++ QSSGRenderGraphObject *QQuick3DRenderOutputProvider::updateSpatialNode(QSSGRenderGraphObject *node)
++ {
++ // Create new node if needed
++ if (!node)
++ node = new QSSGRenderOutputProviderExtension(this);
++
++ QQuick3DTextureProviderExtension::updateSpatialNode(node);
++
++ QSSGRenderOutputProviderExtension *providerNode = static_cast<QSSGRenderOutputProviderExtension *>(node);
++
++ providerNode->m_isDirty = (m_dirtyAttributes != 0);
++
++ if (m_dirtyAttributes & TextureSourceDirty) {
++ switch (m_textureSource) {
++ case QQuick3DRenderOutputProvider::TextureSource::None:
++ providerNode->m_sourceType = QSSGRenderOutputProviderExtension::SourceType::None;
++ // m_builtInPass is not used in this case
++ break;
++ case QQuick3DRenderOutputProvider::TextureSource::UserPassTexture:
++ providerNode->m_sourceType = QSSGRenderOutputProviderExtension::SourceType::UserPass;
++ break;
++ case QQuick3DRenderOutputProvider::TextureSource::AoTexture:
++ providerNode->m_sourceType = QSSGRenderOutputProviderExtension::SourceType::BuiltInPass;
++ providerNode->m_builtInPass = QSSGFrameData::RenderResult::AoTexture;
++ break;
++ case QQuick3DRenderOutputProvider::TextureSource::DepthTexture:
++ providerNode->m_sourceType = QSSGRenderOutputProviderExtension::SourceType::BuiltInPass;
++ providerNode->m_builtInPass = QSSGFrameData::RenderResult::DepthTexture;
++ break;
++ case QQuick3DRenderOutputProvider::TextureSource::ScreenTexture:
++ providerNode->m_sourceType = QSSGRenderOutputProviderExtension::SourceType::BuiltInPass;
++ providerNode->m_builtInPass = QSSGFrameData::RenderResult::ScreenTexture;
++ break;
++ case QQuick3DRenderOutputProvider::TextureSource::NormalTexture:
++ providerNode->m_sourceType = QSSGRenderOutputProviderExtension::SourceType::BuiltInPass;
++ providerNode->m_builtInPass = QSSGFrameData::RenderResult::NormalTexture;
++ break;
++ case QQuick3DRenderOutputProvider::TextureSource::MotionVectorTexture:
++ providerNode->m_sourceType = QSSGRenderOutputProviderExtension::SourceType::BuiltInPass;
++ providerNode->m_builtInPass = QSSGFrameData::RenderResult::MotionVectorTexture;
++ break;
++ }
++ }
++
++ if (m_dirtyAttributes & UserPassTextureDirty) {
++ // m_builtInPass is not used in this case
++ QSSGResourceId userPassId = m_renderPass ? QQuick3DExtensionHelpers::getResourceId(*m_renderPass) : QSSGResourceId::Invalid;
++ providerNode->userPassId = userPassId;
++ providerNode->attachmentSelector = QSSGFrameData::AttachmentSelector(m_attachmentSelector);
++ }
++
++ m_dirtyAttributes = 0;
++
++ return node;
++ }
++
++ void QQuick3DRenderOutputProvider::markAllDirty()
++ {
++ m_dirtyAttributes = AllDirty;
++ QQuick3DTextureProviderExtension::markAllDirty();
++ }
++
++ void QQuick3DRenderOutputProvider::markDirty(QQuick3DRenderOutputProvider::DirtyType type)
++ {
++ if (!(m_dirtyAttributes & quint32(type))) {
++ m_dirtyAttributes |= quint32(type);
++ update();
++ }
++ }
++
++ /*!
++ \qmlproperty enumeration RenderOutputProvider::textureSource
++ This property holds which pass buffer texture to expose.
++
++ \value RenderOutputProvider.None No texture source is selected.
++ \value RenderOutputProvider.UserPassTexture Accesses a color attachment from a user-defined
++ \l RenderPass. Use the \l renderPass and \l attachmentSelector properties to specify
++ which pass and attachment to use.
++ \value RenderOutputProvider.AoTexture The ambient occlusion texture created by QtQuick3D's SSAO pass.
++ \value RenderOutputProvider.DepthTexture The depth texture created by QtQuick3D's depth pre-pass.
++ \value RenderOutputProvider.ScreenTexture The texture containing the rendered scene.
++ \value RenderOutputProvider.NormalTexture The world-space normal texture created by QtQuick3D's normal pre-pass.
++ \value RenderOutputProvider.MotionVectorTexture The motion vector texture created by QtQuick3D's motion vector pass.
++ */
++
++ QQuick3DRenderOutputProvider::TextureSource QQuick3DRenderOutputProvider::textureSource() const
++ {
++ return m_textureSource;
++ }
++
++ void QQuick3DRenderOutputProvider::setTextureSource(TextureSource newTextureSource)
++ {
++ if (m_textureSource == newTextureSource)
++ return;
++ m_textureSource = newTextureSource;
++ emit textureSourceChanged();
++ markDirty(TextureSourceDirty);
++ }
++
++ /*!
++ \qmlproperty RenderPass RenderOutputProvider::renderPass
++ The \l RenderPass whose output attachments will be exposed as a texture. Setting this
++ property automatically sets \l textureSource to \c UserPassTexture. Use
++ \l attachmentSelector to choose which color attachment of the pass to expose.
++
++ \sa attachmentSelector, textureSource, RenderPass
++ */
++
++ QQuick3DRenderPass *QQuick3DRenderOutputProvider::renderPass() const
++ {
++ return m_renderPass;
++ }
++
++ void QQuick3DRenderOutputProvider::setRenderPass(QQuick3DRenderPass *newRenderPass)
++ {
++ if (m_renderPass == newRenderPass)
++ return;
++
++ QQuick3DObjectPrivate::attachWatcher(this, &QQuick3DRenderOutputProvider::setRenderPass, newRenderPass, m_renderPass);
++
++ m_renderPass = newRenderPass;
++
++ setTextureSource(m_renderPass ? TextureSource::UserPassTexture : TextureSource::None);
++
++ markDirty(UserPassTextureDirty);
++
++ emit renderPassChanged();
++ }
++
++ /*!
++ \qmlproperty enumeration RenderOutputProvider::attachmentSelector
++ Selects which color attachment of the user-defined \l RenderPass to expose as a texture.
++ Only relevant when \l textureSource is set to \c UserPassTexture.
++
++ \value RenderOutputProvider.Attachment0 Use color attachment 0 (default).
++ \value RenderOutputProvider.Attachment1 Use color attachment 1.
++ \value RenderOutputProvider.Attachment2 Use color attachment 2.
++ \value RenderOutputProvider.Attachment3 Use color attachment 3.
++
++ \sa renderPass, textureSource
++ */
++
++ QQuick3DRenderOutputProvider::AttachmentSelector QQuick3DRenderOutputProvider::attachmentSelector() const
++ {
++ return m_attachmentSelector;
++ }
++
++ void QQuick3DRenderOutputProvider::setAttachmentSelector(AttachmentSelector newAttachmentSelector)
++ {
++ if (m_attachmentSelector == newAttachmentSelector)
++ return;
++ m_attachmentSelector = newAttachmentSelector;
++ emit attachmentSelectorChanged();
++ }
++
++ QT_END_NAMESPACE