1 | /**************************************************************************/ |
2 | /* framebuffer_cache_rd.h */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #ifndef FRAMEBUFFER_CACHE_RD_H |
32 | #define FRAMEBUFFER_CACHE_RD_H |
33 | |
34 | #include "core/templates/local_vector.h" |
35 | #include "core/templates/paged_allocator.h" |
36 | #include "servers/rendering/rendering_device.h" |
37 | |
38 | class FramebufferCacheRD : public Object { |
39 | GDCLASS(FramebufferCacheRD, Object) |
40 | |
41 | struct Cache { |
42 | Cache *prev = nullptr; |
43 | Cache *next = nullptr; |
44 | uint32_t hash = 0; |
45 | RID cache; |
46 | LocalVector<RID> textures; |
47 | LocalVector<RD::FramebufferPass> passes; |
48 | uint32_t views = 0; |
49 | }; |
50 | |
51 | PagedAllocator<Cache> cache_allocator; |
52 | |
53 | enum { |
54 | HASH_TABLE_SIZE = 16381 // Prime |
55 | }; |
56 | |
57 | Cache *hash_table[HASH_TABLE_SIZE] = {}; |
58 | |
59 | static _FORCE_INLINE_ uint32_t _hash_pass(const RD::FramebufferPass &p, uint32_t h) { |
60 | h = hash_murmur3_one_32(p.depth_attachment, h); |
61 | h = hash_murmur3_one_32(p.vrs_attachment, h); |
62 | |
63 | h = hash_murmur3_one_32(p.color_attachments.size(), h); |
64 | for (int i = 0; i < p.color_attachments.size(); i++) { |
65 | h = hash_murmur3_one_32(p.color_attachments[i], h); |
66 | } |
67 | |
68 | h = hash_murmur3_one_32(p.resolve_attachments.size(), h); |
69 | for (int i = 0; i < p.resolve_attachments.size(); i++) { |
70 | h = hash_murmur3_one_32(p.resolve_attachments[i], h); |
71 | } |
72 | |
73 | h = hash_murmur3_one_32(p.preserve_attachments.size(), h); |
74 | for (int i = 0; i < p.preserve_attachments.size(); i++) { |
75 | h = hash_murmur3_one_32(p.preserve_attachments[i], h); |
76 | } |
77 | |
78 | return h; |
79 | } |
80 | |
81 | static _FORCE_INLINE_ bool _compare_pass(const RD::FramebufferPass &a, const RD::FramebufferPass &b) { |
82 | if (a.depth_attachment != b.depth_attachment) { |
83 | return false; |
84 | } |
85 | |
86 | if (a.vrs_attachment != b.vrs_attachment) { |
87 | return false; |
88 | } |
89 | |
90 | if (a.color_attachments.size() != b.color_attachments.size()) { |
91 | return false; |
92 | } |
93 | |
94 | for (int i = 0; i < a.color_attachments.size(); i++) { |
95 | if (a.color_attachments[i] != b.color_attachments[i]) { |
96 | return false; |
97 | } |
98 | } |
99 | |
100 | if (a.resolve_attachments.size() != b.resolve_attachments.size()) { |
101 | return false; |
102 | } |
103 | |
104 | for (int i = 0; i < a.resolve_attachments.size(); i++) { |
105 | if (a.resolve_attachments[i] != b.resolve_attachments[i]) { |
106 | return false; |
107 | } |
108 | } |
109 | |
110 | if (a.preserve_attachments.size() != b.preserve_attachments.size()) { |
111 | return false; |
112 | } |
113 | |
114 | for (int i = 0; i < a.preserve_attachments.size(); i++) { |
115 | if (a.preserve_attachments[i] != b.preserve_attachments[i]) { |
116 | return false; |
117 | } |
118 | } |
119 | |
120 | return true; |
121 | } |
122 | |
123 | _FORCE_INLINE_ uint32_t _hash_rids(uint32_t h, const RID &arg) { |
124 | return hash_murmur3_one_64(arg.get_id(), h); |
125 | } |
126 | |
127 | template <typename... Args> |
128 | uint32_t _hash_rids(uint32_t h, const RID &arg, Args... args) { |
129 | h = hash_murmur3_one_64(arg.get_id(), h); |
130 | return _hash_rids(h, args...); |
131 | } |
132 | |
133 | _FORCE_INLINE_ bool _compare_args(uint32_t idx, const LocalVector<RID> &textures, const RID &arg) { |
134 | return textures[idx] == arg; |
135 | } |
136 | |
137 | template <typename... Args> |
138 | _FORCE_INLINE_ bool _compare_args(uint32_t idx, const LocalVector<RID> &textures, const RID &arg, Args... args) { |
139 | if (textures[idx] != arg) { |
140 | return false; |
141 | } |
142 | return _compare_args(idx + 1, textures, args...); |
143 | } |
144 | |
145 | _FORCE_INLINE_ void _create_args(Vector<RID> &textures, const RID &arg) { |
146 | textures.push_back(arg); |
147 | } |
148 | |
149 | template <typename... Args> |
150 | _FORCE_INLINE_ void _create_args(Vector<RID> &textures, const RID &arg, Args... args) { |
151 | textures.push_back(arg); |
152 | _create_args(textures, args...); |
153 | } |
154 | |
155 | static FramebufferCacheRD *singleton; |
156 | |
157 | uint32_t cache_instances_used = 0; |
158 | |
159 | void _invalidate(Cache *p_cache); |
160 | static void _framebuffer_invalidation_callback(void *p_userdata); |
161 | |
162 | RID _allocate_from_data(uint32_t p_views, uint32_t p_hash, uint32_t p_table_idx, const Vector<RID> &p_textures, const Vector<RD::FramebufferPass> &p_passes) { |
163 | RID rid; |
164 | if (p_passes.size()) { |
165 | rid = RD::get_singleton()->framebuffer_create_multipass(p_textures, p_passes, RD::INVALID_ID, p_views); |
166 | } else { |
167 | rid = RD::get_singleton()->framebuffer_create(p_textures, RD::INVALID_ID, p_views); |
168 | } |
169 | |
170 | ERR_FAIL_COND_V(rid.is_null(), rid); |
171 | |
172 | Cache *c = cache_allocator.alloc(); |
173 | c->views = p_views; |
174 | c->cache = rid; |
175 | c->hash = p_hash; |
176 | c->textures.resize(p_textures.size()); |
177 | for (uint32_t i = 0; i < c->textures.size(); i++) { |
178 | c->textures[i] = p_textures[i]; |
179 | } |
180 | c->passes.resize(p_passes.size()); |
181 | for (uint32_t i = 0; i < c->passes.size(); i++) { |
182 | c->passes[i] = p_passes[i]; |
183 | } |
184 | c->prev = nullptr; |
185 | c->next = hash_table[p_table_idx]; |
186 | if (hash_table[p_table_idx]) { |
187 | hash_table[p_table_idx]->prev = c; |
188 | } |
189 | hash_table[p_table_idx] = c; |
190 | |
191 | RD::get_singleton()->framebuffer_set_invalidation_callback(rid, _framebuffer_invalidation_callback, c); |
192 | |
193 | cache_instances_used++; |
194 | |
195 | return rid; |
196 | } |
197 | |
198 | public: |
199 | template <typename... Args> |
200 | RID get_cache(Args... args) { |
201 | uint32_t h = hash_murmur3_one_32(1); //1 view |
202 | h = hash_murmur3_one_32(sizeof...(Args), h); |
203 | h = _hash_rids(h, args...); |
204 | h = hash_murmur3_one_32(0, h); // 0 passes |
205 | h = hash_fmix32(h); |
206 | |
207 | uint32_t table_idx = h % HASH_TABLE_SIZE; |
208 | { |
209 | const Cache *c = hash_table[table_idx]; |
210 | |
211 | while (c) { |
212 | if (c->hash == h && c->passes.size() == 0 && c->textures.size() == sizeof...(Args) && c->views == 1 && _compare_args(0, c->textures, args...)) { |
213 | return c->cache; |
214 | } |
215 | c = c->next; |
216 | } |
217 | } |
218 | |
219 | // Not in cache, create: |
220 | |
221 | Vector<RID> textures; |
222 | _create_args(textures, args...); |
223 | |
224 | return _allocate_from_data(1, h, table_idx, textures, Vector<RD::FramebufferPass>()); |
225 | } |
226 | |
227 | template <typename... Args> |
228 | RID get_cache_multiview(uint32_t p_views, Args... args) { |
229 | uint32_t h = hash_murmur3_one_32(p_views); |
230 | h = hash_murmur3_one_32(sizeof...(Args), h); |
231 | h = _hash_rids(h, args...); |
232 | h = hash_murmur3_one_32(0, h); // 0 passes |
233 | h = hash_fmix32(h); |
234 | |
235 | uint32_t table_idx = h % HASH_TABLE_SIZE; |
236 | { |
237 | const Cache *c = hash_table[table_idx]; |
238 | |
239 | while (c) { |
240 | if (c->hash == h && c->passes.size() == 0 && c->textures.size() == sizeof...(Args) && c->views == p_views && _compare_args(0, c->textures, args...)) { |
241 | return c->cache; |
242 | } |
243 | c = c->next; |
244 | } |
245 | } |
246 | |
247 | // Not in cache, create: |
248 | |
249 | Vector<RID> textures; |
250 | _create_args(textures, args...); |
251 | |
252 | return _allocate_from_data(p_views, h, table_idx, textures, Vector<RD::FramebufferPass>()); |
253 | } |
254 | |
255 | RID get_cache_multipass(const Vector<RID> &p_textures, const Vector<RD::FramebufferPass> &p_passes, uint32_t p_views = 1) { |
256 | uint32_t h = hash_murmur3_one_32(p_views); |
257 | h = hash_murmur3_one_32(p_textures.size(), h); |
258 | for (int i = 0; i < p_textures.size(); i++) { |
259 | h = hash_murmur3_one_64(p_textures[i].get_id(), h); |
260 | } |
261 | h = hash_murmur3_one_32(p_passes.size(), h); |
262 | for (int i = 0; i < p_passes.size(); i++) { |
263 | h = _hash_pass(p_passes[i], h); |
264 | } |
265 | |
266 | h = hash_fmix32(h); |
267 | |
268 | uint32_t table_idx = h % HASH_TABLE_SIZE; |
269 | { |
270 | const Cache *c = hash_table[table_idx]; |
271 | |
272 | while (c) { |
273 | if (c->hash == h && c->views == p_views && c->textures.size() == (uint32_t)p_textures.size() && c->passes.size() == (uint32_t)p_passes.size()) { |
274 | bool all_ok = true; |
275 | |
276 | for (int i = 0; i < p_textures.size(); i++) { |
277 | if (p_textures[i] != c->textures[i]) { |
278 | all_ok = false; |
279 | break; |
280 | } |
281 | } |
282 | |
283 | if (all_ok) { |
284 | for (int i = 0; i < p_passes.size(); i++) { |
285 | if (!_compare_pass(p_passes[i], c->passes[i])) { |
286 | all_ok = false; |
287 | break; |
288 | } |
289 | } |
290 | } |
291 | |
292 | if (all_ok) { |
293 | return c->cache; |
294 | } |
295 | } |
296 | c = c->next; |
297 | } |
298 | } |
299 | |
300 | // Not in cache, create: |
301 | return _allocate_from_data(p_views, h, table_idx, p_textures, p_passes); |
302 | } |
303 | |
304 | static FramebufferCacheRD *get_singleton() { return singleton; } |
305 | |
306 | FramebufferCacheRD(); |
307 | ~FramebufferCacheRD(); |
308 | }; |
309 | |
310 | #endif // FRAMEBUFFER_CACHE_RD_H |
311 | |