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
|
build: clean
|
||||||
$(CC) $(CFLAGS) $(LDLIBS) $(INCLUDES) $(OBJECTS) $(P).c -o $(P)
|
$(CC) $(CFLAGS) $(LDLIBS) $(INCLUDES) $(OBJECTS) $(P).c -o $(P)
|
||||||
|
|
||||||
.PHONY: texturepacker
|
.PHONY: texpack
|
||||||
texturepacker:
|
texpack:
|
||||||
$(CC) $(CFLAGS) -lSDL2 -lm -lcyaml -L objs/ $(INCLUDES) tools/texturepacker.c -o texturepacker
|
$(CC) $(CFLAGS) -lSDL2 -lm -lcyaml -L objs/ $(INCLUDES) tools/texturepacker.c -o texpack
|
||||||
|
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: build
|
run: build
|
||||||
|
132
lib.h
132
lib.h
@ -2,6 +2,8 @@
|
|||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#define ARENA_IMPLEMENTATION
|
||||||
|
#include "libs/arena/arena.h"
|
||||||
|
|
||||||
typedef uint8_t u8;
|
typedef uint8_t u8;
|
||||||
// typedef char16_t c16;
|
// typedef char16_t c16;
|
||||||
@ -25,115 +27,33 @@ typedef struct Animation {
|
|||||||
u16 cols;
|
u16 cols;
|
||||||
u16 width;
|
u16 width;
|
||||||
u16 height;
|
u16 height;
|
||||||
} SpriteSheet;
|
} Animation;
|
||||||
|
|
||||||
void checkCode(int code, char* errorMsg) {
|
typedef struct s8 {
|
||||||
if (code < 0) {
|
u8* data;
|
||||||
fprintf(stderr, "Application Error %i: %s\n", code, errorMsg);
|
usize size;
|
||||||
exit(1);
|
} 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) {
|
void print_str(s8 string, bool print_nl) {
|
||||||
if (ptr == NULL) {
|
for (usize i = 0; i < string.size; i++) {
|
||||||
fprintf(stderr, "Application Error: %s\n", errorMsg);
|
printf("%c", string.data[i]);
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
return ptr;
|
if (print_nl) {
|
||||||
}
|
printf("\n");
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
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