Revision control

Copy as Markdown

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "shaderpoker.h"
#include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrhieffectsystem_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
#include <QtQuick3DRuntimeRender/private/qssgdebugdrawsystem_p.h>
#include <QtQuick3DRuntimeRender/ssg/qssgrendercontextcore.h>
#include <QtQuick3DRuntimeRender/private/qssgvertexpipelineimpl_p.h>
namespace {
QStringList splitIgnoringBraces(const QString &input) {
QStringList result;
QString currentPart;
int braceDepth = 0;
for (QChar c : input) {
if (c == '{') {
++braceDepth;
currentPart += c;
} else if (c == '}') {
--braceDepth;
if (braceDepth < 0) {
// because something is busted
braceDepth = 0;
}
currentPart += c;
} else if (c == ';' && braceDepth == 0) {
result.append(currentPart);
currentPart.clear();
} else {
currentPart += c;
}
}
if (!currentPart.isEmpty()) {
result.append(currentPart); // Add the last segment
}
return result;
}
}
ShaderPoker::ShaderPoker(QObject *parent)
: QObject{parent}
{
m_rhi = QRhi::create(QRhi::Null, nullptr);
std::unique_ptr<QSSGRhiContext> rhiContext = std::make_unique<QSSGRhiContext>(m_rhi);
m_renderContext = std::make_shared<QSSGRenderContextInterface>(std::make_unique<QSSGBufferManager>(),
std::make_unique<QSSGRenderer>(),
std::make_shared<QSSGShaderLibraryManager>(),
std::make_unique<QSSGShaderCache>(*rhiContext),
std::make_unique<QSSGCustomMaterialSystem>(),
std::make_unique<QSSGProgramGenerator>(),
std::move(rhiContext));
m_material = new QSSGRenderDefaultMaterial(QSSGRenderGraphObject::Type::DefaultMaterial);
connect(this, &ShaderPoker::shaderIndexChanged, this, &ShaderPoker::fragmentShaderChanged);
connect(this, &ShaderPoker::shaderIndexChanged, this, &ShaderPoker::vertexShaderChanged);
// points to builtin
setShaderProperties(&m_builtinProperties);
generateShader();
}
ShaderPoker::~ShaderPoker()
{
delete m_material;
}
void ShaderPoker::generateShader()
{
const auto &shaderLibraryManager = m_renderContext->shaderLibraryManager();
const auto &shaderCache = m_renderContext->shaderCache();
const auto &shaderProgramGenerator = m_renderContext->shaderProgramGenerator();
const auto &renderer = m_renderContext->renderer();
QSSGRenderModel renderModel;
QSSGRenderSubset renderSubset;
QSSGModelContext modelContext(renderModel, QMatrix4x4(), QMatrix3x3(), QSSGRenderMvpArray());
QSSGShaderLightListView lights;
QSSGRenderableImage *images = nullptr;
QSSGShaderDefaultMaterialKey shaderKey;
QSSGSubsetRenderable renderable(QSSGRenderableObject::Type::DefaultMaterialMeshSubset,
QSSGRenderableObjectFlags(),
QVector3D(),
&*renderer,
renderSubset,
modelContext,
1,
0,
*m_material,
images,
m_shaderProperties->key(),
lights,
false);
QByteArray materialInfoString;
m_currentShaderPipeline = QSSGRendererPrivate::generateRhiShaderPipelineImpl(renderable,
*shaderLibraryManager,
*shaderCache,
*shaderProgramGenerator,
m_shaderProperties->properties(),
m_featureSet,
{ /* augmented shader */ },
materialInfoString);
QSSGShaderCacheKey cacheKey(materialInfoString);
cacheKey.m_features = m_featureSet;
cacheKey.updateHashCode();
const auto theIter = m_generatedShaderCodeCache.constFind(cacheKey);
if (theIter != m_generatedShaderCodeCache.constEnd()) {
auto &shaderCode = theIter.value();
// Add it to the list
m_vertexShader = shaderCode.first;
m_fragmentShader = shaderCode.second;
} else {
// put it in the cache instead
auto shaderCodePair = std::pair<QString, QString>(QString::fromLocal8Bit(shaderProgramGenerator->m_vs.m_finalBuilder.constData()),
QString::fromLocal8Bit(shaderProgramGenerator->m_fs.m_finalBuilder.constData()));
m_generatedShaderCodeCache.insert(cacheKey, shaderCodePair);
m_vertexShader = shaderCodePair.first;
m_fragmentShader = shaderCodePair.second;
}
emit vertexShaderChanged();
emit fragmentShaderChanged();
//qDebug() << test->fragmentStage()->shader().availableShaders();
m_shaderKey = QString::fromLocal8Bit(materialInfoString);
emit shaderKeyChanged();
m_shaderKeysList = splitIgnoringBraces(m_shaderKey);
emit shaderKeysListChanged();
updateAvailableShadersList();
// // m_vertexShader = QString::fromLocal8Bit(shaderProgramGenerator->m_vs.m_finalBuilder.constData());
// m_vertexShader = test->vertexStage()->shader().shader(test->vertexStage()->shader().availableShaders().last()).shader();
// emit vertexShaderChanged();
// // m_fragmentShader = QString::fromLocal8Bit(shaderProgramGenerator->m_fs.m_finalBuilder.constData());
// m_fragmentShader = test->fragmentStage()->shader().shader(test->fragmentStage()->shader().availableShaders().last()).shader();
// emit fragmentShaderChanged();
}
QString ShaderPoker::vertexShader() const
{
if (m_shaderIndex == 0)
return m_vertexShader;
int index = m_shaderIndex - 1;
if (index >= m_availableShadersList.size() || index < 0)
return QString();
return QString::fromLocal8Bit(m_currentShaderPipeline->vertexStage()->shader().shader(m_currentShaderPipeline->vertexStage()->shader().availableShaders().at(index)).shader());
}
QString ShaderPoker::fragmentShader() const
{
if (m_shaderIndex == 0)
return m_fragmentShader;
int index = m_shaderIndex - 1;
if (index >= m_availableShadersList.size() || index < 0)
return QString();
return QString::fromLocal8Bit(m_currentShaderPipeline->fragmentStage()->shader().shader(m_currentShaderPipeline->fragmentStage()->shader().availableShaders().at(index)).shader());
}
QString ShaderPoker::shaderKey() const
{
return m_shaderKey;
}
QStringList ShaderPoker::shaderKeysList() const
{
return m_shaderKeysList;
}
ShaderPoker::MaterialType ShaderPoker::materialType() const
{
return m_materialType;
}
void ShaderPoker::setMaterialType(const MaterialType &newMaterialType)
{
if (m_materialType == newMaterialType)
return;
m_materialType = newMaterialType;
delete m_material;
// This doesn't really matter in practice
if (m_materialType == MaterialType::Default)
m_material = new QSSGRenderDefaultMaterial(QSSGRenderGraphObject::Type::DefaultMaterial);
else if (m_materialType == MaterialType::Principled)
m_material = new QSSGRenderDefaultMaterial(QSSGRenderGraphObject::Type::PrincipledMaterial);
else if (m_materialType == MaterialType::SpecularGlossy)
m_material = new QSSGRenderDefaultMaterial(QSSGRenderGraphObject::Type::SpecularGlossyMaterial);
emit materialTypeChanged();
generateShader();
}
quint32 ShaderPoker::featureBitfield() const
{
return m_featureBitfield;
}
void ShaderPoker::setFeatureBitfield(quint32 newFeatureBitfield)
{
if (m_featureBitfield == newFeatureBitfield)
return;
m_featureBitfield = newFeatureBitfield;
emit featureBitfieldChanged();
m_featureSet.flags = newFeatureBitfield;
generateShader();
}
QStringList ShaderPoker::availableShadersList() const
{
return m_availableShadersList;
}
void ShaderPoker::updateAvailableShadersList()
{
m_availableShadersList.clear();
m_availableShadersList.append("Original");
if (m_currentShaderPipeline) {
const auto &availableShaders = m_currentShaderPipeline->fragmentStage()->shader().availableShaders();
for (const QShaderKey &shaderKey : availableShaders) {
QString source;
QString version;
switch (shaderKey.source()) {
case QShader::SpirvShader:
source = "SPIR-V";
break;
case QShader::GlslShader:
source = "GLSL";
break;
case QShader::HlslShader:
source = "HLSL";
break;
case QShader::DxbcShader:
source = "DXBC";
break;
case QShader::MslShader:
source = "MSL";
break;
case QShader::DxilShader:
source = "DXIL";
break;
case QShader::MetalLibShader:
source = "MetalLib";
break;
case QShader::WgslShader:
source = "WGSL";
break;
}
version = QString::number(shaderKey.sourceVersion().version());
m_availableShadersList.append(source + " Version: " + version);
}
}
emit availableShadersListChanged();
}
int ShaderPoker::shaderIndex() const
{
return m_shaderIndex;
}
void ShaderPoker::setShaderIndex(int newShaderIndex)
{
if (m_shaderIndex == newShaderIndex)
return;
m_shaderIndex = newShaderIndex;
emit shaderIndexChanged();
}
DefaultMaterialShaderProperties *ShaderPoker::shaderProperties() const
{
return m_shaderProperties;
}
void ShaderPoker::setShaderProperties(DefaultMaterialShaderProperties *newShaderProperties)
{
if (m_shaderProperties == newShaderProperties)
return;
if (m_shaderPropertiesDirtyConnection)
disconnect(m_shaderPropertiesDirtyConnection);
if (newShaderProperties == nullptr)
m_shaderProperties = &m_builtinProperties;
else
m_shaderProperties = newShaderProperties;
m_shaderPropertiesDirtyConnection = connect(m_shaderProperties, &DefaultMaterialShaderProperties::shaderKeyUpdated, this, &ShaderPoker::shaderPropertiesDirty);
emit shaderPropertiesChanged();
generateShader();
}
void ShaderPoker::shaderPropertiesDirty()
{
generateShader();
}