Splitting things up and adding stuff to the libs
This commit is contained in:
parent
dc6365257d
commit
ad815adc9f
6
Makefile
6
Makefile
@ -10,9 +10,9 @@ RM=rm -vf
|
||||
build: clean
|
||||
$(CC) $(CFLAGS) $(LDLIBS) $(INCLUDES) $(OBJECTS) $(P).c -o $(P)
|
||||
|
||||
.PHONY: texturepacker
|
||||
texturepacker:
|
||||
$(CC) $(CFLAGS) -lSDL2 -lm -lcyaml -L objs/ $(INCLUDES) tools/texturepacker.c -o texturepacker
|
||||
.PHONY: texpack
|
||||
texpack:
|
||||
$(CC) $(CFLAGS) -lSDL2 -lm -lcyaml -L objs/ $(INCLUDES) tools/texturepacker.c -o texpack
|
||||
|
||||
.PHONY: run
|
||||
run: build
|
||||
|
132
lib.h
132
lib.h
@ -2,6 +2,8 @@
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#define ARENA_IMPLEMENTATION
|
||||
#include "libs/arena/arena.h"
|
||||
|
||||
typedef uint8_t u8;
|
||||
// typedef char16_t c16;
|
||||
@ -25,115 +27,33 @@ typedef struct Animation {
|
||||
u16 cols;
|
||||
u16 width;
|
||||
u16 height;
|
||||
} SpriteSheet;
|
||||
} Animation;
|
||||
|
||||
void checkCode(int code, char* errorMsg) {
|
||||
if (code < 0) {
|
||||
fprintf(stderr, "Application Error %i: %s\n", code, errorMsg);
|
||||
exit(1);
|
||||
}
|
||||
typedef struct s8 {
|
||||
u8* data;
|
||||
usize size;
|
||||
} s8;
|
||||
|
||||
typedef struct s8_buf {
|
||||
char* data;
|
||||
usize size;
|
||||
usize capcity;
|
||||
Arena* arena;
|
||||
} str_buf;
|
||||
|
||||
// TODO: It would be cool to use abstract allocators
|
||||
s8 cstr(char *cstr, Arena *arena) {
|
||||
int size = strlen(cstr);
|
||||
void *data = arena_alloc(arena, size);
|
||||
memcpy(data, cstr, size);
|
||||
return (s8) { (u8*)data, size };
|
||||
}
|
||||
|
||||
void* checkPtr(void *ptr, char* errorMsg) {
|
||||
if (ptr == NULL) {
|
||||
fprintf(stderr, "Application Error: %s\n", errorMsg);
|
||||
exit(1);
|
||||
void print_str(s8 string, bool print_nl) {
|
||||
for (usize i = 0; i < string.size; i++) {
|
||||
printf("%c", string.data[i]);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void checkShader(unsigned int shader, int statusFlag, char* actionName) {
|
||||
int success;
|
||||
glGetShaderiv(shader, statusFlag, &success);
|
||||
if (success < 0) {
|
||||
fprintf(stderr, "%s Error %i\n", actionName, success);
|
||||
exit(1);
|
||||
if (print_nl) {
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
char* loadText(char* path) {
|
||||
char* buffer = NULL;
|
||||
long length;
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (f) {
|
||||
fseek(f, 0, SEEK_END);
|
||||
length = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
buffer = calloc(length, sizeof(char));
|
||||
if (buffer) {
|
||||
fread(buffer, 1, length, f);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void checkCompileErrors(unsigned int object, char* type)
|
||||
{
|
||||
int success;
|
||||
char infoLog[1024];
|
||||
if (strcmp("PROGRAM", type))
|
||||
{
|
||||
glGetShaderiv(object, GL_COMPILE_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
glGetShaderInfoLog(object, 1024, NULL, infoLog);
|
||||
fprintf(stderr, "| ERROR::SHADER: Compile-time error: Type: %s \n %s\n", type, infoLog);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glGetProgramiv(object, GL_LINK_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
glGetProgramInfoLog(object, 1024, NULL, infoLog);
|
||||
fprintf(stderr, "| ERROR::Shader: Link-time error: Type: %s \n %s\n", type, infoLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int compileShaderProgram(char* vertSrcPath, char* fragSrcPath, char* geoSrcPath) {
|
||||
unsigned int vertShader, fragShader, geoShader;
|
||||
|
||||
char* vertSrc = loadText(vertSrcPath);
|
||||
vertShader = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertShader, 1, (const GLchar* const*)&vertSrc, NULL);
|
||||
glCompileShader(vertShader);
|
||||
checkCompileErrors(vertShader, "VERTEX");
|
||||
|
||||
char* fragSrc = loadText(fragSrcPath);
|
||||
|
||||
fragShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fragShader, 1, (const GLchar* const*)&fragSrc, NULL);
|
||||
glCompileShader(fragShader);
|
||||
checkCompileErrors(fragShader, "FRAGMENT");
|
||||
|
||||
char* geoSrc = NULL;
|
||||
if (geoSrcPath != NULL) {
|
||||
geoSrc = loadText(geoSrcPath);
|
||||
geoShader = glCreateShader(GL_GEOMETRY_SHADER);
|
||||
glShaderSource(geoShader, 1, (const GLchar* const*)&geoSrc, NULL);
|
||||
glCompileShader(geoShader);
|
||||
checkCompileErrors(geoShader, "GEOMETRY");
|
||||
}
|
||||
|
||||
unsigned int program = glCreateProgram();
|
||||
glAttachShader(program, vertShader);
|
||||
glAttachShader(program, fragShader);
|
||||
if (geoSrcPath != NULL) {
|
||||
glAttachShader(program, geoShader);
|
||||
}
|
||||
|
||||
glLinkProgram(program);
|
||||
checkCompileErrors(program, "PROGRAM");
|
||||
|
||||
glDeleteShader(vertShader);
|
||||
glDeleteShader(fragShader);
|
||||
free(vertSrc);
|
||||
free(fragSrc);
|
||||
if (geoSrc != NULL) {
|
||||
free(geoSrc);
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
118
libs/arena.h
Normal file
118
libs/arena.h
Normal file
@ -0,0 +1,118 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
bool is_power_of_two(uintptr_t x) {
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
uintptr_t align_forward(uintptr_t ptr, size_t align) {
|
||||
uintptr_t p, a, modulo;
|
||||
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
p = ptr;
|
||||
a = (uintptr_t)align;
|
||||
// Same as (p % a) but faster as 'a' is a power of two
|
||||
modulo = p & (a-1);
|
||||
|
||||
if (modulo != 0) {
|
||||
// If 'p' address is not aligned, push the address to the
|
||||
// next value which is aligned
|
||||
p += a - modulo;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#ifndef DEFAULT_ALIGNMENT
|
||||
#define DEFAULT_ALIGNMENT (2*sizeof(void *))
|
||||
#endif
|
||||
|
||||
typedef struct Arena Arena;
|
||||
struct Arena {
|
||||
unsigned char *buf;
|
||||
size_t buf_len;
|
||||
size_t prev_offset; // This will be useful for later on
|
||||
size_t curr_offset;
|
||||
};
|
||||
|
||||
void arena_init(Arena *a, void *backing_buffer, size_t backing_buffer_length) {
|
||||
a->buf = (unsigned char *)backing_buffer;
|
||||
a->buf_len = backing_buffer_length;
|
||||
a->curr_offset = 0;
|
||||
a->prev_offset = 0;
|
||||
}
|
||||
|
||||
|
||||
void *arena_alloc_align(Arena *a, size_t size, size_t align) {
|
||||
// Align 'curr_offset' forward to the specified alignment
|
||||
uintptr_t curr_ptr = (uintptr_t)a->buf + (uintptr_t)a->curr_offset;
|
||||
uintptr_t offset = align_forward(curr_ptr, align);
|
||||
offset -= (uintptr_t)a->buf; // Change to relative offset
|
||||
|
||||
// Check to see if the backing memory has space left
|
||||
if (offset+size <= a->buf_len) {
|
||||
void *ptr = &a->buf[offset];
|
||||
a->prev_offset = offset;
|
||||
a->curr_offset = offset+size;
|
||||
|
||||
// Zero new memory by default
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
// Return NULL if the arena is out of memory (or handle differently)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Because C doesn't have default parameters
|
||||
void *arena_alloc(Arena *a, size_t size) {
|
||||
return arena_alloc_align(a, size, DEFAULT_ALIGNMENT);
|
||||
}
|
||||
|
||||
// void arena_free(Arena *a, void *ptr) {
|
||||
// // Do nothing
|
||||
// }
|
||||
|
||||
void *arena_resize_align(Arena *a, void *old_memory, size_t old_size, size_t new_size, size_t align) {
|
||||
unsigned char *old_mem = (unsigned char *)old_memory;
|
||||
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
if (old_mem == NULL || old_size == 0) {
|
||||
return arena_alloc_align(a, new_size, align);
|
||||
} else if (a->buf <= old_mem && old_mem < a->buf+a->buf_len) {
|
||||
if (a->buf+a->prev_offset == old_mem) {
|
||||
a->curr_offset = a->prev_offset + new_size;
|
||||
if (new_size > old_size) {
|
||||
// Zero the new memory by default
|
||||
memset(&a->buf[a->curr_offset], 0, new_size-old_size);
|
||||
}
|
||||
return old_memory;
|
||||
} else {
|
||||
void *new_memory = arena_alloc_align(a, new_size, align);
|
||||
size_t copy_size = old_size < new_size ? old_size : new_size;
|
||||
// Copy across old memory to the new memory
|
||||
memmove(new_memory, old_memory, copy_size);
|
||||
return new_memory;
|
||||
}
|
||||
|
||||
} else {
|
||||
assert(0 && "Memory is out of bounds of the buffer in this arena");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Because C doesn't have default parameters
|
||||
void *arena_resize(Arena *a, void *old_memory, size_t old_size, size_t new_size) {
|
||||
return arena_resize_align(a, old_memory, old_size, new_size, DEFAULT_ALIGNMENT);
|
||||
}
|
||||
|
||||
void arena_free_all(Arena *a) {
|
||||
a->curr_offset = 0;
|
||||
a->prev_offset = 0;
|
||||
}
|
||||
|
20
libs/arena/LICENSE
Normal file
20
libs/arena/LICENSE
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright 2022 Alexey Kutepov <reximkut@gmail.com>
|
||||
|
||||
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.
|
47
libs/arena/README.md
Normal file
47
libs/arena/README.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Arena Allocator
|
||||
|
||||
[Arena Allocator](https://en.wikipedia.org/wiki/Region-based_memory_management) implementation in pure C as an [stb-style single-file library](https://github.com/nothings/stb).
|
||||
|
||||
*I just caught myself implementing this over and over again in my projects, so I decided to turn it into a copy-pastable library similar to [sv](http://github.com/tsoding/sv)*
|
||||
|
||||
## Quick Start
|
||||
|
||||
> The truly reusable code is the one that you can simply copy-paste.
|
||||
|
||||
The library itself does not require any special building. You can simple copy-paste [./arena.h](./arena.h) to your project and `#include` it.
|
||||
|
||||
```c
|
||||
#define ARENA_IMPLEMENTATION
|
||||
#include "arena.h"
|
||||
|
||||
static Arena default_arena = {0};
|
||||
static Arena temporary_arena = {0};
|
||||
static Arena *context_arena = &default_arena;
|
||||
|
||||
void *context_alloc(size_t size)
|
||||
{
|
||||
assert(context_arena);
|
||||
return arena_alloc(context_arena, size);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// Allocate stuff in default_arena
|
||||
context_alloc(64);
|
||||
context_alloc(128);
|
||||
context_alloc(256);
|
||||
context_alloc(512);
|
||||
|
||||
// Allocate stuff in temporary_arena;
|
||||
context_arena = &temporary_arena;
|
||||
context_alloc(64);
|
||||
context_alloc(128);
|
||||
context_alloc(256);
|
||||
context_alloc(512);
|
||||
|
||||
// Deallocate everything at once
|
||||
arena_free(&default_arena);
|
||||
arena_free(&temporary_arena);
|
||||
return 0;
|
||||
}
|
||||
```
|
217
libs/arena/arena.h
Normal file
217
libs/arena/arena.h
Normal file
@ -0,0 +1,217 @@
|
||||
// Copyright 2022 Alexey Kutepov <reximkut@gmail.com>
|
||||
|
||||
// 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 ARENA_H_
|
||||
#define ARENA_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef ARENA_ASSERT
|
||||
#include <assert.h>
|
||||
#define ARENA_ASSERT assert
|
||||
#endif
|
||||
|
||||
#define ARENA_BACKEND_LIBC_MALLOC 0
|
||||
#define ARENA_BACKEND_LINUX_MMAP 1
|
||||
#define ARENA_BACKEND_WIN32_VIRTUALALLOC 2
|
||||
#define ARENA_BACKEND_WASM_HEAPBASE 3
|
||||
|
||||
#ifndef ARENA_BACKEND
|
||||
#define ARENA_BACKEND ARENA_BACKEND_LIBC_MALLOC
|
||||
#endif // ARENA_BACKEND
|
||||
|
||||
typedef struct Region Region;
|
||||
|
||||
struct Region {
|
||||
Region *next;
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
uintptr_t data[];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
Region *begin, *end;
|
||||
} Arena;
|
||||
|
||||
#define REGION_DEFAULT_CAPACITY (8*1024)
|
||||
|
||||
Region *new_region(size_t capacity);
|
||||
void free_region(Region *r);
|
||||
|
||||
// TODO: snapshot/rewind capability for the arena
|
||||
// - Snapshot should be combination of a->end and a->end->count.
|
||||
// - Rewinding should be restoring a->end and a->end->count from the snapshot and
|
||||
// setting count-s of all the Region-s after the remembered a->end to 0.
|
||||
void *arena_alloc(Arena *a, size_t size_bytes);
|
||||
void *arena_realloc(Arena *a, void *oldptr, size_t oldsz, size_t newsz);
|
||||
|
||||
void arena_reset(Arena *a);
|
||||
void arena_free(Arena *a);
|
||||
|
||||
#endif // ARENA_H_
|
||||
|
||||
#ifdef ARENA_IMPLEMENTATION
|
||||
|
||||
#if ARENA_BACKEND == ARENA_BACKEND_LIBC_MALLOC
|
||||
#include <stdlib.h>
|
||||
|
||||
// TODO: instead of accepting specific capacity new_region() should accept the size of the object we want to fit into the region
|
||||
// It should be up to new_region() to decide the actual capacity to allocate
|
||||
Region *new_region(size_t capacity)
|
||||
{
|
||||
size_t size_bytes = sizeof(Region) + sizeof(uintptr_t)*capacity;
|
||||
// TODO: it would be nice if we could guarantee that the regions are allocated by ARENA_BACKEND_LIBC_MALLOC are page aligned
|
||||
Region *r = malloc(size_bytes);
|
||||
ARENA_ASSERT(r);
|
||||
r->next = NULL;
|
||||
r->count = 0;
|
||||
r->capacity = capacity;
|
||||
return r;
|
||||
}
|
||||
|
||||
void free_region(Region *r)
|
||||
{
|
||||
free(r);
|
||||
}
|
||||
#elif ARENA_BACKEND == ARENA_BACKEND_LINUX_MMAP
|
||||
# error "TODO: Linux mmap backend is not implemented yet"
|
||||
#elif ARENA_BACKEND == ARENA_BACKEND_WIN32_VIRTUALALLOC
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# error "Current platform is not Windows"
|
||||
#endif
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#define INV_HANDLE(x) (((x) == NULL) || ((x) == INVALID_HANDLE_VALUE))
|
||||
|
||||
Region *new_region(size_t capacity)
|
||||
{
|
||||
SIZE_T size_bytes = sizeof(Region) + sizeof(uintptr_t) * capacity;
|
||||
Region *r = VirtualAllocEx(
|
||||
GetCurrentProcess(), /* Allocate in current process address space */
|
||||
NULL, /* Unknown position */
|
||||
size_bytes, /* Bytes to allocate */
|
||||
MEM_COMMIT | MEM_RESERVE, /* Reserve and commit allocated page */
|
||||
PAGE_READWRITE /* Permissions ( Read/Write )*/
|
||||
);
|
||||
if (INV_HANDLE(r))
|
||||
ARENA_ASSERT(0 && "VirtualAllocEx() failed.");
|
||||
|
||||
r->next = NULL;
|
||||
r->count = 0;
|
||||
r->capacity = capacity;
|
||||
return r;
|
||||
}
|
||||
|
||||
void free_region(Region *r)
|
||||
{
|
||||
if (INV_HANDLE(r))
|
||||
return;
|
||||
|
||||
BOOL free_result = VirtualFreeEx(
|
||||
GetCurrentProcess(), /* Deallocate from current process address space */
|
||||
(LPVOID)r, /* Address to deallocate */
|
||||
0, /* Bytes to deallocate ( Unknown, deallocate entire page ) */
|
||||
MEM_RELEASE /* Release the page ( And implicitly decommit it ) */
|
||||
);
|
||||
|
||||
if (FALSE == free_result)
|
||||
ARENA_ASSERT(0 && "VirtualFreeEx() failed.");
|
||||
}
|
||||
|
||||
#elif ARENA_BACKEND == ARENA_BACKEND_WASM_HEAPBASE
|
||||
# error "TODO: WASM __heap_base backend is not implemented yet"
|
||||
#else
|
||||
# error "Unknown Arena backend"
|
||||
#endif
|
||||
|
||||
// TODO: add debug statistic collection mode for arena
|
||||
// Should collect things like:
|
||||
// - How many times new_region was called
|
||||
// - How many times existing region was skipped
|
||||
// - How many times allocation exceeded REGION_DEFAULT_CAPACITY
|
||||
|
||||
void *arena_alloc(Arena *a, size_t size_bytes)
|
||||
{
|
||||
size_t size = (size_bytes + sizeof(uintptr_t) - 1)/sizeof(uintptr_t);
|
||||
|
||||
if (a->end == NULL) {
|
||||
ARENA_ASSERT(a->begin == NULL);
|
||||
size_t capacity = REGION_DEFAULT_CAPACITY;
|
||||
if (capacity < size) capacity = size;
|
||||
a->end = new_region(capacity);
|
||||
a->begin = a->end;
|
||||
}
|
||||
|
||||
while (a->end->count + size > a->end->capacity && a->end->next != NULL) {
|
||||
a->end = a->end->next;
|
||||
}
|
||||
|
||||
if (a->end->count + size > a->end->capacity) {
|
||||
ARENA_ASSERT(a->end->next == NULL);
|
||||
size_t capacity = REGION_DEFAULT_CAPACITY;
|
||||
if (capacity < size) capacity = size;
|
||||
a->end->next = new_region(capacity);
|
||||
a->end = a->end->next;
|
||||
}
|
||||
|
||||
void *result = &a->end->data[a->end->count];
|
||||
a->end->count += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
void *arena_realloc(Arena *a, void *oldptr, size_t oldsz, size_t newsz)
|
||||
{
|
||||
if (newsz <= oldsz) return oldptr;
|
||||
void *newptr = arena_alloc(a, newsz);
|
||||
char *newptr_char = newptr;
|
||||
char *oldptr_char = oldptr;
|
||||
for (size_t i = 0; i < oldsz; ++i) {
|
||||
newptr_char[i] = oldptr_char[i];
|
||||
}
|
||||
return newptr;
|
||||
}
|
||||
|
||||
void arena_reset(Arena *a)
|
||||
{
|
||||
for (Region *r = a->begin; r != NULL; r = r->next) {
|
||||
r->count = 0;
|
||||
}
|
||||
|
||||
a->end = a->begin;
|
||||
}
|
||||
|
||||
void arena_free(Arena *a)
|
||||
{
|
||||
Region *r = a->begin;
|
||||
while (r) {
|
||||
Region *r0 = r;
|
||||
r = r->next;
|
||||
free_region(r0);
|
||||
}
|
||||
a->begin = NULL;
|
||||
a->end = NULL;
|
||||
}
|
||||
|
||||
#endif // ARENA_IMPLEMENTATION
|
113
rendering.h
Normal file
113
rendering.h
Normal file
@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
void checkCode(int code, char* errorMsg) {
|
||||
if (code < 0) {
|
||||
fprintf(stderr, "Application Error %i: %s\n", code, errorMsg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void* checkPtr(void *ptr, char* errorMsg) {
|
||||
if (ptr == NULL) {
|
||||
fprintf(stderr, "Application Error: %s\n", errorMsg);
|
||||
exit(1);
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void checkShader(unsigned int shader, int statusFlag, char* actionName) {
|
||||
int success;
|
||||
glGetShaderiv(shader, statusFlag, &success);
|
||||
if (success < 0) {
|
||||
fprintf(stderr, "%s Error %i\n", actionName, success);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
char* loadText(char* path) {
|
||||
char* buffer = NULL;
|
||||
long length;
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (f) {
|
||||
fseek(f, 0, SEEK_END);
|
||||
length = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
buffer = calloc(length, sizeof(char));
|
||||
if (buffer) {
|
||||
fread(buffer, 1, length, f);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void checkCompileErrors(unsigned int object, char* type)
|
||||
{
|
||||
int success;
|
||||
char infoLog[1024];
|
||||
if (strcmp("PROGRAM", type))
|
||||
{
|
||||
glGetShaderiv(object, GL_COMPILE_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
glGetShaderInfoLog(object, 1024, NULL, infoLog);
|
||||
fprintf(stderr, "| ERROR::SHADER: Compile-time error: Type: %s \n %s\n", type, infoLog);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
glGetProgramiv(object, GL_LINK_STATUS, &success);
|
||||
if (!success)
|
||||
{
|
||||
glGetProgramInfoLog(object, 1024, NULL, infoLog);
|
||||
fprintf(stderr, "| ERROR::Shader: Link-time error: Type: %s \n %s\n", type, infoLog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int compileShaderProgram(char* vertSrcPath, char* fragSrcPath, char* geoSrcPath) {
|
||||
unsigned int vertShader, fragShader, geoShader;
|
||||
|
||||
char* vertSrc = loadText(vertSrcPath);
|
||||
vertShader = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertShader, 1, (const GLchar* const*)&vertSrc, NULL);
|
||||
glCompileShader(vertShader);
|
||||
checkCompileErrors(vertShader, "VERTEX");
|
||||
|
||||
char* fragSrc = loadText(fragSrcPath);
|
||||
|
||||
fragShader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fragShader, 1, (const GLchar* const*)&fragSrc, NULL);
|
||||
glCompileShader(fragShader);
|
||||
checkCompileErrors(fragShader, "FRAGMENT");
|
||||
|
||||
char* geoSrc = NULL;
|
||||
if (geoSrcPath != NULL) {
|
||||
geoSrc = loadText(geoSrcPath);
|
||||
geoShader = glCreateShader(GL_GEOMETRY_SHADER);
|
||||
glShaderSource(geoShader, 1, (const GLchar* const*)&geoSrc, NULL);
|
||||
glCompileShader(geoShader);
|
||||
checkCompileErrors(geoShader, "GEOMETRY");
|
||||
}
|
||||
|
||||
unsigned int program = glCreateProgram();
|
||||
glAttachShader(program, vertShader);
|
||||
glAttachShader(program, fragShader);
|
||||
if (geoSrcPath != NULL) {
|
||||
glAttachShader(program, geoShader);
|
||||
}
|
||||
|
||||
glLinkProgram(program);
|
||||
checkCompileErrors(program, "PROGRAM");
|
||||
|
||||
glDeleteShader(vertShader);
|
||||
glDeleteShader(fragShader);
|
||||
free(vertSrc);
|
||||
free(fragSrc);
|
||||
if (geoSrc != NULL) {
|
||||
free(geoSrc);
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
176
stack.h
Normal file
176
stack.h
Normal file
@ -0,0 +1,176 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef DEFAULT_ALIGNMENT
|
||||
#define DEFAULT_ALIGNMENT (2*sizeof(void *))
|
||||
#endif
|
||||
|
||||
bool is_power_of_two(uintptr_t x) {
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
typedef struct Stack {
|
||||
unsigned char *buf;
|
||||
size_t buf_len;
|
||||
size_t prev_offset;
|
||||
size_t curr_offset;
|
||||
} Stack;
|
||||
|
||||
typedef struct Stack_Allocation_Header {
|
||||
size_t prev_offset;
|
||||
size_t padding;
|
||||
} Stack_Allocation_Header;
|
||||
|
||||
void stack_init(Stack *s, void *backing_buffer, size_t backing_buffer_length) {
|
||||
s->buf = (unsigned char *)backing_buffer;
|
||||
s->buf_len = backing_buffer_length;
|
||||
s->prev_offset = 0;
|
||||
s->curr_offset = 0;
|
||||
}
|
||||
size_t calc_padding_with_header(uintptr_t ptr, uintptr_t alignment, size_t header_size) {
|
||||
uintptr_t p, a, modulo, padding, needed_space;
|
||||
|
||||
assert(is_power_of_two(alignment));
|
||||
|
||||
p = ptr;
|
||||
a = alignment;
|
||||
modulo = p & (a-1); // (p % a) as it assumes alignment is a power of two
|
||||
|
||||
padding = 0;
|
||||
needed_space = 0;
|
||||
|
||||
if (modulo != 0) { // Same logic as 'align_forward'
|
||||
padding = a - modulo;
|
||||
}
|
||||
|
||||
needed_space = (uintptr_t)header_size;
|
||||
|
||||
if (padding < needed_space) {
|
||||
needed_space -= padding;
|
||||
|
||||
if ((needed_space & (a-1)) != 0) {
|
||||
padding += a * (1+(needed_space/a));
|
||||
} else {
|
||||
padding += a * (needed_space/a);
|
||||
}
|
||||
}
|
||||
|
||||
return (size_t)padding;
|
||||
}
|
||||
|
||||
void *stack_alloc_align(Stack *s, size_t size, size_t alignment) {
|
||||
uintptr_t curr_addr, next_addr;
|
||||
size_t padding;
|
||||
Stack_Allocation_Header *header;
|
||||
|
||||
|
||||
assert(is_power_of_two(alignment));
|
||||
|
||||
if (alignment > 128) {
|
||||
// As the padding is 8 bits (1 byte), the largest alignment that can
|
||||
// be used is 128 bytes
|
||||
alignment = 128;
|
||||
}
|
||||
|
||||
curr_addr = (uintptr_t)s->buf + (uintptr_t)s->curr_offset;
|
||||
usize sah_size = sizeof(Stack_Allocation_Header);
|
||||
padding = calc_padding_with_header(curr_addr, (uintptr_t)alignment, sah_size);
|
||||
if (s->curr_offset + padding + size > s->buf_len) {
|
||||
// Stack allocator is out of memory
|
||||
return NULL;
|
||||
}
|
||||
|
||||
s->prev_offset = s->curr_offset; // Store the previous offset
|
||||
s->curr_offset += padding;
|
||||
|
||||
next_addr = curr_addr + (uintptr_t)padding;
|
||||
header = (Stack_Allocation_Header *)(next_addr - sizeof(Stack_Allocation_Header));
|
||||
header->padding = padding;
|
||||
header->prev_offset = s->prev_offset; // store the previous offset in the header
|
||||
|
||||
s->curr_offset += size;
|
||||
|
||||
return memset((void *)next_addr, 0, size);
|
||||
}
|
||||
|
||||
// Because C does not have default parameters
|
||||
void *stack_alloc(Stack *s, size_t size) {
|
||||
return stack_alloc_align(s, size, DEFAULT_ALIGNMENT);
|
||||
}
|
||||
|
||||
void stack_free(Stack *s, void *ptr) {
|
||||
if (ptr != NULL) {
|
||||
uintptr_t start, end, curr_addr;
|
||||
Stack_Allocation_Header *header;
|
||||
size_t prev_offset;
|
||||
|
||||
start = (uintptr_t)s->buf;
|
||||
end = start + (uintptr_t)s->buf_len;
|
||||
curr_addr = (uintptr_t)ptr;
|
||||
|
||||
if (!(start <= curr_addr && curr_addr < end)) {
|
||||
assert(0 && "Out of bounds memory address passed to stack allocator (free)");
|
||||
return;
|
||||
}
|
||||
|
||||
if curr_addr >= start+(uintptr_t)s->offset {
|
||||
// Allow double frees
|
||||
return;
|
||||
}
|
||||
|
||||
header = (Stack_Allocation_Header *)(curr_addr - sizeof(Stack_Allocation_Header));
|
||||
|
||||
// Calculate previous offset from the header and its address
|
||||
prev_offset = (size_t)(curr_addr - (uintptr_t)header->padding - start);
|
||||
|
||||
if (prev_offset != header->prev_offset) {
|
||||
assert(0 && "Out of order stack allocator free");
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the offsets to the previous allocation
|
||||
s->curr_offset = s->prev_offset;
|
||||
s->prev_offset = header->prev_offset;
|
||||
}
|
||||
}
|
||||
|
||||
void *stack_resize_align(Stack *s, void *ptr, size_t old_size, size_t new_size, size_t alignment) {
|
||||
if (ptr == NULL) {
|
||||
return stack_alloc_align(s, new_size, alignment);
|
||||
} else if (new_size == 0) {
|
||||
stack_free(s, ptr);
|
||||
return NULL;
|
||||
} else {
|
||||
uintptr_t start, end, curr_addr;
|
||||
size_t min_size = old_size < new_size ? old_size : new_size;
|
||||
void *new_ptr;
|
||||
|
||||
start = (uintptr_t)s->buf;
|
||||
end = start + (uintptr_t)s->buf_len;
|
||||
curr_addr = (uintptr_t)ptr;
|
||||
if (!(start <= curr_addr && curr_addr < end)) {
|
||||
assert(0 && "Out of bounds memory address passed to stack allocator (resize)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (curr_addr >= start + (uintptr_t)s->offset) {
|
||||
// Treat as a double free
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if old_size == size {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
new_ptr = stack_alloc_align(s, new_size, alignment);
|
||||
memmove(new_ptr, ptr, min_size);
|
||||
return new_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
void *stack_resize(Stack *s, void *ptr, size_t old_size, size_t new_size) {
|
||||
return stack_resize_align(s, ptr, old_size, new_size, DEFAULT_ALIGNMENT);
|
||||
}
|
||||
|
||||
void stack_free_all(Stack *s) {
|
||||
s->offset = 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user