1// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QT3DRENDER_RENDER_APISHADERMANAGER_H
5#define QT3DRENDER_RENDER_APISHADERMANAGER_H
6
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists for the convenience
13// of other Qt classes. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18
19#include <Qt3DCore/qnodeid.h>
20#include <Qt3DRender/private/shader_p.h>
21#include <QtCore/QReadLocker>
22#include <Qt3DCore/private/vector_helper_p.h>
23
24QT_BEGIN_NAMESPACE
25
26namespace Qt3DRender {
27
28namespace Render {
29
30class Shader;
31
32template<class APIShader>
33class APIShaderManager
34{
35public:
36 explicit APIShaderManager()
37 {
38 }
39
40 ~APIShaderManager()
41 {
42 }
43
44 std::vector<APIShader *> takeActiveResources() const
45 {
46 QReadLocker lock(&m_readWriteLock);
47
48 std::vector<APIShader *> keysV;
49 const QList<APIShader *> tmp = m_apiShaders.keys();
50
51 std::copy(tmp.cbegin(), tmp.cend(), std::back_insert_iterator(keysV));
52 keysV.insert(keysV.end(),
53 m_abandonedShaders.begin(),
54 m_abandonedShaders.end());
55
56 return keysV;
57 }
58
59 APIShader *lookupResource(Qt3DCore::QNodeId shaderId)
60 {
61 QReadLocker lock(&m_readWriteLock);
62 return m_nodeIdToAPIShader.value(shaderId);
63 }
64
65 // Note: automatically adopts the Shader if it needs to be created
66 APIShader *createOrAdoptExisting(const Shader *shader)
67 {
68 // Try to find if an APIShader that matches shader
69 // already exists
70
71 {
72 QReadLocker readLock(&m_readWriteLock);
73 {
74 const auto end = m_apiShaders.cend();
75 for (auto it = m_apiShaders.cbegin(); it != end; ++it)
76 if (isSameShader(apiShader: it.key(), shaderNode: shader)) {
77 APIShader *apiShader = it.key();
78 // Adopt if needed
79 readLock.unlock();
80 adopt(apiShader, shader);
81 return apiShader;
82 }
83 }
84
85 // Try to find if one of the scheduled for deletion APIShader
86 // could be reused
87 {
88 const auto end = m_abandonedShaders.end();
89 for (auto it = m_abandonedShaders.begin(); it != end; ++it)
90 if (isSameShader(apiShader: *it, shaderNode: shader)) {
91 APIShader *apiShader = *it;
92 // Adopt if needed
93 readLock.unlock();
94 // Remove from list of shaders scheduled for relase
95 m_abandonedShaders.erase(it);
96 adopt(apiShader, shader);
97 return apiShader;
98 }
99 }
100 }
101
102 // If not create one
103 APIShader *apiShader = create();
104 adopt(apiShader, shader);
105 return apiShader;
106 }
107
108 // Should never be called from outside code
109 // but left public to maintain adopt/abandon symmetry
110 void adopt(APIShader *apiShader, const Shader *shader)
111 {
112 QWriteLocker lock(&m_readWriteLock);
113 auto &shaderIds = m_apiShaders[apiShader];
114
115 if (!Qt3DCore::contains(shaderIds, shader->peerId())) {
116 shaderIds.push_back(shader->peerId());
117 m_nodeIdToAPIShader.insert(shader->peerId(), apiShader);
118 }
119 }
120
121 void abandon(APIShader *apiShader, const Shader *shader)
122 {
123 QWriteLocker lock(&m_readWriteLock);
124 APIShader *storedApiShader = m_nodeIdToAPIShader.take(shader->peerId());
125 Q_ASSERT(apiShader != nullptr && apiShader == storedApiShader);
126
127 std::vector<Qt3DCore::QNodeId> &referencedShaderNodes = m_apiShaders[apiShader];
128 referencedShaderNodes.erase(first: std::remove(first: referencedShaderNodes.begin(),
129 last: referencedShaderNodes.end(),
130 value: shader->peerId()),
131 last: referencedShaderNodes.end());
132
133 if (referencedShaderNodes.empty()) {
134 m_abandonedShaders.push_back(apiShader);
135 m_apiShaders.remove(apiShader);
136 }
137 }
138
139 std::vector<APIShader *> takeAbandonned()
140 {
141 QWriteLocker lock(&m_readWriteLock);
142 return Qt3DCore::moveAndClear(m_abandonedShaders);
143 }
144
145 std::vector<APIShader *> takeUpdated()
146 {
147 QWriteLocker lock(&m_readWriteLock);
148 return Qt3DCore::moveAndClear(m_updatedShaders);
149 }
150
151 std::vector<Qt3DCore::QNodeId> shaderIdsForProgram(APIShader *glShader) const
152 {
153 QReadLocker lock(&m_readWriteLock);
154 return m_apiShaders.value(glShader);
155 }
156
157 void purge()
158 {
159 qDeleteAll(takeAbandonned());
160 }
161
162private:
163
164 bool isSameShader(const APIShader *apiShader, const Shader *shaderNode)
165 {
166 const std::vector<QByteArray> &nodeShaderCode = shaderNode->shaderCode();
167 const std::vector<QByteArray> &apiShaderCode = apiShader->shaderCode();
168
169 const size_t s = nodeShaderCode.size();
170
171 Q_ASSERT(s == apiShaderCode.size());
172
173 for (size_t i = 0; i < s; ++i)
174 if (nodeShaderCode[i] != apiShaderCode[i])
175 return false;
176
177 return true;
178 }
179
180 APIShader *create()
181 {
182 APIShader *apiShader = new APIShader();
183 m_updatedShaders.push_back(apiShader);
184 return apiShader;
185 }
186
187
188 QHash<Qt3DCore::QNodeId, APIShader *> m_nodeIdToAPIShader;
189 QHash<APIShader *, std::vector<Qt3DCore::QNodeId>> m_apiShaders;
190
191 std::vector<APIShader *> m_abandonedShaders;
192 std::vector<APIShader *> m_updatedShaders;
193
194 mutable QReadWriteLock m_readWriteLock;
195};
196
197} // Render
198
199} // Qt3DRender
200
201QT_END_NAMESPACE
202
203#endif // QT3DRENDER_RENDER_APISHADERMANAGER_H
204

source code of qt3d/src/render/backend/apishadermanager_p.h