prosperon/source/engine/thirdparty/sokol/tests/functional/sokol_debugtext_test.c

520 lines
17 KiB
C

//------------------------------------------------------------------------------
// sokol-debugtext-test.c
// For best results, run with ASAN and UBSAN.
//------------------------------------------------------------------------------
#include "sokol_gfx.h"
#include "sokol_log.h"
#define SOKOL_DEBUGTEXT_IMPL
#include "sokol_debugtext.h"
#include "utest.h"
#define T(b) EXPECT_TRUE(b)
#define TFLT(f0,f1) {T(fabs((f0)-(f1))<=(0.000001));}
static void init(void) {
sg_setup(&(sg_desc){ .logger = { .func = slog_func }});
sdtx_setup(&(sdtx_desc_t){ .logger = { .func = slog_func }});
}
static void init_with(const sdtx_desc_t* desc) {
sg_setup(&(sg_desc){0});
sdtx_setup(desc);
}
static void shutdown(void) {
sdtx_shutdown();
sg_shutdown();
}
UTEST(sokol_debugtext, default_init_shutdown) {
init();
T(_sdtx.init_cookie == _SDTX_INIT_COOKIE);
T(_sdtx.desc.context_pool_size == _SDTX_DEFAULT_CONTEXT_POOL_SIZE);
T(_sdtx.desc.printf_buf_size == _SDTX_DEFAULT_PRINTF_BUF_SIZE);
T(_sdtx.desc.context.char_buf_size == _SDTX_DEFAULT_CHAR_BUF_SIZE);
T(_sdtx.desc.context.canvas_width == _SDTX_DEFAULT_CANVAS_WIDTH);
T(_sdtx.desc.context.canvas_height == _SDTX_DEFAULT_CANVAS_HEIGHT);
T(_sdtx.desc.context.tab_width == _SDTX_DEFAULT_TAB_WIDTH);
T(_sdtx.desc.context.color_format == 0);
T(_sdtx.desc.context.depth_format == 0);
T(_sdtx.desc.context.sample_count == 0);
for (int i = 0; i < SDTX_MAX_FONTS; i++) {
T(_sdtx.desc.fonts[i].data.ptr == 0);
T(_sdtx.desc.fonts[i].data.size == 0);
T(_sdtx.desc.fonts[i].first_char == 0);
T(_sdtx.desc.fonts[i].last_char == 0);
}
T(_sdtx.font_img.id != SG_INVALID_ID);
T(_sdtx.shader.id != SG_INVALID_ID);
T(_sdtx.fmt_buf_size == (_SDTX_DEFAULT_CHAR_BUF_SIZE + 1));
T(_sdtx.fmt_buf);
T(_sdtx.def_ctx_id.id != 0);
T(_sdtx.def_ctx_id.id == _sdtx.cur_ctx_id.id);
T(_sdtx.cur_ctx == _sdtx_lookup_context(_sdtx.cur_ctx_id.id));
T(_sdtx.cur_ctx->desc.char_buf_size == _sdtx.desc.context.char_buf_size);
T(_sdtx.cur_ctx->desc.canvas_width == _sdtx.desc.context.canvas_width);
T(_sdtx.cur_ctx->desc.canvas_height == _sdtx.desc.context.canvas_height);
T(_sdtx.cur_ctx->desc.tab_width == _sdtx.desc.context.tab_width);
T(_sdtx.cur_ctx->desc.color_format == 0);
T(_sdtx.cur_ctx->desc.depth_format == 0);
T(_sdtx.cur_ctx->desc.sample_count == 0);
T(_sdtx.cur_ctx->vertices.cap == _SDTX_DEFAULT_CHAR_BUF_SIZE * 6);
T(_sdtx.cur_ctx->vertices.next == 0);
T(_sdtx.cur_ctx->vertices.ptr);
T(_sdtx.cur_ctx->commands.cap == _SDTX_DEFAULT_MAX_COMMANDS);
T(_sdtx.cur_ctx->commands.next == 1);
T(_sdtx.cur_ctx->commands.ptr);
T(_sdtx.cur_ctx->vbuf.id != 0);
T(_sdtx.cur_ctx->pip.id != 0);
TFLT(_sdtx.cur_ctx->canvas_size.x, 640.0f);
TFLT(_sdtx.cur_ctx->canvas_size.y, 480.0f);
TFLT(_sdtx.cur_ctx->glyph_size.x, 8.0f / 640.0f);
TFLT(_sdtx.cur_ctx->glyph_size.y, 8.0f / 480.0f);
TFLT(_sdtx.cur_ctx->origin.x, 0.0f);
TFLT(_sdtx.cur_ctx->origin.y, 0.0f);
TFLT(_sdtx.cur_ctx->pos.x, 0.0f);
TFLT(_sdtx.cur_ctx->pos.y, 0.0f);
TFLT(_sdtx.cur_ctx->tab_width, 4.0f);
T(_sdtx.cur_ctx->color == _SDTX_DEFAULT_COLOR);
T(_sdtx.context_pool.contexts);
T(_sdtx.context_pool.pool.size == (_SDTX_DEFAULT_CONTEXT_POOL_SIZE + 1));
shutdown();
T(_sdtx.init_cookie == 0);
}
UTEST(sokol_debugtext, init_with_params) {
init_with(&(sdtx_desc_t){
.context_pool_size = 2,
.printf_buf_size = 128,
.context = {
.char_buf_size = 256,
.canvas_width = 320,
.canvas_height = 200,
.tab_width = 8,
.color_format = SG_PIXELFORMAT_RGBA8,
.depth_format = SG_PIXELFORMAT_DEPTH_STENCIL,
.sample_count = 4,
}
});
T(_sdtx.init_cookie == _SDTX_INIT_COOKIE);
T(_sdtx.desc.context_pool_size == 2);
T(_sdtx.desc.printf_buf_size == 128);
T(_sdtx.desc.context.char_buf_size == 256);
T(_sdtx.desc.context.canvas_width == 320);
T(_sdtx.desc.context.canvas_height == 200);
T(_sdtx.desc.context.tab_width == 8);
T(_sdtx.desc.context.color_format == SG_PIXELFORMAT_RGBA8);
T(_sdtx.desc.context.depth_format == SG_PIXELFORMAT_DEPTH_STENCIL);
T(_sdtx.desc.context.sample_count == 4);
T(_sdtx.fmt_buf_size == 129);
T(_sdtx.cur_ctx->desc.char_buf_size == _sdtx.desc.context.char_buf_size);
T(_sdtx.cur_ctx->desc.canvas_width == _sdtx.desc.context.canvas_width);
T(_sdtx.cur_ctx->desc.canvas_height == _sdtx.desc.context.canvas_height);
T(_sdtx.cur_ctx->desc.tab_width == _sdtx.desc.context.tab_width);
T(_sdtx.cur_ctx->desc.color_format == SG_PIXELFORMAT_RGBA8);
T(_sdtx.cur_ctx->desc.depth_format == SG_PIXELFORMAT_DEPTH_STENCIL);
T(_sdtx.cur_ctx->desc.sample_count == 4);
T(_sdtx.cur_ctx->vertices.cap == (256 * 6));
TFLT(_sdtx.cur_ctx->canvas_size.x, 320.0f);
TFLT(_sdtx.cur_ctx->canvas_size.y, 200.0f);
TFLT(_sdtx.cur_ctx->glyph_size.x, 8.0f / 320.0f);
TFLT(_sdtx.cur_ctx->glyph_size.y, 8.0f / 200.0f);
TFLT(_sdtx.cur_ctx->tab_width, 8.0f);
T(_sdtx.context_pool.pool.size == 3);
shutdown();
}
UTEST(sokol_debugtext, make_destroy_context) {
init();
sdtx_context ctx_id = sdtx_make_context(&(sdtx_context_desc_t){
.char_buf_size = 64,
.canvas_width = 1024,
.canvas_height = 768,
.tab_width = 3,
.color_format = SG_PIXELFORMAT_RGBA32F,
.sample_count = 2
});
T(ctx_id.id != 0);
T(ctx_id.id != _sdtx.cur_ctx_id.id);
_sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id);
T(ctx);
T(ctx != _sdtx.cur_ctx);
T(ctx->desc.char_buf_size == 64);
T(ctx->desc.canvas_width == 1024);
T(ctx->desc.canvas_height == 768);
T(ctx->desc.tab_width == 3);
T(ctx->desc.color_format == SG_PIXELFORMAT_RGBA32F);
T(ctx->desc.depth_format == 0);
T(ctx->desc.sample_count == 2);
T(ctx->vertices.ptr);
T(ctx->vertices.next == 0);
T(ctx->vertices.cap == (64 * 6));
TFLT(ctx->canvas_size.x, 1024.0f);
TFLT(ctx->canvas_size.y, 768.0f);
TFLT(ctx->glyph_size.x, 8.0f / 1024.0f);
TFLT(ctx->glyph_size.y, 8.0f / 768.0f);
TFLT(ctx->tab_width, 3.0f);
sdtx_destroy_context(ctx_id);
T(0 == _sdtx_lookup_context(ctx_id.id));
T(ctx->desc.char_buf_size == 0);
T(ctx->vertices.ptr == 0);
shutdown();
}
UTEST(sokol_debugtext, get_default_context) {
// getting the default context must always return SDTX_DEFAULT_CONTEXT
init();
T(sdtx_get_context().id == SDTX_DEFAULT_CONTEXT.id);
shutdown();
}
UTEST(sokol_debugtext, set_get_context) {
init();
sdtx_context ctx_id = sdtx_make_context(&(sdtx_context_desc_t){ 0 });
T(ctx_id.id != 0);
T(ctx_id.id != _sdtx.cur_ctx_id.id);
sdtx_set_context(ctx_id);
T(sdtx_get_context().id == ctx_id.id);
T(ctx_id.id == _sdtx.cur_ctx_id.id);
const _sdtx_context_t* ctx = _sdtx_lookup_context(ctx_id.id);
T(ctx == _sdtx.cur_ctx);
sdtx_set_context(SDTX_DEFAULT_CONTEXT);
T(sdtx_get_context().id == SDTX_DEFAULT_CONTEXT.id);
T(_sdtx.cur_ctx);
T(ctx != _sdtx.cur_ctx);
T(_sdtx.cur_ctx == _sdtx_lookup_context(_sdtx.def_ctx_id.id));
shutdown();
}
UTEST(sokol_debugtext, destroy_default_context) {
// destroying the default context is not allowed
init();
sdtx_context def_ctx_id = _sdtx.def_ctx_id;
T(def_ctx_id.id == _sdtx.cur_ctx_id.id);
sdtx_destroy_context(def_ctx_id);
T(def_ctx_id.id == _sdtx.def_ctx_id.id);
T(def_ctx_id.id == _sdtx.cur_ctx_id.id);
T(_sdtx.cur_ctx);
shutdown();
}
UTEST(sokol_debugtext, destroy_current_context) {
// destroying the current context has the same effect
// as setting a current context with an invalid context handle
init();
sdtx_context ctx_id = sdtx_make_context(&(sdtx_context_desc_t){ 0 });
sdtx_set_context(ctx_id);
T(sdtx_get_context().id == ctx_id.id);
T(ctx_id.id == _sdtx.cur_ctx_id.id);
T(_sdtx_lookup_context(ctx_id.id) == _sdtx.cur_ctx);
sdtx_destroy_context(ctx_id);
T(_sdtx.cur_ctx_id.id == ctx_id.id);
T(_sdtx.cur_ctx == 0);
T(sdtx_get_context().id == ctx_id.id);
shutdown();
}
UTEST(sokol_debugtext, ignore_invalid_context_handle) {
// trying to render with an invalid context handle must not crash,
// instead ignore all operations
init();
sdtx_context ctx_id = sdtx_make_context(&(sdtx_context_desc_t){ 0 });
sdtx_set_context(ctx_id);
sdtx_destroy_context(ctx_id);
T(0 == _sdtx.cur_ctx);
T(sdtx_get_context().id == ctx_id.id);
sdtx_font(0);
sdtx_canvas(100.0f, 200.0f);
sdtx_origin(10.0f, 10.0f);
sdtx_home();
sdtx_pos(1.0f, 2.0f);
sdtx_pos_x(1.0f);
sdtx_pos_y(2.0f);
sdtx_move(2.0f, 3.0f);
sdtx_move_x(2.0f);
sdtx_move_y(3.0f);
sdtx_crlf();
sdtx_color3b(255, 255, 255);
sdtx_color3f(1.0f, 1.0f, 1.0f);
sdtx_color4b(255, 255, 255, 255);
sdtx_color4f(1.0f, 1.0f, 1.0f, 1.0f);
sdtx_color1i(0xFFFFFFFF);
sdtx_putc('A');
sdtx_puts("Hello World!");
sdtx_putr("Hello World!", 5);
sdtx_printf("Hello World %d %d %d", 1, 2, 3);
shutdown();
}
UTEST(sokol_debugtext, set_font) {
init();
T(0 == _sdtx.cur_ctx->cur_font);
sdtx_font(1);
T(1 == _sdtx.cur_ctx->cur_font);
sdtx_font(2);
T(2 == _sdtx.cur_ctx->cur_font);
shutdown();
}
UTEST(sokol_debugtext, set_canvas) {
init();
sdtx_origin(10.0f, 11.0f);
sdtx_pos(1.0f, 2.0f);
sdtx_canvas(320.0f, 200.0f);
TFLT(_sdtx.cur_ctx->canvas_size.x, 320.0f);
TFLT(_sdtx.cur_ctx->canvas_size.y, 200.0f);
TFLT(_sdtx.cur_ctx->glyph_size.x, 8.0f / 320.0f);
TFLT(_sdtx.cur_ctx->glyph_size.y, 8.0f / 200.0f);
// origin and pos must be reset to 0 when canvas is set
TFLT(_sdtx.cur_ctx->origin.x, 0.0f);
TFLT(_sdtx.cur_ctx->origin.y, 0.0f);
TFLT(_sdtx.cur_ctx->pos.x, 0.0f);
TFLT(_sdtx.cur_ctx->pos.y, 0.0f);
shutdown();
}
UTEST(sokol_debugtext, set_origin) {
init();
sdtx_origin(10.0f, 20.0f);
TFLT(_sdtx.cur_ctx->origin.x, 10.0f);
TFLT(_sdtx.cur_ctx->origin.y, 20.0f);
shutdown();
}
UTEST(sokol_debugtext, cursor_movement) {
init();
sdtx_pos(1.0f, 2.0f);
TFLT(_sdtx.cur_ctx->pos.x, 1.0f);
TFLT(_sdtx.cur_ctx->pos.y, 2.0f);
sdtx_pos_x(5.0f);
TFLT(_sdtx.cur_ctx->pos.x, 5.0f);
TFLT(_sdtx.cur_ctx->pos.y, 2.0f);
sdtx_pos_y(6.0f);
TFLT(_sdtx.cur_ctx->pos.x, 5.0f);
TFLT(_sdtx.cur_ctx->pos.y, 6.0f);
sdtx_move(-1.0f, -3.0f);
TFLT(_sdtx.cur_ctx->pos.x, 4.0f);
TFLT(_sdtx.cur_ctx->pos.y, 3.0f);
sdtx_move_x(+1.0f);
TFLT(_sdtx.cur_ctx->pos.x, 5.0f);
TFLT(_sdtx.cur_ctx->pos.y, 3.0f);
sdtx_move_y(+3.0f);
TFLT(_sdtx.cur_ctx->pos.x, 5.0f);
TFLT(_sdtx.cur_ctx->pos.y, 6.0f);
sdtx_crlf();
TFLT(_sdtx.cur_ctx->pos.x, 0.0f);
TFLT(_sdtx.cur_ctx->pos.y, 7.0f);
sdtx_pos(20.0f, 30.0f);
sdtx_home();
TFLT(_sdtx.cur_ctx->pos.x, 0.0f);
TFLT(_sdtx.cur_ctx->pos.y, 0.0f);
shutdown();
}
UTEST(sokol_debugtext, set_color) {
init();
T(_sdtx.cur_ctx->color == _SDTX_DEFAULT_COLOR);
sdtx_color3b(255, 127, 0);
T(_sdtx.cur_ctx->color == 0xFF007FFF);
sdtx_color4b(0, 127, 255, 255);
T(_sdtx.cur_ctx->color == 0xFFFF7F00);
sdtx_color3f(1.0f, 0.5f, 0.0f);
T(_sdtx.cur_ctx->color == 0xFF007FFF);
sdtx_color4f(0.0f, 0.5f, 1.0f, 1.0f);
T(_sdtx.cur_ctx->color == 0xFFFF7F00);
sdtx_color1i(0xFF000000);
T(_sdtx.cur_ctx->color == 0xFF000000);
shutdown();
}
UTEST(sokol_debugtext, vertex_overflow) {
// overflowing the vertex buffer must not crash
init_with(&(sdtx_desc_t){
.context.char_buf_size = 8,
});
for (int i = 0; i < 32; i++) {
sdtx_putc('A');
}
sdtx_puts("1234567890");
sdtx_putr("1234567890", 5);
sdtx_printf("Hello World %d!\n", 12);
T(_sdtx.cur_ctx->vertices.next == _sdtx.cur_ctx->vertices.cap);
shutdown();
}
UTEST(sokol_debugtext, context_overflow) {
// creating too many contexts should not crash
init_with(&(sdtx_desc_t){
.context_pool_size = 4,
});
T(_sdtx.context_pool.pool.size == 5);
// one slot is taken by the default context
sdtx_context ctx[4];
for (int i = 0; i < 4; i++) {
ctx[i] = sdtx_make_context(&(sdtx_context_desc_t){ 0 });
if (i < 3) {
T(ctx[i].id != 0);
}
else {
T(ctx[i].id == 0);
}
}
// destroying an invalid context should not crash
for (int i = 0; i < 4; i++) {
sdtx_destroy_context(ctx[i]);
}
shutdown();
}
UTEST(sokol_debugtext, printf_overflow) {
// overflowing the printf formatting buffer should not crash
init_with(&(sdtx_desc_t){
.printf_buf_size = 8
});
T(9 == _sdtx.fmt_buf_size);
T(16 == sdtx_printf("Hello %d\n", 123456789));
T('H' == _sdtx.fmt_buf[0])
T('e' == _sdtx.fmt_buf[1])
T('l' == _sdtx.fmt_buf[2])
T('l' == _sdtx.fmt_buf[3])
T('o' == _sdtx.fmt_buf[4])
T(' ' == _sdtx.fmt_buf[5])
T('1' == _sdtx.fmt_buf[6])
T('2' == _sdtx.fmt_buf[7])
T(0 == _sdtx.fmt_buf[8])
shutdown();
}
UTEST(sokol_debugtext, rewind_after_draw) {
// calling sdtx_draw() must rewind the cursor position, font and
// vertex pointer, to keep canvas size and origin as is
init();
sdtx_canvas(256, 128);
TFLT(_sdtx.cur_ctx->canvas_size.x, 256);
TFLT(_sdtx.cur_ctx->canvas_size.y, 128);
sdtx_origin(5, 5);
TFLT(_sdtx.cur_ctx->origin.x, 5);
TFLT(_sdtx.cur_ctx->origin.y, 5);
sdtx_pos(10, 20);
TFLT(_sdtx.cur_ctx->pos.x, 10);
TFLT(_sdtx.cur_ctx->pos.y, 20);
sdtx_font(3);
T(_sdtx.cur_ctx->cur_font == 3);
sdtx_printf("Hello World!\n");
T(_sdtx.cur_ctx->vertices.next != 0);
sg_begin_pass(&(sg_pass){
.swapchain = {
.width = 256,
.height = 256,
.sample_count = 1,
.color_format = SG_PIXELFORMAT_RGBA8,
.depth_format = SG_PIXELFORMAT_DEPTH_STENCIL,
}
});
sdtx_draw();
sg_end_pass();
sg_commit();
TFLT(_sdtx.cur_ctx->canvas_size.x, 256);
TFLT(_sdtx.cur_ctx->canvas_size.y, 128);
TFLT(_sdtx.cur_ctx->origin.x, 5);
TFLT(_sdtx.cur_ctx->origin.y, 5);
TFLT(_sdtx.cur_ctx->pos.x, 0);
TFLT(_sdtx.cur_ctx->pos.x, 0);
T(_sdtx.cur_ctx->cur_font == 0);
T(_sdtx.cur_ctx->vertices.next == 0);
shutdown();
}
UTEST(sokol_debugtext, putr) {
// test if sdtx_putr() draws the right amount of characters
init();
int start_index = _sdtx.cur_ctx->vertices.next;
sdtx_putr("Hello World!", 5);
T((5 * 6) == (_sdtx.cur_ctx->vertices.next - start_index));
start_index = _sdtx.cur_ctx->vertices.next;
sdtx_putr("Hello!\n\n\n\n\n\n\n\n\n\n\n", 10);
// NOTE: the \n's don't result in rendered vertices
T((6 * 6) == (_sdtx.cur_ctx->vertices.next - start_index));
shutdown();
}
UTEST(sokol_debugtext, default_context) {
init();
T(sdtx_default_context().id == SDTX_DEFAULT_CONTEXT.id);
shutdown();
}
// switching layers without any text inbetween should not advance the current draw command
UTEST(sokol_debug_text, empty_layers) {
init();
T(_sdtx.cur_ctx->commands.next == 1);
T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0);
sdtx_layer(1);
T(_sdtx.cur_ctx->commands.next == 1);
T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 1);
sdtx_layer(2);
T(_sdtx.cur_ctx->commands.next == 1);
T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 2);
sdtx_layer(0);
T(_sdtx.cur_ctx->commands.next == 1);
T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0);
shutdown();
}
// switching layers with text inbetween should advance the current draw command
UTEST(sokol_debug_text, non_empty_layers) {
init();
T(_sdtx.cur_ctx->commands.next == 1);
T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0);
T(_sdtx.cur_ctx->commands.ptr[0].first_vertex == 0);
T(_sdtx.cur_ctx->commands.ptr[0].num_vertices == 0);
sdtx_puts("123");
T(_sdtx.cur_ctx->commands.next == 1);
T(_sdtx.cur_ctx->commands.ptr[0].layer_id == 0);
T(_sdtx.cur_ctx->commands.ptr[0].first_vertex == 0);
T(_sdtx.cur_ctx->commands.ptr[0].num_vertices == (3 * 6));
sdtx_layer(1);
sdtx_puts("1234");
T(_sdtx.cur_ctx->commands.next == 2);
T(_sdtx.cur_ctx->commands.ptr[1].layer_id == 1);
T(_sdtx.cur_ctx->commands.ptr[1].first_vertex == (3 * 6));
T(_sdtx.cur_ctx->commands.ptr[1].num_vertices == (4 * 6));
// switching to same layer should not start a new draw commands
sdtx_layer(1);
sdtx_puts("12345");
T(_sdtx.cur_ctx->commands.next == 2);
T(_sdtx.cur_ctx->commands.ptr[1].layer_id == 1);
T(_sdtx.cur_ctx->commands.ptr[1].first_vertex == (3 * 6));
T(_sdtx.cur_ctx->commands.ptr[1].num_vertices == (9 * 6));
sdtx_layer(0);
sdtx_puts("123456");
T(_sdtx.cur_ctx->commands.next == 3);
T(_sdtx.cur_ctx->commands.ptr[2].layer_id == 0);
T(_sdtx.cur_ctx->commands.ptr[2].first_vertex == (12 * 6));
T(_sdtx.cur_ctx->commands.ptr[2].num_vertices == (6 * 6));
shutdown();
}
UTEST(sokol_debug_text, command_buffer_overflow) {
init_with(&(sdtx_desc_t){
.context = {
.max_commands = 4
}
});
sdtx_puts("0");
T(_sdtx.cur_ctx->commands.next == 1);
sdtx_layer(1);
sdtx_puts("1");
T(_sdtx.cur_ctx->commands.next == 2);
sdtx_layer(2);
sdtx_puts("2");
T(_sdtx.cur_ctx->commands.next == 3);
sdtx_layer(3);
sdtx_puts("3");
T(_sdtx.cur_ctx->commands.next == 4);
// from here on should fail
sdtx_layer(4);
sdtx_puts("4");
T(_sdtx.cur_ctx->commands.next == 4);
}