From eba4a1312c50bbebca2c50f6566d16b41ea7fb9d Mon Sep 17 00:00:00 2001 From: 0scar Date: Mon, 12 Feb 2024 09:47:11 +0100 Subject: Add shader resource management --- src/rendering/include/engine/rendering/rendering.h | 23 +++- src/rendering/src/gl.c | 134 +++++++++++++++----- src/resources/CMakeLists.txt | 1 + src/resources/include/engine/resources.h | 95 ++++++++------- src/resources/src/resources.c | 135 +++++++++++++++++++++ 5 files changed, 314 insertions(+), 74 deletions(-) create mode 100644 src/resources/src/resources.c (limited to 'src') diff --git a/src/rendering/include/engine/rendering/rendering.h b/src/rendering/include/engine/rendering/rendering.h index 7b67248..ed066d6 100644 --- a/src/rendering/include/engine/rendering/rendering.h +++ b/src/rendering/include/engine/rendering/rendering.h @@ -28,8 +28,20 @@ typedef struct { v2_i32 coord; } Sprite; +typedef enum { + //GL_COMPUTE_SHADER, GL_VERTEX_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, GL_GEOMETRY_SHADER, GL_FRAGMENT_SHADER + Shader_Error, + Shader_Program, /* Collection of shaders */ + Shader_Vertex, + Shader_Tessellation, + Shader_Geometry, + Shader_Fragment, + Shader_Compute, +} ShaderType; + typedef struct { /* Shader proram */ + ShaderType type; u32 program; } Shader; @@ -71,7 +83,7 @@ typedef enum { RenderDrawCallType_Sprite, RenderDrawCallType_Model, } RenderDrawCallType; -// + typedef struct { RenderDrawCallType type; union { @@ -90,6 +102,13 @@ typedef struct { } data; } RenderDrawCall; -RenderObject RenderObject_new(float* model, usize sz, float* uv, usize uv_sz); +RenderObject RenderObject_new(float* model, Shader* shader, usize sz, float* uv, usize uv_sz); + +Shader compile_shader(const char* file_path, const ShaderType shader_type); +Shader compose_shader(Shader *shaders, usize shaders_len); + +u32 ComposeShader(u32 *shaders, usize shaders_len); + +ShaderType guess_shadertype_from_filename(const char *restrict fname); #endif diff --git a/src/rendering/src/gl.c b/src/rendering/src/gl.c index bef19b6..ab41f01 100644 --- a/src/rendering/src/gl.c +++ b/src/rendering/src/gl.c @@ -1,5 +1,6 @@ #include #include +#include #include @@ -11,6 +12,16 @@ extern Platform* GLOBAL_PLATFORM; +const char* ShaderType_str[] = { + [Shader_Error] = "Shader_Error", + [Shader_Program] = "Shader_Program", + [Shader_Vertex] = "Shader_Vertex", + [Shader_Tessellation] = "Shader_Tessellation", + [Shader_Geometry] = "Shader_Geometry", + [Shader_Fragment] = "Shader_Fragment", + [Shader_Compute] = "Shader_Compute", +}; + isize f_get_sz(FILE* f) { if (f == NULL) { ERROR("File was null!"); @@ -28,22 +39,37 @@ isize f_get_sz(FILE* f) { return size; } -const GLuint -compile_shader(const GladGLContext *gl, const char* file_path, const GLenum shader_type) { +Shader compile_shader(const char* file_path, const ShaderType shader_type) { GLuint shaderID = 0; + GLenum shadertype = GL_INVALID_ENUM; + + GLint Result = GL_FALSE; + int InfoLogLength; + + char* source = NULL; + FILE* file = NULL; + + const GladGLContext* gl = GLOBAL_PLATFORM->window->context; if (file_path == NULL) { WARN("Empty path to shader"); - return (GLuint)0; + return (Shader){.program = 0, .type = Shader_Error}; } - GLint Result = GL_FALSE; - int InfoLogLength; + switch (shader_type) { + case Shader_Vertex: + shadertype = GL_VERTEX_SHADER; + break; + case Shader_Fragment: + shadertype = GL_FRAGMENT_SHADER; + break; + default: break; + } - char* source; - FILE* file = fopen(file_path, "r"); + file = fopen(file_path, "r"); - shaderID = gl->CreateShader(shader_type); + shaderID = gl->CreateShader(shadertype); + LOG("CREATED SHADER ID %d", shaderID); if(file != NULL) { const i64 size = f_get_sz(file); @@ -56,12 +82,12 @@ compile_shader(const GladGLContext *gl, const char* file_path, const GLenum shad fclose(file); } else { ERROR("Cannot open \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET"\".", file_path); - return 0; + return (Shader){.program = 0, .type = Shader_Error}; } // Compile shader INFO("Compiling shader \"" TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET"\".", file_path); - char const * src_ptr = source; + char const* src_ptr = source; gl->ShaderSource(shaderID, 1, &src_ptr , NULL); gl->CompileShader(shaderID); @@ -74,31 +100,31 @@ compile_shader(const GladGLContext *gl, const char* file_path, const GLenum shad ERROR("Failed to compile shader: " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, msg); free(msg); } - free(source); + //free(source); - return shaderID; + return (Shader){.program = shaderID, .type = shader_type}; } // http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/ -GLuint LoadShaders( +GLuint load_shaders( const GladGLContext* gl, - const char * vertex_file_path, - const char * fragment_file_path) { + const char* vertex_file_path, + const char* fragment_file_path) { GLint Result = GL_FALSE; int InfoLogLength; // Create the shaders - const GLuint vertexShader = compile_shader(gl, vertex_file_path, GL_VERTEX_SHADER); - const GLuint fragmentShader = compile_shader(gl, fragment_file_path, GL_FRAGMENT_SHADER); + const Shader vertexShader = compile_shader(vertex_file_path, Shader_Vertex); + const Shader fragmentShader = compile_shader(fragment_file_path, Shader_Fragment); // Link the program INFO("Linking program"); GLuint ProgramID = gl->CreateProgram(); - if (vertex_file_path != NULL) gl->AttachShader(ProgramID, vertexShader); - if (fragment_file_path != NULL) gl->AttachShader(ProgramID, fragmentShader); + if (vertex_file_path != NULL) gl->AttachShader(ProgramID, vertexShader.program); + if (fragment_file_path != NULL) gl->AttachShader(ProgramID, fragmentShader.program); gl->LinkProgram(ProgramID); @@ -112,24 +138,64 @@ GLuint LoadShaders( free(msg); } - gl->DetachShader(ProgramID, vertexShader); - gl->DetachShader(ProgramID, fragmentShader); + gl->DetachShader(ProgramID, vertexShader.program); + gl->DetachShader(ProgramID, fragmentShader.program); - gl->DeleteShader(vertexShader); - gl->DeleteShader(fragmentShader); + gl->DeleteShader(vertexShader.program); + gl->DeleteShader(fragmentShader.program); return ProgramID; } -RenderObject RenderObject_new(float* model, usize sz, float* uv, usize uv_sz) { +/* Returns a shader program */ +Shader compose_shader(Shader *shaders, usize shaders_len) { + const GladGLContext* gl = GLOBAL_PLATFORM->window->context; + GLint Result = GL_FALSE; + int InfoLogLength = 0; + + if (shaders_len == 0) { + ERROR("No shaders provided!"); + return (Shader){.program = 0, .type = Shader_Error}; + } + + u32 prog = gl->CreateProgram(); + + for (int i = 0; i < shaders_len; i++) { + DEBUG("Attaching shader [%d] : %s (%d) = %d\n", i, ShaderType_str[shaders[i].type], shaders[i].type, shaders[i].program); + gl->AttachShader(prog, shaders[i].program); + } + + gl->LinkProgram(prog); + + // Check the program + gl->GetProgramiv(prog, GL_LINK_STATUS, &Result); + gl->GetProgramiv(prog, GL_INFO_LOG_LENGTH, &InfoLogLength); + if ( InfoLogLength > 0 ) { + char* msg = calloc(InfoLogLength + 1, sizeof(char)); + gl->GetShaderInfoLog(prog, InfoLogLength, NULL, msg); + ERROR("(Compose) Compiling shader[%d]: " TERM_COLOR_YELLOW "%s" TERM_COLOR_RESET, InfoLogLength, msg); + free(msg); + } + + for (int i = 0; i < shaders_len; i++) { + gl->DetachShader(prog, shaders[i].program); + } + + return (Shader){.program = prog, .type = Shader_Program}; +} + +RenderObject RenderObject_new(float* model, Shader* shader, usize sz, float* uv, usize uv_sz) { GladGLContext *gl = GLOBAL_PLATFORM->window->context; RenderObject o; + //DEBUG("RenderObject got %d: %s\n", shader->program, ShaderType_str[shader->type]); + // TODO: implement index buffer! gl->GenVertexArrays(1, &(o.vao)); gl->BindVertexArray(o.vao); + /* For each buffer in the shader, */ gl->GenBuffers(1, &(o.vbo)); gl->BindBuffer(GL_ARRAY_BUFFER, o.vbo); gl->BufferData(GL_ARRAY_BUFFER, sz, model, GL_STATIC_DRAW); @@ -137,11 +203,23 @@ RenderObject RenderObject_new(float* model, usize sz, float* uv, usize uv_sz) { gl->GenBuffers(1, &(o.col)); gl->BindBuffer(GL_ARRAY_BUFFER, o.col); gl->BufferData(GL_ARRAY_BUFFER, uv_sz, uv, GL_STATIC_DRAW); - //gl->GenBuffers(1, &(o.ibo)); - //gl->BindBuffer(GL_ARRAY_BUFFER, o.ibo); - //gl->BufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW); - o.shader.program = LoadShaders(gl, "shader.vert", "shader.frag"); + o.shader = *shader; return o; } + +ShaderType guess_shadertype_from_filename(const char *restrict fname) { + u32 stype = 0; + const usize path_len = strlen(fname); + + if (path_len <= 4) { + ERROR("Unable to determine shader type from suffix! (%s)", fname); + return Shader_Error; + } + + if (!strncmp(".vert", &fname[path_len - 5], 5)) { return Shader_Vertex; } + if (!strncmp(".frag", &fname[path_len - 5], 5)) { return Shader_Fragment; } + + return Shader_Error; +} diff --git a/src/resources/CMakeLists.txt b/src/resources/CMakeLists.txt index 0c14524..6cb68d3 100644 --- a/src/resources/CMakeLists.txt +++ b/src/resources/CMakeLists.txt @@ -6,6 +6,7 @@ #FetchContent_MakeAvailable(assimp) add_library(daw_resources + src/resources.c src/fonts.c src/scripts.c src/textures.c diff --git a/src/resources/include/engine/resources.h b/src/resources/include/engine/resources.h index e151d7e..cefae24 100644 --- a/src/resources/include/engine/resources.h +++ b/src/resources/include/engine/resources.h @@ -6,8 +6,8 @@ // TODO /* We need some "global resources", available to all states. - * These are resources such as common fonts, GUI frames, button background - * images. + * These are resources used throughout the applications lifetime, such as common + * fonts, GUI frames, button background images. * * We need to define state-specific resources as well. * - Can both be defined alike? @@ -16,81 +16,84 @@ * font? * - Then declare to the engine, in the main function for the game, that these * resources are to be made available throughout? - * - Make all resource specifications A UNION?! 🤯 + * - Shaders, oh boy oh shaders. + * We need to make some meta-shader declaration, so we can declare a set of + * shaders, that are used to link with other shaders, s.t. we can free up the + * shaders after compilation. + * - Make all resource specifications a union. * */ enum Asset { Asset_error, + Asset_audio, Asset_font, + Asset_shader, + Asset_shaderprog, Asset_texture, - Asset_audio, }; typedef struct { enum Asset type; - const char* font_path; - i32 ptsize; + const char* path; +} Asset_AudioSpec; + +typedef struct { + enum Asset type; + const char* path; } Asset_FontSpec; +// if a shader is declared GLOBALLY, we should not destroy it when done with the +// "main" shader compilations. typedef struct { enum Asset type; + // Assume shader type from filename const char* path; - i32 width; - i32 height; -} Asset_TextureSpec; +} Asset_ShaderSpec; + +// Use list of gluint shader program ids to link against. This translates to a +// call to compose_shader. +typedef struct { + enum Asset type; + const u32* shader; + const usize shader_len; +} Asset_ShaderProgramSpec; typedef struct { enum Asset type; const char* path; -} Asset_AudioSpec; + i32 width; + i32 height; +} Asset_TextureSpec; typedef union { enum Asset type; + Asset_AudioSpec audio; Asset_FontSpec font; + Asset_ShaderSpec shader; + Asset_ShaderProgramSpec shaderprog; Asset_TextureSpec texture; - Asset_AudioSpec audio; } asset_t; // The resource spec typedef struct { - // Was: -// usize textures_len; -// usize textures_size; -// usize fonts_len; -// -// usize texturepaths_len; -// usize fontpaths_len; -// -// /* Paths for our sources, kept in case the user wants to reload them */ -// Asset_TextureSpec** texture_paths; -// Asset_FontSpec** font_paths; -// -// /* Our actual sources */ -// Texture** textures; -// //TTF_Font** fonts; - - // But with the new way: - // usize assets_len; - // asset_t assets*; - Shader* shaders; - usize shaders_len; -} Resources; + /* Assorted asset specification, makes reloading them easier. */ + usize assets_len; + asset_t* assets; -#define Resource_FontDefinition(_path, _fontsize) \ - (const Asset_FontSpec) { \ - .type = Asset_font, .font_path = _path, .ptsize = _fontsize \ - } + /* Translation from `assets`'s indices to type-specific loaded assets: */ + usize* get; // Let r=Resources, then use as: `r.shader[ r.get[ MyShader ] ]` -#define Resource_TextureAtlasDefinition(_path, _subtexture_width, \ - _subtexture_height) \ - (const Asset_TextureSpec) { \ - .type = Asset_texture, .width = _subtexture_width, \ - .height = _subtexture_height, .path = _path \ - } + /* Loaded assets */ + usize shader_len; + Shader* shader; +} Resources; #define TextureDefinition(_path, ...) unimplemented - #define Resource_AudioDefinition(_path, ...) unimplemented +#define Declare_Shader(_path) (const asset_t){.shader = {.type = Asset_shader, .path=_path}} +#define Declare_ShaderProgram(SHADERS, NUMSHADERS) (const asset_t){.shaderprog = {.type = Asset_shaderprog, .shader=SHADERS, .shader_len=NUMSHADERS}} + +void* get_asset(Resources* r, u32 idx); /* Each of resource_load_font, resource_load_texture, and resource_load_audio * loads a given resource into the engines memory and returns an identifier. @@ -99,6 +102,10 @@ isize resource_load_font(Asset_FontSpec font_def); isize resource_load_texture(Asset_TextureSpec texture_def); isize resource_load_audio(Asset_AudioSpec audio_def); +// Loads all resources specified in `assets` and populates corresponding +// resources members. +i32 resources_load(Resources *resources); + /* Makes a resource globally available. This must be called **BEFORE** any call * to `engine_run` */ isize resource_make_global(isize resource_id); diff --git a/src/resources/src/resources.c b/src/resources/src/resources.c new file mode 100644 index 0000000..99f6f99 --- /dev/null +++ b/src/resources/src/resources.c @@ -0,0 +1,135 @@ +#include + +#include +#include +#include +#include + +extern Platform* GLOBAL_PLATFORM; + +extern const char* ShaderType_str[]; + +void* get_asset(Resources* r, u32 idx) { + switch (r->assets[idx].type) { + case Asset_shader: + // The shaders used to compile shader programs are not preserved. + WARN("We don't store pure shaders"); + break; + + case Asset_shaderprog: + LOG("Idx: r->get[idx] = %d", r->get[idx]); + LOG("Ptr: &r->shader[r->get[idx]] = %z", &r->shader[r->get[idx]]); + return &r->shader[r->get[idx]]; + + case Asset_texture: + case Asset_error: + case Asset_audio: + case Asset_font: + default: + ERROR("Asset type Not implemented"); + break; + } + return NULL; +} + +i32 resources_load(Resources *resources) { + resources->get = calloc(resources->assets_len, sizeof(usize)); + isize audio_len = 0; + isize font_len = 0; + isize shader_len = 0; + isize shaderprog_len = 0; + isize texture_len = 0; + + isize i = 0; + + for (i = 0; i < resources->assets_len; i++) { + isize idx = 0; + + switch (resources->assets[i].type) { + case Asset_audio: idx = audio_len++; WARN("Audio resource type not implemented!"); break; + case Asset_font: idx = font_len++; WARN("Font resource type not implemented!"); break; + case Asset_shader: idx = shader_len++; break; + case Asset_shaderprog: idx = shaderprog_len++; break; + case Asset_texture: idx = texture_len++; WARN("Texture resource type not implemented!");break; + + case Asset_error: + default: + ERROR("Unknown resource type!"); + exit(EXIT_FAILURE); + break; + } + + resources->get[i] = idx; + } + + //resources->audio = calloc(audio_len, sizeof()); + //resources->font = calloc(font_len, sizeof()); + resources->shader = calloc(shaderprog_len, sizeof(Shader)); + //resources->texture = calloc(texture_len, sizeof()); + + Shader* imm_shader = calloc(shader_len, sizeof(Shader)); + + audio_len = 0; + font_len = 0; + shader_len = 0; + shaderprog_len = 0; + texture_len = 0; + + for (i = 0; i < resources->assets_len; i++) { + isize idx = 0; + + switch (resources->assets[i].type) { + case Asset_audio: + //resources->audio_len++; + WARN("Audio resource type not implemented!"); + break; + case Asset_font: + //resources->font_len++; + WARN("Font resource type not implemented!"); + break; + case Asset_shader: { + ShaderType t = + guess_shadertype_from_filename(resources->assets[i].shader.path); + const Shader s = compile_shader(resources->assets[i].shader.path, t); + LOG("Compiled %s! (%s)", resources->assets[i].shader.path, ShaderType_str[t]); + imm_shader[shader_len++] = s; + } break; + case Asset_shaderprog: { + const isize sz = resources->assets[i].shaderprog.shader_len; + Shader* shaders = calloc(sz, sizeof(Shader)); + + for (int j = 0; j < sz; j++) { + //DEBUG("shader[%d] = %d\n", j, imm_shader[resources->assets[i].shaderprog.shader[j]].program); + //shaders[j] = imm_shader[resources->assets[i].shaderprog.shader[j]]; + shaders[j] = imm_shader[j]; + DEBUG("shader[%d] = %d -- %s\n", j, shaders[j].program, ShaderType_str[shaders[j].type]); + } + const Shader s = compose_shader(shaders, sz); + DEBUG("shader = %d -- %s\n", s.program, ShaderType_str[s.type]); + + resources->shader[resources->shader_len++] = s; + } break; + case Asset_texture: + texture_len++; + WARN("Texture resource type not implemented!"); + break; + + case Asset_error: + default: + ERROR("Unknown resource type!"); + exit(EXIT_FAILURE); + break; + } + + resources->get[i] = idx; + } + + free(imm_shader); + + return 0; +} + +isize resource_make_global(isize resource_id) { + ERROR("`resource_make_global` Not implemented"); + return -1; +} -- cgit v1.3