diff options
author | 3gg <3gg@shellblade.net> | 2023-02-10 08:32:01 -0800 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2023-02-10 08:32:01 -0800 |
commit | 8f0726b6343073f3a3677cadd217736f96e0d4dd (patch) | |
tree | bb6b9b71f0184f52281d1efc9f7f1d25c2e686c3 | |
parent | a8ba7e5e9c98890a13c0eb6868acf97f6fffaac3 (diff) |
Add shader and shader program caches.
-rw-r--r-- | gfx/src/render/render_backend.c | 115 | ||||
-rw-r--r-- | gfx/src/render/render_backend_impl.h | 21 |
2 files changed, 133 insertions, 3 deletions
diff --git a/gfx/src/render/render_backend.c b/gfx/src/render/render_backend.c index fed906b..0fc8c85 100644 --- a/gfx/src/render/render_backend.c +++ b/gfx/src/render/render_backend.c | |||
@@ -3,12 +3,13 @@ | |||
3 | #include "gl_util.h" | 3 | #include "gl_util.h" |
4 | 4 | ||
5 | #include <GL/gl.h> | 5 | #include <GL/gl.h> |
6 | #include <log/log.h> | 6 | // #include <log/log.h> |
7 | 7 | ||
8 | #include <assert.h> | 8 | #include <assert.h> |
9 | 9 | ||
10 | void gfx_init_render_backend(RenderBackend* render_backend) { | 10 | void gfx_init_render_backend(RenderBackend* render_backend) { |
11 | assert(render_backend); | 11 | assert(render_backend); |
12 | |||
12 | mempool_make(&render_backend->buffers); | 13 | mempool_make(&render_backend->buffers); |
13 | mempool_make(&render_backend->framebuffers); | 14 | mempool_make(&render_backend->framebuffers); |
14 | mempool_make(&render_backend->geometries); | 15 | mempool_make(&render_backend->geometries); |
@@ -17,6 +18,9 @@ void gfx_init_render_backend(RenderBackend* render_backend) { | |||
17 | mempool_make(&render_backend->shader_programs); | 18 | mempool_make(&render_backend->shader_programs); |
18 | mempool_make(&render_backend->textures); | 19 | mempool_make(&render_backend->textures); |
19 | 20 | ||
21 | mempool_make(&render_backend->shader_cache); | ||
22 | mempool_make(&render_backend->program_cache); | ||
23 | |||
20 | glEnable(GL_CULL_FACE); | 24 | glEnable(GL_CULL_FACE); |
21 | glFrontFace(GL_CCW); | 25 | glFrontFace(GL_CCW); |
22 | glCullFace(GL_BACK); | 26 | glCullFace(GL_BACK); |
@@ -259,10 +263,83 @@ void gfx_destroy_framebuffer( | |||
259 | // Shaders. | 263 | // Shaders. |
260 | // ----------------------------------------------------------------------------- | 264 | // ----------------------------------------------------------------------------- |
261 | 265 | ||
266 | static uint64_t hash_shader_desc(const ShaderDesc* desc) { | ||
267 | assert(desc); | ||
268 | // Note that defines may affect shader permutations, so we need to hash those | ||
269 | // as well. | ||
270 | uint64_t hash = 0; | ||
271 | for (size_t i = 0; i < desc->num_defines; ++i) { | ||
272 | const ShaderCompilerDefine* define = &desc->defines[i]; | ||
273 | hash = (((hash << 13) + sstring_hash(define->name)) << 7) + | ||
274 | sstring_hash(define->value); | ||
275 | } | ||
276 | return (hash << 17) + cstring_hash(desc->code); | ||
277 | } | ||
278 | |||
279 | static uint64_t hash_program_desc(const ShaderProgramDesc* desc) { | ||
280 | assert(desc); | ||
281 | return ((uint64_t)desc->vertex_shader->id << 32) | | ||
282 | (uint64_t)desc->fragment_shader->id; | ||
283 | } | ||
284 | |||
285 | static Shader* find_cached_shader(ShaderCache* cache, uint64_t hash) { | ||
286 | assert(cache); | ||
287 | mempool_foreach(cache, entry, { | ||
288 | if (entry->hash == hash) { | ||
289 | return entry->shader; | ||
290 | } | ||
291 | }); | ||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static ShaderProgram* find_cached_program(ProgramCache* cache, uint64_t hash) { | ||
296 | assert(cache); | ||
297 | mempool_foreach(cache, entry, { | ||
298 | if (entry->hash == hash) { | ||
299 | return entry->program; | ||
300 | } | ||
301 | }); | ||
302 | return 0; | ||
303 | } | ||
304 | |||
305 | static ShaderCacheEntry* find_shader_cache_entry( | ||
306 | ShaderCache* cache, const Shader* shader) { | ||
307 | assert(cache); | ||
308 | assert(shader); | ||
309 | mempool_foreach(cache, entry, { | ||
310 | if (entry->shader == shader) { | ||
311 | return entry; | ||
312 | } | ||
313 | }); | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | static ShaderProgramCacheEntry* find_program_cache_entry( | ||
318 | ProgramCache* cache, const ShaderProgram* prog) { | ||
319 | assert(cache); | ||
320 | assert(prog); | ||
321 | mempool_foreach(cache, entry, { | ||
322 | if (entry->program == prog) { | ||
323 | return entry; | ||
324 | } | ||
325 | }); | ||
326 | return 0; | ||
327 | } | ||
328 | |||
262 | Shader* gfx_make_shader(RenderBackend* render_backend, const ShaderDesc* desc) { | 329 | Shader* gfx_make_shader(RenderBackend* render_backend, const ShaderDesc* desc) { |
263 | assert(render_backend); | 330 | assert(render_backend); |
264 | assert(desc); | 331 | assert(desc); |
265 | Shader* shader = mempool_alloc(&render_backend->shaders); | 332 | |
333 | // Check the shader cache first. | ||
334 | ShaderCache* cache = &render_backend->shader_cache; | ||
335 | const uint64_t hash = hash_shader_desc(desc); | ||
336 | Shader* shader = find_cached_shader(cache, hash); | ||
337 | if (shader) { | ||
338 | // LOGD("Found cached shader with hash %lx", hash); | ||
339 | return shader; | ||
340 | } | ||
341 | |||
342 | shader = mempool_alloc(&render_backend->shaders); | ||
266 | if (!shader) { | 343 | if (!shader) { |
267 | return 0; | 344 | return 0; |
268 | } | 345 | } |
@@ -270,6 +347,10 @@ Shader* gfx_make_shader(RenderBackend* render_backend, const ShaderDesc* desc) { | |||
270 | mempool_free(&render_backend->shaders, &shader); | 347 | mempool_free(&render_backend->shaders, &shader); |
271 | return 0; | 348 | return 0; |
272 | } | 349 | } |
350 | ShaderCacheEntry* entry = mempool_alloc(cache); | ||
351 | assert(entry); | ||
352 | *entry = (ShaderCacheEntry){.hash = hash, .shader = shader}; | ||
353 | // LOGD("Added shader with hash %lx to cache", hash); | ||
273 | return shader; | 354 | return shader; |
274 | } | 355 | } |
275 | 356 | ||
@@ -277,6 +358,13 @@ void gfx_destroy_shader(RenderBackend* render_backend, Shader** shader) { | |||
277 | assert(render_backend); | 358 | assert(render_backend); |
278 | assert(shader); | 359 | assert(shader); |
279 | assert(*shader); | 360 | assert(*shader); |
361 | |||
362 | // Remove the shader from the cache. | ||
363 | ShaderCache* cache = &render_backend->shader_cache; | ||
364 | ShaderCacheEntry* entry = find_shader_cache_entry(cache, *shader); | ||
365 | assert(entry); // Must be there, shaders can't go untracked. | ||
366 | mempool_free(cache, &entry); | ||
367 | |||
280 | gfx_del_shader(*shader); | 368 | gfx_del_shader(*shader); |
281 | mempool_free(&render_backend->shaders, shader); | 369 | mempool_free(&render_backend->shaders, shader); |
282 | } | 370 | } |
@@ -285,7 +373,17 @@ ShaderProgram* gfx_make_shader_program( | |||
285 | RenderBackend* render_backend, const ShaderProgramDesc* desc) { | 373 | RenderBackend* render_backend, const ShaderProgramDesc* desc) { |
286 | assert(render_backend); | 374 | assert(render_backend); |
287 | assert(desc); | 375 | assert(desc); |
288 | ShaderProgram* prog = mempool_alloc(&render_backend->shader_programs); | 376 | |
377 | // Check the shader program cache first. | ||
378 | ProgramCache* cache = &render_backend->program_cache; | ||
379 | const uint64_t hash = hash_program_desc(desc); | ||
380 | ShaderProgram* prog = find_cached_program(cache, hash); | ||
381 | if (prog) { | ||
382 | // LOGD("Found cached shader program with hash %lx", hash); | ||
383 | return prog; | ||
384 | } | ||
385 | |||
386 | prog = mempool_alloc(&render_backend->shader_programs); | ||
289 | if (!prog) { | 387 | if (!prog) { |
290 | return 0; | 388 | return 0; |
291 | } | 389 | } |
@@ -293,6 +391,10 @@ ShaderProgram* gfx_make_shader_program( | |||
293 | mempool_free(&render_backend->shader_programs, &prog); | 391 | mempool_free(&render_backend->shader_programs, &prog); |
294 | return 0; | 392 | return 0; |
295 | } | 393 | } |
394 | ShaderProgramCacheEntry* entry = mempool_alloc(cache); | ||
395 | assert(entry); | ||
396 | *entry = (ShaderProgramCacheEntry){.hash = hash, .program = prog}; | ||
397 | // LOGD("Added shader program with hash %lx to cache", hash); | ||
296 | return prog; | 398 | return prog; |
297 | } | 399 | } |
298 | 400 | ||
@@ -301,6 +403,13 @@ void gfx_destroy_shader_program( | |||
301 | assert(render_backend); | 403 | assert(render_backend); |
302 | assert(prog); | 404 | assert(prog); |
303 | assert(*prog); | 405 | assert(*prog); |
406 | |||
407 | // Remove the shader program from the cache. | ||
408 | ProgramCache* cache = &render_backend->program_cache; | ||
409 | ShaderProgramCacheEntry* entry = find_program_cache_entry(cache, *prog); | ||
410 | assert(entry); // Must be there, shaders can't go untracked. | ||
411 | mempool_free(cache, &entry); | ||
412 | |||
304 | gfx_del_shader_program(*prog); | 413 | gfx_del_shader_program(*prog); |
305 | mempool_free(&render_backend->shader_programs, prog); | 414 | mempool_free(&render_backend->shader_programs, prog); |
306 | } | 415 | } |
diff --git a/gfx/src/render/render_backend_impl.h b/gfx/src/render/render_backend_impl.h index 9e552c2..e149e98 100644 --- a/gfx/src/render/render_backend_impl.h +++ b/gfx/src/render/render_backend_impl.h | |||
@@ -13,6 +13,21 @@ | |||
13 | 13 | ||
14 | #include <mempool.h> | 14 | #include <mempool.h> |
15 | 15 | ||
16 | #include <stdint.h> | ||
17 | |||
18 | // TODO: Make a generic (hash, void*) structure and define functions over it. | ||
19 | // Then define a macro that defines type-safe macros given the type of the | ||
20 | // entry. | ||
21 | typedef struct ShaderCacheEntry { | ||
22 | uint64_t hash; | ||
23 | Shader* shader; | ||
24 | } ShaderCacheEntry; | ||
25 | |||
26 | typedef struct ShaderProgramCacheEntry { | ||
27 | uint64_t hash; | ||
28 | ShaderProgram* program; | ||
29 | } ShaderProgramCacheEntry; | ||
30 | |||
16 | DEF_MEMPOOL(buffer_pool, Buffer, GFX_MAX_NUM_BUFFERS) | 31 | DEF_MEMPOOL(buffer_pool, Buffer, GFX_MAX_NUM_BUFFERS) |
17 | DEF_MEMPOOL(framebuffer_pool, FrameBuffer, GFX_MAX_NUM_FRAMEBUFFERS) | 32 | DEF_MEMPOOL(framebuffer_pool, FrameBuffer, GFX_MAX_NUM_FRAMEBUFFERS) |
18 | DEF_MEMPOOL(geometry_pool, Geometry, GFX_MAX_NUM_GEOMETRIES) | 33 | DEF_MEMPOOL(geometry_pool, Geometry, GFX_MAX_NUM_GEOMETRIES) |
@@ -21,6 +36,9 @@ DEF_MEMPOOL(shader_pool, Shader, GFX_MAX_NUM_SHADERS) | |||
21 | DEF_MEMPOOL(shader_program_pool, ShaderProgram, GFX_MAX_NUM_SHADER_PROGRAMS) | 36 | DEF_MEMPOOL(shader_program_pool, ShaderProgram, GFX_MAX_NUM_SHADER_PROGRAMS) |
22 | DEF_MEMPOOL(texture_pool, Texture, GFX_MAX_NUM_TEXTURES) | 37 | DEF_MEMPOOL(texture_pool, Texture, GFX_MAX_NUM_TEXTURES) |
23 | 38 | ||
39 | DEF_MEMPOOL(ShaderCache, ShaderCacheEntry, GFX_MAX_NUM_SHADERS) | ||
40 | DEF_MEMPOOL(ProgramCache, ShaderProgramCacheEntry, GFX_MAX_NUM_SHADER_PROGRAMS) | ||
41 | |||
24 | typedef struct { | 42 | typedef struct { |
25 | int width; | 43 | int width; |
26 | int height; | 44 | int height; |
@@ -36,6 +54,9 @@ typedef struct RenderBackend { | |||
36 | shader_pool shaders; | 54 | shader_pool shaders; |
37 | shader_program_pool shader_programs; | 55 | shader_program_pool shader_programs; |
38 | texture_pool textures; | 56 | texture_pool textures; |
57 | // Caches. | ||
58 | ShaderCache shader_cache; | ||
59 | ProgramCache program_cache; | ||
39 | } RenderBackend; | 60 | } RenderBackend; |
40 | 61 | ||
41 | /// Create a new render backend. | 62 | /// Create a new render backend. |