- Fixed D3D11 eating 40000000 cycles for no reason (Thank you Bill Gates)

- Refactor drawing to keep blocks persistent in memory
- Make string usage easier & more consisten (macros for string vs const char*)
- rdtsc intrinsic (get cycle count)
- Profiling macros (generates a google trace json)
- Write D3D11 debug messages to stdout
- String_Builder & thorough tests for it
This commit is contained in:
Charlie 2024-07-02 15:27:33 +02:00
parent 6519955513
commit b15c7c6e41
18 changed files with 867 additions and 415 deletions

4
.gitignore vendored
View file

@ -54,4 +54,6 @@ test_doc.vkn
*keybinds
*.rdi
.vscode
.vscode
google_trace.json

View file

@ -7,6 +7,8 @@
// This is only for people developing oogabooga!
#define OOGABOOGA_DEV 1
#define ENABLE_PROFILING 0
// When we need very debug
// #define CONFIGURATION VERY_DEBUG
@ -24,6 +26,6 @@ typedef struct Context_Extra {
//
// Comment & Uncomment to swap projects
// #include "entry_engine_test.c"
// #include "entry_engine_test.c"
// #include "entry_minimal_example.c"
#include "entry_randygame.c"
#include "entry_randygame.c"

14
build_dissassembly.bat Normal file
View file

@ -0,0 +1,14 @@
@echo off
rmdir /S /Q build
mkdir build
pushd build
mkdir release
pushd release
clang -o cgame.asm ../../build.c -Ofast -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -ffast-math -funroll-loops -finline-functions -fvectorize -fslp-vectorize -fomit-frame-pointer -fno-exceptions -fno-rtti -S -masm=intel
popd
popd

View file

@ -2,17 +2,17 @@
int entry(int argc, char **argv) {
window.title = fixed_string("Engine Testing Example");
window.title = STR("My epic game");
window.width = 1280;
window.height = 720;
window.x = 200;
window.y = 200;
window.y = 0;
window.clear_color = hex_to_rgba(0x2a2d3aff);
Gfx_Image *bush_image = load_image_from_disk(fixed_string("berry_bush.png"), get_heap_allocator());
Gfx_Image *bush_image = load_image_from_disk(STR("berry_bush.png"), get_heap_allocator());
assert(bush_image, "Failed loading berry_bush.png");
Gfx_Image *hammer_image = load_image_from_disk(fixed_string("hammer.png"), get_heap_allocator());
Gfx_Image *hammer_image = load_image_from_disk(STR("hammer.png"), get_heap_allocator());
assert(hammer_image, "Failed loading hammer.png");
seed_for_random = os_get_current_cycle_count();
@ -23,7 +23,7 @@ int entry(int argc, char **argv) {
Matrix4 camera_view = m4_scalar(1.0);
float64 last_time = os_get_current_time_in_seconds();
while (!window.should_close) {
while (!window.should_close) tm_scope_cycles("Frame") {
reset_temporary_storage();
float64 now = os_get_current_time_in_seconds();
@ -50,7 +50,7 @@ int entry(int argc, char **argv) {
if (is_key_just_released('Q')) {
delete_image(bush_image);
bush_image = load_image_from_disk(fixed_string("berry_bush.png"), get_heap_allocator());
bush_image = load_image_from_disk(STR("berry_bush.png"), get_heap_allocator());
assert(bush_image, "Failed loading berry_bush.png");
}
@ -70,11 +70,25 @@ int entry(int argc, char **argv) {
}
Vector2 cam_move = v2_mulf(cam_move_axis, delta * cam_move_speed);
camera_view = m4_translate(camera_view, v3(v2_expand(cam_move), 0));
draw_frame.view = camera_view;
seed_for_random = 69;
for (u64 i = 0; i < 100000; i++) {
float32 aspect = (float32)window.width/(float32)window.height;
float min_x = -aspect;
float max_x = aspect;
float min_y = -1;
float max_y = 1;
float x = get_random_float32() * (max_x-min_x) + min_x;
float y = get_random_float32() * (max_y-min_y) + min_y;
draw_image(bush_image, v2(x, y), v2(0.1, 0.1), COLOR_WHITE);
}
seed_for_random = os_get_current_cycle_count();
Matrix4 hammer_xform = m4_scalar(1.0);
hammer_xform = m4_rotate_z(hammer_xform, (f32)now);
hammer_xform = m4_translate(hammer_xform, v3(-.25f, -.25f, 0));

View file

@ -1,26 +1,26 @@
int entry(int argc, char **argv) {
window.title = fixed_string("Minimal Game Example");
window.width = 1280;
window.height = 720;
window.x = 200;
window.y = 200;
window.clear_color = v4(1, 1, 1, 1);
while (!window.should_close) {
reset_temporary_storage();
os_update();
float64 now = os_get_current_time_in_seconds();
Matrix4 hammer_xform = m4_scalar(1.0);
hammer_xform = m4_rotate_z(hammer_xform, (f32)now);
hammer_xform = m4_translate(hammer_xform, v3(-.25f, -.25f, 0));
draw_rect_xform(hammer_xform, v2(.5f, .5f), COLOR_RED);
gfx_update();
}
return 0;
int entry(int argc, char **argv) {
window.title = STR("Minimal Game Example");
window.width = 1280;
window.height = 720;
window.x = 200;
window.y = 200;
window.clear_color = v4(1, 1, 1, 1);
while (!window.should_close) {
reset_temporary_storage();
os_update();
float64 now = os_get_current_time_in_seconds();
Matrix4 hammer_xform = m4_scalar(1.0);
hammer_xform = m4_rotate_z(hammer_xform, (f32)now);
hammer_xform = m4_translate(hammer_xform, v3(-.25f, -.25f, 0));
draw_rect_xform(hammer_xform, v2(.5f, .5f), COLOR_RED);
gfx_update();
}
return 0;
}

View file

@ -1,64 +1,64 @@
int entry(int argc, char **argv) {
window.title = fixed_string("Randy's Game");
window.width = 1280;
window.height = 720;
window.x = 200;
window.y = 200;
window.clear_color = hex_to_rgba(0x2a2d3aff);
Gfx_Image* player = load_image_from_disk(fixed_string("player.png"), get_heap_allocator());
assert(player, "fuckie wucky happen");
Vector2 player_pos = v2(0, 0);
float64 seconds_counter = 0.0;
s32 frame_count = 0;
float64 last_time = os_get_current_time_in_seconds();
while (!window.should_close) {
reset_temporary_storage();
float64 now = os_get_current_time_in_seconds();
float64 delta_t = now - last_time;
last_time = now;
os_update();
if (is_key_just_pressed(KEY_ESCAPE)) {
window.should_close = true;
}
Vector2 input_axis = v2(0, 0);
if (is_key_down('A')) {
input_axis.x -= 1.0;
}
if (is_key_down('D')) {
input_axis.x += 1.0;
}
if (is_key_down('S')) {
input_axis.y -= 1.0;
}
if (is_key_down('W')) {
input_axis.y += 1.0;
}
input_axis = v2_normalize(input_axis);
player_pos = v2_add(player_pos, v2_mulf(input_axis, 5.0 * delta_t));
Matrix4 xform = m4_scalar(1.0);
xform = m4_translate(xform, v3(player_pos.x, player_pos.y, 0));
draw_image_xform(player, xform, v2(.5f, .5f), COLOR_RED);
gfx_update();
seconds_counter += delta_t;
frame_count += 1;
if (seconds_counter > 1.0) {
log("fps: %i", frame_count);
seconds_counter = 0.0;
frame_count = 0;
}
}
return 0;
int entry(int argc, char **argv) {
window.title = STR("Randy's Game");
window.width = 1280;
window.height = 720;
window.x = 200;
window.y = 200;
window.clear_color = hex_to_rgba(0x2a2d3aff);
Gfx_Image* player = load_image_from_disk(STR("player.png"), get_heap_allocator());
assert(player, "fuckie wucky happen");
Vector2 player_pos = v2(0, 0);
float64 seconds_counter = 0.0;
s32 frame_count = 0;
float64 last_time = os_get_current_time_in_seconds();
while (!window.should_close) {
reset_temporary_storage();
float64 now = os_get_current_time_in_seconds();
float64 delta_t = now - last_time;
last_time = now;
os_update();
if (is_key_just_pressed(KEY_ESCAPE)) {
window.should_close = true;
}
Vector2 input_axis = v2(0, 0);
if (is_key_down('A')) {
input_axis.x -= 1.0;
}
if (is_key_down('D')) {
input_axis.x += 1.0;
}
if (is_key_down('S')) {
input_axis.y -= 1.0;
}
if (is_key_down('W')) {
input_axis.y += 1.0;
}
input_axis = v2_normalize(input_axis);
player_pos = v2_add(player_pos, v2_mulf(input_axis, 5.0 * delta_t));
Matrix4 xform = m4_scalar(1.0);
xform = m4_translate(xform, v3(player_pos.x, player_pos.y, 0));
draw_image_xform(player, xform, v2(.5f, .5f), COLOR_RED);
gfx_update();
seconds_counter += delta_t;
frame_count += 1;
if (seconds_counter > 1.0) {
log("fps: %i", frame_count);
seconds_counter = 0.0;
frame_count = 0;
}
}
return 0;
}

View file

@ -42,11 +42,13 @@ typedef u8 bool;
void printf(const char* fmt, ...);
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#define assert_line(line, cond, ...) if(!(cond)) { printf("Assertion failed in file " __FILE__ " on line " STR(line) "\nFailed Condition: " #cond ". Message: " __VA_ARGS__); os_break(); }
#define ASSERT_STR_HELPER(x) #x
#define ASSERT_STR(x) ASSERT_STR_HELPER(x)
#define assert_line(line, cond, ...) if(!(cond)) { printf("Assertion failed in file " __FILE__ " on line " ASSERT_STR(line) "\nFailed Condition: " #cond ". Message: " __VA_ARGS__); os_break(); }
#define assert(cond, ...) assert_line(__LINE__, cond, __VA_ARGS__);
#define DEFER(start, end) for(int _i_ = ((start), 0); _i_ == 0; _i_ += 1, (end))
#if CONFIGURATION == RELEASE
#undef assert
#define assert(...)
@ -65,35 +67,73 @@ void printf(const char* fmt, ...);
// Microsoft Visual C++
#define inline __forceinline
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
#include <intrin.h>
#pragma intrinsic(__rdtsc)
inline u64 rdtsc() {
return __rdtsc();
}
#elif defined(__GNUC__) || defined(__GNUG__)
// GNU GCC/G++
#define inline __attribute__((always_inline)) inline
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
inline u64 rdtsc() {
unsigned int lo, hi;
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
#elif defined(__clang__)
// Clang/LLVM
#define inline __attribute__((always_inline)) inline
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
inline u64 rdtsc() {
unsigned int lo, hi;
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
#elif defined(__INTEL_COMPILER) || defined(__ICC)
// Intel C++ Compiler
#define inline __forceinline
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
inline u64 rdtsc() {
return __rdtsc();
}
#elif defined(__BORLANDC__)
// Borland C++
#define inline __inline
#elif defined(__MINGW32__) || defined(__MINGW64__)
// MinGW (Minimalist GNU for Windows)
#define inline __attribute__((always_inline)) inline
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
inline u64 rdtsc() {
unsigned int lo, hi;
__asm {
rdtsc
mov lo, eax
mov hi, edx
}
return ((u64)hi << 32) | lo;
}
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
// Oracle Solaris Studio
#define inline inline __attribute__((always_inline))
inline u64 rdtsc() {
unsigned int lo, hi;
asm volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
#elif defined(__IBMC__) || defined(__IBMCPP__)
// IBM XL C/C++ Compiler
#define inline __attribute__((always_inline)) inline
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
inline u64 rdtsc() {
unsigned int lo, hi;
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
#elif defined(__PGI)
// Portland Group Compiler
#define inline inline __attribute__((always_inline))
inline u64 rdtsc() {
unsigned int lo, hi;
asm volatile("rdtsc" : "=a"(lo), "=d"(hi));
return ((u64)hi << 32) | lo;
}
#else
// Fallback for unknown compilers
#define inline inline

View file

@ -1,5 +1,5 @@
/*
<<<<<< Bytecode compiled from HLSL code below: >>>>>>
<<<<<< Bytecode compiled fro HLSL code below: >>>>>>
struct VS_INPUT
{
@ -78,7 +78,7 @@ float4 ps_main(PS_INPUT input) : SV_TARGET
}
*/
const u8 IMAGE_SHADER_VERTEX_BLOB_BYTES[] = {
const u8 IMAGE_SHADER_VERTEX_BLOB_BYTES[]= {
0x44, 0x58, 0x42, 0x43, 0xdd, 0x02, 0x55, 0xb0, 0x7b, 0x83, 0x6c, 0x34, 0x45, 0xe8, 0x51, 0xd4,
0x76, 0xbf, 0x66, 0x77, 0x01, 0x00, 0x00, 0x00, 0x3c, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00,
0x00, 0x34, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0xd4, 0x01,
@ -136,7 +136,7 @@ const u8 IMAGE_SHADER_VERTEX_BLOB_BYTES[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
};
const u8 IMAGE_SHADER_PIXEL_BLOB_BYTES[] = {
const u8 IMAGE_SHADER_PIXEL_BLOB_BYTES[]= {
0x44, 0x58, 0x42, 0x43, 0xa6, 0x92, 0xe1, 0x1e, 0x36, 0x1d, 0x06, 0x3b, 0xbf, 0x5b, 0xfa, 0x13,
0xd8, 0xd2, 0xd3, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x74, 0x18, 0x00, 0x00, 0x05, 0x00, 0x00,
0x00, 0x34, 0x00, 0x00, 0x00, 0x64, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x34, 0x07,

View file

@ -64,6 +64,7 @@ typedef struct Draw_Quad {
Vector4 uv;
} Draw_Quad;
typedef struct Draw_Quad_Block {
Draw_Quad quad_buffer[QUADS_PER_BLOCK];
u64 num_quads;
@ -71,8 +72,12 @@ typedef struct Draw_Quad_Block {
struct Draw_Quad_Block *next;
} Draw_Quad_Block;
// I made these blocks part of the frame at first so they were temp allocated BUT I think
// that was a mistake because these blocks are accessed a lot so we want it to just be
// persistent memory that's super hot all the time.
Draw_Quad_Block first_block = {0};
typedef struct Draw_Frame {
Draw_Quad_Block first_block;
Draw_Quad_Block *current;
u64 num_blocks;
@ -89,6 +94,9 @@ Draw_Frame draw_frame = ZERO(Draw_Frame);
void reset_draw_frame(Draw_Frame *frame) {
*frame = (Draw_Frame){0};
frame->current = &first_block;
frame->current->num_quads = 0;
float32 aspect = (float32)window.width/(float32)window.height;
frame->projection = m4_make_orthographic_projection(-aspect, aspect, -1, 1, -1, 10);
@ -101,17 +109,20 @@ Draw_Quad *draw_quad_projected(Draw_Quad quad, Matrix4 world_to_clip) {
quad.top_right = m4_transform(world_to_clip, v4(v2_expand(quad.top_right), 0, 1)).xy;
quad.bottom_right = m4_transform(world_to_clip, v4(v2_expand(quad.bottom_right), 0, 1)).xy;
if (!draw_frame.current) draw_frame.current = &draw_frame.first_block;
if (!draw_frame.current) draw_frame.current = &first_block;
if (draw_frame.current == &draw_frame.first_block) draw_frame.num_blocks = 1;
if (draw_frame.current == &first_block) draw_frame.num_blocks = 1;
assert(draw_frame.current->num_quads <= QUADS_PER_BLOCK);
if (draw_frame.current->num_quads == QUADS_PER_BLOCK) {
draw_frame.current->next = cast(Draw_Quad_Block*)talloc(sizeof(Draw_Quad_Block));
draw_frame.current = draw_frame.current->next;
draw_frame.current->next = 0;
if (!draw_frame.current->next) {
draw_frame.current->next = cast(Draw_Quad_Block*)alloc(get_heap_allocator(), sizeof(Draw_Quad_Block));
*draw_frame.current->next = ZERO(Draw_Quad_Block);
}
draw_frame.current = draw_frame.current->next;
draw_frame.current->num_quads = 0;
draw_frame.num_blocks += 1;

View file

@ -44,6 +44,53 @@ ID3D11InputLayout *d3d11_image_vertex_layout = 0;
ID3D11Buffer *d3d11_quad_vbo = 0;
u32 d3d11_quad_vbo_size = 0;
void *d3d11_staging_quad_buffer = 0;
const char* d3d11_stringify_category(D3D11_MESSAGE_CATEGORY category) {
switch (category) {
case D3D11_MESSAGE_CATEGORY_APPLICATION_DEFINED: return "Application Defined";
case D3D11_MESSAGE_CATEGORY_MISCELLANEOUS: return "Miscellaneous";
case D3D11_MESSAGE_CATEGORY_INITIALIZATION: return "Initialization";
case D3D11_MESSAGE_CATEGORY_CLEANUP: return "Cleanup";
case D3D11_MESSAGE_CATEGORY_COMPILATION: return "Compilation";
case D3D11_MESSAGE_CATEGORY_STATE_CREATION: return "State Creation";
case D3D11_MESSAGE_CATEGORY_STATE_SETTING: return "State Setting";
case D3D11_MESSAGE_CATEGORY_STATE_GETTING: return "State Getting";
case D3D11_MESSAGE_CATEGORY_RESOURCE_MANIPULATION: return "Resource Manipulation";
case D3D11_MESSAGE_CATEGORY_EXECUTION: return "Execution";
default: return "Unknown";
}
}
const char* d3d11_stringify_severity(D3D11_MESSAGE_SEVERITY severity) {
switch (severity) {
case D3D11_MESSAGE_SEVERITY_CORRUPTION: return "Corruption";
case D3D11_MESSAGE_SEVERITY_ERROR: return "Error";
case D3D11_MESSAGE_SEVERITY_WARNING: return "Warning";
case D3D11_MESSAGE_SEVERITY_INFO: return "Info";
case D3D11_MESSAGE_SEVERITY_MESSAGE: return "Message";
default: return "Unknown";
}
}
void CALLBACK d3d11_debug_callback(D3D11_MESSAGE_CATEGORY category, D3D11_MESSAGE_SEVERITY severity, D3D11_MESSAGE_ID id, const char* description)
{
string msg = tprint("D3D11 MESSAGE [Category: %cs, Severity: %cs, id: %d]: %cs", d3d11_stringify_category(category), d3d11_stringify_severity(severity), id, description);
switch (severity) {
case D3D11_MESSAGE_SEVERITY_CORRUPTION:
case D3D11_MESSAGE_SEVERITY_ERROR:
log_error(msg);
case D3D11_MESSAGE_SEVERITY_WARNING:
log_warning(msg);
case D3D11_MESSAGE_SEVERITY_INFO:
log_info(msg);
case D3D11_MESSAGE_SEVERITY_MESSAGE:
log_verbose(msg);
default:
log("Ligma");
}
}
#define win32_check_hr(hr) win32_check_hr_impl(hr, __LINE__, __FILE__);
void win32_check_hr_impl(HRESULT hr, u32 line, const char* file_name) {
@ -313,7 +360,7 @@ void gfx_init() {
#if OOGABOOGA_DEV
string source;
bool source_ok = os_read_entire_file(fxstr("oogabooga/dev/d3d11_image_shader.hlsl"), &source, get_heap_allocator()); // #Leak
bool source_ok = os_read_entire_file("oogabooga/dev/d3d11_image_shader.hlsl", &source, get_heap_allocator()); // #Leak
assert(source_ok, "Could not open d3d11_image_shader source");
// Compile vertex shader
@ -336,28 +383,28 @@ void gfx_init() {
///
// Dump blobs to the .c
File blob_file = os_file_open(fxstr("oogabooga/d3d11_image_shader_bytecode.c"), O_WRITE | O_CREATE);
os_file_write_string(blob_file, fxstr("/*\n"));
os_file_write_string(blob_file, fxstr("<<<<<< Bytecode compiled from HLSL code below: >>>>>>\n\n"));
File blob_file = os_file_open("oogabooga/d3d11_image_shader_bytecode.c", O_WRITE | O_CREATE);
os_file_write_string(blob_file, STR("/*\n"));
os_file_write_string(blob_file, STR("<<<<<< Bytecode compiled fro HLSL code below: >>>>>>\n\n"));
os_file_write_string(blob_file, source);
os_file_write_string(blob_file, fxstr("\n*/\n\n"));
os_file_write_string(blob_file, STR("\n*/\n\n"));
os_file_write_string(blob_file, fxstr("const u8 IMAGE_SHADER_VERTEX_BLOB_BYTES[] = {\n"));
os_file_write_string(blob_file, STR("const u8 IMAGE_SHADER_VERTEX_BLOB_BYTES[]= {\n"));
for (u64 i = 0; i < vs_size; i++) {
os_file_write_string(blob_file, tprint("0x%02x", (int)((u8*)vs_buffer)[i]));
if (i < vs_size-1) os_file_write_string(blob_file, fxstr(", "));
if (i % 15 == 0 && i != 0) os_file_write_string(blob_file, fxstr("\n"));
}
os_file_write_string(blob_file, fxstr("\n};\n"));
if (i < vs_size-1) os_file_write_string(blob_file, STR(", "));
if (i % 15 == 0 && i != 0) os_file_write_string(blob_file, STR("\n"));
}
os_file_write_string(blob_file, STR("\n};\n"));
os_file_write_string(blob_file, fxstr("const u8 IMAGE_SHADER_PIXEL_BLOB_BYTES[] = {\n"));
os_file_write_string(blob_file, STR("const u8 IMAGE_SHADER_PIXEL_BLOB_BYTES[]= {\n"));
for (u64 i = 0; i < ps_size; i++) {
os_file_write_string(blob_file, tprint("0x%02x", (int)((u8*)ps_buffer)[i]));
if (i < ps_size-1) os_file_write_string(blob_file, fxstr(", "));
if (i % 15 == 0 && i != 0) os_file_write_string(blob_file, fxstr("\n"));
}
os_file_write_string(blob_file, fxstr("\n};\n"));
os_file_close(blob_file);
if (i < ps_size-1) os_file_write_string(blob_file, STR(", "));
if (i % 15 == 0 && i != 0) os_file_write_string(blob_file, STR("\n"));
}
os_file_write_string(blob_file, STR("\n};\n"));
os_file_close(blob_file);
#else
@ -451,7 +498,6 @@ void gfx_update() {
VTABLE(ClearRenderTargetView, d3d11_context, d3d11_window_render_target_view, (float*)&window.clear_color);
f64 rest_before = os_get_current_time_in_seconds();
HRESULT hr;
@ -487,9 +533,11 @@ void gfx_update() {
///
// Maybe grow quad vbo
u32 required_size = sizeof(D3D11_Vertex) * draw_frame.num_blocks*QUADS_PER_BLOCK*6;
if (required_size > d3d11_quad_vbo_size) {
if (d3d11_quad_vbo) {
D3D11Release(d3d11_quad_vbo);
dealloc(get_heap_allocator(), d3d11_staging_quad_buffer);
}
D3D11_BUFFER_DESC desc = ZERO(D3D11_BUFFER_DESC);
desc.Usage = D3D11_USAGE_DYNAMIC;
@ -500,126 +548,146 @@ void gfx_update() {
assert(SUCCEEDED(hr), "CreateBuffer failed");
d3d11_quad_vbo_size = required_size;
d3d11_staging_quad_buffer = alloc(get_heap_allocator(), d3d11_quad_vbo_size);
log_verbose("Grew quad vbo to %d bytes.", d3d11_quad_vbo_size);
}
f64 rest_before = os_get_current_time_in_seconds();
if (draw_frame.num_blocks > 0) {
///
// Render geometry from into vbo quad list
D3D11_MAPPED_SUBRESOURCE buffer_mapping;
VTABLE(Map, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0, D3D11_MAP_WRITE_DISCARD, 0, &buffer_mapping);
ID3D11ShaderResourceView *textures[32];
ID3D11ShaderResourceView *last_texture = 0;
u64 num_textures = 0;
D3D11_Vertex* head = buffer_mapping.pData;
D3D11_Vertex* head = (D3D11_Vertex*)d3d11_staging_quad_buffer;
D3D11_Vertex* pointer = head;
u64 number_of_rendered_quads = 0;
Draw_Quad_Block *block = &draw_frame.first_block;
while (block != 0) {
for (u64 i = 0; i < block->num_quads; i++) {
Draw_Quad *q = &block->quad_buffer[i];
int texture_index = -1;
if (q->image) {
if (!q->image->gfx_handle) {
D3D11_TEXTURE2D_DESC desc = ZERO(D3D11_TEXTURE2D_DESC);
desc.Width = q->image->width;
desc.Height = q->image->height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
Draw_Quad_Block *block = &first_block;
tm_scope_cycles("Quad processing") {
while (block != 0 && block->num_quads > 0) tm_scope_cycles("ad2As") {
for (u64 i = 0; i < block->num_quads; i++) tm_scope_cycles("Single quad") {
Draw_Quad *q = &block->quad_buffer[i];
int texture_index = -1;
if (q->image) {
if (!q->image->gfx_handle) {
D3D11_TEXTURE2D_DESC desc = ZERO(D3D11_TEXTURE2D_DESC);
desc.Width = q->image->width;
desc.Height = q->image->height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA data = ZERO(D3D11_SUBRESOURCE_DATA);
data.pSysMem = q->image->data;
data.SysMemPitch = q->image->width * 4; // #Magicvalue assuming 4 channels
ID3D11Texture2D* texture = 0;
HRESULT hr = VTABLE(CreateTexture2D, d3d11_device, &desc, &data, &texture);
win32_check_hr(hr);
hr = VTABLE(CreateShaderResourceView, d3d11_device, (ID3D11Resource*)texture, 0, &q->image->gfx_handle);
win32_check_hr(hr);
log_verbose("Created an image of width %d and height %d.", q->image->width, q->image->height);
}
D3D11_SUBRESOURCE_DATA data = ZERO(D3D11_SUBRESOURCE_DATA);
data.pSysMem = q->image->data;
data.SysMemPitch = q->image->width * 4; // #Magicvalue assuming 4 channels
ID3D11Texture2D* texture = 0;
HRESULT hr = VTABLE(CreateTexture2D, d3d11_device, &desc, &data, &texture);
win32_check_hr(hr);
hr = VTABLE(CreateShaderResourceView, d3d11_device, (ID3D11Resource*)texture, 0, &q->image->gfx_handle);
win32_check_hr(hr);
log_verbose("Created an image of width %d and height %d.", q->image->width, q->image->height);
if (last_texture == q->image->gfx_handle) {
texture_index = (int)(num_textures-1);
} else {
// First look if texture is already bound
for (u64 j = 0; j < num_textures; j++) {
if (textures[j] == q->image->gfx_handle) {
texture_index = (int)j;
break;
}
}
// Otherwise use a new slot
if (texture_index <= -1) {
if (num_textures >= 32) {
// If max textures reached, make a draw call and start over
D3D11_MAPPED_SUBRESOURCE buffer_mapping;
VTABLE(Map, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &buffer_mapping);
memcpy(buffer_mapping.pData, d3d11_staging_quad_buffer, number_of_rendered_quads*sizeof(D3D11_Vertex)*6);
VTABLE(Unmap, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0);
d3d11_draw_call(number_of_rendered_quads, textures, num_textures);
head = (D3D11_Vertex*)d3d11_staging_quad_buffer;
num_textures = 0;
texture_index = 0;
number_of_rendered_quads = 0;
pointer = head;
} else {
texture_index = (int)num_textures;
num_textures += 1;
}
}
}
textures[texture_index] = q->image->gfx_handle;
last_texture = q->image->gfx_handle;
}
if (last_texture == q->image->gfx_handle) {
texture_index = (int)(num_textures-1);
} else {
// First look if texture is already bound
for (u64 j = 0; j < num_textures; j++) {
if (textures[j] == q->image->gfx_handle) {
texture_index = (int)j;
break;
}
}
// Otherwise use a new slot
if (texture_index <= -1) {
if (num_textures >= 32) {
// If max textures reached, make a draw call and start over
d3d11_draw_call(number_of_rendered_quads, textures, num_textures);
num_textures = 0;
texture_index = 0;
number_of_rendered_quads = 0;
pointer = head;
} else {
texture_index = (int)num_textures;
num_textures += 1;
}
}
// We will write to 6 vertices for the one quad (two tris)
{
D3D11_Vertex* BL = pointer + 0;
D3D11_Vertex* TL = pointer + 1;
D3D11_Vertex* TR = pointer + 2;
D3D11_Vertex* BL2 = pointer + 3;
D3D11_Vertex* TR2 = pointer + 4;
D3D11_Vertex* BR = pointer + 5;
pointer += 6;
BL->position = v4(q->bottom_left.x, q->bottom_left.y, 0, 1);
TL->position = v4(q->top_left.x, q->top_left.y, 0, 1);
TR->position = v4(q->top_right.x, q->top_right.y, 0, 1);
BR->position = v4(q->bottom_right.x, q->bottom_right.y, 0, 1);
BL->uv = v2(q->uv.x1, q->uv.y1);
TL->uv = v2(q->uv.x1, q->uv.y2);
TR->uv = v2(q->uv.x2, q->uv.y2);
BR->uv = v2(q->uv.x2, q->uv.y1);
BL->color = TL->color = TR->color = BR->color = q->color;
BL->texture_index=TL->texture_index=TR->texture_index=BR->texture_index = texture_index;
*BL2 = *BL;
*TR2 = *TR;
number_of_rendered_quads += 1;
}
textures[texture_index] = q->image->gfx_handle;
last_texture = q->image->gfx_handle;
}
// We will write to 6 vertices for the one quad (two tris)
D3D11_Vertex* BL = pointer + 0;
D3D11_Vertex* TL = pointer + 1;
D3D11_Vertex* TR = pointer + 2;
D3D11_Vertex* BL2 = pointer + 3;
D3D11_Vertex* TR2 = pointer + 4;
D3D11_Vertex* BR = pointer + 5;
pointer += 6;
BL->position = v4(q->bottom_left.x, q->bottom_left.y, 0, 1);
TL->position = v4(q->top_left.x, q->top_left.y, 0, 1);
TR->position = v4(q->top_right.x, q->top_right.y, 0, 1);
BR->position = v4(q->bottom_right.x, q->bottom_right.y, 0, 1);
BL->uv = v2(q->uv.x1, q->uv.y1);
TL->uv = v2(q->uv.x1, q->uv.y2);
TR->uv = v2(q->uv.x2, q->uv.y2);
BR->uv = v2(q->uv.x2, q->uv.y1);
BL->color = TL->color = TR->color = BR->color = q->color;
BL->texture_index=TL->texture_index=TR->texture_index=BR->texture_index = texture_index;
*BL2 = *BL;
*TR2 = *TR;
number_of_rendered_quads += 1;
block = block->next;
}
block = block->next;
}
D3D11_MAPPED_SUBRESOURCE buffer_mapping;
VTABLE(Map, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &buffer_mapping);
memcpy(buffer_mapping.pData, d3d11_staging_quad_buffer, number_of_rendered_quads*sizeof(D3D11_Vertex)*6);
VTABLE(Unmap, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0);
///
// Draw call
u64 before_draw = os_get_current_cycle_count();
d3d11_draw_call(number_of_rendered_quads, textures, num_textures);
u64 after_draw = os_get_current_cycle_count();
//log("Draw call took %llu cycles", after_draw-before_draw);
}
f64 rest_after = os_get_current_time_in_seconds();
@ -633,5 +701,27 @@ void gfx_update() {
log("Present took %.2fms", (after-before_present)*1000.0);
win32_check_hr(hr);
#if CONFIGURATION == DEBUG
///
// Check debug messages, output to stdout
ID3D11InfoQueue* info_q = 0;
hr = VTABLE(QueryInterface, d3d11_device, &IID_ID3D11InfoQueue, (void**)&info_q);
if (SUCCEEDED(hr)) {
u64 msg_count = VTABLE(GetNumStoredMessagesAllowedByRetrievalFilter, info_q);
for (u64 i = 0; i < msg_count; i++) {
SIZE_T msg_size = 0;
VTABLE(GetMessage, info_q, i, 0, &msg_size);
D3D11_MESSAGE* msg = (D3D11_MESSAGE*)talloc(msg_size);
if (msg) {
VTABLE(GetMessage, info_q, i, msg, &msg_size); // Get the actual message
d3d11_debug_callback(msg->Category, msg->Severity, msg->ID, msg->pDescription);
}
}
}
#endif
reset_draw_frame(&draw_frame);
}

View file

@ -19,7 +19,7 @@ void* initialization_allocator_proc(u64 size, void *p, Allocator_Message message
init_memory_head += size;
if (init_memory_head >= ((u8*)init_memory_arena+INIT_MEMORY_SIZE)) {
os_write_string_to_stdout(cstr("Out of initialization memory! Please provide more by increasing INIT_MEMORY_SIZE"));
os_write_string_to_stdout(STR("Out of initialization memory! Please provide more by increasing INIT_MEMORY_SIZE"));
os_break();
}
return p;
@ -478,7 +478,7 @@ void* talloc(u64 size) {
if ((u8*)temporary_storage_pointer >= (u8*)temporary_storage+TEMPORARY_STORAGE_SIZE) {
if (!has_warned_temporary_storage_overflow) {
os_write_string_to_stdout(cstr("WARNING: temporary storage was overflown, we wrap around at the start.\n"));
os_write_string_to_stdout(STR("WARNING: temporary storage was overflown, we wrap around at the start.\n"));
}
temporary_storage_pointer = temporary_storage;
return talloc(size);;

View file

@ -57,24 +57,7 @@ void lodepng_free(void* ptr) {
// One day I might write my own png decoder so we don't even need this
#include "third_party/lodepng.h"
#include "third_party/lodepng.c"
/*
To decode a png file:
// 1. Read to memory
string path = fixed_string("my_image.png");
string png;
os_read_entire_file(path, &png);
// 2. Decode
u8 *image;
u32 width;
u32 height;
u32 error = lodepng_decode32(&image, &width, &height, png.data, png.count);
// Cleanup
dealloc_string(png);
dealloc(image);
*/
@ -100,6 +83,9 @@ void lodepng_free(void* ptr) {
#error "Current OS not supported!";
#endif
#define GFX_RENDERER_D3D11 0
#define GFX_RENDERER_VULKAN 1
#define GFX_RENDERER_METAL 2
@ -125,6 +111,58 @@ void lodepng_free(void* ptr) {
#include "os_interface.c"
#include "gfx_interface.c"
// #Cleanup #Temporary
// This should be somewhere else
String_Builder _profile_output = {0};
bool profiler_initted = false;
Spinlock *_profiler_lock = 0;
void dump_profile_result() {
File file = os_file_open("google_trace.json", O_CREATE | O_WRITE);
os_file_write_string(file, STR("["));
os_file_write_string(file, _profile_output.result);
os_file_write_string(file, STR("{}]"));
os_file_close(file);
log_verbose("Wrote profiling result to google_trace.json");
}
void _profiler_report_time_cycles(string name, u64 count, u64 start) {
if (!profiler_initted) {
_profiler_lock = os_make_spinlock(get_heap_allocator());
profiler_initted = true;
string_builder_init_reserve(&_profile_output, 1024*1000, get_heap_allocator());
}
os_spinlock_lock(_profiler_lock);
string fmt = STR("{\"cat\":\"function\",\"dur\":%.3f,\"name\":\"%s\",\"ph\":\"X\",\"pid\":0,\"tid\":%zu,\"ts\":%lld},");
string_builder_print(&_profile_output, fmt, (float64)count*1000, name, GetCurrentThreadId(), start*1000);
os_spinlock_unlock(_profiler_lock);
}
#if ENABLE_PROFILING
#define tm_scope_cycles(name) \
for (u64 start_time = os_get_current_cycle_count(), end_time = start_time, elapsed_time = 0; \
elapsed_time == 0; \
elapsed_time = (end_time = os_get_current_cycle_count()) - start_time, _profiler_report_time_cycles(STR(name), elapsed_time, start_time))
#define tm_scope_cycles_var(name, var) \
for (u64 start_time = os_get_current_cycle_count(), end_time = start_time, elapsed_time = 0; \
elapsed_time == 0; \
elapsed_time = (end_time = os_get_current_cycle_count()) - start_time, var=elapsed_time)
#define tm_scope_cycles_accum(name, var) \
for (u64 start_time = os_get_current_cycle_count(), end_time = start_time, elapsed_time = 0; \
elapsed_time == 0; \
elapsed_time = (end_time = os_get_current_cycle_count()) - start_time, var+=elapsed_time)
#else
#define tm_scope_cycles(...)
#define tm_scope_cycles_var(...)
#define tm_scope_cycles_accum(...)
#endif
// I hope whoever caused this @ microsoft is fired.
#ifdef near
#undef near
@ -195,6 +233,12 @@ int main(int argc, char **argv) {
int code = ENTRY_PROC(argc, argv);
#if ENABLE_PROFILING
dump_profile_result();
#endif
printf("Ooga booga program exit with code %i\n", code);
return code;

View file

@ -134,6 +134,11 @@ LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wpar
void os_init(u64 program_memory_size) {
timeBeginPeriod(1);
#if CONFIGURATION == RELEASE
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
#endif
SYSTEM_INFO si;
GetSystemInfo(&si);
os.granularity = cast(u64)si.dwAllocationGranularity;
@ -163,22 +168,22 @@ void os_init(u64 program_memory_size) {
heap_init();
os.crt = os_load_dynamic_library(const_string("msvcrt.dll"));
os.crt = os_load_dynamic_library(STR("msvcrt.dll"));
assert(os.crt != 0, "Could not load win32 crt library. Might be compiled with non-msvc? #Incomplete #Portability");
os.crt_vsnprintf = (Crt_Vsnprintf_Proc)os_dynamic_library_load_symbol(os.crt, const_string("vsnprintf"));
os.crt_vsnprintf = (Crt_Vsnprintf_Proc)os_dynamic_library_load_symbol(os.crt, STR("vsnprintf"));
assert(os.crt_vsnprintf, "Missing vsnprintf in crt");
os.crt_vprintf = (Crt_Vprintf_Proc)os_dynamic_library_load_symbol(os.crt, const_string("vprintf"));
os.crt_vprintf = (Crt_Vprintf_Proc)os_dynamic_library_load_symbol(os.crt, STR("vprintf"));
assert(os.crt_vprintf, "Missing vprintf in crt");
os.crt_vsprintf = (Crt_Vsprintf_Proc)os_dynamic_library_load_symbol(os.crt, const_string("vsprintf"));
os.crt_vsprintf = (Crt_Vsprintf_Proc)os_dynamic_library_load_symbol(os.crt, STR("vsprintf"));
assert(os.crt_vsprintf, "Missing vsprintf in crt");
os.crt_memcpy = (Crt_Memcpy_Proc)os_dynamic_library_load_symbol(os.crt, const_string("memcpy"));
os.crt_memcpy = (Crt_Memcpy_Proc)os_dynamic_library_load_symbol(os.crt, STR("memcpy"));
assert(os.crt_memcpy, "Missing memcpy in crt");
os.crt_memcmp = (Crt_Memcmp_Proc)os_dynamic_library_load_symbol(os.crt, const_string("memcmp"));
os.crt_memcmp = (Crt_Memcmp_Proc)os_dynamic_library_load_symbol(os.crt, STR("memcmp"));
assert(os.crt_memcmp, "Missing crt_memcmp in crt");
os.crt_memset = (Crt_Memset_Proc)os_dynamic_library_load_symbol(os.crt, const_string("memset"));
os.crt_memset = (Crt_Memset_Proc)os_dynamic_library_load_symbol(os.crt, STR("memset"));
assert(os.crt_memset, "Missing memset in crt");
window.title = fixed_string("Unnamed Window");
window.title = STR("Unnamed Window");
window.width = 1280;
window.height = 720;
window.x = 0;
@ -300,6 +305,12 @@ bool os_grow_program_memory(u64 new_size) {
// Thread primitive
DWORD WINAPI win32_thread_invoker(LPVOID param) {
timeBeginPeriod(1);
#if CONFIGURATION == RELEASE
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
#endif
Thread *t = (Thread*)param;
temporary_storage_init();
context = t->initial_context;
@ -447,9 +458,9 @@ void os_high_precision_sleep(f64 ms) {
// Time
///
#include <intrin.h> // #Cdep
u64 os_get_current_cycle_count() {
return __rdtsc();
return rdtsc();
}
float64 os_get_current_time_in_seconds() {
@ -531,7 +542,7 @@ string temp_win32_null_terminated_wide_to_fixed_utf8(const u16 *utf16) {
}
File os_file_open(string path, Os_Io_Open_Flags flags) {
File os_file_open_s(string path, Os_Io_Open_Flags flags) {
DWORD access = GENERIC_READ;
DWORD creation = 0;
@ -553,7 +564,7 @@ void os_file_close(File f) {
CloseHandle(f);
}
bool os_file_delete(string path) {
bool os_file_delete_s(string path) {
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
return (bool)DeleteFileW(path_wide);
}
@ -583,8 +594,8 @@ bool os_write_entire_file_handle(File f, string data) {
return os_file_write_string(f, data);
}
bool os_write_entire_file(string path, string data) {
File file = os_file_open(path, O_WRITE | O_CREATE);
bool os_write_entire_file_s(string path, string data) {
File file = os_file_open_s(path, O_WRITE | O_CREATE);
if (file == OS_INVALID_FILE) {
return false;
}
@ -613,8 +624,8 @@ bool os_read_entire_file_handle(File f, string *result, Allocator allocator) {
return actual_read == file_size.QuadPart;
}
bool os_read_entire_file(string path, string *result, Allocator allocator) {
File file = os_file_open(path, O_READ);
bool os_read_entire_file_s(string path, string *result, Allocator allocator) {
File file = os_file_open_s(path, O_READ);
if (file == OS_INVALID_FILE) {
return false;
}
@ -623,7 +634,7 @@ bool os_read_entire_file(string path, string *result, Allocator allocator) {
return res;
}
bool os_is_file(string path) {
bool os_is_file_s(string path) {
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
assert(path_wide, "Invalid path string");
if (path_wide == NULL) {
@ -639,7 +650,7 @@ bool os_is_file(string path) {
return !(attributes & FILE_ATTRIBUTE_DIRECTORY);
}
bool os_is_directory(string path) {
bool os_is_directory_s(string path) {
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
assert(path_wide, "Invalid path string");
if (path_wide == NULL) {

View file

@ -199,9 +199,11 @@ typedef enum Os_Io_Open_Flags {
// To append, pass WRITE flag without CREATE flag
} Os_Io_Open_Flags;
File os_file_open(string path, Os_Io_Open_Flags flags);
File os_file_open_s(string path, Os_Io_Open_Flags flags);
void os_file_close(File f);
bool os_file_delete(string path);
bool os_file_delete_s(string path);
bool os_make_directory_s(string path, bool recursive);
bool os_file_write_string(File f, string s);
bool os_file_write_bytes(File f, void *buffer, u64 size_in_bytes);
@ -209,12 +211,56 @@ bool os_file_write_bytes(File f, void *buffer, u64 size_in_bytes);
bool os_file_read(File f, void* buffer, u64 bytes_to_read, u64 *actual_read_bytes);
bool os_write_entire_file_handle(File f, string data);
bool os_write_entire_file(string path, string data);
bool os_write_entire_file_s(string path, string data);
bool os_read_entire_file_handle(File f, string *result, Allocator allocator);
bool os_read_entire_file(string path, string *result, Allocator allocator);
bool os_read_entire_file_s(string path, string *result, Allocator allocator);
bool os_is_file(string path);
bool os_is_directory(string path);
bool os_is_file_s(string path);
bool os_is_directory_s(string path);
// It's a little unfortunate that we need to do this but I can't think of a better solution
inline File os_file_open_f(const char *path, Os_Io_Open_Flags flags) {return os_file_open_s(STR(path), flags);}
#define os_file_open(...) _Generic((FIRST_ARG(__VA_ARGS__)), \
string: os_file_open_s, \
default: os_file_open_f \
)(__VA_ARGS__)
inline bool os_file_delete_f(const char *path) {return os_file_delete_s(STR(path));}
#define os_file_delete(...) _Generic((FIRST_ARG(__VA_ARGS__)), \
string: os_file_delete_s, \
default: os_file_delete_f \
)(__VA_ARGS__)
inline bool os_make_directory_f(const char *path, bool recursive) { return os_make_directory_s(STR(path), recursive); }
#define os_make_directory(...) _Generic((FIRST_ARG(__VA_ARGS__)), \
string: os_make_directory_s, \
default: os_make_directory_f \
)(__VA_ARGS__)
inline bool os_write_entire_file_f(const char *path, string data) {return os_write_entire_file_s(STR(path), data);}
#define os_write_entire_file(...) _Generic((FIRST_ARG(__VA_ARGS__)), \
string: os_write_entire_file_s, \
default: os_write_entire_file_f \
)(__VA_ARGS__)
inline bool os_read_entire_file_f(const char *path, string *result, Allocator allocator) {return os_read_entire_file_s(STR(path), result, allocator);}
#define os_read_entire_file(...) _Generic((FIRST_ARG(__VA_ARGS__)), \
string: os_read_entire_file_s, \
default: os_read_entire_file_f \
)(__VA_ARGS__)
inline bool os_is_file_f(const char *path) {return os_is_file_s(STR(path));}
#define os_is_file(...) _Generic((FIRST_ARG(__VA_ARGS__)), \
string: os_is_file_s, \
default: os_is_file_f \
)(__VA_ARGS__)
inline bool os_is_directory_f(const char *path) {return os_is_directory_s(STR(path));}
#define os_is_directory(...) _Generic((FIRST_ARG(__VA_ARGS__)), \
string: os_is_directory_s, \
default: os_is_directory_f \
)(__VA_ARGS__)
void fprints(File f, string fmt, ...);

View file

@ -11,3 +11,7 @@ u64 get_random() {
seed_for_random = seed_for_random * MULTIPLIER + INCREMENT;
return seed_for_random;
}
f32 get_random_float32() {
return (float32)get_random()/(float32)UINT64_MAX;
}

View file

@ -13,11 +13,7 @@ typedef struct string {
u8 *data;
} string;
// Not sure what to call this lol
#define fxstr fixed_string
#define fixed_string const_string
#define cstr const_string
#define const_string(s) ((string){ length_of_null_terminated_string((const char*)s), (u8*)s })
#define STR(s) ((string){ length_of_null_terminated_string((const char*)s), (u8*)s })
inline u64 length_of_null_terminated_string(const char* cstring) {
u64 len = 0;
@ -102,4 +98,6 @@ s64 string_find_from_right(string s, string sub) {
}
return -1;
}
}

View file

@ -83,24 +83,24 @@ u64 format_string_to_buffer(char* buffer, u64 count, const char* fmt, va_list ar
return bufp - buffer;
}
string sprint_null_terminated_string_va_list_to_buffer(const char *fmt, va_list args, void* buffer, u64 count) {
u64 formatted_length = format_string_to_buffer((char*)buffer, count, fmt, args);
string sprint_null_terminated_string_va_list_to_buffer(const char *fmt, va_list args, void* buffer, u64 buffer_size) {
u64 formatted_length = format_string_to_buffer((char*)buffer, buffer_size, fmt, args);
string result;
result.data = (u8*)buffer;
if (formatted_length >= 0 && formatted_length < count) {
if (formatted_length >= 0 && formatted_length < buffer_size) {
result.count = formatted_length;
} else {
result.count = count - 1;
result.count = buffer_size - 1;
}
return result;
}
string sprint_va_list_to_buffer(const string fmt, va_list args, void* buffer, u64 count) {
string sprint_va_list_to_buffer(const string fmt, va_list args, void* buffer, u64 buffer_size) {
char* fmt_cstring = temp_convert_to_null_terminated_string(fmt);
return sprint_null_terminated_string_va_list_to_buffer(fmt_cstring, args, buffer, count);
return sprint_null_terminated_string_va_list_to_buffer(fmt_cstring, args, buffer, buffer_size);
}
string sprint_va_list(Allocator allocator, const string fmt, va_list args) {
@ -230,4 +230,95 @@ void default_logger(Log_Level level, string s) {
case LOG_WARNING: print("[WARNING]: %s\n", s); break;
case LOG_ERROR: print("[ERROR]: %s\n", s); break;
}
}
}
typedef struct String_Builder {
union {
struct {u64 count;u8 *buffer;};
string result;
};
u64 buffer_capacity;
Allocator allocator;
} String_Builder;
void string_builder_reserve(String_Builder *b, u64 required_capacity) {
if (b->buffer_capacity >= required_capacity) return;
u64 new_capacity = max(b->buffer_capacity*2, (u64)(required_capacity*1.5));
u8 *new_buffer = alloc(b->allocator, new_capacity);
if (b->buffer) {
memcpy(new_buffer, b->buffer, b->count);
dealloc(b->allocator, b->buffer);
}
b->buffer = new_buffer;
b->buffer_capacity = new_capacity;
}
void string_builder_init_reserve(String_Builder *b, u64 reserved_capacity, Allocator allocator) {
reserved_capacity = max(reserved_capacity, 128);
b->allocator = allocator;
b->buffer_capacity = 0;
b->buffer = 0;
string_builder_reserve(b, reserved_capacity);
b->count = 0;
}
void string_builder_init(String_Builder *b, Allocator allocator) {
string_builder_init_reserve(b, 128, allocator);
}
void string_builder_append(String_Builder *b, string s) {
assert(b->allocator.proc, "String_Builder is missing allocator");
string_builder_reserve(b, b->count+s.count);
memcpy(b->buffer+b->count, s.data, s.count);
b->count += s.count;
}
void string_builder_prints(String_Builder *b, string fmt, ...) {
assert(b->allocator.proc, "String_Builder is missing allocator");
va_list args1 = 0;
va_start(args1, fmt);
va_list args2 = 0;
va_copy(args2, args1);
u64 formatted_count = format_string_to_buffer(0, 0, temp_convert_to_null_terminated_string(fmt), args1);
va_end(args1);
string_builder_reserve(b, b->count+formatted_count+1);
format_string_to_buffer((char*)b->buffer+b->count, b->count+formatted_count+1, temp_convert_to_null_terminated_string(fmt), args2);
b->count += formatted_count;
va_end(args2);
}
void string_builder_printf(String_Builder *b, const char *fmt, ...) {
assert(b->allocator.proc, "String_Builder is missing allocator");
va_list args1 = 0;
va_start(args1, fmt);
va_list args2 = 0;
va_copy(args2, args1);
u64 formatted_count = format_string_to_buffer(0, 0, fmt, args1);
va_end(args1);
string_builder_reserve(b, b->count+formatted_count+1);
format_string_to_buffer((char*)b->buffer+b->count, b->buffer_capacity-b->count+1, fmt, args2);
b->count += formatted_count;
va_end(args2);
}
#define string_builder_print(...) _Generic((SECOND_ARG(__VA_ARGS__)), \
string: string_builder_prints, \
default: string_builder_printf \
)(__VA_ARGS__)
string string_builder_get_string(String_Builder *b) {
return b->result;
}

View file

@ -1,13 +1,13 @@
void log_heap() {
os_spinlock_lock(heap_lock);
printf("\nHEAP:\n");
print("\nHEAP:\n");
Heap_Block *block = heap_head;
while (block != 0) {
printf("\tBLOCK @ 0x%I64x, %llu bytes\n", (u64)block, block->size);
print("\tBLOCK @ 0x%I64x, %llu bytes\n", (u64)block, block->size);
Heap_Free_Node *node = block->free_head;
@ -15,14 +15,14 @@ void log_heap() {
while (node != 0) {
printf("\t\tFREE NODE @ 0x%I64x, %llu bytes\n", (u64)node, node->size);
print("\t\tFREE NODE @ 0x%I64x, %llu bytes\n", (u64)node, node->size);
total_free += node->size;
node = node->next;
}
printf("\t TOTAL FREE: %llu\n\n", total_free);
print("\t TOTAL FREE: %llu\n\n", total_free);
block = block->next;
}
@ -179,15 +179,15 @@ void test_allocator(bool do_log_heap) {
void test_thread_proc1(Thread* t) {
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
print("Hello from thread %llu\n", t->id);
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
print("Hello from thread %llu\n", t->id);
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
print("Hello from thread %llu\n", t->id);
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
print("Hello from thread %llu\n", t->id);
os_sleep(5);
printf("Hello from thread %llu\n", t->id);
print("Hello from thread %llu\n", t->id);
}
void test_threads() {
@ -195,9 +195,9 @@ void test_threads() {
Thread* t = os_make_thread(test_thread_proc1, get_heap_allocator());
os_start_thread(t);
os_sleep(20);
printf("This should be printed in middle of thread execution\n");
print("This should be printed in middle of thread execution\n");
os_join_thread(t);
printf("Thread is joined\n");
print("Thread is joined\n");
Mutex_Handle m = os_make_mutex();
os_lock_mutex(m);
@ -238,111 +238,196 @@ void test_allocator_threaded(Thread *t) {
}
void test_strings() {
// Test length_of_null_terminated_string
assert(length_of_null_terminated_string("Test") == 4, "Failed: length_of_null_terminated_string");
assert(length_of_null_terminated_string("") == 0, "Failed: length_of_null_terminated_string");
Allocator heap = get_heap_allocator();
// Test alloc_string and dealloc_string
string alloc_str = alloc_string(heap, 10);
assert(alloc_str.data != NULL, "Failed: alloc_string");
assert(alloc_str.count == 10, "Failed: alloc_string");
dealloc_string(heap, alloc_str);
// Test string_concat
string str1 = fixed_string("Hello, ");
string str2 = fixed_string("World!");
string concat_str = string_concat(str1, str2, heap);
assert(concat_str.count == str1.count + str2.count, "Failed: string_concat");
assert(memcmp(concat_str.data, "Hello, World!", concat_str.count) == 0, "Failed: string_concat");
dealloc_string(heap, concat_str);
// Test convert_to_null_terminated_string
char* cstr = convert_to_null_terminated_string(str1, heap);
assert(strcmp(cstr, "Hello, ") == 0, "Failed: convert_to_null_terminated_string");
dealloc(heap, cstr);
// Test temp_convert_to_null_terminated_string
cstr = temp_convert_to_null_terminated_string(str2);
assert(strcmp(cstr, "World!") == 0, "Failed: temp_convert_to_null_terminated_string");
// Test sprint
string format_str = fixed_string("Number: %d");
string formatted_str = sprint(heap, format_str, 42);
char* formatted_cstr = convert_to_null_terminated_string(formatted_str, heap);
assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint");
dealloc(heap, formatted_str.data);
dealloc(heap, formatted_cstr);
// Test tprint
string temp_formatted_str = tprint(format_str, 100);
formatted_cstr = temp_convert_to_null_terminated_string(temp_formatted_str);
assert(strcmp(formatted_cstr, "Number: 100") == 0, "Failed: tprint");
// Test print and printf (visual inspection)
printf("Expected output: Hello, World!\n");
print("Hello, %s!\n", fixed_string("World"));
printf("Expected output: Number: 1234\n");
print(fixed_string("Number: %d\n"), 1234);
{
// Test length_of_null_terminated_string
assert(length_of_null_terminated_string("Test") == 4, "Failed: length_of_null_terminated_string");
assert(length_of_null_terminated_string("") == 0, "Failed: length_of_null_terminated_string");
// Test alloc_string and dealloc_string
string alloc_str = alloc_string(heap, 10);
assert(alloc_str.data != NULL, "Failed: alloc_string");
assert(alloc_str.count == 10, "Failed: alloc_string");
dealloc_string(heap, alloc_str);
// Test string_concat
string str1 = STR("Hello, ");
string str2 = STR("World!");
string concat_str = string_concat(str1, str2, heap);
assert(concat_str.count == str1.count + str2.count, "Failed: string_concat");
assert(memcmp(concat_str.data, "Hello, World!", concat_str.count) == 0, "Failed: string_concat");
dealloc_string(heap, concat_str);
// Test convert_to_null_terminated_string
char* cstr = convert_to_null_terminated_string(str1, heap);
assert(strcmp(cstr, "Hello, ") == 0, "Failed: convert_to_null_terminated_string");
dealloc(heap, cstr);
// Test temp_convert_to_null_terminated_string
cstr = temp_convert_to_null_terminated_string(str2);
assert(strcmp(cstr, "World!") == 0, "Failed: temp_convert_to_null_terminated_string");
// Test sprint
string format_str = STR("Number: %d");
string formatted_str = sprint(heap, format_str, 42);
char* formatted_cstr = convert_to_null_terminated_string(formatted_str, heap);
assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint");
dealloc(heap, formatted_str.data);
dealloc(heap, formatted_cstr);
// Test tprint
string temp_formatted_str = tprint(format_str, 100);
formatted_cstr = temp_convert_to_null_terminated_string(temp_formatted_str);
assert(strcmp(formatted_cstr, "Number: 100") == 0, "Failed: tprint");
// Test print and printf (visual inspection)
print("Expected output: Hello, World!\n");
print("Hello, %s!\n", STR("World"));
print("Expected output: Number: 1234\n");
print("Number: %d\n", 1234);
print("Expected output: Number: 1234\n");
print("Number: %d\n", 1234);
print("Expected output: Mixed values: 42 and 3.14\n");
print("Mixed values: %d and %.2f\n", 42, 3.14);
// This should fail assert and print descriptive error
//print("Expected output (printf): Hello, World!\n");
//print("Hello, %cs!\n", 5);
// This should fail assert and print descriptive error
// print("Expected output (printf): Hello, World!\n");
// print("Hello, %s!\n", "World");
print("Expected output (printf): Hello, World!\n");
print("Hello, %s!\n", STR("World"));
print("Expected output (printf): Number: 5678\n");
print("Number: %d\n", 5678);
print("Expected output (printf): Mixed values: 99 and 2.71\n");
print("Mixed values: %d and %.2f\n", 99, 2.71);
// Test handling of empty strings
string empty_str = STR("");
string concat_empty_str = string_concat(empty_str, empty_str, heap);
assert(concat_empty_str.count == 0, "Failed: string_concat with empty strings");
dealloc_string(heap, concat_empty_str);
// Test very large strings (performance test)
string large_str1 = alloc_string(heap, 1024 * 1024);
string large_str2 = alloc_string(heap, 1024 * 1024);
string large_concat_str = string_concat(large_str1, large_str2, heap);
assert(large_concat_str.count == 2 * 1024 * 1024, "Failed: large string_concat");
dealloc_string(heap, large_str1);
dealloc_string(heap, large_str2);
dealloc_string(heap, large_concat_str);
// Test string with special characters
string special_char_str = STR("Special chars: \n\t\r");
cstr = convert_to_null_terminated_string(special_char_str, heap);
assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string");
dealloc(heap, cstr);
string a = tprint("Hello, %cs!\n", "balls");
string balls1 = string_view(a, 7, 5);
string balls2 = STR("balls");
assert(strings_match(balls1, balls2), "String match failed");
assert(!strings_match(balls1, a), "String match failed");
}
// Test string_builder_init
String_Builder builder;
string_builder_init(&builder, heap);
assert(builder.buffer != NULL, "Failed: string_builder_init");
assert(builder.buffer_capacity >= 128, "Failed: string_builder_init");
assert(builder.count == 0, "Failed: string_builder_init");
printf("Expected output: Number: 1234\n");
print(fixed_string("Number: %d\n"), 1234);
printf("Expected output: Mixed values: 42 and 3.14\n");
print(fixed_string("Mixed values: %d and %.2f\n"), 42, 3.14);
// This should fail assert and print descriptive error
//printf("Expected output (printf): Hello, World!\n");
//printf("Hello, %cs!\n", 5);
// This should fail assert and print descriptive error
// printf("Expected output (printf): Hello, World!\n");
// printf("Hello, %s!\n", "World");
// Test string_builder_reserve
string_builder_reserve(&builder, 256);
assert(builder.buffer_capacity >= 256, "Failed: string_builder_reserve");
printf("Expected output (printf): Hello, World!\n");
printf("Hello, %s!\n", cstr("World"));
printf("Expected output (printf): Number: 5678\n");
printf("Number: %d\n", 5678);
printf("Expected output (printf): Mixed values: 99 and 2.71\n");
printf("Mixed values: %d and %.2f\n", 99, 2.71);
// Test handling of empty strings
string empty_str = fixed_string("");
string concat_empty_str = string_concat(empty_str, empty_str, heap);
assert(concat_empty_str.count == 0, "Failed: string_concat with empty strings");
dealloc_string(heap, concat_empty_str);
// Test very large strings (performance test)
string large_str1 = alloc_string(heap, 1024 * 1024);
string large_str2 = alloc_string(heap, 1024 * 1024);
string large_concat_str = string_concat(large_str1, large_str2, heap);
assert(large_concat_str.count == 2 * 1024 * 1024, "Failed: large string_concat");
dealloc_string(heap, large_str1);
dealloc_string(heap, large_str2);
dealloc_string(heap, large_concat_str);
// Test string with special characters
string special_char_str = fixed_string("Special chars: \n\t\r");
cstr = convert_to_null_terminated_string(special_char_str, heap);
assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string");
dealloc(heap, cstr);
// Test string_builder_append
string str1 = STR("Hello, ");
string_builder_append(&builder, str1);
assert(builder.count == str1.count, "Failed: string_builder_append");
assert(memcmp(builder.buffer, str1.data, str1.count) == 0, "Failed: string_builder_append");
string a = tprintf("Hello, %cs!\n", "balls");
string balls1 = string_view(a, 7, 5);
string balls2 = fixed_string("balls");
string str2 = STR("World!");
string_builder_append(&builder, str2);
assert(builder.count == str1.count + str2.count, "Failed: string_builder_append");
assert(memcmp(builder.buffer, "Hello, World!", builder.count) == 0, "Failed: string_builder_append");
assert(strings_match(balls1, balls2), "String match failed");
assert(!strings_match(balls1, a), "String match failed");
// Test string_builder_prints
string format_str = STR(" Number: %d");
string_builder_prints(&builder, format_str, 42);
char* expected_result = "Hello, World! Number: 42";
assert(builder.count == strlen(expected_result), "Failed: string_builder_prints");
assert(memcmp(builder.buffer, expected_result, builder.count) == 0, "Failed: string_builder_prints");
// Test string_builder_printf
string_builder_printf(&builder, " And a float: %.2f", 3.14);
expected_result = "Hello, World! Number: 42 And a float: 3.14";
assert(builder.count == strlen(expected_result), "Failed: string_builder_printf");
assert(memcmp(builder.buffer, expected_result, builder.count) == 0, "Failed: string_builder_printf");
// Test string_builder_get_string
string result_str = string_builder_get_string(&builder);
assert(result_str.count == builder.count, "Failed: string_builder_get_string");
assert(memcmp(result_str.data, builder.buffer, result_str.count) == 0, "Failed: string_builder_get_string");
// Cleanup
dealloc(heap, builder.buffer);
// Test handling of empty builder
String_Builder empty_builder;
string_builder_init(&empty_builder, heap);
result_str = string_builder_get_string(&empty_builder);
assert(result_str.count == 0, "Failed: empty builder handling");
dealloc(heap, empty_builder.buffer);
// Test appending large strings (performance test)
String_Builder large_builder;
string_builder_init(&large_builder, heap);
string large_str = alloc_string(heap, 1024 * 1024);
memset(large_str.data, 'A', 1024 * 1024);
string_builder_append(&large_builder, large_str);
assert(large_builder.count == 1024 * 1024, "Failed: large string_builder_append");
dealloc_string(heap, large_str);
dealloc(heap, large_builder.buffer);
// Test appending special characters
String_Builder special_char_builder;
string_builder_init(&special_char_builder, heap);
string special_char_str = STR("Special chars: \n\t\r");
string_builder_append(&special_char_builder, special_char_str);
assert(special_char_builder.count == special_char_str.count, "Failed: special character append");
assert(memcmp(special_char_builder.buffer, special_char_str.data, special_char_str.count) == 0, "Failed: special character append");
dealloc(heap, special_char_builder.buffer);
// Test multiple appends
String_Builder multi_append_builder;
string_builder_init(&multi_append_builder, heap);
string str_a = STR("First part");
string str_b = STR(" and second part");
string str_c = STR(" and third part.");
string_builder_append(&multi_append_builder, str_a);
string_builder_append(&multi_append_builder, str_b);
string_builder_append(&multi_append_builder, str_c);
expected_result = "First part and second part and third part.";
assert(multi_append_builder.count == strlen(expected_result), "Failed: multiple appends");
assert(memcmp(multi_append_builder.buffer, expected_result, multi_append_builder.count) == 0, "Failed: multiple appends");
dealloc(heap, multi_append_builder.buffer);
}
void test_file_io() {
#if TARGET_OS == WINDOWS
// Test win32_fixed_utf8_to_null_terminated_wide
string utf8_str = fixed_string("Test");
string utf8_str = STR("Test");
u16 *wide_str = win32_fixed_utf8_to_null_terminated_wide(utf8_str, get_heap_allocator());
assert(wide_str != NULL, "Failed: win32_fixed_utf8_to_null_terminated_wide");
assert(wide_str[4] == 0, "Failed: win32_fixed_utf8_to_null_terminated_wide");
@ -358,20 +443,20 @@ void test_file_io() {
os_file_close(file);
// Test os_file_open and os_file_close
file = os_file_open(fixed_string("test.txt"), O_WRITE | O_CREATE);
file = os_file_open("test.txt", O_WRITE | O_CREATE);
assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)");
os_file_close(file);
// Test os_file_write_string and os_file_read
string hello_world_write = fixed_string("Hello, World!");
file = os_file_open(fixed_string("test.txt"), O_WRITE | O_CREATE);
string hello_world_write = STR("Hello, World!");
file = os_file_open("test.txt", O_WRITE | O_CREATE);
assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)");
bool write_result = os_file_write_string(file, hello_world_write);
assert(write_result, "Failed: os_file_write_string");
os_file_close(file);
file = os_file_open(fixed_string("test.txt"), O_READ);
file = os_file_open("test.txt", O_READ);
assert(file != OS_INVALID_FILE, "Failed: os_file_open (read)");
string hello_world_read = talloc_string(hello_world_write.count);
bool read_result = os_file_read(file, hello_world_read.data, hello_world_read.count, &hello_world_read.count);
@ -380,7 +465,7 @@ void test_file_io() {
os_file_close(file);
// Test os_file_write_bytes
file = os_file_open(fixed_string("test_bytes.txt"), O_WRITE | O_CREATE);
file = os_file_open("test_bytes.txt", O_WRITE | O_CREATE);
assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)");
int int_data = 42;
write_result = os_file_write_bytes(file, &int_data, sizeof(int));
@ -388,28 +473,28 @@ void test_file_io() {
os_file_close(file);
// Test os_read_entire_file and os_write_entire_file
string write_data = fixed_string("Entire file test");
bool write_entire_result = os_write_entire_file(fixed_string("entire_test.txt"), write_data);
string write_data = STR("Entire file test");
bool write_entire_result = os_write_entire_file("entire_test.txt", write_data);
assert(write_entire_result, "Failed: os_write_entire_file");
Allocator heap = get_heap_allocator();
string read_data;
bool read_entire_result = os_read_entire_file(fixed_string("entire_test.txt"), &read_data, heap);
bool read_entire_result = os_read_entire_file("entire_test.txt", &read_data, heap);
assert(read_entire_result, "Failed: os_read_entire_file");
assert(strings_match(read_data, write_data), "Failed: os_read_entire_file write/read mismatch");
assert(memcmp(read_data.data, write_data.data, write_data.count) == 0, "Failed: os_read_entire_file (content mismatch)");
dealloc(heap, read_data.data);
// Test fprint
File balls = os_file_open(fixed_string("balls.txt"), O_WRITE | O_CREATE);
File balls = os_file_open("balls.txt", O_WRITE | O_CREATE);
assert(balls != OS_INVALID_FILE, "Failed: Could not create balls.txt");
fprint(balls, "Hello, %cs!", "Balls");
os_file_close(balls);
string hello_balls;
read_entire_result = os_read_entire_file(fixed_string("balls.txt"), &hello_balls, heap);
read_entire_result = os_read_entire_file("balls.txt", &hello_balls, heap);
assert(read_entire_result, "Failed: could not read balls.txt");
assert(strings_match(hello_balls, fixed_string("Hello, Balls!")), "Failed: balls read/write mismatch. Expected 'Hello, Balls!', got '%s'", hello_balls);
assert(strings_match(hello_balls, STR("Hello, Balls!")), "Failed: balls read/write mismatch. Expected 'Hello, Balls!', got '%s'", hello_balls);
u64 integers[4096];
for (u64 i = 0; i < 4096; i++) {
@ -418,11 +503,11 @@ void test_file_io() {
string integers_data;
integers_data.data = (u8*)integers;
integers_data.count = 4096*sizeof(u64);
bool ok = os_write_entire_file(fxstr("integers"), integers_data);
bool ok = os_write_entire_file("integers", integers_data);
assert(ok, "write integers fail");
string integers_read;
ok = os_read_entire_file(fxstr("integers"), &integers_read, heap);
ok = os_read_entire_file("integers", &integers_read, heap);
assert(ok, "read integers fail");
u64 *new_integers = (u64*)integers_data.data;
assert(integers_read.count == integers_data.count, "Failed: big file read/write mismatch. Read was %d and written was %d", integers_read.count, integers_data.count);
@ -430,32 +515,32 @@ void test_file_io() {
// Clean up test files
bool delete_ok = false;
delete_ok = os_file_delete(fixed_string("test.txt"));
delete_ok = os_file_delete("test.txt");
assert(delete_ok, "Failed: could not delete test.txt");
delete_ok = os_file_delete(fixed_string("test_bytes.txt"));
delete_ok = os_file_delete("test_bytes.txt");
assert(delete_ok, "Failed: could not delete test_bytes.txt");
delete_ok = os_file_delete(fixed_string("entire_test.txt"));
delete_ok = os_file_delete("entire_test.txt");
assert(delete_ok, "Failed: could not delete entire_test.txt");
delete_ok = os_file_delete(fixed_string("balls.txt"));
delete_ok = os_file_delete("balls.txt");
assert(delete_ok, "Failed: could not delete balls.txt");
delete_ok = os_file_delete(fixed_string("integers.txt"));
assert(delete_ok, "Failed: could not delete integers.txt");
delete_ok = os_file_delete("integers");
assert(delete_ok, "Failed: could not delete integers");
}
void oogabooga_run_tests() {
printf("Testing allocator... ");
print("Testing allocator... ");
test_allocator(true);
printf("OK!\n");
print("OK!\n");
printf("Testing threads... ");
print("Testing threads... ");
test_threads();
printf("OK!\n");
print("OK!\n");
printf("Testing strings... ");
print("Testing strings... ");
test_strings();
printf("OK!\n");
print("OK!\n");
printf("Thread bombing allocator... ");
print("Thread bombing allocator... ");
Thread* threads[100];
for (int i = 0; i < 100; i++) {
threads[i] = os_make_thread(test_allocator_threaded, get_heap_allocator());
@ -464,9 +549,9 @@ void oogabooga_run_tests() {
for (int i = 0; i < 100; i++) {
os_join_thread(threads[i]);
}
printf("OK!\n");
print("OK!\n");
printf("Testing file IO... ");
print("Testing file IO... ");
test_file_io();
printf("OK!\n");
print("OK!\n");
}