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 | |
24 | QT_BEGIN_NAMESPACE |
25 | |
26 | namespace Qt3DRender { |
27 | |
28 | namespace Render { |
29 | |
30 | class Shader; |
31 | |
32 | template<class APIShader> |
33 | class APIShaderManager |
34 | { |
35 | public: |
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 | |
162 | private: |
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 | |
201 | QT_END_NAMESPACE |
202 | |
203 | #endif // QT3DRENDER_RENDER_APISHADERMANAGER_H |
204 | |