aboutsummaryrefslogtreecommitdiffstats
path: root/src/runtimerender/graphobjects/qssgrendereffect.cpp
blob: 9199f82b2204b9251873b6849fc6ad4b5bc973f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include <QtQuick3DRuntimeRender/private/qssgrenderer_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendereffect_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercommands_p.h>
#include "../qssgrendercontextcore.h"
#include "../rendererimpl/qssglayerrenderdata_p.h"

#include <QtGui/QVector2D>
#include <QtGui/QVector3D>

QT_BEGIN_NAMESPACE

QSSGRenderEffect::QSSGRenderEffect() : QSSGRenderGraphObject(Type::Effect) {}

QSSGRenderEffect::~QSSGRenderEffect()
{
    resetCommands();
}

void QSSGRenderEffect::markDirty()
{
    flags |= FlagT(Flags::Dirty);
}

void QSSGRenderEffect::clearDirty()
{
    flags &= ~FlagT(Flags::Dirty);
}

// Suffix snippets added to the end of the shader strings. These are appended
// after processing so it must be valid GLSL as-is, no more magic keywords.

static const char *effect_vertex_main_pre =
        "void main()\n"
        "{\n"
        "    qt_inputUV = attr_uv;\n"
        "    qt_textureUV = qt_effectTextureMapUV(attr_uv);\n"
        "    vec4 qt_vertPosition = vec4(attr_pos, 1.0);\n"
        "#if QSHADER_VIEW_COUNT >= 2\n"
        "    qt_viewIndex = gl_ViewIndex;\n"
        "#else\n"
        "    qt_viewIndex = 0;\n"
        "#endif\n"
        "    qt_customMain(qt_vertPosition.xyz);\n";

static const char *effect_vertex_main_position =
        "    gl_Position = qt_modelViewProjection * qt_vertPosition;\n";

static const char *effect_vertex_main_post =
        "}\n";

static const char *effect_fragment_main =
        "void main()\n"
        "{\n"
        "    qt_customMain();\n"
        "}\n";

static const char *effect_fragment_main_with_tonemapping =
        "#include \"tonemapping.glsllib\"\n"
        "void main()\n"
        "{\n"
        "    qt_customMain();\n"
        "    fragOutput = qt_tonemap(fragOutput);\n"
        "}\n";

void QSSGRenderEffect::finalizeShaders(const QSSGRenderLayer &layer, QSSGRenderContextInterface *renderContext)
{
    Q_UNUSED(layer);

    // this is called on every frame, so do nothing if there are no changes
    if (!shaderPrepData.valid)
        return;

    QRhi *rhi = renderContext->rhiContext()->rhi();

    for (int i = 0, ie = shaderPrepData.passes.size(); i != ie; ++i) {
        const ShaderPrepPassData &pass(shaderPrepData.passes[i]);

        // The fragment shader of the last pass of the last effect may need to
        // perform the built-in tonemapping.
        const bool isLastEffect = m_nextEffect == nullptr;
        const bool isLastPass = i == ie - 1;
        const bool shouldTonemapIfEnabled = isLastEffect && isLastPass;

        QSSGShaderFeatures features;
        QByteArray completeVertexShader;
        QByteArray completeFragmentShader;
        QByteArray sourceCodeForHash;

        const bool multiview = layer.viewCount >= 2;
        const int srcIdx = multiview ? QSSGRenderCustomMaterial::MultiViewShaderPathKeyIndex : QSSGRenderCustomMaterial::RegularShaderPathKeyIndex;

        if (!pass.vertexShaderCode[srcIdx].isEmpty()) {
            QByteArray code = pass.vertexShaderCode[srcIdx];
            // add the real main(), with or without assigning gl_Position at the end
            code.append(effect_vertex_main_pre);
            if (!pass.vertexMetaData[srcIdx].flags.testFlag(QSSGCustomShaderMetaData::OverridesPosition))
                code.append(effect_vertex_main_position);
            code.append(effect_vertex_main_post);
            completeVertexShader = code;
            sourceCodeForHash += code;
        }

        if (!pass.fragmentShaderCode[srcIdx].isEmpty()) {
            QByteArray code = pass.fragmentShaderCode[srcIdx];
            if (shouldTonemapIfEnabled)
                code.append(effect_fragment_main_with_tonemapping);
            else
                code.append(effect_fragment_main);
            completeFragmentShader = code;
            sourceCodeForHash += code;
        }

        QByteArray shaderPathKey = pass.shaderPathKeyPrefix;
        shaderPathKey.append(':' + QCryptographicHash::hash(sourceCodeForHash, QCryptographicHash::Algorithm::Sha1).toHex());

        // QSSGRhiEffectSystem will vary the vertex shader code based on this
        // flag from the QRhi. It is therefore important to capture this in the
        // cache key as well.
        shaderPathKey.append(rhi->isYUpInFramebuffer() ? QByteArrayLiteral(":1") : QByteArrayLiteral(":0"));

        if (shouldTonemapIfEnabled) {
            // This does not always mean there will be tonemapping: if the mode
            // is TonemapModeNone, then no extra feature defines are set, and
            // so qt_tonemap() in the shader will not alter the color.
            const QSSGRenderLayer::TonemapMode tonemapMode = layer.tonemapMode;
            shaderPathKey.append(':' + QByteArray::number(int(tonemapMode)));
            QSSGLayerRenderData::setTonemapFeatures(features, tonemapMode);
        }

        shaderPathKey.append(multiview ? QByteArrayLiteral(":1") : QByteArrayLiteral(":0"));

        // Now that the final shaderPathKey is known, store the source and
        // related data; it will be retrieved later by the QSSGRhiEffectSystem.
        if (!completeVertexShader.isEmpty()) {
            renderContext->shaderLibraryManager()->setShaderSource(shaderPathKey,
                                                                   QSSGShaderCache::ShaderType::Vertex,
                                                                   completeVertexShader,
                                                                   pass.vertexMetaData[srcIdx]);
        }
        if (!completeFragmentShader.isEmpty()) {
            QSSGCustomShaderMetaData metaData = pass.fragmentMetaData[srcIdx];
            metaData.features = features;
            renderContext->shaderLibraryManager()->setShaderSource(shaderPathKey,
                                                                   QSSGShaderCache::ShaderType::Fragment,
                                                                   completeFragmentShader,
                                                                   metaData);
        }

        // and update the command
        delete commands[pass.bindShaderCmdIndex].command;
        commands[pass.bindShaderCmdIndex] = { new QSSGBindShader(shaderPathKey), true };
    }

    shaderPrepData.valid = false;
}

void QSSGRenderEffect::resetCommands()
{
    for (const Command &cmd : commands) {
        if (cmd.own)
            delete cmd.command;
    }
    commands.clear();
    shaderPrepData.passes.clear();
}

QT_END_NAMESPACE