Testing framework. Shader creation function. More dynstring functions

This commit is contained in:
2026-07-02 11:57:35 +03:00
parent 94ea46a320
commit 4a155d66ee
12 changed files with 7388 additions and 71 deletions

View File

@@ -1,20 +1,30 @@
project('scuffedcraft', 'c')
cc = meson.get_compiler('c')
vendor_dir_abs = meson.current_source_dir() + '/vendor/'
cargs = [
'-std=c99',
'-Og',
'-g',
'-DBOUNDS_CHECK'
'-DBOUNDS_CHECK',
'-DSHADERS_PATH="../src/shaders/"'
]
dependencies = []
dependencies = [
]
link_args = []
test_link_args = []
include_dirs = [
'vendor/',
'src/'
]
test_include_dirs = [
'src/',
'vendor/'
]
if host_machine.system() == 'linux'
dependencies += [
@@ -24,6 +34,9 @@ elif host_machine.system() == 'windows'
link_args += [
'-lopengl32'
]
test_link_args += [
'-lwinpthread'
]
cargs += [
'-DUNICODE',
@@ -31,7 +44,7 @@ elif host_machine.system() == 'windows'
'-municode'
]
if build_machine.system() == 'linux'
if meson.is_cross_build() and build_machine.system() == 'linux'
link_args += [
'-Lvendor/wine/',
]
@@ -46,8 +59,26 @@ executable(
'src/DynString.c',
'src/log.c',
'src/main.c',
'src/shader.c',
c_args: cargs,
dependencies: dependencies,
link_args: link_args,
include_directories: include_directories(include_dirs)
include_directories: include_directories(include_dirs),
)
executable(
'tests',
'tests/main.c',
'src/DynString.c',
'src/log.c',
c_args: cargs,
include_directories: include_directories(test_include_dirs),
link_args: test_link_args,
dependencies: [
cc.find_library(
'libcmocka',
dirs: [ vendor_dir_abs + 'cmocka/' ]
)
]
)

View File

@@ -1,5 +1,6 @@
[binaries]
c = 'x86_64-w64-mingw32-clang'
strip = 'x86_64-w64-mingw32-strip'
exe_wrapper = 'wine'
[host_machine]

View File

@@ -1,19 +1,22 @@
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "DynString.h"
#include "log.h"
DynString DynString_alloc(const char* str, const size_t len) {
DynString dstr = {
.data = (char*) malloc(sizeof(char) * len),
.len = len
.data = (char*) malloc(sizeof(char) * len + 1),
.len = len,
.capacity = len
};
if (dstr.data == NULL) {
LOG_FATAL("Ran out of memory");
}
dstr.data = memcpy(dstr.data, str, len);
dstr.data[len] = '\0';
return dstr;
}
@@ -21,14 +24,20 @@ DynString DynString_alloc(const char* str, const size_t len) {
void DynString_free(DynString* dstr) {
if (dstr->data != NULL) {
free(dstr->data);
dstr->data = NULL;
}
dstr->len = 0;
dstr->capacity = 0;
}
bool DynString_is_null(const DynString* dstr) {
return dstr->data == NULL;
}
const char* DynString_c_str(const DynString* dstr) {
return dstr->data;
}
bool DynString_equal(const DynString* s1, const DynString* s2) {
if (s1->len != s2->len) {
return false;
@@ -47,3 +56,41 @@ bool DynString_equal(const DynString* s1, const DynString* s2) {
return true;
}
void DynString_reserve(DynString* dstr, const size_t new_capacity) {
if (dstr->capacity >= new_capacity) {
return;
}
errno = 0;
dstr->data = realloc(dstr->data, new_capacity);
if (errno != 0) {
LOG_FATAL("Ran out of memory");
}
dstr->capacity = new_capacity;
}
void DynString_concat_c_str(
DynString* dstr,
const char* str,
const size_t str_len
) {
const size_t new_len = dstr->len + str_len;
if (new_len > dstr->capacity) {
DynString_reserve(dstr, new_len + 1);
}
strncpy(dstr->data + dstr->len, str, str_len);
dstr->data[new_len] = '\0';
dstr->len = new_len;
}
DynString DynString_alloc_concat_c_strs(
const char* s1,
const size_t s1_len,
const char* s2,
const size_t s2_len
) {
DynString dstr = DynString_alloc(s1, s1_len);
DynString_concat_c_str(&dstr, s2, s2_len);
return dstr;
}

View File

@@ -9,11 +9,24 @@
typedef struct {
char* data;
size_t len;
size_t capacity;
} DynString;
DynString DynString_alloc(const char* str, const size_t len);
void DynString_free(DynString* dstr);
bool DynString_is_null(const DynString* dstr);
const char* DynString_c_str(const DynString* dstr);
bool DynString_equal(const DynString* s1, const DynString* s2);
void DynString_concat_c_str(
DynString* dstr,
const char* str,
const size_t str_len
);
DynString DynString_alloc_concat_c_strs(
const char* s1,
const size_t s1_len,
const char* s2,
const size_t s2_len
);
#endif // DYN_STRING_H_

View File

@@ -15,6 +15,7 @@
#include "log.h"
#include "pltf/gl_funcs.h"
#include "pltf/pltf.h"
#include "shader.h"
// int _main() {
// return 0;
@@ -396,69 +397,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine,
glBindVertexArray(0);
// clang-format off
const char* vertex_shader_src =
"#version 460 core\n"
"layout (location = 0) in vec3 a_pos;\n"
"void main() {\n"
" gl_Position = vec4(a_pos, 1.0);\n"
"}\n"
;
const char* fragment_shader_src =
"#version 460 core\n"
"out vec4 frag_color;\n"
"void main() {\n"
" frag_color = vec4(0.0, 0.0, 0.0, 1.0);\n"
"}\n"
;
// clang-format on
const unsigned int vert_shader = glCreateShader(GL_VERTEX_SHADER);
{
const int len = strlen(vertex_shader_src);
glShaderSource(vert_shader, 1, &vertex_shader_src, &len);
glCompileShader(vert_shader);
int compile_status = 0;
glGetShaderiv(vert_shader, GL_COMPILE_STATUS, &compile_status);
if (compile_status != GL_TRUE) {
int info_log_len = 0;
glGetShaderiv(vert_shader, GL_INFO_LOG_LENGTH, &info_log_len);
char* err_str = (char*) malloc(sizeof(char) * info_log_len);
glGetShaderInfoLog(vert_shader, info_log_len - 1, NULL, err_str);
LOG_FATALF("Failed to compile vertex shader: %s", err_str);
}
}
const unsigned int frag_shader = glCreateShader(GL_FRAGMENT_SHADER);
{
const int len = strlen(fragment_shader_src);
glShaderSource(frag_shader, 1, &fragment_shader_src, &len);
glCompileShader(frag_shader);
int compile_status = 0;
glGetShaderiv(frag_shader, GL_COMPILE_STATUS, &compile_status);
if (compile_status != GL_TRUE) {
int info_log_len = 0;
glGetShaderiv(frag_shader, GL_INFO_LOG_LENGTH, &info_log_len);
char* err_str = (char*) malloc(sizeof(char) * info_log_len);
glGetShaderInfoLog(frag_shader, info_log_len - 1, NULL, err_str);
LOG_FATALF("Failed to compile fragment shader: %s", err_str);
}
}
const unsigned int shader_program = glCreateProgram();
{
glAttachShader(shader_program, vert_shader);
glAttachShader(shader_program, frag_shader);
glLinkProgram(shader_program);
int compile_status = 0;
glGetProgramiv(shader_program, GL_LINK_STATUS, &compile_status);
if (compile_status != GL_TRUE) {
LOG_FATAL("Failed to link shader program.");
}
glDeleteShader(vert_shader);
glDeleteShader(frag_shader);
}
const unsigned int shader = shader_create(
"shader.vert",
"shader.frag"
);
MSG msg = { 0 };
while (true) {
@@ -472,7 +414,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine,
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(shader_program);
glUseProgram(shader);
glBindVertexArray(triangle_vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, triangle_vert_count);

88
src/shader.c Normal file
View File

@@ -0,0 +1,88 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include "DynString.h"
#include "pltf/gl_funcs.h"
#include "log.h"
static char read_buf[512] = { 0 };
static const size_t read_buf_len = sizeof(read_buf) / sizeof(read_buf[0]);
static DynString read_shader_source(const char* path) {
const DynString full_path = DynString_alloc_concat_c_strs(
SHADERS_PATH,
strlen(SHADERS_PATH),
path,
strlen(path)
);
DynString source = DynString_alloc("", 0);
FILE* shader_file = fopen(DynString_c_str(&full_path), "r");
if (shader_file == NULL) {
LOG_ERRORF("Failed to open file %.*s. Errno: %d", DYN_STRING_FMT(full_path), errno);
return source;
}
size_t bytes_read = 0;
do {
bytes_read = fread(read_buf, 1, read_buf_len - 1, shader_file);
read_buf[bytes_read] = '\0';
DynString_concat_c_str(&source, read_buf, bytes_read);
} while (bytes_read == read_buf_len - 1);
if (fclose(shader_file) == EOF) {
LOG_ERRORF("Failed to close file %s. Errno: %d", path, errno);
}
return source;
}
static unsigned int compile_shader(const DynString* shader_src, const GLenum shader_type) {
const unsigned int shader = glCreateShader(shader_type);
const char* shader_src_c_str = DynString_c_str(shader_src);
const int len = (int) shader_src->len;
glShaderSource(shader, 1, &shader_src_c_str, &len);
glCompileShader(shader);
int compile_status = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
if (compile_status != GL_TRUE) {
int info_log_len = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_len);
char* err_str = (char*) malloc(sizeof(char) * info_log_len);
glGetShaderInfoLog(shader, info_log_len - 1, NULL, err_str);
LOG_FATALF("Failed to compile vertex shader: %s", err_str);
}
return shader;
}
unsigned int shader_create(
const char* vertex_shader_path,
const char* fragment_shader_path
) {
DynString vert_source = read_shader_source(vertex_shader_path);
DynString frag_source = read_shader_source(fragment_shader_path);
const unsigned int vert_shader = compile_shader(&vert_source, GL_VERTEX_SHADER);
const unsigned int frag_shader = compile_shader(&frag_source, GL_FRAGMENT_SHADER);
DynString_free(&vert_source);
DynString_free(&frag_source);
const unsigned int shader_program = glCreateProgram();
glAttachShader(shader_program, vert_shader);
glAttachShader(shader_program, frag_shader);
glLinkProgram(shader_program);
int compile_status = 0;
glGetProgramiv(shader_program, GL_LINK_STATUS, &compile_status);
if (compile_status != GL_TRUE) {
LOG_FATAL("Failed to link shader program.");
}
glDeleteShader(vert_shader);
glDeleteShader(frag_shader);
return shader_program;
}

9
src/shader.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef SHADER_H_
#define SHADER_H_
unsigned int shader_create(
const char* vertex_shader_path,
const char* fragment_shader_path
);
#endif // SHADER_H_

7
src/shaders/shader.frag Normal file
View File

@@ -0,0 +1,7 @@
#version 460 core
out vec4 frag_color;
void main() {
frag_color = vec4(0.0, 0.0, 0.0, 1.0);
}

7
src/shaders/shader.vert Normal file
View File

@@ -0,0 +1,7 @@
#version 460 core
layout (location = 0) in vec3 a_pos;
void main() {
gl_Position = vec4(a_pos, 1.0);
}

80
tests/main.c Normal file
View File

@@ -0,0 +1,80 @@
#include <string.h>
#include <stdlib.h>
#include "cmocka/cmocka.h"
#include "DynString.h"
void test_DynString_alloc(void** state) {
const char* c_str = "Hello";
const size_t c_str_len = strlen(c_str);
DynString dstr = DynString_alloc(c_str, c_str_len);
assert_memory_equal(dstr.data, c_str, c_str_len + 1);
assert_true(dstr.len == c_str_len);
assert_true(dstr.capacity == c_str_len);
free(dstr.data);
}
void test_DynString_free(void** state) {
DynString dstr = DynString_alloc("Hello", strlen("Hello"));
DynString_free(&dstr);
assert_null(dstr.data);
assert_true(dstr.len == 0);
assert_true(dstr.capacity == 0);
}
void test_DynString_equal(void** state) {
DynString s1 = DynString_alloc("Hello", strlen("Hello"));
DynString s2 = DynString_alloc("Hello", strlen("Hello"));
assert_true(DynString_equal(&s1, &s2));
DynString_free(&s1);
DynString_free(&s2);
s1 = DynString_alloc("Hello", strlen("Hello"));
s2 = DynString_alloc("Hello", strlen("Hello"));
assert_true(DynString_equal(&s1, &s2));
DynString_free(&s1);
DynString_free(&s2);
}
void test_DynString_concat_c_str(void** state) {
const char* dstr_c_str = "Hello";
const char* c_str = " World";
const size_t combined_length = strlen(dstr_c_str) + strlen(c_str);
DynString dstr = DynString_alloc(dstr_c_str, strlen("Hello"));
DynString_concat_c_str(&dstr, c_str, strlen(c_str));
assert_memory_equal(dstr.data, "Hello World", strlen("Hello World"));
assert_true(dstr.len == combined_length);
assert_true(dstr.capacity == combined_length + 1);
DynString_free(&dstr);
}
void test_DynString_c_str(void** state) {
const char* c_str = "Hello";
DynString dstr = DynString_alloc(c_str, strlen(c_str));
assert_string_equal(c_str, DynString_c_str(&dstr));
DynString_concat_c_str(&dstr, " World", strlen(" World"));
assert_string_equal("Hello World", DynString_c_str(&dstr));
}
int main() {
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_DynString_alloc),
cmocka_unit_test(test_DynString_free),
cmocka_unit_test(test_DynString_equal),
cmocka_unit_test(test_DynString_concat_c_str),
cmocka_unit_test(test_DynString_c_str),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

7092
vendor/cmocka/cmocka.h vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
vendor/cmocka/libcmocka.a vendored Normal file

Binary file not shown.