1/****************************************************************************
2**
3** Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "shaderbuilder_p.h"
41
42#include <Qt3DRender/private/qshaderprogrambuilder_p.h>
43#include <Qt3DRender/qshaderprogram.h>
44#include <Qt3DRender/private/qshaderprogram_p.h>
45#include <Qt3DRender/private/qurlhelper_p.h>
46
47#include <QtGui/private/qshaderformat_p.h>
48#include <QtGui/private/qshadergraphloader_p.h>
49#include <QtGui/private/qshadergenerator_p.h>
50#include <QtGui/private/qshadernodesloader_p.h>
51
52#include <QFile>
53#include <QFileInfo>
54#include <QUrl>
55
56static void initResources()
57{
58#ifdef QT_STATIC
59 Q_INIT_RESOURCE(materialsystem);
60#endif
61}
62
63QT_BEGIN_NAMESPACE
64
65class GlobalShaderPrototypes
66{
67public:
68 GlobalShaderPrototypes()
69 {
70 initResources();
71 setPrototypesFile(QStringLiteral(":/prototypes/default.json"));
72 }
73
74 QString prototypesFile() const
75 {
76 return m_fileName;
77 }
78
79 void setPrototypesFile(const QString &fileName)
80 {
81 m_fileName = fileName;
82 load();
83 }
84
85 QHash<QString, QShaderNode> prototypes() const
86 {
87 return m_prototypes;
88 }
89
90private:
91 void load()
92 {
93 QFile file(m_fileName);
94 if (!file.open(QFile::ReadOnly)) {
95 qWarning() << "Couldn't open file:" << m_fileName;
96 return;
97 }
98
99 QShaderNodesLoader loader;
100 loader.setDevice(&file);
101 loader.load();
102 m_prototypes = loader.nodes();
103 }
104
105 QString m_fileName;
106 QHash<QString, QShaderNode> m_prototypes;
107};
108
109Q_GLOBAL_STATIC(GlobalShaderPrototypes, qt3dGlobalShaderPrototypes)
110
111using namespace Qt3DCore;
112
113namespace Qt3DRender {
114namespace Render {
115
116
117namespace {
118
119QShaderProgram::ShaderType toQShaderProgramType(ShaderBuilder::ShaderType type)
120{
121 switch (type) {
122 case ShaderBuilder::ShaderType::Vertex:
123 return QShaderProgram::Vertex;
124 case ShaderBuilder::ShaderType::TessellationControl:
125 return QShaderProgram::TessellationControl;
126 case ShaderBuilder::ShaderType::TessellationEvaluation:
127 return QShaderProgram::TessellationEvaluation;
128 case ShaderBuilder::ShaderType::Geometry:
129 return QShaderProgram::Geometry;
130 case ShaderBuilder::ShaderType::Fragment:
131 return QShaderProgram::Fragment;
132 case ShaderBuilder::ShaderType::Compute:
133 return QShaderProgram::Compute;
134 default:
135 Q_UNREACHABLE();
136 }
137}
138
139} // anonymous
140
141QString ShaderBuilder::getPrototypesFile()
142{
143 return qt3dGlobalShaderPrototypes->prototypesFile();
144}
145
146void ShaderBuilder::setPrototypesFile(const QString &file)
147{
148 qt3dGlobalShaderPrototypes->setPrototypesFile(file);
149}
150
151QStringList ShaderBuilder::getPrototypeNames()
152{
153 return qt3dGlobalShaderPrototypes->prototypes().keys();
154}
155
156ShaderBuilder::ShaderBuilder()
157 : BackendNode(ReadWrite)
158{
159}
160
161ShaderBuilder::~ShaderBuilder()
162{
163}
164
165void ShaderBuilder::cleanup()
166{
167 m_shaderProgramId = Qt3DCore::QNodeId();
168 m_enabledLayers.clear();
169 m_graphs.clear();
170 m_dirtyTypes.clear();
171 QBackendNode::setEnabled(false);
172}
173
174Qt3DCore::QNodeId ShaderBuilder::shaderProgramId() const
175{
176 return m_shaderProgramId;
177}
178
179QStringList ShaderBuilder::enabledLayers() const
180{
181 return m_enabledLayers;
182}
183
184
185void ShaderBuilder::setEnabledLayers(const QStringList &layers)
186{
187 if (m_enabledLayers == layers)
188 return;
189
190 m_enabledLayers = layers;
191
192 for (QHash<ShaderType, QUrl>::const_iterator it = m_graphs.cbegin(); it != m_graphs.cend(); ++it) {
193 if (!it.value().isEmpty())
194 m_dirtyTypes.insert(it.key());
195 }
196}
197
198GraphicsApiFilterData ShaderBuilder::graphicsApi() const
199{
200 return m_graphicsApi;
201}
202
203void ShaderBuilder::setGraphicsApi(const GraphicsApiFilterData &graphicsApi)
204{
205 if (m_graphicsApi == graphicsApi)
206 return;
207
208 m_graphicsApi = graphicsApi;
209 for (QHash<ShaderType, QUrl>::const_iterator it = m_graphs.cbegin(); it != m_graphs.cend(); ++it) {
210 if (!it.value().isEmpty())
211 m_dirtyTypes.insert(it.key());
212 }
213}
214
215QUrl ShaderBuilder::shaderGraph(ShaderBuilder::ShaderType type) const
216{
217 return m_graphs.value(type);
218}
219
220void ShaderBuilder::setShaderGraph(ShaderBuilder::ShaderType type, const QUrl &url)
221{
222 if (url != m_graphs.value(type)) {
223 m_graphs.insert(type, url);
224 m_dirtyTypes.insert(type);
225 }
226}
227
228QByteArray ShaderBuilder::shaderCode(ShaderBuilder::ShaderType type) const
229{
230 return m_codes.value(type);
231}
232
233bool ShaderBuilder::isShaderCodeDirty(ShaderBuilder::ShaderType type) const
234{
235 return m_dirtyTypes.contains(type);
236}
237
238void ShaderBuilder::generateCode(ShaderBuilder::ShaderType type)
239{
240 const auto graphPath = QUrlHelper::urlToLocalFileOrQrc(shaderGraph(type));
241 QFile file(graphPath);
242 if (!file.open(QFile::ReadOnly)) {
243 qWarning() << "Couldn't open file:" << graphPath;
244 return;
245 }
246
247 auto graphLoader = QShaderGraphLoader();
248 graphLoader.setPrototypes(qt3dGlobalShaderPrototypes->prototypes());
249 graphLoader.setDevice(&file);
250 graphLoader.load();
251
252 if (graphLoader.status() == QShaderGraphLoader::Error)
253 return;
254
255 const auto graph = graphLoader.graph();
256
257 auto format = QShaderFormat();
258 format.setApi(m_graphicsApi.m_api == QGraphicsApiFilter::OpenGLES ? QShaderFormat::OpenGLES
259 : m_graphicsApi.m_profile == QGraphicsApiFilter::CoreProfile ? QShaderFormat::OpenGLCoreProfile
260 : m_graphicsApi.m_profile == QGraphicsApiFilter::CompatibilityProfile ? QShaderFormat::OpenGLCompatibilityProfile
261 : QShaderFormat::OpenGLNoProfile);
262 format.setVersion(QVersionNumber(m_graphicsApi.m_major, m_graphicsApi.m_minor));
263 format.setExtensions(m_graphicsApi.m_extensions);
264 format.setVendor(m_graphicsApi.m_vendor);
265
266 auto generator = QShaderGenerator();
267 generator.format = format;
268 generator.graph = graph;
269
270 const auto code = generator.createShaderCode(m_enabledLayers);
271 m_codes.insert(type, QShaderProgramPrivate::deincludify(code, graphPath + QStringLiteral(".glsl")));
272 m_dirtyTypes.remove(type);
273
274 // Send notification to the frontend
275 Qt3DCore::QPropertyUpdatedChangePtr propertyChange = Qt3DCore::QPropertyUpdatedChangePtr::create(peerId());
276 propertyChange->setDeliveryFlags(Qt3DCore::QSceneChange::DeliverToAll);
277 propertyChange->setPropertyName("generatedShaderCode");
278 propertyChange->setValue(QVariant::fromValue(qMakePair(int(toQShaderProgramType(type)), m_codes.value(type))));
279 notifyObservers(propertyChange);
280}
281
282void ShaderBuilder::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
283{
284 if (e->type() == PropertyUpdated) {
285 QPropertyUpdatedChangePtr propertyChange = e.staticCast<QPropertyUpdatedChange>();
286 QVariant propertyValue = propertyChange->value();
287
288 if (propertyChange->propertyName() == QByteArrayLiteral("shaderProgram"))
289 m_shaderProgramId = propertyValue.value<Qt3DCore::QNodeId>();
290 else if (propertyChange->propertyName() == QByteArrayLiteral("enabledLayers"))
291 setEnabledLayers(propertyValue.toStringList());
292 else if (propertyChange->propertyName() == QByteArrayLiteral("vertexShaderGraph"))
293 setShaderGraph(Vertex, propertyValue.toUrl());
294 else if (propertyChange->propertyName() == QByteArrayLiteral("tessellationControlShaderGraph"))
295 setShaderGraph(TessellationControl, propertyValue.toUrl());
296 else if (propertyChange->propertyName() == QByteArrayLiteral("tessellationEvaluationShaderGraph"))
297 setShaderGraph(TessellationEvaluation, propertyValue.toUrl());
298 else if (propertyChange->propertyName() == QByteArrayLiteral("geometryShaderGraph"))
299 setShaderGraph(Geometry, propertyValue.toUrl());
300 else if (propertyChange->propertyName() == QByteArrayLiteral("fragmentShaderGraph"))
301 setShaderGraph(Fragment, propertyValue.toUrl());
302 else if (propertyChange->propertyName() == QByteArrayLiteral("computeShaderGraph"))
303 setShaderGraph(Compute, propertyValue.toUrl());
304
305 markDirty(AbstractRenderer::ShadersDirty);
306 }
307 BackendNode::sceneChangeEvent(e);
308}
309
310void ShaderBuilder::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
311{
312 const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<QShaderProgramBuilderData>>(change);
313 const auto &data = typedChange->data;
314
315 m_shaderProgramId = data.shaderProgramId;
316 m_enabledLayers = data.enabledLayers;
317 setShaderGraph(Vertex, data.vertexShaderGraph);
318 setShaderGraph(TessellationControl, data.tessellationControlShaderGraph);
319 setShaderGraph(TessellationEvaluation, data.tessellationEvaluationShaderGraph);
320 setShaderGraph(Geometry, data.geometryShaderGraph);
321 setShaderGraph(Fragment, data.fragmentShaderGraph);
322 setShaderGraph(Compute, data.computeShaderGraph);
323}
324
325} // namespace Render
326} // namespace Qt3DRender
327
328QT_END_NAMESPACE
329