- 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:
parent
6519955513
commit
b15c7c6e41
18 changed files with 867 additions and 415 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -55,3 +55,5 @@ test_doc.vkn
|
||||||
*.rdi
|
*.rdi
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
|
|
||||||
|
google_trace.json
|
4
build.c
4
build.c
|
@ -7,6 +7,8 @@
|
||||||
// This is only for people developing oogabooga!
|
// This is only for people developing oogabooga!
|
||||||
#define OOGABOOGA_DEV 1
|
#define OOGABOOGA_DEV 1
|
||||||
|
|
||||||
|
#define ENABLE_PROFILING 0
|
||||||
|
|
||||||
// When we need very debug
|
// When we need very debug
|
||||||
// #define CONFIGURATION VERY_DEBUG
|
// #define CONFIGURATION VERY_DEBUG
|
||||||
|
|
||||||
|
@ -26,4 +28,4 @@ typedef struct Context_Extra {
|
||||||
|
|
||||||
// #include "entry_engine_test.c"
|
// #include "entry_engine_test.c"
|
||||||
// #include "entry_minimal_example.c"
|
// #include "entry_minimal_example.c"
|
||||||
#include "entry_randygame.c"
|
#include "entry_randygame.c"
|
14
build_dissassembly.bat
Normal file
14
build_dissassembly.bat
Normal 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
|
|
@ -2,17 +2,17 @@
|
||||||
|
|
||||||
int entry(int argc, char **argv) {
|
int entry(int argc, char **argv) {
|
||||||
|
|
||||||
window.title = fixed_string("Engine Testing Example");
|
window.title = STR("My epic game");
|
||||||
window.width = 1280;
|
window.width = 1280;
|
||||||
window.height = 720;
|
window.height = 720;
|
||||||
window.x = 200;
|
window.x = 200;
|
||||||
window.y = 200;
|
window.y = 0;
|
||||||
|
|
||||||
window.clear_color = hex_to_rgba(0x2a2d3aff);
|
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");
|
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");
|
assert(hammer_image, "Failed loading hammer.png");
|
||||||
|
|
||||||
seed_for_random = os_get_current_cycle_count();
|
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);
|
Matrix4 camera_view = m4_scalar(1.0);
|
||||||
|
|
||||||
float64 last_time = os_get_current_time_in_seconds();
|
float64 last_time = os_get_current_time_in_seconds();
|
||||||
while (!window.should_close) {
|
while (!window.should_close) tm_scope_cycles("Frame") {
|
||||||
reset_temporary_storage();
|
reset_temporary_storage();
|
||||||
|
|
||||||
float64 now = os_get_current_time_in_seconds();
|
float64 now = os_get_current_time_in_seconds();
|
||||||
|
@ -50,7 +50,7 @@ int entry(int argc, char **argv) {
|
||||||
|
|
||||||
if (is_key_just_released('Q')) {
|
if (is_key_just_released('Q')) {
|
||||||
delete_image(bush_image);
|
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");
|
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);
|
Vector2 cam_move = v2_mulf(cam_move_axis, delta * cam_move_speed);
|
||||||
|
|
||||||
camera_view = m4_translate(camera_view, v3(v2_expand(cam_move), 0));
|
camera_view = m4_translate(camera_view, v3(v2_expand(cam_move), 0));
|
||||||
|
|
||||||
draw_frame.view = camera_view;
|
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);
|
Matrix4 hammer_xform = m4_scalar(1.0);
|
||||||
hammer_xform = m4_rotate_z(hammer_xform, (f32)now);
|
hammer_xform = m4_rotate_z(hammer_xform, (f32)now);
|
||||||
hammer_xform = m4_translate(hammer_xform, v3(-.25f, -.25f, 0));
|
hammer_xform = m4_translate(hammer_xform, v3(-.25f, -.25f, 0));
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
int entry(int argc, char **argv) {
|
int entry(int argc, char **argv) {
|
||||||
|
|
||||||
window.title = fixed_string("Minimal Game Example");
|
window.title = STR("Minimal Game Example");
|
||||||
window.width = 1280;
|
window.width = 1280;
|
||||||
window.height = 720;
|
window.height = 720;
|
||||||
window.x = 200;
|
window.x = 200;
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
|
|
||||||
int entry(int argc, char **argv) {
|
int entry(int argc, char **argv) {
|
||||||
|
|
||||||
window.title = fixed_string("Randy's Game");
|
window.title = STR("Randy's Game");
|
||||||
window.width = 1280;
|
window.width = 1280;
|
||||||
window.height = 720;
|
window.height = 720;
|
||||||
window.x = 200;
|
window.x = 200;
|
||||||
window.y = 200;
|
window.y = 200;
|
||||||
window.clear_color = hex_to_rgba(0x2a2d3aff);
|
window.clear_color = hex_to_rgba(0x2a2d3aff);
|
||||||
|
|
||||||
Gfx_Image* player = load_image_from_disk(fixed_string("player.png"), get_heap_allocator());
|
Gfx_Image* player = load_image_from_disk(STR("player.png"), get_heap_allocator());
|
||||||
assert(player, "fuckie wucky happen");
|
assert(player, "fuckie wucky happen");
|
||||||
|
|
||||||
Vector2 player_pos = v2(0, 0);
|
Vector2 player_pos = v2(0, 0);
|
||||||
|
|
|
@ -42,11 +42,13 @@ typedef u8 bool;
|
||||||
|
|
||||||
|
|
||||||
void printf(const char* fmt, ...);
|
void printf(const char* fmt, ...);
|
||||||
#define STR_HELPER(x) #x
|
#define ASSERT_STR_HELPER(x) #x
|
||||||
#define STR(x) STR_HELPER(x)
|
#define ASSERT_STR(x) ASSERT_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_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 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
|
#if CONFIGURATION == RELEASE
|
||||||
#undef assert
|
#undef assert
|
||||||
#define assert(...)
|
#define assert(...)
|
||||||
|
@ -65,35 +67,73 @@ void printf(const char* fmt, ...);
|
||||||
// Microsoft Visual C++
|
// Microsoft Visual C++
|
||||||
#define inline __forceinline
|
#define inline __forceinline
|
||||||
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
||||||
|
#include <intrin.h>
|
||||||
|
#pragma intrinsic(__rdtsc)
|
||||||
|
inline u64 rdtsc() {
|
||||||
|
return __rdtsc();
|
||||||
|
}
|
||||||
#elif defined(__GNUC__) || defined(__GNUG__)
|
#elif defined(__GNUC__) || defined(__GNUG__)
|
||||||
// GNU GCC/G++
|
// GNU GCC/G++
|
||||||
#define inline __attribute__((always_inline)) inline
|
#define inline __attribute__((always_inline)) inline
|
||||||
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
#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__)
|
#elif defined(__clang__)
|
||||||
// Clang/LLVM
|
// Clang/LLVM
|
||||||
#define inline __attribute__((always_inline)) inline
|
#define inline __attribute__((always_inline)) inline
|
||||||
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
#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)
|
#elif defined(__INTEL_COMPILER) || defined(__ICC)
|
||||||
// Intel C++ Compiler
|
// Intel C++ Compiler
|
||||||
#define inline __forceinline
|
#define inline __forceinline
|
||||||
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
||||||
|
inline u64 rdtsc() {
|
||||||
|
return __rdtsc();
|
||||||
|
}
|
||||||
#elif defined(__BORLANDC__)
|
#elif defined(__BORLANDC__)
|
||||||
// Borland C++
|
// Borland C++
|
||||||
#define inline __inline
|
#define inline __inline
|
||||||
#elif defined(__MINGW32__) || defined(__MINGW64__)
|
inline u64 rdtsc() {
|
||||||
// MinGW (Minimalist GNU for Windows)
|
unsigned int lo, hi;
|
||||||
#define inline __attribute__((always_inline)) inline
|
__asm {
|
||||||
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
rdtsc
|
||||||
|
mov lo, eax
|
||||||
|
mov hi, edx
|
||||||
|
}
|
||||||
|
return ((u64)hi << 32) | lo;
|
||||||
|
}
|
||||||
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
|
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
|
||||||
// Oracle Solaris Studio
|
// Oracle Solaris Studio
|
||||||
#define inline inline __attribute__((always_inline))
|
#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__)
|
#elif defined(__IBMC__) || defined(__IBMCPP__)
|
||||||
// IBM XL C/C++ Compiler
|
// IBM XL C/C++ Compiler
|
||||||
#define inline __attribute__((always_inline)) inline
|
#define inline __attribute__((always_inline)) inline
|
||||||
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
#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)
|
#elif defined(__PGI)
|
||||||
// Portland Group Compiler
|
// Portland Group Compiler
|
||||||
#define inline inline __attribute__((always_inline))
|
#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
|
#else
|
||||||
// Fallback for unknown compilers
|
// Fallback for unknown compilers
|
||||||
#define inline inline
|
#define inline inline
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
<<<<<< Bytecode compiled from HLSL code below: >>>>>>
|
<<<<<< Bytecode compiled fro HLSL code below: >>>>>>
|
||||||
|
|
||||||
struct VS_INPUT
|
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,
|
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,
|
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,
|
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, 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,
|
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,
|
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,
|
0x00, 0x34, 0x00, 0x00, 0x00, 0x64, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x34, 0x07,
|
||||||
|
|
|
@ -64,6 +64,7 @@ typedef struct Draw_Quad {
|
||||||
Vector4 uv;
|
Vector4 uv;
|
||||||
} Draw_Quad;
|
} Draw_Quad;
|
||||||
|
|
||||||
|
|
||||||
typedef struct Draw_Quad_Block {
|
typedef struct Draw_Quad_Block {
|
||||||
Draw_Quad quad_buffer[QUADS_PER_BLOCK];
|
Draw_Quad quad_buffer[QUADS_PER_BLOCK];
|
||||||
u64 num_quads;
|
u64 num_quads;
|
||||||
|
@ -71,8 +72,12 @@ typedef struct Draw_Quad_Block {
|
||||||
struct Draw_Quad_Block *next;
|
struct Draw_Quad_Block *next;
|
||||||
} Draw_Quad_Block;
|
} 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 {
|
typedef struct Draw_Frame {
|
||||||
Draw_Quad_Block first_block;
|
|
||||||
Draw_Quad_Block *current;
|
Draw_Quad_Block *current;
|
||||||
u64 num_blocks;
|
u64 num_blocks;
|
||||||
|
|
||||||
|
@ -89,6 +94,9 @@ Draw_Frame draw_frame = ZERO(Draw_Frame);
|
||||||
void reset_draw_frame(Draw_Frame *frame) {
|
void reset_draw_frame(Draw_Frame *frame) {
|
||||||
*frame = (Draw_Frame){0};
|
*frame = (Draw_Frame){0};
|
||||||
|
|
||||||
|
frame->current = &first_block;
|
||||||
|
frame->current->num_quads = 0;
|
||||||
|
|
||||||
float32 aspect = (float32)window.width/(float32)window.height;
|
float32 aspect = (float32)window.width/(float32)window.height;
|
||||||
|
|
||||||
frame->projection = m4_make_orthographic_projection(-aspect, aspect, -1, 1, -1, 10);
|
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.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;
|
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);
|
assert(draw_frame.current->num_quads <= QUADS_PER_BLOCK);
|
||||||
|
|
||||||
if (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.current->num_quads = 0;
|
||||||
|
|
||||||
draw_frame.num_blocks += 1;
|
draw_frame.num_blocks += 1;
|
||||||
|
|
|
@ -44,6 +44,53 @@ ID3D11InputLayout *d3d11_image_vertex_layout = 0;
|
||||||
|
|
||||||
ID3D11Buffer *d3d11_quad_vbo = 0;
|
ID3D11Buffer *d3d11_quad_vbo = 0;
|
||||||
u32 d3d11_quad_vbo_size = 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__);
|
#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) {
|
void win32_check_hr_impl(HRESULT hr, u32 line, const char* file_name) {
|
||||||
|
@ -313,7 +360,7 @@ void gfx_init() {
|
||||||
#if OOGABOOGA_DEV
|
#if OOGABOOGA_DEV
|
||||||
|
|
||||||
string source;
|
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");
|
assert(source_ok, "Could not open d3d11_image_shader source");
|
||||||
|
|
||||||
// Compile vertex shader
|
// Compile vertex shader
|
||||||
|
@ -336,27 +383,27 @@ void gfx_init() {
|
||||||
|
|
||||||
///
|
///
|
||||||
// Dump blobs to the .c
|
// Dump blobs to the .c
|
||||||
File blob_file = os_file_open(fxstr("oogabooga/d3d11_image_shader_bytecode.c"), O_WRITE | O_CREATE);
|
File blob_file = os_file_open("oogabooga/d3d11_image_shader_bytecode.c", O_WRITE | O_CREATE);
|
||||||
os_file_write_string(blob_file, fxstr("/*\n"));
|
os_file_write_string(blob_file, STR("/*\n"));
|
||||||
os_file_write_string(blob_file, fxstr("<<<<<< Bytecode compiled from HLSL code below: >>>>>>\n\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, 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++) {
|
for (u64 i = 0; i < vs_size; i++) {
|
||||||
os_file_write_string(blob_file, tprint("0x%02x", (int)((u8*)vs_buffer)[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 < vs_size-1) os_file_write_string(blob_file, STR(", "));
|
||||||
if (i % 15 == 0 && i != 0) os_file_write_string(blob_file, fxstr("\n"));
|
if (i % 15 == 0 && i != 0) os_file_write_string(blob_file, STR("\n"));
|
||||||
}
|
}
|
||||||
os_file_write_string(blob_file, fxstr("\n};\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++) {
|
for (u64 i = 0; i < ps_size; i++) {
|
||||||
os_file_write_string(blob_file, tprint("0x%02x", (int)((u8*)ps_buffer)[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 < ps_size-1) os_file_write_string(blob_file, STR(", "));
|
||||||
if (i % 15 == 0 && i != 0) os_file_write_string(blob_file, fxstr("\n"));
|
if (i % 15 == 0 && i != 0) os_file_write_string(blob_file, STR("\n"));
|
||||||
}
|
}
|
||||||
os_file_write_string(blob_file, fxstr("\n};\n"));
|
os_file_write_string(blob_file, STR("\n};\n"));
|
||||||
os_file_close(blob_file);
|
os_file_close(blob_file);
|
||||||
|
|
||||||
|
|
||||||
|
@ -451,7 +498,6 @@ void gfx_update() {
|
||||||
|
|
||||||
VTABLE(ClearRenderTargetView, d3d11_context, d3d11_window_render_target_view, (float*)&window.clear_color);
|
VTABLE(ClearRenderTargetView, d3d11_context, d3d11_window_render_target_view, (float*)&window.clear_color);
|
||||||
|
|
||||||
f64 rest_before = os_get_current_time_in_seconds();
|
|
||||||
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
|
@ -487,9 +533,11 @@ void gfx_update() {
|
||||||
///
|
///
|
||||||
// Maybe grow quad vbo
|
// Maybe grow quad vbo
|
||||||
u32 required_size = sizeof(D3D11_Vertex) * draw_frame.num_blocks*QUADS_PER_BLOCK*6;
|
u32 required_size = sizeof(D3D11_Vertex) * draw_frame.num_blocks*QUADS_PER_BLOCK*6;
|
||||||
|
|
||||||
if (required_size > d3d11_quad_vbo_size) {
|
if (required_size > d3d11_quad_vbo_size) {
|
||||||
if (d3d11_quad_vbo) {
|
if (d3d11_quad_vbo) {
|
||||||
D3D11Release(d3d11_quad_vbo);
|
D3D11Release(d3d11_quad_vbo);
|
||||||
|
dealloc(get_heap_allocator(), d3d11_staging_quad_buffer);
|
||||||
}
|
}
|
||||||
D3D11_BUFFER_DESC desc = ZERO(D3D11_BUFFER_DESC);
|
D3D11_BUFFER_DESC desc = ZERO(D3D11_BUFFER_DESC);
|
||||||
desc.Usage = D3D11_USAGE_DYNAMIC;
|
desc.Usage = D3D11_USAGE_DYNAMIC;
|
||||||
|
@ -500,26 +548,30 @@ void gfx_update() {
|
||||||
assert(SUCCEEDED(hr), "CreateBuffer failed");
|
assert(SUCCEEDED(hr), "CreateBuffer failed");
|
||||||
d3d11_quad_vbo_size = required_size;
|
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);
|
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) {
|
if (draw_frame.num_blocks > 0) {
|
||||||
///
|
///
|
||||||
// Render geometry from into vbo quad list
|
// 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 *textures[32];
|
||||||
ID3D11ShaderResourceView *last_texture = 0;
|
ID3D11ShaderResourceView *last_texture = 0;
|
||||||
u64 num_textures = 0;
|
u64 num_textures = 0;
|
||||||
|
|
||||||
D3D11_Vertex* head = buffer_mapping.pData;
|
D3D11_Vertex* head = (D3D11_Vertex*)d3d11_staging_quad_buffer;
|
||||||
D3D11_Vertex* pointer = head;
|
D3D11_Vertex* pointer = head;
|
||||||
u64 number_of_rendered_quads = 0;
|
u64 number_of_rendered_quads = 0;
|
||||||
Draw_Quad_Block *block = &draw_frame.first_block;
|
Draw_Quad_Block *block = &first_block;
|
||||||
while (block != 0) {
|
|
||||||
|
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") {
|
||||||
|
|
||||||
for (u64 i = 0; i < block->num_quads; i++) {
|
|
||||||
Draw_Quad *q = &block->quad_buffer[i];
|
Draw_Quad *q = &block->quad_buffer[i];
|
||||||
|
|
||||||
int texture_index = -1;
|
int texture_index = -1;
|
||||||
|
@ -567,7 +619,12 @@ void gfx_update() {
|
||||||
if (texture_index <= -1) {
|
if (texture_index <= -1) {
|
||||||
if (num_textures >= 32) {
|
if (num_textures >= 32) {
|
||||||
// If max textures reached, make a draw call and start over
|
// 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);
|
d3d11_draw_call(number_of_rendered_quads, textures, num_textures);
|
||||||
|
head = (D3D11_Vertex*)d3d11_staging_quad_buffer;
|
||||||
num_textures = 0;
|
num_textures = 0;
|
||||||
texture_index = 0;
|
texture_index = 0;
|
||||||
number_of_rendered_quads = 0;
|
number_of_rendered_quads = 0;
|
||||||
|
@ -583,6 +640,8 @@ void gfx_update() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We will write to 6 vertices for the one quad (two tris)
|
// We will write to 6 vertices for the one quad (two tris)
|
||||||
|
{
|
||||||
|
|
||||||
D3D11_Vertex* BL = pointer + 0;
|
D3D11_Vertex* BL = pointer + 0;
|
||||||
D3D11_Vertex* TL = pointer + 1;
|
D3D11_Vertex* TL = pointer + 1;
|
||||||
D3D11_Vertex* TR = pointer + 2;
|
D3D11_Vertex* TR = pointer + 2;
|
||||||
|
@ -610,16 +669,25 @@ void gfx_update() {
|
||||||
|
|
||||||
number_of_rendered_quads += 1;
|
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);
|
VTABLE(Unmap, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0);
|
||||||
|
|
||||||
///
|
///
|
||||||
// Draw call
|
// Draw call
|
||||||
|
|
||||||
|
u64 before_draw = os_get_current_cycle_count();
|
||||||
d3d11_draw_call(number_of_rendered_quads, textures, num_textures);
|
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();
|
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);
|
log("Present took %.2fms", (after-before_present)*1000.0);
|
||||||
win32_check_hr(hr);
|
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);
|
reset_draw_frame(&draw_frame);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ void* initialization_allocator_proc(u64 size, void *p, Allocator_Message message
|
||||||
init_memory_head += size;
|
init_memory_head += size;
|
||||||
|
|
||||||
if (init_memory_head >= ((u8*)init_memory_arena+INIT_MEMORY_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();
|
os_break();
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
|
@ -478,7 +478,7 @@ void* talloc(u64 size) {
|
||||||
|
|
||||||
if ((u8*)temporary_storage_pointer >= (u8*)temporary_storage+TEMPORARY_STORAGE_SIZE) {
|
if ((u8*)temporary_storage_pointer >= (u8*)temporary_storage+TEMPORARY_STORAGE_SIZE) {
|
||||||
if (!has_warned_temporary_storage_overflow) {
|
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;
|
temporary_storage_pointer = temporary_storage;
|
||||||
return talloc(size);;
|
return talloc(size);;
|
||||||
|
|
|
@ -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
|
// 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.h"
|
||||||
#include "third_party/lodepng.c"
|
#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!";
|
#error "Current OS not supported!";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define GFX_RENDERER_D3D11 0
|
#define GFX_RENDERER_D3D11 0
|
||||||
#define GFX_RENDERER_VULKAN 1
|
#define GFX_RENDERER_VULKAN 1
|
||||||
#define GFX_RENDERER_METAL 2
|
#define GFX_RENDERER_METAL 2
|
||||||
|
@ -125,6 +111,58 @@ void lodepng_free(void* ptr) {
|
||||||
#include "os_interface.c"
|
#include "os_interface.c"
|
||||||
#include "gfx_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.
|
// I hope whoever caused this @ microsoft is fired.
|
||||||
#ifdef near
|
#ifdef near
|
||||||
#undef near
|
#undef near
|
||||||
|
@ -195,6 +233,12 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
int code = ENTRY_PROC(argc, argv);
|
int code = ENTRY_PROC(argc, argv);
|
||||||
|
|
||||||
|
#if ENABLE_PROFILING
|
||||||
|
|
||||||
|
dump_profile_result();
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
printf("Ooga booga program exit with code %i\n", code);
|
printf("Ooga booga program exit with code %i\n", code);
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
|
|
|
@ -134,6 +134,11 @@ LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wpar
|
||||||
|
|
||||||
void os_init(u64 program_memory_size) {
|
void os_init(u64 program_memory_size) {
|
||||||
|
|
||||||
|
timeBeginPeriod(1);
|
||||||
|
#if CONFIGURATION == RELEASE
|
||||||
|
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
||||||
|
#endif
|
||||||
|
|
||||||
SYSTEM_INFO si;
|
SYSTEM_INFO si;
|
||||||
GetSystemInfo(&si);
|
GetSystemInfo(&si);
|
||||||
os.granularity = cast(u64)si.dwAllocationGranularity;
|
os.granularity = cast(u64)si.dwAllocationGranularity;
|
||||||
|
@ -163,22 +168,22 @@ void os_init(u64 program_memory_size) {
|
||||||
|
|
||||||
heap_init();
|
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");
|
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");
|
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");
|
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");
|
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");
|
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");
|
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");
|
assert(os.crt_memset, "Missing memset in crt");
|
||||||
|
|
||||||
window.title = fixed_string("Unnamed Window");
|
window.title = STR("Unnamed Window");
|
||||||
window.width = 1280;
|
window.width = 1280;
|
||||||
window.height = 720;
|
window.height = 720;
|
||||||
window.x = 0;
|
window.x = 0;
|
||||||
|
@ -300,6 +305,12 @@ bool os_grow_program_memory(u64 new_size) {
|
||||||
// Thread primitive
|
// Thread primitive
|
||||||
|
|
||||||
DWORD WINAPI win32_thread_invoker(LPVOID param) {
|
DWORD WINAPI win32_thread_invoker(LPVOID param) {
|
||||||
|
|
||||||
|
timeBeginPeriod(1);
|
||||||
|
#if CONFIGURATION == RELEASE
|
||||||
|
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
||||||
|
#endif
|
||||||
|
|
||||||
Thread *t = (Thread*)param;
|
Thread *t = (Thread*)param;
|
||||||
temporary_storage_init();
|
temporary_storage_init();
|
||||||
context = t->initial_context;
|
context = t->initial_context;
|
||||||
|
@ -447,9 +458,9 @@ void os_high_precision_sleep(f64 ms) {
|
||||||
// Time
|
// Time
|
||||||
///
|
///
|
||||||
|
|
||||||
#include <intrin.h> // #Cdep
|
|
||||||
u64 os_get_current_cycle_count() {
|
u64 os_get_current_cycle_count() {
|
||||||
return __rdtsc();
|
return rdtsc();
|
||||||
}
|
}
|
||||||
|
|
||||||
float64 os_get_current_time_in_seconds() {
|
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 access = GENERIC_READ;
|
||||||
DWORD creation = 0;
|
DWORD creation = 0;
|
||||||
|
|
||||||
|
@ -553,7 +564,7 @@ void os_file_close(File f) {
|
||||||
CloseHandle(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);
|
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
|
||||||
return (bool)DeleteFileW(path_wide);
|
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);
|
return os_file_write_string(f, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os_write_entire_file(string path, string data) {
|
bool os_write_entire_file_s(string path, string data) {
|
||||||
File file = os_file_open(path, O_WRITE | O_CREATE);
|
File file = os_file_open_s(path, O_WRITE | O_CREATE);
|
||||||
if (file == OS_INVALID_FILE) {
|
if (file == OS_INVALID_FILE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -613,8 +624,8 @@ bool os_read_entire_file_handle(File f, string *result, Allocator allocator) {
|
||||||
return actual_read == file_size.QuadPart;
|
return actual_read == file_size.QuadPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os_read_entire_file(string path, string *result, Allocator allocator) {
|
bool os_read_entire_file_s(string path, string *result, Allocator allocator) {
|
||||||
File file = os_file_open(path, O_READ);
|
File file = os_file_open_s(path, O_READ);
|
||||||
if (file == OS_INVALID_FILE) {
|
if (file == OS_INVALID_FILE) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -623,7 +634,7 @@ bool os_read_entire_file(string path, string *result, Allocator allocator) {
|
||||||
return res;
|
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);
|
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
|
||||||
assert(path_wide, "Invalid path string");
|
assert(path_wide, "Invalid path string");
|
||||||
if (path_wide == NULL) {
|
if (path_wide == NULL) {
|
||||||
|
@ -639,7 +650,7 @@ bool os_is_file(string path) {
|
||||||
return !(attributes & FILE_ATTRIBUTE_DIRECTORY);
|
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);
|
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
|
||||||
assert(path_wide, "Invalid path string");
|
assert(path_wide, "Invalid path string");
|
||||||
if (path_wide == NULL) {
|
if (path_wide == NULL) {
|
||||||
|
|
|
@ -199,9 +199,11 @@ typedef enum Os_Io_Open_Flags {
|
||||||
// To append, pass WRITE flag without CREATE flag
|
// To append, pass WRITE flag without CREATE flag
|
||||||
} Os_Io_Open_Flags;
|
} 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);
|
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_string(File f, string s);
|
||||||
bool os_file_write_bytes(File f, void *buffer, u64 size_in_bytes);
|
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_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_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_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_file_s(string path);
|
||||||
bool os_is_directory(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, ...);
|
void fprints(File f, string fmt, ...);
|
||||||
|
|
|
@ -11,3 +11,7 @@ u64 get_random() {
|
||||||
seed_for_random = seed_for_random * MULTIPLIER + INCREMENT;
|
seed_for_random = seed_for_random * MULTIPLIER + INCREMENT;
|
||||||
return seed_for_random;
|
return seed_for_random;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f32 get_random_float32() {
|
||||||
|
return (float32)get_random()/(float32)UINT64_MAX;
|
||||||
|
}
|
|
@ -13,11 +13,7 @@ typedef struct string {
|
||||||
u8 *data;
|
u8 *data;
|
||||||
} string;
|
} string;
|
||||||
|
|
||||||
// Not sure what to call this lol
|
#define STR(s) ((string){ length_of_null_terminated_string((const char*)s), (u8*)s })
|
||||||
#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 })
|
|
||||||
|
|
||||||
inline u64 length_of_null_terminated_string(const char* cstring) {
|
inline u64 length_of_null_terminated_string(const char* cstring) {
|
||||||
u64 len = 0;
|
u64 len = 0;
|
||||||
|
@ -103,3 +99,5 @@ s64 string_find_from_right(string s, string sub) {
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -83,24 +83,24 @@ u64 format_string_to_buffer(char* buffer, u64 count, const char* fmt, va_list ar
|
||||||
|
|
||||||
return bufp - buffer;
|
return bufp - buffer;
|
||||||
}
|
}
|
||||||
string sprint_null_terminated_string_va_list_to_buffer(const char *fmt, va_list args, void* buffer, u64 count) {
|
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, count, fmt, args);
|
u64 formatted_length = format_string_to_buffer((char*)buffer, buffer_size, fmt, args);
|
||||||
|
|
||||||
string result;
|
string result;
|
||||||
result.data = (u8*)buffer;
|
result.data = (u8*)buffer;
|
||||||
|
|
||||||
if (formatted_length >= 0 && formatted_length < count) {
|
if (formatted_length >= 0 && formatted_length < buffer_size) {
|
||||||
result.count = formatted_length;
|
result.count = formatted_length;
|
||||||
} else {
|
} else {
|
||||||
result.count = count - 1;
|
result.count = buffer_size - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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);
|
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) {
|
string sprint_va_list(Allocator allocator, const string fmt, va_list args) {
|
||||||
|
@ -231,3 +231,94 @@ void default_logger(Log_Level level, string s) {
|
||||||
case LOG_ERROR: print("[ERROR]: %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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
|
|
||||||
void log_heap() {
|
void log_heap() {
|
||||||
os_spinlock_lock(heap_lock);
|
os_spinlock_lock(heap_lock);
|
||||||
printf("\nHEAP:\n");
|
print("\nHEAP:\n");
|
||||||
|
|
||||||
Heap_Block *block = heap_head;
|
Heap_Block *block = heap_head;
|
||||||
|
|
||||||
while (block != 0) {
|
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;
|
Heap_Free_Node *node = block->free_head;
|
||||||
|
|
||||||
|
@ -15,14 +15,14 @@ void log_heap() {
|
||||||
|
|
||||||
while (node != 0) {
|
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;
|
total_free += node->size;
|
||||||
|
|
||||||
node = node->next;
|
node = node->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("\t TOTAL FREE: %llu\n\n", total_free);
|
print("\t TOTAL FREE: %llu\n\n", total_free);
|
||||||
|
|
||||||
block = block->next;
|
block = block->next;
|
||||||
}
|
}
|
||||||
|
@ -179,15 +179,15 @@ void test_allocator(bool do_log_heap) {
|
||||||
|
|
||||||
void test_thread_proc1(Thread* t) {
|
void test_thread_proc1(Thread* t) {
|
||||||
os_sleep(5);
|
os_sleep(5);
|
||||||
printf("Hello from thread %llu\n", t->id);
|
print("Hello from thread %llu\n", t->id);
|
||||||
os_sleep(5);
|
os_sleep(5);
|
||||||
printf("Hello from thread %llu\n", t->id);
|
print("Hello from thread %llu\n", t->id);
|
||||||
os_sleep(5);
|
os_sleep(5);
|
||||||
printf("Hello from thread %llu\n", t->id);
|
print("Hello from thread %llu\n", t->id);
|
||||||
os_sleep(5);
|
os_sleep(5);
|
||||||
printf("Hello from thread %llu\n", t->id);
|
print("Hello from thread %llu\n", t->id);
|
||||||
os_sleep(5);
|
os_sleep(5);
|
||||||
printf("Hello from thread %llu\n", t->id);
|
print("Hello from thread %llu\n", t->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_threads() {
|
void test_threads() {
|
||||||
|
@ -195,9 +195,9 @@ void test_threads() {
|
||||||
Thread* t = os_make_thread(test_thread_proc1, get_heap_allocator());
|
Thread* t = os_make_thread(test_thread_proc1, get_heap_allocator());
|
||||||
os_start_thread(t);
|
os_start_thread(t);
|
||||||
os_sleep(20);
|
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);
|
os_join_thread(t);
|
||||||
printf("Thread is joined\n");
|
print("Thread is joined\n");
|
||||||
|
|
||||||
Mutex_Handle m = os_make_mutex();
|
Mutex_Handle m = os_make_mutex();
|
||||||
os_lock_mutex(m);
|
os_lock_mutex(m);
|
||||||
|
@ -238,11 +238,12 @@ void test_allocator_threaded(Thread *t) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_strings() {
|
void test_strings() {
|
||||||
|
Allocator heap = get_heap_allocator();
|
||||||
|
{
|
||||||
// Test length_of_null_terminated_string
|
// 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("Test") == 4, "Failed: length_of_null_terminated_string");
|
||||||
assert(length_of_null_terminated_string("") == 0, "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
|
// Test alloc_string and dealloc_string
|
||||||
string alloc_str = alloc_string(heap, 10);
|
string alloc_str = alloc_string(heap, 10);
|
||||||
|
@ -251,8 +252,8 @@ void test_strings() {
|
||||||
dealloc_string(heap, alloc_str);
|
dealloc_string(heap, alloc_str);
|
||||||
|
|
||||||
// Test string_concat
|
// Test string_concat
|
||||||
string str1 = fixed_string("Hello, ");
|
string str1 = STR("Hello, ");
|
||||||
string str2 = fixed_string("World!");
|
string str2 = STR("World!");
|
||||||
string concat_str = string_concat(str1, str2, heap);
|
string concat_str = string_concat(str1, str2, heap);
|
||||||
assert(concat_str.count == str1.count + str2.count, "Failed: string_concat");
|
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");
|
assert(memcmp(concat_str.data, "Hello, World!", concat_str.count) == 0, "Failed: string_concat");
|
||||||
|
@ -268,7 +269,7 @@ void test_strings() {
|
||||||
assert(strcmp(cstr, "World!") == 0, "Failed: temp_convert_to_null_terminated_string");
|
assert(strcmp(cstr, "World!") == 0, "Failed: temp_convert_to_null_terminated_string");
|
||||||
|
|
||||||
// Test sprint
|
// Test sprint
|
||||||
string format_str = fixed_string("Number: %d");
|
string format_str = STR("Number: %d");
|
||||||
string formatted_str = sprint(heap, format_str, 42);
|
string formatted_str = sprint(heap, format_str, 42);
|
||||||
char* formatted_cstr = convert_to_null_terminated_string(formatted_str, heap);
|
char* formatted_cstr = convert_to_null_terminated_string(formatted_str, heap);
|
||||||
assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint");
|
assert(strcmp(formatted_cstr, "Number: 42") == 0, "Failed: sprint");
|
||||||
|
@ -281,36 +282,36 @@ void test_strings() {
|
||||||
assert(strcmp(formatted_cstr, "Number: 100") == 0, "Failed: tprint");
|
assert(strcmp(formatted_cstr, "Number: 100") == 0, "Failed: tprint");
|
||||||
|
|
||||||
// Test print and printf (visual inspection)
|
// Test print and printf (visual inspection)
|
||||||
printf("Expected output: Hello, World!\n");
|
print("Expected output: Hello, World!\n");
|
||||||
print("Hello, %s!\n", fixed_string("World"));
|
print("Hello, %s!\n", STR("World"));
|
||||||
|
|
||||||
printf("Expected output: Number: 1234\n");
|
print("Expected output: Number: 1234\n");
|
||||||
print(fixed_string("Number: %d\n"), 1234);
|
print("Number: %d\n", 1234);
|
||||||
|
|
||||||
printf("Expected output: Number: 1234\n");
|
print("Expected output: Number: 1234\n");
|
||||||
print(fixed_string("Number: %d\n"), 1234);
|
print("Number: %d\n", 1234);
|
||||||
|
|
||||||
printf("Expected output: Mixed values: 42 and 3.14\n");
|
print("Expected output: Mixed values: 42 and 3.14\n");
|
||||||
print(fixed_string("Mixed values: %d and %.2f\n"), 42, 3.14);
|
print("Mixed values: %d and %.2f\n", 42, 3.14);
|
||||||
|
|
||||||
// This should fail assert and print descriptive error
|
// This should fail assert and print descriptive error
|
||||||
//printf("Expected output (printf): Hello, World!\n");
|
//print("Expected output (printf): Hello, World!\n");
|
||||||
//printf("Hello, %cs!\n", 5);
|
//print("Hello, %cs!\n", 5);
|
||||||
// This should fail assert and print descriptive error
|
// This should fail assert and print descriptive error
|
||||||
// printf("Expected output (printf): Hello, World!\n");
|
// print("Expected output (printf): Hello, World!\n");
|
||||||
// printf("Hello, %s!\n", "World");
|
// print("Hello, %s!\n", "World");
|
||||||
|
|
||||||
printf("Expected output (printf): Hello, World!\n");
|
print("Expected output (printf): Hello, World!\n");
|
||||||
printf("Hello, %s!\n", cstr("World"));
|
print("Hello, %s!\n", STR("World"));
|
||||||
|
|
||||||
printf("Expected output (printf): Number: 5678\n");
|
print("Expected output (printf): Number: 5678\n");
|
||||||
printf("Number: %d\n", 5678);
|
print("Number: %d\n", 5678);
|
||||||
|
|
||||||
printf("Expected output (printf): Mixed values: 99 and 2.71\n");
|
print("Expected output (printf): Mixed values: 99 and 2.71\n");
|
||||||
printf("Mixed values: %d and %.2f\n", 99, 2.71);
|
print("Mixed values: %d and %.2f\n", 99, 2.71);
|
||||||
|
|
||||||
// Test handling of empty strings
|
// Test handling of empty strings
|
||||||
string empty_str = fixed_string("");
|
string empty_str = STR("");
|
||||||
string concat_empty_str = string_concat(empty_str, empty_str, heap);
|
string concat_empty_str = string_concat(empty_str, empty_str, heap);
|
||||||
assert(concat_empty_str.count == 0, "Failed: string_concat with empty strings");
|
assert(concat_empty_str.count == 0, "Failed: string_concat with empty strings");
|
||||||
dealloc_string(heap, concat_empty_str);
|
dealloc_string(heap, concat_empty_str);
|
||||||
|
@ -325,24 +326,108 @@ void test_strings() {
|
||||||
dealloc_string(heap, large_concat_str);
|
dealloc_string(heap, large_concat_str);
|
||||||
|
|
||||||
// Test string with special characters
|
// Test string with special characters
|
||||||
string special_char_str = fixed_string("Special chars: \n\t\r");
|
string special_char_str = STR("Special chars: \n\t\r");
|
||||||
cstr = convert_to_null_terminated_string(special_char_str, heap);
|
cstr = convert_to_null_terminated_string(special_char_str, heap);
|
||||||
assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string");
|
assert(strcmp(cstr, "Special chars: \n\t\r") == 0, "Failed: special character string");
|
||||||
dealloc(heap, cstr);
|
dealloc(heap, cstr);
|
||||||
|
|
||||||
string a = tprintf("Hello, %cs!\n", "balls");
|
string a = tprint("Hello, %cs!\n", "balls");
|
||||||
string balls1 = string_view(a, 7, 5);
|
string balls1 = string_view(a, 7, 5);
|
||||||
string balls2 = fixed_string("balls");
|
string balls2 = STR("balls");
|
||||||
|
|
||||||
assert(strings_match(balls1, balls2), "String match failed");
|
assert(strings_match(balls1, balls2), "String match failed");
|
||||||
assert(!strings_match(balls1, a), "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");
|
||||||
|
|
||||||
|
// Test string_builder_reserve
|
||||||
|
string_builder_reserve(&builder, 256);
|
||||||
|
assert(builder.buffer_capacity >= 256, "Failed: string_builder_reserve");
|
||||||
|
|
||||||
|
// 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 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");
|
||||||
|
|
||||||
|
// 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() {
|
void test_file_io() {
|
||||||
|
|
||||||
#if TARGET_OS == WINDOWS
|
#if TARGET_OS == WINDOWS
|
||||||
// Test win32_fixed_utf8_to_null_terminated_wide
|
// 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());
|
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 != NULL, "Failed: win32_fixed_utf8_to_null_terminated_wide");
|
||||||
assert(wide_str[4] == 0, "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);
|
os_file_close(file);
|
||||||
// Test os_file_open and os_file_close
|
// 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)");
|
assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)");
|
||||||
os_file_close(file);
|
os_file_close(file);
|
||||||
|
|
||||||
|
|
||||||
// Test os_file_write_string and os_file_read
|
// Test os_file_write_string and os_file_read
|
||||||
string hello_world_write = fixed_string("Hello, World!");
|
string hello_world_write = STR("Hello, World!");
|
||||||
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)");
|
assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)");
|
||||||
bool write_result = os_file_write_string(file, hello_world_write);
|
bool write_result = os_file_write_string(file, hello_world_write);
|
||||||
assert(write_result, "Failed: os_file_write_string");
|
assert(write_result, "Failed: os_file_write_string");
|
||||||
os_file_close(file);
|
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)");
|
assert(file != OS_INVALID_FILE, "Failed: os_file_open (read)");
|
||||||
string hello_world_read = talloc_string(hello_world_write.count);
|
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);
|
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);
|
os_file_close(file);
|
||||||
|
|
||||||
// Test os_file_write_bytes
|
// 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)");
|
assert(file != OS_INVALID_FILE, "Failed: os_file_open (write/create)");
|
||||||
int int_data = 42;
|
int int_data = 42;
|
||||||
write_result = os_file_write_bytes(file, &int_data, sizeof(int));
|
write_result = os_file_write_bytes(file, &int_data, sizeof(int));
|
||||||
|
@ -388,28 +473,28 @@ void test_file_io() {
|
||||||
os_file_close(file);
|
os_file_close(file);
|
||||||
|
|
||||||
// Test os_read_entire_file and os_write_entire_file
|
// Test os_read_entire_file and os_write_entire_file
|
||||||
string write_data = fixed_string("Entire file test");
|
string write_data = STR("Entire file test");
|
||||||
bool write_entire_result = os_write_entire_file(fixed_string("entire_test.txt"), write_data);
|
bool write_entire_result = os_write_entire_file("entire_test.txt", write_data);
|
||||||
assert(write_entire_result, "Failed: os_write_entire_file");
|
assert(write_entire_result, "Failed: os_write_entire_file");
|
||||||
|
|
||||||
Allocator heap = get_heap_allocator();
|
Allocator heap = get_heap_allocator();
|
||||||
|
|
||||||
string read_data;
|
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(read_entire_result, "Failed: os_read_entire_file");
|
||||||
assert(strings_match(read_data, write_data), "Failed: os_read_entire_file write/read mismatch");
|
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)");
|
assert(memcmp(read_data.data, write_data.data, write_data.count) == 0, "Failed: os_read_entire_file (content mismatch)");
|
||||||
dealloc(heap, read_data.data);
|
dealloc(heap, read_data.data);
|
||||||
|
|
||||||
// Test fprint
|
// 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");
|
assert(balls != OS_INVALID_FILE, "Failed: Could not create balls.txt");
|
||||||
fprint(balls, "Hello, %cs!", "Balls");
|
fprint(balls, "Hello, %cs!", "Balls");
|
||||||
os_file_close(balls);
|
os_file_close(balls);
|
||||||
string hello_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(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];
|
u64 integers[4096];
|
||||||
for (u64 i = 0; i < 4096; i++) {
|
for (u64 i = 0; i < 4096; i++) {
|
||||||
|
@ -418,11 +503,11 @@ void test_file_io() {
|
||||||
string integers_data;
|
string integers_data;
|
||||||
integers_data.data = (u8*)integers;
|
integers_data.data = (u8*)integers;
|
||||||
integers_data.count = 4096*sizeof(u64);
|
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");
|
assert(ok, "write integers fail");
|
||||||
|
|
||||||
string integers_read;
|
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");
|
assert(ok, "read integers fail");
|
||||||
u64 *new_integers = (u64*)integers_data.data;
|
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);
|
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
|
// Clean up test files
|
||||||
bool delete_ok = false;
|
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");
|
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");
|
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");
|
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");
|
assert(delete_ok, "Failed: could not delete balls.txt");
|
||||||
delete_ok = os_file_delete(fixed_string("integers.txt"));
|
delete_ok = os_file_delete("integers");
|
||||||
assert(delete_ok, "Failed: could not delete integers.txt");
|
assert(delete_ok, "Failed: could not delete integers");
|
||||||
}
|
}
|
||||||
|
|
||||||
void oogabooga_run_tests() {
|
void oogabooga_run_tests() {
|
||||||
printf("Testing allocator... ");
|
print("Testing allocator... ");
|
||||||
test_allocator(true);
|
test_allocator(true);
|
||||||
printf("OK!\n");
|
print("OK!\n");
|
||||||
|
|
||||||
printf("Testing threads... ");
|
print("Testing threads... ");
|
||||||
test_threads();
|
test_threads();
|
||||||
printf("OK!\n");
|
print("OK!\n");
|
||||||
|
|
||||||
printf("Testing strings... ");
|
print("Testing strings... ");
|
||||||
test_strings();
|
test_strings();
|
||||||
printf("OK!\n");
|
print("OK!\n");
|
||||||
|
|
||||||
printf("Thread bombing allocator... ");
|
print("Thread bombing allocator... ");
|
||||||
Thread* threads[100];
|
Thread* threads[100];
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
threads[i] = os_make_thread(test_allocator_threaded, get_heap_allocator());
|
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++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
os_join_thread(threads[i]);
|
os_join_thread(threads[i]);
|
||||||
}
|
}
|
||||||
printf("OK!\n");
|
print("OK!\n");
|
||||||
|
|
||||||
printf("Testing file IO... ");
|
print("Testing file IO... ");
|
||||||
test_file_io();
|
test_file_io();
|
||||||
printf("OK!\n");
|
print("OK!\n");
|
||||||
}
|
}
|
Reference in a new issue