opengl moment #1
@ -23,6 +23,14 @@ add_subdirectory(third-party/physfs)
|
|||||||
add_subdirectory(third-party/libxm)
|
add_subdirectory(third-party/libxm)
|
||||||
|
|
||||||
|
|
||||||
|
if(UNIX)
|
||||||
|
set(SYSTEM_SOURCE_FILES
|
||||||
|
src/system/linux/elf.c
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
set(SYSTEM_SOURCE_FILES)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(SOURCE_FILES
|
set(SOURCE_FILES
|
||||||
third-party/physfs/extras/physfsrwops.c
|
third-party/physfs/extras/physfsrwops.c
|
||||||
third-party/stb/stb_vorbis.c
|
third-party/stb/stb_vorbis.c
|
||||||
@ -49,6 +57,8 @@ set(SOURCE_FILES
|
|||||||
src/game/scenes/scene.c src/game/scenes/scene.h
|
src/game/scenes/scene.c src/game/scenes/scene.h
|
||||||
src/game/scenes/title.c src/game/scenes/title.h
|
src/game/scenes/title.c src/game/scenes/title.h
|
||||||
src/game/scenes/ingame.c src/game/scenes/ingame.h
|
src/game/scenes/ingame.c src/game/scenes/ingame.h
|
||||||
|
|
||||||
|
${SYSTEM_SOURCE_FILES}
|
||||||
)
|
)
|
||||||
|
|
||||||
# target
|
# target
|
||||||
@ -90,19 +100,24 @@ else()
|
|||||||
# these SHOULDN'T break anything...
|
# these SHOULDN'T break anything...
|
||||||
-fno-math-errno
|
-fno-math-errno
|
||||||
-ffp-contract=fast
|
-ffp-contract=fast
|
||||||
-fno-signed-zeros
|
-fno-signed-zeros)
|
||||||
-fno-trapping-math
|
|
||||||
-freciprocal-math)
|
|
||||||
set(BUILD_FLAGS_RELEASE
|
set(BUILD_FLAGS_RELEASE
|
||||||
|
-O3
|
||||||
-flto
|
-flto
|
||||||
-Wl,-gc-sections
|
-Wl,-gc-sections
|
||||||
-fdata-sections
|
-fdata-sections
|
||||||
-ffunction-sections)
|
-ffunction-sections
|
||||||
|
-funroll-loops
|
||||||
|
-fomit-frame-pointer
|
||||||
|
-fallow-store-data-races)
|
||||||
set(BUILD_FLAGS_DEBUG
|
set(BUILD_FLAGS_DEBUG
|
||||||
|
-O0
|
||||||
-g3
|
-g3
|
||||||
-gdwarf
|
-gdwarf
|
||||||
|
-fno-omit-frame-pointer
|
||||||
|
-fstack-protector-all
|
||||||
-fsanitize=undefined
|
-fsanitize=undefined
|
||||||
-fsanitize-trap=undefined)
|
-fsanitize=address)
|
||||||
target_compile_options(${PROJECT_NAME} PRIVATE
|
target_compile_options(${PROJECT_NAME} PRIVATE
|
||||||
${WARNING_FLAGS}
|
${WARNING_FLAGS}
|
||||||
${BUILD_FLAGS}
|
${BUILD_FLAGS}
|
||||||
|
@ -244,7 +244,7 @@ static void audio_sample_and_mixin_channel(const struct audio_channel *channel,
|
|||||||
static uint8_t buffer[16384];
|
static uint8_t buffer[16384];
|
||||||
const int int16_buffer_frames = sizeof (buffer) / sizeof (int16_t);
|
const int int16_buffer_frames = sizeof (buffer) / sizeof (int16_t);
|
||||||
const int float_buffer_frames = sizeof (buffer) / sizeof (float);
|
const int float_buffer_frames = sizeof (buffer) / sizeof (float);
|
||||||
const int stream_frames = len / sizeof (int16_t);
|
const int stream_frames = len / (int)(sizeof (int16_t));
|
||||||
|
|
||||||
switch (channel->file_type) {
|
switch (channel->file_type) {
|
||||||
case audio_file_type_ogg: {
|
case audio_file_type_ogg: {
|
||||||
|
@ -65,6 +65,7 @@ static void ingame_end(struct state *state) {
|
|||||||
struct scene_ingame *scn = (struct scene_ingame *)state->scene;
|
struct scene_ingame *scn = (struct scene_ingame *)state->scene;
|
||||||
player_destroy(scn->player);
|
player_destroy(scn->player);
|
||||||
world_destroy(scn->world);
|
world_destroy(scn->world);
|
||||||
|
free(state->scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ static void title_end(struct state *state) {
|
|||||||
struct scene_title *scn = (struct scene_title *)state->scene;
|
struct scene_title *scn = (struct scene_title *)state->scene;
|
||||||
player_destroy(scn->player);
|
player_destroy(scn->player);
|
||||||
world_destroy(scn->world);
|
world_destroy(scn->world);
|
||||||
|
free(state->scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -95,7 +95,6 @@ union uncolored_space_triangle {
|
|||||||
/* batch of primitives with overlapping properties */
|
/* batch of primitives with overlapping properties */
|
||||||
struct mesh_batch {
|
struct mesh_batch {
|
||||||
GLuint buffer; /* server side storage */
|
GLuint buffer; /* server side storage */
|
||||||
size_t buffer_len; /* element count */
|
|
||||||
uint8_t *primitives;
|
uint8_t *primitives;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
68
src/system/linux/elf.c
Normal file
68
src/system/linux/elf.c
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include "elf.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/auxv.h>
|
||||||
|
#include <elf.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
bool infer_elf_section_bounds(const char *const restrict name,
|
||||||
|
const char **restrict vm_start,
|
||||||
|
const char **restrict vm_end)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
ssize_t l = readlink("/proc/self/exe", buf, PATH_MAX);
|
||||||
|
if (l == -1)
|
||||||
|
goto ERR_CANT_READLINK;
|
||||||
|
buf[l] = 0; /* readlink() doesn't write a terminator */
|
||||||
|
|
||||||
|
int elf = open(buf, O_RDONLY);
|
||||||
|
if (elf == -1)
|
||||||
|
goto ERR_CANT_OPEN_SELF;
|
||||||
|
|
||||||
|
/* elf header */
|
||||||
|
Elf64_Ehdr ehdr;
|
||||||
|
read(elf, &ehdr, sizeof ehdr);
|
||||||
|
if (ehdr.e_ident[EI_MAG0] != ELFMAG0 ||
|
||||||
|
ehdr.e_ident[EI_MAG1] != ELFMAG1 ||
|
||||||
|
ehdr.e_ident[EI_MAG2] != ELFMAG2 ||
|
||||||
|
ehdr.e_ident[EI_MAG3] != ELFMAG3)
|
||||||
|
goto ERR_NOT_ELF;
|
||||||
|
|
||||||
|
/* section header string table */
|
||||||
|
Elf64_Shdr shstrdr;
|
||||||
|
lseek(elf, ehdr.e_shoff + ehdr.e_shstrndx * sizeof (Elf64_Shdr), SEEK_SET);
|
||||||
|
read(elf, &shstrdr, sizeof shstrdr);
|
||||||
|
char *sh = malloc(shstrdr.sh_size);
|
||||||
|
lseek(elf, shstrdr.sh_offset, SEEK_SET);
|
||||||
|
read(elf, sh, shstrdr.sh_size);
|
||||||
|
|
||||||
|
/* walk sections searching for needed name */
|
||||||
|
lseek(elf, ehdr.e_shoff, SEEK_SET);
|
||||||
|
for (size_t s = 0; s < ehdr.e_shnum; ++s) {
|
||||||
|
Elf64_Shdr shdr;
|
||||||
|
read(elf, &shdr, sizeof shdr);
|
||||||
|
|
||||||
|
if (strcmp(&sh[shdr.sh_name], name) == 0) {
|
||||||
|
result = true;
|
||||||
|
*vm_start = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr;
|
||||||
|
*vm_end = getauxval(AT_ENTRY) - ehdr.e_entry + (char *)shdr.sh_addr + shdr.sh_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sh);
|
||||||
|
|
||||||
|
ERR_NOT_ELF:
|
||||||
|
close(elf);
|
||||||
|
|
||||||
|
ERR_CANT_OPEN_SELF:
|
||||||
|
ERR_CANT_READLINK:
|
||||||
|
return result;
|
||||||
|
}
|
10
src/system/linux/elf.h
Normal file
10
src/system/linux/elf.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef ELF_H
|
||||||
|
#define ELF_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
bool infer_elf_section_bounds(const char *restrict name,
|
||||||
|
const char **restrict vm_start,
|
||||||
|
const char **restrict vm_end);
|
||||||
|
|
||||||
|
#endif
|
@ -295,8 +295,9 @@ void textures_dump_atlases(struct texture_cache *cache) {
|
|||||||
|
|
||||||
static t_texture_key textures_load(struct texture_cache *cache, const char *path) {
|
static t_texture_key textures_load(struct texture_cache *cache, const char *path) {
|
||||||
/* no need to do anything if it was loaded already */
|
/* no need to do anything if it was loaded already */
|
||||||
if (shgeti(cache->hash, path) >= 0)
|
const ptrdiff_t i = shgeti(cache->hash, path);
|
||||||
return (t_texture_key){0};
|
if (i >= 0)
|
||||||
|
return (t_texture_key){ (uint16_t)i };
|
||||||
|
|
||||||
SDL_Surface *surface = image_to_surface(path);
|
SDL_Surface *surface = image_to_surface(path);
|
||||||
struct texture new_texture = {0};
|
struct texture new_texture = {0};
|
||||||
@ -308,12 +309,12 @@ static t_texture_key textures_load(struct texture_cache *cache, const char *path
|
|||||||
upload_texture_from_surface(new_texture.loner_texture, surface);
|
upload_texture_from_surface(new_texture.loner_texture, surface);
|
||||||
new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h };
|
new_texture.srcrect = (t_rect) { .w = surface->w, .h = surface->h };
|
||||||
shput(cache->hash, path, new_texture);
|
shput(cache->hash, path, new_texture);
|
||||||
return (t_texture_key){ shgeti(cache->hash, path) };
|
return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) };
|
||||||
} else {
|
} else {
|
||||||
new_texture.atlas_index = cache->atlas_index;
|
new_texture.atlas_index = cache->atlas_index;
|
||||||
shput(cache->hash, path, new_texture);
|
shput(cache->hash, path, new_texture);
|
||||||
cache->is_dirty = true;
|
cache->is_dirty = true;
|
||||||
return (t_texture_key){ shgeti(cache->hash, path) };
|
return (t_texture_key){ (uint16_t)shgeti(cache->hash, path) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,35 +364,44 @@ void textures_update_atlas(struct texture_cache *cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* EXPERIMANTAL: LIKELY TO BE REMOVED! */
|
/* EXPERIMANTAL: LIKELY TO BE REMOVED! */
|
||||||
/* todo: If it's proven to be useful: add runtime checking for .rodata > .data */
|
#ifdef __linux__ /* use rodata elf section for fast lookups of repeating textures */
|
||||||
#ifdef __unix__ /* use rodata elf section for fast lookups of repeating textures */
|
|
||||||
|
|
||||||
extern const char start_rodata_address[];
|
#include "system/linux/elf.h"
|
||||||
extern const char stop_rodata_heuristic[];
|
|
||||||
|
|
||||||
asm(".set start_rodata_address, .rodata");
|
static const char *rodata_start;
|
||||||
asm(".set stop_rodata_heuristic, .data"); /* there's nothing in default linker script to know the size of .rodata */
|
static const char *rodata_stop;
|
||||||
|
|
||||||
/* TODO: it might be better to contruct a new table that hashes pointers, not strings, to texture keys */
|
|
||||||
/* this way every used texture will benefit, no matter the order of commission */
|
|
||||||
t_texture_key textures_get_key(struct texture_cache *cache, const char *path) {
|
t_texture_key textures_get_key(struct texture_cache *cache, const char *path) {
|
||||||
static const char *last_path = NULL;
|
static const char *last_path = NULL;
|
||||||
static t_texture_key last_texture;
|
static t_texture_key last_texture;
|
||||||
|
static struct ptr_to_texture {
|
||||||
|
const void *key;
|
||||||
|
t_texture_key value;
|
||||||
|
} *ptr_to_texture;
|
||||||
|
|
||||||
/* fast path */
|
if (rodata_stop == NULL)
|
||||||
|
if (!infer_elf_section_bounds(".rodata", &rodata_start, &rodata_stop))
|
||||||
|
CRY("Section inference", ".rodata section lookup failed");
|
||||||
|
|
||||||
|
/* the fastest path */
|
||||||
if (path == last_path)
|
if (path == last_path)
|
||||||
return last_texture;
|
return last_texture;
|
||||||
|
else {
|
||||||
|
/* moderately fast path, by pointer hashing */
|
||||||
|
const ptrdiff_t texture = hmgeti(ptr_to_texture, path);
|
||||||
|
if (texture != -1) {
|
||||||
|
if (path >= rodata_start && path < rodata_stop)
|
||||||
|
last_path = path;
|
||||||
|
last_texture = ptr_to_texture[texture].value;
|
||||||
|
return last_texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* hash tables are assumed to be stable, so we just return indices */
|
/* try loading */
|
||||||
ptrdiff_t texture = shgeti(cache->hash, path);
|
|
||||||
|
|
||||||
/* load it if it isn't */
|
|
||||||
if (texture == -1) {
|
|
||||||
last_texture = textures_load(cache, path);
|
last_texture = textures_load(cache, path);
|
||||||
} else
|
hmput(ptr_to_texture, path, last_texture);
|
||||||
last_texture = (t_texture_key){ (uint16_t)texture };
|
|
||||||
|
|
||||||
if (path >= start_rodata_address && path < stop_rodata_heuristic)
|
if (path >= rodata_start && path < rodata_stop)
|
||||||
last_path = path;
|
last_path = path;
|
||||||
|
|
||||||
return last_texture;
|
return last_texture;
|
||||||
@ -400,7 +410,7 @@ t_texture_key textures_get_key(struct texture_cache *cache, const char *path) {
|
|||||||
#else
|
#else
|
||||||
t_texture_key textures_get_key(struct texture_cache *cache, const char *path) {
|
t_texture_key textures_get_key(struct texture_cache *cache, const char *path) {
|
||||||
/* hash tables are assumed to be stable, so we just return indices */
|
/* hash tables are assumed to be stable, so we just return indices */
|
||||||
ptrdiff_t texture = shgeti(cache->hash, path);
|
const ptrdiff_t texture = shgeti(cache->hash, path);
|
||||||
|
|
||||||
/* load it if it isn't */
|
/* load it if it isn't */
|
||||||
if (texture == -1) {
|
if (texture == -1) {
|
||||||
|
6
third-party/stb/stb_ds.h
vendored
6
third-party/stb/stb_ds.h
vendored
@ -1121,7 +1121,7 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
|
|||||||
unsigned char *d = (unsigned char *) p;
|
unsigned char *d = (unsigned char *) p;
|
||||||
|
|
||||||
if (len == 4) {
|
if (len == 4) {
|
||||||
unsigned int hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
|
uint32_t hash = (uint32_t)d[0] | ((uint32_t)d[1] << 8) | ((uint32_t)d[2] << 16) | ((uint32_t)d[3] << 24);
|
||||||
#if 0
|
#if 0
|
||||||
// HASH32-A Bob Jenkin's hash function w/o large constants
|
// HASH32-A Bob Jenkin's hash function w/o large constants
|
||||||
hash ^= seed;
|
hash ^= seed;
|
||||||
@ -1177,8 +1177,8 @@ size_t stbds_hash_bytes(void *p, size_t len, size_t seed)
|
|||||||
|
|
||||||
return (((size_t) hash << 16 << 16) | hash) ^ seed;
|
return (((size_t) hash << 16 << 16) | hash) ^ seed;
|
||||||
} else if (len == 8 && sizeof(size_t) == 8) {
|
} else if (len == 8 && sizeof(size_t) == 8) {
|
||||||
size_t hash = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
|
size_t hash = (size_t)d[0] | ((size_t)d[1] << 8) | ((size_t)d[2] << 16) | ((size_t)d[3] << 24);
|
||||||
hash |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // avoid warning if size_t == 4
|
hash |= ((size_t)d[4] | ((size_t)d[5] << 8) | ((size_t)d[6] << 16) | ((size_t)d[7] << 24)) << 16 << 16;
|
||||||
hash ^= seed;
|
hash ^= seed;
|
||||||
hash = (~hash) + (hash << 21);
|
hash = (~hash) + (hash << 21);
|
||||||
hash ^= STBDS_ROTATE_RIGHT(hash,24);
|
hash ^= STBDS_ROTATE_RIGHT(hash,24);
|
||||||
|
Loading…
Reference in New Issue
Block a user