From b55bc7d6620f069f576cbabaf47c571ee2e545b9 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 15 Mar 2024 21:44:15 -0700 Subject: Rename render -> core. --- gfx/CMakeLists.txt | 16 +- gfx/src/core/buffer.c | 85 +++++++++ gfx/src/core/buffer.h | 26 +++ gfx/src/core/constants.h | 9 + gfx/src/core/core.c | 414 ++++++++++++++++++++++++++++++++++++++++ gfx/src/core/core_impl.h | 66 +++++++ gfx/src/core/framebuffer.c | 151 +++++++++++++++ gfx/src/core/framebuffer.h | 15 ++ gfx/src/core/geometry.c | 326 +++++++++++++++++++++++++++++++ gfx/src/core/geometry.h | 28 +++ gfx/src/core/gl_util.h | 47 +++++ gfx/src/core/renderbuffer.c | 35 ++++ gfx/src/core/renderbuffer.h | 15 ++ gfx/src/core/shader.c | 92 +++++++++ gfx/src/core/shader.h | 17 ++ gfx/src/core/shader_program.c | 291 ++++++++++++++++++++++++++++ gfx/src/core/shader_program.h | 24 +++ gfx/src/core/texture.c | 218 +++++++++++++++++++++ gfx/src/core/texture.h | 35 ++++ gfx/src/gfx.c | 5 +- gfx/src/render/buffer.c | 85 --------- gfx/src/render/buffer.h | 26 --- gfx/src/render/constants.h | 9 - gfx/src/render/core.c | 414 ---------------------------------------- gfx/src/render/core_impl.h | 66 ------- gfx/src/render/framebuffer.c | 151 --------------- gfx/src/render/framebuffer.h | 15 -- gfx/src/render/geometry.c | 326 ------------------------------- gfx/src/render/geometry.h | 28 --- gfx/src/render/gl_util.h | 47 ----- gfx/src/render/renderbuffer.c | 35 ---- gfx/src/render/renderbuffer.h | 15 -- gfx/src/render/shader.c | 92 --------- gfx/src/render/shader.h | 17 -- gfx/src/render/shader_program.c | 291 ---------------------------- gfx/src/render/shader_program.h | 24 --- gfx/src/render/texture.c | 218 --------------------- gfx/src/render/texture.h | 35 ---- 38 files changed, 1903 insertions(+), 1906 deletions(-) create mode 100644 gfx/src/core/buffer.c create mode 100644 gfx/src/core/buffer.h create mode 100644 gfx/src/core/constants.h create mode 100644 gfx/src/core/core.c create mode 100644 gfx/src/core/core_impl.h create mode 100644 gfx/src/core/framebuffer.c create mode 100644 gfx/src/core/framebuffer.h create mode 100644 gfx/src/core/geometry.c create mode 100644 gfx/src/core/geometry.h create mode 100644 gfx/src/core/gl_util.h create mode 100644 gfx/src/core/renderbuffer.c create mode 100644 gfx/src/core/renderbuffer.h create mode 100644 gfx/src/core/shader.c create mode 100644 gfx/src/core/shader.h create mode 100644 gfx/src/core/shader_program.c create mode 100644 gfx/src/core/shader_program.h create mode 100644 gfx/src/core/texture.c create mode 100644 gfx/src/core/texture.h delete mode 100644 gfx/src/render/buffer.c delete mode 100644 gfx/src/render/buffer.h delete mode 100644 gfx/src/render/constants.h delete mode 100644 gfx/src/render/core.c delete mode 100644 gfx/src/render/core_impl.h delete mode 100644 gfx/src/render/framebuffer.c delete mode 100644 gfx/src/render/framebuffer.h delete mode 100644 gfx/src/render/geometry.c delete mode 100644 gfx/src/render/geometry.h delete mode 100644 gfx/src/render/gl_util.h delete mode 100644 gfx/src/render/renderbuffer.c delete mode 100644 gfx/src/render/renderbuffer.h delete mode 100644 gfx/src/render/shader.c delete mode 100644 gfx/src/render/shader.h delete mode 100644 gfx/src/render/shader_program.c delete mode 100644 gfx/src/render/shader_program.h delete mode 100644 gfx/src/render/texture.c delete mode 100644 gfx/src/render/texture.h diff --git a/gfx/CMakeLists.txt b/gfx/CMakeLists.txt index c835bd9..7aa118b 100644 --- a/gfx/CMakeLists.txt +++ b/gfx/CMakeLists.txt @@ -36,14 +36,14 @@ add_library(gfx SHARED src/asset/asset_cache.c src/asset/model.c src/asset/texture.c - src/render/buffer.c - src/render/core.c - src/render/framebuffer.c - src/render/geometry.c - src/render/renderbuffer.c - src/render/shader_program.c - src/render/shader.c - src/render/texture.c + src/core/buffer.c + src/core/core.c + src/core/framebuffer.c + src/core/geometry.c + src/core/renderbuffer.c + src/core/shader_program.c + src/core/shader.c + src/core/texture.c src/renderer/imm_renderer.c src/renderer/renderer.c src/scene/animation.c diff --git a/gfx/src/core/buffer.c b/gfx/src/core/buffer.c new file mode 100644 index 0000000..3b7e4bc --- /dev/null +++ b/gfx/src/core/buffer.c @@ -0,0 +1,85 @@ +#include "buffer.h" + +#include +#include + +#include +#include +#include + +static size_t get_buffer_size_bytes( + BufferType type, const BufferDataDesc* desc) { + return desc->count * gfx_get_buffer_type_size_bytes(type); +} + +static GLenum get_buffer_usage(BufferUsage usage) { + switch (usage) { + case BufferStatic: + return GL_STATIC_DRAW; + case BufferDynamic: + return GL_DYNAMIC_DRAW; + } + FAIL("Unhandled buffer usage"); + return GL_STATIC_DRAW; +} + +size_t gfx_get_buffer_type_size_bytes(BufferType type) { + switch (type) { + case BufferUntyped: + return 1; + case Buffer2d: + return sizeof(vec2); + case Buffer3d: + return sizeof(vec3); + case Buffer4d: + return sizeof(vec4); + case BufferFloat: + return sizeof(float); + case BufferU8: + return sizeof(uint8_t); + case BufferU16: + return sizeof(uint16_t); + } + FAIL("Unhandled buffer type"); + return 0; +} + +bool gfx_init_buffer(Buffer* buffer, const BufferDesc* desc) { + assert(buffer); + + buffer->type = desc->type; + buffer->usage = desc->usage; + buffer->size_bytes = get_buffer_size_bytes(desc->type, &desc->data); + const GLenum usage = get_buffer_usage(desc->usage); + + glGenBuffers(1, &buffer->vbo); + glBindBuffer(GL_ARRAY_BUFFER, buffer->vbo); + glBufferData(GL_ARRAY_BUFFER, buffer->size_bytes, desc->data.data, usage); + glBindBuffer(GL_ARRAY_BUFFER, 0); + ASSERT_GL; + + return true; +} + +void gfx_del_buffer(Buffer* buffer) { + assert(buffer); + if (buffer->vbo) { + glDeleteBuffers(1, &buffer->vbo); + buffer->vbo = 0; + } +} + +void gfx_update_buffer(Buffer* buffer, const BufferDataDesc* desc) { + assert(buffer); + assert(desc); + // OpenGL allows updating static buffers, but it is not optimal for + // performance, so we enforce data in static buffers remain static. + assert(buffer->usage == BufferDynamic); + + const size_t update_size_bytes = get_buffer_size_bytes(buffer->type, desc); + assert(update_size_bytes <= buffer->size_bytes); + + glBindBuffer(GL_ARRAY_BUFFER, buffer->vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, update_size_bytes, desc->data); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} diff --git a/gfx/src/core/buffer.h b/gfx/src/core/buffer.h new file mode 100644 index 0000000..b9080f0 --- /dev/null +++ b/gfx/src/core/buffer.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "gl_util.h" + +#include + +#include +#include + +typedef struct Buffer { + GLuint vbo; + BufferType type; + BufferUsage usage; + size_t size_bytes; +} Buffer; + +/// Return the buffer type size in bytes. +size_t gfx_get_buffer_type_size_bytes(BufferType); + +/// Create a buffer from raw data. +bool gfx_init_buffer(Buffer*, const BufferDesc*); + +/// Destroy the buffer. +void gfx_del_buffer(Buffer*); diff --git a/gfx/src/core/constants.h b/gfx/src/core/constants.h new file mode 100644 index 0000000..a6a3b94 --- /dev/null +++ b/gfx/src/core/constants.h @@ -0,0 +1,9 @@ +#pragma once + +// Shaders vertex attribute locations must match the channels here. +#define GFX_POSITION_CHANNEL 0 +#define GFX_NORMAL_CHANNEL 1 +#define GFX_TANGENT_CHANNEL 2 +#define GFX_TEXCOORDS_CHANNEL 3 +#define GFX_JOINTS_CHANNEL 4 +#define GFX_WEIGHTS_CHANNEL 5 diff --git a/gfx/src/core/core.c b/gfx/src/core/core.c new file mode 100644 index 0000000..7a6d9cc --- /dev/null +++ b/gfx/src/core/core.c @@ -0,0 +1,414 @@ +#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(0, 0, 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 width, int height) { + assert(gfxcore); + gfxcore->viewport.width = width; + gfxcore->viewport.height = height; +} + +void gfx_get_viewport(GfxCore* gfxcore, int* width, int* height) { + assert(gfxcore); + assert(width); + assert(height); + *width = gfxcore->viewport.width; + *height = gfxcore->viewport.height; +} + +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); + } +} diff --git a/gfx/src/core/core_impl.h b/gfx/src/core/core_impl.h new file mode 100644 index 0000000..e27c0f2 --- /dev/null +++ b/gfx/src/core/core_impl.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +#include "buffer.h" +#include "framebuffer.h" +#include "geometry.h" +#include "renderbuffer.h" +#include "shader.h" +#include "shader_program.h" +#include "texture.h" + +#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) +DEF_MEMPOOL(renderbuffer_pool, RenderBuffer, GFX_MAX_NUM_RENDERBUFFERS) +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; +} Viewport; + +typedef struct GfxCore { + Viewport viewport; + // mempools for render-specific objects: textures, geometry, etc. + buffer_pool buffers; + framebuffer_pool framebuffers; + geometry_pool geometries; + renderbuffer_pool renderbuffers; + shader_pool shaders; + shader_program_pool shader_programs; + texture_pool textures; + // Caches. + ShaderCache shader_cache; + ProgramCache program_cache; +} GfxCore; + +/// Create a new render backend. +void gfx_init_gfxcore(GfxCore*); + +/// Destroy the render backend. +void gfx_del_gfxcore(GfxCore*); diff --git a/gfx/src/core/framebuffer.c b/gfx/src/core/framebuffer.c new file mode 100644 index 0000000..76d9002 --- /dev/null +++ b/gfx/src/core/framebuffer.c @@ -0,0 +1,151 @@ +#include "framebuffer.h" + +#include "renderbuffer.h" +#include "texture.h" + +#include + +#include + +static void framebuffer_attach_colour( + FrameBuffer* framebuffer, const FrameBufferAttachment* attachment) { + assert(framebuffer); + assert(attachment); + + switch (attachment->type) { + case FrameBufferNoAttachment: + break; + case FrameBufferTexture: + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + attachment->texture.texture->id, attachment->texture.mip_level); + break; + case FrameBufferCubemapTexture: + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + to_GL_cubemap_face(attachment->cubemap.face), + attachment->cubemap.texture->id, attachment->cubemap.mip_level); + break; + case FrameBufferRenderBuffer: + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + attachment->renderbuffer->id); + break; + } + + ASSERT_GL; +} + +static void framebuffer_attach_depth( + FrameBuffer* framebuffer, const FrameBufferAttachment* attachment) { + assert(framebuffer); + assert(attachment); + + switch (attachment->type) { + case FrameBufferNoAttachment: + break; + case FrameBufferTexture: + glFramebufferTexture2D( + GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, + attachment->texture.texture->id, attachment->texture.mip_level); + break; + // TODO: Could distinguish between colour and depth attachment types to make + // this a compile-time error. + case FrameBufferCubemapTexture: + log_error("Cannot use a cubemap texture as a depth framebuffer attachment"); + break; + case FrameBufferRenderBuffer: + glFramebufferRenderbuffer( + GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, + attachment->renderbuffer->id); + break; + } + + ASSERT_GL; +} + +bool gfx_init_framebuffer( + FrameBuffer* framebuffer, const FrameBufferDesc* desc) { + assert(framebuffer); + assert(desc); + + glGenFramebuffers(1, &framebuffer->id); + if (!framebuffer->id) { + log_error("glGenFramebuffers() failed"); + return false; + } + + // Allow incomplete framebuffers for flexibility. + // Attach buffers and check the framebuffer status only if buffers are given + // up front. + if (desc->colour.type != FrameBufferNoAttachment || + desc->depth.type != FrameBufferNoAttachment) { + // TODO: Could use the "named" API to avoid having to bind the framebuffer. + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->id); + framebuffer_attach_colour(framebuffer, &desc->colour); + framebuffer_attach_depth(framebuffer, &desc->depth); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + log_error("glCheckFramebufferStatus() failed"); + gfx_del_framebuffer(framebuffer); + return false; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } + + ASSERT_GL; + return true; +} + +bool gfx_framebuffer_attach_colour( + FrameBuffer* framebuffer, const FrameBufferAttachment* attachment) { + assert(framebuffer); + assert(attachment); + + // TODO: Could use the "named" API to avoid having to bind the framebuffer. + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->id); + framebuffer_attach_colour(framebuffer, attachment); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + log_error("glCheckFramebufferStatus() failed"); + return false; + } + return true; +} + +bool gfx_framebuffer_attach_depth( + FrameBuffer* framebuffer, const FrameBufferAttachment* attachment) { + assert(framebuffer); + assert(attachment); + + // TODO: Could use the "named" API to avoid having to bind the framebuffer. + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->id); + framebuffer_attach_depth(framebuffer, attachment); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + log_error("glCheckFramebufferStatus() failed"); + return false; + } + return true; +} + +void gfx_del_framebuffer(FrameBuffer* framebuffer) { + assert(framebuffer); + if (framebuffer->id) { + glDeleteFramebuffers(1, &framebuffer->id); + framebuffer->id = 0; + } +} + +void gfx_activate_framebuffer(const FrameBuffer* framebuffer) { + assert(framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->id); +} + +void gfx_deactivate_framebuffer(const FrameBuffer* framebuffer) { + assert(framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void gfx_framebuffer_set_viewport( + FrameBuffer* framebuffer, int x, int y, int width, int height) { + assert(framebuffer); + glViewport(x, y, width, height); +} diff --git a/gfx/src/core/framebuffer.h b/gfx/src/core/framebuffer.h new file mode 100644 index 0000000..1a3439c --- /dev/null +++ b/gfx/src/core/framebuffer.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "gl_util.h" + +typedef struct FrameBuffer { + GLuint id; +} FrameBuffer; + +/// Create a new framebuffer. +bool gfx_init_framebuffer(FrameBuffer*, const FrameBufferDesc*); + +/// Destroy the framebuffer. +void gfx_del_framebuffer(FrameBuffer*); diff --git a/gfx/src/core/geometry.c b/gfx/src/core/geometry.c new file mode 100644 index 0000000..cfc749f --- /dev/null +++ b/gfx/src/core/geometry.c @@ -0,0 +1,326 @@ +#include "geometry.h" + +#include "buffer.h" +#include "constants.h" + +#include + +#include +#include + +/// Determines whether a view is populated. +/// +/// Note that views are allowed to have no data, in which case a buffer of the +/// specified size is created. +#define view_is_populated(BUFFER_VIEW) (BUFFER_VIEW.size_bytes > 0) + +static GLenum primitive_type_to_gl(PrimitiveType type) { + switch (type) { + case Triangles: + return GL_TRIANGLES; + case TriangleFan: + return GL_TRIANGLE_FAN; + case TriangleStrip: + return GL_TRIANGLE_STRIP; + } + FAIL("primitive_type_to_gl(): missing case"); + return GL_INVALID_ENUM; +} + +/// Create a typed buffer for the buffer view if the view does not already point +/// to a buffer. +void init_view_buffer( + GfxCore* gfxcore, BufferView* view, BufferType buffer_type, + BufferUsage buffer_usage) { + if (!view->buffer) { + view->buffer = gfx_make_buffer( + gfxcore, + &(BufferDesc){ + .usage = buffer_usage, + .type = buffer_type, + .data.data = view->data, + .data.count = view->size_bytes / + gfx_get_buffer_type_size_bytes(buffer_type)}); + } + assert(view->size_bytes <= view->buffer->size_bytes); +} + +/// Configure the buffer in teh VAO. +static void configure_buffer( + GfxCore* gfxcore, const GeometryDesc* desc, BufferView* view, + size_t num_components, size_t component_size_bytes, GLenum component_type, + GLboolean normalized, GLuint channel) { + assert(gfxcore); + assert(desc); + assert(view); + assert(view->buffer); + assert( + desc->num_verts <= + view->size_bytes / (num_components * component_size_bytes)); + assert(view->size_bytes <= view->buffer->size_bytes); + + glBindBuffer(GL_ARRAY_BUFFER, view->buffer->vbo); + glEnableVertexAttribArray(channel); + if ((component_type == GL_FLOAT) || normalized) { + glVertexAttribPointer( + channel, num_components, component_type, normalized, view->stride_bytes, + (const void*)view->offset_bytes); + } else { + assert(!normalized); + assert( + (component_type == GL_BYTE) || (component_type == GL_UNSIGNED_BYTE) || + (component_type == GL_SHORT) || (component_type == GL_UNSIGNED_SHORT) || + (component_type == GL_INT) || component_type == GL_UNSIGNED_INT); + glVertexAttribIPointer( + channel, num_components, component_type, view->stride_bytes, + (const void*)view->offset_bytes); + } + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { + assert(gfxcore); + assert(desc); + + if (view_is_populated(desc->positions3d)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->positions3d, Buffer3d, desc->buffer_usage); + if (!desc->positions3d.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->positions3d, 3, sizeof(float), + GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); + } else if (view_is_populated(desc->positions2d)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->positions2d, Buffer2d, desc->buffer_usage); + if (!desc->positions2d.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->positions2d, 2, sizeof(float), + GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); + } + if (view_is_populated(desc->normals)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->normals, Buffer3d, desc->buffer_usage); + if (!desc->normals.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->normals, 3, sizeof(float), GL_FLOAT, + GL_FALSE, GFX_NORMAL_CHANNEL); + } + if (view_is_populated(desc->tangents)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->tangents, Buffer4d, desc->buffer_usage); + if (!desc->tangents.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->tangents, 4, sizeof(float), GL_FLOAT, + GL_FALSE, GFX_TANGENT_CHANNEL); + } + if (view_is_populated(desc->texcoords)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->texcoords, Buffer2d, desc->buffer_usage); + if (!desc->texcoords.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->texcoords, 2, sizeof(float), + GL_FLOAT, GL_FALSE, GFX_TEXCOORDS_CHANNEL); + } + if (view_is_populated(desc->joints.u8)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->joints.u8, BufferU8, desc->buffer_usage); + if (!desc->joints.u8.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->joints.u8, 4, sizeof(uint8_t), + GL_UNSIGNED_BYTE, GL_FALSE, GFX_JOINTS_CHANNEL); + } else if (view_is_populated(desc->joints.u16)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->joints.u16, BufferU16, desc->buffer_usage); + if (!desc->joints.u16.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->joints.u16, 4, sizeof(uint16_t), + GL_UNSIGNED_SHORT, GL_FALSE, GFX_JOINTS_CHANNEL); + } + + // If weights are given as unsigned integers, then they are normalized + // when read by the shader. + if (view_is_populated(desc->weights.u8)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->weights.u8, BufferU8, desc->buffer_usage); + if (!desc->weights.u8.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->weights.u8, 4, sizeof(uint8_t), + GL_UNSIGNED_BYTE, GL_TRUE, GFX_WEIGHTS_CHANNEL); + } else if (view_is_populated(desc->weights.u16)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->weights.u16, BufferU16, + desc->buffer_usage); + if (!desc->weights.u16.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->weights.u16, 4, sizeof(uint16_t), + GL_UNSIGNED_SHORT, GL_TRUE, GFX_WEIGHTS_CHANNEL); + } else if (view_is_populated(desc->weights.floats)) { + init_view_buffer( + gfxcore, (BufferView*)&desc->weights.floats, BufferFloat, + desc->buffer_usage); + if (!desc->weights.floats.buffer) { + return false; + } + configure_buffer( + gfxcore, desc, (BufferView*)&desc->weights.floats, 4, sizeof(float), + GL_FLOAT, GL_FALSE, GFX_WEIGHTS_CHANNEL); + } + + return true; +} + +static bool configure_indices(GfxCore* gfxcore, GeometryDesc* desc) { + assert(gfxcore); + assert(desc); + + if (view_is_populated(desc->indices8)) { + assert(desc->num_indices > 0); + assert( + desc->num_indices <= desc->indices8.size_bytes / sizeof(VertexIndex8)); + init_view_buffer( + gfxcore, (BufferView*)&desc->indices8, BufferU8, desc->buffer_usage); + if (!desc->indices8.buffer) { + return false; + } + } else if (view_is_populated(desc->indices16)) { + assert(desc->num_indices > 0); + assert( + desc->num_indices <= + desc->indices16.size_bytes / sizeof(VertexIndex16)); + init_view_buffer( + gfxcore, (BufferView*)&desc->indices16, BufferU16, desc->buffer_usage); + if (!desc->indices16.buffer) { + return false; + } + } + + return true; +} + +bool gfx_init_geometry( + Geometry* geometry, GfxCore* gfxcore, const GeometryDesc* input_desc) { + assert(geometry); + assert(gfxcore); + assert(input_desc); + assert( + view_is_populated(input_desc->positions3d) || + view_is_populated(input_desc->positions2d)); + assert(input_desc->num_verts > 0); + + geometry->mode = primitive_type_to_gl(input_desc->type); + geometry->desc = *input_desc; + geometry->num_verts = input_desc->num_verts; + geometry->gfxcore = gfxcore; + + // The geometry's copy of the descriptor is manipulated below. Create a + // shorter name for it. + GeometryDesc* desc = &geometry->desc; + + glGenVertexArrays(1, &geometry->vao); + glBindVertexArray(geometry->vao); + if (!configure_vertex_attributes(gfxcore, desc)) { + goto cleanup; + } + if (!configure_indices(gfxcore, desc)) { + goto cleanup; + } + glBindVertexArray(0); + ASSERT_GL; + + return true; + +cleanup: + gfx_del_geometry(geometry); + return 0; +} + +void gfx_del_geometry(Geometry* geometry) { + assert(geometry); + if (geometry->vao) { + glDeleteVertexArrays(1, &geometry->vao); + geometry->vao = 0; + } +} + +void gfx_update_geometry(Geometry* geometry, const GeometryDesc* desc) { + assert(geometry); + assert(desc); + // New geometry size cannot exceed original size. + assert(desc->positions3d.size_bytes <= geometry->desc.positions3d.size_bytes); + assert(desc->positions2d.size_bytes <= geometry->desc.positions2d.size_bytes); + assert(desc->normals.size_bytes <= geometry->desc.normals.size_bytes); + assert(desc->tangents.size_bytes <= geometry->desc.tangents.size_bytes); + assert(desc->texcoords.size_bytes <= geometry->desc.texcoords.size_bytes); + assert(desc->joints.u8.size_bytes <= geometry->desc.joints.u8.size_bytes); + assert(desc->joints.u16.size_bytes <= geometry->desc.joints.u16.size_bytes); + assert(desc->weights.u8.size_bytes <= geometry->desc.weights.u8.size_bytes); + assert(desc->weights.u16.size_bytes <= geometry->desc.weights.u16.size_bytes); + assert( + desc->weights.floats.size_bytes <= + geometry->desc.weights.floats.size_bytes); + + if (desc->positions3d.data) { + // The geometry must already have an underlying GPU buffer. + assert(geometry->desc.positions3d.buffer); + gfx_update_buffer( + geometry->desc.positions3d.buffer, + &(BufferDataDesc){ + .vec3s = desc->positions3d.data, + .count = desc->positions3d.size_bytes / sizeof(vec3)}); + } + // TODO: more + else { + FAIL("TODO: gfx_update_geometry() - handle other buffer types"); + } + + if (desc->num_verts != 0) { + geometry->num_verts = desc->num_verts; + } +} + +void gfx_render_geometry(const Geometry* geometry) { + assert(geometry); + assert(geometry->vao); + + const GeometryDesc* desc = &geometry->desc; + glBindVertexArray(geometry->vao); + + if (desc->indices8.buffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices8.buffer->vbo); + glDrawElements( + geometry->mode, desc->num_indices, GL_UNSIGNED_BYTE, + (const void*)desc->indices8.offset_bytes); + } else if (desc->indices16.buffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices16.buffer->vbo); + glDrawElements( + geometry->mode, desc->num_indices, GL_UNSIGNED_SHORT, + (const void*)desc->indices16.offset_bytes); + } else { + glDrawArrays(geometry->mode, 0, geometry->num_verts); + } + + glBindVertexArray(0); +} + +aabb3 gfx_get_geometry_aabb(const Geometry* geometry) { + assert(geometry); + return geometry->desc.aabb; +} diff --git a/gfx/src/core/geometry.h b/gfx/src/core/geometry.h new file mode 100644 index 0000000..c37a76f --- /dev/null +++ b/gfx/src/core/geometry.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "gl_util.h" + +#include + +/// A piece of renderable geometry. +/// +/// The Geometry does not own its buffers, since buffers are typically shared +/// to reduce the memory footprint and the number of draw calls. More generally, +/// the renderer assumes ownership of all rendering resources, which simplifies +/// their management. +typedef struct Geometry { + GLuint vao; + GLenum mode; + GeometryDesc desc; + size_t num_verts; // May differ from the initial value in the descriptor if + // the geometry is updated. + GfxCore* gfxcore; +} Geometry; + +/// Create new geometry. +bool gfx_init_geometry(Geometry*, GfxCore*, const GeometryDesc*); + +/// Destroy the geometry. +void gfx_del_geometry(Geometry*); diff --git a/gfx/src/core/gl_util.h b/gfx/src/core/gl_util.h new file mode 100644 index 0000000..a3cc629 --- /dev/null +++ b/gfx/src/core/gl_util.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +#include + +#define GFX_GL_CONTEXT_PC 1 +#define GFX_GL_CONTEXT_ES 2 + +#ifndef GFX_GL_CONTEXT +#define GFX_GL_CONTEXT GFX_GL_CONTEXT_PC +#endif // GFX_GL_CONTEXT + +/// Log an error if an OpenGL has occurred. +#ifndef NDEBUG +#define ASSERT_GL \ + { \ + GLenum e = glGetError(); \ + switch (e) { \ + case GL_NO_ERROR: \ + break; \ + case GL_INVALID_ENUM: \ + LOGE("GL_INVALID_ENUM"); \ + break; \ + case GL_INVALID_VALUE: \ + LOGE("GL_INVALID_VALUE"); \ + break; \ + case GL_INVALID_OPERATION: \ + LOGE("GL_INVALID_OPERATION"); \ + break; \ + case GL_INVALID_FRAMEBUFFER_OPERATION: \ + LOGE("GL_INVALID_FRAMEBUFFER_OPERATION"); \ + break; \ + case GL_OUT_OF_MEMORY: \ + LOGE("GL_OUT_OF_MEMORY"); \ + break; \ + /*case GL_STACK_UNDERFLOW: LOGE("GL_STACK_UNDERFLOW");*/ \ + /*case GL_STACK_OVERFLOW: LOGE("GL_STACK_OVERFLOW");*/ \ + default: \ + LOGE("Unknown OpenGL error"); \ + break; \ + } \ + } +#else // Not NDEBUG. +#define ASSERT_GL +#endif diff --git a/gfx/src/core/renderbuffer.c b/gfx/src/core/renderbuffer.c new file mode 100644 index 0000000..2753f3b --- /dev/null +++ b/gfx/src/core/renderbuffer.c @@ -0,0 +1,35 @@ +#include "renderbuffer.h" + +#include "texture.h" + +#include + +bool gfx_init_renderbuffer( + RenderBuffer* renderbuffer, const RenderBufferDesc* desc) { + assert(renderbuffer); + assert(desc); + + glGenRenderbuffers(1, &renderbuffer->id); + if (!renderbuffer->id) { + log_error("glGenRenderbuffers failed"); + return false; + } + + glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer->id); + glRenderbufferStorage( + GL_RENDERBUFFER, to_GL_internal_format(desc->texture_format), desc->width, + desc->height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + ASSERT_GL; + return true; +} + +void gfx_del_renderbuffer(RenderBuffer* renderbuffer) { + assert(renderbuffer); + + if (renderbuffer->id) { + glDeleteRenderbuffers(1, &renderbuffer->id); + renderbuffer->id = 0; + } +} diff --git a/gfx/src/core/renderbuffer.h b/gfx/src/core/renderbuffer.h new file mode 100644 index 0000000..ea11610 --- /dev/null +++ b/gfx/src/core/renderbuffer.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "gl_util.h" + +typedef struct RenderBuffer { + GLuint id; +} RenderBuffer; + +/// Create a new renderbuffer. +bool gfx_init_renderbuffer(RenderBuffer*, const RenderBufferDesc*); + +/// Destroy the renderbuffer. +void gfx_del_renderbuffer(RenderBuffer*); diff --git a/gfx/src/core/shader.c b/gfx/src/core/shader.c new file mode 100644 index 0000000..af2f89f --- /dev/null +++ b/gfx/src/core/shader.c @@ -0,0 +1,92 @@ +#include "shader.h" + +#include "gl_util.h" +#include + +#include +#include + +#include +#include + +static GLenum shader_type_to_gl(ShaderType type) { + switch (type) { + case VertexShader: + return GL_VERTEX_SHADER; + case FragmentShader: + return GL_FRAGMENT_SHADER; + } + FAIL("shader_type_to_gl(): missing case"); + return GL_INVALID_ENUM; +} + +static lstring make_defines_string(const ShaderDesc* desc) { + lstring defines = {0}; + for (size_t i = 0; i < desc->num_defines; ++i) { + const ShaderCompilerDefine* define = &desc->defines[i]; + lstring_append_cstr(&defines, "#define "); + lstring_append_cstr(&defines, sstring_cstr(&define->name)); + lstring_append_cstr(&defines, " "); + lstring_append_cstr(&defines, sstring_cstr(&define->value)); + lstring_append_cstr(&defines, "\n"); + } + return defines; +} + +/// Creates an OpenGL shader. +/// Returns non-zero on success, 0 on failure. +static GLuint create_shader(const ShaderDesc* desc) { + const GLuint shader = glCreateShader(shader_type_to_gl(desc->type)); + if (!shader) { + return 0; + } + +#if GFX_GL_CONTEXT == GFX_GL_CONTEXT_ES + const char* header = "#version 300 es\n\nprecision highp float;"; +#else + const char* header = "#version 400 core\n\n"; +#endif + + lstring defines = make_defines_string(desc); + + const char* source_bits[] = {header, lstring_cstr(&defines), desc->code}; + const GLint source_lengths[] = { + strlen(header), lstring_length(defines), strlen(desc->code)}; + + glShaderSource(shader, 3, source_bits, source_lengths); + glCompileShader(shader); + GLint result; + glGetShaderiv(shader, GL_COMPILE_STATUS, &result); + if (result == GL_FALSE) { + GLint log_len; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + char* log = calloc(log_len, sizeof(char)); + glGetShaderInfoLog(shader, log_len, NULL, log); + static const char* sep = "----------"; + LOGE("Failed loading shader: %s\n%s\n%s\n%s", log, sep, desc->code, sep); + free(log); + } else { + LOGE("Failed loading shader:\n%s", desc->code); + } + glDeleteShader(shader); + return 0; + } + ASSERT_GL; + return shader; +} + +bool gfx_compile_shader(Shader* shader, const ShaderDesc* desc) { + shader->id = create_shader(desc); + return shader->id != 0; +} + +void gfx_del_shader(Shader* shader) { + assert(shader); + + if (shader->id) { + glDeleteShader(shader->id); + shader->id = 0; + } + ASSERT_GL; +} diff --git a/gfx/src/core/shader.h b/gfx/src/core/shader.h new file mode 100644 index 0000000..b9f5679 --- /dev/null +++ b/gfx/src/core/shader.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#include "gl_util.h" + +#include + +typedef struct Shader { + GLuint id; +} Shader; + +/// Compile a new shader. +bool gfx_compile_shader(Shader*, const ShaderDesc*); + +/// Destroy the shader. +void gfx_del_shader(Shader*); diff --git a/gfx/src/core/shader_program.c b/gfx/src/core/shader_program.c new file mode 100644 index 0000000..3cbe48d --- /dev/null +++ b/gfx/src/core/shader_program.c @@ -0,0 +1,291 @@ +#include "shader_program.h" + +#include "gl_util.h" +#include "shader.h" +#include "texture.h" +#include + +#include + +#include +#include + +/// Creates an OpenGL shader program. +/// Returns non-zero on success, 0 on failure. +static GLuint create_program(GLuint vertex_shader, GLuint fragment_shader) { + const GLuint prog = glCreateProgram(); + if (prog == 0) { + LOGE("Failed creating shader program"); + return 0; + } + glAttachShader(prog, vertex_shader); + glAttachShader(prog, fragment_shader); + glLinkProgram(prog); + GLint result; + glGetProgramiv(prog, GL_LINK_STATUS, &result); + if (result == GL_FALSE) { + GLint log_len; + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + char* log = calloc(log_len, sizeof(char)); + glGetProgramInfoLog(prog, log_len, NULL, log); + LOGE("Failed creating shader program: %s", log); + free(log); + } else { + LOGE("Failed creating shader program"); + } + glDeleteProgram(prog); + return 0; + } + ASSERT_GL; + return prog; +} + +bool gfx_build_shader_program( + ShaderProgram* prog, const ShaderProgramDesc* desc) { + assert(prog); + assert(desc); + + prog->id = create_program(desc->vertex_shader->id, desc->fragment_shader->id); + return prog->id != 0; +} + +void gfx_del_shader_program(ShaderProgram* prog) { + assert(prog); + + if (prog->id) { + glDeleteProgram(prog->id); + prog->id = 0; + } + ASSERT_GL; +} + +void gfx_activate_shader_program(const ShaderProgram* prog) { + assert(prog); + glUseProgram(prog->id); + ASSERT_GL; +} + +void gfx_deactivate_shader_program(const ShaderProgram* prog) { + assert(prog); + glUseProgram(0); + ASSERT_GL; +} + +static void set_texture_uniform( + GLuint prog, const char* name, int texture_unit, const Texture* texture) { + assert(prog != 0); + assert(name); + assert(texture); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(texture->target, texture->id); + glUniform1i(location, texture_unit); + } +} + +static void set_mat4_uniform( + GLuint prog, const char* name, const mat4* mats, size_t count) { + assert(prog != 0); + assert(name); + assert(mats); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glUniformMatrix4fv(location, count, GL_FALSE, (const float*)mats); + } +} + +static void set_vec3_uniform(GLuint prog, const char* name, vec3 value) { + assert(prog != 0); + assert(name); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glUniform3f(location, value.x, value.y, value.z); + } +} + +static void set_vec4_uniform(GLuint prog, const char* name, vec4 value) { + assert(prog != 0); + assert(name); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glUniform4f(location, value.x, value.y, value.z, value.w); + } +} + +static void set_float_uniform(GLuint prog, const char* name, float value) { + assert(prog != 0); + assert(name); + + const GLint location = glGetUniformLocation(prog, name); + if (location >= 0) { + glUniform1f(location, value); + } +} + +void gfx_apply_uniforms(const ShaderProgram* prog) { + assert(prog); + + int next_texture_unit = 0; + for (int i = 0; i < prog->num_uniforms; ++i) { + const ShaderUniform* uniform = &prog->uniforms[i]; + switch (uniform->type) { + case UniformTexture: + set_texture_uniform( + prog->id, uniform->name.str, next_texture_unit, + uniform->value.texture); + next_texture_unit++; + break; + case UniformMat4: + set_mat4_uniform(prog->id, uniform->name.str, &uniform->value.mat4, 1); + break; + case UniformVec3: + set_vec3_uniform(prog->id, uniform->name.str, uniform->value.vec3); + break; + case UniformVec4: + set_vec4_uniform(prog->id, uniform->name.str, uniform->value.vec4); + break; + case UniformFloat: + set_float_uniform(prog->id, uniform->name.str, uniform->value.scalar); + break; + case UniformMat4Array: + set_mat4_uniform( + prog->id, uniform->name.str, uniform->value.array.values, + uniform->value.array.count); + break; + } + } +} + +// Get the ShaderUniform object by name from the shader program if it already +// exists, or allocate a new one otherwise. +static ShaderUniform* get_or_allocate_uniform( + ShaderProgram* prog, const char* name) { + assert(prog); + assert(name); + + // First search for the uniform in the list. + for (int i = 0; i < prog->num_uniforms; ++i) { + ShaderUniform* uniform = &prog->uniforms[i]; + if (sstring_eq_cstr(uniform->name, name)) { + return uniform; + } + } + + // Create the uniform if it does not exist. + if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) { + FAIL("Exceeded the maximum number of uniforms per shader. Please increase " + "this value."); + return 0; + } + ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms]; + prog->num_uniforms++; + return uniform; +} + +// The functions below save the value of a uniform in the shader program. If the +// uniform does not even exist, then there is no need to store the value. + +void gfx_set_texture_uniform( + ShaderProgram* prog, const char* name, const Texture* texture) { + assert(prog); + assert(name); + assert(texture); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformTexture; + uniform->value.texture = texture; +} + +void gfx_set_mat4_uniform( + ShaderProgram* prog, const char* name, const mat4* mat) { + assert(prog); + assert(name); + assert(mat); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformMat4; + uniform->value.mat4 = *mat; +} + +void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) { + assert(prog); + assert(name); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformVec3; + uniform->value.vec3 = value; +} + +void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) { + assert(prog); + assert(name); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformVec4; + uniform->value.vec4 = value; +} + +void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) { + assert(prog); + assert(name); + + // No need to store the uniform on our side if it does not exist in the + // program. + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformFloat; + uniform->value.scalar = value; +} + +void gfx_set_mat4_array_uniform( + ShaderProgram* prog, const char* name, const mat4* mats, size_t count) { + assert(prog); + assert(name); + assert(mats); + + const GLint location = glGetUniformLocation(prog->id, name); + if (location < 0) { + return; + } + ShaderUniform* uniform = get_or_allocate_uniform(prog, name); + assert(uniform); + uniform->name = sstring_make(name); + uniform->type = UniformMat4Array; + uniform->value.array.count = count; + uniform->value.array.values = mats; +} diff --git a/gfx/src/core/shader_program.h b/gfx/src/core/shader_program.h new file mode 100644 index 0000000..1443663 --- /dev/null +++ b/gfx/src/core/shader_program.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +#include "gl_util.h" + +#include + +#include + +typedef struct Texture Texture; + +typedef struct ShaderProgram { + GLuint id; + ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_SHADER]; + int num_uniforms; +} ShaderProgram; + +/// Create a new shader program. +bool gfx_build_shader_program(ShaderProgram*, const ShaderProgramDesc*); + +/// Destroy the shader program. +void gfx_del_shader_program(ShaderProgram*); diff --git a/gfx/src/core/texture.c b/gfx/src/core/texture.c new file mode 100644 index 0000000..89f7ec0 --- /dev/null +++ b/gfx/src/core/texture.c @@ -0,0 +1,218 @@ +#include "texture.h" + +#include + +#include +#include + +#include + +bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { + assert(texture); + assert(desc); + + glGenTextures(1, &texture->id); + if (!texture->id) { + log_error("glGenTextures() failed"); + return false; + } + texture->target = to_GL_dimension(desc->dimension); + glBindTexture(texture->target, texture->id); + + // glTexStorageXD + const int levels = + desc->mipmaps + ? max(max(log2(desc->width), log2(desc->height)), log2(desc->depth)) + + 1 + : 1; + const GLenum internal_format = to_GL_internal_format(desc->format); + switch (texture->target) { + case GL_TEXTURE_2D: + case GL_TEXTURE_CUBE_MAP: + glTexStorage2D( + texture->target, levels, internal_format, desc->width, desc->height); + break; + default: + FAIL("Unhandled texture dimension"); + gfx_del_texture(texture); + return false; + } + + texture->format = to_GL_format(desc->format); + texture->type = to_GL_type(desc->format); + texture->width = desc->width; + texture->height = desc->height; + gfx_update_texture(texture, &desc->data); + + // gfx_update_texture() unbinds the texture at the end, so re-bind it here. + glBindTexture(texture->target, texture->id); + + // Mipmaps. + if (desc->mipmaps) { + glGenerateMipmap(texture->target); + } + + // Texture filtering. + const bool linear = desc->filtering == LinearFiltering; + GLenum min = desc->mipmaps ? (linear ? GL_LINEAR_MIPMAP_LINEAR + : GL_NEAREST_MIPMAP_NEAREST) + : (linear ? GL_LINEAR : GL_NEAREST); + GLenum mag = linear ? GL_LINEAR : GL_NEAREST; + glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); + glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); + + // Texture wrapping. + GLenum wrap = GL_INVALID_ENUM; + switch (desc->wrap) { + case Repeat: + wrap = GL_REPEAT; + break; + case ClampToEdge: + wrap = GL_CLAMP_TO_EDGE; + break; + } + glTexParameteri(texture->target, GL_TEXTURE_WRAP_R, wrap); + glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, wrap); + glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, wrap); + + glBindTexture(texture->target, 0); + return true; +} + +void gfx_del_texture(Texture* texture) { + assert(texture); + + if (texture->id) { + glDeleteTextures(1, &texture->id); + texture->id = 0; + } +} + +void gfx_update_texture(Texture* texture, const TextureDataDesc* desc) { + assert(texture); + assert(desc); + + glBindTexture(texture->target, texture->id); + + // glTexSubImageXD + switch (texture->target) { + case GL_TEXTURE_2D: + if (desc->pixels) { + glTexSubImage2D( + GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0, + /*yoffset=*/0, texture->width, texture->height, texture->format, + texture->type, desc->pixels); + } + break; + case GL_TEXTURE_CUBE_MAP: + for (int i = 0; i < 6; ++i) { + const void* pixels = *(&desc->cubemap.pixels_pos_x + i); + if (pixels) { + glTexSubImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, /*level=*/0, /*xoffset=*/0, + /*yoffset=*/0, texture->width, texture->height, texture->format, + texture->type, pixels); + } + } + break; + default: + FAIL("Unhandled texture dimension"); + break; + } + + glBindTexture(texture->target, 0); +} + +GLenum to_GL_dimension(TextureDimension dim) { + switch (dim) { + case Texture2D: + return GL_TEXTURE_2D; + case TextureCubeMap: + return GL_TEXTURE_CUBE_MAP; + default: + FAIL("Unhandled TextureDimension"); + return GL_INVALID_ENUM; + } +} + +GLenum to_GL_internal_format(TextureFormat format) { + switch (format) { + case TextureDepth: + return GL_DEPTH_COMPONENT; + case TextureRG16: + return GL_RG16; + case TextureRG16F: + return GL_RG16F; + case TextureRGB8: + return GL_RGB8; + case TextureR11G11B10F: + return GL_R11F_G11F_B10F; + case TextureRGBA8: + return GL_RGBA8; + case TextureSRGB8: + return GL_SRGB8; + case TextureSRGBA8: + return GL_SRGB8_ALPHA8; + default: + FAIL("Unhandled TextureFormat"); + return GL_INVALID_ENUM; + } +} + +GLenum to_GL_format(TextureFormat format) { + switch (format) { + case TextureDepth: + return GL_DEPTH_COMPONENT; + case TextureRG16: + case TextureRG16F: + return GL_RG; + case TextureRGB8: + case TextureR11G11B10F: + case TextureSRGB8: + return GL_RGB; + case TextureRGBA8: + case TextureSRGBA8: + return GL_RGBA; + default: + FAIL("Unhandled TextureFormat"); + return GL_INVALID_ENUM; + } +} + +GLenum to_GL_type(TextureFormat format) { + switch (format) { + case TextureDepth: + case TextureRG16F: + case TextureR11G11B10F: + return GL_FLOAT; + case TextureRG16: + case TextureRGB8: + case TextureRGBA8: + case TextureSRGB8: + case TextureSRGBA8: + return GL_UNSIGNED_BYTE; + default: + FAIL("Unhandled TextureFormat"); + return GL_INVALID_ENUM; + } +} + +GLenum to_GL_cubemap_face(CubemapFace face) { + switch (face) { + case CubemapFacePosX: + return GL_TEXTURE_CUBE_MAP_POSITIVE_X; + case CubemapFaceNegX: + return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + case CubemapFacePosY: + return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + case CubemapFaceNegY: + return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + case CubemapFacePosZ: + return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + case CubemapFaceNegZ: + return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + default: + FAIL("Unhandled CubemapFace"); + return GL_INVALID_ENUM; + } +} diff --git a/gfx/src/core/texture.h b/gfx/src/core/texture.h new file mode 100644 index 0000000..4af41e9 --- /dev/null +++ b/gfx/src/core/texture.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "gl_util.h" + +typedef struct Texture { + GLuint id; + GLenum target; + GLenum format; + GLenum type; + int width; + int height; +} Texture; + +/// Create a new texture. +bool gfx_init_texture(Texture*, const TextureDesc*); + +/// Destroy the texture. +void gfx_del_texture(Texture*); + +/// Converts a TextureDimension into the OpenGL enum equivalent. +GLenum to_GL_dimension(TextureDimension dim); + +/// Converts a texture format into an OpenGL internal format. +GLenum to_GL_internal_format(TextureFormat format); + +/// Converts a texture format into an OpenGL format. +GLenum to_GL_format(TextureFormat format); + +/// Converts a texture format into an OpenGL type. +GLenum to_GL_type(TextureFormat format); + +/// Converts a cubemap face into the OpenGL enum equivalent. +GLenum to_GL_cubemap_face(CubemapFace face); diff --git a/gfx/src/gfx.c b/gfx/src/gfx.c index 8c513a1..36020df 100644 --- a/gfx/src/gfx.c +++ b/gfx/src/gfx.c @@ -1,14 +1,11 @@ #include #include "asset/asset_cache.h" -#include "render/core_impl.h" +#include "core/core_impl.h" #include "renderer/imm_renderer_impl.h" #include "renderer/renderer_impl.h" -#include "scene/scene_graph.h" -#include "scene/scene_impl.h" #include "scene/scene_memory.h" -#include #include #include diff --git a/gfx/src/render/buffer.c b/gfx/src/render/buffer.c deleted file mode 100644 index 3b7e4bc..0000000 --- a/gfx/src/render/buffer.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "buffer.h" - -#include -#include - -#include -#include -#include - -static size_t get_buffer_size_bytes( - BufferType type, const BufferDataDesc* desc) { - return desc->count * gfx_get_buffer_type_size_bytes(type); -} - -static GLenum get_buffer_usage(BufferUsage usage) { - switch (usage) { - case BufferStatic: - return GL_STATIC_DRAW; - case BufferDynamic: - return GL_DYNAMIC_DRAW; - } - FAIL("Unhandled buffer usage"); - return GL_STATIC_DRAW; -} - -size_t gfx_get_buffer_type_size_bytes(BufferType type) { - switch (type) { - case BufferUntyped: - return 1; - case Buffer2d: - return sizeof(vec2); - case Buffer3d: - return sizeof(vec3); - case Buffer4d: - return sizeof(vec4); - case BufferFloat: - return sizeof(float); - case BufferU8: - return sizeof(uint8_t); - case BufferU16: - return sizeof(uint16_t); - } - FAIL("Unhandled buffer type"); - return 0; -} - -bool gfx_init_buffer(Buffer* buffer, const BufferDesc* desc) { - assert(buffer); - - buffer->type = desc->type; - buffer->usage = desc->usage; - buffer->size_bytes = get_buffer_size_bytes(desc->type, &desc->data); - const GLenum usage = get_buffer_usage(desc->usage); - - glGenBuffers(1, &buffer->vbo); - glBindBuffer(GL_ARRAY_BUFFER, buffer->vbo); - glBufferData(GL_ARRAY_BUFFER, buffer->size_bytes, desc->data.data, usage); - glBindBuffer(GL_ARRAY_BUFFER, 0); - ASSERT_GL; - - return true; -} - -void gfx_del_buffer(Buffer* buffer) { - assert(buffer); - if (buffer->vbo) { - glDeleteBuffers(1, &buffer->vbo); - buffer->vbo = 0; - } -} - -void gfx_update_buffer(Buffer* buffer, const BufferDataDesc* desc) { - assert(buffer); - assert(desc); - // OpenGL allows updating static buffers, but it is not optimal for - // performance, so we enforce data in static buffers remain static. - assert(buffer->usage == BufferDynamic); - - const size_t update_size_bytes = get_buffer_size_bytes(buffer->type, desc); - assert(update_size_bytes <= buffer->size_bytes); - - glBindBuffer(GL_ARRAY_BUFFER, buffer->vbo); - glBufferSubData(GL_ARRAY_BUFFER, 0, update_size_bytes, desc->data); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} diff --git a/gfx/src/render/buffer.h b/gfx/src/render/buffer.h deleted file mode 100644 index b9080f0..0000000 --- a/gfx/src/render/buffer.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include - -#include "gl_util.h" - -#include - -#include -#include - -typedef struct Buffer { - GLuint vbo; - BufferType type; - BufferUsage usage; - size_t size_bytes; -} Buffer; - -/// Return the buffer type size in bytes. -size_t gfx_get_buffer_type_size_bytes(BufferType); - -/// Create a buffer from raw data. -bool gfx_init_buffer(Buffer*, const BufferDesc*); - -/// Destroy the buffer. -void gfx_del_buffer(Buffer*); diff --git a/gfx/src/render/constants.h b/gfx/src/render/constants.h deleted file mode 100644 index a6a3b94..0000000 --- a/gfx/src/render/constants.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -// Shaders vertex attribute locations must match the channels here. -#define GFX_POSITION_CHANNEL 0 -#define GFX_NORMAL_CHANNEL 1 -#define GFX_TANGENT_CHANNEL 2 -#define GFX_TEXCOORDS_CHANNEL 3 -#define GFX_JOINTS_CHANNEL 4 -#define GFX_WEIGHTS_CHANNEL 5 diff --git a/gfx/src/render/core.c b/gfx/src/render/core.c deleted file mode 100644 index 7a6d9cc..0000000 --- a/gfx/src/render/core.c +++ /dev/null @@ -1,414 +0,0 @@ -#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(0, 0, 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 width, int height) { - assert(gfxcore); - gfxcore->viewport.width = width; - gfxcore->viewport.height = height; -} - -void gfx_get_viewport(GfxCore* gfxcore, int* width, int* height) { - assert(gfxcore); - assert(width); - assert(height); - *width = gfxcore->viewport.width; - *height = gfxcore->viewport.height; -} - -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); - } -} diff --git a/gfx/src/render/core_impl.h b/gfx/src/render/core_impl.h deleted file mode 100644 index e27c0f2..0000000 --- a/gfx/src/render/core_impl.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include - -#include "buffer.h" -#include "framebuffer.h" -#include "geometry.h" -#include "renderbuffer.h" -#include "shader.h" -#include "shader_program.h" -#include "texture.h" - -#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) -DEF_MEMPOOL(renderbuffer_pool, RenderBuffer, GFX_MAX_NUM_RENDERBUFFERS) -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; -} Viewport; - -typedef struct GfxCore { - Viewport viewport; - // mempools for render-specific objects: textures, geometry, etc. - buffer_pool buffers; - framebuffer_pool framebuffers; - geometry_pool geometries; - renderbuffer_pool renderbuffers; - shader_pool shaders; - shader_program_pool shader_programs; - texture_pool textures; - // Caches. - ShaderCache shader_cache; - ProgramCache program_cache; -} GfxCore; - -/// Create a new render backend. -void gfx_init_gfxcore(GfxCore*); - -/// Destroy the render backend. -void gfx_del_gfxcore(GfxCore*); diff --git a/gfx/src/render/framebuffer.c b/gfx/src/render/framebuffer.c deleted file mode 100644 index 76d9002..0000000 --- a/gfx/src/render/framebuffer.c +++ /dev/null @@ -1,151 +0,0 @@ -#include "framebuffer.h" - -#include "renderbuffer.h" -#include "texture.h" - -#include - -#include - -static void framebuffer_attach_colour( - FrameBuffer* framebuffer, const FrameBufferAttachment* attachment) { - assert(framebuffer); - assert(attachment); - - switch (attachment->type) { - case FrameBufferNoAttachment: - break; - case FrameBufferTexture: - glFramebufferTexture2D( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - attachment->texture.texture->id, attachment->texture.mip_level); - break; - case FrameBufferCubemapTexture: - glFramebufferTexture2D( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - to_GL_cubemap_face(attachment->cubemap.face), - attachment->cubemap.texture->id, attachment->cubemap.mip_level); - break; - case FrameBufferRenderBuffer: - glFramebufferRenderbuffer( - GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, - attachment->renderbuffer->id); - break; - } - - ASSERT_GL; -} - -static void framebuffer_attach_depth( - FrameBuffer* framebuffer, const FrameBufferAttachment* attachment) { - assert(framebuffer); - assert(attachment); - - switch (attachment->type) { - case FrameBufferNoAttachment: - break; - case FrameBufferTexture: - glFramebufferTexture2D( - GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, - attachment->texture.texture->id, attachment->texture.mip_level); - break; - // TODO: Could distinguish between colour and depth attachment types to make - // this a compile-time error. - case FrameBufferCubemapTexture: - log_error("Cannot use a cubemap texture as a depth framebuffer attachment"); - break; - case FrameBufferRenderBuffer: - glFramebufferRenderbuffer( - GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, - attachment->renderbuffer->id); - break; - } - - ASSERT_GL; -} - -bool gfx_init_framebuffer( - FrameBuffer* framebuffer, const FrameBufferDesc* desc) { - assert(framebuffer); - assert(desc); - - glGenFramebuffers(1, &framebuffer->id); - if (!framebuffer->id) { - log_error("glGenFramebuffers() failed"); - return false; - } - - // Allow incomplete framebuffers for flexibility. - // Attach buffers and check the framebuffer status only if buffers are given - // up front. - if (desc->colour.type != FrameBufferNoAttachment || - desc->depth.type != FrameBufferNoAttachment) { - // TODO: Could use the "named" API to avoid having to bind the framebuffer. - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->id); - framebuffer_attach_colour(framebuffer, &desc->colour); - framebuffer_attach_depth(framebuffer, &desc->depth); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - log_error("glCheckFramebufferStatus() failed"); - gfx_del_framebuffer(framebuffer); - return false; - } - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - - ASSERT_GL; - return true; -} - -bool gfx_framebuffer_attach_colour( - FrameBuffer* framebuffer, const FrameBufferAttachment* attachment) { - assert(framebuffer); - assert(attachment); - - // TODO: Could use the "named" API to avoid having to bind the framebuffer. - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->id); - framebuffer_attach_colour(framebuffer, attachment); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - log_error("glCheckFramebufferStatus() failed"); - return false; - } - return true; -} - -bool gfx_framebuffer_attach_depth( - FrameBuffer* framebuffer, const FrameBufferAttachment* attachment) { - assert(framebuffer); - assert(attachment); - - // TODO: Could use the "named" API to avoid having to bind the framebuffer. - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->id); - framebuffer_attach_depth(framebuffer, attachment); - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - log_error("glCheckFramebufferStatus() failed"); - return false; - } - return true; -} - -void gfx_del_framebuffer(FrameBuffer* framebuffer) { - assert(framebuffer); - if (framebuffer->id) { - glDeleteFramebuffers(1, &framebuffer->id); - framebuffer->id = 0; - } -} - -void gfx_activate_framebuffer(const FrameBuffer* framebuffer) { - assert(framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->id); -} - -void gfx_deactivate_framebuffer(const FrameBuffer* framebuffer) { - assert(framebuffer); - glBindFramebuffer(GL_FRAMEBUFFER, 0); -} - -void gfx_framebuffer_set_viewport( - FrameBuffer* framebuffer, int x, int y, int width, int height) { - assert(framebuffer); - glViewport(x, y, width, height); -} diff --git a/gfx/src/render/framebuffer.h b/gfx/src/render/framebuffer.h deleted file mode 100644 index 1a3439c..0000000 --- a/gfx/src/render/framebuffer.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#include "gl_util.h" - -typedef struct FrameBuffer { - GLuint id; -} FrameBuffer; - -/// Create a new framebuffer. -bool gfx_init_framebuffer(FrameBuffer*, const FrameBufferDesc*); - -/// Destroy the framebuffer. -void gfx_del_framebuffer(FrameBuffer*); diff --git a/gfx/src/render/geometry.c b/gfx/src/render/geometry.c deleted file mode 100644 index cfc749f..0000000 --- a/gfx/src/render/geometry.c +++ /dev/null @@ -1,326 +0,0 @@ -#include "geometry.h" - -#include "buffer.h" -#include "constants.h" - -#include - -#include -#include - -/// Determines whether a view is populated. -/// -/// Note that views are allowed to have no data, in which case a buffer of the -/// specified size is created. -#define view_is_populated(BUFFER_VIEW) (BUFFER_VIEW.size_bytes > 0) - -static GLenum primitive_type_to_gl(PrimitiveType type) { - switch (type) { - case Triangles: - return GL_TRIANGLES; - case TriangleFan: - return GL_TRIANGLE_FAN; - case TriangleStrip: - return GL_TRIANGLE_STRIP; - } - FAIL("primitive_type_to_gl(): missing case"); - return GL_INVALID_ENUM; -} - -/// Create a typed buffer for the buffer view if the view does not already point -/// to a buffer. -void init_view_buffer( - GfxCore* gfxcore, BufferView* view, BufferType buffer_type, - BufferUsage buffer_usage) { - if (!view->buffer) { - view->buffer = gfx_make_buffer( - gfxcore, - &(BufferDesc){ - .usage = buffer_usage, - .type = buffer_type, - .data.data = view->data, - .data.count = view->size_bytes / - gfx_get_buffer_type_size_bytes(buffer_type)}); - } - assert(view->size_bytes <= view->buffer->size_bytes); -} - -/// Configure the buffer in teh VAO. -static void configure_buffer( - GfxCore* gfxcore, const GeometryDesc* desc, BufferView* view, - size_t num_components, size_t component_size_bytes, GLenum component_type, - GLboolean normalized, GLuint channel) { - assert(gfxcore); - assert(desc); - assert(view); - assert(view->buffer); - assert( - desc->num_verts <= - view->size_bytes / (num_components * component_size_bytes)); - assert(view->size_bytes <= view->buffer->size_bytes); - - glBindBuffer(GL_ARRAY_BUFFER, view->buffer->vbo); - glEnableVertexAttribArray(channel); - if ((component_type == GL_FLOAT) || normalized) { - glVertexAttribPointer( - channel, num_components, component_type, normalized, view->stride_bytes, - (const void*)view->offset_bytes); - } else { - assert(!normalized); - assert( - (component_type == GL_BYTE) || (component_type == GL_UNSIGNED_BYTE) || - (component_type == GL_SHORT) || (component_type == GL_UNSIGNED_SHORT) || - (component_type == GL_INT) || component_type == GL_UNSIGNED_INT); - glVertexAttribIPointer( - channel, num_components, component_type, view->stride_bytes, - (const void*)view->offset_bytes); - } - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -static bool configure_vertex_attributes(GfxCore* gfxcore, GeometryDesc* desc) { - assert(gfxcore); - assert(desc); - - if (view_is_populated(desc->positions3d)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->positions3d, Buffer3d, desc->buffer_usage); - if (!desc->positions3d.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->positions3d, 3, sizeof(float), - GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); - } else if (view_is_populated(desc->positions2d)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->positions2d, Buffer2d, desc->buffer_usage); - if (!desc->positions2d.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->positions2d, 2, sizeof(float), - GL_FLOAT, GL_FALSE, GFX_POSITION_CHANNEL); - } - if (view_is_populated(desc->normals)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->normals, Buffer3d, desc->buffer_usage); - if (!desc->normals.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->normals, 3, sizeof(float), GL_FLOAT, - GL_FALSE, GFX_NORMAL_CHANNEL); - } - if (view_is_populated(desc->tangents)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->tangents, Buffer4d, desc->buffer_usage); - if (!desc->tangents.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->tangents, 4, sizeof(float), GL_FLOAT, - GL_FALSE, GFX_TANGENT_CHANNEL); - } - if (view_is_populated(desc->texcoords)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->texcoords, Buffer2d, desc->buffer_usage); - if (!desc->texcoords.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->texcoords, 2, sizeof(float), - GL_FLOAT, GL_FALSE, GFX_TEXCOORDS_CHANNEL); - } - if (view_is_populated(desc->joints.u8)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->joints.u8, BufferU8, desc->buffer_usage); - if (!desc->joints.u8.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->joints.u8, 4, sizeof(uint8_t), - GL_UNSIGNED_BYTE, GL_FALSE, GFX_JOINTS_CHANNEL); - } else if (view_is_populated(desc->joints.u16)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->joints.u16, BufferU16, desc->buffer_usage); - if (!desc->joints.u16.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->joints.u16, 4, sizeof(uint16_t), - GL_UNSIGNED_SHORT, GL_FALSE, GFX_JOINTS_CHANNEL); - } - - // If weights are given as unsigned integers, then they are normalized - // when read by the shader. - if (view_is_populated(desc->weights.u8)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->weights.u8, BufferU8, desc->buffer_usage); - if (!desc->weights.u8.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->weights.u8, 4, sizeof(uint8_t), - GL_UNSIGNED_BYTE, GL_TRUE, GFX_WEIGHTS_CHANNEL); - } else if (view_is_populated(desc->weights.u16)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->weights.u16, BufferU16, - desc->buffer_usage); - if (!desc->weights.u16.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->weights.u16, 4, sizeof(uint16_t), - GL_UNSIGNED_SHORT, GL_TRUE, GFX_WEIGHTS_CHANNEL); - } else if (view_is_populated(desc->weights.floats)) { - init_view_buffer( - gfxcore, (BufferView*)&desc->weights.floats, BufferFloat, - desc->buffer_usage); - if (!desc->weights.floats.buffer) { - return false; - } - configure_buffer( - gfxcore, desc, (BufferView*)&desc->weights.floats, 4, sizeof(float), - GL_FLOAT, GL_FALSE, GFX_WEIGHTS_CHANNEL); - } - - return true; -} - -static bool configure_indices(GfxCore* gfxcore, GeometryDesc* desc) { - assert(gfxcore); - assert(desc); - - if (view_is_populated(desc->indices8)) { - assert(desc->num_indices > 0); - assert( - desc->num_indices <= desc->indices8.size_bytes / sizeof(VertexIndex8)); - init_view_buffer( - gfxcore, (BufferView*)&desc->indices8, BufferU8, desc->buffer_usage); - if (!desc->indices8.buffer) { - return false; - } - } else if (view_is_populated(desc->indices16)) { - assert(desc->num_indices > 0); - assert( - desc->num_indices <= - desc->indices16.size_bytes / sizeof(VertexIndex16)); - init_view_buffer( - gfxcore, (BufferView*)&desc->indices16, BufferU16, desc->buffer_usage); - if (!desc->indices16.buffer) { - return false; - } - } - - return true; -} - -bool gfx_init_geometry( - Geometry* geometry, GfxCore* gfxcore, const GeometryDesc* input_desc) { - assert(geometry); - assert(gfxcore); - assert(input_desc); - assert( - view_is_populated(input_desc->positions3d) || - view_is_populated(input_desc->positions2d)); - assert(input_desc->num_verts > 0); - - geometry->mode = primitive_type_to_gl(input_desc->type); - geometry->desc = *input_desc; - geometry->num_verts = input_desc->num_verts; - geometry->gfxcore = gfxcore; - - // The geometry's copy of the descriptor is manipulated below. Create a - // shorter name for it. - GeometryDesc* desc = &geometry->desc; - - glGenVertexArrays(1, &geometry->vao); - glBindVertexArray(geometry->vao); - if (!configure_vertex_attributes(gfxcore, desc)) { - goto cleanup; - } - if (!configure_indices(gfxcore, desc)) { - goto cleanup; - } - glBindVertexArray(0); - ASSERT_GL; - - return true; - -cleanup: - gfx_del_geometry(geometry); - return 0; -} - -void gfx_del_geometry(Geometry* geometry) { - assert(geometry); - if (geometry->vao) { - glDeleteVertexArrays(1, &geometry->vao); - geometry->vao = 0; - } -} - -void gfx_update_geometry(Geometry* geometry, const GeometryDesc* desc) { - assert(geometry); - assert(desc); - // New geometry size cannot exceed original size. - assert(desc->positions3d.size_bytes <= geometry->desc.positions3d.size_bytes); - assert(desc->positions2d.size_bytes <= geometry->desc.positions2d.size_bytes); - assert(desc->normals.size_bytes <= geometry->desc.normals.size_bytes); - assert(desc->tangents.size_bytes <= geometry->desc.tangents.size_bytes); - assert(desc->texcoords.size_bytes <= geometry->desc.texcoords.size_bytes); - assert(desc->joints.u8.size_bytes <= geometry->desc.joints.u8.size_bytes); - assert(desc->joints.u16.size_bytes <= geometry->desc.joints.u16.size_bytes); - assert(desc->weights.u8.size_bytes <= geometry->desc.weights.u8.size_bytes); - assert(desc->weights.u16.size_bytes <= geometry->desc.weights.u16.size_bytes); - assert( - desc->weights.floats.size_bytes <= - geometry->desc.weights.floats.size_bytes); - - if (desc->positions3d.data) { - // The geometry must already have an underlying GPU buffer. - assert(geometry->desc.positions3d.buffer); - gfx_update_buffer( - geometry->desc.positions3d.buffer, - &(BufferDataDesc){ - .vec3s = desc->positions3d.data, - .count = desc->positions3d.size_bytes / sizeof(vec3)}); - } - // TODO: more - else { - FAIL("TODO: gfx_update_geometry() - handle other buffer types"); - } - - if (desc->num_verts != 0) { - geometry->num_verts = desc->num_verts; - } -} - -void gfx_render_geometry(const Geometry* geometry) { - assert(geometry); - assert(geometry->vao); - - const GeometryDesc* desc = &geometry->desc; - glBindVertexArray(geometry->vao); - - if (desc->indices8.buffer) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices8.buffer->vbo); - glDrawElements( - geometry->mode, desc->num_indices, GL_UNSIGNED_BYTE, - (const void*)desc->indices8.offset_bytes); - } else if (desc->indices16.buffer) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, desc->indices16.buffer->vbo); - glDrawElements( - geometry->mode, desc->num_indices, GL_UNSIGNED_SHORT, - (const void*)desc->indices16.offset_bytes); - } else { - glDrawArrays(geometry->mode, 0, geometry->num_verts); - } - - glBindVertexArray(0); -} - -aabb3 gfx_get_geometry_aabb(const Geometry* geometry) { - assert(geometry); - return geometry->desc.aabb; -} diff --git a/gfx/src/render/geometry.h b/gfx/src/render/geometry.h deleted file mode 100644 index c37a76f..0000000 --- a/gfx/src/render/geometry.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -#include "gl_util.h" - -#include - -/// A piece of renderable geometry. -/// -/// The Geometry does not own its buffers, since buffers are typically shared -/// to reduce the memory footprint and the number of draw calls. More generally, -/// the renderer assumes ownership of all rendering resources, which simplifies -/// their management. -typedef struct Geometry { - GLuint vao; - GLenum mode; - GeometryDesc desc; - size_t num_verts; // May differ from the initial value in the descriptor if - // the geometry is updated. - GfxCore* gfxcore; -} Geometry; - -/// Create new geometry. -bool gfx_init_geometry(Geometry*, GfxCore*, const GeometryDesc*); - -/// Destroy the geometry. -void gfx_del_geometry(Geometry*); diff --git a/gfx/src/render/gl_util.h b/gfx/src/render/gl_util.h deleted file mode 100644 index a3cc629..0000000 --- a/gfx/src/render/gl_util.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include - -#include - -#define GFX_GL_CONTEXT_PC 1 -#define GFX_GL_CONTEXT_ES 2 - -#ifndef GFX_GL_CONTEXT -#define GFX_GL_CONTEXT GFX_GL_CONTEXT_PC -#endif // GFX_GL_CONTEXT - -/// Log an error if an OpenGL has occurred. -#ifndef NDEBUG -#define ASSERT_GL \ - { \ - GLenum e = glGetError(); \ - switch (e) { \ - case GL_NO_ERROR: \ - break; \ - case GL_INVALID_ENUM: \ - LOGE("GL_INVALID_ENUM"); \ - break; \ - case GL_INVALID_VALUE: \ - LOGE("GL_INVALID_VALUE"); \ - break; \ - case GL_INVALID_OPERATION: \ - LOGE("GL_INVALID_OPERATION"); \ - break; \ - case GL_INVALID_FRAMEBUFFER_OPERATION: \ - LOGE("GL_INVALID_FRAMEBUFFER_OPERATION"); \ - break; \ - case GL_OUT_OF_MEMORY: \ - LOGE("GL_OUT_OF_MEMORY"); \ - break; \ - /*case GL_STACK_UNDERFLOW: LOGE("GL_STACK_UNDERFLOW");*/ \ - /*case GL_STACK_OVERFLOW: LOGE("GL_STACK_OVERFLOW");*/ \ - default: \ - LOGE("Unknown OpenGL error"); \ - break; \ - } \ - } -#else // Not NDEBUG. -#define ASSERT_GL -#endif diff --git a/gfx/src/render/renderbuffer.c b/gfx/src/render/renderbuffer.c deleted file mode 100644 index 2753f3b..0000000 --- a/gfx/src/render/renderbuffer.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "renderbuffer.h" - -#include "texture.h" - -#include - -bool gfx_init_renderbuffer( - RenderBuffer* renderbuffer, const RenderBufferDesc* desc) { - assert(renderbuffer); - assert(desc); - - glGenRenderbuffers(1, &renderbuffer->id); - if (!renderbuffer->id) { - log_error("glGenRenderbuffers failed"); - return false; - } - - glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer->id); - glRenderbufferStorage( - GL_RENDERBUFFER, to_GL_internal_format(desc->texture_format), desc->width, - desc->height); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - ASSERT_GL; - return true; -} - -void gfx_del_renderbuffer(RenderBuffer* renderbuffer) { - assert(renderbuffer); - - if (renderbuffer->id) { - glDeleteRenderbuffers(1, &renderbuffer->id); - renderbuffer->id = 0; - } -} diff --git a/gfx/src/render/renderbuffer.h b/gfx/src/render/renderbuffer.h deleted file mode 100644 index ea11610..0000000 --- a/gfx/src/render/renderbuffer.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#include "gl_util.h" - -typedef struct RenderBuffer { - GLuint id; -} RenderBuffer; - -/// Create a new renderbuffer. -bool gfx_init_renderbuffer(RenderBuffer*, const RenderBufferDesc*); - -/// Destroy the renderbuffer. -void gfx_del_renderbuffer(RenderBuffer*); diff --git a/gfx/src/render/shader.c b/gfx/src/render/shader.c deleted file mode 100644 index af2f89f..0000000 --- a/gfx/src/render/shader.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "shader.h" - -#include "gl_util.h" -#include - -#include -#include - -#include -#include - -static GLenum shader_type_to_gl(ShaderType type) { - switch (type) { - case VertexShader: - return GL_VERTEX_SHADER; - case FragmentShader: - return GL_FRAGMENT_SHADER; - } - FAIL("shader_type_to_gl(): missing case"); - return GL_INVALID_ENUM; -} - -static lstring make_defines_string(const ShaderDesc* desc) { - lstring defines = {0}; - for (size_t i = 0; i < desc->num_defines; ++i) { - const ShaderCompilerDefine* define = &desc->defines[i]; - lstring_append_cstr(&defines, "#define "); - lstring_append_cstr(&defines, sstring_cstr(&define->name)); - lstring_append_cstr(&defines, " "); - lstring_append_cstr(&defines, sstring_cstr(&define->value)); - lstring_append_cstr(&defines, "\n"); - } - return defines; -} - -/// Creates an OpenGL shader. -/// Returns non-zero on success, 0 on failure. -static GLuint create_shader(const ShaderDesc* desc) { - const GLuint shader = glCreateShader(shader_type_to_gl(desc->type)); - if (!shader) { - return 0; - } - -#if GFX_GL_CONTEXT == GFX_GL_CONTEXT_ES - const char* header = "#version 300 es\n\nprecision highp float;"; -#else - const char* header = "#version 400 core\n\n"; -#endif - - lstring defines = make_defines_string(desc); - - const char* source_bits[] = {header, lstring_cstr(&defines), desc->code}; - const GLint source_lengths[] = { - strlen(header), lstring_length(defines), strlen(desc->code)}; - - glShaderSource(shader, 3, source_bits, source_lengths); - glCompileShader(shader); - GLint result; - glGetShaderiv(shader, GL_COMPILE_STATUS, &result); - if (result == GL_FALSE) { - GLint log_len; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len); - if (log_len > 0) { - char* log = calloc(log_len, sizeof(char)); - glGetShaderInfoLog(shader, log_len, NULL, log); - static const char* sep = "----------"; - LOGE("Failed loading shader: %s\n%s\n%s\n%s", log, sep, desc->code, sep); - free(log); - } else { - LOGE("Failed loading shader:\n%s", desc->code); - } - glDeleteShader(shader); - return 0; - } - ASSERT_GL; - return shader; -} - -bool gfx_compile_shader(Shader* shader, const ShaderDesc* desc) { - shader->id = create_shader(desc); - return shader->id != 0; -} - -void gfx_del_shader(Shader* shader) { - assert(shader); - - if (shader->id) { - glDeleteShader(shader->id); - shader->id = 0; - } - ASSERT_GL; -} diff --git a/gfx/src/render/shader.h b/gfx/src/render/shader.h deleted file mode 100644 index b9f5679..0000000 --- a/gfx/src/render/shader.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -#include "gl_util.h" - -#include - -typedef struct Shader { - GLuint id; -} Shader; - -/// Compile a new shader. -bool gfx_compile_shader(Shader*, const ShaderDesc*); - -/// Destroy the shader. -void gfx_del_shader(Shader*); diff --git a/gfx/src/render/shader_program.c b/gfx/src/render/shader_program.c deleted file mode 100644 index 3cbe48d..0000000 --- a/gfx/src/render/shader_program.c +++ /dev/null @@ -1,291 +0,0 @@ -#include "shader_program.h" - -#include "gl_util.h" -#include "shader.h" -#include "texture.h" -#include - -#include - -#include -#include - -/// Creates an OpenGL shader program. -/// Returns non-zero on success, 0 on failure. -static GLuint create_program(GLuint vertex_shader, GLuint fragment_shader) { - const GLuint prog = glCreateProgram(); - if (prog == 0) { - LOGE("Failed creating shader program"); - return 0; - } - glAttachShader(prog, vertex_shader); - glAttachShader(prog, fragment_shader); - glLinkProgram(prog); - GLint result; - glGetProgramiv(prog, GL_LINK_STATUS, &result); - if (result == GL_FALSE) { - GLint log_len; - glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_len); - if (log_len > 0) { - char* log = calloc(log_len, sizeof(char)); - glGetProgramInfoLog(prog, log_len, NULL, log); - LOGE("Failed creating shader program: %s", log); - free(log); - } else { - LOGE("Failed creating shader program"); - } - glDeleteProgram(prog); - return 0; - } - ASSERT_GL; - return prog; -} - -bool gfx_build_shader_program( - ShaderProgram* prog, const ShaderProgramDesc* desc) { - assert(prog); - assert(desc); - - prog->id = create_program(desc->vertex_shader->id, desc->fragment_shader->id); - return prog->id != 0; -} - -void gfx_del_shader_program(ShaderProgram* prog) { - assert(prog); - - if (prog->id) { - glDeleteProgram(prog->id); - prog->id = 0; - } - ASSERT_GL; -} - -void gfx_activate_shader_program(const ShaderProgram* prog) { - assert(prog); - glUseProgram(prog->id); - ASSERT_GL; -} - -void gfx_deactivate_shader_program(const ShaderProgram* prog) { - assert(prog); - glUseProgram(0); - ASSERT_GL; -} - -static void set_texture_uniform( - GLuint prog, const char* name, int texture_unit, const Texture* texture) { - assert(prog != 0); - assert(name); - assert(texture); - - const GLint location = glGetUniformLocation(prog, name); - if (location >= 0) { - glActiveTexture(GL_TEXTURE0 + texture_unit); - glBindTexture(texture->target, texture->id); - glUniform1i(location, texture_unit); - } -} - -static void set_mat4_uniform( - GLuint prog, const char* name, const mat4* mats, size_t count) { - assert(prog != 0); - assert(name); - assert(mats); - - const GLint location = glGetUniformLocation(prog, name); - if (location >= 0) { - glUniformMatrix4fv(location, count, GL_FALSE, (const float*)mats); - } -} - -static void set_vec3_uniform(GLuint prog, const char* name, vec3 value) { - assert(prog != 0); - assert(name); - - const GLint location = glGetUniformLocation(prog, name); - if (location >= 0) { - glUniform3f(location, value.x, value.y, value.z); - } -} - -static void set_vec4_uniform(GLuint prog, const char* name, vec4 value) { - assert(prog != 0); - assert(name); - - const GLint location = glGetUniformLocation(prog, name); - if (location >= 0) { - glUniform4f(location, value.x, value.y, value.z, value.w); - } -} - -static void set_float_uniform(GLuint prog, const char* name, float value) { - assert(prog != 0); - assert(name); - - const GLint location = glGetUniformLocation(prog, name); - if (location >= 0) { - glUniform1f(location, value); - } -} - -void gfx_apply_uniforms(const ShaderProgram* prog) { - assert(prog); - - int next_texture_unit = 0; - for (int i = 0; i < prog->num_uniforms; ++i) { - const ShaderUniform* uniform = &prog->uniforms[i]; - switch (uniform->type) { - case UniformTexture: - set_texture_uniform( - prog->id, uniform->name.str, next_texture_unit, - uniform->value.texture); - next_texture_unit++; - break; - case UniformMat4: - set_mat4_uniform(prog->id, uniform->name.str, &uniform->value.mat4, 1); - break; - case UniformVec3: - set_vec3_uniform(prog->id, uniform->name.str, uniform->value.vec3); - break; - case UniformVec4: - set_vec4_uniform(prog->id, uniform->name.str, uniform->value.vec4); - break; - case UniformFloat: - set_float_uniform(prog->id, uniform->name.str, uniform->value.scalar); - break; - case UniformMat4Array: - set_mat4_uniform( - prog->id, uniform->name.str, uniform->value.array.values, - uniform->value.array.count); - break; - } - } -} - -// Get the ShaderUniform object by name from the shader program if it already -// exists, or allocate a new one otherwise. -static ShaderUniform* get_or_allocate_uniform( - ShaderProgram* prog, const char* name) { - assert(prog); - assert(name); - - // First search for the uniform in the list. - for (int i = 0; i < prog->num_uniforms; ++i) { - ShaderUniform* uniform = &prog->uniforms[i]; - if (sstring_eq_cstr(uniform->name, name)) { - return uniform; - } - } - - // Create the uniform if it does not exist. - if (prog->num_uniforms == GFX_MAX_UNIFORMS_PER_SHADER) { - FAIL("Exceeded the maximum number of uniforms per shader. Please increase " - "this value."); - return 0; - } - ShaderUniform* uniform = &prog->uniforms[prog->num_uniforms]; - prog->num_uniforms++; - return uniform; -} - -// The functions below save the value of a uniform in the shader program. If the -// uniform does not even exist, then there is no need to store the value. - -void gfx_set_texture_uniform( - ShaderProgram* prog, const char* name, const Texture* texture) { - assert(prog); - assert(name); - assert(texture); - - const GLint location = glGetUniformLocation(prog->id, name); - if (location < 0) { - return; - } - ShaderUniform* uniform = get_or_allocate_uniform(prog, name); - assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformTexture; - uniform->value.texture = texture; -} - -void gfx_set_mat4_uniform( - ShaderProgram* prog, const char* name, const mat4* mat) { - assert(prog); - assert(name); - assert(mat); - - const GLint location = glGetUniformLocation(prog->id, name); - if (location < 0) { - return; - } - ShaderUniform* uniform = get_or_allocate_uniform(prog, name); - assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformMat4; - uniform->value.mat4 = *mat; -} - -void gfx_set_vec3_uniform(ShaderProgram* prog, const char* name, vec3 value) { - assert(prog); - assert(name); - - const GLint location = glGetUniformLocation(prog->id, name); - if (location < 0) { - return; - } - ShaderUniform* uniform = get_or_allocate_uniform(prog, name); - assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformVec3; - uniform->value.vec3 = value; -} - -void gfx_set_vec4_uniform(ShaderProgram* prog, const char* name, vec4 value) { - assert(prog); - assert(name); - - const GLint location = glGetUniformLocation(prog->id, name); - if (location < 0) { - return; - } - ShaderUniform* uniform = get_or_allocate_uniform(prog, name); - assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformVec4; - uniform->value.vec4 = value; -} - -void gfx_set_float_uniform(ShaderProgram* prog, const char* name, float value) { - assert(prog); - assert(name); - - // No need to store the uniform on our side if it does not exist in the - // program. - const GLint location = glGetUniformLocation(prog->id, name); - if (location < 0) { - return; - } - ShaderUniform* uniform = get_or_allocate_uniform(prog, name); - assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformFloat; - uniform->value.scalar = value; -} - -void gfx_set_mat4_array_uniform( - ShaderProgram* prog, const char* name, const mat4* mats, size_t count) { - assert(prog); - assert(name); - assert(mats); - - const GLint location = glGetUniformLocation(prog->id, name); - if (location < 0) { - return; - } - ShaderUniform* uniform = get_or_allocate_uniform(prog, name); - assert(uniform); - uniform->name = sstring_make(name); - uniform->type = UniformMat4Array; - uniform->value.array.count = count; - uniform->value.array.values = mats; -} diff --git a/gfx/src/render/shader_program.h b/gfx/src/render/shader_program.h deleted file mode 100644 index 1443663..0000000 --- a/gfx/src/render/shader_program.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include - -#include "gl_util.h" - -#include - -#include - -typedef struct Texture Texture; - -typedef struct ShaderProgram { - GLuint id; - ShaderUniform uniforms[GFX_MAX_UNIFORMS_PER_SHADER]; - int num_uniforms; -} ShaderProgram; - -/// Create a new shader program. -bool gfx_build_shader_program(ShaderProgram*, const ShaderProgramDesc*); - -/// Destroy the shader program. -void gfx_del_shader_program(ShaderProgram*); diff --git a/gfx/src/render/texture.c b/gfx/src/render/texture.c deleted file mode 100644 index 89f7ec0..0000000 --- a/gfx/src/render/texture.c +++ /dev/null @@ -1,218 +0,0 @@ -#include "texture.h" - -#include - -#include -#include - -#include - -bool gfx_init_texture(Texture* texture, const TextureDesc* desc) { - assert(texture); - assert(desc); - - glGenTextures(1, &texture->id); - if (!texture->id) { - log_error("glGenTextures() failed"); - return false; - } - texture->target = to_GL_dimension(desc->dimension); - glBindTexture(texture->target, texture->id); - - // glTexStorageXD - const int levels = - desc->mipmaps - ? max(max(log2(desc->width), log2(desc->height)), log2(desc->depth)) + - 1 - : 1; - const GLenum internal_format = to_GL_internal_format(desc->format); - switch (texture->target) { - case GL_TEXTURE_2D: - case GL_TEXTURE_CUBE_MAP: - glTexStorage2D( - texture->target, levels, internal_format, desc->width, desc->height); - break; - default: - FAIL("Unhandled texture dimension"); - gfx_del_texture(texture); - return false; - } - - texture->format = to_GL_format(desc->format); - texture->type = to_GL_type(desc->format); - texture->width = desc->width; - texture->height = desc->height; - gfx_update_texture(texture, &desc->data); - - // gfx_update_texture() unbinds the texture at the end, so re-bind it here. - glBindTexture(texture->target, texture->id); - - // Mipmaps. - if (desc->mipmaps) { - glGenerateMipmap(texture->target); - } - - // Texture filtering. - const bool linear = desc->filtering == LinearFiltering; - GLenum min = desc->mipmaps ? (linear ? GL_LINEAR_MIPMAP_LINEAR - : GL_NEAREST_MIPMAP_NEAREST) - : (linear ? GL_LINEAR : GL_NEAREST); - GLenum mag = linear ? GL_LINEAR : GL_NEAREST; - glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, min); - glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, mag); - - // Texture wrapping. - GLenum wrap = GL_INVALID_ENUM; - switch (desc->wrap) { - case Repeat: - wrap = GL_REPEAT; - break; - case ClampToEdge: - wrap = GL_CLAMP_TO_EDGE; - break; - } - glTexParameteri(texture->target, GL_TEXTURE_WRAP_R, wrap); - glTexParameteri(texture->target, GL_TEXTURE_WRAP_S, wrap); - glTexParameteri(texture->target, GL_TEXTURE_WRAP_T, wrap); - - glBindTexture(texture->target, 0); - return true; -} - -void gfx_del_texture(Texture* texture) { - assert(texture); - - if (texture->id) { - glDeleteTextures(1, &texture->id); - texture->id = 0; - } -} - -void gfx_update_texture(Texture* texture, const TextureDataDesc* desc) { - assert(texture); - assert(desc); - - glBindTexture(texture->target, texture->id); - - // glTexSubImageXD - switch (texture->target) { - case GL_TEXTURE_2D: - if (desc->pixels) { - glTexSubImage2D( - GL_TEXTURE_2D, /*level=*/0, /*xoffset=*/0, - /*yoffset=*/0, texture->width, texture->height, texture->format, - texture->type, desc->pixels); - } - break; - case GL_TEXTURE_CUBE_MAP: - for (int i = 0; i < 6; ++i) { - const void* pixels = *(&desc->cubemap.pixels_pos_x + i); - if (pixels) { - glTexSubImage2D( - GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, /*level=*/0, /*xoffset=*/0, - /*yoffset=*/0, texture->width, texture->height, texture->format, - texture->type, pixels); - } - } - break; - default: - FAIL("Unhandled texture dimension"); - break; - } - - glBindTexture(texture->target, 0); -} - -GLenum to_GL_dimension(TextureDimension dim) { - switch (dim) { - case Texture2D: - return GL_TEXTURE_2D; - case TextureCubeMap: - return GL_TEXTURE_CUBE_MAP; - default: - FAIL("Unhandled TextureDimension"); - return GL_INVALID_ENUM; - } -} - -GLenum to_GL_internal_format(TextureFormat format) { - switch (format) { - case TextureDepth: - return GL_DEPTH_COMPONENT; - case TextureRG16: - return GL_RG16; - case TextureRG16F: - return GL_RG16F; - case TextureRGB8: - return GL_RGB8; - case TextureR11G11B10F: - return GL_R11F_G11F_B10F; - case TextureRGBA8: - return GL_RGBA8; - case TextureSRGB8: - return GL_SRGB8; - case TextureSRGBA8: - return GL_SRGB8_ALPHA8; - default: - FAIL("Unhandled TextureFormat"); - return GL_INVALID_ENUM; - } -} - -GLenum to_GL_format(TextureFormat format) { - switch (format) { - case TextureDepth: - return GL_DEPTH_COMPONENT; - case TextureRG16: - case TextureRG16F: - return GL_RG; - case TextureRGB8: - case TextureR11G11B10F: - case TextureSRGB8: - return GL_RGB; - case TextureRGBA8: - case TextureSRGBA8: - return GL_RGBA; - default: - FAIL("Unhandled TextureFormat"); - return GL_INVALID_ENUM; - } -} - -GLenum to_GL_type(TextureFormat format) { - switch (format) { - case TextureDepth: - case TextureRG16F: - case TextureR11G11B10F: - return GL_FLOAT; - case TextureRG16: - case TextureRGB8: - case TextureRGBA8: - case TextureSRGB8: - case TextureSRGBA8: - return GL_UNSIGNED_BYTE; - default: - FAIL("Unhandled TextureFormat"); - return GL_INVALID_ENUM; - } -} - -GLenum to_GL_cubemap_face(CubemapFace face) { - switch (face) { - case CubemapFacePosX: - return GL_TEXTURE_CUBE_MAP_POSITIVE_X; - case CubemapFaceNegX: - return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; - case CubemapFacePosY: - return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; - case CubemapFaceNegY: - return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; - case CubemapFacePosZ: - return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; - case CubemapFaceNegZ: - return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; - default: - FAIL("Unhandled CubemapFace"); - return GL_INVALID_ENUM; - } -} diff --git a/gfx/src/render/texture.h b/gfx/src/render/texture.h deleted file mode 100644 index 4af41e9..0000000 --- a/gfx/src/render/texture.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include "gl_util.h" - -typedef struct Texture { - GLuint id; - GLenum target; - GLenum format; - GLenum type; - int width; - int height; -} Texture; - -/// Create a new texture. -bool gfx_init_texture(Texture*, const TextureDesc*); - -/// Destroy the texture. -void gfx_del_texture(Texture*); - -/// Converts a TextureDimension into the OpenGL enum equivalent. -GLenum to_GL_dimension(TextureDimension dim); - -/// Converts a texture format into an OpenGL internal format. -GLenum to_GL_internal_format(TextureFormat format); - -/// Converts a texture format into an OpenGL format. -GLenum to_GL_format(TextureFormat format); - -/// Converts a texture format into an OpenGL type. -GLenum to_GL_type(TextureFormat format); - -/// Converts a cubemap face into the OpenGL enum equivalent. -GLenum to_GL_cubemap_face(CubemapFace face); -- cgit v1.2.3