summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author0scar <qgt268@alumni.ku.dk>2024-02-12 08:47:11 +0000
committer0scar <qgt268@alumni.ku.dk>2024-02-12 15:37:24 +0000
commiteba4a1312c50bbebca2c50f6566d16b41ea7fb9d (patch)
tree1b4626786dc5b65d5d1109e04207d373d87c3b7e
parent63e6c8fe178082f3ab7ff46f4c7f7216626723bf (diff)
Add shader resource management
-rw-r--r--src/rendering/include/engine/rendering/rendering.h23
-rw-r--r--src/rendering/src/gl.c134
-rw-r--r--src/resources/CMakeLists.txt1
-rw-r--r--src/resources/include/engine/resources.h95
-rw-r--r--src/resources/src/resources.c135
5 files changed, 314 insertions, 74 deletions
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 <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <glad/gl.h>
@@ -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;
+ /* Assorted asset specification, makes reloading them easier. */
+ usize assets_len;
+ asset_t* assets;
- // But with the new way:
- // usize assets_len;
- // asset_t assets*;
- Shader* shaders;
- usize shaders_len;
-} Resources;
-
-#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 <string.h>
+
+#include <engine/core/types.h>
+#include <engine/core/logging.h>
+#include <engine/core/platform.h>
+#include <engine/resources.h>
+
+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;
+}