1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "genshaders.h"
5
6#include <QtCore/qdir.h>
7
8#include <QtQml/qqmllist.h>
9
10#include <QtQuick3D/private/qquick3dsceneenvironment_p.h>
11#include <QtQuick3D/private/qquick3dprincipledmaterial_p.h>
12#include <QtQuick3D/private/qquick3dviewport_p.h>
13#include <QtQuick3D/private/qquick3dscenerenderer_p.h>
14#include <QtQuick3D/private/qquick3dscenemanager_p.h>
15#include <QtQuick3D/private/qquick3dperspectivecamera_p.h>
16
17// Lights
18#include <QtQuick3D/private/qquick3dspotlight_p.h>
19#include <QtQuick3D/private/qquick3ddirectionallight_p.h>
20#include <QtQuick3D/private/qquick3dpointlight_p.h>
21
22#include <QtQuick3DUtils/private/qqsbcollection_p.h>
23
24#include <private/qssgrenderer_p.h>
25#include <private/qssglayerrenderdata_p.h>
26
27#include <QtQuick3DRuntimeRender/private/qssgrhieffectsystem_p.h>
28#include <QtQuick3DRuntimeRender/private/qssgrhicustommaterialsystem_p.h>
29#include <QtQuick3DRuntimeRender/private/qssgdebugdrawsystem_p.h>
30
31#include <rhi/qshaderbaker.h>
32
33static inline void qDryRunPrintQsbcAdd(const QByteArray &id)
34{
35 printf(format: "Shader pipeline generated for (dry run):\n %s\n\n", qPrintable(id));
36}
37
38static void initBaker(QShaderBaker *baker, QRhi *rhi)
39{
40 Q_UNUSED(rhi); // that's a Null-backed rhi here anyways
41 QVector<QShaderBaker::GeneratedShader> outputs;
42 // TODO: For simplicity we're just going to add all off these for now.
43 outputs.append(t: { QShader::SpirvShader, QShaderVersion(100) }); // Vulkan 1.0
44 outputs.append(t: { QShader::HlslShader, QShaderVersion(50) }); // Shader Model 5.0
45 outputs.append(t: { QShader::MslShader, QShaderVersion(12) }); // Metal 1.2
46 outputs.append(t: { QShader::GlslShader, QShaderVersion(300, QShaderVersion::GlslEs) }); // GLES 3.0+
47 outputs.append(t: { QShader::GlslShader, QShaderVersion(140) }); // OpenGL 3.1+
48
49 baker->setGeneratedShaders(outputs);
50 baker->setGeneratedShaderVariants({ QShader::StandardShader });
51}
52
53GenShaders::GenShaders()
54{
55 sceneManager = new QQuick3DSceneManager;
56
57 rhi = QRhi::create(impl: QRhi::Null, params: nullptr);
58 QRhiCommandBuffer *cb;
59 rhi->beginOffscreenFrame(cb: &cb);
60
61 std::unique_ptr<QSSGRhiContext> rhiContext = std::make_unique<QSSGRhiContext>(args&: rhi);
62 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(q: rhiContext.get());
63 rhiCtxD->setCommandBuffer(cb);
64
65 renderContext = std::make_shared<QSSGRenderContextInterface>(args: std::make_unique<QSSGBufferManager>(),
66 args: std::make_unique<QSSGRenderer>(),
67 args: std::make_shared<QSSGShaderLibraryManager>(),
68 args: std::make_unique<QSSGShaderCache>(args&: *rhiContext, args: &initBaker),
69 args: std::make_unique<QSSGCustomMaterialSystem>(),
70 args: std::make_unique<QSSGProgramGenerator>(),
71 args: std::move(rhiContext));
72 wa = new QQuick3DWindowAttachment(nullptr);
73 wa->setRci(renderContext);
74 sceneManager->wattached = wa;
75}
76
77GenShaders::~GenShaders() = default;
78
79bool GenShaders::process(const MaterialParser::SceneData &sceneData,
80 QVector<QString> &qsbcFiles,
81 const QDir &outDir,
82 bool generateMultipleLights,
83 bool dryRun)
84{
85 Q_UNUSED(generateMultipleLights);
86
87 const QString resourceFolderRelative = QSSGShaderCache::resourceFolder().mid(index: 2);
88 if (!dryRun && !outDir.exists(name: resourceFolderRelative)) {
89 if (!outDir.mkpath(dirPath: resourceFolderRelative)) {
90 qDebug(msg: "Unable to create folder: %s", qPrintable(outDir.path() + QDir::separator() + resourceFolderRelative));
91 return false;
92 }
93 }
94
95 const QString outputFolder = outDir.canonicalPath() + QDir::separator() + resourceFolderRelative;
96
97 QSSGRenderLayer layer;
98 renderContext->renderer()->setViewport(QRect(QPoint(), QSize(888,666)));
99 const auto &renderer = renderContext->renderer();
100 QSSGLayerRenderData layerData(layer, *renderer);
101
102 const auto &shaderLibraryManager = renderContext->shaderLibraryManager();
103 const auto &shaderCache = renderContext->shaderCache();
104 const auto &shaderProgramGenerator = renderContext->shaderProgramGenerator();
105
106 bool aaIsDirty = false;
107 bool temporalIsDirty = false;
108
109 QQuick3DViewport *view3D = sceneData.viewport;
110 Q_ASSERT(view3D);
111
112 QVector<QSSGRenderGraphObject *> nodes;
113
114 if (!view3D->camera()) {
115 auto camera = new QQuick3DPerspectiveCamera();
116 auto node = QQuick3DObjectPrivate::updateSpatialNode(o: camera, n: nullptr);
117 QQuick3DObjectPrivate::get(item: camera)->spatialNode = node;
118 nodes.append(t: node);
119 view3D->setCamera(camera);
120 }
121
122 // Realize resources
123 // Textures
124 const auto &textures = sceneData.textures;
125 for (const auto &tex : textures) {
126 auto node = QQuick3DObjectPrivate::updateSpatialNode(o: tex, n: nullptr);
127 auto obj = QQuick3DObjectPrivate::get(item: tex);
128 obj->spatialNode = node;
129 nodes.append(t: node);
130 }
131
132 // Free Materials (see also the model section)
133 const auto &materials = sceneData.materials;
134 for (const auto &mat : materials) {
135 auto obj = QQuick3DObjectPrivate::get(item: mat);
136 obj->sceneManager = sceneManager;
137 auto node = QQuick3DObjectPrivate::updateSpatialNode(o: mat, n: nullptr);
138 obj->spatialNode = node;
139 nodes.append(t: node);
140 }
141
142 bool shadowPerspectivePass = false;
143 bool shadowOrthoPass = false;
144
145 // Lights
146 const auto &lights = sceneData.lights;
147 for (const auto &light : lights) {
148 if (auto node = QQuick3DObjectPrivate::updateSpatialNode(o: light, n: nullptr)) {
149 nodes.append(t: node);
150 layer.addChild(inChild&: static_cast<QSSGRenderNode &>(*node));
151 const auto &lightNode = static_cast<const QSSGRenderLight &>(*node);
152 if (lightNode.type == QSSGRenderLight::Type::DirectionalLight)
153 shadowOrthoPass |= true;
154 else
155 shadowPerspectivePass |= true;
156 }
157 }
158
159 // NOTE: Model.castsShadows; Model.receivesShadows; variants needs to be added for runtime support
160 const auto &models = sceneData.models;
161 for (const auto &model : models) {
162 auto materialList = model->materials();
163 for (int i = 0, e = materialList.count(&materialList); i != e; ++i) {
164 auto mat = materialList.at(&materialList, i);
165 auto obj = QQuick3DObjectPrivate::get(item: mat);
166 obj->sceneManager = sceneManager;
167 QSSGRenderGraphObject *node = nullptr;
168 if (obj->type == QQuick3DObjectPrivate::Type::CustomMaterial) {
169 auto customMatNode = new QSSGRenderCustomMaterial;
170 customMatNode->incompleteBuildTimeObject = true;
171 node = QQuick3DObjectPrivate::updateSpatialNode(o: mat, n: customMatNode);
172 customMatNode->incompleteBuildTimeObject = false;
173 } else {
174 node = QQuick3DObjectPrivate::updateSpatialNode(o: mat, n: nullptr);
175 }
176 QQuick3DObjectPrivate::get(item: mat)->spatialNode = node;
177 nodes.append(t: node);
178 }
179 if (auto instanceList = qobject_cast<QQuick3DInstanceList *>(object: model->instancing())) {
180 auto obj = QQuick3DObjectPrivate::get(item: instanceList);
181 auto node = QQuick3DObjectPrivate::updateSpatialNode(o: instanceList, n: nullptr);
182 obj->spatialNode = node;
183 nodes.append(t: node);
184 }
185
186 auto node = QQuick3DObjectPrivate::updateSpatialNode(o: model, n: nullptr);
187 QQuick3DObjectPrivate::get(item: model)->spatialNode = node;
188 nodes.append(t: node);
189 }
190
191 QQuick3DRenderLayerHelpers::updateLayerNodeHelper(view3D: *view3D, rci: renderContext, layerNode&: layer, aaIsDirty, temporalIsDirty);
192
193 const QString outCollectionFile = outputFolder + QString::fromLatin1(ba: QSSGShaderCache::shaderCollectionFile());
194 QQsbIODeviceCollection qsbc(outCollectionFile);
195 if (!dryRun && !qsbc.map(mode: QQsbIODeviceCollection::Write))
196 return false;
197
198 QByteArray shaderString;
199 const auto generateShaderForModel = [&](QSSGRenderModel &model) {
200 layerData.resetForFrame();
201 layer.addChild(inChild&: model);
202 layerData.prepareForRender();
203
204 const auto &features = layerData.getShaderFeatures();
205
206 const auto &propertyTable = layerData.getDefaultMaterialPropertyTable();
207
208 const auto &opaqueObjects = layerData.getSortedOpaqueRenderableObjects(camera: *layerData.renderedCameras[0]);
209 const auto &transparentObjects = layerData.getSortedTransparentRenderableObjects(camera: *layerData.renderedCameras[0]);
210
211 QSSGRenderableObject *renderable = nullptr;
212 if (!opaqueObjects.isEmpty())
213 renderable = opaqueObjects[0].obj;
214 else if (!transparentObjects.isEmpty())
215 renderable = transparentObjects[0].obj;
216
217 auto generateShader = [&](const QSSGShaderFeatures &features) {
218 if ((renderable->type == QSSGSubsetRenderable::Type::DefaultMaterialMeshSubset)) {
219 auto shaderPipeline = QSSGRendererPrivate::generateRhiShaderPipelineImpl(renderable&: *static_cast<QSSGSubsetRenderable *>(renderable), shaderLibraryManager&: *shaderLibraryManager, shaderCache&: *shaderCache, shaderProgramGenerator&: *shaderProgramGenerator, shaderKeyProperties: propertyTable, featureSet: features, shaderString);
220 if (shaderPipeline != nullptr) {
221 const auto qsbcFeatureList = QQsbCollection::toFeatureSet(ssgFeatureSet: features);
222 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(materialKey: shaderString, featureSet: qsbcFeatureList);
223 const auto vertexStage = shaderPipeline->vertexStage();
224 const auto fragmentStage = shaderPipeline->fragmentStage();
225 if (vertexStage && fragmentStage) {
226 if (dryRun)
227 qDryRunPrintQsbcAdd(id: shaderString);
228 else
229 qsbc.addEntry(key: qsbcKey, entryDesc: { .materialKey: shaderString, .featureSet: qsbcFeatureList, .vertShader: vertexStage->shader(), .fragShader: fragmentStage->shader() });
230 }
231 }
232 } else if ((renderable->type == QSSGSubsetRenderable::Type::CustomMaterialMeshSubset)) {
233 Q_ASSERT(!layerData.renderedCameras.isEmpty());
234 QSSGSubsetRenderable &cmr(static_cast<QSSGSubsetRenderable &>(*renderable));
235 auto pipelineState = layerData.getPipelineState();
236 const auto &cms = renderContext->customMaterialSystem();
237 const auto &material = static_cast<const QSSGRenderCustomMaterial &>(cmr.getMaterial());
238 auto shaderPipeline = cms->shadersForCustomMaterial(ps: &pipelineState,
239 material,
240 renderable&: cmr,
241 defaultMaterialShaderKeyProperties: propertyTable,
242 featureSet: features);
243
244 if (shaderPipeline) {
245 shaderString = material.m_shaderPathKey[QSSGRenderCustomMaterial::RegularShaderPathKeyIndex];
246 const auto qsbcFeatureList = QQsbCollection::toFeatureSet(ssgFeatureSet: features);
247 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(materialKey: shaderString, featureSet: qsbcFeatureList);
248 const auto vertexStage = shaderPipeline->vertexStage();
249 const auto fragmentStage = shaderPipeline->fragmentStage();
250 if (vertexStage && fragmentStage) {
251 if (dryRun)
252 qDryRunPrintQsbcAdd(id: shaderString);
253 else
254 qsbc.addEntry(key: qsbcKey, entryDesc: { .materialKey: shaderString, .featureSet: qsbcFeatureList, .vertShader: vertexStage->shader(), .fragShader: fragmentStage->shader() });
255 }
256 }
257 }
258 };
259
260 if (renderable) {
261 generateShader(features);
262
263 QSSGShaderFeatures depthPassFeatures;
264 depthPassFeatures.set(feature: QSSGShaderFeatures::Feature::DepthPass, val: true);
265 generateShader(depthPassFeatures);
266
267 if (shadowPerspectivePass) {
268 QSSGShaderFeatures shadowPassFeatures;
269 shadowPassFeatures.set(feature: QSSGShaderFeatures::Feature::PerspectiveShadowPass, val: true);
270 generateShader(shadowPassFeatures);
271 }
272
273 if (shadowOrthoPass) {
274 QSSGShaderFeatures shadowPassFeatures;
275 shadowPassFeatures.set(feature: QSSGShaderFeatures::Feature::OrthoShadowPass, val: true);
276 generateShader(shadowPassFeatures);
277 }
278 }
279 layer.removeChild(inChild&: model);
280 };
281
282 for (const auto &model : models)
283 generateShaderForModel(static_cast<QSSGRenderModel &>(*QQuick3DObjectPrivate::get(item: model)->spatialNode));
284
285 // Let's generate some shaders for the "free" materials as well.
286 QSSGRenderModel model; // dummy
287 model.meshPath = QSSGRenderPath("#Cube");
288 for (const auto &mat : materials) {
289 model.materials = { QQuick3DObjectPrivate::get(item: mat)->spatialNode };
290 generateShaderForModel(model);
291 }
292
293 // Now generate the shaders for the effects
294 const auto generateEffectShader = [&](QQuick3DEffect &effect) {
295 auto obj = QQuick3DObjectPrivate::get(item: &effect);
296 obj->sceneManager = sceneManager;
297 QSSGRenderEffect *renderEffect = new QSSGRenderEffect;
298 renderEffect->incompleteBuildTimeObject = true;
299 if (auto ret = QQuick3DObjectPrivate::updateSpatialNode(o: &effect, n: renderEffect))
300 Q_ASSERT(ret == renderEffect);
301 renderEffect->incompleteBuildTimeObject = false;
302 obj->spatialNode = renderEffect;
303 nodes.append(t: renderEffect);
304
305 const auto &commands = renderEffect->commands;
306 for (const QSSGRenderEffect::Command &c : commands) {
307 QSSGCommand *command = c.command;
308 if (command->m_type == CommandType::BindShader) {
309 auto bindShaderCommand = static_cast<const QSSGBindShader &>(*command);
310 for (const auto isYUpInFramebuffer : { true, false }) { // Generate effects for both up-directions.
311 const auto shaderPipeline = QSSGRhiEffectSystem::buildShaderForEffect(inCmd: bindShaderCommand,
312 generator&: *shaderProgramGenerator,
313 shaderLib&: *shaderLibraryManager,
314 shaderCache&: *shaderCache,
315 isYUpInFramebuffer,
316 viewCount: 1); // no multiview support here yet
317 if (shaderPipeline) {
318 const auto &key = bindShaderCommand.m_shaderPathKey;
319 const QSSGShaderFeatures features = shaderLibraryManager->getShaderMetaData(inShaderPathKey: key, type: QSSGShaderCache::ShaderType::Fragment).features;
320 const auto qsbcFeatureList = QQsbCollection::toFeatureSet(ssgFeatureSet: features);
321 QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(materialKey: key, featureSet: qsbcFeatureList);
322 const auto vertexStage = shaderPipeline->vertexStage();
323 const auto fragmentStage = shaderPipeline->fragmentStage();
324 if (vertexStage && fragmentStage) {
325 if (dryRun)
326 qDryRunPrintQsbcAdd(id: key);
327 else
328 qsbc.addEntry(key: qsbcKey, entryDesc: { .materialKey: key, .featureSet: qsbcFeatureList, .vertShader: vertexStage->shader(), .fragShader: fragmentStage->shader() });
329 }
330 }
331 }
332 }
333 }
334 };
335
336 // Effects
337 if (sceneData.viewport && sceneData.viewport->environment()) {
338 auto &env = *sceneData.viewport->environment();
339 auto effects = env.effects();
340 const auto effectCount = effects.count(&effects);
341 for (int i = 0; i < effectCount; ++i) {
342 auto effect = effects.at(&effects, i);
343 generateEffectShader(*effect);
344 }
345 }
346
347 // Free Effects
348 for (const auto &effect : std::as_const(t: sceneData.effects))
349 generateEffectShader(*effect);
350
351 if (!qsbc.availableEntries().isEmpty())
352 qsbcFiles.push_back(t: resourceFolderRelative + QDir::separator() + QString::fromLatin1(ba: QSSGShaderCache::shaderCollectionFile()));
353 qsbc.unmap();
354
355 auto &children = layer.children;
356 for (auto it = children.begin(), end = children.end(); it != end;)
357 children.remove(inObj&: *it++);
358
359 qDeleteAll(c: nodes);
360
361 return true;
362}
363

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtquick3d/tools/shadergen/genshaders.cpp