Revision control

++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++
++ #include "genshaders.h"
++
++ #include <QtCore/qdir.h>
++
++ #include <QtQml/qqmllist.h>
++
++ #include <QtQuick3D/private/qquick3dsceneenvironment_p.h>
++ #include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
++ #include <QtQuick3D/private/qquick3dviewport_p.h>
++ #include <QtQuick3D/private/qquick3dscenerenderer_p.h>
++ #include <QtQuick3D/private/qquick3dscenemanager_p.h>
++ #include <QtQuick3D/private/qquick3dperspectivecamera_p.h>
++
++ #include <QtQuick3D/private/qquick3dspotlight_p.h>
++ #include <QtQuick3D/private/qquick3ddirectionallight_p.h>
++ #include <QtQuick3D/private/qquick3dpointlight_p.h>
++
++ #include <QtQuick3DUtils/private/qqsbcollection_p.h>
++
++ #include <private/qssgrenderer_p.h>
++ #include <private/qssglayerrenderdata_p.h>
++ #include <private/qssgrhiparticles_p.h>
++ #include <private/qssgrhicustommaterialsystem_p.h>
++ #include <QtQuick3DRuntimeRender/private/qssgrenderroot_p.h>
++
++ #include <rhi/qshaderbaker.h>
++
++ #include <memory>
++
++ static inline void qDryRunPrintQsbcAdd(const QByteArray &id)
++ {
++ printf("Shader pipeline generated for (dry run):\n %s\n\n", qPrintable(id));
++ }
++
++ GenShaders::GenShaders()
++ {
++ sceneManager.reset(new QQuick3DSceneManager);
++
++ rhi.reset(QRhi::create(QRhi::Null, nullptr));
++ QRhiCommandBuffer *cb;
++ rhi->beginOffscreenFrame(&cb);
++
++ std::unique_ptr<QSSGRhiContext> rhiContext = std::make_unique<QSSGRhiContext>(rhi.get());
++ QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiContext.get());
++ rhiCtxD->setCommandBuffer(cb);
++
++ renderContext = std::make_shared<QSSGRenderContextInterface>(std::make_unique<QSSGBufferManager>(),
++ std::make_unique<QSSGRenderer>(),
++ std::make_shared<QSSGShaderLibraryManager>(),
++ std::make_unique<QSSGShaderCache>(*rhiContext, &QSSGShaderCache::initBakerForPersistentUse),
++ std::make_unique<QSSGCustomMaterialSystem>(),
++ std::make_unique<QSSGProgramGenerator>(),
++ std::move(rhiContext));
++ }
++
++ GenShaders::~GenShaders() = default;
++
++ bool GenShaders::process(QVector<QString> &qsbcFiles,
++ const QDir &outDir,
++ bool verbose,
++ bool dryRun)
++ {
++ const QString resourceFolderRelative = QSSGShaderCache::resourceFolder().mid(2);
++ if (!dryRun && !outDir.exists(resourceFolderRelative)) {
++ if (!outDir.mkpath(resourceFolderRelative)) {
++ qDebug("Unable to create folder: %s", qPrintable(outDir.path() + QDir::separator() + resourceFolderRelative));
++ return false;
++ }
++ }
++
++ const QString outputFolder = outDir.canonicalPath() + QDir::separator() + resourceFolderRelative;
++
++ renderContext->renderer()->setViewport(QRect(QPoint(), QSize(888,666)));
++ const auto &renderer = renderContext->renderer();
++
++ const QString outCollectionFile = outputFolder + QString::fromLatin1(QSSGShaderCache::particleShaderCollectionFile());
++ QQsbIODeviceCollection qsbc(outCollectionFile);
++ if (!dryRun && !qsbc.map(QQsbIODeviceCollection::Write))
++ return false;
++
++ QSSGRenderLayer layer;
++ QSSGRenderRoot rootNode;
++ if (!layer.rootNode) {
++ rootNode.addChild(layer);
++ layer.rootNode = &rootNode;
++ }
++ const auto layerRenderDataPtr = std::make_unique<QSSGLayerRenderData>(layer, *renderer);
++ auto &layerRenderData = *layerRenderDataPtr;
++
++ layerRenderData.prepareForRender();
++
++ // Disable shader caching so that we always generate the shaders
++ QSSGParticleRenderer::setShaderCacheEnabled(false);
++
++ const auto &features = QSSGParticleRenderer::particleShaderFeatures(layerRenderData.getShaderFeatures());
++ const auto &propertyTable = layerRenderData.getParticleMaterialPropertyTable();
++
++ /*
++ QSSGShaderKeyBoolean m_isSpriteLinear;
++ QSSGShaderKeyBoolean m_isColorTableLinear;
++ QSSGShaderKeyBoolean m_hasLighting;
++ QSSGShaderKeyBoolean m_isLineParticle;
++ QSSGShaderKeyBoolean m_isMapped;
++ QSSGShaderKeyBoolean m_isAnimated;
++ QSSGShaderKeyUnsigned<3> m_viewCount;
++ QSSGShaderKeyUnsigned<3> m_orderIndependentTransparency;
++ */
++ /* Only generate srgb */
++ const bool isSpriteLinear = false;
++ const bool isColorTableLinear = false;
++ /* Number of shaders to generate.
++ We'll add the bits here, but skip generating for undefined values in the loop.
++ */
++ uint32_t permutations = 2 * 2 * 2 * 2 * 2 * 4;
++
++ for (uint32_t i = 0; i < permutations; i++) {
++ QByteArray shaderString;
++
++ QSSGRenderableObjectFlags flags;
++ flags.setHasTransparency(true);
++ QSSGRenderParticles particles;
++ QSSGShaderParticleMaterialKey &key(particles.materialKey);
++
++ propertyTable.m_isSpriteLinear.setValue(key, isSpriteLinear);
++ propertyTable.m_isColorTableLinear.setValue(key, isColorTableLinear);
++
++ uint32_t perm = i;
++ propertyTable.m_hasLighting.setValue(key, (perm&1));
++ perm >>= 1;
++ propertyTable.m_isLineParticle.setValue(key, (perm&1));
++ perm >>= 1;
++ propertyTable.m_isMapped.setValue(key, (perm&1));
++ perm >>= 1;
++ propertyTable.m_isAnimated.setValue(key, (perm&1));
++ perm >>= 1;
++ propertyTable.m_viewCount.setValue(key, (perm&1) ? 2 : 1);
++ perm >>= 1;
++ int oit = (perm&3);
++ if (oit > int(QSSGRenderLayer::OITMethod::LinkedList))
++ continue;
++ propertyTable.m_orderIndependentTransparency.setValue(key, oit);
++
++ QSSGParticlesRenderable particlesRenderable(flags, {}, renderer.get(), {}, particles, nullptr, nullptr, {}, 1.0, key);
++ auto shaderPipeline = QSSGParticleRenderer::generateRhiShaderPipeline(*renderer.get(), particlesRenderable, features, shaderString, propertyTable);
++
++ if (shaderPipeline != nullptr) {
++ const auto qsbcFeatureList = QQsbCollection::toFeatureSet(features);
++ const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, qsbcFeatureList);
++ const auto vertexStage = shaderPipeline->vertexStage();
++ const auto fragmentStage = shaderPipeline->fragmentStage();
++ if (vertexStage && fragmentStage) {
++ if (verbose)
++ qDebug () << shaderString;
++ if (dryRun)
++ qDryRunPrintQsbcAdd(shaderString);
++ else
++ qsbc.addEntry(qsbcKey, { shaderString, qsbcFeatureList, vertexStage->shader(), fragmentStage->shader() });
++ }
++ }
++ }
++
++ if (!qsbc.availableEntries().isEmpty())
++ qsbcFiles.push_back(resourceFolderRelative + QDir::separator() + QString::fromLatin1(QSSGShaderCache::particleShaderCollectionFile()));
++ qsbc.unmap();
++
++ return true;
++ }