From 8f0726b6343073f3a3677cadd217736f96e0d4dd Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 10 Feb 2023 08:32:01 -0800 Subject: Add shader and shader program caches. --- gfx/src/render/render_backend.c | 115 ++++++++++++++++++++++++++++++++++- 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 @@ #include "gl_util.h" #include -#include +// #include #include void gfx_init_render_backend(RenderBackend* render_backend) { assert(render_backend); + mempool_make(&render_backend->buffers); mempool_make(&render_backend->framebuffers); mempool_make(&render_backend->geometries); @@ -17,6 +18,9 @@ void gfx_init_render_backend(RenderBackend* render_backend) { mempool_make(&render_backend->shader_programs); mempool_make(&render_backend->textures); + mempool_make(&render_backend->shader_cache); + mempool_make(&render_backend->program_cache); + glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); @@ -259,10 +263,83 @@ void gfx_destroy_framebuffer( // Shaders. // ----------------------------------------------------------------------------- +static uint64_t hash_shader_desc(const ShaderDesc* desc) { + assert(desc); + // Note that defines may affect shader permutations, so we need to hash those + // as well. + uint64_t hash = 0; + for (size_t i = 0; i < desc->num_defines; ++i) { + const ShaderCompilerDefine* define = &desc->defines[i]; + hash = (((hash << 13) + sstring_hash(define->name)) << 7) + + sstring_hash(define->value); + } + return (hash << 17) + cstring_hash(desc->code); +} + +static uint64_t hash_program_desc(const ShaderProgramDesc* desc) { + assert(desc); + return ((uint64_t)desc->vertex_shader->id << 32) | + (uint64_t)desc->fragment_shader->id; +} + +static Shader* find_cached_shader(ShaderCache* cache, uint64_t hash) { + assert(cache); + mempool_foreach(cache, entry, { + if (entry->hash == hash) { + return entry->shader; + } + }); + return 0; +} + +static ShaderProgram* find_cached_program(ProgramCache* cache, uint64_t hash) { + assert(cache); + mempool_foreach(cache, entry, { + if (entry->hash == hash) { + return entry->program; + } + }); + return 0; +} + +static ShaderCacheEntry* find_shader_cache_entry( + ShaderCache* cache, const Shader* shader) { + assert(cache); + assert(shader); + mempool_foreach(cache, entry, { + if (entry->shader == shader) { + return entry; + } + }); + return 0; +} + +static ShaderProgramCacheEntry* find_program_cache_entry( + ProgramCache* cache, const ShaderProgram* prog) { + assert(cache); + assert(prog); + mempool_foreach(cache, entry, { + if (entry->program == prog) { + return entry; + } + }); + return 0; +} + Shader* gfx_make_shader(RenderBackend* render_backend, const ShaderDesc* desc) { assert(render_backend); assert(desc); - Shader* shader = mempool_alloc(&render_backend->shaders); + + // Check the shader cache first. + ShaderCache* cache = &render_backend->shader_cache; + const uint64_t hash = hash_shader_desc(desc); + Shader* shader = find_cached_shader(cache, hash); + if (shader) { + // LOGD("Found cached shader with hash %lx", hash); + return shader; + } + + shader = mempool_alloc(&render_backend->shaders); if (!shader) { return 0; } @@ -270,6 +347,10 @@ Shader* gfx_make_shader(RenderBackend* render_backend, const ShaderDesc* desc) { mempool_free(&render_backend->shaders, &shader); return 0; } + ShaderCacheEntry* entry = mempool_alloc(cache); + assert(entry); + *entry = (ShaderCacheEntry){.hash = hash, .shader = shader}; + // LOGD("Added shader with hash %lx to cache", hash); return shader; } @@ -277,6 +358,13 @@ void gfx_destroy_shader(RenderBackend* render_backend, Shader** shader) { assert(render_backend); assert(shader); assert(*shader); + + // Remove the shader from the cache. + ShaderCache* cache = &render_backend->shader_cache; + ShaderCacheEntry* entry = find_shader_cache_entry(cache, *shader); + assert(entry); // Must be there, shaders can't go untracked. + mempool_free(cache, &entry); + gfx_del_shader(*shader); mempool_free(&render_backend->shaders, shader); } @@ -285,7 +373,17 @@ ShaderProgram* gfx_make_shader_program( RenderBackend* render_backend, const ShaderProgramDesc* desc) { assert(render_backend); assert(desc); - ShaderProgram* prog = mempool_alloc(&render_backend->shader_programs); + + // Check the shader program cache first. + ProgramCache* cache = &render_backend->program_cache; + const uint64_t hash = hash_program_desc(desc); + ShaderProgram* prog = find_cached_program(cache, hash); + if (prog) { + // LOGD("Found cached shader program with hash %lx", hash); + return prog; + } + + prog = mempool_alloc(&render_backend->shader_programs); if (!prog) { return 0; } @@ -293,6 +391,10 @@ ShaderProgram* gfx_make_shader_program( mempool_free(&render_backend->shader_programs, &prog); return 0; } + ShaderProgramCacheEntry* entry = mempool_alloc(cache); + assert(entry); + *entry = (ShaderProgramCacheEntry){.hash = hash, .program = prog}; + // LOGD("Added shader program with hash %lx to cache", hash); return prog; } @@ -301,6 +403,13 @@ void gfx_destroy_shader_program( assert(render_backend); assert(prog); assert(*prog); + + // Remove the shader program from the cache. + ProgramCache* cache = &render_backend->program_cache; + ShaderProgramCacheEntry* entry = find_program_cache_entry(cache, *prog); + assert(entry); // Must be there, shaders can't go untracked. + mempool_free(cache, &entry); + gfx_del_shader_program(*prog); mempool_free(&render_backend->shader_programs, prog); } 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 @@ #include +#include + +// TODO: Make a generic (hash, void*) structure and define functions over it. +// Then define a macro that defines type-safe macros given the type of the +// entry. +typedef struct ShaderCacheEntry { + uint64_t hash; + Shader* shader; +} ShaderCacheEntry; + +typedef struct ShaderProgramCacheEntry { + uint64_t hash; + ShaderProgram* program; +} ShaderProgramCacheEntry; + DEF_MEMPOOL(buffer_pool, Buffer, GFX_MAX_NUM_BUFFERS) DEF_MEMPOOL(framebuffer_pool, FrameBuffer, GFX_MAX_NUM_FRAMEBUFFERS) DEF_MEMPOOL(geometry_pool, Geometry, GFX_MAX_NUM_GEOMETRIES) @@ -21,6 +36,9 @@ DEF_MEMPOOL(shader_pool, Shader, GFX_MAX_NUM_SHADERS) DEF_MEMPOOL(shader_program_pool, ShaderProgram, GFX_MAX_NUM_SHADER_PROGRAMS) DEF_MEMPOOL(texture_pool, Texture, GFX_MAX_NUM_TEXTURES) +DEF_MEMPOOL(ShaderCache, ShaderCacheEntry, GFX_MAX_NUM_SHADERS) +DEF_MEMPOOL(ProgramCache, ShaderProgramCacheEntry, GFX_MAX_NUM_SHADER_PROGRAMS) + typedef struct { int width; int height; @@ -36,6 +54,9 @@ typedef struct RenderBackend { shader_pool shaders; shader_program_pool shader_programs; texture_pool textures; + // Caches. + ShaderCache shader_cache; + ProgramCache program_cache; } RenderBackend; /// Create a new render backend. -- cgit v1.2.3