1 | /* |
2 | * SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl> |
3 | * |
4 | * SPDX-License-Identifier: LGPL-2.0-or-later |
5 | */ |
6 | |
7 | #include "shadermaterial.h" |
8 | |
9 | #include <QSGTexture> |
10 | #include <QVariant> |
11 | |
12 | #include "uniformdatastream.h" |
13 | |
14 | using namespace Qt::StringLiterals; |
15 | |
16 | ShaderMaterial::ShaderMaterial(const QString &name) |
17 | : m_name(name) |
18 | { |
19 | m_type = typeForName(name); |
20 | setFlag(flags: QSGMaterial::Blending, on: true); |
21 | } |
22 | |
23 | ShaderMaterial::ShaderMaterial(QSGMaterialType *type) |
24 | : m_type(type) |
25 | { |
26 | m_name = nameForType(type); |
27 | setFlag(flags: QSGMaterial::Blending, on: true); |
28 | } |
29 | |
30 | QString ShaderMaterial::name() const |
31 | { |
32 | return m_name; |
33 | } |
34 | |
35 | QSGMaterialShader *ShaderMaterial::createShader(QSGRendererInterface::RenderMode) const |
36 | { |
37 | return new ShaderMaterialShader{m_name}; |
38 | } |
39 | |
40 | QSGMaterialType *ShaderMaterial::type() const |
41 | { |
42 | return m_type; |
43 | } |
44 | |
45 | int ShaderMaterial::compare(const QSGMaterial *other) const |
46 | { |
47 | auto material = static_cast<const ShaderMaterial *>(other); |
48 | if (m_uniformData == material->m_uniformData && m_textures == material->m_textures) { |
49 | return 0; |
50 | } |
51 | |
52 | return QSGMaterial::compare(other); |
53 | } |
54 | |
55 | void ShaderMaterial::setUniformBufferSize(qsizetype size) |
56 | { |
57 | if (size == m_uniformData.size()) { |
58 | return; |
59 | } |
60 | |
61 | m_uniformData = QByteArray{size, '\0'}; |
62 | } |
63 | |
64 | std::span<char> ShaderMaterial::uniformData() |
65 | { |
66 | return std::span(m_uniformData.data(), m_uniformData.size()); |
67 | } |
68 | |
69 | QSGTexture *ShaderMaterial::texture(int binding) |
70 | { |
71 | return m_textures.value(key: binding, defaultValue: nullptr); |
72 | } |
73 | |
74 | void ShaderMaterial::setTexture(int binding, QSGTexture *texture) |
75 | { |
76 | m_textures[binding] = texture; |
77 | } |
78 | |
79 | QString ShaderMaterial::nameForType(QSGMaterialType *type) |
80 | { |
81 | return s_materialTypes.key(value: type, defaultKey: QString{}); |
82 | } |
83 | |
84 | QSGMaterialType *ShaderMaterial::typeForName(const QString &name) |
85 | { |
86 | if (s_materialTypes.contains(key: name)) { |
87 | return s_materialTypes.value(key: name); |
88 | } else { |
89 | auto type = new QSGMaterialType{}; |
90 | s_materialTypes.insert(key: name, value: type); |
91 | return type; |
92 | } |
93 | } |
94 | |
95 | ShaderMaterialShader::ShaderMaterialShader(const QString &shaderName) |
96 | { |
97 | static const auto shaderRoot = QStringLiteral(":/qt/qml/org/kde/kirigami/primitives/shaders/" ); |
98 | |
99 | setShaderFileName(stage: Stage::VertexStage, filename: shaderRoot + shaderName + u".vert.qsb" ); |
100 | setShaderFileName(stage: Stage::FragmentStage, filename: shaderRoot + shaderName + u".frag.qsb" ); |
101 | } |
102 | |
103 | bool ShaderMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) |
104 | { |
105 | bool changed = false; |
106 | |
107 | auto data = state.uniformData()->data(); |
108 | auto remainingSize = std::size_t(state.uniformData()->size()); |
109 | |
110 | if (state.isMatrixDirty()) { |
111 | auto matrix = state.combinedMatrix(); |
112 | memcpy(dest: data, src: matrix.data(), n: sizeof(float) * 16); |
113 | changed = true; |
114 | } |
115 | data += sizeof(float) * 16; |
116 | remainingSize -= sizeof(float) * 16; |
117 | |
118 | if (state.isOpacityDirty()) { |
119 | auto opacity = state.opacity(); |
120 | memcpy(dest: data, src: &opacity, n: sizeof(float)); |
121 | changed = true; |
122 | } |
123 | |
124 | data += sizeof(float); |
125 | remainingSize -= sizeof(float); |
126 | |
127 | if (!oldMaterial || newMaterial->compare(other: oldMaterial) != 0) { |
128 | const auto uniformData = static_cast<ShaderMaterial *>(newMaterial)->uniformData(); |
129 | memcpy(dest: data, src: uniformData.data() + sizeof(float) * 17, n: remainingSize); |
130 | changed = true; |
131 | } |
132 | |
133 | return changed; |
134 | } |
135 | |
136 | void ShaderMaterialShader::updateSampledImage(QSGMaterialShader::RenderState &state, |
137 | int binding, |
138 | QSGTexture **texture, |
139 | QSGMaterial *newMaterial, |
140 | QSGMaterial *oldMaterial) |
141 | { |
142 | Q_UNUSED(oldMaterial); |
143 | |
144 | auto material = static_cast<ShaderMaterial *>(newMaterial); |
145 | auto source = material->texture(binding); |
146 | if (source) { |
147 | source->setFiltering(QSGTexture::Filtering::Linear); |
148 | source->commitTextureOperations(rhi: state.rhi(), resourceUpdates: state.resourceUpdateBatch()); |
149 | *texture = source; |
150 | } else { |
151 | *texture = nullptr; |
152 | } |
153 | } |
154 | |