Files
RedBear-OS/recipes/wip/qt/qtquick3d/source/tools/shadergen/genshaders.cpp
T
vasilito ff4ff35918 feat: track all source trees in git — full fork offline-first model
Red Bear OS is a full fork. All sources must be available from git clone
with zero network access. Removed gitignore rules that excluded fetched
source trees under recipes/*/source/, local/recipes/kde/*/source/,
local/recipes/qt/*/source/, and vendor source trees.

Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded.

127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt
frameworks, mesa, wayland, DRM drivers, and every other recipe source.
2026-05-14 10:55:53 +01:00

380 lines
17 KiB
C++

// Copyright (C) 2020 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>
// Lights
#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 <QtQuick3DRuntimeRender/private/qssgrhieffectsystem_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
#include <QtQuick3DRuntimeRender/private/qssgdebugdrawsystem_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderroot_p.h>
#include <rhi/qshaderbaker.h>
static inline void qDryRunPrintQsbcAdd(const QByteArray &id)
{
printf("Shader pipeline generated for (dry run):\n %s\n\n", qPrintable(id));
}
static void initBaker(QShaderBaker *baker, QRhi *rhi)
{
Q_UNUSED(rhi); // that's a Null-backed rhi here anyways
QVector<QShaderBaker::GeneratedShader> outputs;
// TODO: For simplicity we're just going to add all off these for now.
outputs.append({ QShader::SpirvShader, QShaderVersion(100) }); // Vulkan 1.0
outputs.append({ QShader::HlslShader, QShaderVersion(50) }); // Shader Model 5.0
outputs.append({ QShader::HlslShader, QShaderVersion(60) }); // Shader Model 6.0
outputs.append({ QShader::MslShader, QShaderVersion(12) }); // Metal 1.2
outputs.append({ QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) }); // GLES 3.0+
outputs.append({ QShader::GlslShader, QShaderVersion(140) }); // OpenGL 3.1+
baker->setGeneratedShaders(outputs);
baker->setGeneratedShaderVariants({ QShader::StandardShader });
}
GenShaders::GenShaders()
{
sceneManager = new QQuick3DSceneManager;
rhi = QRhi::create(QRhi::Null, nullptr);
QRhiCommandBuffer *cb;
rhi->beginOffscreenFrame(&cb);
std::unique_ptr<QSSGRhiContext> rhiContext = std::make_unique<QSSGRhiContext>(rhi);
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, &initBaker),
std::make_unique<QSSGCustomMaterialSystem>(),
std::make_unique<QSSGProgramGenerator>(),
std::move(rhiContext));
wa = new QQuick3DWindowAttachment(nullptr);
wa->setRci(renderContext);
sceneManager->wattached = wa;
}
GenShaders::~GenShaders() = default;
bool GenShaders::process(const MaterialParser::SceneData &sceneData,
QVector<QString> &qsbcFiles,
const QDir &outDir,
bool generateMultipleLights,
bool dryRun)
{
Q_UNUSED(generateMultipleLights);
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;
QSSGRenderLayer layer;
if (!layer.rootNode) {
auto *rootNode = new QSSGRenderRoot;
rootNode->addChild(layer);
layer.rootNode = rootNode;
}
renderContext->renderer()->setViewport(QRect(QPoint(), QSize(888,666)));
const auto &renderer = renderContext->renderer();
QSSGLayerRenderData layerData(layer, *renderer);
const auto &shaderLibraryManager = renderContext->shaderLibraryManager();
const auto &shaderCache = renderContext->shaderCache();
const auto &shaderProgramGenerator = renderContext->shaderProgramGenerator();
bool aaIsDirty = false;
bool temporalIsDirty = false;
QQuick3DViewport *view3D = sceneData.viewport;
Q_ASSERT(view3D);
QVector<QSSGRenderGraphObject *> nodes;
if (!view3D->camera()) {
auto camera = new QQuick3DPerspectiveCamera();
auto node = QQuick3DObjectPrivate::updateSpatialNode(camera, nullptr);
QQuick3DObjectPrivate::get(camera)->spatialNode = node;
nodes.append(node);
view3D->setCamera(camera);
}
// Realize resources
// Textures
const auto &textures = sceneData.textures;
for (const auto &tex : textures) {
auto node = QQuick3DObjectPrivate::updateSpatialNode(tex, nullptr);
auto obj = QQuick3DObjectPrivate::get(tex);
obj->spatialNode = node;
nodes.append(node);
}
// Free Materials (see also the model section)
const auto &materials = sceneData.materials;
for (const auto &mat : materials) {
auto obj = QQuick3DObjectPrivate::get(mat);
obj->sceneManager = sceneManager;
auto node = QQuick3DObjectPrivate::updateSpatialNode(mat, nullptr);
obj->spatialNode = node;
nodes.append(node);
}
bool shadowPerspectivePass = false;
bool shadowOrthoPass = false;
// Lights
const auto &lights = sceneData.lights;
for (const auto &light : lights) {
if (auto node = QQuick3DObjectPrivate::updateSpatialNode(light, nullptr)) {
nodes.append(node);
layer.addChild(static_cast<QSSGRenderNode &>(*node));
const auto &lightNode = static_cast<const QSSGRenderLight &>(*node);
if (lightNode.type == QSSGRenderLight::Type::DirectionalLight)
shadowOrthoPass |= true;
else
shadowPerspectivePass |= true;
}
}
// NOTE: Model.castsShadows; Model.receivesShadows; variants needs to be added for runtime support
const auto &models = sceneData.models;
for (const auto &model : models) {
auto materialList = model->materials();
for (int i = 0, e = materialList.count(&materialList); i != e; ++i) {
auto mat = materialList.at(&materialList, i);
auto obj = QQuick3DObjectPrivate::get(mat);
obj->sceneManager = sceneManager;
QSSGRenderGraphObject *node = nullptr;
if (obj->type == QQuick3DObjectPrivate::Type::CustomMaterial) {
auto customMatNode = new QSSGRenderCustomMaterial;
customMatNode->incompleteBuildTimeObject = true;
node = QQuick3DObjectPrivate::updateSpatialNode(mat, customMatNode);
customMatNode->incompleteBuildTimeObject = false;
} else {
node = QQuick3DObjectPrivate::updateSpatialNode(mat, nullptr);
}
QQuick3DObjectPrivate::get(mat)->spatialNode = node;
nodes.append(node);
}
if (auto instanceList = qobject_cast<QQuick3DInstanceList *>(model->instancing())) {
auto obj = QQuick3DObjectPrivate::get(instanceList);
auto node = QQuick3DObjectPrivate::updateSpatialNode(instanceList, nullptr);
obj->spatialNode = node;
nodes.append(node);
}
auto node = QQuick3DObjectPrivate::updateSpatialNode(model, nullptr);
QQuick3DObjectPrivate::get(model)->spatialNode = node;
nodes.append(node);
}
// Ensure all spatial node types are in the render tree
for (auto *node : std::as_const(nodes)) {
if (QSSGRenderGraphObject::isNodeType(node->type)) {
QSSGRenderNode *n = static_cast<QSSGRenderNode *>(node);
if (!n->parent)
layer.addChild(*n);
}
}
QQuick3DRenderLayerHelpers::updateLayerNodeHelper(*view3D, renderContext, layer, aaIsDirty, temporalIsDirty);
const QString outCollectionFile = outputFolder + QString::fromLatin1(QSSGShaderCache::shaderCollectionFile());
QQsbIODeviceCollection qsbc(outCollectionFile);
if (!dryRun && !qsbc.map(QQsbIODeviceCollection::Write))
return false;
QByteArray shaderString;
const auto generateShaderForModel = [&](QSSGRenderModel &model) {
layerData.resetForFrame();
if (!model.parent)
layer.addChild(model);
layerData.layer.rootNode->reindex();
layerData.prepareForRender();
const auto &features = layerData.getShaderFeatures();
const auto &propertyTable = layerData.getDefaultMaterialPropertyTable();
const auto &opaqueObjects = layerData.getSortedOpaqueRenderableObjects(*layerData.renderedCameras[0]);
const auto &transparentObjects = layerData.getSortedTransparentRenderableObjects(*layerData.renderedCameras[0]);
QSSGRenderableObject *renderable = nullptr;
if (!opaqueObjects.isEmpty())
renderable = opaqueObjects[0].obj;
else if (!transparentObjects.isEmpty())
renderable = transparentObjects[0].obj;
auto generateShader = [&](const QSSGShaderFeatures &features) {
if ((renderable->type == QSSGSubsetRenderable::Type::DefaultMaterialMeshSubset)) {
auto shaderPipeline = QSSGRendererPrivate::generateRhiShaderPipelineImpl(*static_cast<QSSGSubsetRenderable *>(renderable), *shaderLibraryManager, *shaderCache, *shaderProgramGenerator, propertyTable, features, {/*shaderAugmentation*/}, shaderString);
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 (dryRun)
qDryRunPrintQsbcAdd(shaderString);
else
qsbc.addEntry(qsbcKey, { shaderString, qsbcFeatureList, vertexStage->shader(), fragmentStage->shader() });
}
}
} else if ((renderable->type == QSSGSubsetRenderable::Type::CustomMaterialMeshSubset)) {
Q_ASSERT(!layerData.renderedCameras.isEmpty());
QSSGSubsetRenderable &cmr(static_cast<QSSGSubsetRenderable &>(*renderable));
auto pipelineState = layerData.getPipelineState();
const auto &cms = renderContext->customMaterialSystem();
const auto &material = static_cast<const QSSGRenderCustomMaterial &>(cmr.getMaterial());
auto shaderPipeline = cms->shadersForCustomMaterial(&pipelineState,
material,
cmr,
propertyTable,
features);
if (shaderPipeline) {
shaderString = material.m_shaderPathKey[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex];
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 (dryRun)
qDryRunPrintQsbcAdd(shaderString);
else
qsbc.addEntry(qsbcKey, { shaderString, qsbcFeatureList, vertexStage->shader(), fragmentStage->shader() });
}
}
}
};
if (renderable) {
generateShader(features);
QSSGShaderFeatures depthPassFeatures;
depthPassFeatures.set(QSSGShaderFeatures::Feature::DepthPass, true);
generateShader(depthPassFeatures);
if (shadowPerspectivePass) {
QSSGShaderFeatures shadowPassFeatures;
shadowPassFeatures.set(QSSGShaderFeatures::Feature::PerspectiveShadowPass, true);
generateShader(shadowPassFeatures);
}
if (shadowOrthoPass) {
QSSGShaderFeatures shadowPassFeatures;
shadowPassFeatures.set(QSSGShaderFeatures::Feature::OrthoShadowPass, true);
generateShader(shadowPassFeatures);
}
}
layer.removeChild(model);
};
for (const auto &model : models)
generateShaderForModel(static_cast<QSSGRenderModel &>(*QQuick3DObjectPrivate::get(model)->spatialNode));
// Let's generate some shaders for the "free" materials as well.
QSSGRenderModel model; // dummy
model.meshPath = QSSGRenderPath("#Cube");
for (const auto &mat : materials) {
model.materials = { QQuick3DObjectPrivate::get(mat)->spatialNode };
generateShaderForModel(model);
}
// Now generate the shaders for the effects
const auto generateEffectShader = [&](QQuick3DEffect &effect) {
auto obj = QQuick3DObjectPrivate::get(&effect);
obj->sceneManager = sceneManager;
QSSGRenderEffect *renderEffect = new QSSGRenderEffect;
renderEffect->incompleteBuildTimeObject = true;
if (auto ret = QQuick3DObjectPrivate::updateSpatialNode(&effect, renderEffect))
Q_ASSERT(ret == renderEffect);
renderEffect->incompleteBuildTimeObject = false;
obj->spatialNode = renderEffect;
nodes.append(renderEffect);
const auto &commands = renderEffect->commands;
for (const QSSGCommand *command : commands) {
if (command->m_type == CommandType::BindShader) {
auto bindShaderCommand = static_cast<const QSSGBindShader &>(*command);
for (const auto isYUpInFramebuffer : { true, false }) { // Generate effects for both up-directions.
const auto shaderPipeline = QSSGRhiEffectSystem::buildShaderForEffect(bindShaderCommand,
*shaderProgramGenerator,
*shaderLibraryManager,
*shaderCache,
isYUpInFramebuffer,
1); // no multiview support here yet
if (shaderPipeline) {
const auto &key = bindShaderCommand.m_shaderPathKey;
const QSSGShaderFeatures features = shaderLibraryManager->getShaderMetaData(key, QSSGShaderCache::ShaderType::Fragment).features;
const auto qsbcFeatureList = QQsbCollection::toFeatureSet(features);
QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(key, qsbcFeatureList);
const auto vertexStage = shaderPipeline->vertexStage();
const auto fragmentStage = shaderPipeline->fragmentStage();
if (vertexStage && fragmentStage) {
if (dryRun)
qDryRunPrintQsbcAdd(key);
else
qsbc.addEntry(qsbcKey, { key, qsbcFeatureList, vertexStage->shader(), fragmentStage->shader() });
}
}
}
}
}
};
// Effects
if (sceneData.viewport && sceneData.viewport->environment()) {
auto &env = *sceneData.viewport->environment();
auto effects = env.effects();
const auto effectCount = effects.count(&effects);
for (int i = 0; i < effectCount; ++i) {
auto effect = effects.at(&effects, i);
generateEffectShader(*effect);
}
}
// Free Effects
for (const auto &effect : std::as_const(sceneData.effects))
generateEffectShader(*effect);
if (!qsbc.availableEntries().isEmpty())
qsbcFiles.push_back(resourceFolderRelative + QDir::separator() + QString::fromLatin1(QSSGShaderCache::shaderCollectionFile()));
qsbc.unmap();
auto &children = layer.children;
for (auto it = children.begin(), end = children.end(); it != end;)
children.remove(*it++);
qDeleteAll(nodes);
return true;
}