diff options
author | gramanas <anastasis.gramm2@gmail.com> | 2024-05-31 21:00:57 +0300 |
---|---|---|
committer | gramanas <anastasis.gramm2@gmail.com> | 2024-05-31 21:00:57 +0300 |
commit | a5a82709028c475d0ff6cd54f6d07f16375e156e (patch) | |
tree | db1386b3e5af7075ee6e5c45c3a4964cdecdc9d2 /src | |
parent | 1f6538bcaf379cb3fe257a0053e4e7567f8ddb98 (diff) | |
download | cgame-a5a82709028c475d0ff6cd54f6d07f16375e156e.tar.gz cgame-a5a82709028c475d0ff6cd54f6d07f16375e156e.tar.bz2 cgame-a5a82709028c475d0ff6cd54f6d07f16375e156e.zip |
Allaboard
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 8 | ||||
-rw-r--r-- | src/b.h | 860 | ||||
-rw-r--r-- | src/render.c | 518 | ||||
-rw-r--r-- | src/shader.frag | 73 | ||||
-rw-r--r-- | src/shader.vert | 4 | ||||
-rw-r--r-- | src/state.h | 39 | ||||
-rw-r--r-- | src/vksetup.h | 172 |
7 files changed, 1540 insertions, 134 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 30ca37f..d2f2f8f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,6 +2,10 @@ bin_PROGRAMS = game common_sources = state.h +debug_flags = -O2 -DVKDEBUG +release_flags = -O3 +common_flags = $(release_flags) -Wall -Wextra -march=native -fno-math-errno -funroll-loops -flto -pthread -ggdb -Wno-string-plus-int + AM_LDFLAGS = -fuse-ld=lld -flto # -fwhole-program allows cross-file inlining, but only works when you put all @@ -14,10 +18,10 @@ AM_LDFLAGS = -fuse-ld=lld -flto # -fno-math-errno and stuff like that, without enabling # -funsafe-math-optimizations. Some FP code can get big speedups from # fast-math, like auto-vectorization. -AM_CFLAGS = -march=native -fno-math-errno -funroll-loops -flto -pthread -ggdb -DVKDEBUG +AM_CFLAGS = $(common_flags) # AM_CFLAGS = -O0 -march=native -fno-math-errno -funroll-loops -flto -pthread -ggdb -fsanitize=address -AM_CXXFLAGS = -Wall -Wextra -O2 -g -std=c++17 +AM_CXXFLAGS = $(common_flags) -std=c++17 game_SOURCES = render.c cplusplus.cpp $(common_sources) game_LDADD = -lSDL2 -lm -lvulkan -lshaderc_shared -lstdc++ @@ -0,0 +1,860 @@ +// This is an attempt on build library for building C with C akin to nob.h from +// tsoding which is itself based on https://github.com/tsoding/nobuild +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef B_H_ +#define B_H_ + +#define B_ASSERT assert +#define B_REALLOC realloc +#define B_FREE free + +#include <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> + +#define B_ARRAY_LEN(array) (sizeof(array)/sizeof(array[0])) +#define B_ARRAY_GET(array, index) \ + (B_ASSERT(index >= 0), B_ASSERT(index < B_ARRAY_LEN(array)), array[index]) + +typedef enum { + B_INFO, + B_WARNING, + B_ERROR, +} B_Log_Level; + +void b_log(B_Log_Level level, const char *fmt, ...); + +// It is an equivalent of shift command from bash. It basically pops a command line +// argument from the beginning. +char *b_shift_args(int *argc, char ***argv); + +typedef struct { + const char **items; + size_t count; + size_t capacity; +} B_File_Paths; + +typedef enum { + B_FILE_REGULAR = 0, + B_FILE_DIRECTORY, + B_FILE_SYMLINK, + B_FILE_OTHER, +} B_File_Type; + +bool b_mkdir_if_not_exists(const char *path); +bool b_copy_file(const char *src_path, const char *dst_path); +bool b_copy_directory_recursively(const char *src_path, const char *dst_path); +bool b_read_entire_dir(const char *parent, B_File_Paths *children); +bool b_write_entire_file(const char *path, const void *data, size_t size); +B_File_Type b_get_file_type(const char *path); + +#define b_return_defer(value) do { result = (value); goto defer; } while(0) + +// Initial capacity of a dynamic array +#define B_DA_INIT_CAP 256 + +// Append an item to a dynamic array +#define b_da_append(da, item) \ + do { \ + if ((da)->count >= (da)->capacity) { \ + (da)->capacity = (da)->capacity == 0 ? B_DA_INIT_CAP : (da)->capacity*2; \ + (da)->items = B_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ + B_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ + } \ + \ + (da)->items[(da)->count++] = (item); \ + } while (0) + +#define b_da_free(da) B_FREE((da).items) + +// Append several items to a dynamic array +#define b_da_append_many(da, new_items, new_items_count) \ + do { \ + if ((da)->count + (new_items_count) > (da)->capacity) { \ + if ((da)->capacity == 0) { \ + (da)->capacity = B_DA_INIT_CAP; \ + } \ + while ((da)->count + (new_items_count) > (da)->capacity) { \ + (da)->capacity *= 2; \ + } \ + (da)->items = B_REALLOC((da)->items, (da)->capacity*sizeof(*(da)->items)); \ + B_ASSERT((da)->items != NULL && "Buy more RAM lol"); \ + } \ + memcpy((da)->items + (da)->count, (new_items), (new_items_count)*sizeof(*(da)->items)); \ + (da)->count += (new_items_count); \ + } while (0) + +typedef struct { + char *items; + size_t count; + size_t capacity; +} B_String_Builder; + +bool b_read_entire_file(const char *path, B_String_Builder *sb); + +// Append a sized buffer to a string builder +#define b_sb_append_buf(sb, buf, size) b_da_append_many(sb, buf, size) + +// Append a NULL-terminated string to a string builder +#define b_sb_append_cstr(sb, cstr) \ + do { \ + const char *s = (cstr); \ + size_t n = strlen(s); \ + b_da_append_many(sb, s, n); \ + } while (0) + +// Append a single NULL character at the end of a string builder. So then you can +// use it a NULL-terminated C string +#define b_sb_append_null(sb) b_da_append_many(sb, "", 1) + +// Free the memory allocated by a string builder +#define b_sb_free(sb) B_FREE((sb).items) + +// Process handle +typedef int B_Proc; +#define B_INVALID_PROC (-1) + +typedef struct { + B_Proc *items; + size_t count; + size_t capacity; +} B_Procs; + +bool b_procs_wait(B_Procs procs); + +// Wait until the process has finished +bool b_proc_wait(B_Proc proc); + +// A command - the main workhorse of B. B is all about building commands an running them +typedef struct { + const char **items; + size_t count; + size_t capacity; +} B_Cmd; + +// Render a string representation of a command into a string builder. Keep in mind the the +// string builder is not NULL-terminated by default. Use b_sb_append_null if you plan to +// use it as a C string. +void b_cmd_render(B_Cmd cmd, B_String_Builder *render); + +#define b_cmd_append(cmd, ...) \ + b_da_append_many(cmd, ((const char*[]){__VA_ARGS__}), (sizeof((const char*[]){__VA_ARGS__})/sizeof(const char*))) + +// Free all the memory allocated by command arguments +#define b_cmd_free(cmd) B_FREE(cmd.items) + +// Run command asynchronously +B_Proc b_cmd_run_async(B_Cmd cmd); + +// Run command synchronously +bool b_cmd_run_sync(B_Cmd cmd); + +#ifndef B_TEMP_CAPACITY +#define B_TEMP_CAPACITY (8*1024*1024) +#endif // B_TEMP_CAPACITY +char *b_temp_strdup(const char *cstr); +void *b_temp_alloc(size_t size); +char *b_temp_sprintf(const char *format, ...); +void b_temp_reset(void); +size_t b_temp_save(void); +void b_temp_rewind(size_t checkpoint); + +int is_path1_modified_after_path2(const char *path1, const char *path2); +bool b_rename(const char *old_path, const char *new_path); +int b_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count); +int b_needs_rebuild1(const char *output_path, const char *input_path); +int b_file_exists(const char *file_path); + +// TODO: add MinGW support for Go Rebuild Urself™ Technology +#ifndef B_REBUILD_URSELF +# if _WIN32 +# if defined(__GNUC__) +# define B_REBUILD_URSELF(binary_path, source_path) "gcc", "-o", binary_path, source_path +# elif defined(__clang__) +# define B_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path +# elif defined(_MSC_VER) +# define B_REBUILD_URSELF(binary_path, source_path) "cl.exe", b_temp_sprintf("/Fe:%s", (binary_path)), source_path +# endif +# else +# if defined(__clang__) +# define B_REBUILD_URSELF(binary_path, source_path) "clang", "-o", binary_path, source_path +# else +# define B_REBUILD_URSELF(binary_path, source_path) "cc", "-o", binary_path, source_path +# endif +# endif +#endif + +// Go Rebuild Urself™ Technology +// +// How to use it: +// int main(int argc, char** argv) { +// GO_REBUILD_URSELF(argc, argv); +// // actual work +// return 0; +// } +// +// After your added this macro every time you run ./build it will detect +// that you modified its original source code and will try to rebuild itself +// before doing any actual work. So you only need to bootstrap your build system +// once. +// +// The modification is detected by comparing the last modified times of the executable +// and its source code. The same way the make utility usually does it. +// +// The rebuilding is done by using the REBUILD_URSELF macro which you can redefine +// if you need a special way of bootstraping your build system. (which I personally +// do not recommend since the whole idea of build is to keep the process of bootstrapping +// as simple as possible and doing all of the actual work inside of the build) +// +#define B_GO_REBUILD_URSELF(argc, argv) \ + do { \ + const char *source_path = __FILE__; \ + assert(argc >= 1); \ + const char *binary_path = argv[0]; \ + \ + int rebuild_is_needed = b_needs_rebuild(binary_path, &source_path, 1); \ + if (rebuild_is_needed < 0) exit(1); \ + if (rebuild_is_needed) { \ + B_String_Builder sb = {0}; \ + b_sb_append_cstr(&sb, binary_path); \ + b_sb_append_cstr(&sb, ".old"); \ + b_sb_append_null(&sb); \ + \ + if (!b_rename(binary_path, sb.items)) exit(1); \ + B_Cmd rebuild = {0}; \ + b_cmd_append(&rebuild, B_REBUILD_URSELF(binary_path, source_path)); \ + bool rebuild_succeeded = b_cmd_run_sync(rebuild); \ + b_cmd_free(rebuild); \ + if (!rebuild_succeeded) { \ + b_rename(sb.items, binary_path); \ + exit(1); \ + } \ + \ + B_Cmd cmd = {0}; \ + b_da_append_many(&cmd, argv, argc); \ + if (!b_cmd_run_sync(cmd)) exit(1); \ + exit(0); \ + } \ + } while(0) +// The implementation idea is stolen from https://github.com/zhiayang/nabs + +typedef struct { + size_t count; + const char *data; +} B_String_View; + +const char *b_temp_sv_to_cstr(B_String_View sv); + +B_String_View b_sv_chop_by_delim(B_String_View *sv, char delim); +B_String_View b_sv_trim(B_String_View sv); +bool b_sv_eq(B_String_View a, B_String_View b); +B_String_View b_sv_from_cstr(const char *cstr); +B_String_View b_sv_from_parts(const char *data, size_t count); + +// printf macros for String_View +#ifndef SV_Fmt +#define SV_Fmt "%.*s" +#endif // SV_Fmt +#ifndef SV_Arg +#define SV_Arg(sv) (int) (sv).count, (sv).data +#endif // SV_Arg +// USAGE: +// String_View name = ...; +// printf("Name: "SV_Fmt"\n", SV_Arg(name)); + +#endif // B_H_ + +#ifdef B_IMPLEMENTATION + +static size_t b_temp_size = 0; +static char b_temp[B_TEMP_CAPACITY] = {0}; + +bool b_mkdir_if_not_exists(const char *path) +{ + int result = mkdir(path, 0755); + if (result < 0) { + if (errno == EEXIST) { + b_log(B_INFO, "directory `%s` already exists", path); + return true; + } + b_log(B_ERROR, "could not create directory `%s`: %s", path, strerror(errno)); + return false; + } + + b_log(B_INFO, "created directory `%s`", path); + return true; +} + +bool b_copy_file(const char *src_path, const char *dst_path) +{ + b_log(B_INFO, "copying %s -> %s", src_path, dst_path); + + int src_fd = -1; + int dst_fd = -1; + size_t buf_size = 32*1024; + char *buf = B_REALLOC(NULL, buf_size); + B_ASSERT(buf != NULL && "Buy more RAM lol!!"); + bool result = true; + + src_fd = open(src_path, O_RDONLY); + if (src_fd < 0) { + b_log(B_ERROR, "Could not open file %s: %s", src_path, strerror(errno)); + b_return_defer(false); + } + + struct stat src_stat; + if (fstat(src_fd, &src_stat) < 0) { + b_log(B_ERROR, "Could not get mode of file %s: %s", src_path, strerror(errno)); + b_return_defer(false); + } + + dst_fd = open(dst_path, O_CREAT | O_TRUNC | O_WRONLY, src_stat.st_mode); + if (dst_fd < 0) { + b_log(B_ERROR, "Could not create file %s: %s", dst_path, strerror(errno)); + b_return_defer(false); + } + + for (;;) { + ssize_t n = read(src_fd, buf, buf_size); + if (n == 0) break; + if (n < 0) { + b_log(B_ERROR, "Could not read from file %s: %s", src_path, strerror(errno)); + b_return_defer(false); + } + char *buf2 = buf; + while (n > 0) { + ssize_t m = write(dst_fd, buf2, n); + if (m < 0) { + b_log(B_ERROR, "Could not write to file %s: %s", dst_path, strerror(errno)); + b_return_defer(false); + } + n -= m; + buf2 += m; + } + } + +defer: + free(buf); + close(src_fd); + close(dst_fd); + return result; +} + +void b_cmd_render(B_Cmd cmd, B_String_Builder *render) +{ + for (size_t i = 0; i < cmd.count; ++i) { + const char *arg = cmd.items[i]; + if (arg == NULL) break; + if (i > 0) b_sb_append_cstr(render, " "); + if (!strchr(arg, ' ')) { + b_sb_append_cstr(render, arg); + } else { + b_da_append(render, '\''); + b_sb_append_cstr(render, arg); + b_da_append(render, '\''); + } + } +} + +B_Proc b_cmd_run_async(B_Cmd cmd) +{ + if (cmd.count < 1) { + b_log(B_ERROR, "Could not run empty command"); + return B_INVALID_PROC; + } + + B_String_Builder sb = {0}; + b_cmd_render(cmd, &sb); + b_sb_append_null(&sb); + b_log(B_INFO, "CMD: %s", sb.items); + b_sb_free(sb); + memset(&sb, 0, sizeof(sb)); + + pid_t cpid = fork(); + if (cpid < 0) { + b_log(B_ERROR, "Could not fork child process: %s", strerror(errno)); + return B_INVALID_PROC; + } + + if (cpid == 0) { + // NOTE: This leaks a bit of memory in the child process. + // But do we actually care? It's a one off leak anyway... + B_Cmd cmd_null = {0}; + b_da_append_many(&cmd_null, cmd.items, cmd.count); + b_cmd_append(&cmd_null, NULL); + + if (execvp(cmd.items[0], (char * const*) cmd_null.items) < 0) { + b_log(B_ERROR, "Could not exec child process: %s", strerror(errno)); + exit(1); + } + B_ASSERT(0 && "unreachable"); + } + + return cpid; +} + +bool b_procs_wait(B_Procs procs) +{ + bool success = true; + for (size_t i = 0; i < procs.count; ++i) { + success = b_proc_wait(procs.items[i]) && success; + } + return success; +} + +bool b_proc_wait(B_Proc proc) +{ + if (proc == B_INVALID_PROC) return false; + + for (;;) { + int wstatus = 0; + if (waitpid(proc, &wstatus, 0) < 0) { + b_log(B_ERROR, "could not wait on command (pid %d): %s", proc, strerror(errno)); + return false; + } + + if (WIFEXITED(wstatus)) { + int exit_status = WEXITSTATUS(wstatus); + if (exit_status != 0) { + b_log(B_ERROR, "command exited with exit code %d", exit_status); + return false; + } + + break; + } + + if (WIFSIGNALED(wstatus)) { + b_log(B_ERROR, "command process was terminated by %s", strsignal(WTERMSIG(wstatus))); + return false; + } + } + + return true; +} + +bool b_cmd_run_sync(B_Cmd cmd) +{ + B_Proc p = b_cmd_run_async(cmd); + if (p == B_INVALID_PROC) return false; + return b_proc_wait(p); +} + +char *b_shift_args(int *argc, char ***argv) +{ + B_ASSERT(*argc > 0); + char *result = **argv; + (*argv) += 1; + (*argc) -= 1; + return result; +} + +void b_log(B_Log_Level level, const char *fmt, ...) +{ + switch (level) { + case B_INFO: + fprintf(stderr, "[INFO] "); + break; + case B_WARNING: + fprintf(stderr, "[WARNING] "); + break; + case B_ERROR: + fprintf(stderr, "[ERROR] "); + break; + default: + B_ASSERT(0 && "unreachable"); + } + + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); +} + +bool b_read_entire_dir(const char *parent, B_File_Paths *children) +{ + bool result = true; + DIR *dir = NULL; + + dir = opendir(parent); + if (dir == NULL) { + b_log(B_ERROR, "Could not open directory %s: %s", parent, strerror(errno)); + b_return_defer(false); + } + + errno = 0; + struct dirent *ent = readdir(dir); + while (ent != NULL) { + b_da_append(children, b_temp_strdup(ent->d_name)); + ent = readdir(dir); + } + + if (errno != 0) { + b_log(B_ERROR, "Could not read directory %s: %s", parent, strerror(errno)); + b_return_defer(false); + } + +defer: + if (dir) closedir(dir); + return result; +} + +bool b_write_entire_file(const char *path, const void *data, size_t size) +{ + bool result = true; + + FILE *f = fopen(path, "wb"); + if (f == NULL) { + b_log(B_ERROR, "Could not open file %s for writing: %s\n", path, strerror(errno)); + b_return_defer(false); + } + + // len + // v + // aaaaaaaaaa + // ^ + // data + + const char *buf = data; + while (size > 0) { + size_t n = fwrite(buf, 1, size, f); + if (ferror(f)) { + b_log(B_ERROR, "Could not write into file %s: %s\n", path, strerror(errno)); + b_return_defer(false); + } + size -= n; + buf += n; + } + +defer: + if (f) fclose(f); + return result; +} + +B_File_Type b_get_file_type(const char *path) +{ + struct stat statbuf; + if (stat(path, &statbuf) < 0) { + b_log(B_ERROR, "Could not get stat of %s: %s", path, strerror(errno)); + return -1; + } + + switch (statbuf.st_mode & S_IFMT) { + case S_IFDIR: return B_FILE_DIRECTORY; + case S_IFREG: return B_FILE_REGULAR; + case S_IFLNK: return B_FILE_SYMLINK; + default: return B_FILE_OTHER; + } +} + +bool b_copy_directory_recursively(const char *src_path, const char *dst_path) +{ + bool result = true; + B_File_Paths children = {0}; + B_String_Builder src_sb = {0}; + B_String_Builder dst_sb = {0}; + size_t temp_checkpoint = b_temp_save(); + + B_File_Type type = b_get_file_type(src_path); + if (type < 0) return false; + + switch (type) { + case B_FILE_DIRECTORY: { + if (!b_mkdir_if_not_exists(dst_path)) b_return_defer(false); + if (!b_read_entire_dir(src_path, &children)) b_return_defer(false); + + for (size_t i = 0; i < children.count; ++i) { + if (strcmp(children.items[i], ".") == 0) continue; + if (strcmp(children.items[i], "..") == 0) continue; + + src_sb.count = 0; + b_sb_append_cstr(&src_sb, src_path); + b_sb_append_cstr(&src_sb, "/"); + b_sb_append_cstr(&src_sb, children.items[i]); + b_sb_append_null(&src_sb); + + dst_sb.count = 0; + b_sb_append_cstr(&dst_sb, dst_path); + b_sb_append_cstr(&dst_sb, "/"); + b_sb_append_cstr(&dst_sb, children.items[i]); + b_sb_append_null(&dst_sb); + + if (!b_copy_directory_recursively(src_sb.items, dst_sb.items)) { + b_return_defer(false); + } + } + } break; + + case B_FILE_REGULAR: { + if (!b_copy_file(src_path, dst_path)) { + b_return_defer(false); + } + } break; + + case B_FILE_SYMLINK: { + b_log(B_WARNING, "TODO: Copying symlinks is not supported yet"); + } break; + + case B_FILE_OTHER: { + b_log(B_ERROR, "Unsupported type of file %s", src_path); + b_return_defer(false); + } break; + + default: B_ASSERT(0 && "unreachable"); + } + +defer: + b_temp_rewind(temp_checkpoint); + b_da_free(src_sb); + b_da_free(dst_sb); + b_da_free(children); + return result; +} + +char *b_temp_strdup(const char *cstr) +{ + size_t n = strlen(cstr); + char *result = b_temp_alloc(n + 1); + B_ASSERT(result != NULL && "Increase B_TEMP_CAPACITY"); + memcpy(result, cstr, n); + result[n] = '\0'; + return result; +} + +void *b_temp_alloc(size_t size) +{ + if (b_temp_size + size > B_TEMP_CAPACITY) return NULL; + void *result = &b_temp[b_temp_size]; + b_temp_size += size; + return result; +} + +char *b_temp_sprintf(const char *format, ...) +{ + va_list args; + va_start(args, format); + int n = vsnprintf(NULL, 0, format, args); + va_end(args); + + B_ASSERT(n >= 0); + char *result = b_temp_alloc(n + 1); + B_ASSERT(result != NULL && "Extend the size of the temporary allocator"); + // TODO: use proper arenas for the temporary allocator; + va_start(args, format); + vsnprintf(result, n + 1, format, args); + va_end(args); + + return result; +} + +void b_temp_reset(void) +{ + b_temp_size = 0; +} + +size_t b_temp_save(void) +{ + return b_temp_size; +} + +void b_temp_rewind(size_t checkpoint) +{ + b_temp_size = checkpoint; +} + +const char *b_temp_sv_to_cstr(B_String_View sv) +{ + char *result = b_temp_alloc(sv.count + 1); + B_ASSERT(result != NULL && "Extend the size of the temporary allocator"); + memcpy(result, sv.data, sv.count); + result[sv.count] = '\0'; + return result; +} + +int b_needs_rebuild(const char *output_path, const char **input_paths, size_t input_paths_count) +{ + struct stat statbuf = {0}; + + if (stat(output_path, &statbuf) < 0) { + // NOTE: if output does not exist it 100% must be rebuilt + if (errno == ENOENT) return 1; + b_log(B_ERROR, "could not stat %s: %s", output_path, strerror(errno)); + return -1; + } + int output_path_time = statbuf.st_mtime; + + for (size_t i = 0; i < input_paths_count; ++i) { + const char *input_path = input_paths[i]; + if (stat(input_path, &statbuf) < 0) { + // NOTE: non-existing input is an error cause it is needed for building in the first place + b_log(B_ERROR, "could not stat %s: %s", input_path, strerror(errno)); + return -1; + } + int input_path_time = statbuf.st_mtime; + // NOTE: if even a single input_path is fresher than output_path that's 100% rebuild + if (input_path_time > output_path_time) return 1; + } + + return 0; +} + +int b_needs_rebuild1(const char *output_path, const char *input_path) +{ + return b_needs_rebuild(output_path, &input_path, 1); +} + +bool b_rename(const char *old_path, const char *new_path) +{ + b_log(B_INFO, "renaming %s -> %s", old_path, new_path); + if (rename(old_path, new_path) < 0) { + b_log(B_ERROR, "could not rename %s to %s: %s", old_path, new_path, strerror(errno)); + return false; + } + return true; +} + +bool b_read_entire_file(const char *path, B_String_Builder *sb) +{ + bool result = true; + + FILE *f = fopen(path, "rb"); + if (f == NULL) b_return_defer(false); + if (fseek(f, 0, SEEK_END) < 0) b_return_defer(false); + long m = ftell(f); + if (m < 0) b_return_defer(false); + if (fseek(f, 0, SEEK_SET) < 0) b_return_defer(false); + + size_t new_count = sb->count + m; + if (new_count > sb->capacity) { + sb->items = realloc(sb->items, new_count); + B_ASSERT(sb->items != NULL && "Buy more RAM lool!!"); + sb->capacity = new_count; + } + + fread(sb->items + sb->count, m, 1, f); + if (ferror(f)) { + // TODO: Afaik, ferror does not set errno. So the error reporting in defer is not correct in this case. + b_return_defer(false); + } + sb->count = new_count; + +defer: + if (!result) b_log(B_ERROR, "Could not read file %s: %s", path, strerror(errno)); + if (f) fclose(f); + return result; +} + +B_String_View b_sv_chop_by_delim(B_String_View *sv, char delim) +{ + size_t i = 0; + while (i < sv->count && sv->data[i] != delim) { + i += 1; + } + + B_String_View result = b_sv_from_parts(sv->data, i); + + if (i < sv->count) { + sv->count -= i + 1; + sv->data += i + 1; + } else { + sv->count -= i; + sv->data += i; + } + + return result; +} + +B_String_View b_sv_from_parts(const char *data, size_t count) +{ + B_String_View sv; + sv.count = count; + sv.data = data; + return sv; +} + +B_String_View b_sv_trim_left(B_String_View sv) +{ + size_t i = 0; + while (i < sv.count && isspace(sv.data[i])) { + i += 1; + } + + return b_sv_from_parts(sv.data + i, sv.count - i); +} + +B_String_View b_sv_trim_right(B_String_View sv) +{ + size_t i = 0; + while (i < sv.count && isspace(sv.data[sv.count - 1 - i])) { + i += 1; + } + + return b_sv_from_parts(sv.data, sv.count - i); +} + +B_String_View b_sv_trim(B_String_View sv) +{ + return b_sv_trim_right(b_sv_trim_left(sv)); +} + +B_String_View b_sv_from_cstr(const char *cstr) +{ + return b_sv_from_parts(cstr, strlen(cstr)); +} + +bool b_sv_eq(B_String_View a, B_String_View b) +{ + if (a.count != b.count) { + return false; + } else { + return memcmp(a.data, b.data, a.count) == 0; + } +} + +// RETURNS: +// 0 - file does not exists +// 1 - file exists +// -1 - error while checking if file exists. The error is logged +int b_file_exists(const char *file_path) +{ + struct stat statbuf; + if (stat(file_path, &statbuf) < 0) { + if (errno == ENOENT) return 0; + b_log(B_ERROR, "Could not check if file %s exists: %s", file_path, strerror(errno)); + return -1; + } + return 1; +} + +#endif diff --git a/src/render.c b/src/render.c index b6383c4..db3c168 100644 --- a/src/render.c +++ b/src/render.c @@ -1,4 +1,7 @@ + +#include <emmintrin.h> #include <stddef.h> +#include <stdint.h> #include <stdlib.h> #include <time.h> @@ -22,48 +25,25 @@ #define VKSETUP_IMPLEMENTATION #include "vksetup.h" +#define FAST_OBJ_IMPLEMENTATION +#include "../lib/fast_obj.h" + +#define CGLTF_IMPLEMENTATION +#include "../lib/cgltf.h" + //#include "cplusplus.h" -#include "vkutil.h" +//#include "vkutil.h" #include "state.h" +#define MODEL_PATH "assets/human.obj" +#define TEXTURE_PATH "assets/viking_room.png" + +void load_model_obj(); + // embedded clgm library uint32_t currentFrame = 0; state_t s; -const char *const validation_layers[] = { - "VK_LAYER_KHRONOS_validation" -}; -const uint32_t validation_layer_count = VK_ARRAY_LEN(validation_layers); - -const char *const device_extensions[] = { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, -}; -const uint32_t deviceExtensionCount = VK_ARRAY_LEN(device_extensions); - - -#ifdef VKDEBUG - const bool enableValidationLayers = true; -#else - const bool enableValidationLayers = false; -#endif - -typedef struct { - float x; - float y; -} V2; - -typedef struct { - float x; - float y; - float z; -} V3; - -typedef struct { - V3 pos; - V3 color; - V2 texCoord; -} Vertex; - /* Vertex vertices[] = { */ /* (Vertex) { (V2) {-0.2f, -0.5f}, (V3) {0.0f, 1.0f, 0.0f}}, */ /* (Vertex) { (V2) {0.5f, 0.3f}, (V3) {0.0f, 0.0f, 1.0f}}, */ @@ -97,7 +77,7 @@ Vertex vertices[] = { }; const int VERTICES_SIZE = VK_ARRAY_LEN(vertices); -const uint16_t indices[] = { +const uint32_t indices[] = { 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 5, 4 }; const int INDICES_SIZE = VK_ARRAY_LEN(indices); @@ -117,7 +97,8 @@ const int INDICES_SIZE = VK_ARRAY_LEN(indices); /* }; */ /* const int INDICES_SIZE = VK_ARRAY_LEN(indices); */ -static int resizing_event_watcher(void* data, SDL_Event* event) { +static int resizing_event_watcher(void *data, SDL_Event *event) { + (void) data; if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED) { s.sdl_window_resized = 1; @@ -180,6 +161,9 @@ debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { + (void) messageSeverity; + (void) messageType; + (void) pUserData; vk_log(VK_ERROR, "validation layer: %s\n", pCallbackData->pMessage); return VK_FALSE; } @@ -527,7 +511,6 @@ void mouseCallback(SDL_Event *event) { if (event->type != SDL_MOUSEMOTION) return; - if (!s.mouse_pressed) return; float xoffset = event->motion.xrel; float yoffset = -event->motion.yrel; // Reversed since y-coordinates range from bottom to top @@ -535,7 +518,20 @@ mouseCallback(SDL_Event *event) xoffset *= sensitivity; yoffset *= sensitivity; - update_camera(xoffset, yoffset); + if (s.mouse_pressed) { + update_camera(xoffset, yoffset); + } else if (s.middle_click) { + vec4 _up = {0, 1, 0, 0}; + vec4 _right = {1, 0, 0, 0}; + + vec4 up, right; + glm_mat4_mulv(s.ubo.model, _up, up); + glm_mat4_mulv(s.ubo.model, _right, right); + + glm_rotate(s.ubo.model, glm_rad(xoffset * glm_rad(90.0f)), GLM_ZUP); + glm_rotate(s.ubo.model, glm_rad(yoffset * glm_rad(90.0f)), GLM_XUP); + } + return; } void @@ -612,7 +608,7 @@ vulkan_create_swap_chain() VkImageView -create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) +create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels) { VkImageViewCreateInfo viewInfo = {0}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -621,7 +617,7 @@ create_image_view(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags viewInfo.format = format; viewInfo.subresourceRange.aspectMask = aspectFlags; viewInfo.subresourceRange.baseMipLevel = 0; - viewInfo.subresourceRange.levelCount = 1; + viewInfo.subresourceRange.levelCount = mipLevels; viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; @@ -635,7 +631,7 @@ void vulkan_create_image_views() { for (size_t i = 0; i < s.vk_swap_chain_image_count; i++) { - s.vk_swap_chain_image_views[i] = create_image_view(s.vk_swap_chain_images[i], s.vk_swap_chain_image_format, VK_IMAGE_ASPECT_COLOR_BIT); + s.vk_swap_chain_image_views[i] = create_image_view(s.vk_swap_chain_images[i], s.vk_swap_chain_image_format, VK_IMAGE_ASPECT_COLOR_BIT, 1); } vk_log(VK_INFO, "Vulkan image views created!\n"); } @@ -725,7 +721,7 @@ findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) } void -createImage(uint32_t width, uint32_t height, VkFormat format, +createImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage *image, VkDeviceMemory *imageMemory) @@ -736,7 +732,7 @@ createImage(uint32_t width, uint32_t height, VkFormat format, imageInfo.extent.width = width; imageInfo.extent.height = height; imageInfo.extent.depth = 1; - imageInfo.mipLevels = 1; + imageInfo.mipLevels = mipLevels; imageInfo.arrayLayers = 1; imageInfo.format = format; imageInfo.tiling = tiling; @@ -807,7 +803,7 @@ void transitionImageLayout(VkImage image, VkFormat format, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, - VkImageLayout oldLayout, VkImageLayout newLayout) + VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels) { VkCommandBuffer commandBuffer = beginSingleTimeCommands(); @@ -833,7 +829,7 @@ transitionImageLayout(VkImage image, VkFormat format, } barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.levelCount = mipLevels; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; @@ -880,18 +876,18 @@ void vulkan_create_depth_resources() { VkFormat depthFormat = findDepthFormat(); - createImage(s.vk_swap_chain_extent.width, s.vk_swap_chain_extent.height, + createImage(s.vk_swap_chain_extent.width, s.vk_swap_chain_extent.height, 1, depthFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &s.vk_depth_image, &s.vk_depth_image_memory); s.vk_depth_image_view = create_image_view(s.vk_depth_image, depthFormat, - VK_IMAGE_ASPECT_DEPTH_BIT); + VK_IMAGE_ASPECT_DEPTH_BIT, 1); transitionImageLayout(s.vk_depth_image, depthFormat, VK_ACCESS_NONE, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 1); } void @@ -1013,7 +1009,7 @@ vulkan_create_graphics_pipeline(VkPolygonMode polygon_mode) rasterizer.lineWidth = 1.0f; rasterizer.cullMode = VK_CULL_MODE_NONE; //rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; - //rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; rasterizer.depthBiasEnable = VK_FALSE; rasterizer.depthBiasConstantFactor = 0.0f; // Optional rasterizer.depthBiasClamp = 0.0f; // Optional @@ -1159,7 +1155,7 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) s.vk_swap_chain_images[imageIndex], VK_FORMAT_R8G8B8A8_SRGB, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1); VkRenderingAttachmentInfo colorAttachment = {0}; colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; @@ -1168,7 +1164,7 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachment.clearValue.color = - (VkClearColorValue){0.0f, 0.0f, 0.0f, 1.0f}; + (VkClearColorValue){{0.0f, 0.0f, 0.0f, 1.0f}}; VkRenderingAttachmentInfo depthAttachment = {0}; depthAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; @@ -1210,13 +1206,13 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) VkDeviceSize offsets[] = {0}; vkCmdBindVertexBuffers(commandBuffer, 0, 1, vertexBuffers, offsets); vkCmdBindIndexBuffer(commandBuffer, s.vk_index_buffer, 0, - VK_INDEX_TYPE_UINT16); + VK_INDEX_TYPE_UINT32); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, s.vk_pipeline_layout, 0, 1, &s.frames[currentFrame].vk_descriptor_set, 0, NULL); - vkCmdDrawIndexed(commandBuffer, INDICES_SIZE, 1, 0, 0, 0); + vkCmdDrawIndexed(commandBuffer, s.indices_count, 1, 0, 0, 0); vkCmdEndRendering(commandBuffer); transitionImageLayout(s.vk_swap_chain_images[imageIndex], @@ -1225,7 +1221,7 @@ recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 1); VK_CHECK(vkEndCommandBuffer(commandBuffer)); } @@ -1288,7 +1284,8 @@ void copy_buffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) { void vulkan_create_vertex_buffer() { - VkDeviceSize bufferSize = sizeof(vertices[0]) * VERTICES_SIZE; + VkDeviceSize bufferSize = sizeof(s.vertices[0]) * s.vertices_count; + /* VkDeviceSize bufferSize = sizeof(vertices[0]) * VERTICES_SIZE; */ VkBuffer stagingBuffer; VkDeviceMemory stagingBufferMemory; @@ -1300,7 +1297,8 @@ vulkan_create_vertex_buffer() void* data; VK_CHECK(vkMapMemory(s.vk_device, stagingBufferMemory, 0, bufferSize, 0, &data)); - memcpy(data, vertices, (size_t) bufferSize); + // memcpy(data, vertices, (size_t) bufferSize); + memcpy(data, s.vertices, (size_t) bufferSize); vkUnmapMemory(s.vk_device, stagingBufferMemory); createBuffer(bufferSize, @@ -1317,7 +1315,8 @@ vulkan_create_vertex_buffer() void vulkan_create_index_buffer() { - VkDeviceSize bufferSize = sizeof(indices[0]) * INDICES_SIZE; + VkDeviceSize bufferSize = sizeof(s.indices[0]) * s.indices_count; + /* VkDeviceSize bufferSize = sizeof(indices[0]) * INDICES_SIZE; */ VkBuffer stagingBuffer; VkDeviceMemory stagingBufferMemory; @@ -1329,7 +1328,8 @@ vulkan_create_index_buffer() void* data; VK_CHECK(vkMapMemory(s.vk_device, stagingBufferMemory, 0, bufferSize, 0, &data)); - memcpy(data, indices, (size_t) bufferSize); + memcpy(data, s.indices, (size_t) bufferSize); + /* memcpy(data, indices, (size_t) bufferSize); */ vkUnmapMemory(s.vk_device, stagingBufferMemory); createBuffer(bufferSize, @@ -1544,6 +1544,25 @@ handle_input(bool * quit) case SDLK_l: s.rotate = s.rotate ? 0 : 1; break; + case SDLK_m: + vkDeviceWaitIdle(s.vk_device); + free(s.vertices); + free(s.indices); + if (!strcmp(s.model_path, "assets/human.obj")) + strcpy(s.model_path, "assets/viking_room.obj"); + else if (!strcmp(s.model_path, "assets/viking_room.obj")) + strcpy(s.model_path, "assets/test.obj"); + else + strcpy(s.model_path, "assets/human.obj"); + load_model_obj(); + vkDestroyBuffer(s.vk_device, s.vk_vertex_buffer, NULL); + vkFreeMemory(s.vk_device, s.vk_vertex_buffer_memory, NULL); + + vkDestroyBuffer(s.vk_device, s.vk_index_buffer, NULL); + vkFreeMemory(s.vk_device, s.vk_index_buffer_memory, NULL); + vulkan_create_vertex_buffer(); + vulkan_create_index_buffer(); + break; case SDLK_r: s.camera.pos[0] = 1.0f; s.camera.pos[1] = 1.0f; @@ -1562,6 +1581,10 @@ handle_input(bool * quit) s.camera.lastX = 400.0f; s.camera.lastY = 300.0f; s.camera.fov = 45.0f; + + glm_mat4_identity(s.ubo.model); + + s.zoom = 10; update_camera(0,0); break; } @@ -1569,19 +1592,28 @@ handle_input(bool * quit) else if (e.type == SDL_MOUSEWHEEL) { if(e.wheel.y > 0) // scroll up { - s.zoom += 1; + s.zoom -= 1; } else if(e.wheel.y < 0) // scroll down { - s.zoom -= 1; + s.zoom += 1; if (s.zoom == -100) s.zoom = 1; } } else if (e.type == SDL_MOUSEBUTTONDOWN) { - s.mouse_pressed = 1; + if (e.button.button != 2) { + s.mouse_pressed = 1; + } else { + s.middle_click = 1; + } + } else if (e.type == SDL_MOUSEBUTTONUP) { - s.mouse_pressed = 0; + if (e.button.button != 2) { + s.mouse_pressed = 0; + } else { + s.middle_click = 0; + } } mouseCallback(&e); } @@ -1617,13 +1649,88 @@ copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, endSingleTimeCommands(commandBuffer); } +void generateMipmaps(VkImage image, VkFormat imageFormat, int32_t texWidth, int32_t texHeight, uint32_t mipLevels) { + VkCommandBuffer commandBuffer = beginSingleTimeCommands(); + + VkImageMemoryBarrier barrier = {0}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.image = image; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + + int32_t mipWidth = texWidth; + int32_t mipHeight = texHeight; + + for (uint32_t i = 1; i < mipLevels; i++) { + barrier.subresourceRange.baseMipLevel = i - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, + &barrier); + + VkImageBlit blit = {0}; + blit.srcOffsets[0] = (VkOffset3D){ 0, 0, 0 }; + blit.srcOffsets[1] = (VkOffset3D){ mipWidth, mipHeight, 1 }; + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.mipLevel = i - 1; + blit.srcSubresource.baseArrayLayer = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0] = (VkOffset3D){ 0, 0, 0 }; + blit.dstOffsets[1] = (VkOffset3D){ mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 }; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.mipLevel = i; + blit.dstSubresource.baseArrayLayer = 0; + blit.dstSubresource.layerCount = 1; + + vkCmdBlitImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, + VK_FILTER_LINEAR); + + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, + NULL, 1, &barrier); + + if (mipWidth > 1) mipWidth /= 2; + if (mipHeight > 1) mipHeight /= 2; + } + + /* barrier.subresourceRange.baseMipLevel = mipLevels - 1; */ + /* barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; */ + /* barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; */ + /* barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; */ + /* barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; */ + + /* vkCmdPipelineBarrier(commandBuffer, */ + /* VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, */ + /* 0, NULL, */ + /* 0, NULL, */ + /* 1, &barrier); */ + + endSingleTimeCommands(commandBuffer); +} + void vulkan_create_texture_image() { int texWidth, texHeight, texChannels; - stbi_uc* pixels = stbi_load("assets/texture.jpg", &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); + stbi_uc* pixels = stbi_load(TEXTURE_PATH, &texWidth, &texHeight, &texChannels, STBI_rgb_alpha); VkDeviceSize imageSize = texWidth * texHeight * 4; + s.vk_mip_levels = (uint32_t)floor(log2(fmax(texWidth, texHeight))) + 1; + if (!pixels) { vk_log(VK_ERROR, "failed to load texture image!\n"); } @@ -1639,9 +1746,9 @@ vulkan_create_texture_image() stbi_image_free(pixels); - createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_SRGB, + createImage(texWidth, texHeight, s.vk_mip_levels, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, - VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, + VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &s.vk_texture_image, &s.vk_texture_image_memory); @@ -1649,15 +1756,16 @@ vulkan_create_texture_image() s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, s.vk_mip_levels); copyBufferToImage(stagingBuffer, s.vk_texture_image, (uint32_t)texWidth, (uint32_t)texHeight); + generateMipmaps(s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB, texWidth, texHeight, s.vk_mip_levels); transitionImageLayout( s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, s.vk_mip_levels); vkDestroyBuffer(s.vk_device, stagingBuffer, NULL); vkFreeMemory(s.vk_device, stagingBufferMemory, NULL); @@ -1666,7 +1774,7 @@ vulkan_create_texture_image() void vulkan_create_texture_image_view() { - s.vk_texture_image_view = create_image_view(s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT); + s.vk_texture_image_view = create_image_view(s.vk_texture_image, VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_ASPECT_COLOR_BIT, s.vk_mip_levels); } void vulkan_create_texture_sampler() { @@ -1690,12 +1798,235 @@ void vulkan_create_texture_sampler() { samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo.mipLodBias = 0.0f; samplerInfo.minLod = 0.0f; - samplerInfo.maxLod = 0.0f; + samplerInfo.maxLod = VK_LOD_CLAMP_NONE; VK_CHECK(vkCreateSampler(s.vk_device, &samplerInfo, NULL, &s.vk_texture_sampler)); } void +load_model_obj() +{ + fastObjMesh *m = fast_obj_read(s.model_path); + + if (!m) { + vk_log(VK_ERROR, "Can't load object\n"); + } + + s.indices_count = m->index_count; + s.indices = (uint32_t *)malloc(s.indices_count * sizeof(uint32_t)); + s.vertices_count = 0; + + if (m->group_count > 1) { + vk_log(VK_WARN, "fast_obj Mesh with groups > 1 not supported."); + } + + /* + Since blender exports objs indexed we use those same indices for our + indexed draw call. + */ + + /* Count indexs and vertices */ + size_t c = 0; + for (size_t ii = 0; ii < m->group_count; ii++) { + const fastObjGroup *grp = &m->groups[ii]; + for (unsigned int jj = 0; jj < grp->face_count; jj++) { + unsigned int fv = m->face_vertices[grp->face_offset + jj]; + for (unsigned int kk = 0; kk < fv; kk++) { + /* position index */ + uint32_t mip = m->indices[grp->index_offset + c].p - 1; /* make index start from zero */ + + /* flag if we've seen the index*/ + int index_seen = 0; + for (size_t i = 0; i < c; i++) { + if (mip == s.indices[i]) { + index_seen = 1; + break; + } + } + s.indices[c] = mip; + if (!index_seen) { + /* If not seen, incremet vertices */ + s.vertices_count++; + } + c++; + } + } + } + + printf("vertex count %ld!!!!!!!!!!!!!!!!!!\n", s.vertices_count); + s.vertices = (Vertex *)malloc(s.vertices_count * sizeof(Vertex)); + + /* for (size_t i = 0; i < s.indices_count; i++) { */ + /* uint32_t mip = s.indices[i]; */ + /* int index_seen = 0; */ + /* for (size_t j = 0; j < i; j++) { */ + /* if (mip == s.indices[j]) { */ + /* index_seen = 1; */ + /* break; */ + /* } */ + /* } */ + /* if (!index_seen) { */ + /* s.vertices[mip].pos.x = m->positions[3 * (mip + 1) + 0]; */ + /* s.vertices[mip].pos.y = m->positions[3 * (mip + 1) + 1]; */ + /* s.vertices[mip].pos.z = m->positions[3 * (mip + 1) + 2]; */ + /* } */ + /* } */ + + for (size_t ii = 0; ii < m->group_count; ii++) { + const fastObjGroup *grp = &m->groups[ii]; + size_t idx = 0; + for (unsigned int jj = 0; jj < grp->face_count; jj++) { + unsigned int fv = m->face_vertices[grp->face_offset + jj]; + for (unsigned int kk = 0; kk < fv; kk++) { + fastObjIndex mi = m->indices[grp->index_offset + idx]; + + int flag = 0; + for (size_t i = 0; i < idx; i++) { + /* if it exists */ + if (mi.p - 1 == s.indices[i]) { + flag = 1; + break; + } + } + if (!flag) { + int index = mi.p - 1; /* zero indexed */ + if (mi.p) { + s.vertices[index].pos.x = m->positions[3 * mi.p + 0]; + s.vertices[index].pos.y = m->positions[3 * mi.p + 1]; + s.vertices[index].pos.z = m->positions[3 * mi.p + 2]; + } + if (mi.t) { + s.vertices[index].texCoord.x = m->texcoords[2 * mi.t + 0]; + s.vertices[index].texCoord.y = 1.0f - m->texcoords[2 * mi.t + 1]; + } + if (mi.n) { + s.vertices[index].color.x = m->normals[3 * mi.n + 0]; + s.vertices[index].color.y = m->normals[3 * mi.n + 1]; + s.vertices[index].color.z = m->normals[3 * mi.n + 2]; + } + } + idx++; + } + } + } + + fast_obj_destroy(m); +} + +void load_model_gltf() { + // TODO maybe copy the raylib implemenetation + /* + RESTRICTIONS: + - Only triangle meshes supported + - Vertex attribute types and formats supported: + > Vertices (position): vec3: float + > Normals: vec3: float + > Texcoords: vec2: float + > Colors: vec4: u8, u16, f32 (normalized) + > Indices: u16, u32 (truncated to u16) + - Node hierarchies or transforms not supported + */ + + /* cgltf_options options; */ + /* memset(&options, 0, sizeof(cgltf_options)); */ + /* cgltf_data* data = NULL; */ + /* cgltf_result result = cgltf_parse_file(&options, MODEL_PATH, &data); */ + + /* if (result == cgltf_result_success) */ + /* result = cgltf_load_buffers(&options, data, MODEL_PATH); */ + + /* if (result == cgltf_result_success) */ + /* result = cgltf_validate(data); */ + + /* if (data->meshes_count < 1) { */ + /* cgltf_free(data); */ + /* return; */ + /* } */ + + /* int idx = 0; */ + /* int meshIndex = 0; */ + /* for (unsigned int i = 0, meshIndex = 0; i < data->meshes_count; i++) { */ + /* for (unsigned int p = 0; p < data->meshes[i].primitives_count; p++) { */ + /* // NOTE: We only support primitives defined by triangles */ + /* // Other alternatives: points, lines, line_strip, triangle_strip */ + /* if (data->meshes[i].primitives[p].type != cgltf_primitive_type_triangles) continue; */ + + /* // NOTE: Attributes data could be provided in several data formats (8, 8u, 16u, 32...), */ + /* // Only some formats for each attribute type are supported, read info at the top of this function! */ + + /* for (unsigned int j = 0; j < data->meshes[i].primitives[p].attributes_count; j++) { */ + /* // Check the different attributes for every primitive */ + /* if (data->meshes[i].primitives[p].attributes[j].type == cgltf_attribute_type_position) { // POSITION, vec3, float */ + /* cgltf_accessor *attribute = data->meshes[i].primitives[p].attributes[j].data; */ + + /* // WARNING: SPECS: POSITION accessor MUST have its min and max properties defined */ + + /* if ((attribute->type == cgltf_type_vec3) && (attribute->component_type == cgltf_component_type_r_32f)) { */ + /* // Init raylib mesh vertices to copy glTF attribute data */ + /* s.vertices_count = attribute->count / 3; */ + /* s.vertices = calloc(attribute->count, attribute->count*sizeof(Vertex)); */ + + /* size_t float_count = cgltf_accessor_unpack_floats(attribute, NULL, 0); */ + /* printf("This many floats: %ld\n", float_count); */ + /* float floats[float_count] = {}; */ + /* cgltf_accessor_unpack_floats(attribute, floats, float_count); */ + + /* for (unsigned int k = 0; k < attribute->count + 3; k+=3) { */ + /* s.vertices[idx].pos.x = floats[k + 0]; */ + /* s.vertices[idx].pos.y = floats[k + 1]; */ + /* s.vertices[idx].pos.z = floats[k + 2]; */ + /* //s.indices[idx] = idx; */ + /* idx++; */ + /* } */ + /* // Load 3 components of float data type into mesh.vertices */ + /* } */ + /* } */ + /* } */ + /* if (data->meshes[i].primitives[p].indices != NULL) { */ + /* printf("THERE ARE INDIXCES!!!\n"); */ + /* cgltf_accessor *attribute = data->meshes[i].primitives[p].indices; */ + + /* s.indices_count = attribute->count / 3; */ + /* s.indices = calloc(attribute->count, attribute->count*sizeof(uint32_t)); */ + + /* if (attribute->component_type == cgltf_component_type_r_16u) { */ + /* size_t float_count = cgltf_accessor_unpack_indices(attribute, NULL, 0, 0); */ + /* printf("This many floats: %ld\n", float_count); */ + /* uint16_t floats[float_count] = {}; */ + /* cgltf_accessor_unpack_indices(attribute, floats, float_count, */ + /* float_count); */ + + /* for (int x = 0; x < float_count; x++) { */ + /* printf("%d \n", floats[x]); */ + /* } */ + + /* } */ + /* else if (attribute->component_type == cgltf_component_type_r_32u) { */ + /* printf("ASDASDASDAS2222222!\n"); */ + /* // Init raylib mesh indices to copy glTF attribute data */ + /* /\* model.meshes[meshIndex].indices = RL_MALLOC(attribute->count*sizeof(unsigned short)); *\/ */ + + /* /\* // Load data into a temp buffer to be converted to raylib data type *\/ */ + /* /\* unsigned int *temp = malloc(attribute->count*sizeof(unsigned int)); *\/ */ + /* /\* LOAD_ATTRIBUTE(attribute, 1, unsigned int, temp); *\/ */ + + /* /\* // Convert data to raylib indices data type (unsigned short) *\/ */ + /* /\* for (unsigned int d = 0; d < attribute->count; d++) model.meshes[meshIndex].indices[d] = (unsigned short)temp[d]; *\/ */ + + /* //free(temp); */ + /* } */ + /* } */ + /* } */ + /* } */ + + /* for (int i = 0; i < s.vertices_count; i++) { */ + /* printf("%d: %f %f %f\n",i, s.vertices[i].pos.x, s.vertices[i].pos.y, s.vertices[i].pos.z); */ + /* } */ + + /* cgltf_free(data); */ +} + +void init_vulkan() { vk_log(VK_WARN, "====================================\n"); @@ -1704,8 +2035,8 @@ init_vulkan() //vulkan_create_instance(); s.vk_instance = - vksetup_create_instance(enableValidationLayers, validation_layers, - validation_layer_count, s.sdl_window); + vks_create_instance(enableValidationLayers, validation_layers, + validation_layer_count, s.sdl_window); // vulkan_setup_debug_messenger(); vulkan_create_surface(); vulkan_pick_physical_device(); @@ -1719,6 +2050,8 @@ init_vulkan() vulkan_create_texture_image(); vulkan_create_texture_image_view(); vulkan_create_texture_sampler(); + load_model_obj(); + //load_model_gltf(); vulkan_create_vertex_buffer(); vulkan_create_index_buffer(); vulkan_create_uniform_buffers(); @@ -1777,6 +2110,9 @@ close_vulkan() if (s.prev_frag_result) { shaderc_result_release(s.prev_frag_result); } + + free(s.vertices); + free(s.indices); } float @@ -1798,24 +2134,28 @@ current_time() } void -updateUniformBuffer(uint32_t currentImage) +updateUniformBuffer(uint32_t currentImage, float dt) { - float time = current_time(); - UniformBufferObject ubo = {0}; + ubo.time = current_time(); + ubo.dt = dt; + ubo.resolution[0] = s.vk_swap_chain_extent.width; ubo.resolution[1] = s.vk_swap_chain_extent.height; - glm_mat4_identity(ubo.model); + //glm_mat4_identity(ubo.model); + glm_mat4_dup(s.ubo.model, ubo.model); + + if (s.rotate) + glm_rotate(ubo.model, glm_rad(30 * dt * glm_rad(90.0f)), GLM_ZUP); - if (!s.rotate) - glm_rotate(ubo.model, glm_rad(50 * time * glm_rad(90.0f)), GLM_ZUP); + glm_mat4_dup(ubo.model, s.ubo.model); - vec3 eye = GLM_VEC3_ONE_INIT; - vec3 center = GLM_VEC3_ZERO_INIT; + /* vec3 eye = GLM_VEC3_ONE_INIT; */ + /* vec3 center = GLM_VEC3_ZERO_INIT; */ -// vk_log(VK_INFO, "{%.2f, %.2f, %.2f}\n", s.camera.front[0], s.camera.front[1], s.camera.front[2]); + // vk_log(VK_INFO, "{%.2f, %.2f, %.2f}\n", s.camera.front[0], s.camera.front[1], s.camera.front[2]); /* glm_vec3_add(s.camera.pos, s.camera.front, center); */ glm_lookat(s.camera.pos, s.camera.front, s.camera.up, ubo.view); @@ -1830,8 +2170,15 @@ updateUniformBuffer(uint32_t currentImage) memcpy(s.frames[currentImage].vk_uniform_buffer_mapped, &ubo, sizeof(ubo)); } +float prev_time = 0; + void draw_frame() { + float time = current_time(); + + float dt = time - prev_time; + + vkWaitForFences(s.vk_device, 1, &s.frames[currentFrame].in_flight_fence, VK_TRUE, UINT64_MAX); uint32_t imageIndex; @@ -1844,7 +2191,7 @@ draw_frame() { vk_log(VK_ERROR, "failed to acquire swap chain image!\n"); } - updateUniformBuffer(currentFrame); + updateUniformBuffer(currentFrame, dt); vkResetFences(s.vk_device, 1, &s.frames[currentFrame].in_flight_fence); @@ -1892,11 +2239,16 @@ draw_frame() { } currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT; + + prev_time = time; } int -main(int argc, char* args[]) +main(int argc, char* argv[]) { + (void) argc; + (void)argv; + init_state(&s); if (!init()) { vk_log(VK_INFO, "Failed to initialize!\n"); diff --git a/src/shader.frag b/src/shader.frag index 3f76290..075c2ae 100644 --- a/src/shader.frag +++ b/src/shader.frag @@ -12,10 +12,77 @@ layout(binding = 0) uniform UniformBufferObject { mat4 view; mat4 proj; vec2 resolution; + float time; + float dt; } ubo; void main() { - //outColor = vec4(fragColor * texture(texSampler, fragTexCoord).rgb, 1.0); - outColor = texture(texSampler, fragTexCoord); - //outColor = vec4(fragColor ,1); + float pulse = sin(ubo.time * .2) * .5 + .5; + outColor = vec4(fragColor * texture(texSampler, fragTexCoord).rgb * pulse, 1.0); + //outColor = texture(texSampler, fragTexCoord); +// float repeat = 10; +// float f = mod(ubo.time, repeat); +// if (f < repeat / 2) { +// outColor = vec4(fragColor * radians(f) ,1); +// } else { +// outColor = vec4(fragColor * radians(1 - f) ,1); +// } +// //outColor = (vec4(1) * ubo.model + vec4(1)) * vec4(texture(texSampler, fragTexCoord).rgb * fragColor, 1.0); } + +// float noise(vec2 p) { +// return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); +// } + +// float smoothNoise(vec2 p) { +// vec2 i = floor(p); +// vec2 f = fract(p); +// vec2 u = f * f * (3.0 - 2.0 * f); +// return mix(mix(noise(i + vec2(0.0, 0.0)), noise(i + vec2(1.0, 0.0)), u.x), +// mix(noise(i + vec2(0.0, 1.0)), noise(i + vec2(1.0, 1.0)), u.x), u.y); +// } + +// float fbm(vec2 p) { +// float value = 0.0; +// float amplitude = 0.5; +// for (int i = 0; i < 6; i++) { +// value += amplitude * smoothNoise(p); +// p *= 2.0; +// amplitude *= 0.5; +// } +// return value; +// } + +// void main() { +// vec2 uv = gl_FragCoord.xy / ubo.resolution.xy; +// vec2 p = uv * 2.0 - vec2(1.0); +// float n = fbm(p * 5.0 - vec2(0.0, ubo.time * 2.0)); +// outColor = vec4(n, n * 0.5, 0.0, 1.0) * vec4(texture(texSampler, fragTexCoord).rgb, 1.0); +// } + +// void main() { + +// // Calculate a pulsating factor based on sine wave +// float pulse = sin(ubo.time * 2.0) * 0.5 + 0.5; + +// // Set the color using the pulse factor +// outColor = vec4(pulse, 0.0, 1.0 - pulse, .5); +// } + +// void main() { +// vec2 uv = gl_FragCoord.xy / ubo.resolution; +// vec2 center = uv - 0.5; + +// float angle = ubo.time; +// float cosA = cos(angle); +// float sinA = sin(angle); + +// vec2 rotated = vec2( +// center.x * cosA - center.y * sinA, +// center.x * sinA + center.y * cosA +// ); + +// float pattern = step(0.5, mod(rotated.x * 10.0, 1.0)) * step(0.5, mod(rotated.y * 10.0, 1.0)); + +// outColor = vec4(vec3(pattern), 1.0); +// } diff --git a/src/shader.vert b/src/shader.vert index dd50683..182bd51 100644 --- a/src/shader.vert +++ b/src/shader.vert @@ -5,6 +5,8 @@ layout(binding = 0) uniform UniformBufferObject { mat4 view; mat4 proj; vec2 resolution; + float time; + float dt; } ubo; layout(location = 0) in vec3 inPosition; @@ -17,7 +19,7 @@ layout(location = 1) out vec2 fragTexCoord; void main() { gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); //gl_Position = vec4(inPosition * 2, 1.0); - gl_PointSize = 10.0f; + gl_PointSize = 1.5f; fragColor = inColor; fragTexCoord = inTexCoord; diff --git a/src/state.h b/src/state.h index b56544a..c7b567f 100644 --- a/src/state.h +++ b/src/state.h @@ -27,6 +27,8 @@ typedef struct { mat4 view; mat4 proj; vec2 resolution; + float time; + float dt; } UniformBufferObject; typedef struct { @@ -43,6 +45,23 @@ typedef struct { VkDescriptorSet vk_descriptor_set; } frame_data; +typedef struct { + float x; + float y; +} V2; + +typedef struct { + float x; + float y; + float z; +} V3; + +typedef struct { + V3 pos; + V3 color; + V2 texCoord; +} Vertex; + typedef struct state { camera3d camera; @@ -54,6 +73,7 @@ typedef struct state { int rotate; int mouse_pressed; + int middle_click; UniformBufferObject ubo; @@ -102,6 +122,7 @@ typedef struct state { shaderc_compilation_result_t prev_vert_result; shaderc_compilation_result_t prev_frag_result; + uint32_t vk_mip_levels; VkImage vk_texture_image; VkDeviceMemory vk_texture_image_memory; VkImageView vk_texture_image_view; @@ -112,11 +133,22 @@ typedef struct state { VkImageView vk_depth_image_view; VkPolygonMode polygon_mode; -} state_t ; + + Vertex *vertices; + size_t vertices_count; + uint32_t * indices; + size_t indices_count; + VkBuffer vertexBuffer; + VkDeviceMemory vertexBufferMemory; + + char model_path[1000]; +} state_t; static void -init_state(state_t * s) +init_state(state_t *s) { + + strcpy(s->model_path, "assets/viking_room.obj"); s->camera.pos[0] = 2.0f; s->camera.pos[1] = 2.0f; s->camera.pos[2] = 2.0f; @@ -137,6 +169,7 @@ init_state(state_t * s) s->rotate = 0; s->mouse_pressed = 0; + s->middle_click = 0; s->sdl_window_resized = 0; @@ -154,6 +187,8 @@ init_state(state_t * s) s->zoom = 10; s->x = 0.0f; + glm_mat4_identity(s->ubo.model); + s->sdl_window = NULL; s->vk_swap_chain_image_count = 0; diff --git a/src/vksetup.h b/src/vksetup.h index 2feb533..88f579a 100644 --- a/src/vksetup.h +++ b/src/vksetup.h @@ -15,6 +15,10 @@ - Using shaderc for compiling glsl - Using vulkan(!) + This is not meant to be a generic werapper around vulkan. It is actively + paired and chaned by the currently in development application, whatever that + might be. Think of it as the vulkan setup configuratior and helper. + USAGE: ~~~~~~ Do this: @@ -32,60 +36,141 @@ #define SDL_MAIN_HANDLED #define VK_USE_PLATFORM_XCB_KHR +#include <stdarg.h> + #include <SDL2/SDL.h> #include <SDL2/SDL_vulkan.h> #include <vulkan/vulkan.h> +#include <vulkan/vk_enum_string_helper.h> // for string_VkResult #include <shaderc/shaderc.h> #include "../lib/cglm/include/cglm/cglm.h" +#define VK_ARRAY_LEN(arr) sizeof((arr))/sizeof((arr)[0]) + +#ifdef __clang__ +#define __FILENAME__ __FILE_NAME__ +#else +#define __FILENAME__ __FILE__ +#endif + +#define VK_CHECK(x) \ +do { \ + VkResult err = x; \ + if (err < 0) { \ + fprintf(stderr, "src/%s:%d:0: vulkan error: %s \n", \ + __FILENAME__, __LINE__, string_VkResult(err)); \ + abort(); \ + } \ +} while (0) + +typedef enum { + VK_INFO = 0, + VK_WARN, + VK_ERROR, +} log_type; + +static inline void +_vk_log(log_type t, const char * f, ...) +{ +#ifdef VKDEBUG + va_list args; + va_start(args, f); + switch (t) { + case VK_INFO: + printf("INFO: "); + vprintf(f, args); + break; + case VK_WARN: + fprintf(stderr, "WARN: "); + vfprintf(stderr, f, args); + break; + case VK_ERROR: + fprintf(stderr, "ERROR: "); + vfprintf(stderr, f, args); + break; + } + va_end(args); +#else + return; +#endif +} + +#ifdef VKDEBUG +#define vk_log(t, ...) do { \ + fprintf(t == VK_INFO ? stdout : stderr, "src/%s:%d:0: ", \ + __FILENAME__, __LINE__); \ + _vk_log(t, __VA_ARGS__); \ +} while(0) +#else +#define vk_log(t, ...) +#endif + +/**********/ +/* config */ +/**********/ + +const char *const validation_layers[] = { + "VK_LAYER_KHRONOS_validation" +}; +const uint32_t validation_layer_count = VK_ARRAY_LEN(validation_layers); + +const char *const device_extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, +}; +const uint32_t deviceExtensionCount = VK_ARRAY_LEN(device_extensions); + +#ifdef VKDEBUG + const bool enableValidationLayers = true; +#else + const bool enableValidationLayers = false; +#endif + + #ifdef __cplusplus extern "C" { #endif -#ifndef VKSETUPDEF -#ifdef VKSETUP_STATIC -#define VKSETUPDEF static +#ifndef VKSDEF +#ifdef VKS_STATIC +#define VKSDEF static #else -#define VKSETUPDEF extern +#define VKSDEF extern #endif #endif // TODO Create structs for vulkan data -typedef struct {void * a;} vksetup_vulkan; -typedef struct {void * a;} vksetup_image; -typedef struct {void * a;} vksetup_; - -// need abstraction with the - +typedef struct {void * a;} vks_vulkan; +typedef struct {void * a;} vks_image; +typedef struct {void * a;} vks_; /** Create a VkInstance */ -VKSETUPDEF VkInstance vksetup_create_instance(bool validation_layers_toggle, const char * const validation_layers[], uint32_t validation_layer_count, SDL_Window *window); -/* VKSETUPDEF void vulkan_create_surface(); */ -/* VKSETUPDEF void vulkan_pick_physical_device(); */ -/* VKSETUPDEF void vulkan_create_logical_device(); */ -/* VKSETUPDEF void vulkan_create_swap_chain(); */ -/* VKSETUPDEF void vulkan_create_image_views(); */ -/* VKSETUPDEF void vulkan_create_descriptor_set_layout(); */ -/* VKSETUPDEF void vulkan_create_graphics_pipeline(); */ -/* VKSETUPDEF void vulkan_create_command_pool(); */ -/* VKSETUPDEF void vulkan_create_depth_resources(); */ -/* VKSETUPDEF void vulkan_create_texture_image(); */ -/* VKSETUPDEF void vulkan_create_texture_image_view(); */ -/* VKSETUPDEF void vulkan_create_texture_sampler(); */ -/* VKSETUPDEF void vulkan_create_vertex_buffer(); */ -/* VKSETUPDEF void vulkan_create_index_buffer(); */ -/* VKSETUPDEF void vulkan_create_uniform_buffers(); */ -/* VKSETUPDEF void vulkan_create_descriptor_pool(); */ -/* VKSETUPDEF void vulkan_create_descriptor_sets(); */ -/* VKSETUPDEF void vulkan_create_command_buffer(); */ -/* VKSETUPDEF void vulkan_create_sync_objects(); */ +VKSDEF VkInstance vks_create_instance(bool validation_layers_toggle, const char * const validation_layers[], uint32_t validation_layer_count, SDL_Window *window); +/* VKSDEF void vulkan_create_surface(); */ +/* VKSDEF void vulkan_pick_physical_device(); */ +/* VKSDEF void vulkan_create_logical_device(); */ +/* VKSDEF void vulkan_create_swap_chain(); */ +/* VKSDEF void vulkan_create_image_views(); */ +/* VKSDEF void vulkan_create_descriptor_set_layout(); */ +/* VKSDEF void vulkan_create_graphics_pipeline(); */ +/* VKSDEF void vulkan_create_command_pool(); */ +/* VKSDEF void vulkan_create_depth_resources(); */ +/* VKSDEF void vulkan_create_texture_image(); */ +/* VKSDEF void vulkan_create_texture_image_view(); */ +/* VKSDEF void vulkan_create_texture_sampler(); */ +/* VKSDEF void vulkan_create_vertex_buffer(); */ +/* VKSDEF void vulkan_create_index_buffer(); */ +/* VKSDEF void vulkan_create_uniform_buffers(); */ +/* VKSDEF void vulkan_create_descriptor_pool(); */ +/* VKSDEF void vulkan_create_descriptor_sets(); */ +/* VKSDEF void vulkan_create_command_buffer(); */ +/* VKSDEF void vulkan_create_sync_objects(); */ #ifdef __cplusplus } @@ -99,10 +184,11 @@ VKSETUPDEF VkInstance vksetup_create_instance(bool validation_layers_toggle, con #include <stddef.h> #include <stdlib.h> #include <time.h> -#include "vkutil.h" + +/* Vks helpers */ bool -_checkValidationLayerSupport(const char * const validation_layers[], uint32_t validation_layer_count) +vks_check_validation_layer_support(const char * const validation_layers[], uint32_t validation_layer_count) { uint32_t layerCount; vkEnumerateInstanceLayerProperties(&layerCount, NULL); @@ -110,29 +196,28 @@ _checkValidationLayerSupport(const char * const validation_layers[], uint32_t va VkLayerProperties availableLayers[layerCount]; vkEnumerateInstanceLayerProperties(&layerCount, availableLayers); + bool layerFound = false; for (uint32_t i = 0; i < validation_layer_count; i++) { - bool layerFound = false; - for (uint32_t j = 0; j < layerCount; j++) { if (strcmp(validation_layers[i], availableLayers[j].layerName) == 0) { layerFound = true; break; } } - - if (!layerFound) { - return false; - } } - return true; + return layerFound; } -VKSETUPDEF VkInstance -vksetup_create_instance(bool validation_layers_toggle, const char * const validation_layers[], uint32_t validation_layer_count, SDL_Window *window) +/* Vks API implementation */ + +VKSDEF VkInstance +vks_create_instance(bool validation_layers_toggle, const char * const validation_layers[], + uint32_t validation_layer_count, SDL_Window *window) { - if (validation_layers_toggle && !_checkValidationLayerSupport(validation_layers, validation_layer_count)) { + if (validation_layers_toggle && !vks_check_validation_layer_support(validation_layers, validation_layer_count)) { vk_log(VK_ERROR, "validation layers requested, but not available!\n"); + abort(); } uint32_t instanceVersion; @@ -166,6 +251,7 @@ vksetup_create_instance(bool validation_layers_toggle, const char * const valida if (SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, NULL) == SDL_FALSE) { vk_log(VK_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", SDL_GetError()); + abort(); } // make space for debug extenetion @@ -177,6 +263,7 @@ vksetup_create_instance(bool validation_layers_toggle, const char * const valida if (SDL_Vulkan_GetInstanceExtensions(window, &sdlExtensionCount, sdlExtensions) == SDL_FALSE) { vk_log(VK_ERROR, "SDL_Vulkan_GetInstanceExtensions failed: %s\n", SDL_GetError()); + abort(); } // add debug extenetion @@ -213,4 +300,3 @@ vksetup_create_instance(bool validation_layers_toggle, const char * const valida } #endif /* VKSETUP_IMPLEMENTATION */ - |