[RELEASE] u22-v04
This version is submitted for U22 final presentation. (squashed 158 commits)
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
add_compile_options(${LEFTONE_C_FLAGS})
|
||||
|
||||
add_subdirectory(chaos)
|
||||
add_subdirectory(coly2d)
|
||||
add_subdirectory(container)
|
||||
add_subdirectory(conv)
|
||||
add_subdirectory(dictres)
|
||||
add_subdirectory(flasy)
|
||||
add_subdirectory(gleasy)
|
||||
add_subdirectory(glyphas)
|
||||
add_subdirectory(jukebox)
|
||||
@@ -10,3 +13,4 @@ add_subdirectory(math)
|
||||
add_subdirectory(memory)
|
||||
add_subdirectory(mpkutil)
|
||||
add_subdirectory(parsarg)
|
||||
add_subdirectory(statman)
|
||||
|
18
util/flasy/CMakeLists.txt
Normal file
18
util/flasy/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
add_library(flasy
|
||||
flasy.c
|
||||
)
|
||||
target_link_libraries(flasy
|
||||
memory
|
||||
tinycthread
|
||||
)
|
||||
if (BUILD_TESTING)
|
||||
add_executable(flasy-test test.c)
|
||||
target_link_libraries(flasy-test flasy)
|
||||
|
||||
set(workdir "${CMAKE_CURRENT_BINARY_DIR}/test")
|
||||
file(MAKE_DIRECTORY ${workdir})
|
||||
add_test(
|
||||
NAME flasy-test
|
||||
COMMAND bash -c "../flasy-test $(seq 9)"
|
||||
WORKING_DIRECTORY ${workdir})
|
||||
endif()
|
4
util/flasy/README.md
Normal file
4
util/flasy/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
filasy
|
||||
====
|
||||
|
||||
ASYnc file FLusher
|
146
util/flasy/flasy.c
Normal file
146
util/flasy/flasy.c
Normal file
@@ -0,0 +1,146 @@
|
||||
#include "./flasy.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <tinycthread.h>
|
||||
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
typedef struct {
|
||||
atomic_uintptr_t fp;
|
||||
atomic_bool request_flush;
|
||||
uint8_t buf[1];
|
||||
} flasy_handler_t;
|
||||
|
||||
struct flasy_t {
|
||||
mtx_t mtx;
|
||||
thrd_t thread;
|
||||
|
||||
size_t bufsz;
|
||||
|
||||
atomic_bool alive;
|
||||
|
||||
size_t length;
|
||||
uint8_t handlers[1];
|
||||
};
|
||||
|
||||
static int flasy_main_(void* srv_) {
|
||||
assert(srv_ != NULL);
|
||||
flasy_t* srv = srv_;
|
||||
|
||||
const size_t hsize = srv->bufsz + offsetof(flasy_handler_t, buf);
|
||||
uint8_t* end = srv->handlers + hsize*srv->length;
|
||||
|
||||
for (;;) {
|
||||
const bool alive = atomic_load(&srv->alive);
|
||||
|
||||
for (uint8_t* itr = srv->handlers; itr < end; itr += hsize) {
|
||||
flasy_handler_t* handler = (typeof(handler)) itr;
|
||||
|
||||
FILE* fp = (typeof(fp)) atomic_load(&handler->fp);
|
||||
if (fp != NULL && atomic_load(&handler->request_flush)) {
|
||||
/* while request_flush is true and fp isn't NULL,
|
||||
no other thread modifies the handler */
|
||||
fclose(fp);
|
||||
atomic_store(&handler->fp, 0);
|
||||
}
|
||||
}
|
||||
thrd_sleep(&(struct timespec) {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 10000000, /* = 10 ms */
|
||||
}, NULL);
|
||||
|
||||
if (!alive) break;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
flasy_t* flasy_new(size_t bufsz, size_t hlen) {
|
||||
assert(bufsz > 0);
|
||||
assert(hlen > 0);
|
||||
|
||||
const size_t hsize = bufsz + offsetof(flasy_handler_t, buf);
|
||||
|
||||
flasy_t* srv = memory_new(sizeof(*srv) + hlen*hsize - 1);
|
||||
*srv = (typeof(*srv)) {
|
||||
.bufsz = bufsz,
|
||||
.length = hlen,
|
||||
};
|
||||
|
||||
atomic_store(&srv->alive, true);
|
||||
|
||||
uint8_t* itr = srv->handlers;
|
||||
for (size_t i = 0; i < srv->length; ++i) {
|
||||
atomic_init(&((flasy_handler_t*) itr)->fp, 0);
|
||||
itr += hsize;
|
||||
}
|
||||
|
||||
if (mtx_init(&srv->mtx, mtx_plain) != thrd_success) {
|
||||
fprintf(stderr, "flasy: failed to create mutex\n");
|
||||
abort();
|
||||
}
|
||||
if (thrd_create(&srv->thread, &flasy_main_, srv) != thrd_success) {
|
||||
fprintf(stderr, "flasy: failed to create thread\n");
|
||||
abort();
|
||||
}
|
||||
return srv;
|
||||
}
|
||||
|
||||
void flasy_delete(flasy_t* srv) {
|
||||
if (srv == NULL) return;
|
||||
|
||||
atomic_store(&srv->alive, false);
|
||||
thrd_join(srv->thread, NULL);
|
||||
|
||||
mtx_destroy(&srv->mtx);
|
||||
|
||||
memory_delete(srv);
|
||||
}
|
||||
|
||||
FILE* flasy_open_file(flasy_t* srv, const char* path, bool binary) {
|
||||
assert(srv != NULL);
|
||||
assert(path != NULL);
|
||||
|
||||
const size_t hsize = srv->bufsz + offsetof(flasy_handler_t, buf);
|
||||
|
||||
uint8_t* itr = srv->handlers;
|
||||
uint8_t* end = srv->handlers + hsize*srv->length;
|
||||
for (; itr < end; itr += hsize) {
|
||||
if (atomic_load(&((flasy_handler_t*) itr)->fp) == 0) break;
|
||||
}
|
||||
if (itr >= end) return NULL;
|
||||
|
||||
FILE* fp = fopen(path, binary? "wb": "w");
|
||||
if (fp == NULL) return NULL;
|
||||
|
||||
flasy_handler_t* handler = (typeof(handler)) itr;
|
||||
/* while handler->fp is NULL, no other threads modifies the handler */
|
||||
|
||||
setvbuf(fp, (char*) handler->buf, _IOFBF, srv->bufsz);
|
||||
atomic_store(&handler->request_flush, false);
|
||||
atomic_store(&handler->fp, (uintptr_t) fp);
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
void flasy_close_file(flasy_t* srv, FILE* fp) {
|
||||
assert(srv != NULL);
|
||||
assert(fp != NULL);
|
||||
|
||||
const size_t hsize = srv->bufsz + offsetof(flasy_handler_t, buf);
|
||||
|
||||
uint8_t* itr = srv->handlers;
|
||||
uint8_t* end = srv->handlers + hsize*srv->length;
|
||||
for (; itr < end; itr += hsize) {
|
||||
if (atomic_load(&((flasy_handler_t*) itr)->fp) == (uintptr_t) fp) break;
|
||||
}
|
||||
assert(itr < end);
|
||||
|
||||
flasy_handler_t* handler = (typeof(handler)) itr;
|
||||
atomic_store(&handler->request_flush, true);
|
||||
}
|
34
util/flasy/flasy.h
Normal file
34
util/flasy/flasy.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* plz note that all functions are not thread-safe */
|
||||
|
||||
struct flasy_t;
|
||||
typedef struct flasy_t flasy_t;
|
||||
|
||||
flasy_t* /* OWNERSHIP */
|
||||
flasy_new(
|
||||
size_t bufsz,
|
||||
size_t hlen
|
||||
);
|
||||
|
||||
void
|
||||
flasy_delete(
|
||||
flasy_t* srv /* OWNERSHIP */
|
||||
);
|
||||
|
||||
FILE* /* OWNERSHIP/NULLABLE */
|
||||
flasy_open_file(
|
||||
flasy_t* srv,
|
||||
const char* path,
|
||||
bool binary
|
||||
);
|
||||
|
||||
void
|
||||
flasy_close_file(
|
||||
flasy_t* srv,
|
||||
FILE* fp /* OWNERSHIP */
|
||||
);
|
46
util/flasy/test.c
Normal file
46
util/flasy/test.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#undef NDEBUG
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./flasy.h"
|
||||
|
||||
#define MAX_ 16
|
||||
#define COUNT_ (512*1024)
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
assert(2 <= argc && argc <= MAX_);
|
||||
|
||||
flasy_t* srv = flasy_new(COUNT_*sizeof(uint32_t), MAX_);
|
||||
|
||||
for (size_t i = 0; i < (size_t) argc-1; ++i) {
|
||||
FILE* fp = flasy_open_file(srv, argv[1+i], true);
|
||||
assert(fp != NULL);
|
||||
for (uint32_t i = 0; i < COUNT_; ++i) {
|
||||
assert(fwrite(&i, sizeof(i), 1, fp) == 1);
|
||||
}
|
||||
flasy_close_file(srv, fp);
|
||||
}
|
||||
|
||||
flasy_delete(srv); /* join */
|
||||
|
||||
for (size_t i = 0; i < (size_t) argc-1; ++i) {
|
||||
FILE* fp = fopen(argv[1+i], "rb");
|
||||
assert(fp != NULL);
|
||||
for (uint32_t i = 0; i < COUNT_; ++i) {
|
||||
uint32_t a;
|
||||
assert(fread(&a, sizeof(a), 1, fp) == 1);
|
||||
if (a != i) printf("%d %d\n", i, a);
|
||||
assert(a == i);
|
||||
}
|
||||
uint8_t dummy_;
|
||||
assert(fread(&dummy_, 1, 1, fp) == 0 && feof(fp) != 0);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@@ -50,7 +50,8 @@ static void gleasy_atlas_resize_bitmap_(
|
||||
const size_t pixel = type*fmt;
|
||||
assert(pixel > 0);
|
||||
|
||||
container_array_resize(atlas->resize_buffer, out->width*out->height*pixel);
|
||||
container_array_resize(
|
||||
atlas->resize_buffer, (size_t) out->width*out->height*pixel);
|
||||
out->buffer = atlas->resize_buffer;
|
||||
|
||||
const int32_t ymax = out->height * pixel;
|
||||
@@ -128,7 +129,7 @@ gleasy_atlas_t* gleasy_atlas_new(
|
||||
|
||||
assert(glGetError() == GL_NO_ERROR);
|
||||
|
||||
container_array_reserve(atlas->resize_buffer, width*height*4);
|
||||
container_array_reserve(atlas->resize_buffer, (size_t) width*height*4);
|
||||
return atlas;
|
||||
}
|
||||
|
||||
|
@@ -27,7 +27,7 @@ target_link_libraries(glyphas
|
||||
if (BUILD_TESTING)
|
||||
add_executable(glyphas-test test.c)
|
||||
target_link_libraries(glyphas-test
|
||||
SDL2::SDL2
|
||||
${SDL2_TARGET}
|
||||
|
||||
glyphas)
|
||||
endif()
|
||||
|
@@ -9,31 +9,20 @@
|
||||
#include "util/gleasy/texture.h"
|
||||
#include "util/gleasy/program.h"
|
||||
#include "util/math/vector.h"
|
||||
#include "util/memory/memory.h"
|
||||
|
||||
#include "./block.h"
|
||||
|
||||
/* resources */
|
||||
#include "anysrc/drawer.vshader.h"
|
||||
#include "anysrc/drawer.fshader.h"
|
||||
#include "util/glyphas/anysrc/drawer.vshader.h"
|
||||
#include "util/glyphas/anysrc/drawer.fshader.h"
|
||||
|
||||
#define GLYPHAS_DRAWER_UNIFORM_TEX 0
|
||||
#define UNIFORM_TEX_ 0
|
||||
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_POS 0
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_SIZE 1
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_UV_POS 2
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_UV_SIZE 3
|
||||
#define GLYPHAS_DRAWER_VSHADER_IN_COLOR 4
|
||||
|
||||
struct glyphas_drawer_t {
|
||||
gleasy_texture_2d_t tex;
|
||||
|
||||
GLuint vao;
|
||||
|
||||
gleasy_buffer_array_t instances;
|
||||
size_t instances_reserved;
|
||||
size_t instances_length;
|
||||
};
|
||||
#define VSHADER_IN_POS_ 0
|
||||
#define VSHADER_IN_SIZE_ 1
|
||||
#define VSHADER_IN_UV_POS_ 2
|
||||
#define VSHADER_IN_UV_SIZE_ 3
|
||||
#define VSHADER_IN_COLOR_ 4
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
@@ -45,73 +34,60 @@ typedef struct {
|
||||
} glyphas_drawer_instance_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
static void glyphas_drawer_setup_vao_(
|
||||
gleasy_buffer_array_t instances) {
|
||||
assert(instances != 0);
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, instances);
|
||||
|
||||
# define enable_attrib_(NAME, name, dim, type) do { \
|
||||
glEnableVertexAttribArray(GLYPHAS_DRAWER_VSHADER_IN_##NAME); \
|
||||
glVertexAttribPointer( \
|
||||
GLYPHAS_DRAWER_VSHADER_IN_##NAME, dim, type, GL_FALSE, \
|
||||
sizeof(glyphas_drawer_instance_t), \
|
||||
NULL + offsetof(glyphas_drawer_instance_t, name)); \
|
||||
glVertexAttribDivisor(GLYPHAS_DRAWER_VSHADER_IN_##NAME, 1); \
|
||||
} while (0)
|
||||
|
||||
enable_attrib_(POS, pos, 2, GL_FLOAT);
|
||||
enable_attrib_(SIZE, size, 2, GL_FLOAT);
|
||||
enable_attrib_(UV_POS, uv_pos, 2, GL_FLOAT);
|
||||
enable_attrib_(UV_SIZE, uv_size, 2, GL_FLOAT);
|
||||
enable_attrib_(COLOR, color, 4, GL_FLOAT);
|
||||
|
||||
# undef enable_attrib_
|
||||
}
|
||||
|
||||
gleasy_program_t glyphas_drawer_create_default_program(void) {
|
||||
return gleasy_program_new("", 0,
|
||||
glyphas_drawer_vshader_, sizeof(glyphas_drawer_vshader_),
|
||||
glyphas_drawer_fshader_, sizeof(glyphas_drawer_fshader_));
|
||||
}
|
||||
|
||||
glyphas_drawer_t* glyphas_drawer_new(void) {
|
||||
glyphas_drawer_t* drawer = memory_new(sizeof(*drawer));
|
||||
*drawer = (typeof(*drawer)) {0};
|
||||
void glyphas_drawer_initialize(
|
||||
glyphas_drawer_t* drawer, gleasy_texture_2d_t tex) {
|
||||
assert(drawer != NULL);
|
||||
|
||||
*drawer = (typeof(*drawer)) {
|
||||
.tex = tex,
|
||||
};
|
||||
|
||||
glCreateVertexArrays(1, &drawer->vao);
|
||||
glBindVertexArray(drawer->vao);
|
||||
|
||||
glGenBuffers(1, &drawer->instances);
|
||||
glyphas_drawer_setup_vao_(drawer->instances);
|
||||
|
||||
return drawer;
|
||||
glBindVertexArray(drawer->vao);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, drawer->instances);
|
||||
|
||||
# define enable_(index, var, dim, type) do { \
|
||||
glEnableVertexAttribArray(index); \
|
||||
glVertexAttribPointer( \
|
||||
index, dim, type, GL_FALSE, \
|
||||
sizeof(glyphas_drawer_instance_t), \
|
||||
NULL + offsetof(glyphas_drawer_instance_t, var)); \
|
||||
glVertexAttribDivisor(index, 1); \
|
||||
} while (0)
|
||||
|
||||
enable_(VSHADER_IN_POS_, pos, 2, GL_FLOAT);
|
||||
enable_(VSHADER_IN_SIZE_, size, 2, GL_FLOAT);
|
||||
enable_(VSHADER_IN_UV_POS_, uv_pos, 2, GL_FLOAT);
|
||||
enable_(VSHADER_IN_UV_SIZE_, uv_size, 2, GL_FLOAT);
|
||||
enable_(VSHADER_IN_COLOR_, color, 4, GL_FLOAT);
|
||||
|
||||
# undef enable_attrib_
|
||||
}
|
||||
|
||||
void glyphas_drawer_delete(glyphas_drawer_t* drawer) {
|
||||
if (drawer == NULL) return;
|
||||
void glyphas_drawer_deinitialize(glyphas_drawer_t* drawer) {
|
||||
assert(drawer != NULL);
|
||||
|
||||
glDeleteBuffers(1, &drawer->instances);
|
||||
|
||||
glDeleteVertexArrays(1, &drawer->vao);
|
||||
|
||||
memory_delete(drawer);
|
||||
}
|
||||
|
||||
void glyphas_drawer_clear(
|
||||
glyphas_drawer_t* drawer, gleasy_texture_2d_t tex, size_t reserve) {
|
||||
void glyphas_drawer_clear(glyphas_drawer_t* drawer, size_t reserve) {
|
||||
assert(drawer != NULL);
|
||||
assert(tex != 0);
|
||||
assert(reserve > 0);
|
||||
|
||||
drawer->tex = tex;
|
||||
|
||||
drawer->instances_length = 0;
|
||||
if (drawer->instances_reserved < reserve) {
|
||||
glBindBuffer(GL_ARRAY_BUFFER, drawer->instances);
|
||||
glBufferData(GL_ARRAY_BUFFER,
|
||||
reserve * sizeof(glyphas_drawer_instance_t),
|
||||
NULL, GL_DYNAMIC_DRAW);
|
||||
reserve*sizeof(glyphas_drawer_instance_t), NULL, GL_DYNAMIC_DRAW);
|
||||
drawer->instances_reserved = reserve;
|
||||
}
|
||||
}
|
||||
@@ -158,7 +134,7 @@ void glyphas_drawer_draw(const glyphas_drawer_t* drawer) {
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, drawer->tex);
|
||||
glUniform1i(GLYPHAS_DRAWER_UNIFORM_TEX, 0);
|
||||
glUniform1i(UNIFORM_TEX_, 0);
|
||||
|
||||
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, drawer->instances_length);
|
||||
}
|
||||
|
@@ -1,33 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "util/gleasy/buffer.h"
|
||||
#include "util/gleasy/texture.h"
|
||||
#include "util/gleasy/program.h"
|
||||
|
||||
#include "./block.h"
|
||||
|
||||
struct glyphas_drawer_t;
|
||||
typedef struct glyphas_drawer_t glyphas_drawer_t;
|
||||
typedef struct {
|
||||
gleasy_texture_2d_t tex;
|
||||
|
||||
GLuint vao;
|
||||
|
||||
gleasy_buffer_array_t instances;
|
||||
size_t instances_reserved;
|
||||
size_t instances_length;
|
||||
} glyphas_drawer_t;
|
||||
|
||||
gleasy_program_t
|
||||
glyphas_drawer_create_default_program(
|
||||
void
|
||||
);
|
||||
|
||||
glyphas_drawer_t*
|
||||
glyphas_drawer_new(
|
||||
void
|
||||
void
|
||||
glyphas_drawer_initialize(
|
||||
glyphas_drawer_t* drawer,
|
||||
gleasy_texture_2d_t tex
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_drawer_delete(
|
||||
glyphas_drawer_deinitialize(
|
||||
glyphas_drawer_t* drawer
|
||||
);
|
||||
|
||||
void
|
||||
glyphas_drawer_clear(
|
||||
glyphas_drawer_t* drawer,
|
||||
gleasy_texture_2d_t tex,
|
||||
size_t reserve
|
||||
glyphas_drawer_t* drawer,
|
||||
size_t reserve
|
||||
);
|
||||
|
||||
void
|
||||
@@ -44,7 +52,7 @@ glyphas_drawer_add_block_item(
|
||||
/* The drawer doesn't hold the reference. */
|
||||
);
|
||||
|
||||
/* bind the program before drawing */
|
||||
/* call glUseProgram before calling */
|
||||
void
|
||||
glyphas_drawer_draw(
|
||||
const glyphas_drawer_t* drawer
|
||||
|
@@ -11,6 +11,16 @@
|
||||
|
||||
#include "./context.h"
|
||||
|
||||
static const char* glyphas_face_get_ft_error_str_(FT_Error err) {
|
||||
/* what a fucking trick lol.
|
||||
* https://stackoverflow.com/questions/61641364/gcc-cant-find-ft-error-string-when-trying-to-compile */
|
||||
# undef FTERRORS_H_
|
||||
# define FT_ERRORDEF(code, val, str) case code: return str;
|
||||
# define FT_ERROR_START_LIST switch(err) {
|
||||
# define FT_ERROR_END_LIST default: return "unknown"; }
|
||||
# include FT_ERRORS_H
|
||||
}
|
||||
|
||||
void glyphas_face_initialize_from_file(
|
||||
glyphas_face_t* face,
|
||||
const glyphas_context_t* ctx,
|
||||
@@ -23,8 +33,8 @@ void glyphas_face_initialize_from_file(
|
||||
|
||||
const FT_Error err = FT_New_Face(ctx->ft, path, index, &face->ft);
|
||||
if (err != FT_Err_Ok) {
|
||||
fprintf(stderr,
|
||||
"failed to load font file '%s': %s\n", path, FT_Error_String(err));
|
||||
fprintf(stderr, "failed to load font file '%s': %s\n",
|
||||
path, glyphas_face_get_ft_error_str_(err));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
@@ -43,8 +53,8 @@ void glyphas_face_initialize_from_buffer(
|
||||
const FT_Error err =
|
||||
FT_New_Memory_Face(ctx->ft, data, length, index, &face->ft);
|
||||
if (err != FT_Err_Ok) {
|
||||
fprintf(stderr,
|
||||
"failed to load font on memory: %s\n", FT_Error_String(err));
|
||||
fprintf(stderr, "failed to load font on memory: %s\n",
|
||||
glyphas_face_get_ft_error_str_(err));
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
@@ -28,8 +28,8 @@ typedef struct {
|
||||
gleasy_atlas_t* atlas;
|
||||
glyphas_cache_t* cache;
|
||||
|
||||
gleasy_program_t prog;
|
||||
glyphas_drawer_t* drawer;
|
||||
gleasy_program_t prog;
|
||||
glyphas_drawer_t drawer;
|
||||
} context_t;
|
||||
|
||||
#define SCALE 0.01f
|
||||
@@ -56,8 +56,8 @@ static void align_text_(context_t* ctx, const char* str) {
|
||||
static const vec2_t origin = vec2(.5f, -.5f);
|
||||
glyphas_block_set_origin(block, &origin);
|
||||
|
||||
glyphas_drawer_clear(ctx->drawer, gleasy_atlas_get_texture(ctx->atlas), len);
|
||||
glyphas_drawer_add_block(ctx->drawer, block);
|
||||
glyphas_drawer_clear(&ctx->drawer, len);
|
||||
glyphas_drawer_add_block(&ctx->drawer, block);
|
||||
glyphas_block_delete(block);
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ static void initialize_(context_t* ctx, const char* path, const char* str) {
|
||||
ctx->atlas = gleasy_atlas_new(GL_RED, 256, 256, false /* anti-alias */);
|
||||
ctx->cache = glyphas_cache_new(ctx->atlas, &ctx->face, 16, 16);
|
||||
|
||||
ctx->prog = glyphas_drawer_create_default_program();
|
||||
ctx->drawer = glyphas_drawer_new();
|
||||
ctx->prog = glyphas_drawer_create_default_program();
|
||||
glyphas_drawer_initialize(&ctx->drawer, gleasy_atlas_get_texture(ctx->atlas));
|
||||
assert(glGetError() == GL_NO_ERROR);
|
||||
|
||||
align_text_(ctx, str);
|
||||
@@ -87,13 +87,13 @@ static void draw_(const context_t* ctx) {
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
glUseProgram(ctx->prog);
|
||||
glyphas_drawer_draw(ctx->drawer);
|
||||
glyphas_drawer_draw(&ctx->drawer);
|
||||
}
|
||||
|
||||
static void deinitialize_(context_t* ctx) {
|
||||
assert(ctx != NULL);
|
||||
|
||||
glyphas_drawer_delete(ctx->drawer);
|
||||
glyphas_drawer_deinitialize(&ctx->drawer);
|
||||
glDeleteProgram(ctx->prog);
|
||||
|
||||
glyphas_cache_delete(ctx->cache);
|
||||
|
@@ -49,7 +49,7 @@ static void jukebox_composite_affect_(
|
||||
jukebox_effect_affect(c->effects[i], &chunk_pcm);
|
||||
}
|
||||
|
||||
const size_t len = chunk_pcm.frames * channels;
|
||||
const size_t len = (size_t) chunk_pcm.frames*channels;
|
||||
const float* src = chunk;
|
||||
float* dst = pcm->ptr + read * channels;
|
||||
for (uint64_t i = 0; i < len; ++i) {
|
||||
|
@@ -8,7 +8,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "thirdparty/miniaudio/miniaudio.h"
|
||||
#include <miniaudio.h>
|
||||
|
||||
#include "util/math/algorithm.h"
|
||||
#include "util/math/rational.h"
|
||||
|
@@ -32,7 +32,7 @@ static void jukebox_delay_affect_(
|
||||
jukebox_delay_t* d = (typeof(d)) effect;
|
||||
|
||||
const int32_t ch = d->format.channels;
|
||||
const size_t pcmlen = pcm->frames*ch;
|
||||
const size_t pcmlen = (size_t) pcm->frames*ch;
|
||||
|
||||
for (size_t i = 0; i < pcmlen; ++i) {
|
||||
float* bufoff = &d->buffer[d->offset];
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "thirdparty/miniaudio/miniaudio.h"
|
||||
#include <miniaudio.h>
|
||||
|
||||
#include "util/container/array.h"
|
||||
#include "util/memory/memory.h"
|
||||
|
@@ -6,7 +6,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "thirdparty/miniaudio/miniaudio.h"
|
||||
#include <miniaudio.h>
|
||||
|
||||
#include "util/math/rational.h"
|
||||
#include "util/memory/memory.h"
|
||||
|
@@ -14,7 +14,8 @@
|
||||
|
||||
#define MATH_CLAMP(x, min, max) ((x) < (min)? (min): (x) > (max)? (max): (x))
|
||||
|
||||
#define MATH_SIGN(a) (a < 0? -1: a > 0? 1: 0)
|
||||
#define MATH_SIGN(a) (a < 0? -1: a > 0? 1: 0)
|
||||
#define MATH_SIGN_NOZERO(a) (a < 0? -1: 1)
|
||||
|
||||
#define MATH_FLOAT_EQUAL(a, b) (MATH_ABS((a) - (b)) < MATH_EPSILON)
|
||||
#define MATH_FLOAT_VALID(a) (!isnan(a) && !isinf(a))
|
||||
|
9
util/statman/CMakeLists.txt
Normal file
9
util/statman/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
add_library(statman
|
||||
statman.c
|
||||
)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
add_executable(statman-test test.c)
|
||||
target_link_libraries(statman-test statman)
|
||||
add_test(statman-test statman-test)
|
||||
endif()
|
4
util/statman/README.md
Normal file
4
util/statman/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
statman
|
||||
====
|
||||
|
||||
the simplest STATe MANagement system
|
89
util/statman/statman.c
Normal file
89
util/statman/statman.c
Normal file
@@ -0,0 +1,89 @@
|
||||
#include "./statman.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define REDIRECT_MAX_ 100
|
||||
|
||||
static const statman_meta_t* statman_get_meta_by_state_(
|
||||
const statman_meta_t* meta, statman_state_t state) {
|
||||
assert(meta != NULL);
|
||||
|
||||
for (size_t i = 0; meta[i].name != NULL; ++i) {
|
||||
if (meta[i].state == state) return &meta[i];
|
||||
}
|
||||
fprintf(stderr, "statman: unknown state %"PRIu16"\n", state);
|
||||
abort();
|
||||
}
|
||||
|
||||
static void statman_initialize_state_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state,
|
||||
size_t depth) {
|
||||
assert(meta != NULL);
|
||||
assert(state != NULL);
|
||||
assert(depth < REDIRECT_MAX_);
|
||||
|
||||
const statman_meta_t* m = statman_get_meta_by_state_(meta, *state);
|
||||
if (m->initialize == NULL) return;
|
||||
|
||||
const statman_state_t backup = *state;
|
||||
m->initialize(m, instance, state);
|
||||
|
||||
if (backup != *state) {
|
||||
statman_initialize_state_(meta, instance, state, depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
static void statman_update_state_(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state,
|
||||
size_t depth) {
|
||||
assert(meta != NULL);
|
||||
assert(state != NULL);
|
||||
assert(depth < REDIRECT_MAX_);
|
||||
|
||||
const statman_meta_t* m = statman_get_meta_by_state_(meta, *state);
|
||||
if (m->update == NULL) return;
|
||||
|
||||
const statman_state_t backup = *state;
|
||||
m->update(m, instance, state);
|
||||
|
||||
if (backup != *state) {
|
||||
statman_initialize_state_(meta, instance, state, depth+1);
|
||||
statman_update_state_(meta, instance, state, depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
void statman_update(
|
||||
const statman_meta_t* meta, void* instance, statman_state_t* state) {
|
||||
assert(meta != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
statman_update_state_(meta, instance, state, 0);
|
||||
}
|
||||
|
||||
void statman_transition_to(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state,
|
||||
statman_state_t next) {
|
||||
assert(meta != NULL);
|
||||
assert(state != NULL);
|
||||
|
||||
if (*state == next) return;
|
||||
|
||||
const statman_meta_t* m = statman_get_meta_by_state_(meta, *state);
|
||||
if (m->finalize != NULL) {
|
||||
m->finalize(m, instance, &next);
|
||||
}
|
||||
if (*state == next) return;
|
||||
|
||||
*state = next;
|
||||
statman_initialize_state_(meta, instance, state, 0);
|
||||
}
|
47
util/statman/statman.h
Normal file
47
util/statman/statman.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint16_t statman_state_t;
|
||||
|
||||
typedef struct statman_meta_t statman_meta_t;
|
||||
struct statman_meta_t {
|
||||
statman_state_t state;
|
||||
const char* name;
|
||||
const void* data;
|
||||
|
||||
void
|
||||
(*initialize)(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
void
|
||||
(*update)(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
void
|
||||
(*finalize)(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* next
|
||||
);
|
||||
};
|
||||
|
||||
void
|
||||
statman_update(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state
|
||||
);
|
||||
|
||||
/* don't call in callback functions */
|
||||
void
|
||||
statman_transition_to(
|
||||
const statman_meta_t* meta,
|
||||
void* instance,
|
||||
statman_state_t* state,
|
||||
statman_state_t next
|
||||
);
|
160
util/statman/test.c
Normal file
160
util/statman/test.c
Normal file
@@ -0,0 +1,160 @@
|
||||
#undef NDEBUG
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "./statman.h"
|
||||
|
||||
typedef enum {
|
||||
CHECK_POINT_NONE,
|
||||
|
||||
CHECK_POINT_STATE0_INITIALIZE,
|
||||
CHECK_POINT_STATE1_INITIALIZE,
|
||||
CHECK_POINT_STATE2_INITIALIZE,
|
||||
CHECK_POINT_STATE3_INITIALIZE,
|
||||
|
||||
CHECK_POINT_STATE0_UPDATE,
|
||||
CHECK_POINT_STATE1_UPDATE,
|
||||
CHECK_POINT_STATE2_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE0_FINALIZE,
|
||||
CHECK_POINT_STATE1_FINALIZE,
|
||||
CHECK_POINT_STATE2_FINALIZE,
|
||||
CHECK_POINT_STATE3_FINALIZE,
|
||||
} check_point_t;
|
||||
|
||||
static check_point_t actual_[256] = {0};
|
||||
static size_t actual_length_ = 0;
|
||||
|
||||
static inline void check_point_(check_point_t cp) {
|
||||
assert(actual_length_ < sizeof(actual_)/sizeof(actual_[0]));
|
||||
actual_[actual_length_++] = cp;
|
||||
}
|
||||
|
||||
static void state_initialize_(
|
||||
const statman_meta_t* meta, void* time, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(time != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
check_point_(CHECK_POINT_STATE0_INITIALIZE+meta->state);
|
||||
}
|
||||
|
||||
static void state3_initialize_(
|
||||
const statman_meta_t* meta, void* time, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(time != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
check_point_(CHECK_POINT_STATE3_INITIALIZE);
|
||||
*next = 0;
|
||||
}
|
||||
|
||||
static void state_update_(
|
||||
const statman_meta_t* meta, void* time, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(time != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
check_point_(CHECK_POINT_STATE0_UPDATE+meta->state);
|
||||
|
||||
size_t *t = (typeof(t)) time;
|
||||
if ((*t)++%2 == 0) *next = meta->state+1;
|
||||
}
|
||||
|
||||
static void state_finalize_(
|
||||
const statman_meta_t* meta, void* time, statman_state_t* next) {
|
||||
assert(meta != NULL);
|
||||
assert(time != NULL);
|
||||
assert(next != NULL);
|
||||
|
||||
check_point_(CHECK_POINT_STATE0_FINALIZE+meta->state);
|
||||
*next = 0;
|
||||
}
|
||||
|
||||
static const statman_meta_t table_[] = {
|
||||
{
|
||||
.state = 0,
|
||||
.name = "0",
|
||||
.initialize = state_initialize_,
|
||||
.update = state_update_,
|
||||
.finalize = state_finalize_,
|
||||
},
|
||||
{
|
||||
.state = 1,
|
||||
.name = "1",
|
||||
.initialize = state_initialize_,
|
||||
.update = state_update_,
|
||||
.finalize = state_finalize_,
|
||||
},
|
||||
{
|
||||
.state = 2,
|
||||
.name = "2",
|
||||
.initialize = state_initialize_,
|
||||
.update = state_update_,
|
||||
.finalize = state_finalize_,
|
||||
},
|
||||
{
|
||||
.state = 3,
|
||||
.name = "3",
|
||||
.initialize = state3_initialize_,
|
||||
},
|
||||
};
|
||||
|
||||
int main(void) {
|
||||
statman_state_t state = 0;
|
||||
size_t time = 0;
|
||||
|
||||
/* state0 -> state1 */
|
||||
statman_update(table_, &time, &state);
|
||||
assert(state == 1);
|
||||
assert(time == 2);
|
||||
|
||||
/* state1 -> state2 */
|
||||
statman_update(table_, &time, &state);
|
||||
assert(state == 2);
|
||||
assert(time == 4);
|
||||
|
||||
/* state2 -> state0 */
|
||||
statman_update(table_, &time, &state);
|
||||
assert(state == 0);
|
||||
assert(time == 6);
|
||||
|
||||
/* state0 -> state1 */
|
||||
statman_update(table_, &time, &state);
|
||||
assert(state == 1);
|
||||
assert(time == 8);
|
||||
|
||||
/* stat1's finalizer forces to move to 0 */
|
||||
statman_transition_to(table_, &time, &state, 2);
|
||||
assert(state == 0);
|
||||
assert(time == 8);
|
||||
|
||||
static const check_point_t expects[] = {
|
||||
CHECK_POINT_STATE0_UPDATE,
|
||||
CHECK_POINT_STATE1_INITIALIZE,
|
||||
CHECK_POINT_STATE1_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE1_UPDATE,
|
||||
CHECK_POINT_STATE2_INITIALIZE,
|
||||
CHECK_POINT_STATE2_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE2_UPDATE,
|
||||
CHECK_POINT_STATE3_INITIALIZE,
|
||||
CHECK_POINT_STATE0_INITIALIZE,
|
||||
CHECK_POINT_STATE0_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE0_UPDATE,
|
||||
CHECK_POINT_STATE1_INITIALIZE,
|
||||
CHECK_POINT_STATE1_UPDATE,
|
||||
|
||||
CHECK_POINT_STATE1_FINALIZE,
|
||||
CHECK_POINT_STATE0_INITIALIZE,
|
||||
};
|
||||
|
||||
assert(actual_length_ == sizeof(expects)/sizeof(expects[0]));
|
||||
for (size_t i = 0; i < actual_length_; ++i) {
|
||||
assert(expects[i] == actual_[i]);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user