#include "core_impl.h" #include "gl_util.h" // #include #include void gfx_init_gfxcore(GfxCore* gfxcore) { assert(gfxcore); mempool_make(&gfxcore->buffers); mempool_make(&gfxcore->framebuffers); mempool_make(&gfxcore->geometries); mempool_make(&gfxcore->renderbuffers); mempool_make(&gfxcore->shaders); mempool_make(&gfxcore->shader_programs); mempool_make(&gfxcore->textures); mempool_make(&gfxcore->shader_cache); mempool_make(&gfxcore->program_cache); glEnable(GL_CULL_FACE); glFrontFace(GL_CCW); glCullFace(GL_BACK); glEnable(GL_DEPTH_TEST); // Filter cubemaps across their faces to avoid seams. // https://www.khronos.org/opengl/wiki/Cubemap_Texture#Seamless_cubemap glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); } // Conveniently destroy any objects that have not been destroyed by the // application. void gfx_del_gfxcore(GfxCore* gfxcore) { assert(gfxcore); mempool_foreach(&gfxcore->buffers, buffer, { gfx_del_buffer(buffer); }); mempool_foreach(&gfxcore->framebuffers, framebuffer, { gfx_del_framebuffer(framebuffer); }); mempool_foreach( &gfxcore->geometries, geometry, { gfx_del_geometry(geometry); }); mempool_foreach(&gfxcore->renderbuffers, renderbuffer, { gfx_del_renderbuffer(renderbuffer); }); mempool_foreach( &gfxcore->shader_programs, prog, { gfx_del_shader_program(prog); }); mempool_foreach(&gfxcore->shaders, shader, { gfx_del_shader(shader); }); mempool_foreach(&gfxcore->textures, texture, { gfx_del_texture(texture); }); } // ----------------------------------------------------------------------------- // Render commands. // ----------------------------------------------------------------------------- void gfx_start_frame(GfxCore* gfxcore) { assert(gfxcore); glViewport( gfxcore->viewport.x, gfxcore->viewport.y, gfxcore->viewport.width, gfxcore->viewport.height); glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ASSERT_GL; } void gfx_end_frame(GfxCore* gfxcore) { assert(gfxcore); ASSERT_GL; } void gfx_set_viewport(GfxCore* gfxcore, int x, int y, int width, int height) { assert(gfxcore); gfxcore->viewport = (Viewport){.x = x, .y = y, .width = width, .height = height}; } void gfx_get_viewport( GfxCore* gfxcore, int* x, int* y, int* width, int* height) { assert(gfxcore); assert(x); assert(y); assert(width); assert(height); *x = gfxcore->viewport.x; *y = gfxcore->viewport.y; *width = gfxcore->viewport.width; *height = gfxcore->viewport.height; } void gfx_clear(GfxCore* gfxcore, vec4 colour) { assert(gfxcore); glClearColor(colour.x, colour.y, colour.z, colour.w); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void gfx_set_blending(GfxCore* gfxcore, bool enable) { assert(gfxcore); if (enable) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } } void gfx_set_depth_mask(GfxCore* gfxcore, bool enable) { assert(gfxcore); glDepthMask(enable ? GL_TRUE : GL_FALSE); } void gfx_set_culling(GfxCore* gfxcore, bool enable) { assert(gfxcore); if (enable) { glEnable(GL_CULL_FACE); } else { glDisable(GL_CULL_FACE); } } void gfx_set_polygon_offset(GfxCore* gfxcore, float scale, float bias) { assert(gfxcore); if ((scale != 0.0f) || (bias != 0.0f)) { glEnable(GL_POLYGON_OFFSET_FILL); } else { glDisable(GL_POLYGON_OFFSET_FILL); } glPolygonOffset(scale, bias); } void gfx_reset_polygon_offset(GfxCore* gfxcore) { assert(gfxcore); glPolygonOffset(0, 0); glDisable(GL_POLYGON_OFFSET_FILL); } // ----------------------------------------------------------------------------- // Buffers. // ----------------------------------------------------------------------------- Buffer* gfx_make_buffer(GfxCore* gfxcore, const BufferDesc* desc) { assert(gfxcore); assert(desc); Buffer* buffer = mempool_alloc(&gfxcore->buffers); if (!gfx_init_buffer(buffer, desc)) { mempool_free(&gfxcore->buffers, &buffer); return 0; } return buffer; } void gfx_destroy_buffer(GfxCore* gfxcore, Buffer** buffer) { assert(gfxcore); assert(buffer); if (*buffer) { gfx_del_buffer(*buffer); mempool_free(&gfxcore->buffers, buffer); } } // ----------------------------------------------------------------------------- // Geometry. // ----------------------------------------------------------------------------- Geometry* gfx_make_geometry(GfxCore* gfxcore, const GeometryDesc* desc) { assert(gfxcore); assert(desc); Geometry* geometry = mempool_alloc(&gfxcore->geometries); if (!gfx_init_geometry(geometry, gfxcore, desc)) { mempool_free(&gfxcore->geometries, &geometry); return 0; } return geometry; } void gfx_destroy_geometry(GfxCore* gfxcore, Geometry** geometry) { assert(gfxcore); assert(geometry); if (*geometry) { gfx_del_geometry(*geometry); mempool_free(&gfxcore->geometries, geometry); } } // ----------------------------------------------------------------------------- // Textures. // ----------------------------------------------------------------------------- Texture* gfx_make_texture(GfxCore* gfxcore, const TextureDesc* desc) { assert(gfxcore); assert(desc); Texture* texture = mempool_alloc(&gfxcore->textures); if (!gfx_init_texture(texture, desc)) { mempool_free(&gfxcore->textures, &texture); return 0; } return texture; } void gfx_destroy_texture(GfxCore* gfxcore, Texture** texture) { assert(gfxcore); assert(texture); assert(*texture); if (*texture) { gfx_del_texture(*texture); mempool_free(&gfxcore->textures, texture); } } // ----------------------------------------------------------------------------- // Renderbuffers. // ----------------------------------------------------------------------------- RenderBuffer* gfx_make_renderbuffer( GfxCore* gfxcore, const RenderBufferDesc* desc) { assert(gfxcore); assert(desc); RenderBuffer* renderbuffer = mempool_alloc(&gfxcore->renderbuffers); if (!gfx_init_renderbuffer(renderbuffer, desc)) { mempool_free(&gfxcore->renderbuffers, &renderbuffer); } return renderbuffer; } void gfx_destroy_renderbuffer(GfxCore* gfxcore, RenderBuffer** renderbuffer) { assert(gfxcore); assert(renderbuffer); assert(*renderbuffer); if (*renderbuffer) { gfx_del_renderbuffer(*renderbuffer); mempool_free(&gfxcore->renderbuffers, renderbuffer); } } // ----------------------------------------------------------------------------- // Framebuffers. // ----------------------------------------------------------------------------- FrameBuffer* gfx_make_framebuffer( GfxCore* gfxcore, const FrameBufferDesc* desc) { assert(gfxcore); assert(desc); FrameBuffer* framebuffer = mempool_alloc(&gfxcore->framebuffers); if (!gfx_init_framebuffer(framebuffer, desc)) { mempool_free(&gfxcore->framebuffers, &framebuffer); return 0; } return framebuffer; } void gfx_destroy_framebuffer(GfxCore* gfxcore, FrameBuffer** framebuffer) { assert(gfxcore); assert(framebuffer); assert(*framebuffer); if (*framebuffer) { gfx_del_framebuffer(*framebuffer); mempool_free(&gfxcore->framebuffers, 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(GfxCore* gfxcore, const ShaderDesc* desc) { assert(gfxcore); assert(desc); // Check the shader cache first. ShaderCache* cache = &gfxcore->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(&gfxcore->shaders); if (!shader) { return 0; } if (!gfx_compile_shader(shader, desc)) { mempool_free(&gfxcore->shaders, &shader); return 0; } ShaderCacheEntry* entry = mempool_alloc(cache); *entry = (ShaderCacheEntry){.hash = hash, .shader = shader}; // LOGD("Added shader with hash [%lx] to cache", hash); return shader; } void gfx_destroy_shader(GfxCore* gfxcore, Shader** shader) { assert(gfxcore); assert(shader); if (*shader) { // Remove the shader from the cache. ShaderCache* cache = &gfxcore->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(&gfxcore->shaders, shader); } } ShaderProgram* gfx_make_shader_program( GfxCore* gfxcore, const ShaderProgramDesc* desc) { assert(gfxcore); assert(desc); // Check the shader program cache first. ProgramCache* cache = &gfxcore->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(&gfxcore->shader_programs); if (!gfx_build_shader_program(prog, desc)) { mempool_free(&gfxcore->shader_programs, &prog); return 0; } ShaderProgramCacheEntry* entry = mempool_alloc(cache); *entry = (ShaderProgramCacheEntry){.hash = hash, .program = prog}; // LOGD("Added shader program with hash [%lx] to cache", hash); return prog; } void gfx_destroy_shader_program(GfxCore* gfxcore, ShaderProgram** prog) { assert(gfxcore); assert(prog); if (*prog) { // Remove the shader program from the cache. ProgramCache* cache = &gfxcore->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(&gfxcore->shader_programs, prog); } }