diff --git a/lib/Base/Arena_Table.cpp b/lib/Base/Arena_Table.cpp index 9d53af1..7e4b78f 100644 --- a/lib/Base/Arena_Table.cpp +++ b/lib/Base/Arena_Table.cpp @@ -3,8 +3,6 @@ // There should be some parameters regarding what the upper limit for idle // committed pages should be and a heuristic for maximum number of arenas waiting -// API in Arena.h - global Arena_Table* arena_table; // Only call once from main thread! @@ -65,15 +63,13 @@ void release_arena (Arena* arena, bool delete_extra_pages) { array_add(arena_table->free_table[reserve_index], arena); arena_table->in_flight_count[reserve_index] -= 1; - /* // #TODO #garbage_collection - if (arena_free_table[reserve_index].count > 64) { - // s64 arenas_to_delete_count = arena_free_table[reserve_index].count - 64. - // while (arenas_to_delete_count > 0) { - // arena_delete(arena_free_table[arena_free_table.count-1]); - // array_unordered_remove_by_index(..); - // arenas_to_delete_count -= 1; - // } - } - */ + // if (arena_free_table[reserve_index].count > 64) { + // s64 arenas_to_delete_count = arena_free_table[reserve_index].count - 64; + // while (arenas_to_delete_count > 0) { + // arena_delete(arena_free_table[arena_free_table.count-1]); + // array_unordered_remove_by_index(..); + // arenas_to_delete_count -= 1; + // } + // } } diff --git a/lib/Base/Base_Thread_Context.cpp b/lib/Base/Base_Thread_Context.cpp index 432ac36..f693a13 100644 --- a/lib/Base/Base_Thread_Context.cpp +++ b/lib/Base/Base_Thread_Context.cpp @@ -1,6 +1,7 @@ // See Context_Base in jai, and TCTX in raddebugger: internal void Bootstrap_Main_Thread_Context () { + // Timed_Block_Print_No_Context("Bootstrap_Main_Thread_Context"); // 0. Setup general allocator GPAllocator_Initialize_Allocation_Tracker(); @@ -10,6 +11,7 @@ internal void Bootstrap_Main_Thread_Context () { initialize_arena_table(GPAllocator()); // 2. Setup thread local context + // #NewContext ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16); thread_local_context = New(get_allocator(arena_ex)); @@ -75,7 +77,7 @@ void temp_reset_keeping_memory() { arena_reset(context->temp, false); } -void temp_reset() { +void temp_reset() { // alias: reset_temporary_storage. Thread_Context* context = get_thread_context(); arena_reset(context->temp, true); } \ No newline at end of file diff --git a/lib/Base/Base_Thread_Context.h b/lib/Base/Base_Thread_Context.h index e402075..f2e46d7 100644 --- a/lib/Base/Base_Thread_Context.h +++ b/lib/Base/Base_Thread_Context.h @@ -23,6 +23,8 @@ thread_static Thread_Context* thread_local_context; Thread_Context* get_thread_context (); +// #TODO #NewContext void create_thread_context (Thread_Context** context, string thread_name, bool is_main_thread); + internal void Bootstrap_Main_Thread_Context (); struct Push_Allocator { diff --git a/lib/Base/String.cpp b/lib/Base/String.cpp index 9b90cc9..7b2e8be 100644 --- a/lib/Base/String.cpp +++ b/lib/Base/String.cpp @@ -35,7 +35,7 @@ string copy_string (string s) { } string copy_string (char* c_string) { - string str = {0}; + string str = {}; s64 string_length = strlen(c_string); if (string_length == 0) return ""; @@ -72,11 +72,6 @@ force_inline string string_view (string s, s64 start_index, s64 view_count) { return { new_count, s.data + start_index }; } -string copy_string_view (string s, s64 start_index, s64 view_count) { - // maybe redundant... - return copy_string(string_view(s, start_index, view_count)); -} - bool strings_match (string first_string, string second_string) { return (first_string == second_string); } @@ -109,10 +104,33 @@ string wide_to_utf8 (u16* source, s32 length) { return utf8_string; } +wstring utf8_to_wide (string source) { + if (!source) return {}; + s32 query_num_chars = MultiByteToWideChar(CP_UTF8, 0, + (LPCCH)source.data, (s32)source.count, // @Robustness: Silent failure if too long. @Cleanup. + nullptr, 0); + if (query_num_chars <= 0) return {}; + + wstring name_u16s = wstring(query_num_chars); + s32 result_num_chars = MultiByteToWideChar(CP_UTF8, 0, + (LPCCH)source.data, (s32)source.count, // @Robustness: Silent failure if too long. @Cleanup. + (LPWSTR)name_u16s.data, query_num_chars); + + if (!result_num_chars) { + internal_free(name_u16s.data); + return {}; + } + + Assert(result_num_chars <= query_num_chars); + name_u16s.data[result_num_chars] = 0; // null terminate + + return name_u16s; +} + string format_string (char* format, ...) { constexpr s64 BUFFER_SIZE = 4096; - string str = {0}; + string str = {}; str.data = NewArray(BUFFER_SIZE); @@ -125,6 +143,23 @@ string format_string (char* format, ...) { return str; } +// #MemoryLeak #NoContext - memory will leak from this operation. +string format_string_no_context (char* format, ...) { + constexpr s64 BUFFER_SIZE = 4096; + + string str = {}; + + str.data = (u8*)GPAllocator_New(BUFFER_SIZE); + + va_list args; + va_start(args, format); + // Note that this *is* null-terminated for compatibility. + str.count = (s64)vsnprintf((char*)str.data, (size_t)BUFFER_SIZE, format, args); + va_end(args); + + return str; +} + force_inline String_Builder* new_string_builder (Arena_Reserve new_reserve) { return arena_array_new(1, new_reserve); } diff --git a/lib/Base/String.h b/lib/Base/String.h index 2f852d6..6a694d0 100644 --- a/lib/Base/String.h +++ b/lib/Base/String.h @@ -40,6 +40,11 @@ struct string { return true; } + + bool operator ! () { + Assert(count >= 0); + return (data == nullptr || count == 0); + } }; struct wstring { @@ -50,6 +55,17 @@ struct wstring { count = 0; data = nullptr; } + + wstring (s32 length) { + data = NewArray(length + 1); + s32 length_bytes = (length + 1) * sizeof(u16); + count = length_bytes; + } + + bool operator ! () { + Assert(count >= 0); + return (data == nullptr || count == 0); + } }; // ~Keep these API @@ -63,23 +79,28 @@ void free(string& s); // String manipulation & comparison force_inline string string_view (string s, s64 start_index, s64 view_count); -string copy_string_view (string s, s64 start_index, s64 view_count); bool strings_match (string first_string, string second_string); // #Unicode +// #TODO: Make a raw version that returns the raw pointer? string wide_to_utf8 (u16* source, s32 length); -// wstring utf8_to_wide (string source); TODO. +wstring utf8_to_wide (string source); string format_string (char* format, ...); +string format_string_no_context (char* format, ...); -// Parsing stuff: +// #TODO #Parsing stuff: // is_white_space(char: u8) // advance // eat_spaces -// Print stuff +// string to type or type to string conversions // s64 string_to_int (string v, s32 base = 10, s64* remainder=nullptr); -// +// f64 string_to_f64 +// f32 string_to_f32 + +// Need an API for inserting various types (ints, floats, etc.) into a String_Builder, and advancing +// the count. // #string_builder // #limitations This won't be as fast as Jon's String_Builder in jai because we're backing it with an diff --git a/lib/Base/Timing.h b/lib/Base/Timing.h new file mode 100644 index 0000000..8a5671a --- /dev/null +++ b/lib/Base/Timing.h @@ -0,0 +1,130 @@ +// #Timing API: +#define System_Timed_Block_Print(name) \ + system_timed_block_print Concat(_sys_timed_block_print_guard, __LINE__)(name) +#define Timed_Block_Print(name) \ + timed_block_print Concat(_timed_block_print_guard, __LINE__)(name) +// This is for timing stuff that happens where context is not available. +// memory will leak from this operation. #MemoryLeak #NoContext +#define Timed_Block_Print_No_Context(name) \ + timed_block_print_no_context Concat(_timed_block_print_guard, __LINE__)(name) + + +force_inline u64 rdtsc(); + +#if OS_WINDOWS +#include + +#pragma intrinsic(__rdtsc) + +force_inline u64 rdtsc() { + return __rdtsc(); +} +#endif + +global u32 g_cpu_base_frequency_megahertz; + +void set_cpu_base_frequency (u32 frequency_megahertz) { + g_cpu_base_frequency_megahertz = frequency_megahertz; +} + +string format_time_seconds_no_context (f64 time) { // #MemoryLeak #NoContext + if (time < 1.0e-6) { return format_string_no_context("%1.2f ns", time * 1.0e9); } + if (time < 1.0e-3) { return format_string_no_context("%1.3f us", time * 1.0e6); } + if (time < 1) { return format_string_no_context("%1.3f ms", time * 1.0e3); } + + return format_string_no_context("%1.3f s", time); +} + +string format_cycles_no_context (u64 ticks) { // #MemoryLeak #NoContext + string units[5] = { "cycles", "K cycles", "M cycles", "B cycles", "T cycles" }; + + f64 count_f64 = (f64)ticks; + s64 unit_index = 0; + while (count_f64 >= 1000 && unit_index < 4) { // 4 is from (units.count-1) + count_f64 /= 1000.0; + unit_index += 1; + } + + return format_string_no_context("%1.2f %s", count_f64, units[unit_index].data); +} + +string format_time_seconds (f64 time) { + if (time < 1.0e-6) { return format_string("%1.2f ns", time * 1.0e9); } + if (time < 1.0e-3) { return format_string("%1.3f us", time * 1.0e6); } + if (time < 1) { return format_string("%1.3f ms", time * 1.0e3); } + + return format_string("%1.3f s", time); +} + +string format_cycles (u64 ticks) { + string units[5] = { "cycles", "K cycles", "M cycles", "B cycles", "T cycles" }; + + f64 count_f64 = (f64)ticks; + s64 unit_index = 0; + while (count_f64 >= 1000 && unit_index < 4) { // 4 is from (units.count-1) + count_f64 /= 1000.0; + unit_index += 1; + } + + return format_string("%1.2f %s", count_f64, units[unit_index].data); +} + +struct timed_block_print { + string block_name; + u64 start_tick; + + timed_block_print(string _block_name) { + Assert(g_cpu_base_frequency_megahertz != 0); + Assert(thread_local_context != nullptr); // we need temp allocator initialized! + block_name = _block_name; + start_tick = rdtsc(); + } + + ~timed_block_print() { + u64 end_tick = rdtsc(); + u64 tick_difference = end_tick - start_tick; + f64 ticks_f64 = (f64)tick_difference; + f64 elapsed_time_seconds = ticks_f64 / (f64)((s64)g_cpu_base_frequency_megahertz * 1000000); + push_allocator(get_temp_allocator()); + log("[Timed_Block %s]: %s (%s)", block_name.data, format_time_seconds(elapsed_time_seconds).data, format_cycles(tick_difference).data); + } +}; + +struct timed_block_print_no_context { + string block_name; + u64 start_tick; + + timed_block_print_no_context(string _block_name) { + Assert(g_cpu_base_frequency_megahertz != 0); + block_name = _block_name; + start_tick = rdtsc(); + } + + ~timed_block_print_no_context() { + u64 end_tick = rdtsc(); + u64 tick_difference = end_tick - start_tick; + f64 ticks_f64 = (f64)tick_difference; + f64 elapsed_time_seconds = ticks_f64 / (f64)((s64)g_cpu_base_frequency_megahertz * 1000000); + printf("[Timed_Block(No Context) %s]: %s (%s)\n", + block_name.data, + format_time_seconds_no_context(elapsed_time_seconds).data, + format_cycles_no_context(tick_difference).data); + } +}; + +struct system_timed_block_print { + string block_name; + f64 start_time; + + system_timed_block_print(string _block_name) { + block_name = _block_name; + start_time = GetUnixTimestamp(); + } + + ~system_timed_block_print() { + f64 end_time = GetUnixTimestamp(); + f64 elapsed_time_seconds = end_time - start_time; + push_allocator(get_temp_allocator()); + log("[Timed_Block %s]: %s", block_name.data, format_time_seconds(elapsed_time_seconds).data); + } +}; diff --git a/lib/OS/OS_Win32.cpp b/lib/OS/OS_Win32.cpp index 761e6a6..c84eece 100644 --- a/lib/OS/OS_Win32.cpp +++ b/lib/OS/OS_Win32.cpp @@ -74,6 +74,7 @@ internal LONG WINAPI Win32_Exception_Filter (EXCEPTION_POINTERS* exception_ptrs) // internal void Main_Entry_Point (int argc, WCHAR **argv); internal void Win32_Entry_Point (int argc, WCHAR **argv) { + // Timed_Block_Print("Win32_Entry_Point"); // See: w32_entry_point_caller(); (raddebugger) SetUnhandledExceptionFilter(&Win32_Exception_Filter); @@ -173,7 +174,7 @@ internal void Win32_Entry_Point (int argc, WCHAR **argv) { // [ ] Get Working directory (info->working_path) // [ ] GetEnvironmentStringsW - // temp_reset(); + temp_reset(); printf("Hello there!\n\n"); } @@ -201,7 +202,7 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name= } s64 this_thread_index = InterlockedIncrement(&next_thread_index); - + // #NewContext ExpandableArena* arena_ex = expandable_arena_new(Arena_Reserve::Size_64M, 16); thread->context = New(get_allocator(arena_ex)); @@ -209,8 +210,8 @@ internal bool thread_init (Thread* thread, Thread_Proc proc, string thread_name= thread->context->arena = arena_ex; thread->context->allocator = get_allocator(arena_ex); thread->context->thread_idx = (s32)this_thread_index; - push_arena(arena_ex); thread->context->thread_name = copy_string(thread_name); + thread->context->log_builder = new_string_builder(Arena_Reserve::Size_64M); thread->os_thread.windows_thread = windows_thread; thread->os_thread.windows_thread_id = windows_thread_id; @@ -229,6 +230,9 @@ internal void thread_deinit (Thread* thread) { arena_delete(thread->context->temp); arena_delete(thread->context->arena); + free_string_builder(thread->context->log_builder); + + // memset(thread, 0xCD, sizeof(Thread); } internal void thread_start (Thread* thread) { diff --git a/lib_main.cpp b/lib_main.cpp index f6b8f7d..ba8f876 100644 --- a/lib_main.cpp +++ b/lib_main.cpp @@ -23,8 +23,9 @@ #include "lib/Base/String.cpp" #include "lib/Base/ErrorType.cpp" -#include "lib/Base/Arena_Table.cpp" #include "lib/Base/Base_Thread_Context.h" +#include "lib/Base/Timing.h" +#include "lib/Base/Arena_Table.cpp" #include "lib/Base/Expandable_Arena.h" #include "lib/Base/Arena.cpp" diff --git a/src/Base_Entry_Point.cpp b/src/Base_Entry_Point.cpp index c6135cf..75a2d78 100644 --- a/src/Base_Entry_Point.cpp +++ b/src/Base_Entry_Point.cpp @@ -184,40 +184,52 @@ void ImGui_Application () { string string_literal_example = "Hello, I am a string literal."; internal void Main_Entry_Point (int argc, WCHAR **argv) { + set_cpu_base_frequency(3200); // REQUIRED FOR TIMING MODULE! + // #NOTE: Be careful using a timing or auto-release macros + // before setting up the thread context! Bootstrap_Main_Thread_Context(); +#if OS_WINDOWS Win32_Entry_Point(argc, argv); // This might be the problem. +#endif +#if OS_LINUX + // Linux_Entry_Point(argc, argv); +#endif // See: main_thread_base_entry_point - temp_reset(); - push_allocator(get_temp_allocator()); - // tip: use auto_reset or auto_release with `get_thread_context()->arena` - + { Timed_Block_Print("string_builder_testing"); + temp_reset(); + push_allocator(get_temp_allocator()); + // tip: use auto_reset or auto_release with `get_thread_context()->arena` + + // String builder example: + // OK. I can work with this. + + auto sb = new_string_builder(Arena_Reserve::Size_64K); + append(sb, "string_literal_example"); + append(sb, " "); + print_to_builder(sb, "There are %d cats in the %s", 64, "house.\n"); + append(sb, " > "); + print_to_builder(sb, "some size_t: %u", (u64)3982739867); + append(sb, "\n"); + print_to_builder(sb, "the literal: %s", string_literal_example.data); + + // string result = string_view(sb); + string result = builder_to_string(sb); // also frees + // print((char*)result.data); + log("Hello."); + log("log() should automatically append newlines to these prints."); + log("If it doesn't, I will be very upset."); + log(result); + log("Hello. There are %s things here\n", string_literal_example.data); + + print("Hello, I am just a printed message to stdout\n\n"); + } + { string s = "hello I am cool"; + wstring sw = utf8_to_wide(s); + printf("testing operator overload: %d\n", !s); + printf("testing utf8_to_wide: %ls\n", sw.data); + } // debug_break(); - // String builder example: - // OK. I can work with this. - - auto sb = new_string_builder(Arena_Reserve::Size_64K); - append(sb, "string_literal_example"); - append(sb, " "); - print_to_builder(sb, "There are %d cats in the %s", 64, "house.\n"); - append(sb, " > "); - print_to_builder(sb, "some size_t: %u", (u64)3982739867); - append(sb, "\n"); - print_to_builder(sb, "the literal: %s", string_literal_example.data); - - // string result = string_view(sb); - string result = builder_to_string(sb); // also frees - // print((char*)result.data); - log("Hello."); - log("log() should automatically append newlines to these prints."); - log("If it doesn't, I will be very upset."); - log(result); - log("Hello. There are %s things here\n", string_literal_example.data); - - print("Hello, I am just a printed message to stdout\n\n"); - - // free_string_builder(sb); - // ImGui_Application(); // #TODO: #Main - `Main_Entry_Point`