shader codegen and compilation

This commit is contained in:
Anton Ljungdahl 2025-05-27 21:51:42 +02:00
parent 531c86a899
commit 84f6eb4583
69 changed files with 18393 additions and 8680 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
src/scratch.c
build/* build/*
*sublime* *sublime*
*ctm* *ctm*

View File

@ -1,8 +1,9 @@
// raddbg 0.9.17 project file // raddbg 0.9.17 project file
recent_file: path: "src/render/d3d12/render_d3d12.c" recent_file: path: "src/render/d3d12/render_d3d12.c"
recent_file: path: "src/os/win32/os_gfx_win32.c" recent_file: path: "d:/os/obj/amd64fre/minkernel/crts/ucrt/src/appcrt/string/mt/objfre/amd64/minkernel/crts/ucrt/src/appcrt/string/amd64/strlen.asm"
recent_file: path: "src/main.c" recent_file: path: "src/main.c"
recent_file: path: "src/os/win32/os_gfx_win32.c"
target: target:
{ {
executable: "build/program.exe" executable: "build/program.exe"

View File

@ -9,7 +9,7 @@ pushd .\build
ctime -begin timeBuild.ctm ctime -begin timeBuild.ctm
cl /Zi /nologo /wd5287 ../src/metagen/codegen.c cl /Zi /nologo /wd5287 ..\\src\\metagen\\metagen_main.c
set LastError=%ERRORLEVEL% set LastError=%ERRORLEVEL%
ctime -end timeBuild.ctm %LastError% ctime -end timeBuild.ctm %LastError%
@ -22,12 +22,7 @@ IF NOT %LastError%==0 GOTO :end
ctime -begin timeBuild.ctm ctime -begin timeBuild.ctm
pushd src pushd src
pushd gen_test ..\build\metagen_main.exe
..\..\build\codegen.exe ./
popd
pushd os
..\..\build\codegen.exe ./
popd
popd popd
echo Codegen time: echo Codegen time:

View File

@ -11,13 +11,16 @@
#define COMPILER_MSVC 1 #define COMPILER_MSVC 1
#endif #endif
#if COMPILER_MSVC
# pragma section(".rdata$", read)
# define read_only __declspec(allocate(".rdata$"))
#endif
//////////////////////////////// ////////////////////////////////
//~ Asserts //~ Asserts
#if COMPILER_MSVC #if COMPILER_MSVC
# define Trap() __debugbreak() # define Trap() __debugbreak()
#elif COMPILER_CLANG || COMPILER_GCC
# define Trap() __builtin_trap()
#else #else
# error Unknown trap intrinsic for this compiler. # error Unknown trap intrinsic for this compiler.
#endif #endif

View File

@ -10,9 +10,24 @@ typedef union Vec3F32
{ {
F32 x; F32 x;
F32 y; F32 y;
F32 z;
}; };
F32 v[3];
} }
Vec3F32; Vec3F32;
typedef union Vec4F32
{
struct
{
F32 x;
F32 y;
F32 z;
F32 w;
};
F32 v[4];
}
Vec4F32;
#endif /* BASE_MATH_H */ #endif /* BASE_MATH_H */

View File

@ -0,0 +1,2 @@
//- GENERATED CODE

View File

@ -0,0 +1,6 @@
//- GENERATED CODE
#ifndef GEN_TEST_META_H
#define GEN_TEST_META_H
#endif // GEN_TEST_META_H

View File

@ -1,8 +0,0 @@
char * test_value_table[4] =
{
"A",
"B",
"C",
"D",
};

View File

@ -1,12 +0,0 @@
typedef enum TestEnum
{
TestEnum_A,
TestEnum_B,
TestEnum_C,
TestEnum_D,
TestEnum_COUNT
}
TestEnum;
extern char * test_value_table[4];

View File

@ -59,7 +59,7 @@ entry_point()
for(U64 i = 0; i < os_events_received_on_frame(); i += 1) for(U64 i = 0; i < os_events_received_on_frame(); i += 1)
{ {
OS_Event *event = &os_events.list[i]; OS_Event *event = &os_events.list[i];
if(event->kind != OS_EventKind_Null) if(event->kind != OS_EventKind_Null && event->kind != OS_EventKind_MouseMove)
{ {
os_console_log_fmt("%s ", os_key_display_string[event->key]); os_console_log_fmt("%s ", os_key_display_string[event->key]);
if(event->kind == OS_EventKind_Release) if(event->kind == OS_EventKind_Release)

4
src/main.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef MAIN_H
#define MAIN_H
#endif // MAIN_H

1228
src/mdesk/mdesk.c Normal file

File diff suppressed because it is too large Load Diff

346
src/mdesk/mdesk.h Normal file
View File

@ -0,0 +1,346 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef MDESK_H
#define MDESK_H
////////////////////////////////
//~ rjf: Messages
typedef enum MD_MsgKind
{
MD_MsgKind_Null,
MD_MsgKind_Note,
MD_MsgKind_Warning,
MD_MsgKind_Error,
MD_MsgKind_FatalError,
}
MD_MsgKind;
typedef struct MD_Msg MD_Msg;
struct MD_Msg
{
MD_Msg *next;
struct MD_Node *node;
MD_MsgKind kind;
String8 string;
};
typedef struct MD_MsgList MD_MsgList;
struct MD_MsgList
{
MD_Msg *first;
MD_Msg *last;
U64 count;
MD_MsgKind worst_message_kind;
};
////////////////////////////////
//~ rjf: Token Types
typedef U32 MD_TokenFlags;
enum
{
// rjf: base kind info
MD_TokenFlag_Identifier = (1<<0),
MD_TokenFlag_Numeric = (1<<1),
MD_TokenFlag_StringLiteral = (1<<2),
MD_TokenFlag_Symbol = (1<<3),
MD_TokenFlag_Reserved = (1<<4),
MD_TokenFlag_Comment = (1<<5),
MD_TokenFlag_Whitespace = (1<<6),
MD_TokenFlag_Newline = (1<<7),
// rjf: decoration info
MD_TokenFlag_StringSingleQuote = (1<<8),
MD_TokenFlag_StringDoubleQuote = (1<<9),
MD_TokenFlag_StringTick = (1<<10),
MD_TokenFlag_StringTriplet = (1<<11),
// rjf: error info
MD_TokenFlag_BrokenComment = (1<<12),
MD_TokenFlag_BrokenStringLiteral = (1<<13),
MD_TokenFlag_BadCharacter = (1<<14),
};
typedef U32 MD_TokenGroups;
enum
{
MD_TokenGroup_Comment = MD_TokenFlag_Comment,
MD_TokenGroup_Whitespace = (MD_TokenFlag_Whitespace|
MD_TokenFlag_Newline),
MD_TokenGroup_Irregular = (MD_TokenGroup_Comment|
MD_TokenGroup_Whitespace),
MD_TokenGroup_Regular = ~MD_TokenGroup_Irregular,
MD_TokenGroup_Label = (MD_TokenFlag_Identifier|
MD_TokenFlag_Numeric|
MD_TokenFlag_StringLiteral|
MD_TokenFlag_Symbol),
MD_TokenGroup_Error = (MD_TokenFlag_BrokenComment|
MD_TokenFlag_BrokenStringLiteral|
MD_TokenFlag_BadCharacter),
};
typedef struct MD_Token MD_Token;
struct MD_Token
{
Rng1U64 range;
MD_TokenFlags flags;
};
typedef struct MD_TokenChunkNode MD_TokenChunkNode;
struct MD_TokenChunkNode
{
MD_TokenChunkNode *next;
MD_Token *v;
U64 count;
U64 cap;
};
typedef struct MD_TokenChunkList MD_TokenChunkList;
struct MD_TokenChunkList
{
MD_TokenChunkNode *first;
MD_TokenChunkNode *last;
U64 chunk_count;
U64 total_token_count;
};
typedef struct MD_TokenArray MD_TokenArray;
struct MD_TokenArray
{
MD_Token *v;
U64 count;
};
////////////////////////////////
//~ rjf: Node Types
typedef enum MD_NodeKind
{
MD_NodeKind_Nil,
MD_NodeKind_File,
MD_NodeKind_ErrorMarker,
MD_NodeKind_Main,
MD_NodeKind_Tag,
MD_NodeKind_List,
MD_NodeKind_Reference,
MD_NodeKind_COUNT
}
MD_NodeKind;
typedef U32 MD_NodeFlags;
enum
{
MD_NodeFlag_MaskSetDelimiters = (0x3F<<0),
MD_NodeFlag_HasParenLeft = (1<<0),
MD_NodeFlag_HasParenRight = (1<<1),
MD_NodeFlag_HasBracketLeft = (1<<2),
MD_NodeFlag_HasBracketRight = (1<<3),
MD_NodeFlag_HasBraceLeft = (1<<4),
MD_NodeFlag_HasBraceRight = (1<<5),
MD_NodeFlag_MaskSeparators = (0xF<<6),
MD_NodeFlag_IsBeforeSemicolon = (1<<6),
MD_NodeFlag_IsAfterSemicolon = (1<<7),
MD_NodeFlag_IsBeforeComma = (1<<8),
MD_NodeFlag_IsAfterComma = (1<<9),
MD_NodeFlag_MaskStringDelimiters = (0xF<<10),
MD_NodeFlag_StringSingleQuote = (1<<10),
MD_NodeFlag_StringDoubleQuote = (1<<11),
MD_NodeFlag_StringTick = (1<<12),
MD_NodeFlag_StringTriplet = (1<<13),
MD_NodeFlag_MaskLabelKind = (0xF<<14),
MD_NodeFlag_Numeric = (1<<14),
MD_NodeFlag_Identifier = (1<<15),
MD_NodeFlag_StringLiteral = (1<<16),
MD_NodeFlag_Symbol = (1<<17),
};
#define MD_NodeFlag_AfterFromBefore(f) ((f) << 1)
typedef struct MD_Node MD_Node;
struct MD_Node
{
// rjf: tree links
MD_Node *next;
MD_Node *prev;
MD_Node *parent;
MD_Node *first;
MD_Node *last;
// rjf: tag links
MD_Node *first_tag;
MD_Node *last_tag;
// rjf: node info
MD_NodeKind kind;
MD_NodeFlags flags;
String8 string;
String8 raw_string;
// rjf: source code info
U64 src_offset;
// rjf: user-controlled generation number
//
// (unused by mdesk layer, but can be used by usage code to use MD_Node trees
// in a "retained mode" way, where stable generational handles can be formed
// to nodes)
U64 user_gen;
// rjf: extra padding to 128 bytes
U64 _unused_[2];
};
typedef struct MD_NodeRec MD_NodeRec;
struct MD_NodeRec
{
MD_Node *next;
S32 push_count;
S32 pop_count;
};
typedef struct MD_NodePtrNode MD_NodePtrNode;
struct MD_NodePtrNode
{
MD_NodePtrNode *next;
MD_Node *v;
};
typedef struct MD_NodePtrList MD_NodePtrList;
struct MD_NodePtrList
{
MD_NodePtrNode *first;
MD_NodePtrNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Text -> Tokens Types
typedef struct MD_TokenizeResult MD_TokenizeResult;
struct MD_TokenizeResult
{
MD_TokenArray tokens;
MD_MsgList msgs;
};
////////////////////////////////
//~ rjf: Tokens -> Tree Types
typedef struct MD_ParseResult MD_ParseResult;
struct MD_ParseResult
{
MD_Node *root;
MD_MsgList msgs;
};
////////////////////////////////
//~ rjf: Globals
global read_only MD_Node md_nil_node =
{
&md_nil_node,
&md_nil_node,
&md_nil_node,
&md_nil_node,
&md_nil_node,
&md_nil_node,
&md_nil_node,
};
////////////////////////////////
//~ rjf: Message Type Functions
internal void md_msg_list_push(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, String8 string);
internal void md_msg_list_pushf(Arena *arena, MD_MsgList *msgs, MD_Node *node, MD_MsgKind kind, char *fmt, ...);
internal void md_msg_list_concat_in_place(MD_MsgList *dst, MD_MsgList *to_push);
////////////////////////////////
//~ rjf: Token Type Functions
internal MD_Token md_token_make(Rng1U64 range, MD_TokenFlags flags);
internal B32 md_token_match(MD_Token a, MD_Token b);
internal String8List md_string_list_from_token_flags(Arena *arena, MD_TokenFlags flags);
internal void md_token_chunk_list_push(Arena *arena, MD_TokenChunkList *list, U64 cap, MD_Token token);
internal MD_TokenArray md_token_array_from_chunk_list(Arena *arena, MD_TokenChunkList *chunks);
internal String8 md_content_string_from_token_flags_str8(MD_TokenFlags flags, String8 string);
////////////////////////////////
//~ rjf: Node Type Functions
//- rjf: flag conversions
internal MD_NodeFlags md_node_flags_from_token_flags(MD_TokenFlags flags);
//- rjf: nil
internal B32 md_node_is_nil(MD_Node *node);
//- rjf: iteration
#define MD_EachNode(it, first) (MD_Node *it = first; !md_node_is_nil(it); it = it->next)
internal MD_NodeRec md_node_rec_depth_first(MD_Node *node, MD_Node *subtree_root, U64 child_off, U64 sib_off);
#define md_node_rec_depth_first_pre(node, subtree_root) md_node_rec_depth_first((node), (subtree_root), OffsetOf(MD_Node, first), OffsetOf(MD_Node, next))
#define md_node_rec_depth_first_pre_rev(node, subtree_root) md_node_rec_depth_first((node), (subtree_root), OffsetOf(MD_Node, last), OffsetOf(MD_Node, prev))
//- rjf: tree building
internal MD_Node *md_push_node(Arena *arena, MD_NodeKind kind, MD_NodeFlags flags, String8 string, String8 raw_string, U64 src_offset);
internal void md_node_insert_child(MD_Node *parent, MD_Node *prev_child, MD_Node *node);
internal void md_node_insert_tag(MD_Node *parent, MD_Node *prev_child, MD_Node *node);
internal void md_node_push_child(MD_Node *parent, MD_Node *node);
internal void md_node_push_tag(MD_Node *parent, MD_Node *node);
internal void md_unhook(MD_Node *node);
//- rjf: tree introspection
internal MD_Node * md_node_from_chain_string(MD_Node *first, MD_Node *opl, String8 string, StringMatchFlags flags);
internal MD_Node * md_node_from_chain_index(MD_Node *first, MD_Node *opl, U64 index);
internal MD_Node * md_node_from_chain_flags(MD_Node *first, MD_Node *opl, MD_NodeFlags flags);
internal U64 md_index_from_node(MD_Node *node);
internal MD_Node * md_root_from_node(MD_Node *node);
internal MD_Node * md_child_from_string(MD_Node *node, String8 child_string, StringMatchFlags flags);
internal MD_Node * md_tag_from_string(MD_Node *node, String8 tag_string, StringMatchFlags flags);
internal MD_Node * md_child_from_index(MD_Node *node, U64 index);
internal MD_Node * md_tag_from_index(MD_Node *node, U64 index);
internal MD_Node * md_tag_arg_from_index(MD_Node *node, String8 tag_string, StringMatchFlags flags, U64 index);
internal MD_Node * md_tag_arg_from_string(MD_Node *node, String8 tag_string, StringMatchFlags tag_str_flags, String8 arg_string, StringMatchFlags arg_str_flags);
internal B32 md_node_has_child(MD_Node *node, String8 string, StringMatchFlags flags);
internal B32 md_node_has_tag(MD_Node *node, String8 string, StringMatchFlags flags);
internal U64 md_child_count_from_node(MD_Node *node);
internal U64 md_tag_count_from_node(MD_Node *node);
internal String8 md_string_from_children(Arena *arena, MD_Node *root);
//- rjf: tree comparison
internal B32 md_tree_match(MD_Node *a, MD_Node *b, StringMatchFlags flags);
internal B32 md_node_match(MD_Node *a, MD_Node *b, StringMatchFlags flags);
//- rjf: tree duplication
internal MD_Node *md_tree_copy(Arena *arena, MD_Node *src_root);
////////////////////////////////
//~ rjf: Text -> Tokens Functions
internal MD_TokenizeResult md_tokenize_from_text(Arena *arena, String8 text);
////////////////////////////////
//~ rjf: Tokens -> Tree Functions
internal MD_ParseResult md_parse_from_text_tokens(Arena *arena, String8 filename, String8 text, MD_TokenArray tokens);
////////////////////////////////
//~ rjf: Bundled Text -> Tree Functions
internal MD_ParseResult md_parse_from_text(Arena *arena, String8 filename, String8 text);
#define md_tree_from_string(arena, string) (md_parse_from_text((arena), str8_zero(), (string)).root)
////////////////////////////////
//~ rjf: Tree -> Text Functions
internal String8List md_debug_string_list_from_tree(Arena *arena, MD_Node *root);
////////////////////////////////
//~ rjf: Node Pointer List Functions
internal void md_node_ptr_list_push(Arena *arena, MD_NodePtrList *list, MD_Node *node);
internal void md_node_ptr_list_push_front(Arena *arena, MD_NodePtrList *list, MD_Node *node);
#endif // MDESK_H

View File

@ -1,148 +0,0 @@
#include <inttypes.h>
#include "metadesk_base/md.h"
#include "metadesk_base/md.c"
#include "codegen.h"
#include "codegen_table.h"
#include "codegen_table.c"
////////////////////////////////
//~ rjf: Helpers
static CG_FilePair
CG_FilePairFromNode(MD_Node *node)
{
CG_FilePair result = {0};
MD_CodeLoc loc = MD_CodeLocFromNode(node);
MD_String8 filename = loc.filename;
MD_b32 found = 0;
for(int i = 0; i < cg_file_pair_count; i += 1)
{
if(MD_S8Match(filename, cg_file_pairs[i].src_filename, 0))
{
result = cg_file_pairs[i];
found = 1;
break;
}
}
if(found == 0)
{
MD_String8 folder = MD_PathChopLastSlash(filename);
MD_String8 layer_name = MD_PathChopLastPeriod(MD_PathSkipLastSlash(loc.filename));
MD_String8 gen_folder = MD_S8Fmt(cg_arena, "%.*s/generated", MD_S8VArg(folder));
MD_String8 h_filename = MD_S8Fmt(cg_arena, "%.*s/%.*s.meta.h", MD_S8VArg(gen_folder), MD_S8VArg(layer_name));
MD_String8 c_filename = MD_S8Fmt(cg_arena, "%.*s/%.*s.meta.c", MD_S8VArg(gen_folder), MD_S8VArg(layer_name));
result.src_filename = filename;
result.h = fopen((char *)h_filename.str, "w");
result.c = fopen((char *)c_filename.str, "w");
cg_file_pairs[cg_file_pair_count] = result;
cg_file_pair_count += 1;
}
return result;
}
static void
CG_CloseAllFiles(void)
{
for(int i = 0; i < cg_file_pair_count; i += 1)
{
fclose(cg_file_pairs[i].h);
fclose(cg_file_pairs[i].c);
}
}
static MD_String8
CG_EscapedFromString(MD_Arena *arena, MD_String8 string)
{
MD_ArenaTemp scratch = MD_GetScratch(&arena, 1);
MD_String8List strs = {0};
MD_b32 escaped = 0;
MD_u64 start = 0;
for(MD_u64 idx = 0; idx <= string.size; idx += 1)
{
if(idx < string.size && escaped)
{
escaped = 0;
start = idx+1;
MD_u8 replace_char = 0;
switch(string.str[idx])
{
default: break;
case 'a': replace_char = 0x07; break;
case 'b': replace_char = 0x08; break;
case 'e': replace_char = 0x1b; break;
case 'f': replace_char = 0x0c; break;
case 'n': replace_char = 0x0a; break;
case 'r': replace_char = 0x0d; break;
case 't': replace_char = 0x09; break;
case 'v': replace_char = 0x0b; break;
case '\\': replace_char = 0x5c; break;
case '\'': replace_char = 0x27; break;
case '\"': replace_char = 0x22; break;
case '\?': replace_char = 0x3f; break;
}
if(replace_char)
{
MD_String8 string = MD_S8Copy(scratch.arena, MD_S8(&replace_char, 1));
MD_S8ListPush(scratch.arena, &strs, string);
}
}
else if(idx == string.size || string.str[idx] == '\\')
{
escaped = (string.str[idx] == '\\');
MD_String8 part = MD_S8Substring(string, start, idx);
MD_S8ListPush(scratch.arena, &strs, part);
start = idx;
}
}
MD_String8 result = MD_S8ListJoin(arena, strs, 0);
MD_ReleaseScratch(scratch);
return result;
}
////////////////////////////////
//~ rjf: Entry Point
int main(int argument_count, char **arguments)
{
cg_arena = MD_ArenaAlloc();
//- rjf: parse command line
MD_String8List options = MD_StringListFromArgCV(cg_arena, argument_count, arguments);
MD_CmdLine cmdln = MD_MakeCmdLineFromOptions(cg_arena, options);
//- rjf: parse all files
MD_Node *file_list = MD_MakeList(cg_arena);
for(MD_String8Node *n = cmdln.inputs.first; n != 0; n = n->next)
{
MD_String8 code_dir = n->string;
printf("searching %.*s for metacode...\n", MD_S8VArg(code_dir));
MD_FileIter it = {0};
MD_FileIterBegin(&it, code_dir);
for(MD_FileInfo info = {0};;)
{
info = MD_FileIterNext(cg_arena, &it);
if(info.filename.size == 0)
{
break;
}
if(!(info.flags & MD_FileFlag_Directory) &&
MD_S8Match(MD_PathSkipLastPeriod(info.filename), MD_S8Lit("mdesk"), MD_StringMatchFlag_CaseInsensitive))
{
printf("parsing %.*s...\n", MD_S8VArg(info.filename));
MD_String8 path = MD_S8Fmt(cg_arena, "%.*s/%.*s", MD_S8VArg(code_dir), MD_S8VArg(info.filename));
MD_ParseResult parse = MD_ParseWholeFile(cg_arena, path);
MD_PushNewReference(cg_arena, file_list, parse.node);
}
}
MD_FileIterEnd(&it);
}
//- rjf: send all parses to backends
CG_TBL_Generate(file_list);
CG_CloseAllFiles();
return 0;
}

View File

@ -1,26 +0,0 @@
#ifndef METAPROGRAM_H
#define METAPROGRAM_H
typedef struct CG_FilePair CG_FilePair;
struct CG_FilePair
{
MD_String8 src_filename;
FILE *h;
FILE *c;
};
////////////////////////////////
//~ rjf: Helpers
static CG_FilePair CG_FilePairFromNode(MD_Node *node);
static void CG_CloseAllFiles(void);
static MD_String8 CG_EscapedFromString(MD_Arena *arena, MD_String8 string);
////////////////////////////////
//~ rjf: Globals
static MD_Arena *cg_arena = 0;
static int cg_file_pair_count = 0;
static CG_FilePair cg_file_pairs[4096] = {0};
#endif // METAPROGRAM_H

View File

@ -1,564 +0,0 @@
static MD_Map cg_tbl_top_level_node_grid_map = {0};
static MD_Map cg_tbl_top_level_table_header_map = {0};
static MD_Map cg_tbl_layer_map_gen = {0};
static MD_Map cg_tbl_layer_map_gen_enum = {0};
static MD_Map cg_tbl_layer_map_gen_data = {0};
static MD_String8 cg_tbl_tag__table = MD_S8LitComp("table");
static MD_String8 cg_tbl_tag__table_gen = MD_S8LitComp("table_gen");
static MD_String8 cg_tbl_tag__table_gen_enum = MD_S8LitComp("table_gen_enum");
static MD_String8 cg_tbl_tag__table_gen_data = MD_S8LitComp("table_gen_data");
static CG_NodeArray
CG_NodeArrayMake(MD_u64 count)
{
CG_NodeArray result = {0};
result.count = count;
result.v = MD_PushArrayZero(cg_arena, MD_Node *, result.count);
for(MD_u64 idx = 0; idx < result.count; idx += 1)
{
result.v[idx] = MD_NilNode();
}
return result;
}
static CG_NodeGrid
CG_GridFromNode(MD_Node *node)
{
CG_NodeGrid grid = {0};
//- rjf: determine dimensions
MD_u64 row_count = 0;
MD_u64 column_count = 0;
{
for(MD_EachNode(row, node->first_child))
{
row_count += 1;
MD_u64 cell_count_this_row = MD_ChildCountFromNode(row);
column_count = MD_Max(cell_count_this_row, column_count);
}
}
//- rjf: allocate cells / row parents
{
grid.cells = CG_NodeArrayMake(row_count * column_count);
grid.row_parents = CG_NodeArrayMake(row_count);
}
//- rjf: fill cells
{
MD_u64 row_idx = 0;
for(MD_EachNode(row, node->first_child))
{
MD_u64 col_idx = 0;
grid.row_parents.v[row_idx] = row;
for(MD_EachNode(cell, row->first_child))
{
grid.cells.v[row_idx * column_count + col_idx] = cell;
col_idx += 1;
}
row_idx += 1;
}
}
return grid;
}
static CG_TableHeader
CG_TableHeaderFromTag(MD_Node *tag)
{
CG_TableHeader result = {0};
result.column_count = MD_ChildCountFromNode(tag);
result.column_descs = MD_PushArrayZero(cg_arena, CG_ColumnDesc, result.column_count);
MD_u64 idx = 0;
for(MD_EachNode(column_node, tag->first_child))
{
result.column_descs[idx].kind = CG_ColumnKind_Default;
result.column_descs[idx].name = column_node->string;
MD_Node *check_for_tag = MD_TagFromString(column_node, MD_S8Lit("check_for_tag"), 0);
if(!MD_NodeIsNil(check_for_tag))
{
result.column_descs[idx].kind = CG_ColumnKind_CheckForTag;
result.column_descs[idx].tag_string = check_for_tag->first_child->string;
}
idx += 1;
}
return result;
}
static MD_u64
CG_RowChildIndexFromColumnName(CG_TableHeader *header, MD_String8 column_name)
{
MD_u64 result = 0;
for(MD_u64 idx = 0; idx < header->column_count; idx += 1)
{
if(MD_S8Match(header->column_descs[idx].name, column_name, 0))
{
break;
}
if(header->column_descs[idx].kind == CG_ColumnKind_Default)
{
result += 1;
}
}
return result;
}
static MD_i64
CG_TableExprEvaluate_Numeric(CG_ExpandInfo *info, MD_Expr *expr)
{
MD_i64 result = 0;
CG_TableOp op = expr->op ? expr->op->op_id : CG_TableOp_Null;
switch(op)
{
case CG_TableOp_Equal:
case CG_TableOp_IsNotEqual:
{
MD_ArenaTemp scratch = MD_GetScratch(0, 0);
MD_String8List left_strs = {0};
MD_String8List right_strs = {0};
CG_TableExprEvaluate_String(info, expr->left, &left_strs);
CG_TableExprEvaluate_String(info, expr->right, &right_strs);
MD_String8 left_str = MD_S8ListJoin(scratch.arena, left_strs, 0);
MD_String8 right_str = MD_S8ListJoin(scratch.arena, right_strs, 0);
result = MD_S8Match(left_str, right_str, 0);
if(op == CG_TableOp_IsNotEqual)
{
result = !result;
}
MD_ReleaseScratch(scratch);
}break;
case CG_TableOp_BooleanAnd:
case CG_TableOp_BooleanOr:
{
MD_i64 left = CG_TableExprEvaluate_Numeric(info, expr->left);
MD_i64 right = CG_TableExprEvaluate_Numeric(info, expr->right);
switch(op)
{
case CG_TableOp_BooleanAnd: result = left && right; break;
case CG_TableOp_BooleanOr: result = left || right; break;
}
}break;
}
return result;
}
static void
CG_TableExprEvaluate_String(CG_ExpandInfo *info, MD_Expr *expr, MD_String8List *out)
{
CG_TableOp op = expr->op ? expr->op->op_id : CG_TableOp_Null;
switch(op)
{
default:
case CG_TableOp_Null:
{
MD_S8ListPush(cg_arena, out, expr->md_node->string);
}break;
case CG_TableOp_Dot:
{
MD_Expr *label_expr = expr->left;
MD_Expr *column_query_expr = expr->right;
MD_Node *label_node = label_expr->md_node;
MD_Node *column_query_node = column_query_expr->md_node;
MD_String8 label = label_node->string;
MD_String8 column_query = column_query_node->string;
MD_b32 column_query_is_by_name = column_query_node->flags & MD_NodeFlag_Identifier;
MD_b32 column_query_is_by_index = column_query_node->flags & MD_NodeFlag_Numeric;
// rjf: find which expansion this label refers to, grab its iterator
CG_ExpandIter *iter = 0;
for(CG_ExpandIter *it = info->first_expand_iter; it != 0; it = it->next)
{
if(MD_S8Match(it->label, label, 0))
{
iter = it;
break;
}
}
// rjf: error on invalid label
if(iter == 0)
{
MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(label_node), MD_MessageKind_Error, "Expansion label \"%S\" was not found as referring to a valid @expand tag.", label);
}
// rjf: generate strings from iterator's table
if(iter != 0)
{
CG_NodeGrid *grid = iter->grid;
CG_TableHeader *header = iter->header;
MD_Node *row = grid->row_parents.v[iter->idx];
// rjf: grab the cell string given the row & column_query
MD_String8 cell_string = {0};
{
// NOTE(rjf): by-name index (look into table header)
if(column_query_is_by_name && header != 0)
{
MD_u64 column_idx = 0;
CG_ColumnDesc *column = 0;
for(MD_u64 col_idx = 0; col_idx < header->column_count; col_idx += 1)
{
if(MD_S8Match(header->column_descs[col_idx].name, column_query, 0))
{
column = &header->column_descs[col_idx];
column_idx = col_idx;
break;
}
}
MD_u64 row_child_idx = CG_RowChildIndexFromColumnName(header, column_query);
// rjf: error on invalid column
if(column == 0)
{
MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(column_query_node), MD_MessageKind_Error, "Column query \"%S\" did not map to a valid column for expansion label \"%S\".", column_query, label);
}
if(column != 0)
{
switch(column->kind)
{
default:
case CG_ColumnKind_Default:
{
MD_Node *cell_node = MD_ChildFromIndex(row, row_child_idx);
cell_string = cell_node->string;
if(MD_S8Match(cell_node->raw_string, MD_S8Lit("/"), 0))
{
cell_string = MD_S8Lit("");
}
}break;
case CG_ColumnKind_CheckForTag:
{
MD_b32 has_tag = MD_NodeHasTag(row, column->tag_string, 0);
cell_string = has_tag ? MD_S8Lit("1") : MD_S8Lit("0");
}break;
}
}
}
// NOTE(rjf): by-index (grab nth child of row)
else if(column_query_is_by_index)
{
MD_i64 index = MD_CStyleIntFromString(column_query);
cell_string = MD_ChildFromIndex(row, index)->string;
}
}
MD_S8ListPush(cg_arena, out, cell_string);
}
}break;
case CG_TableOp_Bump:
{
MD_u64 dst = MD_CStyleIntFromString(expr->unary_operand->md_node->string);
MD_u64 src = out->total_size;
MD_u64 spaces_to_print = dst - src;
if(dst > src)
{
for(MD_u64 space_idx = 0; space_idx < spaces_to_print; space_idx += 1)
{
MD_S8ListPush(cg_arena, out, MD_S8Lit(" "));
}
}
}break;
case CG_TableOp_CheckIfTrue:
{
MD_i64 check_val = CG_TableExprEvaluate_Numeric(info, expr->left);
if(check_val)
{
CG_TableExprEvaluate_String(info, expr->right, out);
}
}break;
case CG_TableOp_Concat:
{
CG_TableExprEvaluate_String(info, expr->left, out);
CG_TableExprEvaluate_String(info, expr->right, out);
}break;
}
}
static void
CG_LoopExpansionDimension(CG_ExpandIter *it, CG_ExpandInfo *info, MD_String8List *out)
{
if(it->next)
{
for(MD_u64 idx = 0; idx < it->count; idx += 1)
{
it->idx = idx;
CG_LoopExpansionDimension(it->next, info, out);
}
}
else
{
for(MD_u64 idx = 0; idx < it->count; idx += 1)
{
it->idx = idx;
MD_String8List expansion_strs = {0};
MD_u64 start_idx = 0;
for(MD_u64 char_idx = 0; char_idx <= info->strexpr.size; char_idx += 1)
{
MD_b32 is_expr_marker = info->strexpr.str[char_idx] == '$';
// rjf: push regular string contents
if(char_idx == info->strexpr.size || is_expr_marker)
{
MD_String8 normal_string_chunk = MD_S8Substring(info->strexpr, start_idx, char_idx);
MD_String8 escaped = CG_EscapedFromString(cg_arena, normal_string_chunk);
MD_S8ListPush(cg_arena, &expansion_strs, escaped);
}
// rjf: handle expansion
if(is_expr_marker)
{
MD_String8 expr_string = MD_S8Skip(info->strexpr, char_idx+1);
{
MD_i64 paren_nest = 0;
for(MD_u64 expr_str_char_idx = 0; expr_str_char_idx < expr_string.size; expr_str_char_idx += 1)
{
if(expr_string.str[expr_str_char_idx] == '(')
{
paren_nest += 1;
}
else if(expr_string.str[expr_str_char_idx] == ')')
{
paren_nest -= 1;
if(paren_nest == 0)
{
expr_string.size = expr_str_char_idx+1;
break;
}
}
}
}
MD_ParseResult parse = MD_ParseOneNode(cg_arena, expr_string, 0);
MD_Node *node = parse.node;
MD_ExprParseResult expr_parse = MD_ExprParse(cg_arena, &info->expr_op_table, node->first_child, MD_NilNode());
MD_Expr *expr = expr_parse.expr;
CG_TableExprEvaluate_String(info, expr, &expansion_strs);
MD_String8 parsed_string = MD_S8Substring(info->strexpr, char_idx+1, char_idx+1+parse.string_advance);
parsed_string = MD_S8ChopWhitespace(parsed_string);
start_idx = char_idx+1+parsed_string.size;
}
}
// rjf: push expansion string to output list
MD_String8 expansion_str = MD_S8ListJoin(cg_arena, expansion_strs, 0);
MD_S8ListPush(cg_arena, out, expansion_str);
}
}
}
static MD_String8List
CG_GenStringListFromNode(MD_ExprOprTable expr_op_table, MD_Node *gen)
{
MD_String8List result = {0};
MD_ArenaTemp scratch = MD_GetScratch(0, 0);
for(MD_EachNode(strexpr, gen->first_child))
{
//- rjf: build expansion iterator list
CG_ExpandIter *first_iter = 0;
CG_ExpandIter *last_iter = 0;
{
for(MD_EachNode(tag, strexpr->first_tag))
{
if(MD_S8Match(tag->string, MD_S8Lit("expand"), 0))
{
MD_Node *table_name_node = MD_ChildFromIndex(tag, 0);
MD_Node *label_node = MD_ChildFromIndex(tag, 1);
MD_String8 table_name = table_name_node->string;
MD_String8 label = label_node->string;
// rjf: grab the table associated with table_name
CG_NodeGrid *grid = 0;
{
MD_MapSlot *slot = MD_MapLookup(&cg_tbl_top_level_node_grid_map, MD_MapKeyStr(table_name));
if(slot != 0)
{
grid = slot->val;
}
}
// rjf: grab the table header associated with table_name
CG_TableHeader *header = 0;
{
MD_MapSlot *slot = MD_MapLookup(&cg_tbl_top_level_table_header_map, MD_MapKeyStr(table_name));
if(slot != 0)
{
header = slot->val;
}
}
// rjf: make iterator node if we got a grid
if(grid != 0)
{
CG_ExpandIter *iter = MD_PushArrayZero(scratch.arena, CG_ExpandIter, 1);
MD_QueuePush(first_iter, last_iter, iter);
iter->grid = grid;
iter->header = header;
iter->label = label;
iter->count = grid->row_parents.count;
}
// rjf: print out an error if grid is 0
if(grid == 0)
{
MD_PrintMessageFmt(stderr, MD_CodeLocFromNode(tag), MD_MessageKind_Error, "Table \"%S\" was not found.", table_name);
}
}
}
}
//- rjf: generate string list for this strexpr & push to result
if(first_iter != 0)
{
CG_ExpandInfo info = {0};
{
info.strexpr = strexpr->string;
info.first_expand_iter = first_iter;
info.expr_op_table = expr_op_table;
}
CG_LoopExpansionDimension(first_iter, &info, &result);
}
//- rjf: generate non-expansion strings
else
{
MD_String8 escaped = CG_EscapedFromString(cg_arena, strexpr->string);
MD_S8ListPush(cg_arena, &result, escaped);
}
}
MD_ReleaseScratch(scratch);
return result;
}
static void
CG_TBL_Generate(MD_Node *file_list)
{
//- rjf: initialize all maps
cg_tbl_top_level_node_grid_map = MD_MapMake(cg_arena);
cg_tbl_top_level_table_header_map = MD_MapMake(cg_arena);
cg_tbl_layer_map_gen = MD_MapMake(cg_arena);
cg_tbl_layer_map_gen_enum = MD_MapMake(cg_arena);
cg_tbl_layer_map_gen_data = MD_MapMake(cg_arena);
//- rjf: build table expression operator table
MD_ExprOprTable table_expr_op_table = {0};
{
MD_ExprOprList ops_list = {0};
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 10, MD_S8Lit("."), CG_TableOp_Dot, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Prefix, 9, MD_S8Lit("=>"), CG_TableOp_Bump, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 6, MD_S8Lit("??"), CG_TableOp_CheckIfTrue, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 7, MD_S8Lit(".."), CG_TableOp_Concat, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 8, MD_S8Lit("=="), CG_TableOp_Equal, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 8, MD_S8Lit("!="), CG_TableOp_IsNotEqual, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 5, MD_S8Lit("&&"), CG_TableOp_BooleanAnd, 0);
MD_ExprOprPush(cg_arena, &ops_list, MD_ExprOprKind_Binary, 4, MD_S8Lit("||"), CG_TableOp_BooleanOr, 0);
table_expr_op_table = MD_ExprBakeOprTableFromList(cg_arena, &ops_list);
}
//- rjf: gather phase
for(MD_EachNode(file_ref, file_list->first_child))
{
MD_Node *file = MD_ResolveNodeFromReference(file_ref);
MD_String8 layer_name = file->string;
MD_MapKey layer_key = MD_MapKeyStr(layer_name);
for(MD_EachNode(node, file->first_child))
{
MD_Node *table_tag = MD_TagFromString(node, cg_tbl_tag__table, 0);
if(!MD_NodeIsNil(table_tag))
{
CG_NodeGrid *grid = MD_PushArrayZero(cg_arena, CG_NodeGrid, 1);
*grid = CG_GridFromNode(node);
MD_MapOverwrite(cg_arena, &cg_tbl_top_level_node_grid_map, MD_MapKeyStr(node->string), grid);
CG_TableHeader *header = MD_PushArrayZero(cg_arena, CG_TableHeader, 1);
*header = CG_TableHeaderFromTag(table_tag);
MD_MapOverwrite(cg_arena, &cg_tbl_top_level_table_header_map, MD_MapKeyStr(node->string), header);
}
if(MD_NodeHasTag(node, cg_tbl_tag__table_gen, 0))
{
MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen, layer_key, node);
}
if(MD_NodeHasTag(node, cg_tbl_tag__table_gen_enum, 0))
{
MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen_enum, layer_key, node);
}
if(MD_NodeHasTag(node, cg_tbl_tag__table_gen_data, 0))
{
MD_MapInsert(cg_arena, &cg_tbl_layer_map_gen_data, layer_key, node);
}
}
}
//- rjf: generation phase
for(MD_EachNode(file_ref, file_list->first_child))
{
MD_Node *file = MD_ResolveNodeFromReference(file_ref);
MD_String8 layer_name = file->string;
MD_MapKey layer_key = MD_MapKeyStr(layer_name);
//- rjf: generate all table enums
for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen_enum, layer_key);
slot != 0;
slot = MD_MapScan(slot->next, layer_key))
{
MD_Node *gen = (MD_Node *)slot->val;
CG_FilePair f = CG_FilePairFromNode(gen);
fprintf(f.h, "typedef enum %.*s\n{\n", MD_S8VArg(gen->string));
MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen);
MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") };
MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join);
fprintf(f.h, "%.*s", MD_S8VArg(gen_string));
fprintf(f.h, "\n}\n%.*s;\n\n", MD_S8VArg(gen->string));
}
//- rjf: generate all data tables
for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen_data, layer_key);
slot != 0;
slot = MD_MapScan(slot->next, layer_key))
{
MD_Node *gen = (MD_Node *)slot->val;
MD_Node *tag = MD_TagFromString(gen, cg_tbl_tag__table_gen_data, 0);
MD_Node *data_table_type_node = tag->first_child;
MD_String8 data_table_type = data_table_type_node->string;
MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen);
MD_String8 h_decl_specifier = MD_S8Lit("extern");
CG_FilePair f = CG_FilePairFromNode(gen);
fprintf(f.h, "%.*s %.*s %.*s[%" PRIu64 "];\n\n", MD_S8VArg(h_decl_specifier), MD_S8VArg(data_table_type), MD_S8VArg(gen->string), gen_strings.node_count);
fprintf(f.c, "%.*s %.*s[%" PRIu64 "] =\n{\n", MD_S8VArg(data_table_type), MD_S8VArg(gen->string), gen_strings.node_count);
MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") };
MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join);
fprintf(f.c, "%.*s", MD_S8VArg(gen_string));
fprintf(f.c, "\n};\n");
fprintf(f.c, "\n");
}
//- rjf: generate all general generations
for(MD_MapSlot *slot = MD_MapLookup(&cg_tbl_layer_map_gen, layer_key);
slot != 0;
slot = MD_MapScan(slot->next, layer_key))
{
MD_Node *gen = (MD_Node *)slot->val;
CG_FilePair f = CG_FilePairFromNode(gen);
FILE *file = MD_NodeHasTag(gen, MD_S8Lit("c"), 0) ? f.c : f.h;
MD_String8List gen_strings = CG_GenStringListFromNode(table_expr_op_table, gen);
MD_StringJoin join = { MD_S8Lit(""), MD_S8Lit("\n"), MD_S8Lit("") };
MD_String8 gen_string = MD_S8ListJoin(cg_arena, gen_strings, &join);
fprintf(file, "%.*s", MD_S8VArg(gen_string));
fprintf(file, "\n\n");
}
}
}

View File

@ -1,92 +0,0 @@
#ifndef METAPROGRAM_TABLE_H
#define METAPROGRAM_TABLE_H
typedef enum CG_TableOp
{
CG_TableOp_Null,
CG_TableOp_BeginStringOps,
CG_TableOp_Dot,
CG_TableOp_Bump,
CG_TableOp_CheckIfTrue,
CG_TableOp_Concat,
CG_TableOp_EndStringOps,
CG_TableOp_BeginNumericOps,
CG_TableOp_Equal,
CG_TableOp_IsNotEqual,
CG_TableOp_BooleanAnd,
CG_TableOp_BooleanOr,
CG_TableOp_EndNumericOps,
CG_TableOp_COUNT
}
CG_TableOp;
typedef struct CG_NodeArray CG_NodeArray;
struct CG_NodeArray
{
MD_u64 count;
MD_Node **v;
};
typedef struct CG_NodeGrid CG_NodeGrid;
struct CG_NodeGrid
{
CG_NodeArray cells;
CG_NodeArray row_parents;
};
typedef enum CG_ColumnKind
{
CG_ColumnKind_Default,
CG_ColumnKind_CheckForTag,
CG_ColumnKind_COUNT
}
CG_ColumnKind;
typedef struct CG_ColumnDesc CG_ColumnDesc;
struct CG_ColumnDesc
{
CG_ColumnKind kind;
MD_String8 name;
MD_String8 tag_string;
};
typedef struct CG_TableHeader CG_TableHeader;
struct CG_TableHeader
{
MD_u64 column_count;
CG_ColumnDesc *column_descs;
};
typedef struct CG_ExpandIter CG_ExpandIter;
struct CG_ExpandIter
{
CG_ExpandIter *next;
CG_NodeGrid *grid;
CG_TableHeader *header;
MD_String8 label;
MD_u64 idx;
MD_u64 count;
};
typedef struct CG_ExpandInfo CG_ExpandInfo;
struct CG_ExpandInfo
{
MD_String8 strexpr;
CG_ExpandIter *first_expand_iter;
MD_ExprOprTable expr_op_table;
};
static CG_NodeArray CG_NodeArrayMake(MD_u64 count);
static CG_NodeGrid CG_GridFromNode(MD_Node *node);
static CG_TableHeader CG_TableHeaderFromTag(MD_Node *tag);
static MD_u64 CG_RowChildIndexFromColumnName(CG_TableHeader *header, MD_String8 column_name);
static MD_i64 CG_TableExprEvaluate_Numeric(CG_ExpandInfo *info, MD_Expr *expr);
static void CG_TableExprEvaluate_String(CG_ExpandInfo *info, MD_Expr *expr, MD_String8List *out);
static void CG_LoopExpansionDimension(CG_ExpandIter *it, CG_ExpandInfo *info, MD_String8List *out);
static MD_String8List CG_GenStringListFromNode(MD_ExprOprTable expr_op_table, MD_Node *gen);
static void CG_TBL_Generate(MD_Node *file_list);
#endif // METAPROGRAM_TABLE_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1144
src/metagen/metagen.c Normal file

File diff suppressed because it is too large Load Diff

329
src/metagen/metagen.h Normal file
View File

@ -0,0 +1,329 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef METAGEN_H
#define METAGEN_H
////////////////////////////////
//~ rjf: Message Type
typedef struct MG_Msg MG_Msg;
struct MG_Msg
{
String8 location;
String8 kind;
String8 msg;
};
typedef struct MG_MsgNode MG_MsgNode;
struct MG_MsgNode
{
MG_MsgNode *next;
MG_Msg v;
};
typedef struct MG_MsgList MG_MsgList;
struct MG_MsgList
{
MG_MsgNode *first;
MG_MsgNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Parse Artifact Types
typedef struct MG_FileParse MG_FileParse;
struct MG_FileParse
{
MD_Node *root;
};
typedef struct MG_FileParseNode MG_FileParseNode;
struct MG_FileParseNode
{
MG_FileParseNode *next;
MG_FileParse v;
};
typedef struct MG_FileParseList MG_FileParseList;
struct MG_FileParseList
{
MG_FileParseNode *first;
MG_FileParseNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Map Type
typedef struct MG_MapNode MG_MapNode;
struct MG_MapNode
{
MG_MapNode *next;
String8 key;
void *val;
};
typedef struct MG_MapSlot MG_MapSlot;
struct MG_MapSlot
{
MG_MapNode *first;
MG_MapNode *last;
};
typedef struct MG_Map MG_Map;
struct MG_Map
{
MG_MapSlot *slots;
U64 slots_count;
};
////////////////////////////////
//~ rjf: String Expression Types
typedef enum MG_StrExprOpKind
{
MG_StrExprOpKind_Null,
MG_StrExprOpKind_Prefix,
MG_StrExprOpKind_Postfix,
MG_StrExprOpKind_Binary,
MG_StrExprOpKind_COUNT
}
MG_StrExprOpKind;
typedef enum MG_StrExprOp
{
MG_StrExprOp_Null,
#define MG_StrExprOp_FirstString MG_StrExprOp_Dot
MG_StrExprOp_Dot,
MG_StrExprOp_ExpandIfTrue,
MG_StrExprOp_Concat,
MG_StrExprOp_BumpToColumn,
#define MG_StrExprOp_LastString MG_StrExprOp_BumpToColumn
#define MG_StrExprOp_FirstNumeric MG_StrExprOp_Add
MG_StrExprOp_Add,
MG_StrExprOp_Subtract,
MG_StrExprOp_Multiply,
MG_StrExprOp_Divide,
MG_StrExprOp_Modulo,
MG_StrExprOp_LeftShift,
MG_StrExprOp_RightShift,
MG_StrExprOp_BitwiseAnd,
MG_StrExprOp_BitwiseOr,
MG_StrExprOp_BitwiseXor,
MG_StrExprOp_BitwiseNegate,
MG_StrExprOp_BooleanAnd,
MG_StrExprOp_BooleanOr,
MG_StrExprOp_BooleanNot,
MG_StrExprOp_Equals,
MG_StrExprOp_DoesNotEqual,
#define MG_StrExprOp_LastNumeric MG_StrExprOp_DoesNotEqual
MG_StrExprOp_COUNT,
}
MG_StrExprOp;
typedef struct MG_StrExpr MG_StrExpr;
struct MG_StrExpr
{
MG_StrExpr *parent;
MG_StrExpr *left;
MG_StrExpr *right;
MG_StrExprOp op;
MD_Node *node;
};
typedef struct MG_StrExprParseResult MG_StrExprParseResult;
struct MG_StrExprParseResult
{
MG_StrExpr *root;
MD_MsgList msgs;
MD_Node *next_node;
};
////////////////////////////////
//~ rjf: Table Generation Types
typedef struct MG_NodeArray MG_NodeArray;
struct MG_NodeArray
{
MD_Node **v;
U64 count;
};
typedef struct MG_NodeGrid MG_NodeGrid;
struct MG_NodeGrid
{
U64 x_stride;
U64 y_stride;
MG_NodeArray cells;
MG_NodeArray row_parents;
};
typedef enum MG_ColumnKind
{
MG_ColumnKind_DirectCell,
MG_ColumnKind_CheckForTag,
MG_ColumnKind_TagChild,
MG_ColumnKind_COUNT
}
MG_ColumnKind;
typedef struct MG_ColumnDesc MG_ColumnDesc;
struct MG_ColumnDesc
{
String8 name;
MG_ColumnKind kind;
String8 tag_name;
};
typedef struct MG_ColumnDescArray MG_ColumnDescArray;
struct MG_ColumnDescArray
{
U64 count;
MG_ColumnDesc *v;
};
typedef struct MG_TableExpandTask MG_TableExpandTask;
struct MG_TableExpandTask
{
MG_TableExpandTask *next;
String8 expansion_label;
MG_NodeGrid *grid;
MG_ColumnDescArray column_descs;
U64 count;
U64 idx;
};
typedef struct MG_TableExpandInfo MG_TableExpandInfo;
struct MG_TableExpandInfo
{
MG_TableExpandTask *first_expand_task;
String8 missing_value_fallback;
};
////////////////////////////////
//~ rjf: Main Output Path Types
typedef struct MG_Layer MG_Layer;
struct MG_Layer
{
String8 key;
B32 is_library;
String8 gen_folder_name;
String8 h_name_override;
String8 c_name_override;
String8List enums;
String8List structs;
String8List h_functions;
String8List h_tables;
String8List h_catchall;
String8List h_header;
String8List h_footer;
String8List c_functions;
String8List c_tables;
String8List c_catchall;
String8List c_header;
String8List c_footer;
};
typedef struct MG_LayerNode MG_LayerNode;
struct MG_LayerNode
{
MG_LayerNode *next;
MG_Layer v;
};
typedef struct MG_LayerSlot MG_LayerSlot;
struct MG_LayerSlot
{
MG_LayerNode *first;
MG_LayerNode *last;
};
typedef struct MG_State MG_State;
struct MG_State
{
U64 slots_count;
MG_LayerSlot *slots;
};
////////////////////////////////
//~ rjf: Globals
global Arena *mg_arena = 0;
global MG_State *mg_state = 0;
read_only global MG_StrExpr mg_str_expr_nil = {&mg_str_expr_nil, &mg_str_expr_nil, &mg_str_expr_nil};
////////////////////////////////
//~ rjf: Basic Helpers
internal U64 mg_hash_from_string(String8 string);
internal TxtPt mg_txt_pt_from_string_off(String8 string, U64 off);
////////////////////////////////
//~ rjf: Message Lists
internal void mg_msg_list_push(Arena *arena, MG_MsgList *msgs, MG_Msg *msg);
////////////////////////////////
//~ rjf: String Escaping
internal String8 mg_escaped_from_str8(Arena *arena, String8 string);
////////////////////////////////
//~ rjf: String Wrapping
internal String8List mg_wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent);
////////////////////////////////
//~ rjf: C-String-Izing
internal String8 mg_c_string_literal_from_multiline_string(String8 string);
internal String8 mg_c_array_literal_contents_from_data(String8 data);
////////////////////////////////
//~ rjf: Map Functions
internal MG_Map mg_push_map(Arena *arena, U64 slot_count);
internal void *mg_map_ptr_from_string(MG_Map *map, String8 string);
internal void mg_map_insert_ptr(Arena *arena, MG_Map *map, String8 string, void *val);
////////////////////////////////
//~ rjf: String Expression Parsing
internal MG_StrExpr *mg_push_str_expr(Arena *arena, MG_StrExprOp op, MD_Node *node);
internal MG_StrExprParseResult mg_str_expr_parse_from_first_opl__min_prec(Arena *arena, MD_Node *first, MD_Node *opl, S8 min_prec);
internal MG_StrExprParseResult mg_str_expr_parse_from_first_opl(Arena *arena, MD_Node *first, MD_Node *opl);
internal MG_StrExprParseResult mg_str_expr_parse_from_root(Arena *arena, MD_Node *root);
////////////////////////////////
//~ rjf: Table Generation Functions
internal MG_NodeArray mg_node_array_make(Arena *arena, U64 count);
internal MG_NodeArray mg_child_array_from_node(Arena *arena, MD_Node *node);
internal MG_NodeGrid mg_node_grid_make_from_node(Arena *arena, MD_Node *root);
internal MG_NodeArray mg_row_from_index(MG_NodeGrid grid, U64 index);
internal MG_NodeArray mg_column_from_index(Arena *arena, MG_NodeGrid grid, U64 index);
internal MD_Node *mg_node_from_grid_xy(MG_NodeGrid grid, U64 x, U64 y);
internal MG_ColumnDescArray mg_column_desc_array_make(Arena *arena, U64 count, MG_ColumnDesc *descs);
internal MG_ColumnDescArray mg_column_desc_array_from_tag(Arena *arena, MD_Node *tag);
internal U64 mg_column_index_from_name(MG_ColumnDescArray descs, String8 name);
internal String8 mg_string_from_row_desc_idx(MD_Node *row_parent, MG_ColumnDescArray descs, U64 idx);
internal S64 mg_eval_table_expand_expr__numeric(MG_StrExpr *expr, MG_TableExpandInfo *info);
internal void mg_eval_table_expand_expr__string(Arena *arena, MG_StrExpr *expr, MG_TableExpandInfo *info, String8List *out);
internal void mg_loop_table_column_expansion(Arena *arena, String8 strexpr, MG_TableExpandInfo *info, MG_TableExpandTask *task, String8List *out);
internal String8List mg_string_list_from_table_gen(Arena *arena, MG_Map grid_name_map, MG_Map grid_column_desc_map, String8 fallback, MD_Node *gen);
////////////////////////////////
//~ rjf: Layer Lookup Functions
internal String8 mg_layer_key_from_path(String8 path);
internal MG_Layer *mg_layer_from_key(String8 key);
#endif //METAGEN_H

View File

@ -0,0 +1,248 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Arena Functions
//- rjf: arena creation/destruction
internal Arena *
arena_alloc_(ArenaParams *params)
{
// rjf: round up reserve/commit sizes
U64 reserve_size = params->reserve_size;
U64 commit_size = params->commit_size;
if(params->flags & ArenaFlag_LargePages)
{
reserve_size = AlignPow2(reserve_size, os_get_system_info()->large_page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->large_page_size);
}
else
{
reserve_size = AlignPow2(reserve_size, os_get_system_info()->page_size);
commit_size = AlignPow2(commit_size, os_get_system_info()->page_size);
}
// rjf: reserve/commit initial block
void *base = params->optional_backing_buffer;
if(base == 0)
{
if(params->flags & ArenaFlag_LargePages)
{
base = os_reserve_large(reserve_size);
os_commit_large(base, commit_size);
}
else
{
base = os_reserve(reserve_size);
os_commit(base, commit_size);
}
}
// rjf: panic on arena creation failure
#if OS_FEATURE_GRAPHICAL
if(Unlikely(base == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
// rjf: extract arena header & fill
Arena *arena = (Arena *)base;
arena->current = arena;
arena->flags = params->flags;
arena->cmt_size = params->commit_size;
arena->res_size = params->reserve_size;
arena->base_pos = 0;
arena->pos = ARENA_HEADER_SIZE;
arena->cmt = commit_size;
arena->res = reserve_size;
#if ARENA_FREE_LIST
arena->free_size = 0;
arena->free_last = 0;
#endif
AsanPoisonMemoryRegion(base, commit_size);
AsanUnpoisonMemoryRegion(base, ARENA_HEADER_SIZE);
return arena;
}
internal void
arena_release(Arena *arena)
{
for(Arena *n = arena->current, *prev = 0; n != 0; n = prev)
{
prev = n->prev;
os_release(n, n->res);
}
}
//- rjf: arena push/pop core functions
internal void *
arena_push(Arena *arena, U64 size, U64 align)
{
Arena *current = arena->current;
U64 pos_pre = AlignPow2(current->pos, align);
U64 pos_pst = pos_pre + size;
// rjf: chain, if needed
if(current->res < pos_pst && !(arena->flags & ArenaFlag_NoChain))
{
Arena *new_block = 0;
#if ARENA_FREE_LIST
Arena *prev_block;
for(new_block = arena->free_last, prev_block = 0; new_block != 0; prev_block = new_block, new_block = new_block->prev)
{
if(new_block->res >= AlignPow2(size, align))
{
if(prev_block)
{
prev_block->prev = new_block->prev;
}
else
{
arena->free_last = new_block->prev;
}
arena->free_size -= new_block->res_size;
AsanUnpoisonMemoryRegion((U8*)new_block + ARENA_HEADER_SIZE, new_block->res_size - ARENA_HEADER_SIZE);
break;
}
}
#endif
if(new_block == 0)
{
U64 res_size = current->res_size;
U64 cmt_size = current->cmt_size;
if(size + ARENA_HEADER_SIZE > res_size)
{
res_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
cmt_size = AlignPow2(size + ARENA_HEADER_SIZE, align);
}
new_block = arena_alloc(.reserve_size = res_size,
.commit_size = cmt_size,
.flags = current->flags);
}
new_block->base_pos = current->base_pos + current->res;
SLLStackPush_N(arena->current, new_block, prev);
current = new_block;
pos_pre = AlignPow2(current->pos, align);
pos_pst = pos_pre + size;
}
// rjf: commit new pages, if needed
if(current->cmt < pos_pst)
{
U64 cmt_pst_aligned = pos_pst + current->cmt_size-1;
cmt_pst_aligned -= cmt_pst_aligned%current->cmt_size;
U64 cmt_pst_clamped = ClampTop(cmt_pst_aligned, current->res);
U64 cmt_size = cmt_pst_clamped - current->cmt;
U8 *cmt_ptr = (U8 *)current + current->cmt;
if(current->flags & ArenaFlag_LargePages)
{
os_commit_large(cmt_ptr, cmt_size);
}
else
{
os_commit(cmt_ptr, cmt_size);
}
current->cmt = cmt_pst_clamped;
}
// rjf: push onto current block
void *result = 0;
if(current->cmt >= pos_pst)
{
result = (U8 *)current+pos_pre;
current->pos = pos_pst;
AsanUnpoisonMemoryRegion(result, size);
}
// rjf: panic on failure
#if OS_FEATURE_GRAPHICAL
if(Unlikely(result == 0))
{
os_graphical_message(1, str8_lit("Fatal Allocation Failure"), str8_lit("Unexpected memory allocation failure."));
os_abort(1);
}
#endif
return result;
}
internal U64
arena_pos(Arena *arena)
{
Arena *current = arena->current;
U64 pos = current->base_pos + current->pos;
return pos;
}
internal void
arena_pop_to(Arena *arena, U64 pos)
{
U64 big_pos = ClampBot(ARENA_HEADER_SIZE, pos);
Arena *current = arena->current;
#if ARENA_FREE_LIST
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
{
prev = current->prev;
current->pos = ARENA_HEADER_SIZE;
arena->free_size += current->res_size;
SLLStackPush_N(arena->free_last, current, prev);
AsanPoisonMemoryRegion((U8*)current + ARENA_HEADER_SIZE, current->res_size - ARENA_HEADER_SIZE);
}
#else
for(Arena *prev = 0; current->base_pos >= big_pos; current = prev)
{
prev = current->prev;
os_release(current, current->res);
}
#endif
arena->current = current;
U64 new_pos = big_pos - current->base_pos;
AssertAlways(new_pos <= current->pos);
AsanPoisonMemoryRegion((U8*)current + new_pos, (current->pos - new_pos));
current->pos = new_pos;
}
//- rjf: arena push/pop helpers
internal void
arena_clear(Arena *arena)
{
arena_pop_to(arena, 0);
}
internal void
arena_pop(Arena *arena, U64 amt)
{
U64 pos_old = arena_pos(arena);
U64 pos_new = pos_old;
if(amt < pos_old)
{
pos_new = pos_old - amt;
}
arena_pop_to(arena, pos_new);
}
//- rjf: temporary arena scopes
internal Temp
temp_begin(Arena *arena)
{
U64 pos = arena_pos(arena);
Temp temp = {arena, pos};
return temp;
}
internal void
temp_end(Temp temp)
{
arena_pop_to(temp.arena, temp.pos);
}

View File

@ -0,0 +1,91 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_ARENA_H
#define BASE_ARENA_H
////////////////////////////////
//~ rjf: Constants
#define ARENA_HEADER_SIZE 128
////////////////////////////////
//~ rjf: Types
typedef U64 ArenaFlags;
enum
{
ArenaFlag_NoChain = (1<<0),
ArenaFlag_LargePages = (1<<1),
};
typedef struct ArenaParams ArenaParams;
struct ArenaParams
{
ArenaFlags flags;
U64 reserve_size;
U64 commit_size;
void *optional_backing_buffer;
};
typedef struct Arena Arena;
struct Arena
{
Arena *prev; // previous arena in chain
Arena *current; // current arena in chain
ArenaFlags flags;
U64 cmt_size;
U64 res_size;
U64 base_pos;
U64 pos;
U64 cmt;
U64 res;
#if ARENA_FREE_LIST
U64 free_size;
Arena *free_last;
#endif
};
StaticAssert(sizeof(Arena) <= ARENA_HEADER_SIZE, arena_header_size_check);
typedef struct Temp Temp;
struct Temp
{
Arena *arena;
U64 pos;
};
////////////////////////////////
//~ rjf: Global Defaults
global U64 arena_default_reserve_size = MB(64);
global U64 arena_default_commit_size = KB(64);
global ArenaFlags arena_default_flags = 0;
////////////////////////////////
//~ rjf: Arena Functions
//- rjf: arena creation/destruction
internal Arena *arena_alloc_(ArenaParams *params);
#define arena_alloc(...) arena_alloc_(&(ArenaParams){.reserve_size = arena_default_reserve_size, .commit_size = arena_default_commit_size, .flags = arena_default_flags, __VA_ARGS__})
internal void arena_release(Arena *arena);
//- rjf: arena push/pop/pos core functions
internal void *arena_push(Arena *arena, U64 size, U64 align);
internal U64 arena_pos(Arena *arena);
internal void arena_pop_to(Arena *arena, U64 pos);
//- rjf: arena push/pop helpers
internal void arena_clear(Arena *arena);
internal void arena_pop(Arena *arena, U64 amt);
//- rjf: temporary arena scopes
internal Temp temp_begin(Arena *arena);
internal void temp_end(Temp temp);
//- rjf: push helper macros
#define push_array_no_zero_aligned(a, T, c, align) (T *)arena_push((a), sizeof(T)*(c), (align))
#define push_array_aligned(a, T, c, align) (T *)MemoryZero(push_array_no_zero_aligned(a, T, c, align), sizeof(T)*(c))
#define push_array_no_zero(a, T, c) push_array_no_zero_aligned(a, T, c, Max(8, AlignOf(T)))
#define push_array(a, T, c) push_array_aligned(a, T, c, Max(8, AlignOf(T)))
#endif // BASE_ARENA_H

View File

@ -0,0 +1,250 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ NOTE(rjf): Command Line Option Parsing
internal U64
cmd_line_hash_from_string(String8 string)
{
U64 result = 5381;
for(U64 i = 0; i < string.size; i += 1)
{
result = ((result << 5) + result) + string.str[i];
}
return result;
}
internal CmdLineOpt **
cmd_line_slot_from_string(CmdLine *cmd_line, String8 string)
{
CmdLineOpt **slot = 0;
if(cmd_line->option_table_size != 0)
{
U64 hash = cmd_line_hash_from_string(string);
U64 bucket = hash % cmd_line->option_table_size;
slot = &cmd_line->option_table[bucket];
}
return slot;
}
internal CmdLineOpt *
cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string)
{
CmdLineOpt *result = 0;
for(CmdLineOpt *var = *slot; var; var = var->hash_next)
{
if(str8_match(string, var->string, 0))
{
result = var;
break;
}
}
return result;
}
internal void
cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var)
{
SLLQueuePush(list->first, list->last, var);
list->count += 1;
}
internal CmdLineOpt *
cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values)
{
CmdLineOpt *var = 0;
CmdLineOpt **slot = cmd_line_slot_from_string(cmd_line, string);
CmdLineOpt *existing_var = cmd_line_opt_from_slot(slot, string);
if(existing_var != 0)
{
var = existing_var;
}
else
{
var = push_array(arena, CmdLineOpt, 1);
var->hash_next = *slot;
var->hash = cmd_line_hash_from_string(string);
var->string = push_str8_copy(arena, string);
var->value_strings = values;
StringJoin join = {0};
join.pre = str8_lit("");
join.sep = str8_lit(",");
join.post = str8_lit("");
var->value_string = str8_list_join(arena, &var->value_strings, &join);
*slot = var;
cmd_line_push_opt(&cmd_line->options, var);
}
return var;
}
internal CmdLine
cmd_line_from_string_list(Arena *arena, String8List command_line)
{
CmdLine parsed = {0};
parsed.exe_name = command_line.first->string;
// NOTE(rjf): Set up config option table.
{
parsed.option_table_size = 4096;
parsed.option_table = push_array(arena, CmdLineOpt *, parsed.option_table_size);
}
// NOTE(rjf): Parse command line.
B32 after_passthrough_option = 0;
B32 first_passthrough = 1;
for(String8Node *node = command_line.first->next, *next = 0; node != 0; node = next)
{
next = node->next;
String8 option_name = node->string;
// NOTE(rjf): Look at --, -, or / (only on Windows) at the start of an
// argument to determine if it's a flag option. All arguments after a
// single "--" (with no trailing string on the command line will be
// considered as input files.
B32 is_option = 1;
if(after_passthrough_option == 0)
{
if(str8_match(node->string, str8_lit("--"), 0))
{
after_passthrough_option = 1;
is_option = 0;
}
else if(str8_match(str8_prefix(node->string, 2), str8_lit("--"), 0))
{
option_name = str8_skip(option_name, 2);
}
else if(str8_match(str8_prefix(node->string, 1), str8_lit("-"), 0))
{
option_name = str8_skip(option_name, 1);
}
else if(operating_system_from_context() == OperatingSystem_Windows &&
str8_match(str8_prefix(node->string, 1), str8_lit("/"), 0))
{
option_name = str8_skip(option_name, 1);
}
else
{
is_option = 0;
}
}
else
{
is_option = 0;
}
// NOTE(rjf): This string is an option.
if(is_option)
{
B32 has_arguments = 0;
U64 arg_signifier_position1 = str8_find_needle(option_name, 0, str8_lit(":"), 0);
U64 arg_signifier_position2 = str8_find_needle(option_name, 0, str8_lit("="), 0);
U64 arg_signifier_position = Min(arg_signifier_position1, arg_signifier_position2);
String8 arg_portion_this_string = str8_skip(option_name, arg_signifier_position+1);
if(arg_signifier_position < option_name.size)
{
has_arguments = 1;
}
option_name = str8_prefix(option_name, arg_signifier_position);
String8List arguments = {0};
// NOTE(rjf): Parse arguments.
if(has_arguments)
{
for(String8Node *n = node; n; n = n->next)
{
next = n->next;
String8 string = n->string;
if(n == node)
{
string = arg_portion_this_string;
}
U8 splits[] = { ',' };
String8List args_in_this_string = str8_split(arena, string, splits, ArrayCount(splits), 0);
for(String8Node *sub_arg = args_in_this_string.first; sub_arg; sub_arg = sub_arg->next)
{
str8_list_push(arena, &arguments, sub_arg->string);
}
if(!str8_match(str8_postfix(n->string, 1), str8_lit(","), 0) &&
(n != node || arg_portion_this_string.size != 0))
{
break;
}
}
}
// NOTE(rjf): Register config variable.
cmd_line_insert_opt(arena, &parsed, option_name, arguments);
}
// NOTE(rjf): Default path, treat as a passthrough config option to be
// handled by tool-specific code.
else if(!str8_match(node->string, str8_lit("--"), 0) || !first_passthrough)
{
str8_list_push(arena, &parsed.inputs, node->string);
after_passthrough_option = 1;
first_passthrough = 0;
}
}
// rjf: fill argc/argv
parsed.argc = command_line.node_count;
parsed.argv = push_array(arena, char *, parsed.argc);
{
U64 idx = 0;
for(String8Node *n = command_line.first; n != 0; n = n->next)
{
parsed.argv[idx] = (char *)push_str8_copy(arena, n->string).str;
idx += 1;
}
}
return parsed;
}
internal CmdLineOpt *
cmd_line_opt_from_string(CmdLine *cmd_line, String8 name)
{
return cmd_line_opt_from_slot(cmd_line_slot_from_string(cmd_line, name), name);
}
internal String8List
cmd_line_strings(CmdLine *cmd_line, String8 name)
{
String8List result = {0};
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
if(var != 0)
{
result = var->value_strings;
}
return result;
}
internal String8
cmd_line_string(CmdLine *cmd_line, String8 name)
{
String8 result = {0};
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
if(var != 0)
{
result = var->value_string;
}
return result;
}
internal B32
cmd_line_has_flag(CmdLine *cmd_line, String8 name)
{
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
return(var != 0);
}
internal B32
cmd_line_has_argument(CmdLine *cmd_line, String8 name)
{
CmdLineOpt *var = cmd_line_opt_from_string(cmd_line, name);
return(var != 0 && var->value_strings.node_count > 0);
}

View File

@ -0,0 +1,56 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_COMMAND_LINE_H
#define BASE_COMMAND_LINE_H
////////////////////////////////
//~ rjf: Parsed Command Line Types
typedef struct CmdLineOpt CmdLineOpt;
struct CmdLineOpt
{
CmdLineOpt *next;
CmdLineOpt *hash_next;
U64 hash;
String8 string;
String8List value_strings;
String8 value_string;
};
typedef struct CmdLineOptList CmdLineOptList;
struct CmdLineOptList
{
U64 count;
CmdLineOpt *first;
CmdLineOpt *last;
};
typedef struct CmdLine CmdLine;
struct CmdLine
{
String8 exe_name;
CmdLineOptList options;
String8List inputs;
U64 option_table_size;
CmdLineOpt **option_table;
U64 argc;
char **argv;
};
////////////////////////////////
//~ NOTE(rjf): Command Line Option Parsing
internal U64 cmd_line_hash_from_string(String8 string);
internal CmdLineOpt** cmd_line_slot_from_string(CmdLine *cmd_line, String8 string);
internal CmdLineOpt* cmd_line_opt_from_slot(CmdLineOpt **slot, String8 string);
internal void cmd_line_push_opt(CmdLineOptList *list, CmdLineOpt *var);
internal CmdLineOpt* cmd_line_insert_opt(Arena *arena, CmdLine *cmd_line, String8 string, String8List values);
internal CmdLine cmd_line_from_string_list(Arena *arena, String8List arguments);
internal CmdLineOpt* cmd_line_opt_from_string(CmdLine *cmd_line, String8 name);
internal String8List cmd_line_strings(CmdLine *cmd_line, String8 name);
internal String8 cmd_line_string(CmdLine *cmd_line, String8 name);
internal B32 cmd_line_has_flag(CmdLine *cmd_line, String8 name);
internal B32 cmd_line_has_argument(CmdLine *cmd_line, String8 name);
#endif // BASE_COMMAND_LINE_H

View File

@ -0,0 +1,247 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_CONTEXT_CRACKING_H
#define BASE_CONTEXT_CRACKING_H
////////////////////////////////
//~ rjf: Clang OS/Arch Cracking
#if defined(__clang__)
# define COMPILER_CLANG 1
# if defined(_WIN32)
# define OS_WINDOWS 1
# elif defined(__gnu_linux__) || defined(__linux__)
# define OS_LINUX 1
# elif defined(__APPLE__) && defined(__MACH__)
# define OS_MAC 1
# else
# error This compiler/OS combo is not supported.
# endif
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
# define ARCH_X64 1
# elif defined(i386) || defined(__i386) || defined(__i386__)
# define ARCH_X86 1
# elif defined(__aarch64__)
# define ARCH_ARM64 1
# elif defined(__arm__)
# define ARCH_ARM32 1
# else
# error Architecture not supported.
# endif
////////////////////////////////
//~ rjf: MSVC OS/Arch Cracking
#elif defined(_MSC_VER)
# define COMPILER_MSVC 1
# if _MSC_VER >= 1920
# define COMPILER_MSVC_YEAR 2019
# elif _MSC_VER >= 1910
# define COMPILER_MSVC_YEAR 2017
# elif _MSC_VER >= 1900
# define COMPILER_MSVC_YEAR 2015
# elif _MSC_VER >= 1800
# define COMPILER_MSVC_YEAR 2013
# elif _MSC_VER >= 1700
# define COMPILER_MSVC_YEAR 2012
# elif _MSC_VER >= 1600
# define COMPILER_MSVC_YEAR 2010
# elif _MSC_VER >= 1500
# define COMPILER_MSVC_YEAR 2008
# elif _MSC_VER >= 1400
# define COMPILER_MSVC_YEAR 2005
# else
# define COMPILER_MSVC_YEAR 0
# endif
# if defined(_WIN32)
# define OS_WINDOWS 1
# else
# error This compiler/OS combo is not supported.
# endif
# if defined(_M_AMD64)
# define ARCH_X64 1
# elif defined(_M_IX86)
# define ARCH_X86 1
# elif defined(_M_ARM64)
# define ARCH_ARM64 1
# elif defined(_M_ARM)
# define ARCH_ARM32 1
# else
# error Architecture not supported.
# endif
////////////////////////////////
//~ rjf: GCC OS/Arch Cracking
#elif defined(__GNUC__) || defined(__GNUG__)
# define COMPILER_GCC 1
# if defined(__gnu_linux__) || defined(__linux__)
# define OS_LINUX 1
# else
# error This compiler/OS combo is not supported.
# endif
# if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
# define ARCH_X64 1
# elif defined(i386) || defined(__i386) || defined(__i386__)
# define ARCH_X86 1
# elif defined(__aarch64__)
# define ARCH_ARM64 1
# elif defined(__arm__)
# define ARCH_ARM32 1
# else
# error Architecture not supported.
# endif
#else
# error Compiler not supported.
#endif
////////////////////////////////
//~ rjf: Arch Cracking
#if defined(ARCH_X64)
# define ARCH_64BIT 1
#elif defined(ARCH_X86)
# define ARCH_32BIT 1
#endif
#if ARCH_ARM32 || ARCH_ARM64 || ARCH_X64 || ARCH_X86
# define ARCH_LITTLE_ENDIAN 1
#else
# error Endianness of this architecture not understood by context cracker.
#endif
////////////////////////////////
//~ rjf: Language Cracking
#if defined(__cplusplus)
# define LANG_CPP 1
#else
# define LANG_C 1
#endif
////////////////////////////////
//~ rjf: Build Option Cracking
#if !defined(BUILD_DEBUG)
# define BUILD_DEBUG 1
#endif
#if !defined(BUILD_SUPPLEMENTARY_UNIT)
# define BUILD_SUPPLEMENTARY_UNIT 0
#endif
#if !defined(BUILD_ENTRY_DEFINING_UNIT)
# define BUILD_ENTRY_DEFINING_UNIT 1
#endif
#if !defined(BUILD_CONSOLE_INTERFACE)
# define BUILD_CONSOLE_INTERFACE 0
#endif
#if !defined(BUILD_VERSION_MAJOR)
# define BUILD_VERSION_MAJOR 0
#endif
#if !defined(BUILD_VERSION_MINOR)
# define BUILD_VERSION_MINOR 9
#endif
#if !defined(BUILD_VERSION_PATCH)
# define BUILD_VERSION_PATCH 14
#endif
#define BUILD_VERSION_STRING_LITERAL Stringify(BUILD_VERSION_MAJOR) "." Stringify(BUILD_VERSION_MINOR) "." Stringify(BUILD_VERSION_PATCH)
#if BUILD_DEBUG
# define BUILD_MODE_STRING_LITERAL_APPEND " [Debug]"
#else
# define BUILD_MODE_STRING_LITERAL_APPEND ""
#endif
#if defined(BUILD_GIT_HASH)
# define BUILD_GIT_HASH_STRING_LITERAL_APPEND " [" BUILD_GIT_HASH "]"
#else
# define BUILD_GIT_HASH_STRING_LITERAL_APPEND ""
#endif
#if !defined(BUILD_TITLE)
# define BUILD_TITLE "Untitled"
#endif
#if !defined(BUILD_RELEASE_PHASE_STRING_LITERAL)
# define BUILD_RELEASE_PHASE_STRING_LITERAL "ALPHA"
#endif
#if !defined(BUILD_ISSUES_LINK_STRING_LITERAL)
# define BUILD_ISSUES_LINK_STRING_LITERAL "https://github.com/EpicGamesExt/raddebugger/issues"
#endif
#define BUILD_TITLE_STRING_LITERAL BUILD_TITLE " (" BUILD_VERSION_STRING_LITERAL " " BUILD_RELEASE_PHASE_STRING_LITERAL ") - " __DATE__ "" BUILD_GIT_HASH_STRING_LITERAL_APPEND BUILD_MODE_STRING_LITERAL_APPEND
////////////////////////////////
//~ rjf: Zero All Undefined Options
#if !defined(ARCH_32BIT)
# define ARCH_32BIT 0
#endif
#if !defined(ARCH_64BIT)
# define ARCH_64BIT 0
#endif
#if !defined(ARCH_X64)
# define ARCH_X64 0
#endif
#if !defined(ARCH_X86)
# define ARCH_X86 0
#endif
#if !defined(ARCH_ARM64)
# define ARCH_ARM64 0
#endif
#if !defined(ARCH_ARM32)
# define ARCH_ARM32 0
#endif
#if !defined(COMPILER_MSVC)
# define COMPILER_MSVC 0
#endif
#if !defined(COMPILER_GCC)
# define COMPILER_GCC 0
#endif
#if !defined(COMPILER_CLANG)
# define COMPILER_CLANG 0
#endif
#if !defined(OS_WINDOWS)
# define OS_WINDOWS 0
#endif
#if !defined(OS_LINUX)
# define OS_LINUX 0
#endif
#if !defined(OS_MAC)
# define OS_MAC 0
#endif
#if !defined(LANG_CPP)
# define LANG_CPP 0
#endif
#if !defined(LANG_C)
# define LANG_C 0
#endif
////////////////////////////////
//~ rjf: Unsupported Errors
#if ARCH_X86
# error You tried to build in x86 (32 bit) mode, but currently, only building in x64 (64 bit) mode is supported.
#endif
#if !ARCH_X64
# error You tried to build with an unsupported architecture. Currently, only building in x64 mode is supported.
#endif
#endif // BASE_CONTEXT_CRACKING_H

View File

@ -0,0 +1,604 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Safe Casts
internal U16
safe_cast_u16(U32 x)
{
AssertAlways(x <= max_U16);
U16 result = (U16)x;
return result;
}
internal U32
safe_cast_u32(U64 x)
{
AssertAlways(x <= max_U32);
U32 result = (U32)x;
return result;
}
internal S32
safe_cast_s32(S64 x)
{
AssertAlways(x <= max_S32);
S32 result = (S32)x;
return result;
}
////////////////////////////////
//~ rjf: Large Base Type Functions
internal U128
u128_zero(void)
{
U128 v = {0};
return v;
}
internal U128
u128_make(U64 v0, U64 v1)
{
U128 v = {v0, v1};
return v;
}
internal B32
u128_match(U128 a, U128 b)
{
return MemoryMatchStruct(&a, &b);
}
////////////////////////////////
//~ rjf: Bit Patterns
internal U32
u32_from_u64_saturate(U64 x){
U32 x32 = (x > max_U32)?max_U32:(U32)x;
return(x32);
}
internal U64
u64_up_to_pow2(U64 x){
if (x == 0){
x = 1;
}
else{
x -= 1;
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
x |= (x >> 32);
x += 1;
}
return(x);
}
internal S32
extend_sign32(U32 x, U32 size){
U32 high_bit = size * 8;
U32 shift = 32 - high_bit;
S32 result = ((S32)x << shift) >> shift;
return result;
}
internal S64
extend_sign64(U64 x, U64 size){
U64 high_bit = size * 8;
U64 shift = 64 - high_bit;
S64 result = ((S64)x << shift) >> shift;
return result;
}
internal F32
inf32(void){
union { U32 u; F32 f; } x;
x.u = exponent32;
return(x.f);
}
internal F32
neg_inf32(void){
union { U32 u; F32 f; } x;
x.u = sign32 | exponent32;
return(x.f);
}
internal U16
bswap_u16(U16 x)
{
U16 result = (((x & 0xFF00) >> 8) |
((x & 0x00FF) << 8));
return result;
}
internal U32
bswap_u32(U32 x)
{
U32 result = (((x & 0xFF000000) >> 24) |
((x & 0x00FF0000) >> 8) |
((x & 0x0000FF00) << 8) |
((x & 0x000000FF) << 24));
return result;
}
internal U64
bswap_u64(U64 x)
{
// TODO(nick): naive bswap, replace with something that is faster like an intrinsic
U64 result = (((x & 0xFF00000000000000ULL) >> 56) |
((x & 0x00FF000000000000ULL) >> 40) |
((x & 0x0000FF0000000000ULL) >> 24) |
((x & 0x000000FF00000000ULL) >> 8) |
((x & 0x00000000FF000000ULL) << 8) |
((x & 0x0000000000FF0000ULL) << 24) |
((x & 0x000000000000FF00ULL) << 40) |
((x & 0x00000000000000FFULL) << 56));
return result;
}
#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS)
internal U64
count_bits_set32(U32 val)
{
return __popcnt(val);
}
internal U64
count_bits_set64(U64 val)
{
return __popcnt64(val);
}
internal U64
ctz32(U32 mask)
{
unsigned long idx;
_BitScanForward(&idx, mask);
return idx;
}
internal U64
ctz64(U64 mask)
{
unsigned long idx;
_BitScanForward64(&idx, mask);
return idx;
}
internal U64
clz32(U32 mask)
{
unsigned long idx;
_BitScanReverse(&idx, mask);
return 31 - idx;
}
internal U64
clz64(U64 mask)
{
unsigned long idx;
_BitScanReverse64(&idx, mask);
return 63 - idx;
}
#elif COMPILER_CLANG || COMPILER_GCC
internal U64
count_bits_set32(U32 val)
{
return __builtin_popcount(val);
}
internal U64
count_bits_set64(U64 val)
{
return __builtin_popcountll(val);
}
internal U64
ctz32(U32 val)
{
return __builtin_ctz(val);
}
internal U64
clz32(U32 val)
{
return __builtin_clz(val);
}
internal U64
ctz64(U64 val)
{
return __builtin_ctzll(val);
}
internal U64
clz64(U64 val)
{
return __builtin_clzll(val);
}
#else
# error "Bit intrinsic functions not defined for this compiler."
#endif
////////////////////////////////
//~ rjf: Enum -> Sign
internal S32
sign_from_side_S32(Side side){
return((side == Side_Min)?-1:1);
}
internal F32
sign_from_side_F32(Side side){
return((side == Side_Min)?-1.f:1.f);
}
////////////////////////////////
//~ rjf: Memory Functions
internal B32
memory_is_zero(void *ptr, U64 size){
B32 result = 1;
// break down size
U64 extra = (size&0x7);
U64 count8 = (size >> 3);
// check with 8-byte stride
U64 *p64 = (U64*)ptr;
if(result)
{
for (U64 i = 0; i < count8; i += 1, p64 += 1){
if (*p64 != 0){
result = 0;
goto done;
}
}
}
// check extra
if(result)
{
U8 *p8 = (U8*)p64;
for (U64 i = 0; i < extra; i += 1, p8 += 1){
if (*p8 != 0){
result = 0;
goto done;
}
}
}
done:;
return(result);
}
////////////////////////////////
//~ rjf: Text 2D Coordinate/Range Functions
internal TxtPt
txt_pt(S64 line, S64 column)
{
TxtPt p = {0};
p.line = line;
p.column = column;
return p;
}
internal B32
txt_pt_match(TxtPt a, TxtPt b)
{
return a.line == b.line && a.column == b.column;
}
internal B32
txt_pt_less_than(TxtPt a, TxtPt b)
{
B32 result = 0;
if(a.line < b.line)
{
result = 1;
}
else if(a.line == b.line)
{
result = a.column < b.column;
}
return result;
}
internal TxtPt
txt_pt_min(TxtPt a, TxtPt b)
{
TxtPt result = b;
if(txt_pt_less_than(a, b))
{
result = a;
}
return result;
}
internal TxtPt
txt_pt_max(TxtPt a, TxtPt b)
{
TxtPt result = a;
if(txt_pt_less_than(a, b))
{
result = b;
}
return result;
}
internal TxtRng
txt_rng(TxtPt min, TxtPt max)
{
TxtRng range = {0};
if(txt_pt_less_than(min, max))
{
range.min = min;
range.max = max;
}
else
{
range.min = max;
range.max = min;
}
return range;
}
internal TxtRng
txt_rng_intersect(TxtRng a, TxtRng b)
{
TxtRng result = {0};
result.min = txt_pt_max(a.min, b.min);
result.max = txt_pt_min(a.max, b.max);
if(txt_pt_less_than(result.max, result.min))
{
MemoryZeroStruct(&result);
}
return result;
}
internal TxtRng
txt_rng_union(TxtRng a, TxtRng b)
{
TxtRng result = {0};
result.min = txt_pt_min(a.min, b.min);
result.max = txt_pt_max(a.max, b.max);
return result;
}
internal B32
txt_rng_contains(TxtRng r, TxtPt pt)
{
B32 result = ((txt_pt_less_than(r.min, pt) || txt_pt_match(r.min, pt)) &&
txt_pt_less_than(pt, r.max));
return result;
}
////////////////////////////////
//~ rjf: Toolchain/Environment Enum Functions
internal U64
bit_size_from_arch(Arch arch)
{
// TODO(rjf): metacode
U64 arch_bitsize = 0;
switch(arch)
{
case Arch_x64: arch_bitsize = 64; break;
case Arch_x86: arch_bitsize = 32; break;
case Arch_arm64: arch_bitsize = 64; break;
case Arch_arm32: arch_bitsize = 32; break;
default: break;
}
return arch_bitsize;
}
internal U64
max_instruction_size_from_arch(Arch arch)
{
// TODO(rjf): make this real
return 64;
}
internal OperatingSystem
operating_system_from_context(void){
OperatingSystem os = OperatingSystem_Null;
#if OS_WINDOWS
os = OperatingSystem_Windows;
#elif OS_LINUX
os = OperatingSystem_Linux;
#elif OS_MAC
os = OperatingSystem_Mac;
#endif
return os;
}
internal Arch
arch_from_context(void){
Arch arch = Arch_Null;
#if ARCH_X64
arch = Arch_x64;
#elif ARCH_X86
arch = Arch_x86;
#elif ARCH_ARM64
arch = Arch_arm64;
#elif ARCH_ARM32
arch = Arch_arm32;
#endif
return arch;
}
internal Compiler
compiler_from_context(void){
Compiler compiler = Compiler_Null;
#if COMPILER_MSVC
compiler = Compiler_msvc;
#elif COMPILER_GCC
compiler = Compiler_gcc;
#elif COMPILER_CLANG
compiler = Compiler_clang;
#endif
return compiler;
}
////////////////////////////////
//~ rjf: Time Functions
internal DenseTime
dense_time_from_date_time(DateTime date_time){
DenseTime result = 0;
result += date_time.year;
result *= 12;
result += date_time.mon;
result *= 31;
result += date_time.day;
result *= 24;
result += date_time.hour;
result *= 60;
result += date_time.min;
result *= 61;
result += date_time.sec;
result *= 1000;
result += date_time.msec;
return(result);
}
internal DateTime
date_time_from_dense_time(DenseTime time){
DateTime result = {0};
result.msec = time%1000;
time /= 1000;
result.sec = time%61;
time /= 61;
result.min = time%60;
time /= 60;
result.hour = time%24;
time /= 24;
result.day = time%31;
time /= 31;
result.mon = time%12;
time /= 12;
Assert(time <= max_U32);
result.year = (U32)time;
return(result);
}
internal DateTime
date_time_from_micro_seconds(U64 time){
DateTime result = {0};
result.micro_sec = time%1000;
time /= 1000;
result.msec = time%1000;
time /= 1000;
result.sec = time%60;
time /= 60;
result.min = time%60;
time /= 60;
result.hour = time%24;
time /= 24;
result.day = time%31;
time /= 31;
result.mon = time%12;
time /= 12;
Assert(time <= max_U32);
result.year = (U32)time;
return(result);
}
internal DateTime
date_time_from_unix_time(U64 unix_time)
{
DateTime date = {0};
date.year = 1970;
date.day = 1 + (unix_time / 86400);
date.sec = (U32)unix_time % 60;
date.min = (U32)(unix_time / 60) % 60;
date.hour = (U32)(unix_time / 3600) % 24;
for(;;)
{
for(date.month = 0; date.month < 12; ++date.month)
{
U64 c = 0;
switch(date.month)
{
case Month_Jan: c = 31; break;
case Month_Feb:
{
if((date.year % 4 == 0) && ((date.year % 100) != 0 || (date.year % 400) == 0))
{
c = 29;
}
else
{
c = 28;
}
} break;
case Month_Mar: c = 31; break;
case Month_Apr: c = 30; break;
case Month_May: c = 31; break;
case Month_Jun: c = 30; break;
case Month_Jul: c = 31; break;
case Month_Aug: c = 31; break;
case Month_Sep: c = 30; break;
case Month_Oct: c = 31; break;
case Month_Nov: c = 30; break;
case Month_Dec: c = 31; break;
default: InvalidPath;
}
if(date.day <= c)
{
goto exit;
}
date.day -= c;
}
++date.year;
}
exit:;
return date;
}
////////////////////////////////
//~ rjf: Non-Fancy Ring Buffer Reads/Writes
internal U64
ring_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size)
{
Assert(src_data_size <= ring_size);
{
U64 ring_off = ring_pos%ring_size;
U64 bytes_before_split = ring_size-ring_off;
U64 pre_split_bytes = Min(bytes_before_split, src_data_size);
U64 pst_split_bytes = src_data_size-pre_split_bytes;
void *pre_split_data = src_data;
void *pst_split_data = ((U8 *)src_data + pre_split_bytes);
MemoryCopy(ring_base+ring_off, pre_split_data, pre_split_bytes);
MemoryCopy(ring_base+0, pst_split_data, pst_split_bytes);
}
return src_data_size;
}
internal U64
ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_size)
{
Assert(read_size <= ring_size);
{
U64 ring_off = ring_pos%ring_size;
U64 bytes_before_split = ring_size-ring_off;
U64 pre_split_bytes = Min(bytes_before_split, read_size);
U64 pst_split_bytes = read_size-pre_split_bytes;
MemoryCopy(dst_data, ring_base+ring_off, pre_split_bytes);
MemoryCopy((U8 *)dst_data + pre_split_bytes, ring_base+0, pst_split_bytes);
}
return read_size;
}

View File

@ -0,0 +1,888 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_CORE_H
#define BASE_CORE_H
////////////////////////////////
//~ rjf: Foreign Includes
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
#include <string.h>
#include <stdint.h>
////////////////////////////////
//~ rjf: Codebase Keywords
#define internal static
#define global static
#define local_persist static
#if COMPILER_MSVC || (COMPILER_CLANG && OS_WINDOWS)
# pragma section(".rdata$", read)
# define read_only __declspec(allocate(".rdata$"))
#elif (COMPILER_CLANG && OS_LINUX)
# define read_only __attribute__((section(".rodata")))
#else
// NOTE(rjf): I don't know of a useful way to do this in GCC land.
// __attribute__((section(".rodata"))) looked promising, but it introduces a
// strange warning about malformed section attributes, and it doesn't look
// like writing to that section reliably produces access violations, strangely
// enough. (It does on Clang)
# define read_only
#endif
#if COMPILER_MSVC
# define thread_static __declspec(thread)
#elif COMPILER_CLANG || COMPILER_GCC
# define thread_static __thread
#endif
#if COMPILER_MSVC
# define force_inline __forceinline
#elif COMPILER_CLANG || COMPILER_GCC
# define force_inline __attribute__((always_inline))
#endif
////////////////////////////////
//~ rjf: Linkage Keyword Macros
#if OS_WINDOWS
# define shared_function C_LINKAGE __declspec(dllexport)
#else
# define shared_function C_LINKAGE
#endif
#if LANG_CPP
# define C_LINKAGE_BEGIN extern "C"{
# define C_LINKAGE_END }
# define C_LINKAGE extern "C"
#else
# define C_LINKAGE_BEGIN
# define C_LINKAGE_END
# define C_LINKAGE
#endif
////////////////////////////////
//~ rjf: Units
#define KB(n) (((U64)(n)) << 10)
#define MB(n) (((U64)(n)) << 20)
#define GB(n) (((U64)(n)) << 30)
#define TB(n) (((U64)(n)) << 40)
#define Thousand(n) ((n)*1000)
#define Million(n) ((n)*1000000)
#define Billion(n) ((n)*1000000000)
////////////////////////////////
//~ rjf: Branch Predictor Hints
#if defined(__clang__)
# define Expect(expr, val) __builtin_expect((expr), (val))
#else
# define Expect(expr, val) (expr)
#endif
#define Likely(expr) Expect(expr,1)
#define Unlikely(expr) Expect(expr,0)
////////////////////////////////
//~ rjf: Clamps, Mins, Maxes
#define Min(A,B) (((A)<(B))?(A):(B))
#define Max(A,B) (((A)>(B))?(A):(B))
#define ClampTop(A,X) Min(A,X)
#define ClampBot(X,B) Max(X,B)
#define Clamp(A,X,B) (((X)<(A))?(A):((X)>(B))?(B):(X))
////////////////////////////////
//~ rjf: Type -> Alignment
#if COMPILER_MSVC
# define AlignOf(T) __alignof(T)
#elif COMPILER_CLANG
# define AlignOf(T) __alignof(T)
#elif COMPILER_GCC
# define AlignOf(T) __alignof__(T)
#else
# error AlignOf not defined for this compiler.
#endif
////////////////////////////////
//~ rjf: Member Offsets
#define Member(T,m) (((T*)0)->m)
#define OffsetOf(T,m) IntFromPtr(&Member(T,m))
#define MemberFromOffset(T,ptr,off) (T)((((U8 *)ptr)+(off)))
#define CastFromMember(T,m,ptr) (T*)(((U8*)ptr) - OffsetOf(T,m))
////////////////////////////////
//~ rjf: For-Loop Construct Macros
#define DeferLoop(begin, end) for(int _i_ = ((begin), 0); !_i_; _i_ += 1, (end))
#define DeferLoopChecked(begin, end) for(int _i_ = 2 * !(begin); (_i_ == 2 ? ((end), 0) : !_i_); _i_ += 1, (end))
#define EachIndex(it, count) (U64 it = 0; it < (count); it += 1)
#define EachElement(it, array) (U64 it = 0; it < ArrayCount(array); it += 1)
#define EachEnumVal(type, it) (type it = (type)0; it < type##_COUNT; it = (type)(it+1))
#define EachNonZeroEnumVal(type, it) (type it = (type)1; it < type##_COUNT; it = (type)(it+1))
////////////////////////////////
//~ rjf: Memory Operation Macros
#define MemoryCopy(dst, src, size) memmove((dst), (src), (size))
#define MemorySet(dst, byte, size) memset((dst), (byte), (size))
#define MemoryCompare(a, b, size) memcmp((a), (b), (size))
#define MemoryStrlen(ptr) strlen(ptr)
#define MemoryCopyStruct(d,s) MemoryCopy((d),(s),sizeof(*(d)))
#define MemoryCopyArray(d,s) MemoryCopy((d),(s),sizeof(d))
#define MemoryCopyTyped(d,s,c) MemoryCopy((d),(s),sizeof(*(d))*(c))
#define MemoryZero(s,z) memset((s),0,(z))
#define MemoryZeroStruct(s) MemoryZero((s),sizeof(*(s)))
#define MemoryZeroArray(a) MemoryZero((a),sizeof(a))
#define MemoryZeroTyped(m,c) MemoryZero((m),sizeof(*(m))*(c))
#define MemoryMatch(a,b,z) (MemoryCompare((a),(b),(z)) == 0)
#define MemoryMatchStruct(a,b) MemoryMatch((a),(b),sizeof(*(a)))
#define MemoryMatchArray(a,b) MemoryMatch((a),(b),sizeof(a))
#define MemoryRead(T,p,e) ( ((p)+sizeof(T)<=(e))?(*(T*)(p)):(0) )
#define MemoryConsume(T,p,e) ( ((p)+sizeof(T)<=(e))?((p)+=sizeof(T),*(T*)((p)-sizeof(T))):((p)=(e),0) )
////////////////////////////////
//~ rjf: Asserts
#if COMPILER_MSVC
# define Trap() __debugbreak()
#elif COMPILER_CLANG || COMPILER_GCC
# define Trap() __builtin_trap()
#else
# error Unknown trap intrinsic for this compiler.
#endif
#define AssertAlways(x) do{if(!(x)) {Trap();}}while(0)
#if BUILD_DEBUG
# define Assert(x) AssertAlways(x)
#else
# define Assert(x) (void)(x)
#endif
#define InvalidPath Assert(!"Invalid Path!")
#define NotImplemented Assert(!"Not Implemented!")
#define NoOp ((void)0)
#define StaticAssert(C, ID) global U8 Glue(ID, __LINE__)[(C)?1:-1]
////////////////////////////////
//~ rjf: Atomic Operations
#if COMPILER_MSVC
# include <intrin.h>
# if ARCH_X64
# define ins_atomic_u64_eval(x) *((volatile U64 *)(x))
# define ins_atomic_u64_inc_eval(x) InterlockedIncrement64((volatile __int64 *)(x))
# define ins_atomic_u64_dec_eval(x) InterlockedDecrement64((volatile __int64 *)(x))
# define ins_atomic_u64_eval_assign(x,c) InterlockedExchange64((volatile __int64 *)(x),(c))
# define ins_atomic_u64_add_eval(x,c) InterlockedAdd64((volatile __int64 *)(x), c)
# define ins_atomic_u64_eval_cond_assign(x,k,c) InterlockedCompareExchange64((volatile __int64 *)(x),(k),(c))
# define ins_atomic_u32_eval(x) *((volatile U32 *)(x))
# define ins_atomic_u32_inc_eval(x) InterlockedIncrement((volatile LONG *)x)
# define ins_atomic_u32_eval_assign(x,c) InterlockedExchange((volatile LONG *)(x),(c))
# define ins_atomic_u32_eval_cond_assign(x,k,c) InterlockedCompareExchange((volatile LONG *)(x),(k),(c))
# define ins_atomic_u32_add_eval(x,c) InterlockedAdd((volatile LONG *)(x), c)
# else
# error Atomic intrinsics not defined for this compiler / architecture combination.
# endif
#elif COMPILER_CLANG || COMPILER_GCC
# define ins_atomic_u64_eval(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
# define ins_atomic_u64_inc_eval(x) (__atomic_fetch_add((volatile U64 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
# define ins_atomic_u64_dec_eval(x) (__atomic_fetch_sub((volatile U64 *)(x), 1, __ATOMIC_SEQ_CST) - 1)
# define ins_atomic_u64_eval_assign(x,c) __atomic_exchange_n(x, c, __ATOMIC_SEQ_CST)
# define ins_atomic_u64_add_eval(x,c) (__atomic_fetch_add((volatile U64 *)(x), c, __ATOMIC_SEQ_CST) + (c))
# define ins_atomic_u64_eval_cond_assign(x,k,c) ({ U64 _new = (c); __atomic_compare_exchange_n((volatile U64 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; })
# define ins_atomic_u32_eval(x) __atomic_load_n(x, __ATOMIC_SEQ_CST)
# define ins_atomic_u32_inc_eval(x) (__atomic_fetch_add((volatile U32 *)(x), 1, __ATOMIC_SEQ_CST) + 1)
# define ins_atomic_u32_add_eval(x,c) (__atomic_fetch_add((volatile U32 *)(x), c, __ATOMIC_SEQ_CST) + (c))
# define ins_atomic_u32_eval_assign(x,c) __atomic_exchange_n(x, c, __ATOMIC_SEQ_CST)
# define ins_atomic_u32_eval_cond_assign(x,k,c) ({ U32 _new = (c); __atomic_compare_exchange_n((volatile U32 *)(x),&_new,(k),0,__ATOMIC_SEQ_CST,__ATOMIC_SEQ_CST); _new; })
#else
# error Atomic intrinsics not defined for this compiler / architecture.
#endif
#if ARCH_64BIT
# define ins_atomic_ptr_eval_cond_assign(x,k,c) (void*)ins_atomic_u64_eval_cond_assign((volatile U64 *)(x), (U64)(k), (U64)(c))
# define ins_atomic_ptr_eval_assign(x,c) (void*)ins_atomic_u64_eval_assign((volatile U64 *)(x), (U64)(c))
# define ins_atomic_ptr_eval(x) (void*)ins_atomic_u64_eval((volatile U64 *)x)
#elif ARCH_32BIT
# define ins_atomic_ptr_eval_cond_assign(x,k,c) (void*)ins_atomic_u32_eval_cond_assign((volatile U32 *)(x), (U32)(k), (U32)(c))
# define ins_atomic_ptr_eval_assign(x,c) (void*)ins_atomic_u32_eval_assign((volatile U32 *)(x), (U32)(c))
# define ins_atomic_ptr_eval(x) (void*)ins_atomic_u32_eval((volatile U32 *)x)
#else
# error Atomic intrinsics for pointers not defined for this architecture.
#endif
////////////////////////////////
//~ rjf: Linked List Building Macros
//- rjf: linked list macro helpers
#define CheckNil(nil,p) ((p) == 0 || (p) == nil)
#define SetNil(nil,p) ((p) = nil)
//- rjf: doubly-linked-lists
#define DLLInsert_NPZ(nil,f,l,p,n,next,prev) (CheckNil(nil,f) ? \
((f) = (l) = (n), SetNil(nil,(n)->next), SetNil(nil,(n)->prev)) :\
CheckNil(nil,p) ? \
((n)->next = (f), (f)->prev = (n), (f) = (n), SetNil(nil,(n)->prev)) :\
((p)==(l)) ? \
((l)->next = (n), (n)->prev = (l), (l) = (n), SetNil(nil, (n)->next)) :\
(((!CheckNil(nil,p) && CheckNil(nil,(p)->next)) ? (0) : ((p)->next->prev = (n))), ((n)->next = (p)->next), ((p)->next = (n)), ((n)->prev = (p))))
#define DLLPushBack_NPZ(nil,f,l,n,next,prev) DLLInsert_NPZ(nil,f,l,l,n,next,prev)
#define DLLPushFront_NPZ(nil,f,l,n,next,prev) DLLInsert_NPZ(nil,l,f,f,n,prev,next)
#define DLLRemove_NPZ(nil,f,l,n,next,prev) (((n) == (f) ? (f) = (n)->next : (0)),\
((n) == (l) ? (l) = (l)->prev : (0)),\
(CheckNil(nil,(n)->prev) ? (0) :\
((n)->prev->next = (n)->next)),\
(CheckNil(nil,(n)->next) ? (0) :\
((n)->next->prev = (n)->prev)))
//- rjf: singly-linked, doubly-headed lists (queues)
#define SLLQueuePush_NZ(nil,f,l,n,next) (CheckNil(nil,f)?\
((f)=(l)=(n),SetNil(nil,(n)->next)):\
((l)->next=(n),(l)=(n),SetNil(nil,(n)->next)))
#define SLLQueuePushFront_NZ(nil,f,l,n,next) (CheckNil(nil,f)?\
((f)=(l)=(n),SetNil(nil,(n)->next)):\
((n)->next=(f),(f)=(n)))
#define SLLQueuePop_NZ(nil,f,l,next) ((f)==(l)?\
(SetNil(nil,f),SetNil(nil,l)):\
((f)=(f)->next))
//- rjf: singly-linked, singly-headed lists (stacks)
#define SLLStackPush_N(f,n,next) ((n)->next=(f), (f)=(n))
#define SLLStackPop_N(f,next) ((f)=(f)->next)
//- rjf: doubly-linked-list helpers
#define DLLInsert_NP(f,l,p,n,next,prev) DLLInsert_NPZ(0,f,l,p,n,next,prev)
#define DLLPushBack_NP(f,l,n,next,prev) DLLPushBack_NPZ(0,f,l,n,next,prev)
#define DLLPushFront_NP(f,l,n,next,prev) DLLPushFront_NPZ(0,f,l,n,next,prev)
#define DLLRemove_NP(f,l,n,next,prev) DLLRemove_NPZ(0,f,l,n,next,prev)
#define DLLInsert(f,l,p,n) DLLInsert_NPZ(0,f,l,p,n,next,prev)
#define DLLPushBack(f,l,n) DLLPushBack_NPZ(0,f,l,n,next,prev)
#define DLLPushFront(f,l,n) DLLPushFront_NPZ(0,f,l,n,next,prev)
#define DLLRemove(f,l,n) DLLRemove_NPZ(0,f,l,n,next,prev)
//- rjf: singly-linked, doubly-headed list helpers
#define SLLQueuePush_N(f,l,n,next) SLLQueuePush_NZ(0,f,l,n,next)
#define SLLQueuePushFront_N(f,l,n,next) SLLQueuePushFront_NZ(0,f,l,n,next)
#define SLLQueuePop_N(f,l,next) SLLQueuePop_NZ(0,f,l,next)
#define SLLQueuePush(f,l,n) SLLQueuePush_NZ(0,f,l,n,next)
#define SLLQueuePushFront(f,l,n) SLLQueuePushFront_NZ(0,f,l,n,next)
#define SLLQueuePop(f,l) SLLQueuePop_NZ(0,f,l,next)
//- rjf: singly-linked, singly-headed list helpers
#define SLLStackPush(f,n) SLLStackPush_N(f,n,next)
#define SLLStackPop(f) SLLStackPop_N(f,next)
////////////////////////////////
//~ rjf: Address Sanitizer Markup
#if COMPILER_MSVC
# if defined(__SANITIZE_ADDRESS__)
# define ASAN_ENABLED 1
# define NO_ASAN __declspec(no_sanitize_address)
# else
# define NO_ASAN
# endif
#elif COMPILER_CLANG
# if defined(__has_feature)
# if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
# define ASAN_ENABLED 1
# endif
# endif
# define NO_ASAN __attribute__((no_sanitize("address")))
#else
# define NO_ASAN
#endif
#if ASAN_ENABLED
#pragma comment(lib, "clang_rt.asan-x86_64.lib")
C_LINKAGE void __asan_poison_memory_region(void const volatile *addr, size_t size);
C_LINKAGE void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
# define AsanPoisonMemoryRegion(addr, size) __asan_poison_memory_region((addr), (size))
# define AsanUnpoisonMemoryRegion(addr, size) __asan_unpoison_memory_region((addr), (size))
#else
# define AsanPoisonMemoryRegion(addr, size) ((void)(addr), (void)(size))
# define AsanUnpoisonMemoryRegion(addr, size) ((void)(addr), (void)(size))
#endif
////////////////////////////////
//~ rjf: Misc. Helper Macros
#define Stringify_(S) #S
#define Stringify(S) Stringify_(S)
#define Glue_(A,B) A##B
#define Glue(A,B) Glue_(A,B)
#define ArrayCount(a) (sizeof(a) / sizeof((a)[0]))
#define CeilIntegerDiv(a,b) (((a) + (b) - 1)/(b))
#define Swap(T,a,b) do{T t__ = a; a = b; b = t__;}while(0)
#if ARCH_64BIT
# define IntFromPtr(ptr) ((U64)(ptr))
#elif ARCH_32BIT
# define IntFromPtr(ptr) ((U32)(ptr))
#else
# error Missing pointer-to-integer cast for this architecture.
#endif
#define PtrFromInt(i) (void*)((U8*)0 + (i))
#define Compose64Bit(a,b) ((((U64)a) << 32) | ((U64)b));
#define AlignPow2(x,b) (((x) + (b) - 1)&(~((b) - 1)))
#define AlignDownPow2(x,b) ((x)&(~((b) - 1)))
#define AlignPadPow2(x,b) ((0-(x)) & ((b) - 1))
#define IsPow2(x) ((x)!=0 && ((x)&((x)-1))==0)
#define IsPow2OrZero(x) ((((x) - 1)&(x)) == 0)
#define ExtractBit(word, idx) (((word) >> (idx)) & 1)
#if LANG_CPP
# define zero_struct {}
#else
# define zero_struct {0}
#endif
#if COMPILER_MSVC && COMPILER_MSVC_YEAR < 2015
# define this_function_name "unknown"
#else
# define this_function_name __func__
#endif
////////////////////////////////
//~ rjf: Base Types
typedef uint8_t U8;
typedef uint16_t U16;
typedef uint32_t U32;
typedef uint64_t U64;
typedef int8_t S8;
typedef int16_t S16;
typedef int32_t S32;
typedef int64_t S64;
typedef S8 B8;
typedef S16 B16;
typedef S32 B32;
typedef S64 B64;
typedef float F32;
typedef double F64;
typedef void VoidProc(void);
typedef struct U128 U128;
struct U128
{
U64 u64[2];
};
////////////////////////////////
//~ rjf: Basic Types & Spaces
typedef enum Dimension
{
Dimension_X,
Dimension_Y,
Dimension_Z,
Dimension_W,
}
Dimension;
typedef enum Side
{
Side_Invalid = -1,
Side_Min,
Side_Max,
Side_COUNT,
}
Side;
#define side_flip(s) ((Side)(!(s)))
typedef enum Axis2
{
Axis2_Invalid = -1,
Axis2_X,
Axis2_Y,
Axis2_COUNT,
}
Axis2;
#define axis2_flip(a) ((Axis2)(!(a)))
typedef enum Corner
{
Corner_Invalid = -1,
Corner_00,
Corner_01,
Corner_10,
Corner_11,
Corner_COUNT
}
Corner;
typedef enum Dir2
{
Dir2_Invalid = -1,
Dir2_Left,
Dir2_Up,
Dir2_Right,
Dir2_Down,
Dir2_COUNT
}
Dir2;
#define axis2_from_dir2(d) (((d) & 1) ? Axis2_Y : Axis2_X)
#define side_from_dir2(d) (((d) < Dir2_Right) ? Side_Min : Side_Max)
////////////////////////////////
//~ rjf: Toolchain/Environment Enums
typedef enum OperatingSystem
{
OperatingSystem_Null,
OperatingSystem_Windows,
OperatingSystem_Linux,
OperatingSystem_Mac,
OperatingSystem_COUNT,
}
OperatingSystem;
typedef enum ImageType
{
Image_Null,
Image_CoffPe,
Image_Elf32,
Image_Elf64,
Image_Macho
} ImageType;
typedef enum Arch
{
Arch_Null,
Arch_x64,
Arch_x86,
Arch_arm64,
Arch_arm32,
Arch_COUNT,
}
Arch;
typedef enum Compiler
{
Compiler_Null,
Compiler_msvc,
Compiler_gcc,
Compiler_clang,
Compiler_COUNT,
}
Compiler;
////////////////////////////////
//~ rjf: Text 2D Coordinates & Ranges
typedef struct TxtPt TxtPt;
struct TxtPt
{
S64 line;
S64 column;
};
typedef struct TxtRng TxtRng;
struct TxtRng
{
TxtPt min;
TxtPt max;
};
////////////////////////////////
//~ Globally Unique Ids
typedef union Guid Guid;
union Guid
{
struct
{
U32 data1;
U16 data2;
U16 data3;
U8 data4[8];
};
U8 v[16];
};
StaticAssert(sizeof(Guid) == 16, g_guid_size_check);
////////////////////////////////
//~ Arrays
typedef struct U16Array U16Array;
struct U16Array
{
U64 count;
U16 *v;
};
typedef struct U32Array U32Array;
struct U32Array
{
U64 count;
U32 *v;
};
typedef struct U64Array U64Array;
struct U64Array
{
U64 count;
U64 *v;
};
typedef struct U128Array U128Array;
struct U128Array
{
U64 count;
U128 *v;
};
////////////////////////////////
//~ NOTE(allen): Constants
global U32 sign32 = 0x80000000;
global U32 exponent32 = 0x7F800000;
global U32 mantissa32 = 0x007FFFFF;
global F32 big_golden32 = 1.61803398875f;
global F32 small_golden32 = 0.61803398875f;
global F32 pi32 = 3.1415926535897f;
global F64 machine_epsilon64 = 4.94065645841247e-324;
global U64 max_U64 = 0xffffffffffffffffull;
global U32 max_U32 = 0xffffffff;
global U16 max_U16 = 0xffff;
global U8 max_U8 = 0xff;
global S64 max_S64 = (S64)0x7fffffffffffffffull;
global S32 max_S32 = (S32)0x7fffffff;
global S16 max_S16 = (S16)0x7fff;
global S8 max_S8 = (S8)0x7f;
global S64 min_S64 = (S64)0xffffffffffffffffull;
global S32 min_S32 = (S32)0xffffffff;
global S16 min_S16 = (S16)0xffff;
global S8 min_S8 = (S8)0xff;
global const U32 bitmask1 = 0x00000001;
global const U32 bitmask2 = 0x00000003;
global const U32 bitmask3 = 0x00000007;
global const U32 bitmask4 = 0x0000000f;
global const U32 bitmask5 = 0x0000001f;
global const U32 bitmask6 = 0x0000003f;
global const U32 bitmask7 = 0x0000007f;
global const U32 bitmask8 = 0x000000ff;
global const U32 bitmask9 = 0x000001ff;
global const U32 bitmask10 = 0x000003ff;
global const U32 bitmask11 = 0x000007ff;
global const U32 bitmask12 = 0x00000fff;
global const U32 bitmask13 = 0x00001fff;
global const U32 bitmask14 = 0x00003fff;
global const U32 bitmask15 = 0x00007fff;
global const U32 bitmask16 = 0x0000ffff;
global const U32 bitmask17 = 0x0001ffff;
global const U32 bitmask18 = 0x0003ffff;
global const U32 bitmask19 = 0x0007ffff;
global const U32 bitmask20 = 0x000fffff;
global const U32 bitmask21 = 0x001fffff;
global const U32 bitmask22 = 0x003fffff;
global const U32 bitmask23 = 0x007fffff;
global const U32 bitmask24 = 0x00ffffff;
global const U32 bitmask25 = 0x01ffffff;
global const U32 bitmask26 = 0x03ffffff;
global const U32 bitmask27 = 0x07ffffff;
global const U32 bitmask28 = 0x0fffffff;
global const U32 bitmask29 = 0x1fffffff;
global const U32 bitmask30 = 0x3fffffff;
global const U32 bitmask31 = 0x7fffffff;
global const U32 bitmask32 = 0xffffffff;
global const U64 bitmask33 = 0x00000001ffffffffull;
global const U64 bitmask34 = 0x00000003ffffffffull;
global const U64 bitmask35 = 0x00000007ffffffffull;
global const U64 bitmask36 = 0x0000000fffffffffull;
global const U64 bitmask37 = 0x0000001fffffffffull;
global const U64 bitmask38 = 0x0000003fffffffffull;
global const U64 bitmask39 = 0x0000007fffffffffull;
global const U64 bitmask40 = 0x000000ffffffffffull;
global const U64 bitmask41 = 0x000001ffffffffffull;
global const U64 bitmask42 = 0x000003ffffffffffull;
global const U64 bitmask43 = 0x000007ffffffffffull;
global const U64 bitmask44 = 0x00000fffffffffffull;
global const U64 bitmask45 = 0x00001fffffffffffull;
global const U64 bitmask46 = 0x00003fffffffffffull;
global const U64 bitmask47 = 0x00007fffffffffffull;
global const U64 bitmask48 = 0x0000ffffffffffffull;
global const U64 bitmask49 = 0x0001ffffffffffffull;
global const U64 bitmask50 = 0x0003ffffffffffffull;
global const U64 bitmask51 = 0x0007ffffffffffffull;
global const U64 bitmask52 = 0x000fffffffffffffull;
global const U64 bitmask53 = 0x001fffffffffffffull;
global const U64 bitmask54 = 0x003fffffffffffffull;
global const U64 bitmask55 = 0x007fffffffffffffull;
global const U64 bitmask56 = 0x00ffffffffffffffull;
global const U64 bitmask57 = 0x01ffffffffffffffull;
global const U64 bitmask58 = 0x03ffffffffffffffull;
global const U64 bitmask59 = 0x07ffffffffffffffull;
global const U64 bitmask60 = 0x0fffffffffffffffull;
global const U64 bitmask61 = 0x1fffffffffffffffull;
global const U64 bitmask62 = 0x3fffffffffffffffull;
global const U64 bitmask63 = 0x7fffffffffffffffull;
global const U64 bitmask64 = 0xffffffffffffffffull;
global const U32 bit1 = (1<<0);
global const U32 bit2 = (1<<1);
global const U32 bit3 = (1<<2);
global const U32 bit4 = (1<<3);
global const U32 bit5 = (1<<4);
global const U32 bit6 = (1<<5);
global const U32 bit7 = (1<<6);
global const U32 bit8 = (1<<7);
global const U32 bit9 = (1<<8);
global const U32 bit10 = (1<<9);
global const U32 bit11 = (1<<10);
global const U32 bit12 = (1<<11);
global const U32 bit13 = (1<<12);
global const U32 bit14 = (1<<13);
global const U32 bit15 = (1<<14);
global const U32 bit16 = (1<<15);
global const U32 bit17 = (1<<16);
global const U32 bit18 = (1<<17);
global const U32 bit19 = (1<<18);
global const U32 bit20 = (1<<19);
global const U32 bit21 = (1<<20);
global const U32 bit22 = (1<<21);
global const U32 bit23 = (1<<22);
global const U32 bit24 = (1<<23);
global const U32 bit25 = (1<<24);
global const U32 bit26 = (1<<25);
global const U32 bit27 = (1<<26);
global const U32 bit28 = (1<<27);
global const U32 bit29 = (1<<28);
global const U32 bit30 = (1<<29);
global const U32 bit31 = (1<<30);
global const U32 bit32 = (1<<31);
global const U64 bit33 = (1ull<<32);
global const U64 bit34 = (1ull<<33);
global const U64 bit35 = (1ull<<34);
global const U64 bit36 = (1ull<<35);
global const U64 bit37 = (1ull<<36);
global const U64 bit38 = (1ull<<37);
global const U64 bit39 = (1ull<<38);
global const U64 bit40 = (1ull<<39);
global const U64 bit41 = (1ull<<40);
global const U64 bit42 = (1ull<<41);
global const U64 bit43 = (1ull<<42);
global const U64 bit44 = (1ull<<43);
global const U64 bit45 = (1ull<<44);
global const U64 bit46 = (1ull<<45);
global const U64 bit47 = (1ull<<46);
global const U64 bit48 = (1ull<<47);
global const U64 bit49 = (1ull<<48);
global const U64 bit50 = (1ull<<49);
global const U64 bit51 = (1ull<<50);
global const U64 bit52 = (1ull<<51);
global const U64 bit53 = (1ull<<52);
global const U64 bit54 = (1ull<<53);
global const U64 bit55 = (1ull<<54);
global const U64 bit56 = (1ull<<55);
global const U64 bit57 = (1ull<<56);
global const U64 bit58 = (1ull<<57);
global const U64 bit59 = (1ull<<58);
global const U64 bit60 = (1ull<<59);
global const U64 bit61 = (1ull<<60);
global const U64 bit62 = (1ull<<61);
global const U64 bit63 = (1ull<<62);
global const U64 bit64 = (1ull<<63);
////////////////////////////////
//~ allen: Time
typedef enum WeekDay
{
WeekDay_Sun,
WeekDay_Mon,
WeekDay_Tue,
WeekDay_Wed,
WeekDay_Thu,
WeekDay_Fri,
WeekDay_Sat,
WeekDay_COUNT,
}
WeekDay;
typedef enum Month
{
Month_Jan,
Month_Feb,
Month_Mar,
Month_Apr,
Month_May,
Month_Jun,
Month_Jul,
Month_Aug,
Month_Sep,
Month_Oct,
Month_Nov,
Month_Dec,
Month_COUNT,
}
Month;
typedef struct DateTime DateTime;
struct DateTime
{
U16 micro_sec; // [0,999]
U16 msec; // [0,999]
U16 sec; // [0,60]
U16 min; // [0,59]
U16 hour; // [0,24]
U16 day; // [0,30]
union
{
WeekDay week_day;
U32 wday;
};
union
{
Month month;
U32 mon;
};
U32 year; // 1 = 1 CE, 0 = 1 BC
};
typedef U64 DenseTime;
////////////////////////////////
//~ allen: Files
typedef U32 FilePropertyFlags;
enum
{
FilePropertyFlag_IsFolder = (1 << 0),
};
typedef struct FileProperties FileProperties;
struct FileProperties
{
U64 size;
DenseTime modified;
DenseTime created;
FilePropertyFlags flags;
};
////////////////////////////////
//~ rjf: Safe Casts
internal U16 safe_cast_u16(U32 x);
internal U32 safe_cast_u32(U64 x);
internal S32 safe_cast_s32(S64 x);
////////////////////////////////
//~ rjf: Large Base Type Functions
internal U128 u128_zero(void);
internal U128 u128_make(U64 v0, U64 v1);
internal B32 u128_match(U128 a, U128 b);
////////////////////////////////
//~ rjf: Bit Patterns
internal U32 u32_from_u64_saturate(U64 x);
internal U64 u64_up_to_pow2(U64 x);
internal S32 extend_sign32(U32 x, U32 size);
internal S64 extend_sign64(U64 x, U64 size);
internal F32 inf32(void);
internal F32 neg_inf32(void);
internal U16 bswap_u16(U16 x);
internal U32 bswap_u32(U32 x);
internal U64 bswap_u64(U64 x);
#if ARCH_LITTLE_ENDIAN
# define from_be_u16(x) bswap_u16(x)
# define from_be_u32(x) bswap_u32(x)
# define from_be_u64(x) bswap_u64(x)
#else
# define from_be_u16(x) (x)
# define from_be_u32(x) (x)
# define from_be_u64(x) (x)
#endif
internal U64 count_bits_set32(U32 val);
internal U64 count_bits_set64(U64 val);
internal U64 ctz32(U32 val);
internal U64 ctz64(U64 val);
internal U64 clz32(U32 val);
internal U64 clz64(U64 val);
////////////////////////////////
//~ rjf: Enum -> Sign
internal S32 sign_from_side_S32(Side side);
internal F32 sign_from_side_F32(Side side);
////////////////////////////////
//~ rjf: Memory Functions
internal B32 memory_is_zero(void *ptr, U64 size);
////////////////////////////////
//~ rjf: Text 2D Coordinate/Range Functions
internal TxtPt txt_pt(S64 line, S64 column);
internal B32 txt_pt_match(TxtPt a, TxtPt b);
internal B32 txt_pt_less_than(TxtPt a, TxtPt b);
internal TxtPt txt_pt_min(TxtPt a, TxtPt b);
internal TxtPt txt_pt_max(TxtPt a, TxtPt b);
internal TxtRng txt_rng(TxtPt min, TxtPt max);
internal TxtRng txt_rng_intersect(TxtRng a, TxtRng b);
internal TxtRng txt_rng_union(TxtRng a, TxtRng b);
internal B32 txt_rng_contains(TxtRng r, TxtPt pt);
////////////////////////////////
//~ rjf: Toolchain/Environment Enum Functions
internal U64 bit_size_from_arch(Arch arch);
internal U64 max_instruction_size_from_arch(Arch arch);
internal OperatingSystem operating_system_from_context(void);
internal Arch arch_from_context(void);
internal Compiler compiler_from_context(void);
////////////////////////////////
//~ rjf: Time Functions
internal DenseTime dense_time_from_date_time(DateTime date_time);
internal DateTime date_time_from_dense_time(DenseTime time);
internal DateTime date_time_from_micro_seconds(U64 time);
internal DateTime date_time_from_unix_time(U64 unix_time);
////////////////////////////////
//~ rjf: Non-Fancy Ring Buffer Reads/Writes
internal U64 ring_write(U8 *ring_base, U64 ring_size, U64 ring_pos, void *src_data, U64 src_data_size);
internal U64 ring_read(U8 *ring_base, U64 ring_size, U64 ring_pos, void *dst_data, U64 read_size);
#define ring_write_struct(ring_base, ring_size, ring_pos, ptr) ring_write((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr)))
#define ring_read_struct(ring_base, ring_size, ring_pos, ptr) ring_read((ring_base), (ring_size), (ring_pos), (ptr), sizeof(*(ptr)))
////////////////////////////////
//~ rjf: Sorts
#define quick_sort(ptr, count, element_size, cmp_function) qsort((ptr), (count), (element_size), (int (*)(const void *, const void *))(cmp_function))
#endif // BASE_CORE_H

View File

@ -0,0 +1,130 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
global U64 global_update_tick_idx = 0;
internal void
main_thread_base_entry_point(int arguments_count, char **arguments)
{
Temp scratch = scratch_begin(0, 0);
ThreadNameF("[main thread]");
//- rjf: set up telemetry
#if PROFILE_TELEMETRY
local_persist char tm_data[MB(64)];
tmLoadLibrary(TM_RELEASE);
tmSetMaxThreadCount(256);
tmInitialize(sizeof(tm_data), tm_data);
#endif
//- rjf: parse command line
String8List command_line_argument_strings = os_string_list_from_argcv(scratch.arena, arguments_count, arguments);
CmdLine cmdline = cmd_line_from_string_list(scratch.arena, command_line_argument_strings);
//- rjf: begin captures
B32 capture = cmd_line_has_flag(&cmdline, str8_lit("capture"));
if(capture)
{
ProfBeginCapture(arguments[0]);
}
#if PROFILE_TELEMETRY
tmMessage(0, TMMF_ICON_NOTE, BUILD_TITLE);
#endif
//- rjf: initialize all included layers
#if defined(ASYNC_H) && !defined(ASYNC_INIT_MANUAL)
async_init(&cmdline);
#endif
#if defined(RDI_FROM_PDB_H) && !defined(P2R_INIT_MANUAL)
p2r_init();
#endif
#if defined(HASH_STORE_H) && !defined(HS_INIT_MANUAL)
hs_init();
#endif
#if defined(FILE_STREAM_H) && !defined(FS_INIT_MANUAL)
fs_init();
#endif
#if defined(TEXT_CACHE_H) && !defined(TXT_INIT_MANUAL)
txt_init();
#endif
#if defined(MUTABLE_TEXT_H) && !defined(MTX_INIT_MANUAL)
mtx_init();
#endif
#if defined(DASM_CACHE_H) && !defined(DASM_INIT_MANUAL)
dasm_init();
#endif
#if defined(DBGI_H) && !defined(DI_INIT_MANUAL)
di_init();
#endif
#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL)
dmn_init();
#endif
#if defined(CTRL_CORE_H) && !defined(CTRL_INIT_MANUAL)
ctrl_init();
#endif
#if defined(OS_GFX_H) && !defined(OS_GFX_INIT_MANUAL)
os_gfx_init();
#endif
#if defined(FONT_PROVIDER_H) && !defined(FP_INIT_MANUAL)
fp_init();
#endif
#if defined(RENDER_CORE_H) && !defined(R_INIT_MANUAL)
r_init(&cmdline);
#endif
#if defined(TEXTURE_CACHE_H) && !defined(TEX_INIT_MANUAL)
tex_init();
#endif
#if defined(GEO_CACHE_H) && !defined(GEO_INIT_MANUAL)
geo_init();
#endif
#if defined(FONT_CACHE_H) && !defined(FNT_INIT_MANUAL)
fnt_init();
#endif
#if defined(DBG_ENGINE_CORE_H) && !defined(D_INIT_MANUAL)
d_init();
#endif
#if defined(RADDBG_CORE_H) && !defined(RD_INIT_MANUAL)
rd_init(&cmdline);
#endif
//- rjf: call into entry point
entry_point(&cmdline);
//- rjf: end captures
if(capture)
{
ProfEndCapture();
}
scratch_end(scratch);
}
internal void
supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params)
{
TCTX tctx;
tctx_init_and_equip(&tctx);
entry_point(params);
tctx_release();
}
internal U64
update_tick_idx(void)
{
U64 result = ins_atomic_u64_eval(&global_update_tick_idx);
return result;
}
internal B32
update(void)
{
ProfTick(0);
ins_atomic_u64_inc_eval(&global_update_tick_idx);
#if OS_FEATURE_GRAPHICAL
B32 result = frame();
#else
B32 result = 0;
#endif
return result;
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_ENTRY_POINT_H
#define BASE_ENTRY_POINT_H
internal void main_thread_base_entry_point(int argc, char **argv);
internal void supplement_thread_base_entry_point(void (*entry_point)(void *params), void *params);
internal U64 update_tick_idx(void);
internal B32 update(void);
#endif // BASE_ENTRY_POINT_H

View File

@ -0,0 +1,20 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Base Includes
#undef MARKUP_LAYER_COLOR
#define MARKUP_LAYER_COLOR 0.20f, 0.60f, 0.80f
#include "metagen_base_core.c"
#include "metagen_base_profile.c"
#include "metagen_base_arena.c"
#include "metagen_base_math.c"
#include "metagen_base_strings.c"
#include "metagen_base_thread_context.c"
#include "metagen_base_command_line.c"
#include "metagen_base_markup.c"
#include "metagen_base_meta.c"
#include "metagen_base_log.c"
#include "metagen_base_entry_point.c"

View File

@ -0,0 +1,24 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_INC_H
#define BASE_INC_H
////////////////////////////////
//~ rjf: Base Includes
#include "metagen_base_context_cracking.h"
#include "metagen_base_core.h"
#include "metagen_base_profile.h"
#include "metagen_base_arena.h"
#include "metagen_base_math.h"
#include "metagen_base_strings.h"
#include "metagen_base_thread_context.h"
#include "metagen_base_command_line.h"
#include "metagen_base_markup.h"
#include "metagen_base_meta.h"
#include "metagen_base_log.h"
#include "metagen_base_entry_point.h"
#endif // BASE_INC_H

View File

@ -0,0 +1,103 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Globals/Thread-Locals
C_LINKAGE thread_static Log *log_active;
#if !BUILD_SUPPLEMENTARY_UNIT
C_LINKAGE thread_static Log *log_active = 0;
#endif
////////////////////////////////
//~ rjf: Log Creation/Selection
internal Log *
log_alloc(void)
{
Arena *arena = arena_alloc();
Log *log = push_array(arena, Log, 1);
log->arena = arena;
return log;
}
internal void
log_release(Log *log)
{
arena_release(log->arena);
}
internal void
log_select(Log *log)
{
log_active = log;
}
////////////////////////////////
//~ rjf: Log Building/Clearing
internal void
log_msg(LogMsgKind kind, String8 string)
{
if(log_active != 0 && log_active->top_scope != 0)
{
String8 string_copy = push_str8_copy(log_active->arena, string);
str8_list_push(log_active->arena, &log_active->top_scope->strings[kind], string_copy);
}
}
internal void
log_msgf(LogMsgKind kind, char *fmt, ...)
{
if(log_active != 0)
{
Temp scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 string = push_str8fv(scratch.arena, fmt, args);
log_msg(kind, string);
va_end(args);
scratch_end(scratch);
}
}
////////////////////////////////
//~ rjf: Log Scopes
internal void
log_scope_begin(void)
{
if(log_active != 0)
{
U64 pos = arena_pos(log_active->arena);
LogScope *scope = push_array(log_active->arena, LogScope, 1);
scope->pos = pos;
SLLStackPush(log_active->top_scope, scope);
}
}
internal LogScopeResult
log_scope_end(Arena *arena)
{
LogScopeResult result = {0};
if(log_active != 0)
{
LogScope *scope = log_active->top_scope;
if(scope != 0)
{
SLLStackPop(log_active->top_scope);
if(arena != 0)
{
for EachEnumVal(LogMsgKind, kind)
{
Temp scratch = scratch_begin(&arena, 1);
String8 result_unindented = str8_list_join(scratch.arena, &scope->strings[kind], 0);
result.strings[kind] = indented_from_string(arena, result_unindented);
scratch_end(scratch);
}
}
arena_pop_to(log_active->arena, scope->pos);
}
}
return result;
}

View File

@ -0,0 +1,65 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_LOG_H
#define BASE_LOG_H
////////////////////////////////
//~ rjf: Log Types
typedef enum LogMsgKind
{
LogMsgKind_Info,
LogMsgKind_UserError,
LogMsgKind_COUNT
}
LogMsgKind;
typedef struct LogScope LogScope;
struct LogScope
{
LogScope *next;
U64 pos;
String8List strings[LogMsgKind_COUNT];
};
typedef struct LogScopeResult LogScopeResult;
struct LogScopeResult
{
String8 strings[LogMsgKind_COUNT];
};
typedef struct Log Log;
struct Log
{
Arena *arena;
LogScope *top_scope;
};
////////////////////////////////
//~ rjf: Log Creation/Selection
internal Log *log_alloc(void);
internal void log_release(Log *log);
internal void log_select(Log *log);
////////////////////////////////
//~ rjf: Log Building
internal void log_msg(LogMsgKind kind, String8 string);
internal void log_msgf(LogMsgKind kind, char *fmt, ...);
#define log_info(s) log_msg(LogMsgKind_Info, (s))
#define log_infof(fmt, ...) log_msgf(LogMsgKind_Info, (fmt), __VA_ARGS__)
#define log_user_error(s) log_msg(LogMsgKind_UserError, (s))
#define log_user_errorf(fmt, ...) log_msgf(LogMsgKind_UserError, (fmt), __VA_ARGS__)
#define LogInfoNamedBlock(s) DeferLoop(log_infof("%S:\n{\n", (s)), log_infof("}\n"))
#define LogInfoNamedBlockF(fmt, ...) DeferLoop((log_infof(fmt, __VA_ARGS__), log_infof(":\n{\n")), log_infof("}\n"))
////////////////////////////////
//~ rjf: Log Scopes
internal void log_scope_begin(void);
internal LogScopeResult log_scope_end(Arena *arena);
#endif // BASE_LOG_H

View File

@ -0,0 +1,21 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
internal void
set_thread_name(String8 string)
{
ProfThreadName("%.*s", str8_varg(string));
os_set_thread_name(string);
}
internal void
set_thread_namef(char *fmt, ...)
{
Temp scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 string = push_str8fv(scratch.arena, fmt, args);
set_thread_name(string);
va_end(args);
scratch_end(scratch);
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_MARKUP_H
#define BASE_MARKUP_H
internal void set_thread_name(String8 string);
internal void set_thread_namef(char *fmt, ...);
#define ThreadNameF(...) (set_thread_namef(__VA_ARGS__))
#define ThreadName(str) (set_thread_name(str))
#endif // BASE_MARKUP_H

View File

@ -0,0 +1,659 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Scalar Ops
internal F32
mix_1f32(F32 a, F32 b, F32 t)
{
F32 c = (a + (b-a) * Clamp(0.f, t, 1.f));
return c;
}
internal F64
mix_1f64(F64 a, F64 b, F64 t)
{
F64 c = (a + (b-a) * Clamp(0.0, t, 1.0));
return c;
}
////////////////////////////////
//~ rjf: Vector Ops
internal Vec2F32 vec_2f32(F32 x, F32 y) {Vec2F32 v = {x, y}; return v;}
internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x+b.x, a.y+b.y}; return c;}
internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x-b.x, a.y-b.y}; return c;}
internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x*b.x, a.y*b.y}; return c;}
internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b) {Vec2F32 c = {a.x/b.x, a.y/b.y}; return c;}
internal Vec2F32 scale_2f32(Vec2F32 v, F32 s) {Vec2F32 c = {v.x*s, v.y*s}; return c;}
internal F32 dot_2f32(Vec2F32 a, Vec2F32 b) {F32 c = a.x*b.x + a.y*b.y; return c;}
internal F32 length_squared_2f32(Vec2F32 v) {F32 c = v.x*v.x + v.y*v.y; return c;}
internal F32 length_2f32(Vec2F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y); return c;}
internal Vec2F32 normalize_2f32(Vec2F32 v) {v = scale_2f32(v, 1.f/length_2f32(v)); return v;}
internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t) {Vec2F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t)}; return c;}
internal Vec2S64 vec_2s64(S64 x, S64 y) {Vec2S64 v = {x, y}; return v;}
internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x+b.x, a.y+b.y}; return c;}
internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x-b.x, a.y-b.y}; return c;}
internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x*b.x, a.y*b.y}; return c;}
internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b) {Vec2S64 c = {a.x/b.x, a.y/b.y}; return c;}
internal Vec2S64 scale_2s64(Vec2S64 v, S64 s) {Vec2S64 c = {v.x*s, v.y*s}; return c;}
internal S64 dot_2s64(Vec2S64 a, Vec2S64 b) {S64 c = a.x*b.x + a.y*b.y; return c;}
internal S64 length_squared_2s64(Vec2S64 v) {S64 c = v.x*v.x + v.y*v.y; return c;}
internal S64 length_2s64(Vec2S64 v) {S64 c = (S64)sqrt_f64((F64)(v.x*v.x + v.y*v.y)); return c;}
internal Vec2S64 normalize_2s64(Vec2S64 v) {v = scale_2s64(v, (S64)(1.f/length_2s64(v))); return v;}
internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t) {Vec2S64 c = {(S64)mix_1f32((F32)a.x, (F32)b.x, t), (S64)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;}
internal Vec2S32 vec_2s32(S32 x, S32 y) {Vec2S32 v = {x, y}; return v;}
internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x+b.x, a.y+b.y}; return c;}
internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x-b.x, a.y-b.y}; return c;}
internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x*b.x, a.y*b.y}; return c;}
internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b) {Vec2S32 c = {a.x/b.x, a.y/b.y}; return c;}
internal Vec2S32 scale_2s32(Vec2S32 v, S32 s) {Vec2S32 c = {v.x*s, v.y*s}; return c;}
internal S32 dot_2s32(Vec2S32 a, Vec2S32 b) {S32 c = a.x*b.x + a.y*b.y; return c;}
internal S32 length_squared_2s32(Vec2S32 v) {S32 c = v.x*v.x + v.y*v.y; return c;}
internal S32 length_2s32(Vec2S32 v) {S32 c = (S32)sqrt_f32((F32)v.x*(F32)v.x + (F32)v.y*(F32)v.y); return c;}
internal Vec2S32 normalize_2s32(Vec2S32 v) {v = scale_2s32(v, (S32)(1.f/length_2s32(v))); return v;}
internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t) {Vec2S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;}
internal Vec2S16 vec_2s16(S16 x, S16 y) {Vec2S16 v = {x, y}; return v;}
internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x+b.x), (S16)(a.y+b.y)}; return c;}
internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x-b.x), (S16)(a.y-b.y)}; return c;}
internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x*b.x), (S16)(a.y*b.y)}; return c;}
internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b) {Vec2S16 c = {(S16)(a.x/b.x), (S16)(a.y/b.y)}; return c;}
internal Vec2S16 scale_2s16(Vec2S16 v, S16 s) {Vec2S16 c = {(S16)(v.x*s), (S16)(v.y*s)}; return c;}
internal S16 dot_2s16(Vec2S16 a, Vec2S16 b) {S16 c = a.x*b.x + a.y*b.y; return c;}
internal S16 length_squared_2s16(Vec2S16 v) {S16 c = v.x*v.x + v.y*v.y; return c;}
internal S16 length_2s16(Vec2S16 v) {S16 c = (S16)sqrt_f32((F32)(v.x*v.x + v.y*v.y)); return c;}
internal Vec2S16 normalize_2s16(Vec2S16 v) {v = scale_2s16(v, (S16)(1.f/length_2s16(v))); return v;}
internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t) {Vec2S16 c = {(S16)mix_1f32((F32)a.x, (F32)b.x, t), (S16)mix_1f32((F32)a.y, (F32)b.y, t)}; return c;}
internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z) {Vec3F32 v = {x, y, z}; return v;}
internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;}
internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;}
internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;}
internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;}
internal Vec3F32 scale_3f32(Vec3F32 v, F32 s) {Vec3F32 c = {v.x*s, v.y*s, v.z*s}; return c;}
internal F32 dot_3f32(Vec3F32 a, Vec3F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;}
internal F32 length_squared_3f32(Vec3F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;}
internal F32 length_3f32(Vec3F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z); return c;}
internal Vec3F32 normalize_3f32(Vec3F32 v) {v = scale_3f32(v, 1.f/length_3f32(v)); return v;}
internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t) {Vec3F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t)}; return c;}
internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b) {Vec3F32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;}
internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z) {Vec3S32 v = {x, y, z}; return v;}
internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x+b.x, a.y+b.y, a.z+b.z}; return c;}
internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x-b.x, a.y-b.y, a.z-b.z}; return c;}
internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x*b.x, a.y*b.y, a.z*b.z}; return c;}
internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.x/b.x, a.y/b.y, a.z/b.z}; return c;}
internal Vec3S32 scale_3s32(Vec3S32 v, S32 s) {Vec3S32 c = {v.x*s, v.y*s, v.z*s}; return c;}
internal S32 dot_3s32(Vec3S32 a, Vec3S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z; return c;}
internal S32 length_squared_3s32(Vec3S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z; return c;}
internal S32 length_3s32(Vec3S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z)); return c;}
internal Vec3S32 normalize_3s32(Vec3S32 v) {v = scale_3s32(v, (S32)(1.f/length_3s32(v))); return v;}
internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t) {Vec3S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t)}; return c;}
internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b) {Vec3S32 c = {a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x}; return c;}
internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w) {Vec4F32 v = {x, y, z, w}; return v;}
internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;}
internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;}
internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;}
internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b) {Vec4F32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;}
internal Vec4F32 scale_4f32(Vec4F32 v, F32 s) {Vec4F32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;}
internal F32 dot_4f32(Vec4F32 a, Vec4F32 b) {F32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;}
internal F32 length_squared_4f32(Vec4F32 v) {F32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;}
internal F32 length_4f32(Vec4F32 v) {F32 c = sqrt_f32(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w); return c;}
internal Vec4F32 normalize_4f32(Vec4F32 v) {v = scale_4f32(v, 1.f/length_4f32(v)); return v;}
internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t) {Vec4F32 c = {mix_1f32(a.x, b.x, t), mix_1f32(a.y, b.y, t), mix_1f32(a.z, b.z, t), mix_1f32(a.w, b.w, t)}; return c;}
internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w) {Vec4S32 v = {x, y, z, w}; return v;}
internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x+b.x, a.y+b.y, a.z+b.z, a.w+b.w}; return c;}
internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x-b.x, a.y-b.y, a.z-b.z, a.w-b.w}; return c;}
internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return c;}
internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b) {Vec4S32 c = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return c;}
internal Vec4S32 scale_4s32(Vec4S32 v, S32 s) {Vec4S32 c = {v.x*s, v.y*s, v.z*s, v.w*s}; return c;}
internal S32 dot_4s32(Vec4S32 a, Vec4S32 b) {S32 c = a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w; return c;}
internal S32 length_squared_4s32(Vec4S32 v) {S32 c = v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w; return c;}
internal S32 length_4s32(Vec4S32 v) {S32 c = (S32)sqrt_f32((F32)(v.x*v.x + v.y*v.y + v.z*v.z + v.w*v.w)); return c;}
internal Vec4S32 normalize_4s32(Vec4S32 v) {v = scale_4s32(v, (S32)(1.f/length_4s32(v))); return v;}
internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t) {Vec4S32 c = {(S32)mix_1f32((F32)a.x, (F32)b.x, t), (S32)mix_1f32((F32)a.y, (F32)b.y, t), (S32)mix_1f32((F32)a.z, (F32)b.z, t), (S32)mix_1f32((F32)a.w, (F32)b.w, t)}; return c;}
////////////////////////////////
//~ rjf: Matrix Ops
internal Mat3x3F32
mat_3x3f32(F32 diagonal)
{
Mat3x3F32 result = {0};
result.v[0][0] = diagonal;
result.v[1][1] = diagonal;
result.v[2][2] = diagonal;
return result;
}
internal Mat3x3F32
make_translate_3x3f32(Vec2F32 delta)
{
Mat3x3F32 mat = mat_3x3f32(1.f);
mat.v[2][0] = delta.x;
mat.v[2][1] = delta.y;
return mat;
}
internal Mat3x3F32
make_scale_3x3f32(Vec2F32 scale)
{
Mat3x3F32 mat = mat_3x3f32(1.f);
mat.v[0][0] = scale.x;
mat.v[1][1] = scale.y;
return mat;
}
internal Mat3x3F32
mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b)
{
Mat3x3F32 c = {0};
for(int j = 0; j < 3; j += 1)
{
for(int i = 0; i < 3; i += 1)
{
c.v[i][j] = (a.v[0][j]*b.v[i][0] +
a.v[1][j]*b.v[i][1] +
a.v[2][j]*b.v[i][2]);
}
}
return c;
}
internal Mat4x4F32
mat_4x4f32(F32 diagonal)
{
Mat4x4F32 result = {0};
result.v[0][0] = diagonal;
result.v[1][1] = diagonal;
result.v[2][2] = diagonal;
result.v[3][3] = diagonal;
return result;
}
internal Mat4x4F32
make_translate_4x4f32(Vec3F32 delta)
{
Mat4x4F32 result = mat_4x4f32(1.f);
result.v[3][0] = delta.x;
result.v[3][1] = delta.y;
result.v[3][2] = delta.z;
return result;
}
internal Mat4x4F32
make_scale_4x4f32(Vec3F32 scale)
{
Mat4x4F32 result = mat_4x4f32(1.f);
result.v[0][0] = scale.x;
result.v[1][1] = scale.y;
result.v[2][2] = scale.z;
return result;
}
internal Mat4x4F32
make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z)
{
Mat4x4F32 result = mat_4x4f32(1.f);
F32 tan_theta_over_2 = tan_f32(fov / 2);
result.v[0][0] = 1.f / tan_theta_over_2;
result.v[1][1] = aspect_ratio / tan_theta_over_2;
result.v[2][3] = 1.f;
result.v[2][2] = -(near_z + far_z) / (near_z - far_z);
result.v[3][2] = (2.f * near_z * far_z) / (near_z - far_z);
result.v[3][3] = 0.f;
return result;
}
internal Mat4x4F32
make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z)
{
Mat4x4F32 result = mat_4x4f32(1.f);
result.v[0][0] = 2.f / (right - left);
result.v[1][1] = 2.f / (top - bottom);
result.v[2][2] = 2.f / (far_z - near_z);
result.v[3][3] = 1.f;
result.v[3][0] = (left + right) / (left - right);
result.v[3][1] = (bottom + top) / (bottom - top);
result.v[3][2] = (near_z + far_z) / (near_z - far_z);
return result;
}
internal Mat4x4F32
make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up)
{
Mat4x4F32 result;
Vec3F32 f = normalize_3f32(sub_3f32(eye, center));
Vec3F32 s = normalize_3f32(cross_3f32(f, up));
Vec3F32 u = cross_3f32(s, f);
result.v[0][0] = s.x;
result.v[0][1] = u.x;
result.v[0][2] = -f.x;
result.v[0][3] = 0.0f;
result.v[1][0] = s.y;
result.v[1][1] = u.y;
result.v[1][2] = -f.y;
result.v[1][3] = 0.0f;
result.v[2][0] = s.z;
result.v[2][1] = u.z;
result.v[2][2] = -f.z;
result.v[2][3] = 0.0f;
result.v[3][0] = -dot_3f32(s, eye);
result.v[3][1] = -dot_3f32(u, eye);
result.v[3][2] = dot_3f32(f, eye);
result.v[3][3] = 1.0f;
return result;
}
internal Mat4x4F32
make_rotate_4x4f32(Vec3F32 axis, F32 turns)
{
Mat4x4F32 result = mat_4x4f32(1.f);
axis = normalize_3f32(axis);
F32 sin_theta = sin_f32(turns);
F32 cos_theta = cos_f32(turns);
F32 cos_value = 1.f - cos_theta;
result.v[0][0] = (axis.x * axis.x * cos_value) + cos_theta;
result.v[0][1] = (axis.x * axis.y * cos_value) + (axis.z * sin_theta);
result.v[0][2] = (axis.x * axis.z * cos_value) - (axis.y * sin_theta);
result.v[1][0] = (axis.y * axis.x * cos_value) - (axis.z * sin_theta);
result.v[1][1] = (axis.y * axis.y * cos_value) + cos_theta;
result.v[1][2] = (axis.y * axis.z * cos_value) + (axis.x * sin_theta);
result.v[2][0] = (axis.z * axis.x * cos_value) + (axis.y * sin_theta);
result.v[2][1] = (axis.z * axis.y * cos_value) - (axis.x * sin_theta);
result.v[2][2] = (axis.z * axis.z * cos_value) + cos_theta;
return result;
}
internal Mat4x4F32
mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b)
{
Mat4x4F32 c = {0};
for(int j = 0; j < 4; j += 1)
{
for(int i = 0; i < 4; i += 1)
{
c.v[i][j] = (a.v[0][j]*b.v[i][0] +
a.v[1][j]*b.v[i][1] +
a.v[2][j]*b.v[i][2] +
a.v[3][j]*b.v[i][3]);
}
}
return c;
}
internal Mat4x4F32
scale_4x4f32(Mat4x4F32 m, F32 scale)
{
for(int j = 0; j < 4; j += 1)
{
for(int i = 0; i < 4; i += 1)
{
m.v[i][j] *= scale;
}
}
return m;
}
internal Mat4x4F32
inverse_4x4f32(Mat4x4F32 m)
{
F32 coef00 = m.v[2][2] * m.v[3][3] - m.v[3][2] * m.v[2][3];
F32 coef02 = m.v[1][2] * m.v[3][3] - m.v[3][2] * m.v[1][3];
F32 coef03 = m.v[1][2] * m.v[2][3] - m.v[2][2] * m.v[1][3];
F32 coef04 = m.v[2][1] * m.v[3][3] - m.v[3][1] * m.v[2][3];
F32 coef06 = m.v[1][1] * m.v[3][3] - m.v[3][1] * m.v[1][3];
F32 coef07 = m.v[1][1] * m.v[2][3] - m.v[2][1] * m.v[1][3];
F32 coef08 = m.v[2][1] * m.v[3][2] - m.v[3][1] * m.v[2][2];
F32 coef10 = m.v[1][1] * m.v[3][2] - m.v[3][1] * m.v[1][2];
F32 coef11 = m.v[1][1] * m.v[2][2] - m.v[2][1] * m.v[1][2];
F32 coef12 = m.v[2][0] * m.v[3][3] - m.v[3][0] * m.v[2][3];
F32 coef14 = m.v[1][0] * m.v[3][3] - m.v[3][0] * m.v[1][3];
F32 coef15 = m.v[1][0] * m.v[2][3] - m.v[2][0] * m.v[1][3];
F32 coef16 = m.v[2][0] * m.v[3][2] - m.v[3][0] * m.v[2][2];
F32 coef18 = m.v[1][0] * m.v[3][2] - m.v[3][0] * m.v[1][2];
F32 coef19 = m.v[1][0] * m.v[2][2] - m.v[2][0] * m.v[1][2];
F32 coef20 = m.v[2][0] * m.v[3][1] - m.v[3][0] * m.v[2][1];
F32 coef22 = m.v[1][0] * m.v[3][1] - m.v[3][0] * m.v[1][1];
F32 coef23 = m.v[1][0] * m.v[2][1] - m.v[2][0] * m.v[1][1];
Vec4F32 fac0 = { coef00, coef00, coef02, coef03 };
Vec4F32 fac1 = { coef04, coef04, coef06, coef07 };
Vec4F32 fac2 = { coef08, coef08, coef10, coef11 };
Vec4F32 fac3 = { coef12, coef12, coef14, coef15 };
Vec4F32 fac4 = { coef16, coef16, coef18, coef19 };
Vec4F32 fac5 = { coef20, coef20, coef22, coef23 };
Vec4F32 vec0 = { m.v[1][0], m.v[0][0], m.v[0][0], m.v[0][0] };
Vec4F32 vec1 = { m.v[1][1], m.v[0][1], m.v[0][1], m.v[0][1] };
Vec4F32 vec2 = { m.v[1][2], m.v[0][2], m.v[0][2], m.v[0][2] };
Vec4F32 vec3 = { m.v[1][3], m.v[0][3], m.v[0][3], m.v[0][3] };
Vec4F32 inv0 = add_4f32(sub_4f32(mul_4f32(vec1, fac0), mul_4f32(vec2, fac1)), mul_4f32(vec3, fac2));
Vec4F32 inv1 = add_4f32(sub_4f32(mul_4f32(vec0, fac0), mul_4f32(vec2, fac3)), mul_4f32(vec3, fac4));
Vec4F32 inv2 = add_4f32(sub_4f32(mul_4f32(vec0, fac1), mul_4f32(vec1, fac3)), mul_4f32(vec3, fac5));
Vec4F32 inv3 = add_4f32(sub_4f32(mul_4f32(vec0, fac2), mul_4f32(vec1, fac4)), mul_4f32(vec2, fac5));
Vec4F32 sign_a = { +1, -1, +1, -1 };
Vec4F32 sign_b = { -1, +1, -1, +1 };
Mat4x4F32 inverse;
for(U32 i = 0; i < 4; i += 1)
{
inverse.v[0][i] = inv0.v[i] * sign_a.v[i];
inverse.v[1][i] = inv1.v[i] * sign_b.v[i];
inverse.v[2][i] = inv2.v[i] * sign_a.v[i];
inverse.v[3][i] = inv3.v[i] * sign_b.v[i];
}
Vec4F32 row0 = { inverse.v[0][0], inverse.v[1][0], inverse.v[2][0], inverse.v[3][0] };
Vec4F32 m0 = { m.v[0][0], m.v[0][1], m.v[0][2], m.v[0][3] };
Vec4F32 dot0 = mul_4f32(m0, row0);
F32 dot1 = (dot0.x + dot0.y) + (dot0.z + dot0.w);
F32 one_over_det = 1 / dot1;
return scale_4x4f32(inverse, one_over_det);
}
internal Mat4x4F32
derotate_4x4f32(Mat4x4F32 mat)
{
Vec3F32 scale =
{
length_3f32(v3f32(mat.v[0][0], mat.v[0][1], mat.v[0][2])),
length_3f32(v3f32(mat.v[1][0], mat.v[1][1], mat.v[1][2])),
length_3f32(v3f32(mat.v[2][0], mat.v[2][1], mat.v[2][2])),
};
mat.v[0][0] = scale.x;
mat.v[1][0] = 0.f;
mat.v[2][0] = 0.f;
mat.v[0][1] = 0.f;
mat.v[1][1] = scale.y;
mat.v[2][1] = 0.f;
mat.v[0][2] = 0.f;
mat.v[1][2] = 0.f;
mat.v[2][2] = scale.z;
return mat;
}
////////////////////////////////
//~ rjf: Range Ops
internal Rng1U32 rng_1u32(U32 min, U32 max) {Rng1U32 r = {min, max}; if(r.min > r.max) { Swap(U32, r.min, r.max); } return r;}
internal Rng1U32 shift_1u32(Rng1U32 r, U32 x) {r.min += x; r.max += x; return r;}
internal Rng1U32 pad_1u32(Rng1U32 r, U32 x) {r.min -= x; r.max += x; return r;}
internal U32 center_1u32(Rng1U32 r) {U32 c = (r.min+r.max)/2; return c;}
internal B32 contains_1u32(Rng1U32 r, U32 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal U32 dim_1u32(Rng1U32 r) {U32 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;}
internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b) {Rng1U32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal U32 clamp_1u32(Rng1U32 r, U32 v) {v = Clamp(r.min, v, r.max); return v;}
internal Rng1S32 rng_1s32(S32 min, S32 max) {Rng1S32 r = {min, max}; if(r.min > r.max) { Swap(S32, r.min, r.max); } return r;}
internal Rng1S32 shift_1s32(Rng1S32 r, S32 x) {r.min += x; r.max += x; return r;}
internal Rng1S32 pad_1s32(Rng1S32 r, S32 x) {r.min -= x; r.max += x; return r;}
internal S32 center_1s32(Rng1S32 r) {S32 c = (r.min+r.max)/2; return c;}
internal B32 contains_1s32(Rng1S32 r, S32 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal S32 dim_1s32(Rng1S32 r) {S32 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;}
internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b) {Rng1S32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal S32 clamp_1s32(Rng1S32 r, S32 v) {v = Clamp(r.min, v, r.max); return v;}
internal Rng1U64 rng_1u64(U64 min, U64 max) {Rng1U64 r = {min, max}; if(r.min > r.max) { Swap(U64, r.min, r.max); } return r;}
internal Rng1U64 shift_1u64(Rng1U64 r, U64 x) {r.min += x; r.max += x; return r;}
internal Rng1U64 pad_1u64(Rng1U64 r, U64 x) {r.min -= x; r.max += x; return r;}
internal U64 center_1u64(Rng1U64 r) {U64 c = (r.min+r.max)/2; return c;}
internal B32 contains_1u64(Rng1U64 r, U64 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal U64 dim_1u64(Rng1U64 r) {U64 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;}
internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b) {Rng1U64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal U64 clamp_1u64(Rng1U64 r, U64 v) {v = Clamp(r.min, v, r.max); return v;}
internal Rng1S64 rng_1s64(S64 min, S64 max) {Rng1S64 r = {min, max}; if(r.min > r.max) { Swap(S64, r.min, r.max); } return r;}
internal Rng1S64 shift_1s64(Rng1S64 r, S64 x) {r.min += x; r.max += x; return r;}
internal Rng1S64 pad_1s64(Rng1S64 r, S64 x) {r.min -= x; r.max += x; return r;}
internal S64 center_1s64(Rng1S64 r) {S64 c = (r.min+r.max)/2; return c;}
internal B32 contains_1s64(Rng1S64 r, S64 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal S64 dim_1s64(Rng1S64 r) {S64 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;}
internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b) {Rng1S64 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal S64 clamp_1s64(Rng1S64 r, S64 v) {v = Clamp(r.min, v, r.max); return v;}
internal Rng1F32 rng_1f32(F32 min, F32 max) {Rng1F32 r = {min, max}; if(r.min > r.max) { Swap(F32, r.min, r.max); } return r;}
internal Rng1F32 shift_1f32(Rng1F32 r, F32 x) {r.min += x; r.max += x; return r;}
internal Rng1F32 pad_1f32(Rng1F32 r, F32 x) {r.min -= x; r.max += x; return r;}
internal F32 center_1f32(Rng1F32 r) {F32 c = (r.min+r.max)/2; return c;}
internal B32 contains_1f32(Rng1F32 r, F32 x) {B32 c = (r.min <= x && x < r.max); return c;}
internal F32 dim_1f32(Rng1F32 r) {F32 c = ((r.max > r.min) ? (r.max - r.min) : 0); return c;}
internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Min(a.min, b.min), Max(a.max, b.max)}; return c;}
internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b) {Rng1F32 c = {Max(a.min, b.min), Min(a.max, b.max)}; return c;}
internal F32 clamp_1f32(Rng1F32 r, F32 v) {v = Clamp(r.min, v, r.max); return v;}
internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max) {Rng2S16 r = {min, max}; return r;}
internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x) {r.min = add_2s16(r.min, x); r.max = add_2s16(r.max, x); return r;}
internal Rng2S16 pad_2s16(Rng2S16 r, S16 x) {Vec2S16 xv = {x, x}; r.min = sub_2s16(r.min, xv); r.max = add_2s16(r.max, xv); return r;}
internal Vec2S16 center_2s16(Rng2S16 r) {Vec2S16 c = {(S16)((r.min.x+r.max.x)/2), (S16)((r.min.y+r.max.y)/2)}; return c;}
internal B32 contains_2s16(Rng2S16 r, Vec2S16 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;}
internal Vec2S16 dim_2s16(Rng2S16 r) {Vec2S16 dim = {(S16)(((r.max.x > r.min.x) ? (r.max.x - r.min.x) : 0)), (S16)(((r.max.y > r.min.y) ? (r.max.y - r.min.y) : 0))}; return dim;}
internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;}
internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b) {Rng2S16 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;}
internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;}
internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max) {Rng2S32 r = {min, max}; return r;}
internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x) {r.min = add_2s32(r.min, x); r.max = add_2s32(r.max, x); return r;}
internal Rng2S32 pad_2s32(Rng2S32 r, S32 x) {Vec2S32 xv = {x, x}; r.min = sub_2s32(r.min, xv); r.max = add_2s32(r.max, xv); return r;}
internal Vec2S32 center_2s32(Rng2S32 r) {Vec2S32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;}
internal B32 contains_2s32(Rng2S32 r, Vec2S32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;}
internal Vec2S32 dim_2s32(Rng2S32 r) {Vec2S32 dim = {((r.max.x > r.min.x) ? (r.max.x - r.min.x) : 0), ((r.max.y > r.min.y) ? (r.max.y - r.min.y) : 0)}; return dim;}
internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;}
internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b) {Rng2S32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;}
internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;}
internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max) {Rng2S64 r = {min, max}; return r;}
internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x) {r.min = add_2s64(r.min, x); r.max = add_2s64(r.max, x); return r;}
internal Rng2S64 pad_2s64(Rng2S64 r, S64 x) {Vec2S64 xv = {x, x}; r.min = sub_2s64(r.min, xv); r.max = add_2s64(r.max, xv); return r;}
internal Vec2S64 center_2s64(Rng2S64 r) {Vec2S64 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;}
internal B32 contains_2s64(Rng2S64 r, Vec2S64 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;}
internal Vec2S64 dim_2s64(Rng2S64 r) {Vec2S64 dim = {((r.max.x > r.min.x) ? (r.max.x - r.min.x) : 0), ((r.max.y > r.min.y) ? (r.max.y - r.min.y) : 0)}; return dim;}
internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;}
internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b) {Rng2S64 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;}
internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;}
internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max) {Rng2F32 r = {min, max}; return r;}
internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x) {r.min = add_2f32(r.min, x); r.max = add_2f32(r.max, x); return r;}
internal Rng2F32 pad_2f32(Rng2F32 r, F32 x) {Vec2F32 xv = {x, x}; r.min = sub_2f32(r.min, xv); r.max = add_2f32(r.max, xv); return r;}
internal Vec2F32 center_2f32(Rng2F32 r) {Vec2F32 c = {(r.min.x+r.max.x)/2, (r.min.y+r.max.y)/2}; return c;}
internal B32 contains_2f32(Rng2F32 r, Vec2F32 x) {B32 c = (r.min.x <= x.x && x.x < r.max.x && r.min.y <= x.y && x.y < r.max.y); return c;}
internal Vec2F32 dim_2f32(Rng2F32 r) {Vec2F32 dim = {((r.max.x > r.min.x) ? (r.max.x - r.min.x) : 0), ((r.max.y > r.min.y) ? (r.max.y - r.min.y) : 0)}; return dim;}
internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Min(a.min.x, b.min.x); c.p0.y = Min(a.min.y, b.min.y); c.p1.x = Max(a.max.x, b.max.x); c.p1.y = Max(a.max.y, b.max.y); return c;}
internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b) {Rng2F32 c; c.p0.x = Max(a.min.x, b.min.x); c.p0.y = Max(a.min.y, b.min.y); c.p1.x = Min(a.max.x, b.max.x); c.p1.y = Min(a.max.y, b.max.y); return c;}
internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v) {v.x = Clamp(r.min.x, v.x, r.max.x); v.y = Clamp(r.min.y, v.y, r.max.y); return v;}
////////////////////////////////
//~ rjf: Miscellaneous Ops
internal Vec3F32
hsv_from_rgb(Vec3F32 rgb)
{
F32 c_max = Max(rgb.x, Max(rgb.y, rgb.z));
F32 c_min = Min(rgb.x, Min(rgb.y, rgb.z));
F32 delta = c_max - c_min;
F32 h = ((delta == 0.f) ? 0.f :
(c_max == rgb.x) ? mod_f32((rgb.y - rgb.z)/delta + 6.f, 6.f) :
(c_max == rgb.y) ? (rgb.z - rgb.x)/delta + 2.f :
(c_max == rgb.z) ? (rgb.x - rgb.y)/delta + 4.f :
0.f);
F32 s = (c_max == 0.f) ? 0.f : (delta/c_max);
F32 v = c_max;
Vec3F32 hsv = {h/6.f, s, v};
return hsv;
}
internal Vec3F32
rgb_from_hsv(Vec3F32 hsv)
{
F32 h = mod_f32(hsv.x * 360.f, 360.f);
F32 s = hsv.y;
F32 v = hsv.z;
F32 c = v*s;
F32 x = c*(1.f - abs_f32(mod_f32(h/60.f, 2.f) - 1.f));
F32 m = v - c;
F32 r = 0;
F32 g = 0;
F32 b = 0;
if ((h >= 0.f && h < 60.f) || (h >= 360.f && h < 420.f)){
r = c;
g = x;
b = 0;
}
else if (h >= 60.f && h < 120.f){
r = x;
g = c;
b = 0;
}
else if (h >= 120.f && h < 180.f){
r = 0;
g = c;
b = x;
}
else if (h >= 180.f && h < 240.f){
r = 0;
g = x;
b = c;
}
else if (h >= 240.f && h < 300.f){
r = x;
g = 0;
b = c;
}
else if ((h >= 300.f && h <= 360.f) || (h >= -60.f && h <= 0.f)){
r = c;
g = 0;
b = x;
}
Vec3F32 rgb = {r + m, g + m, b + m};
return(rgb);
}
internal Vec4F32
hsva_from_rgba(Vec4F32 rgba)
{
Vec3F32 rgb = v3f32(rgba.x, rgba.y, rgba.z);
Vec3F32 hsv = hsv_from_rgb(rgb);
Vec4F32 hsva = v4f32(hsv.x, hsv.y, hsv.z, rgba.w);
return hsva;
}
internal Vec4F32
rgba_from_hsva(Vec4F32 hsva)
{
Vec3F32 hsv = v3f32(hsva.x, hsva.y, hsva.z);
Vec3F32 rgb = rgb_from_hsv(hsv);
Vec4F32 rgba = v4f32(rgb.x, rgb.y, rgb.z, hsva.w);
return rgba;
}
internal Vec4F32
rgba_from_u32(U32 hex)
{
Vec4F32 result = v4f32(((hex&0xff000000)>>24)/255.f,
((hex&0x00ff0000)>>16)/255.f,
((hex&0x0000ff00)>> 8)/255.f,
((hex&0x000000ff)>> 0)/255.f);
return result;
}
internal U32
u32_from_rgba(Vec4F32 rgba)
{
U32 result = 0;
result |= ((U32)((U8)(rgba.x*255.f))) << 24;
result |= ((U32)((U8)(rgba.y*255.f))) << 16;
result |= ((U32)((U8)(rgba.z*255.f))) << 8;
result |= ((U32)((U8)(rgba.w*255.f))) << 0;
return result;
}
////////////////////////////////
//~ rjf: List Type Functions
internal void
rng1u64_list_push(Arena *arena, Rng1U64List *list, Rng1U64 rng)
{
Rng1U64Node *n = push_array(arena, Rng1U64Node, 1);
MemoryCopyStruct(&n->v, &rng);
SLLQueuePush(list->first, list->last, n);
list->count += 1;
}
internal void
rng1u64_list_concat(Rng1U64List *list, Rng1U64List *to_concat)
{
if(to_concat->first)
{
if(list->first)
{
list->last->next = to_concat->first;
list->last = to_concat->last;
}
else
{
list->first = to_concat->first;
list->last = to_concat->last;
}
MemoryZeroStruct(to_concat);
}
}
internal Rng1U64Array
rng1u64_array_from_list(Arena *arena, Rng1U64List *list)
{
Rng1U64Array arr = {0};
arr.count = list->count;
arr.v = push_array_no_zero(arena, Rng1U64, arr.count);
U64 idx = 0;
for(Rng1U64Node *n = list->first; n != 0; n = n->next)
{
arr.v[idx] = n->v;
idx += 1;
}
return arr;
}
internal void
rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng)
{
Rng1S64Node *n = push_array(arena, Rng1S64Node, 1);
MemoryCopyStruct(&n->v, &rng);
SLLQueuePush(list->first, list->last, n);
list->count += 1;
}
internal Rng1S64Array
rng1s64_array_from_list(Arena *arena, Rng1S64List *list)
{
Rng1S64Array arr = {0};
arr.count = list->count;
arr.v = push_array_no_zero(arena, Rng1S64, arr.count);
U64 idx = 0;
for(Rng1S64Node *n = list->first; n != 0; n = n->next)
{
arr.v[idx] = n->v;
idx += 1;
}
return arr;
}

View File

@ -0,0 +1,675 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_MATH_H
#define BASE_MATH_H
////////////////////////////////
//~ rjf: Vector Types
//- rjf: 2-vectors
typedef union Vec2F32 Vec2F32;
union Vec2F32
{
struct
{
F32 x;
F32 y;
};
F32 v[2];
};
typedef union Vec2S64 Vec2S64;
union Vec2S64
{
struct
{
S64 x;
S64 y;
};
S64 v[2];
};
typedef union Vec2S32 Vec2S32;
union Vec2S32
{
struct
{
S32 x;
S32 y;
};
S32 v[2];
};
typedef union Vec2S16 Vec2S16;
union Vec2S16
{
struct
{
S16 x;
S16 y;
};
S16 v[2];
};
//- rjf: 3-vectors
typedef union Vec3F32 Vec3F32;
union Vec3F32
{
struct
{
F32 x;
F32 y;
F32 z;
};
struct
{
Vec2F32 xy;
F32 _z0;
};
struct
{
F32 _x0;
Vec2F32 yz;
};
F32 v[3];
};
typedef union Vec3S32 Vec3S32;
union Vec3S32
{
struct
{
S32 x;
S32 y;
S32 z;
};
struct
{
Vec2S32 xy;
S32 _z0;
};
struct
{
S32 _x0;
Vec2S32 yz;
};
S32 v[3];
};
//- rjf: 4-vectors
typedef union Vec4F32 Vec4F32;
union Vec4F32
{
struct
{
F32 x;
F32 y;
F32 z;
F32 w;
};
struct
{
Vec2F32 xy;
Vec2F32 zw;
};
struct
{
Vec3F32 xyz;
F32 _z0;
};
struct
{
F32 _x0;
Vec3F32 yzw;
};
F32 v[4];
};
typedef union Vec4S32 Vec4S32;
union Vec4S32
{
struct
{
S32 x;
S32 y;
S32 z;
S32 w;
};
struct
{
Vec2S32 xy;
Vec2S32 zw;
};
struct
{
Vec3S32 xyz;
S32 _z0;
};
struct
{
S32 _x0;
Vec3S32 yzw;
};
S32 v[4];
};
////////////////////////////////
//~ rjf: Matrix Types
typedef struct Mat3x3F32 Mat3x3F32;
struct Mat3x3F32
{
F32 v[3][3];
};
typedef struct Mat4x4F32 Mat4x4F32;
struct Mat4x4F32
{
F32 v[4][4];
};
////////////////////////////////
//~ rjf: Range Types
//- rjf: 1-range
typedef union Rng1U32 Rng1U32;
union Rng1U32
{
struct
{
U32 min;
U32 max;
};
U32 v[2];
};
typedef union Rng1S32 Rng1S32;
union Rng1S32
{
struct
{
S32 min;
S32 max;
};
S32 v[2];
};
typedef union Rng1U64 Rng1U64;
union Rng1U64
{
struct
{
U64 min;
U64 max;
};
U64 v[2];
};
typedef union Rng1S64 Rng1S64;
union Rng1S64
{
struct
{
S64 min;
S64 max;
};
S64 v[2];
};
typedef union Rng1F32 Rng1F32;
union Rng1F32
{
struct
{
F32 min;
F32 max;
};
F32 v[2];
};
//- rjf: 2-range (rectangles)
typedef union Rng2S16 Rng2S16;
union Rng2S16
{
struct
{
Vec2S16 min;
Vec2S16 max;
};
struct
{
Vec2S16 p0;
Vec2S16 p1;
};
struct
{
S16 x0;
S16 y0;
S16 x1;
S16 y1;
};
Vec2S16 v[2];
};
typedef union Rng2S32 Rng2S32;
union Rng2S32
{
struct
{
Vec2S32 min;
Vec2S32 max;
};
struct
{
Vec2S32 p0;
Vec2S32 p1;
};
struct
{
S32 x0;
S32 y0;
S32 x1;
S32 y1;
};
Vec2S32 v[2];
};
typedef union Rng2F32 Rng2F32;
union Rng2F32
{
struct
{
Vec2F32 min;
Vec2F32 max;
};
struct
{
Vec2F32 p0;
Vec2F32 p1;
};
struct
{
F32 x0;
F32 y0;
F32 x1;
F32 y1;
};
Vec2F32 v[2];
};
typedef union Rng2S64 Rng2S64;
union Rng2S64
{
struct
{
Vec2S64 min;
Vec2S64 max;
};
struct
{
Vec2S64 p0;
Vec2S64 p1;
};
struct
{
S64 x0;
S64 y0;
S64 x1;
S64 y1;
};
Vec2S64 v[2];
};
////////////////////////////////
//~ rjf: List Types
typedef struct Rng1U64Node Rng1U64Node;
struct Rng1U64Node
{
Rng1U64Node *next;
Rng1U64 v;
};
typedef struct Rng1U64List Rng1U64List;
struct Rng1U64List
{
U64 count;
Rng1U64Node *first;
Rng1U64Node *last;
};
typedef struct Rng1U64Array Rng1U64Array;
struct Rng1U64Array
{
Rng1U64 *v;
U64 count;
};
typedef struct Rng1S64Node Rng1S64Node;
struct Rng1S64Node
{
Rng1S64Node *next;
Rng1S64 v;
};
typedef struct Rng1S64List Rng1S64List;
struct Rng1S64List
{
Rng1S64Node *first;
Rng1S64Node *last;
U64 count;
};
typedef struct Rng1S64Array Rng1S64Array;
struct Rng1S64Array
{
Rng1S64 *v;
U64 count;
};
////////////////////////////////
//~ rjf: Scalar Ops
#define abs_s64(v) (S64)llabs(v)
#define sqrt_f32(v) sqrtf(v)
#define mod_f32(a, b) fmodf((a), (b))
#define pow_f32(b, e) powf((b), (e))
#define ceil_f32(v) ceilf(v)
#define floor_f32(v) floorf(v)
#define round_f32(v) roundf(v)
#define abs_f32(v) fabsf(v)
#define radians_from_turns_f32(v) ((v)*2*3.1415926535897f)
#define turns_from_radians_f32(v) ((v)/2*3.1415926535897f)
#define degrees_from_turns_f32(v) ((v)*360.f)
#define turns_from_degrees_f32(v) ((v)/360.f)
#define degrees_from_radians_f32(v) (degrees_from_turns_f32(turns_from_radians_f32(v)))
#define radians_from_degrees_f32(v) (radians_from_turns_f32(turns_from_degrees_f32(v)))
#define sin_f32(v) sinf(radians_from_turns_f32(v))
#define cos_f32(v) cosf(radians_from_turns_f32(v))
#define tan_f32(v) tanf(radians_from_turns_f32(v))
#define sqrt_f64(v) sqrt(v)
#define mod_f64(a, b) fmod((a), (b))
#define pow_f64(b, e) pow((b), (e))
#define ceil_f64(v) ceil(v)
#define floor_f64(v) floor(v)
#define round_f64(v) round(v)
#define abs_f64(v) fabs(v)
#define radians_from_turns_f64(v) ((v)*2*3.1415926535897)
#define turns_from_radians_f64(v) ((v)/2*3.1415926535897)
#define degrees_from_turns_f64(v) ((v)*360.0)
#define turns_from_degrees_f64(v) ((v)/360.0)
#define degrees_from_radians_f64(v) (degrees_from_turns_f64(turns_from_radians_f64(v)))
#define radians_from_degrees_f64(v) (radians_from_turns_f64(turns_from_degrees_f64(v)))
#define sin_f64(v) sin(radians_from_turns_f64(v))
#define cos_f64(v) cos(radians_from_turns_f64(v))
#define tan_f64(v) tan(radians_from_turns_f64(v))
internal F32 mix_1f32(F32 a, F32 b, F32 t);
internal F64 mix_1f64(F64 a, F64 b, F64 t);
////////////////////////////////
//~ rjf: Vector Ops
#define v2f32(x, y) vec_2f32((x), (y))
internal Vec2F32 vec_2f32(F32 x, F32 y);
internal Vec2F32 add_2f32(Vec2F32 a, Vec2F32 b);
internal Vec2F32 sub_2f32(Vec2F32 a, Vec2F32 b);
internal Vec2F32 mul_2f32(Vec2F32 a, Vec2F32 b);
internal Vec2F32 div_2f32(Vec2F32 a, Vec2F32 b);
internal Vec2F32 scale_2f32(Vec2F32 v, F32 s);
internal F32 dot_2f32(Vec2F32 a, Vec2F32 b);
internal F32 length_squared_2f32(Vec2F32 v);
internal F32 length_2f32(Vec2F32 v);
internal Vec2F32 normalize_2f32(Vec2F32 v);
internal Vec2F32 mix_2f32(Vec2F32 a, Vec2F32 b, F32 t);
#define v2s64(x, y) vec_2s64((x), (y))
internal Vec2S64 vec_2s64(S64 x, S64 y);
internal Vec2S64 add_2s64(Vec2S64 a, Vec2S64 b);
internal Vec2S64 sub_2s64(Vec2S64 a, Vec2S64 b);
internal Vec2S64 mul_2s64(Vec2S64 a, Vec2S64 b);
internal Vec2S64 div_2s64(Vec2S64 a, Vec2S64 b);
internal Vec2S64 scale_2s64(Vec2S64 v, S64 s);
internal S64 dot_2s64(Vec2S64 a, Vec2S64 b);
internal S64 length_squared_2s64(Vec2S64 v);
internal S64 length_2s64(Vec2S64 v);
internal Vec2S64 normalize_2s64(Vec2S64 v);
internal Vec2S64 mix_2s64(Vec2S64 a, Vec2S64 b, F32 t);
#define v2s32(x, y) vec_2s32((x), (y))
internal Vec2S32 vec_2s32(S32 x, S32 y);
internal Vec2S32 add_2s32(Vec2S32 a, Vec2S32 b);
internal Vec2S32 sub_2s32(Vec2S32 a, Vec2S32 b);
internal Vec2S32 mul_2s32(Vec2S32 a, Vec2S32 b);
internal Vec2S32 div_2s32(Vec2S32 a, Vec2S32 b);
internal Vec2S32 scale_2s32(Vec2S32 v, S32 s);
internal S32 dot_2s32(Vec2S32 a, Vec2S32 b);
internal S32 length_squared_2s32(Vec2S32 v);
internal S32 length_2s32(Vec2S32 v);
internal Vec2S32 normalize_2s32(Vec2S32 v);
internal Vec2S32 mix_2s32(Vec2S32 a, Vec2S32 b, F32 t);
#define v2s16(x, y) vec_2s16((x), (y))
internal Vec2S16 vec_2s16(S16 x, S16 y);
internal Vec2S16 add_2s16(Vec2S16 a, Vec2S16 b);
internal Vec2S16 sub_2s16(Vec2S16 a, Vec2S16 b);
internal Vec2S16 mul_2s16(Vec2S16 a, Vec2S16 b);
internal Vec2S16 div_2s16(Vec2S16 a, Vec2S16 b);
internal Vec2S16 scale_2s16(Vec2S16 v, S16 s);
internal S16 dot_2s16(Vec2S16 a, Vec2S16 b);
internal S16 length_squared_2s16(Vec2S16 v);
internal S16 length_2s16(Vec2S16 v);
internal Vec2S16 normalize_2s16(Vec2S16 v);
internal Vec2S16 mix_2s16(Vec2S16 a, Vec2S16 b, F32 t);
#define v3f32(x, y, z) vec_3f32((x), (y), (z))
internal Vec3F32 vec_3f32(F32 x, F32 y, F32 z);
internal Vec3F32 add_3f32(Vec3F32 a, Vec3F32 b);
internal Vec3F32 sub_3f32(Vec3F32 a, Vec3F32 b);
internal Vec3F32 mul_3f32(Vec3F32 a, Vec3F32 b);
internal Vec3F32 div_3f32(Vec3F32 a, Vec3F32 b);
internal Vec3F32 scale_3f32(Vec3F32 v, F32 s);
internal F32 dot_3f32(Vec3F32 a, Vec3F32 b);
internal F32 length_squared_3f32(Vec3F32 v);
internal F32 length_3f32(Vec3F32 v);
internal Vec3F32 normalize_3f32(Vec3F32 v);
internal Vec3F32 mix_3f32(Vec3F32 a, Vec3F32 b, F32 t);
internal Vec3F32 cross_3f32(Vec3F32 a, Vec3F32 b);
#define v3s32(x, y, z) vec_3s32((x), (y), (z))
internal Vec3S32 vec_3s32(S32 x, S32 y, S32 z);
internal Vec3S32 add_3s32(Vec3S32 a, Vec3S32 b);
internal Vec3S32 sub_3s32(Vec3S32 a, Vec3S32 b);
internal Vec3S32 mul_3s32(Vec3S32 a, Vec3S32 b);
internal Vec3S32 div_3s32(Vec3S32 a, Vec3S32 b);
internal Vec3S32 scale_3s32(Vec3S32 v, S32 s);
internal S32 dot_3s32(Vec3S32 a, Vec3S32 b);
internal S32 length_squared_3s32(Vec3S32 v);
internal S32 length_3s32(Vec3S32 v);
internal Vec3S32 normalize_3s32(Vec3S32 v);
internal Vec3S32 mix_3s32(Vec3S32 a, Vec3S32 b, F32 t);
internal Vec3S32 cross_3s32(Vec3S32 a, Vec3S32 b);
#define v4f32(x, y, z, w) vec_4f32((x), (y), (z), (w))
internal Vec4F32 vec_4f32(F32 x, F32 y, F32 z, F32 w);
internal Vec4F32 add_4f32(Vec4F32 a, Vec4F32 b);
internal Vec4F32 sub_4f32(Vec4F32 a, Vec4F32 b);
internal Vec4F32 mul_4f32(Vec4F32 a, Vec4F32 b);
internal Vec4F32 div_4f32(Vec4F32 a, Vec4F32 b);
internal Vec4F32 scale_4f32(Vec4F32 v, F32 s);
internal F32 dot_4f32(Vec4F32 a, Vec4F32 b);
internal F32 length_squared_4f32(Vec4F32 v);
internal F32 length_4f32(Vec4F32 v);
internal Vec4F32 normalize_4f32(Vec4F32 v);
internal Vec4F32 mix_4f32(Vec4F32 a, Vec4F32 b, F32 t);
#define v4s32(x, y, z, w) vec_4s32((x), (y), (z), (w))
internal Vec4S32 vec_4s32(S32 x, S32 y, S32 z, S32 w);
internal Vec4S32 add_4s32(Vec4S32 a, Vec4S32 b);
internal Vec4S32 sub_4s32(Vec4S32 a, Vec4S32 b);
internal Vec4S32 mul_4s32(Vec4S32 a, Vec4S32 b);
internal Vec4S32 div_4s32(Vec4S32 a, Vec4S32 b);
internal Vec4S32 scale_4s32(Vec4S32 v, S32 s);
internal S32 dot_4s32(Vec4S32 a, Vec4S32 b);
internal S32 length_squared_4s32(Vec4S32 v);
internal S32 length_4s32(Vec4S32 v);
internal Vec4S32 normalize_4s32(Vec4S32 v);
internal Vec4S32 mix_4s32(Vec4S32 a, Vec4S32 b, F32 t);
////////////////////////////////
//~ rjf: Matrix Ops
internal Mat3x3F32 mat_3x3f32(F32 diagonal);
internal Mat3x3F32 make_translate_3x3f32(Vec2F32 delta);
internal Mat3x3F32 make_scale_3x3f32(Vec2F32 scale);
internal Mat3x3F32 mul_3x3f32(Mat3x3F32 a, Mat3x3F32 b);
internal Mat4x4F32 mat_4x4f32(F32 diagonal);
internal Mat4x4F32 make_translate_4x4f32(Vec3F32 delta);
internal Mat4x4F32 make_scale_4x4f32(Vec3F32 scale);
internal Mat4x4F32 make_perspective_4x4f32(F32 fov, F32 aspect_ratio, F32 near_z, F32 far_z);
internal Mat4x4F32 make_orthographic_4x4f32(F32 left, F32 right, F32 bottom, F32 top, F32 near_z, F32 far_z);
internal Mat4x4F32 make_look_at_4x4f32(Vec3F32 eye, Vec3F32 center, Vec3F32 up);
internal Mat4x4F32 make_rotate_4x4f32(Vec3F32 axis, F32 turns);
internal Mat4x4F32 mul_4x4f32(Mat4x4F32 a, Mat4x4F32 b);
internal Mat4x4F32 scale_4x4f32(Mat4x4F32 m, F32 scale);
internal Mat4x4F32 inverse_4x4f32(Mat4x4F32 m);
internal Mat4x4F32 derotate_4x4f32(Mat4x4F32 mat);
////////////////////////////////
//~ rjf: Range Ops
#define r1u32(min, max) rng_1u32((min), (max))
internal Rng1U32 rng_1u32(U32 min, U32 max);
internal Rng1U32 shift_1u32(Rng1U32 r, U32 x);
internal Rng1U32 pad_1u32(Rng1U32 r, U32 x);
internal U32 center_1u32(Rng1U32 r);
internal B32 contains_1u32(Rng1U32 r, U32 x);
internal U32 dim_1u32(Rng1U32 r);
internal Rng1U32 union_1u32(Rng1U32 a, Rng1U32 b);
internal Rng1U32 intersect_1u32(Rng1U32 a, Rng1U32 b);
internal U32 clamp_1u32(Rng1U32 r, U32 v);
#define r1s32(min, max) rng_1s32((min), (max))
internal Rng1S32 rng_1s32(S32 min, S32 max);
internal Rng1S32 shift_1s32(Rng1S32 r, S32 x);
internal Rng1S32 pad_1s32(Rng1S32 r, S32 x);
internal S32 center_1s32(Rng1S32 r);
internal B32 contains_1s32(Rng1S32 r, S32 x);
internal S32 dim_1s32(Rng1S32 r);
internal Rng1S32 union_1s32(Rng1S32 a, Rng1S32 b);
internal Rng1S32 intersect_1s32(Rng1S32 a, Rng1S32 b);
internal S32 clamp_1s32(Rng1S32 r, S32 v);
#define r1u64(min, max) rng_1u64((min), (max))
internal Rng1U64 rng_1u64(U64 min, U64 max);
internal Rng1U64 shift_1u64(Rng1U64 r, U64 x);
internal Rng1U64 pad_1u64(Rng1U64 r, U64 x);
internal U64 center_1u64(Rng1U64 r);
internal B32 contains_1u64(Rng1U64 r, U64 x);
internal U64 dim_1u64(Rng1U64 r);
internal Rng1U64 union_1u64(Rng1U64 a, Rng1U64 b);
internal Rng1U64 intersect_1u64(Rng1U64 a, Rng1U64 b);
internal U64 clamp_1u64(Rng1U64 r, U64 v);
#define r1s64(min, max) rng_1s64((min), (max))
internal Rng1S64 rng_1s64(S64 min, S64 max);
internal Rng1S64 shift_1s64(Rng1S64 r, S64 x);
internal Rng1S64 pad_1s64(Rng1S64 r, S64 x);
internal S64 center_1s64(Rng1S64 r);
internal B32 contains_1s64(Rng1S64 r, S64 x);
internal S64 dim_1s64(Rng1S64 r);
internal Rng1S64 union_1s64(Rng1S64 a, Rng1S64 b);
internal Rng1S64 intersect_1s64(Rng1S64 a, Rng1S64 b);
internal S64 clamp_1s64(Rng1S64 r, S64 v);
#define r1f32(min, max) rng_1f32((min), (max))
internal Rng1F32 rng_1f32(F32 min, F32 max);
internal Rng1F32 shift_1f32(Rng1F32 r, F32 x);
internal Rng1F32 pad_1f32(Rng1F32 r, F32 x);
internal F32 center_1f32(Rng1F32 r);
internal B32 contains_1f32(Rng1F32 r, F32 x);
internal F32 dim_1f32(Rng1F32 r);
internal Rng1F32 union_1f32(Rng1F32 a, Rng1F32 b);
internal Rng1F32 intersect_1f32(Rng1F32 a, Rng1F32 b);
internal F32 clamp_1f32(Rng1F32 r, F32 v);
#define r2s16(min, max) rng_2s16((min), (max))
#define r2s16p(x, y, z, w) r2s16(v2s16((x), (y)), v2s16((z), (w)))
internal Rng2S16 rng_2s16(Vec2S16 min, Vec2S16 max);
internal Rng2S16 shift_2s16(Rng2S16 r, Vec2S16 x);
internal Rng2S16 pad_2s16(Rng2S16 r, S16 x);
internal Vec2S16 center_2s16(Rng2S16 r);
internal B32 contains_2s16(Rng2S16 r, Vec2S16 x);
internal Vec2S16 dim_2s16(Rng2S16 r);
internal Rng2S16 union_2s16(Rng2S16 a, Rng2S16 b);
internal Rng2S16 intersect_2s16(Rng2S16 a, Rng2S16 b);
internal Vec2S16 clamp_2s16(Rng2S16 r, Vec2S16 v);
#define r2s32(min, max) rng_2s32((min), (max))
#define r2s32p(x, y, z, w) r2s32(v2s32((x), (y)), v2s32((z), (w)))
internal Rng2S32 rng_2s32(Vec2S32 min, Vec2S32 max);
internal Rng2S32 shift_2s32(Rng2S32 r, Vec2S32 x);
internal Rng2S32 pad_2s32(Rng2S32 r, S32 x);
internal Vec2S32 center_2s32(Rng2S32 r);
internal B32 contains_2s32(Rng2S32 r, Vec2S32 x);
internal Vec2S32 dim_2s32(Rng2S32 r);
internal Rng2S32 union_2s32(Rng2S32 a, Rng2S32 b);
internal Rng2S32 intersect_2s32(Rng2S32 a, Rng2S32 b);
internal Vec2S32 clamp_2s32(Rng2S32 r, Vec2S32 v);
#define r2s64(min, max) rng_2s64((min), (max))
#define r2s64p(x, y, z, w) r2s64(v2s64((x), (y)), v2s64((z), (w)))
internal Rng2S64 rng_2s64(Vec2S64 min, Vec2S64 max);
internal Rng2S64 shift_2s64(Rng2S64 r, Vec2S64 x);
internal Rng2S64 pad_2s64(Rng2S64 r, S64 x);
internal Vec2S64 center_2s64(Rng2S64 r);
internal B32 contains_2s64(Rng2S64 r, Vec2S64 x);
internal Vec2S64 dim_2s64(Rng2S64 r);
internal Rng2S64 union_2s64(Rng2S64 a, Rng2S64 b);
internal Rng2S64 intersect_2s64(Rng2S64 a, Rng2S64 b);
internal Vec2S64 clamp_2s64(Rng2S64 r, Vec2S64 v);
#define r2f32(min, max) rng_2f32((min), (max))
#define r2f32p(x, y, z, w) r2f32(v2f32((x), (y)), v2f32((z), (w)))
internal Rng2F32 rng_2f32(Vec2F32 min, Vec2F32 max);
internal Rng2F32 shift_2f32(Rng2F32 r, Vec2F32 x);
internal Rng2F32 pad_2f32(Rng2F32 r, F32 x);
internal Vec2F32 center_2f32(Rng2F32 r);
internal B32 contains_2f32(Rng2F32 r, Vec2F32 x);
internal Vec2F32 dim_2f32(Rng2F32 r);
internal Rng2F32 union_2f32(Rng2F32 a, Rng2F32 b);
internal Rng2F32 intersect_2f32(Rng2F32 a, Rng2F32 b);
internal Vec2F32 clamp_2f32(Rng2F32 r, Vec2F32 v);
////////////////////////////////
//~ rjf: Miscellaneous Ops
internal Vec3F32 hsv_from_rgb(Vec3F32 rgb);
internal Vec3F32 rgb_from_hsv(Vec3F32 hsv);
internal Vec4F32 hsva_from_rgba(Vec4F32 rgba);
internal Vec4F32 rgba_from_hsva(Vec4F32 hsva);
internal Vec4F32 rgba_from_u32(U32 hex);
internal U32 u32_from_rgba(Vec4F32 rgba);
#define rgba_from_u32_lit_comp(h) { (((h)&0xff000000)>>24)/255.f, (((h)&0x00ff0000)>>16)/255.f, (((h)&0x0000ff00)>> 8)/255.f, (((h)&0x000000ff)>> 0)/255.f }
////////////////////////////////
//~ rjf: List Type Functions
internal void rng1u64_list_push(Arena *arena, Rng1U64List *list, Rng1U64 rng);
internal void rng1u64_list_concat(Rng1U64List *list, Rng1U64List *to_concat);
internal Rng1U64Array rng1u64_array_from_list(Arena *arena, Rng1U64List *list);
internal void rng1s64_list_push(Arena *arena, Rng1S64List *list, Rng1S64 rng);
internal Rng1S64Array rng1s64_array_from_list(Arena *arena, Rng1S64List *list);
#endif // BASE_MATH_H

View File

@ -0,0 +1,422 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Type Info Lookups
internal Member *
member_from_name(Type *type, String8 name)
{
Member *member = &member_nil;
if(type->members != 0 && name.size != 0)
{
for(U64 idx = 0; idx < type->count; idx += 1)
{
if(str8_match(type->members[idx].name, name, 0))
{
member = &type->members[idx];
break;
}
}
}
return member;
}
////////////////////////////////
//~ rjf: Type Info * Instance Operations
internal void
typed_data_rebase_ptrs(Type *type, String8 data, void *base_ptr)
{
Temp scratch = scratch_begin(0, 0);
typedef struct RebaseTypeTask RebaseTypeTask;
struct RebaseTypeTask
{
RebaseTypeTask *next;
Type *type;
U8 *ptr;
};
RebaseTypeTask start_task = {0, type, data.str};
RebaseTypeTask *first_task = &start_task;
RebaseTypeTask *last_task = first_task;
for(RebaseTypeTask *t = first_task; t != 0; t = t->next)
{
switch(t->type->kind)
{
default:{}break;
case TypeKind_Ptr:
if(!(t->type->flags & TypeFlag_IsExternal))
{
*(U64 *)t->ptr = ((U64)(*(U8 **)t->ptr - (U8 *)base_ptr));
}break;
case TypeKind_Array:
{
for(U64 idx = 0; idx < t->type->count; idx += 1)
{
RebaseTypeTask *task = push_array(scratch.arena, RebaseTypeTask, 1);
task->type = t->type->direct;
task->ptr = t->ptr + t->type->direct->size * idx;
SLLQueuePush(first_task, last_task, task);
}
}break;
case TypeKind_Struct:
{
for(U64 idx = 0; idx < t->type->count; idx += 1)
{
Member *member = &t->type->members[idx];
RebaseTypeTask *task = push_array(scratch.arena, RebaseTypeTask, 1);
task->type = member->type;
task->ptr = t->ptr + member->value;
SLLQueuePush(first_task, last_task, task);
}
}break;
}
}
scratch_end(scratch);
}
internal String8
serialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params)
{
Temp scratch = scratch_begin(&arena, 1);
String8List strings = {0};
str8_serial_begin(scratch.arena, &strings);
{
typedef struct SerializeTypeTask SerializeTypeTask;
struct SerializeTypeTask
{
SerializeTypeTask *next;
Type *type;
U64 count;
U8 *src;
Type *containing_type;
U8 *containing_ptr;
B32 is_post_header;
};
SerializeTypeTask start_task = {0, type, 1, data.str};
SerializeTypeTask *first_task = &start_task;
SerializeTypeTask *last_task = first_task;
for(SerializeTypeTask *t = first_task; t != 0; t = t->next)
{
switch(t->type->kind)
{
//- rjf: leaf -> just copy the data directly
default:
if(TypeKind_FirstLeaf <= t->type->kind && t->type->kind <= TypeKind_LastLeaf)
{
str8_serial_push_string(scratch.arena, &strings, str8(t->src, t->type->size*t->count));
}break;
//- rjf: pointers -> try to interpret/understand pointer & read/write, otherwise just write as plain data
case TypeKind_Ptr:
{
// rjf: unpack info about this pointer
TypeSerializePtrRefInfo *ptr_ref_info = 0;
for(U64 idx = 0; idx < params->ptr_ref_infos_count; idx += 1)
{
if(params->ptr_ref_infos[idx].type == t->type->direct)
{
ptr_ref_info = &params->ptr_ref_infos[idx];
break;
}
}
// rjf: indexification -> subtract base, divide direct size, write index
if(ptr_ref_info != 0 && ptr_ref_info->indexify_base != 0)
{
U64 ptr_value = 0;
MemoryCopy(&ptr_value, t->src, sizeof(ptr_value));
U64 ptr_write_value = ((U64)((U8 *)ptr_value - (U8 *)ptr_ref_info->indexify_base)/t->type->direct->size);
str8_serial_push_struct(scratch.arena, &strings, &ptr_write_value);
}
// rjf: offsetification -> subtract base, write offsets
else if(ptr_ref_info != 0 && ptr_ref_info->offsetify_base != 0)
{
U64 ptr_value = 0;
MemoryCopy(&ptr_value, t->src, sizeof(ptr_value));
U64 ptr_write_value = (U64)((U8 *)ptr_value - (U8 *)ptr_ref_info->offsetify_base);
str8_serial_push_struct(scratch.arena, &strings, &ptr_write_value);
}
// rjf: size-by-member (pre-header): still potentially dependent on other members which
// delimit our size, so push a new post-header task for pointer.
else if(t->type->count_delimiter_name.size != 0 && !t->is_post_header)
{
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type;
task->count = t->count;
task->src = t->src;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
task->is_post_header = 1;
SLLQueuePush(first_task, last_task, task);
}
// rjf: size-by-member (post-header): all flat parts of containing struct have been
// iterated, so now we can read the size, & descend to new task to read pointer
// destination contents
else if(t->type->count_delimiter_name.size != 0 && t->is_post_header)
{
// rjf: determine count of this pointer
U64 count = 0;
{
Member *count_member = member_from_name(t->containing_type, t->type->count_delimiter_name);
MemoryCopy(&count, t->containing_ptr + count_member->value, count_member->type->size);
}
// rjf: push task
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type->direct;
task->count = count;
task->src = *(void **)t->src;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}
// rjf: catch-all: write pointer value
else
{
str8_serial_push_string(scratch.arena, &strings, str8(t->src, t->type->size*t->count));
}
}break;
//- rjf: arrays -> descend to underlying type, + count
case TypeKind_Array:
{
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type->direct;
task->count = t->type->count;
task->src = t->src;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}break;
//- rjf: struct -> descend to members
case TypeKind_Struct:
{
U64 off = 0;
for(U64 idx = 0; idx < t->count; idx += 1)
{
for(U64 member_idx = 0; member_idx < t->type->count; member_idx += 1)
{
if(t->type->members[member_idx].flags & MemberFlag_DoNotSerialize)
{
continue;
}
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type->members[member_idx].type;
task->count = 1;
task->src = t->src + idx*t->type->size + t->type->members[member_idx].value;
task->containing_type = t->type;
task->containing_ptr = t->src;
SLLQueuePush(first_task, last_task, task);
}
}
}break;
//- rjf: enum -> descend to basic type interpretation
case TypeKind_Enum:
{
SerializeTypeTask *task = push_array(scratch.arena, SerializeTypeTask, 1);
task->type = t->type->direct;
task->count = t->count;
task->src = t->src;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}break;
}
}
}
String8 result = str8_serial_end(arena, &strings);
scratch_end(scratch);
return result;
}
internal String8
deserialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params)
{
String8 result = {0};
result.size = type->size;
result.str = push_array(arena, U8, result.size);
{
Temp scratch = scratch_begin(&arena, 1);
typedef struct DeserializeTypeTask DeserializeTypeTask;
struct DeserializeTypeTask
{
DeserializeTypeTask *next;
Type *type;
U64 count;
U8 *dst;
Type *containing_type;
U8 *containing_ptr;
B32 is_post_header;
};
U64 read_off = 0;
DeserializeTypeTask start_task = {0, type, 1, result.str};
DeserializeTypeTask *first_task = &start_task;
DeserializeTypeTask *last_task = first_task;
for(DeserializeTypeTask *t = first_task; t != 0; t = t->next)
{
U8 *t_src = data.str + read_off;
switch(t->type->kind)
{
//- rjf: leaf -> copy the data directly
default:
if(TypeKind_FirstLeaf <= t->type->kind && t->type->kind <= TypeKind_LastLeaf)
{
MemoryCopy(t->dst, t_src, t->type->size*t->count);
read_off += t->type->size*t->count;
}break;
//- rjf: pointers -> try to interpret/understand pointer & read/write, otherwise skip
case TypeKind_Ptr:
{
// rjf: unpack info about this pointer
TypeSerializePtrRefInfo *ptr_ref_info = 0;
for(U64 idx = 0; idx < params->ptr_ref_infos_count; idx += 1)
{
if(params->ptr_ref_infos[idx].type == t->type->direct)
{
ptr_ref_info = &params->ptr_ref_infos[idx];
break;
}
}
// rjf: indexification -> add base, multiply direct size
if(ptr_ref_info != 0 && ptr_ref_info->indexify_base != 0)
{
U64 ptr_value = 0;
MemoryCopy(&ptr_value, t_src, sizeof(ptr_value));
U64 ptr_write_value = (ptr_value + (U64)ptr_ref_info->indexify_base) * t->type->direct->size;
MemoryCopy(t->dst, &ptr_write_value, sizeof(ptr_write_value));
read_off += sizeof(ptr_value);
}
// rjf: offsetification -> subtract base, write offsets
else if(ptr_ref_info != 0 && ptr_ref_info->offsetify_base != 0)
{
U64 ptr_value = 0;
MemoryCopy(&ptr_value, t_src, sizeof(ptr_value));
U64 ptr_write_value = ptr_value + (U64)ptr_ref_info->offsetify_base;
MemoryCopy(t->dst, &ptr_write_value, sizeof(ptr_write_value));
read_off += sizeof(ptr_value);
}
// rjf: size-by-member (pre-header): still potentially dependent on other members which
// delimit our size, so push a new post-header task for pointer.
else if(t->type->count_delimiter_name.size != 0 && !t->is_post_header)
{
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type;
task->count = t->count;
task->dst = t->dst;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
task->is_post_header = 1;
SLLQueuePush(first_task, last_task, task);
}
// rjf: size-by-member (post-header): all flat parts of containing struct have been
// iterated, so now we can read the size, & descend to new task to read pointer
// destination contents
else if(t->type->count_delimiter_name.size != 0 && t->is_post_header)
{
// rjf: determine count of this pointer
U64 count = 0;
{
Member *count_member = member_from_name(t->containing_type, t->type->count_delimiter_name);
MemoryCopy(&count, t->containing_ptr + count_member->value, count_member->type->size);
}
// rjf: allocate buffer for pointer destination; write address into pointer value slot
U64 ptr_dest_buffer_size = (count+1)*t->type->direct->size;
U8 *ptr_dest_buffer = push_array(arena, U8, ptr_dest_buffer_size);
MemoryCopy(t->dst, &ptr_dest_buffer, sizeof(ptr_dest_buffer));
// rjf: push task
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type->direct;
task->count = count;
task->dst = ptr_dest_buffer;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}
// rjf: catch-all: read pointer value
else
{
MemoryCopy(t->dst, t_src, t->type->size*t->count);
read_off += t->type->size*t->count;
}
}break;
//- rjf: arrays -> descend to underlying type, + count
case TypeKind_Array:
{
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type->direct;
task->count = t->type->count;
task->dst = t->dst;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}break;
//- rjf: struct -> descend to members
case TypeKind_Struct:
{
for(U64 idx = 0; idx < t->count; idx += 1)
{
for(U64 member_idx = 0; member_idx < t->type->count; member_idx += 1)
{
if(t->type->members[member_idx].flags & MemberFlag_DoNotSerialize)
{
continue;
}
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type->members[member_idx].type;
task->count = 1;
task->dst = t->dst + idx*t->type->size + t->type->members[member_idx].value;
task->containing_type = t->type;
task->containing_ptr = t->dst;
SLLQueuePush(first_task, last_task, task);
}
}
}break;
//- rjf: enum -> descend to basic type interpretation
case TypeKind_Enum:
{
DeserializeTypeTask *task = push_array(scratch.arena, DeserializeTypeTask, 1);
task->type = t->type->direct;
task->count = t->count;
task->dst = t->dst;
task->containing_type = t->containing_type;
task->containing_ptr = t->containing_ptr;
SLLQueuePush(first_task, last_task, task);
}break;
}
}
if(params->advance_out != 0)
{
params->advance_out[0] = read_off;
}
scratch_end(scratch);
}
return result;
}
internal String8
deep_copy_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params)
{
Temp scratch = scratch_begin(&arena, 1);
String8 data_srlz = serialized_from_typed_data(scratch.arena, type, data, params);
String8 data_copy = deserialized_from_typed_data(arena, type, data_srlz, params);
scratch_end(scratch);
return data_copy;
}

View File

@ -0,0 +1,298 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_META_H
#define BASE_META_H
////////////////////////////////
//~ rjf: Meta Markup Features
#define EmbedFile(name, path)
#define TweakB32(name, default) (TWEAK_##name)
#define TweakF32(name, default, min, max) (TWEAK_##name)
////////////////////////////////
//~ rjf: Tweak Info Tables
typedef struct TweakB32Info TweakB32Info;
struct TweakB32Info
{
String8 name;
B32 default_value;
B32 *value_ptr;
};
typedef struct TweakF32Info TweakF32Info;
struct TweakF32Info
{
String8 name;
F32 default_value;
Rng1F32 value_range;
F32 *value_ptr;
};
typedef struct TweakB32InfoTable TweakB32InfoTable;
struct TweakB32InfoTable
{
TweakB32Info *v;
U64 count;
};
typedef struct TweakF32InfoTable TweakF32InfoTable;
struct TweakF32InfoTable
{
TweakF32Info *v;
U64 count;
};
typedef struct EmbedInfo EmbedInfo;
struct EmbedInfo
{
String8 name;
String8 *data;
U128 *hash;
};
typedef struct EmbedInfoTable EmbedInfoTable;
struct EmbedInfoTable
{
EmbedInfo *v;
U64 count;
};
////////////////////////////////
//~ rjf: Type Info Types
typedef enum TypeKind
{
TypeKind_Null,
// rjf: leaves
TypeKind_Void, TypeKind_FirstLeaf = TypeKind_Void,
TypeKind_U8,
TypeKind_U16,
TypeKind_U32,
TypeKind_U64,
TypeKind_S8,
TypeKind_S16,
TypeKind_S32,
TypeKind_S64,
TypeKind_B8,
TypeKind_B16,
TypeKind_B32,
TypeKind_B64,
TypeKind_F32,
TypeKind_F64, TypeKind_LastLeaf = TypeKind_F64,
// rjf: operators
TypeKind_Ptr,
TypeKind_Array,
// rjf: user-defined-types
TypeKind_Struct,
TypeKind_Union,
TypeKind_Enum,
TypeKind_COUNT
}
TypeKind;
typedef U32 TypeFlags;
enum
{
TypeFlag_IsExternal = (1<<0),
TypeFlag_IsPlainText = (1<<1),
TypeFlag_IsCodeText = (1<<2),
TypeFlag_IsPathText = (1<<3),
};
typedef U32 MemberFlags;
enum
{
MemberFlag_DoNotSerialize = (1<<0),
};
typedef struct Type Type;
typedef struct Member Member;
struct Member
{
String8 name;
String8 pretty_name;
Type *type;
U64 value;
MemberFlags flags;
};
typedef struct Type Type;
struct Type
{
TypeKind kind;
TypeFlags flags;
U64 size;
Type *direct;
String8 name;
String8 count_delimiter_name; // gathered from surrounding members, turns *->[1] into *->[N]
U64 count;
Member *members;
};
////////////////////////////////
//~ rjf: Type Serialization Parameters
typedef struct TypeSerializePtrRefInfo TypeSerializePtrRefInfo;
struct TypeSerializePtrRefInfo
{
Type *type; // pointers to this
void *indexify_base; // can be indexified using this
void *offsetify_base; // can be offsetified using this
void *nil_ptr; // is terminal if matching 0 or this
};
typedef struct TypeSerializeParams TypeSerializeParams;
struct TypeSerializeParams
{
U64 *advance_out;
TypeSerializePtrRefInfo *ptr_ref_infos;
U64 ptr_ref_infos_count;
};
////////////////////////////////
//~ rjf: Type Name -> Type Info
#define type(T) (&T##__type)
////////////////////////////////
//~ rjf: Type Info Table Initializer Helpers
#define member_lit_comp(S, ti, m, ...) {str8_lit_comp(#m), {0}, (ti), OffsetOf(S, m), __VA_ARGS__}
#define struct_members(S) read_only global Member S##__members[] =
#define struct_type(S, ...) read_only global Type S##__type = {TypeKind_Struct, 0, sizeof(S), &type_nil, str8_lit_comp(#S), {0}, ArrayCount(S##__members), S##__members, __VA_ARGS__}
#define named_struct_type(name, S, ...) read_only global Type name##__type = {TypeKind_Struct, 0, sizeof(S), &type_nil, str8_lit_comp(#name), {0}, ArrayCount(name##__members), name##__members, __VA_ARGS__}
#define ptr_type(name, ti, ...) read_only global Type name = {TypeKind_Ptr, 0, sizeof(void *), (ti), __VA_ARGS__}
////////////////////////////////
//~ rjf: Globals
read_only global Type type_nil = {TypeKind_Null, 0, 0, &type_nil};
read_only global Member member_nil = {{0}, {0}, &type_nil};
////////////////////////////////
//~ rjf: Built-In Types
//- rjf: leaves
read_only global Type void__type = {TypeKind_Void, 0, 0, &type_nil, str8_lit_comp("void")};
read_only global Type U8__type = {TypeKind_U8, 0, sizeof(U8), &type_nil, str8_lit_comp("U8")};
read_only global Type U16__type = {TypeKind_U16, 0, sizeof(U16), &type_nil, str8_lit_comp("U16")};
read_only global Type U32__type = {TypeKind_U32, 0, sizeof(U32), &type_nil, str8_lit_comp("U32")};
read_only global Type U64__type = {TypeKind_U64, 0, sizeof(U64), &type_nil, str8_lit_comp("U64")};
read_only global Type S8__type = {TypeKind_S8, 0, sizeof(S8), &type_nil, str8_lit_comp("S8")};
read_only global Type S16__type = {TypeKind_S16, 0, sizeof(S16), &type_nil, str8_lit_comp("S16")};
read_only global Type S32__type = {TypeKind_S32, 0, sizeof(S32), &type_nil, str8_lit_comp("S32")};
read_only global Type S64__type = {TypeKind_S64, 0, sizeof(S64), &type_nil, str8_lit_comp("S64")};
read_only global Type B8__type = {TypeKind_B8, 0, sizeof(B8), &type_nil, str8_lit_comp("B8")};
read_only global Type B16__type = {TypeKind_B16, 0, sizeof(B16), &type_nil, str8_lit_comp("B16")};
read_only global Type B32__type = {TypeKind_B32, 0, sizeof(B32), &type_nil, str8_lit_comp("B32")};
read_only global Type B64__type = {TypeKind_B64, 0, sizeof(B64), &type_nil, str8_lit_comp("B64")};
read_only global Type F32__type = {TypeKind_F32, 0, sizeof(F32), &type_nil, str8_lit_comp("F32")};
read_only global Type F64__type = {TypeKind_F64, 0, sizeof(F64), &type_nil, str8_lit_comp("F64")};
read_only global Type *type_kind_type_table[] =
{
&type_nil,
type(void),
type(U8),
type(U16),
type(U32),
type(U64),
type(S8),
type(S16),
type(S32),
type(S64),
type(B8),
type(B16),
type(B32),
type(B64),
type(F32),
type(F64),
&type_nil,
&type_nil,
&type_nil,
&type_nil,
&type_nil,
};
//- rjf: Rng1U64
struct_members(Rng1U64)
{
member_lit_comp(Rng1U64, type(U64), min),
member_lit_comp(Rng1U64, type(U64), max),
};
struct_type(Rng1U64);
//- rjf: String8
ptr_type(String8__str_ptr_type, type(U8), str8_lit_comp("size"));
struct_members(String8)
{
member_lit_comp(String8, &String8__str_ptr_type, str),
member_lit_comp(String8, type(U64), size),
};
struct_type(String8);
//- rjf: String8Node
extern Type String8Node__type;
Type String8Node__ptr_type = {TypeKind_Ptr, 0, sizeof(void *), &String8Node__type};
Member String8Node__members[] =
{
{str8_lit_comp("next"), {0}, &String8Node__ptr_type, OffsetOf(String8Node, next)},
{str8_lit_comp("string"), {0}, type(String8), OffsetOf(String8Node, string)},
};
Type String8Node__type =
{
TypeKind_Struct,
0,
sizeof(String8Node),
&type_nil,
str8_lit_comp("String8Node"),
{0},
ArrayCount(String8Node__members),
String8Node__members,
};
//- rjf: String8List
Member String8List__members[] =
{
{str8_lit_comp("first"), {0}, &String8Node__ptr_type, OffsetOf(String8List, first)},
{str8_lit_comp("last"), {0}, &String8Node__ptr_type, OffsetOf(String8List, last), MemberFlag_DoNotSerialize},
{str8_lit_comp("node_count"), {0}, type(U64), OffsetOf(String8List, node_count)},
{str8_lit_comp("total_size"), {0}, type(U64), OffsetOf(String8List, total_size)},
};
Type String8List__type =
{
TypeKind_Struct,
0,
sizeof(String8List),
&type_nil,
str8_lit_comp("String8List"),
{0},
ArrayCount(String8List__members),
String8List__members,
};
////////////////////////////////
//~ rjf: Type Info Lookups
internal Member *member_from_name(Type *type, String8 name);
#define EachMember(T, it) (Member *it = (type(T))->members; it != 0 && it < (type(T))->members + (type(T))->count; it += 1)
////////////////////////////////
//~ rjf: Type Info * Instance Operations
internal void typed_data_rebase_ptrs(Type *type, String8 data, void *base_ptr);
internal String8 serialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params);
internal String8 deserialized_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params);
internal String8 deep_copy_from_typed_data(Arena *arena, Type *type, String8 data, TypeSerializeParams *params);
#define struct_rebase_ptrs(T, ptr, base) typed_data_rebase_ptrs(type(T), str8_struct(ptr), (base))
#define serialized_from_struct(arena, T, ptr, ...) serialized_from_typed_data((arena), type(T), str8_struct(ptr), &(TypeSerializeParams){.ptr_ref_infos = 0, __VA_ARGS__})
#define struct_from_serialized(arena, T, string, ...) (T *)deserialized_from_typed_data((arena), type(T), (string), &(TypeSerializeParams){.ptr_ref_infos = 0, __VA_ARGS__}).str
#define deep_copy_from_struct(arena, T, ptr, ...) (T *)deep_copy_from_typed_data((arena), type(T), str8_struct(ptr), &(TypeSerializeParams){.ptr_ref_infos = 0, __VA_ARGS__}).str
#endif // BASE_META_H

View File

@ -0,0 +1,2 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)

View File

@ -0,0 +1,96 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_PROFILE_H
#define BASE_PROFILE_H
////////////////////////////////
//~ rjf: Zero Settings
#if !defined(PROFILE_TELEMETRY)
# define PROFILE_TELEMETRY 0
#endif
#if !defined(MARKUP_LAYER_COLOR)
# define MARKUP_LAYER_COLOR 1.00f, 0.00f, 1.00f
#endif
////////////////////////////////
//~ rjf: Third Party Includes
#if PROFILE_TELEMETRY
# include "rad_tm.h"
# if OS_WINDOWS
# pragma comment(lib, "rad_tm_win64.lib")
# endif
#endif
////////////////////////////////
//~ rjf: Telemetry Profile Defines
#if PROFILE_TELEMETRY
# define ProfBegin(...) tmEnter(0, 0, __VA_ARGS__)
# define ProfBeginDynamic(...) (TM_API_PTR ? TM_API_PTR->_tmEnterZoneV_Core(0, 0, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0)
# define ProfEnd(...) (TM_API_PTR ? TM_API_PTR->_tmLeaveZone(0) : (void)0)
# define ProfTick(...) tmTick(0)
# define ProfIsCapturing(...) tmRunning()
# define ProfBeginCapture(...) tmOpen(0, __VA_ARGS__, __DATE__, "localhost", TMCT_TCP, TELEMETRY_DEFAULT_PORT, TMOF_INIT_NETWORKING|TMOF_CAPTURE_CONTEXT_SWITCHES, 100)
# define ProfEndCapture(...) tmClose(0)
# define ProfThreadName(...) (TM_API_PTR ? TM_API_PTR->_tmThreadName(0, 0, __VA_ARGS__) : (void)0)
# define ProfMsg(...) (TM_API_PTR ? TM_API_PTR->_tmMessageV_Core(0, TMMF_ICON_NOTE, __FILE__, &g_telemetry_filename_id, __LINE__, __VA_ARGS__) : (void)0)
# define ProfBeginLockWait(...) tmStartWaitForLock(0, 0, __VA_ARGS__)
# define ProfEndLockWait(...) tmEndWaitForLock(0)
# define ProfLockTake(...) tmAcquiredLock(0, 0, __VA_ARGS__)
# define ProfLockDrop(...) tmReleasedLock(0, __VA_ARGS__)
# define ProfColor(color) tmZoneColorSticky(color)
# define ProfBeginV(...) \
if (TM_API_PTR) { \
static tm_uint64 file_id = 0; TM_API_PTR->_tmStaticString(&file_id, __FILE__); \
Temp scratch = scratch_begin(0,0); \
String8 string = push_str8f(scratch.arena, __VA_ARGS__); \
tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \
hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \
TM_API_PTR->_tmEnterZoneFast_Core(0, 0, file_id, __LINE__, hash); \
scratch_end(scratch); \
}
# define ProfNoteV(...) \
if (TM_API_PTR) { \
static tm_uint64 file_id = 0; TM_API_PTR->_tmStaticString(&file_id, __FILE__); \
Temp scratch = scratch_begin(0,0); \
String8 string = push_str8f(scratch.arena, __VA_ARGS__); \
tm_uint64 hash = TM_API_PTR->_tmHash((char*)string.str, string.size); \
hash = TM_API_PTR->_tmSendDynamicString(hash, (char*)string.str); \
TM_API_PTR->_tmMessageFast_Core(0, TMMF_ICON_NOTE, file_id, __LINE__, hash); \
scratch_end(scratch); \
}
#endif
////////////////////////////////
//~ rjf: Zeroify Undefined Defines
#if !defined(ProfBegin)
# define ProfBegin(...) (0)
# define ProfBeginDynamic(...) (0)
# define ProfEnd(...) (0)
# define ProfTick(...) (0)
# define ProfIsCapturing(...) (0)
# define ProfBeginCapture(...) (0)
# define ProfEndCapture(...) (0)
# define ProfThreadName(...) (0)
# define ProfMsg(...) (0)
# define ProfBeginLockWait(...) (0)
# define ProfEndLockWait(...) (0)
# define ProfLockTake(...) (0)
# define ProfLockDrop(...) (0)
# define ProfColor(...) (0)
# define ProfBeginV(...) (0)
# define ProfNoteV(...) (0)
#endif
////////////////////////////////
//~ rjf: Helper Wrappers
#define ProfBeginFunction(...) ProfBegin(this_function_name)
#define ProfScope(...) DeferLoop(ProfBeginDynamic(__VA_ARGS__), ProfEnd())
#endif // BASE_PROFILE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,412 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_STRINGS_H
#define BASE_STRINGS_H
////////////////////////////////
//~ rjf: Third Party Includes
#define STB_SPRINTF_DECORATE(name) raddbg_##name
#include "../third_party/stb/stb_sprintf.h"
////////////////////////////////
//~ rjf: String Types
typedef struct String8 String8;
struct String8
{
U8 *str;
U64 size;
};
typedef struct String16 String16;
struct String16
{
U16 *str;
U64 size;
};
typedef struct String32 String32;
struct String32
{
U32 *str;
U64 size;
};
////////////////////////////////
//~ rjf: String List & Array Types
typedef struct String8Node String8Node;
struct String8Node
{
String8Node *next;
String8 string;
};
typedef struct String8MetaNode String8MetaNode;
struct String8MetaNode
{
String8MetaNode *next;
String8Node *node;
};
typedef struct String8List String8List;
struct String8List
{
String8Node *first;
String8Node *last;
U64 node_count;
U64 total_size;
};
typedef struct String8Array String8Array;
struct String8Array
{
String8 *v;
U64 count;
};
////////////////////////////////
//~ rjf: String Matching, Splitting, & Joining Types
typedef U32 StringMatchFlags;
enum
{
StringMatchFlag_CaseInsensitive = (1 << 0),
StringMatchFlag_RightSideSloppy = (1 << 1),
StringMatchFlag_SlashInsensitive = (1 << 2),
};
typedef U32 StringSplitFlags;
enum
{
StringSplitFlag_KeepEmpties = (1 << 0),
};
typedef enum PathStyle
{
PathStyle_Null,
PathStyle_Relative,
PathStyle_WindowsAbsolute,
PathStyle_UnixAbsolute,
#if OS_WINDOWS
PathStyle_SystemAbsolute = PathStyle_WindowsAbsolute
#elif OS_LINUX
PathStyle_SystemAbsolute = PathStyle_UnixAbsolute
#else
# error "absolute path style is undefined for this OS"
#endif
}
PathStyle;
typedef struct StringJoin StringJoin;
struct StringJoin
{
String8 pre;
String8 sep;
String8 post;
};
////////////////////////////////
//~ rjf: String Pair Types
typedef struct String8TxtPtPair String8TxtPtPair;
struct String8TxtPtPair
{
String8 string;
TxtPt pt;
};
////////////////////////////////
//~ rjf: UTF Decoding Types
typedef struct UnicodeDecode UnicodeDecode;
struct UnicodeDecode
{
U32 inc;
U32 codepoint;
};
////////////////////////////////
//~ rjf: String Fuzzy Matching Types
typedef struct FuzzyMatchRangeNode FuzzyMatchRangeNode;
struct FuzzyMatchRangeNode
{
FuzzyMatchRangeNode *next;
Rng1U64 range;
};
typedef struct FuzzyMatchRangeList FuzzyMatchRangeList;
struct FuzzyMatchRangeList
{
FuzzyMatchRangeNode *first;
FuzzyMatchRangeNode *last;
U64 count;
U64 needle_part_count;
U64 total_dim;
};
////////////////////////////////
//~ rjf: Character Classification & Conversion Functions
internal B32 char_is_space(U8 c);
internal B32 char_is_upper(U8 c);
internal B32 char_is_lower(U8 c);
internal B32 char_is_alpha(U8 c);
internal B32 char_is_slash(U8 c);
internal B32 char_is_digit(U8 c, U32 base);
internal U8 char_to_lower(U8 c);
internal U8 char_to_upper(U8 c);
internal U8 char_to_correct_slash(U8 c);
////////////////////////////////
//~ rjf: C-String Measurement
internal U64 cstring8_length(U8 *c);
internal U64 cstring16_length(U16 *c);
internal U64 cstring32_length(U32 *c);
////////////////////////////////
//~ rjf: String Constructors
#define str8_lit(S) str8((U8*)(S), sizeof(S) - 1)
#define str8_lit_comp(S) {(U8*)(S), sizeof(S) - 1,}
#define str8_varg(S) (int)((S).size), ((S).str)
#define str8_array(S,C) str8((U8*)(S), sizeof(*(S))*(C))
#define str8_array_fixed(S) str8((U8*)(S), sizeof(S))
#define str8_struct(S) str8((U8*)(S), sizeof(*(S)))
internal String8 str8(U8 *str, U64 size);
internal String8 str8_range(U8 *first, U8 *one_past_last);
internal String8 str8_zero(void);
internal String16 str16(U16 *str, U64 size);
internal String16 str16_range(U16 *first, U16 *one_past_last);
internal String16 str16_zero(void);
internal String32 str32(U32 *str, U64 size);
internal String32 str32_range(U32 *first, U32 *one_past_last);
internal String32 str32_zero(void);
internal String8 str8_cstring(char *c);
internal String16 str16_cstring(U16 *c);
internal String32 str32_cstring(U32 *c);
internal String8 str8_cstring_capped(void *cstr, void *cap);
internal String16 str16_cstring_capped(void *cstr, void *cap);
internal String8 str8_cstring_capped_reverse(void *raw_start, void *raw_cap);
////////////////////////////////
//~ rjf: String Stylization
internal String8 upper_from_str8(Arena *arena, String8 string);
internal String8 lower_from_str8(Arena *arena, String8 string);
internal String8 backslashed_from_str8(Arena *arena, String8 string);
////////////////////////////////
//~ rjf: String Matching
internal B32 str8_match(String8 a, String8 b, StringMatchFlags flags);
internal U64 str8_find_needle(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
internal U64 str8_find_needle_reverse(String8 string, U64 start_pos, String8 needle, StringMatchFlags flags);
internal B32 str8_ends_with(String8 string, String8 end, StringMatchFlags flags);
////////////////////////////////
//~ rjf: String Slicing
internal String8 str8_substr(String8 str, Rng1U64 range);
internal String8 str8_prefix(String8 str, U64 size);
internal String8 str8_skip(String8 str, U64 amt);
internal String8 str8_postfix(String8 str, U64 size);
internal String8 str8_chop(String8 str, U64 amt);
internal String8 str8_skip_chop_whitespace(String8 string);
////////////////////////////////
//~ rjf: String Formatting & Copying
internal String8 push_str8_cat(Arena *arena, String8 s1, String8 s2);
internal String8 push_str8_copy(Arena *arena, String8 s);
internal String8 push_str8fv(Arena *arena, char *fmt, va_list args);
internal String8 push_str8f(Arena *arena, char *fmt, ...);
////////////////////////////////
//~ rjf: String <=> Integer Conversions
//- rjf: string -> integer
internal S64 sign_from_str8(String8 string, String8 *string_tail);
internal B32 str8_is_integer(String8 string, U32 radix);
internal U64 u64_from_str8(String8 string, U32 radix);
internal S64 s64_from_str8(String8 string, U32 radix);
internal U32 u32_from_str8(String8 string, U32 radix);
internal S32 s32_from_str8(String8 string, U32 radix);
internal B32 try_u64_from_str8_c_rules(String8 string, U64 *x);
internal B32 try_s64_from_str8_c_rules(String8 string, S64 *x);
//- rjf: integer -> string
internal String8 str8_from_memory_size(Arena *arena, U64 size);
internal String8 str8_from_count(Arena *arena, U64 count);
internal String8 str8_from_bits_u32(Arena *arena, U32 x);
internal String8 str8_from_bits_u64(Arena *arena, U64 x);
internal String8 str8_from_u64(Arena *arena, U64 u64, U32 radix, U8 min_digits, U8 digit_group_separator);
internal String8 str8_from_s64(Arena *arena, S64 s64, U32 radix, U8 min_digits, U8 digit_group_separator);
////////////////////////////////
//~ rjf: String <=> Float Conversions
internal F64 f64_from_str8(String8 string);
////////////////////////////////
//~ rjf: String List Construction Functions
internal String8Node* str8_list_push_node(String8List *list, String8Node *node);
internal String8Node* str8_list_push_node_set_string(String8List *list, String8Node *node, String8 string);
internal String8Node* str8_list_push_node_front(String8List *list, String8Node *node);
internal String8Node* str8_list_push_node_front_set_string(String8List *list, String8Node *node, String8 string);
internal String8Node* str8_list_push(Arena *arena, String8List *list, String8 string);
internal String8Node* str8_list_push_front(Arena *arena, String8List *list, String8 string);
internal void str8_list_concat_in_place(String8List *list, String8List *to_push);
internal String8Node* str8_list_push_aligner(Arena *arena, String8List *list, U64 min, U64 align);
internal String8Node* str8_list_pushf(Arena *arena, String8List *list, char *fmt, ...);
internal String8Node* str8_list_push_frontf(Arena *arena, String8List *list, char *fmt, ...);
internal String8List str8_list_copy(Arena *arena, String8List *list);
#define str8_list_first(list) ((list)->first ? (list)->first->string : str8_zero())
////////////////////////////////
//~ rjf: String Splitting & Joining
internal String8List str8_split(Arena *arena, String8 string, U8 *split_chars, U64 split_char_count, StringSplitFlags flags);
internal String8List str8_split_by_string_chars(Arena *arena, String8 string, String8 split_chars, StringSplitFlags flags);
internal String8List str8_list_split_by_string_chars(Arena *arena, String8List list, String8 split_chars, StringSplitFlags flags);
internal String8 str8_list_join(Arena *arena, String8List *list, StringJoin *optional_params);
internal void str8_list_from_flags(Arena *arena, String8List *list, U32 flags, String8 *flag_string_table, U32 flag_string_count);
////////////////////////////////
//~ rjf; String Arrays
internal String8Array str8_array_from_list(Arena *arena, String8List *list);
internal String8Array str8_array_reserve(Arena *arena, U64 count);
////////////////////////////////
//~ rjf: String Path Helpers
internal String8 str8_chop_last_slash(String8 string);
internal String8 str8_skip_last_slash(String8 string);
internal String8 str8_chop_last_dot(String8 string);
internal String8 str8_skip_last_dot(String8 string);
internal PathStyle path_style_from_str8(String8 string);
internal String8List str8_split_path(Arena *arena, String8 string);
internal void str8_path_list_resolve_dots_in_place(String8List *path, PathStyle style);
internal String8 str8_path_list_join_by_style(Arena *arena, String8List *path, PathStyle style);
internal String8TxtPtPair str8_txt_pt_pair_from_string(String8 string);
////////////////////////////////
//~ rjf: UTF-8 & UTF-16 Decoding/Encoding
internal UnicodeDecode utf8_decode(U8 *str, U64 max);
internal UnicodeDecode utf16_decode(U16 *str, U64 max);
internal U32 utf8_encode(U8 *str, U32 codepoint);
internal U32 utf16_encode(U16 *str, U32 codepoint);
internal U32 utf8_from_utf32_single(U8 *buffer, U32 character);
////////////////////////////////
//~ rjf: Unicode String Conversions
internal String8 str8_from_16(Arena *arena, String16 in);
internal String16 str16_from_8(Arena *arena, String8 in);
internal String8 str8_from_32(Arena *arena, String32 in);
internal String32 str32_from_8(Arena *arena, String8 in);
////////////////////////////////
//~ String -> Enum Conversions
internal OperatingSystem operating_system_from_string(String8 string);
////////////////////////////////
//~ rjf: Basic Types & Space Enum -> String Conversions
internal String8 string_from_dimension(Dimension dimension);
internal String8 string_from_side(Side side);
internal String8 string_from_operating_system(OperatingSystem os);
internal String8 string_from_arch(Arch arch);
////////////////////////////////
//~ rjf: Time Types -> String
internal String8 string_from_week_day(WeekDay week_day);
internal String8 string_from_month(Month month);
internal String8 push_date_time_string(Arena *arena, DateTime *date_time);
internal String8 push_file_name_date_time_string(Arena *arena, DateTime *date_time);
internal String8 string_from_elapsed_time(Arena *arena, DateTime dt);
////////////////////////////////
//~ Globally Unique Ids
internal String8 string_from_guid(Arena *arena, Guid guid);
internal B32 try_guid_from_string(String8 string, Guid *guid_out);
internal Guid guid_from_string(String8 string);
////////////////////////////////
//~ rjf: Basic Text Indentation
internal String8 indented_from_string(Arena *arena, String8 string);
////////////////////////////////
//~ rjf: Text Escaping
internal String8 escaped_from_raw_str8(Arena *arena, String8 string);
internal String8 raw_from_escaped_str8(Arena *arena, String8 string);
////////////////////////////////
//~ rjf: Text Wrapping
internal String8List wrapped_lines_from_string(Arena *arena, String8 string, U64 first_line_max_width, U64 max_width, U64 wrap_indent);
////////////////////////////////
//~ rjf: String <-> Color
internal String8 hex_string_from_rgba_4f32(Arena *arena, Vec4F32 rgba);
internal Vec4F32 rgba_from_hex_string_4f32(String8 hex_string);
////////////////////////////////
//~ rjf: String Fuzzy Matching
internal FuzzyMatchRangeList fuzzy_match_find(Arena *arena, String8 needle, String8 haystack);
internal FuzzyMatchRangeList fuzzy_match_range_list_copy(Arena *arena, FuzzyMatchRangeList *src);
////////////////////////////////
//~ NOTE(allen): Serialization Helpers
internal void str8_serial_begin(Arena *arena, String8List *srl);
internal String8 str8_serial_end(Arena *arena, String8List *srl);
internal void str8_serial_write_to_dst(String8List *srl, void *out);
internal U64 str8_serial_push_align(Arena *arena, String8List *srl, U64 align);
internal void * str8_serial_push_size(Arena *arena, String8List *srl, U64 size);
internal void * str8_serial_push_data(Arena *arena, String8List *srl, void *data, U64 size);
internal void str8_serial_push_data_list(Arena *arena, String8List *srl, String8Node *first);
internal void str8_serial_push_u64(Arena *arena, String8List *srl, U64 x);
internal void str8_serial_push_u32(Arena *arena, String8List *srl, U32 x);
internal void str8_serial_push_u16(Arena *arena, String8List *srl, U16 x);
internal void str8_serial_push_u8(Arena *arena, String8List *srl, U8 x);
internal void str8_serial_push_cstr(Arena *arena, String8List *srl, String8 str);
internal void str8_serial_push_string(Arena *arena, String8List *srl, String8 str);
#define str8_serial_push_array(arena, srl, ptr, count) str8_serial_push_data(arena, srl, ptr, sizeof(*(ptr)) * (count))
#define str8_serial_push_struct(arena, srl, ptr) str8_serial_push_array(arena, srl, ptr, 1)
////////////////////////////////
//~ rjf: Deserialization Helpers
internal U64 str8_deserial_read(String8 string, U64 off, void *read_dst, U64 read_size, U64 granularity);
internal U64 str8_deserial_find_first_match(String8 string, U64 off, U16 scan_val);
internal void * str8_deserial_get_raw_ptr(String8 string, U64 off, U64 size);
internal U64 str8_deserial_read_cstr(String8 string, U64 off, String8 *cstr_out);
internal U64 str8_deserial_read_windows_utf16_string16(String8 string, U64 off, String16 *str_out);
internal U64 str8_deserial_read_block(String8 string, U64 off, U64 size, String8 *block_out);
internal U64 str8_deserial_read_uleb128(String8 string, U64 off, U64 *value_out);
internal U64 str8_deserial_read_sleb128(String8 string, U64 off, S64 *value_out);
#define str8_deserial_read_array(string, off, ptr, count) str8_deserial_read((string), (off), (ptr), sizeof(*(ptr))*(count), sizeof(*(ptr)))
#define str8_deserial_read_struct(string, off, ptr) str8_deserial_read_array(string, off, ptr, 1)
#endif // BASE_STRINGS_H

View File

@ -0,0 +1,87 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
// NOTE(allen): Thread Context Functions
C_LINKAGE thread_static TCTX* tctx_thread_local;
#if !BUILD_SUPPLEMENTARY_UNIT
C_LINKAGE thread_static TCTX* tctx_thread_local = 0;
#endif
internal void
tctx_init_and_equip(TCTX *tctx){
MemoryZeroStruct(tctx);
Arena **arena_ptr = tctx->arenas;
for (U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1){
*arena_ptr = arena_alloc();
}
tctx_thread_local = tctx;
}
internal void
tctx_release(void)
{
for(U64 i = 0; i < ArrayCount(tctx_thread_local->arenas); i += 1)
{
arena_release(tctx_thread_local->arenas[i]);
}
}
internal TCTX*
tctx_get_equipped(void){
return(tctx_thread_local);
}
internal Arena*
tctx_get_scratch(Arena **conflicts, U64 count){
TCTX *tctx = tctx_get_equipped();
Arena *result = 0;
Arena **arena_ptr = tctx->arenas;
for (U64 i = 0; i < ArrayCount(tctx->arenas); i += 1, arena_ptr += 1){
Arena **conflict_ptr = conflicts;
B32 has_conflict = 0;
for (U64 j = 0; j < count; j += 1, conflict_ptr += 1){
if (*arena_ptr == *conflict_ptr){
has_conflict = 1;
break;
}
}
if (!has_conflict){
result = *arena_ptr;
break;
}
}
return(result);
}
internal void
tctx_set_thread_name(String8 string){
TCTX *tctx = tctx_get_equipped();
U64 size = ClampTop(string.size, sizeof(tctx->thread_name));
MemoryCopy(tctx->thread_name, string.str, size);
tctx->thread_name_size = size;
}
internal String8
tctx_get_thread_name(void){
TCTX *tctx = tctx_get_equipped();
String8 result = str8(tctx->thread_name, tctx->thread_name_size);
return(result);
}
internal void
tctx_write_srcloc(char *file_name, U64 line_number){
TCTX *tctx = tctx_get_equipped();
tctx->file_name = file_name;
tctx->line_number = line_number;
}
internal void
tctx_read_srcloc(char **file_name, U64 *line_number){
TCTX *tctx = tctx_get_equipped();
*file_name = tctx->file_name;
*line_number = tctx->line_number;
}

View File

@ -0,0 +1,41 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef BASE_THREAD_CONTEXT_H
#define BASE_THREAD_CONTEXT_H
////////////////////////////////
// NOTE(allen): Thread Context
typedef struct TCTX TCTX;
struct TCTX
{
Arena *arenas[2];
U8 thread_name[32];
U64 thread_name_size;
char *file_name;
U64 line_number;
};
////////////////////////////////
// NOTE(allen): Thread Context Functions
internal void tctx_init_and_equip(TCTX *tctx);
internal void tctx_release(void);
internal TCTX* tctx_get_equipped(void);
internal Arena* tctx_get_scratch(Arena **conflicts, U64 countt);
internal void tctx_set_thread_name(String8 name);
internal String8 tctx_get_thread_name(void);
internal void tctx_write_srcloc(char *file_name, U64 line_number);
internal void tctx_read_srcloc(char **file_name, U64 *line_number);
#define tctx_write_this_srcloc() tctx_write_srcloc(__FILE__, __LINE__)
#define scratch_begin(conflicts, count) temp_begin(tctx_get_scratch((conflicts), (count)))
#define scratch_end(scratch) temp_end(scratch)
#endif // BASE_THREAD_CONTEXT_H

652
src/metagen/metagen_main.c Normal file
View File

@ -0,0 +1,652 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Build Options
#define BUILD_CONSOLE_INTERFACE 1
////////////////////////////////
//~ rjf: Includes
//- rjf: headers
#include "metagen_base/metagen_base_inc.h"
#include "metagen_os/metagen_os_inc.h"
#include "../mdesk/mdesk.h"
#include "metagen.h"
//- rjf: impls
#include "metagen_base/metagen_base_inc.c"
#include "metagen_os/metagen_os_inc.c"
#include "../mdesk/mdesk.c"
#include "metagen.c"
////////////////////////////////
//~ rjf: Entry Point
internal void
entry_point(CmdLine *cmdline)
{
//////////////////////////////
//- rjf: set up state
//
MG_MsgList msgs = {0};
mg_arena = arena_alloc(.reserve_size = GB(64), .commit_size = MB(64));
mg_state = push_array(mg_arena, MG_State, 1);
mg_state->slots_count = 256;
mg_state->slots = push_array(mg_arena, MG_LayerSlot, mg_state->slots_count);
//////////////////////////////
//- rjf: extract paths
//
String8 build_dir_path = os_get_process_info()->binary_path;
String8 project_dir_path = str8_chop_last_slash(build_dir_path);
String8 code_dir_path = push_str8f(mg_arena, "%S/src", project_dir_path);
//////////////////////////////
//- rjf: search code directories for all files to consider
//
String8List file_paths = {0};
DeferLoop(printf("searching %.*s...", str8_varg(code_dir_path)), printf(" %i files found\n", (int)file_paths.node_count))
{
typedef struct Task Task;
struct Task
{
Task *next;
String8 path;
};
Task start_task = {0, code_dir_path};
Task *first_task = &start_task;
Task *last_task = &start_task;
for(Task *task = first_task; task != 0; task = task->next)
{
OS_FileIter *it = os_file_iter_begin(mg_arena, task->path, 0);
for(OS_FileInfo info = {0}; os_file_iter_next(mg_arena, it, &info);)
{
String8 file_path = push_str8f(mg_arena, "%S/%S", task->path, info.name);
if(info.props.flags & FilePropertyFlag_IsFolder)
{
Task *next_task = push_array(mg_arena, Task, 1);
SLLQueuePush(first_task, last_task, next_task);
next_task->path = file_path;
}
else
{
str8_list_push(mg_arena, &file_paths, file_path);
}
}
os_file_iter_end(it);
}
}
//////////////////////////////
//- rjf: parse all metadesk files
//
MG_FileParseList parses = {0};
DeferLoop(printf("parsing metadesk..."), printf(" %i metadesk files parsed\n", (int)parses.count))
{
for(String8Node *n = file_paths.first; n != 0; n = n->next)
{
String8 file_path = n->string;
String8 file_ext = str8_skip_last_dot(file_path);
if(str8_match(file_ext, str8_lit("mdesk"), 0))
{
String8 data = os_data_from_file_path(mg_arena, file_path);
MD_TokenizeResult tokenize = md_tokenize_from_text(mg_arena, data);
MD_ParseResult parse = md_parse_from_text_tokens(mg_arena, file_path, data, tokenize.tokens);
for(MD_Msg *m = parse.msgs.first; m != 0; m = m->next)
{
TxtPt pt = mg_txt_pt_from_string_off(data, m->node->src_offset);
String8 msg_kind_string = {0};
switch(m->kind)
{
default:{}break;
case MD_MsgKind_Note: {msg_kind_string = str8_lit("note");}break;
case MD_MsgKind_Warning: {msg_kind_string = str8_lit("warning");}break;
case MD_MsgKind_Error: {msg_kind_string = str8_lit("error");}break;
case MD_MsgKind_FatalError: {msg_kind_string = str8_lit("fatal error");}break;
}
String8 location = push_str8f(mg_arena, "%S:%I64d:%I64d", file_path, pt.line, pt.column);
MG_Msg dst_m = {location, msg_kind_string, m->string};
mg_msg_list_push(mg_arena, &msgs, &dst_m);
}
MG_FileParseNode *parse_n = push_array(mg_arena, MG_FileParseNode, 1);
SLLQueuePush(parses.first, parses.last, parse_n);
parse_n->v.root = parse.root;
parses.count += 1;
}
}
}
//////////////////////////////
//- rjf: gather tables
//
MG_Map table_grid_map = mg_push_map(mg_arena, 1024);
MG_Map table_col_map = mg_push_map(mg_arena, 1024);
U64 table_count = 0;
DeferLoop(printf("gathering tables..."), printf(" %i tables found\n", (int)table_count))
{
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
for MD_EachNode(node, file->first)
{
MD_Node *table_tag = md_tag_from_string(node, str8_lit("table"), 0);
if(!md_node_is_nil(table_tag))
{
MG_NodeGrid *table = push_array(mg_arena, MG_NodeGrid, 1);
MG_ColumnDescArray *col_descs = push_array(mg_arena, MG_ColumnDescArray, 1);
*table = mg_node_grid_make_from_node(mg_arena, node);
*col_descs = mg_column_desc_array_from_tag(mg_arena, table_tag);
mg_map_insert_ptr(mg_arena, &table_grid_map, node->string, table);
mg_map_insert_ptr(mg_arena, &table_col_map, node->string, col_descs);
table_count += 1;
}
}
}
}
//////////////////////////////
//- rjf: gather layer options
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
for MD_EachNode(node, file->first)
{
if(md_node_has_tag(node, str8_lit("option"), 0))
{
if(str8_match(node->string, str8_lit("library"), 0))
{
layer->is_library = 1;
}
}
if(md_node_has_tag(node, str8_lit("gen_folder"), 0))
{
layer->gen_folder_name = node->string;
}
if(md_node_has_tag(node, str8_lit("h_name"), 0))
{
layer->h_name_override = node->string;
}
if(md_node_has_tag(node, str8_lit("c_name"), 0))
{
layer->c_name_override = node->string;
}
if(md_node_has_tag(node, str8_lit("h_header"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &layer->h_header, n->string);
str8_list_push(mg_arena, &layer->h_header, str8_lit("\n"));
}
}
if(md_node_has_tag(node, str8_lit("h_footer"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &layer->h_footer, n->string);
str8_list_push(mg_arena, &layer->h_footer, str8_lit("\n"));
}
}
if(md_node_has_tag(node, str8_lit("c_header"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &layer->c_header, n->string);
str8_list_push(mg_arena, &layer->c_header, str8_lit("\n"));
}
}
if(md_node_has_tag(node, str8_lit("c_footer"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &layer->c_footer, n->string);
str8_list_push(mg_arena, &layer->c_footer, str8_lit("\n"));
}
}
}
}
//////////////////////////////
//- rjf: generate enums
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
for MD_EachNode(node, file->first)
{
MD_Node *tag = md_tag_from_string(node, str8_lit("enum"), 0);
if(!md_node_is_nil(tag))
{
String8 enum_name = node->string;
String8 enum_member_prefix = enum_name;
if(str8_match(str8_postfix(enum_name, 5), str8_lit("Flags"), 0))
{
enum_member_prefix = str8_chop(enum_name, 1);
}
String8 enum_base_type_name = tag->first->string;
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
if(enum_base_type_name.size == 0)
{
str8_list_pushf(mg_arena, &layer->enums, "typedef enum %S\n{\n", enum_name);
}
else
{
str8_list_pushf(mg_arena, &layer->enums, "typedef %S %S;\n", enum_base_type_name, enum_name);
str8_list_pushf(mg_arena, &layer->enums, "typedef enum %SEnum\n{\n", enum_name);
}
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_pushf(mg_arena, &layer->enums, "%S_%S,\n", enum_member_prefix, n->string);
}
if(enum_base_type_name.size == 0)
{
str8_list_pushf(mg_arena, &layer->enums, "} %S;\n\n", enum_name);
}
else
{
str8_list_pushf(mg_arena, &layer->enums, "} %SEnum;\n\n", enum_name);
}
}
}
}
//////////////////////////////
//- rjf: generate xlists
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
for MD_EachNode(node, file->first)
{
MD_Node *tag = md_tag_from_string(node, str8_lit("xlist"), 0);
if(!md_node_is_nil(tag))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
str8_list_pushf(mg_arena, &layer->enums, "#define %S \\\n", node->string);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_pushf(mg_arena, &layer->enums, "X(%S)\\\n", n->string);
}
str8_list_push(mg_arena, &layer->enums, str8_lit("\n"));
}
}
}
//////////////////////////////
//- rjf: generate structs
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
for MD_EachNode(node, file->first)
{
if(md_node_has_tag(node, str8_lit("struct"), 0))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
str8_list_pushf(mg_arena, &layer->structs, "typedef struct %S %S;\n", node->string, node->string);
str8_list_pushf(mg_arena, &layer->structs, "struct %S\n{\n", node->string);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_pushf(mg_arena, &layer->structs, "%S;\n", n->string);
}
str8_list_pushf(mg_arena, &layer->structs, "};\n\n");
}
}
}
//////////////////////////////
//- rjf: generate data tables
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
for MD_EachNode(node, file->first)
{
MD_Node *tag = md_tag_from_string(node, str8_lit("data"), 0);
if(!md_node_is_nil(tag))
{
String8 element_type = tag->first->string;
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
if(!md_node_has_tag(node, str8_lit("c_file"), 0))
{
str8_list_pushf(mg_arena, &layer->h_tables, "extern %S %S[%I64u];\n", element_type, node->string, gen_strings.node_count);
}
str8_list_pushf(mg_arena, &layer->c_tables, "%S %S[%I64u] =\n{\n", element_type, node->string, gen_strings.node_count);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_pushf(mg_arena, &layer->c_tables, "%S,\n", n->string);
}
str8_list_push(mg_arena, &layer->c_tables, str8_lit("};\n\n"));
}
}
}
//////////////////////////////
//- rjf: generate enum -> string mapping functions
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
for MD_EachNode(node, file->first)
{
MD_Node *tag = md_tag_from_string(node, str8_lit("enum2string_switch"), 0);
if(!md_node_is_nil(tag))
{
String8 enum_type = tag->first->string;
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
str8_list_pushf(mg_arena, &layer->h_functions, "internal String8 %S(%S v);\n", node->string, enum_type);
str8_list_pushf(mg_arena, &layer->c_functions, "internal String8\n%S(%S v)\n{\n", node->string, enum_type);
str8_list_pushf(mg_arena, &layer->c_functions, "String8 result = str8_lit(\"<Unknown %S>\");\n", enum_type);
str8_list_pushf(mg_arena, &layer->c_functions, "switch(v)\n");
str8_list_pushf(mg_arena, &layer->c_functions, "{\n");
str8_list_pushf(mg_arena, &layer->c_functions, "default:{}break;\n");
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_pushf(mg_arena, &layer->c_functions, "%S;\n", n->string);
}
str8_list_pushf(mg_arena, &layer->c_functions, "}\n");
str8_list_pushf(mg_arena, &layer->c_functions, "return result;\n");
str8_list_pushf(mg_arena, &layer->c_functions, "}\n\n");
}
}
}
//////////////////////////////
//- rjf: generate catch-all generations
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
for MD_EachNode(node, file->first)
{
MD_Node *tag = md_tag_from_string(node, str8_lit("gen"), 0);
if(!md_node_is_nil(tag))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
B32 prefer_c_file = md_node_has_tag(node, str8_lit("c_file"), 0);
String8List *out = prefer_c_file ? &layer->c_catchall : &layer->h_catchall;
if(tag->first->string.size == 0){}
else if(str8_match(tag->first->string, str8_lit("enums"), 0)) { out = &layer->enums; }
else if(str8_match(tag->first->string, str8_lit("structs"), 0)) { out = &layer->structs; }
else if(str8_match(tag->first->string, str8_lit("functions"), 0)) { out = prefer_c_file ? &layer->c_functions : &layer->h_functions; }
else if(str8_match(tag->first->string, str8_lit("tables"), 0)) { out = prefer_c_file ? &layer->c_tables : &layer->h_tables; }
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), node);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
String8 trimmed = str8_skip_chop_whitespace(n->string);
str8_list_push(mg_arena, out, trimmed);
str8_list_push(mg_arena, out, str8_lit("\n"));
}
}
}
}
//////////////////////////////
//- rjf: gather & generate all embeds
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
for MD_EachNode(node, file->first)
{
if(md_node_has_tag(node, str8_lit("embed_string"), 0))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8 embed_string = mg_c_string_literal_from_multiline_string(node->first->string);
//str8_list_pushf(mg_arena, &layer->h_tables, "read_only global String8 %S =\nstr8_lit_comp(\n", node->string);
str8_list_pushf(mg_arena, &layer->h_tables, "read_only global const char* %S =\n", node->string);
str8_list_push (mg_arena, &layer->h_tables, embed_string);
str8_list_pushf(mg_arena, &layer->h_tables, ";\n");
//str8_list_pushf(mg_arena, &layer->h_tables, ");\n\n");
}
if(md_node_has_tag(node, str8_lit("embed_file"), 0))
{
String8 layer_key = mg_layer_key_from_path(file->string);
MG_Layer *layer = mg_layer_from_key(layer_key);
String8 data = os_data_from_file_path(mg_arena, node->first->string);
String8 embed_string = mg_c_array_literal_contents_from_data(data);
str8_list_pushf(mg_arena, &layer->h_tables, "read_only global U8 %S__data[] =\n{\n", node->string);
str8_list_push (mg_arena, &layer->h_tables, embed_string);
str8_list_pushf(mg_arena, &layer->h_tables, "};\n\n");
str8_list_pushf(mg_arena, &layer->h_tables, "read_only global String8 %S = {%S__data, sizeof(%S__data)};\n",
node->string,
node->string,
node->string);
}
}
}
//////////////////////////////
//- rjf: generate all markdown in build folder
//
for(MG_FileParseNode *n = parses.first; n != 0; n = n->next)
{
MD_Node *file = n->v.root;
for MD_EachNode(node, file->first)
{
//- rjf: generate markdown page
if(md_node_has_tag(node, str8_lit("markdown"), 0))
{
String8List md_strs = {0};
for(MD_Node *piece = node->first; !md_node_is_nil(piece); piece = piece->next)
{
if(md_node_has_tag(piece, str8_lit("title"), 0))
{
str8_list_pushf(mg_arena, &md_strs, "# %S\n\n", piece->string);
}
if(md_node_has_tag(piece, str8_lit("subtitle"), 0))
{
str8_list_pushf(mg_arena, &md_strs, "## %S\n\n", piece->string);
}
if(md_node_has_tag(piece, str8_lit("p"), 0))
{
String8 paragraph_text = piece->string;
String8List paragraph_lines = mg_wrapped_lines_from_string(mg_arena, paragraph_text, 80, 80, 0);
for(String8Node *n = paragraph_lines.first; n != 0; n = n->next)
{
str8_list_push(mg_arena, &md_strs, n->string);
str8_list_push(mg_arena, &md_strs, str8_lit("\n"));
}
str8_list_push(mg_arena, &md_strs, str8_lit("\n"));
}
if(md_node_has_tag(piece, str8_lit("unordered_list"), 0))
{
String8List gen_strings = mg_string_list_from_table_gen(mg_arena, table_grid_map, table_col_map, str8_lit(""), piece);
for(String8Node *n = gen_strings.first; n != 0; n = n->next)
{
str8_list_pushf(mg_arena, &md_strs, " - ");
String8 item_text = n->string;
String8List item_lines = mg_wrapped_lines_from_string(mg_arena, item_text, 80-3, 80, 3);
for(String8Node *line_n = item_lines.first; line_n != 0; line_n = line_n->next)
{
str8_list_push(mg_arena, &md_strs, line_n->string);
str8_list_pushf(mg_arena, &md_strs, "\n");
}
}
str8_list_pushf(mg_arena, &md_strs, "\n");
}
}
String8 output_path = push_str8f(mg_arena, "%S/%S.md", build_dir_path, node->string);
FILE *file = fopen((char *)output_path.str, "w");
for(String8Node *n = md_strs.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, file);
}
fclose(file);
}
}
}
//////////////////////////////
//- rjf: write all layer output files
//
DeferLoop(printf("generating layer code..."), printf("\n"))
{
for(U64 slot_idx = 0; slot_idx < mg_state->slots_count; slot_idx += 1)
{
MG_LayerSlot *slot = &mg_state->slots[slot_idx];
for(MG_LayerNode *n = slot->first; n != 0; n = n->next)
{
MG_Layer *layer = &n->v;
String8 layer_generated_folder = {0};
if(layer->gen_folder_name.size != 0)
{
String8 gen_folder = layer->gen_folder_name;
layer_generated_folder = push_str8f(mg_arena, "%S/%S", code_dir_path, gen_folder);
}
else
{
String8 gen_folder = str8_lit("generated");
layer_generated_folder = push_str8f(mg_arena, "%S/%S/%S", code_dir_path, layer->key, gen_folder);
}
if(os_make_directory(layer_generated_folder))
{
String8List layer_key_parts = str8_split_path(mg_arena, layer->key);
StringJoin join = {0};
join.sep = str8_lit("_");
String8 layer_key_filename = str8_list_join(mg_arena, &layer_key_parts, &join);
String8 layer_key_filename_upper = upper_from_str8(mg_arena, layer_key_filename);
String8 h_path = push_str8f(mg_arena, "%S/%S.meta.h", layer_generated_folder, layer_key_filename);
String8 c_path = push_str8f(mg_arena, "%S/%S.meta.c", layer_generated_folder, layer_key_filename);
if(layer->h_name_override.size != 0)
{
h_path = push_str8f(mg_arena, "%S/%S", layer_generated_folder, str8_skip_last_slash(layer->h_name_override));
}
if(layer->c_name_override.size != 0)
{
c_path = push_str8f(mg_arena, "%S/%S", layer_generated_folder, str8_skip_last_slash(layer->c_name_override));
}
{
FILE *h = fopen((char *)h_path.str, "w");
//fprintf(h, "// Copyright (c) 2024 Epic Games Tools\n");
//fprintf(h, "// Licensed under the MIT license (https://opensource.org/license/mit/)\n\n");
if(layer->h_header.first == 0)
{
fprintf(h, "//- GENERATED CODE\n\n");
fprintf(h, "#ifndef %.*s_META_H\n", str8_varg(layer_key_filename_upper));
fprintf(h, "#define %.*s_META_H\n\n", str8_varg(layer_key_filename_upper));
}
else for(String8Node *n = layer->h_header.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
for(String8Node *n = layer->enums.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
for(String8Node *n = layer->structs.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
for(String8Node *n = layer->h_catchall.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
for(String8Node *n = layer->h_functions.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
if(layer->h_tables.first != 0)
{
if(!layer->is_library)
{
//fprintf(h, "C_LINKAGE_BEGIN\n");
}
for(String8Node *n = layer->h_tables.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
fprintf(h, "\n");
if(!layer->is_library)
{
//fprintf(h, "C_LINKAGE_END\n\n");
}
}
if(layer->h_footer.first == 0)
{
fprintf(h, "#endif // %.*s_META_H\n", str8_varg(layer_key_filename_upper));
}
else for(String8Node *n = layer->h_footer.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, h);
}
fclose(h);
}
{
FILE *c = fopen((char *)c_path.str, "w");
//fprintf(c, "// Copyright (c) 2024 Epic Games Tools\n");
//fprintf(c, "// Licensed under the MIT license (https://opensource.org/license/mit/)\n\n");
if(layer->c_header.first == 0)
{
fprintf(c, "//- GENERATED CODE\n\n");
}
else for(String8Node *n = layer->c_header.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
for(String8Node *n = layer->c_catchall.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
if(layer->c_tables.first != 0)
{
if(!layer->is_library)
{
//fprintf(c, "C_LINKAGE_BEGIN\n");
}
for(String8Node *n = layer->c_tables.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
if(!layer->is_library)
{
//fprintf(c, "C_LINKAGE_END\n\n");
}
}
for(String8Node *n = layer->c_functions.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
if(layer->c_footer.first != 0)
{
for(String8Node *n = layer->c_footer.first; n != 0; n = n->next)
{
fwrite(n->string.str, n->string.size, 1, c);
}
}
fclose(c);
}
}
}
}
}
//////////////////////////////
//- rjf: write out all messages to stderr
//
for(MG_MsgNode *n = msgs.first; n != 0; n = n->next)
{
MG_Msg *msg = &n->v;
fprintf(stderr, "%.*s: %.*s: %.*s\n", str8_varg(msg->location), str8_varg(msg->kind), str8_varg(msg->msg));
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,137 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_CORE_LINUX_H
#define OS_CORE_LINUX_H
////////////////////////////////
//~ rjf: Includes
#define _GNU_SOURCE
#include <dirent.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <features.h>
#include <linux/limits.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/random.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
pid_t gettid(void);
int pthread_setname_np(pthread_t thread, const char *name);
int pthread_getname_np(pthread_t thread, char *name, size_t size);
typedef struct tm tm;
typedef struct timespec timespec;
////////////////////////////////
//~ rjf: File Iterator
typedef struct OS_LNX_FileIter OS_LNX_FileIter;
struct OS_LNX_FileIter
{
DIR *dir;
struct dirent *dp;
String8 path;
};
StaticAssert(sizeof(Member(OS_FileIter, memory)) >= sizeof(OS_LNX_FileIter), os_lnx_file_iter_size_check);
////////////////////////////////
//~ rjf: Safe Call Handler Chain
typedef struct OS_LNX_SafeCallChain OS_LNX_SafeCallChain;
struct OS_LNX_SafeCallChain
{
OS_LNX_SafeCallChain *next;
OS_ThreadFunctionType *fail_handler;
void *ptr;
};
////////////////////////////////
//~ rjf: Entities
typedef enum OS_LNX_EntityKind
{
OS_LNX_EntityKind_Thread,
OS_LNX_EntityKind_Mutex,
OS_LNX_EntityKind_RWMutex,
OS_LNX_EntityKind_ConditionVariable,
}
OS_LNX_EntityKind;
typedef struct OS_LNX_Entity OS_LNX_Entity;
struct OS_LNX_Entity
{
OS_LNX_Entity *next;
OS_LNX_EntityKind kind;
union
{
struct
{
pthread_t handle;
OS_ThreadFunctionType *func;
void *ptr;
} thread;
pthread_mutex_t mutex_handle;
pthread_rwlock_t rwmutex_handle;
struct
{
pthread_cond_t cond_handle;
pthread_mutex_t rwlock_mutex_handle;
} cv;
};
};
////////////////////////////////
//~ rjf: State
typedef struct OS_LNX_State OS_LNX_State;
struct OS_LNX_State
{
Arena *arena;
OS_SystemInfo system_info;
OS_ProcessInfo process_info;
pthread_mutex_t entity_mutex;
Arena *entity_arena;
OS_LNX_Entity *entity_free;
};
////////////////////////////////
//~ rjf: Globals
global OS_LNX_State os_lnx_state = {0};
thread_static OS_LNX_SafeCallChain *os_lnx_safe_call_chain = 0;
////////////////////////////////
//~ rjf: Helpers
internal DateTime os_lnx_date_time_from_tm(tm in, U32 msec);
internal tm os_lnx_tm_from_date_time(DateTime dt);
internal timespec os_lnx_timespec_from_date_time(DateTime dt);
internal DenseTime os_lnx_dense_time_from_timespec(timespec in);
internal FileProperties os_lnx_file_properties_from_stat(struct stat *s);
internal void os_lnx_safe_call_sig_handler(int x);
////////////////////////////////
//~ rjf: Entities
internal OS_LNX_Entity *os_lnx_entity_alloc(OS_LNX_EntityKind kind);
internal void os_lnx_entity_release(OS_LNX_Entity *entity);
////////////////////////////////
//~ rjf: Thread Entry Point
internal void *os_lnx_thread_entry_point(void *ptr);
#endif // OS_CORE_LINUX_H

View File

@ -0,0 +1,240 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Handle Type Functions (Helpers, Implemented Once)
internal OS_Handle
os_handle_zero(void)
{
OS_Handle handle = {0};
return handle;
}
internal B32
os_handle_match(OS_Handle a, OS_Handle b)
{
return a.u64[0] == b.u64[0];
}
internal void
os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle)
{
OS_HandleNode *n = push_array(arena, OS_HandleNode, 1);
n->v = handle;
SLLQueuePush(handles->first, handles->last, n);
handles->count += 1;
}
internal OS_HandleArray
os_handle_array_from_list(Arena *arena, OS_HandleList *list)
{
OS_HandleArray result = {0};
result.count = list->count;
result.v = push_array_no_zero(arena, OS_Handle, result.count);
U64 idx = 0;
for(OS_HandleNode *n = list->first; n != 0; n = n->next, idx += 1)
{
result.v[idx] = n->v;
}
return result;
}
////////////////////////////////
//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once)
internal String8List
os_string_list_from_argcv(Arena *arena, int argc, char **argv)
{
String8List result = {0};
for(int i = 0; i < argc; i += 1)
{
String8 str = str8_cstring(argv[i]);
str8_list_push(arena, &result, str);
}
return result;
}
////////////////////////////////
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
internal String8
os_data_from_file_path(Arena *arena, String8 path)
{
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, path);
FileProperties props = os_properties_from_file(file);
String8 data = os_string_from_file_range(arena, file, r1u64(0, props.size));
os_file_close(file);
return data;
}
internal B32
os_write_data_to_file_path(String8 path, String8 data)
{
B32 good = 0;
OS_Handle file = os_file_open(OS_AccessFlag_Write, path);
if(!os_handle_match(file, os_handle_zero()))
{
good = 1;
os_file_write(file, r1u64(0, data.size), data.str);
os_file_close(file);
}
return good;
}
internal B32
os_write_data_list_to_file_path(String8 path, String8List list)
{
B32 good = 0;
OS_Handle file = os_file_open(OS_AccessFlag_Write, path);
if(!os_handle_match(file, os_handle_zero()))
{
good = 1;
U64 off = 0;
for(String8Node *n = list.first; n != 0; n = n->next)
{
os_file_write(file, r1u64(off, off+n->string.size), n->string.str);
off += n->string.size;
}
os_file_close(file);
}
return good;
}
internal B32
os_append_data_to_file_path(String8 path, String8 data)
{
B32 good = 0;
if(data.size != 0)
{
OS_Handle file = os_file_open(OS_AccessFlag_Write|OS_AccessFlag_Append, path);
if(!os_handle_match(file, os_handle_zero()))
{
good = 1;
U64 pos = os_properties_from_file(file).size;
os_file_write(file, r1u64(pos, pos+data.size), data.str);
os_file_close(file);
}
}
return good;
}
internal OS_FileID
os_id_from_file_path(String8 path)
{
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead, path);
OS_FileID id = os_id_from_file(file);
os_file_close(file);
return id;
}
internal S64
os_file_id_compare(OS_FileID a, OS_FileID b)
{
S64 cmp = MemoryCompare((void*)&a.v[0], (void*)&b.v[0], sizeof(a.v));
return cmp;
}
internal String8
os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range)
{
U64 pre_pos = arena_pos(arena);
String8 result;
result.size = dim_1u64(range);
result.str = push_array_no_zero(arena, U8, result.size);
U64 actual_read_size = os_file_read(file, range, result.str);
if(actual_read_size < result.size)
{
arena_pop_to(arena, pre_pos + actual_read_size);
result.size = actual_read_size;
}
return result;
}
////////////////////////////////
//~ rjf: Process Launcher Helpers
internal OS_Handle
os_cmd_line_launch(String8 string)
{
Temp scratch = scratch_begin(0, 0);
U8 split_chars[] = {' '};
String8List parts = str8_split(scratch.arena, string, split_chars, ArrayCount(split_chars), 0);
OS_Handle handle = {0};
if(parts.node_count != 0)
{
// rjf: unpack exe part
String8 exe = parts.first->string;
String8 exe_folder = str8_chop_last_slash(exe);
if(exe_folder.size == 0)
{
exe_folder = os_get_current_path(scratch.arena);
}
// rjf: find stdout delimiter
String8Node *stdout_delimiter_n = 0;
for(String8Node *n = parts.first; n != 0; n = n->next)
{
if(str8_match(n->string, str8_lit(">"), 0))
{
stdout_delimiter_n = n;
break;
}
}
// rjf: read stdout path
String8 stdout_path = {0};
if(stdout_delimiter_n && stdout_delimiter_n->next)
{
stdout_path = stdout_delimiter_n->next->string;
}
// rjf: open stdout handle
OS_Handle stdout_handle = {0};
if(stdout_path.size != 0)
{
OS_Handle file = os_file_open(OS_AccessFlag_Write|OS_AccessFlag_Read, stdout_path);
os_file_close(file);
stdout_handle = os_file_open(OS_AccessFlag_Write|OS_AccessFlag_Append|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite|OS_AccessFlag_Inherited, stdout_path);
}
// rjf: form command line
String8List cmdline = {0};
for(String8Node *n = parts.first; n != stdout_delimiter_n && n != 0; n = n->next)
{
str8_list_push(scratch.arena, &cmdline, n->string);
}
// rjf: launch
OS_ProcessLaunchParams params = {0};
params.cmd_line = cmdline;
params.path = exe_folder;
params.inherit_env = 1;
params.stdout_file = stdout_handle;
handle = os_process_launch(&params);
// rjf: close stdout handle
{
if(stdout_path.size != 0)
{
os_file_close(stdout_handle);
}
}
}
scratch_end(scratch);
return handle;
}
internal OS_Handle
os_cmd_line_launchf(char *fmt, ...)
{
Temp scratch = scratch_begin(0, 0);
va_list args;
va_start(args, fmt);
String8 string = push_str8fv(scratch.arena, fmt, args);
OS_Handle result = os_cmd_line_launch(string);
va_end(args);
scratch_end(scratch);
return result;
}

View File

@ -0,0 +1,335 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_CORE_H
#define OS_CORE_H
////////////////////////////////
//~ rjf: System Info
typedef struct OS_SystemInfo OS_SystemInfo;
struct OS_SystemInfo
{
U32 logical_processor_count;
U64 page_size;
U64 large_page_size;
U64 allocation_granularity;
String8 machine_name;
};
////////////////////////////////
//~ rjf: Process Info
typedef struct OS_ProcessInfo OS_ProcessInfo;
struct OS_ProcessInfo
{
U32 pid;
B32 large_pages_allowed;
String8 binary_path;
String8 initial_path;
String8 user_program_data_path;
String8List module_load_paths;
String8List environment;
};
////////////////////////////////
//~ rjf: Access Flags
typedef U32 OS_AccessFlags;
enum
{
OS_AccessFlag_Read = (1<<0),
OS_AccessFlag_Write = (1<<1),
OS_AccessFlag_Execute = (1<<2),
OS_AccessFlag_Append = (1<<3),
OS_AccessFlag_ShareRead = (1<<4),
OS_AccessFlag_ShareWrite = (1<<5),
OS_AccessFlag_Inherited = (1<<6),
};
////////////////////////////////
//~ rjf: Files
typedef U32 OS_FileIterFlags;
enum
{
OS_FileIterFlag_SkipFolders = (1 << 0),
OS_FileIterFlag_SkipFiles = (1 << 1),
OS_FileIterFlag_SkipHiddenFiles = (1 << 2),
OS_FileIterFlag_Done = (1 << 31),
};
typedef struct OS_FileIter OS_FileIter;
struct OS_FileIter
{
OS_FileIterFlags flags;
U8 memory[800];
};
typedef struct OS_FileInfo OS_FileInfo;
struct OS_FileInfo
{
String8 name;
FileProperties props;
};
// nick: on-disk file identifier
typedef struct OS_FileID OS_FileID;
struct OS_FileID
{
U64 v[3];
};
////////////////////////////////
//~ rjf: Handle Type
typedef struct OS_Handle OS_Handle;
struct OS_Handle
{
U64 u64[1];
};
typedef struct OS_HandleNode OS_HandleNode;
struct OS_HandleNode
{
OS_HandleNode *next;
OS_Handle v;
};
typedef struct OS_HandleList OS_HandleList;
struct OS_HandleList
{
OS_HandleNode *first;
OS_HandleNode *last;
U64 count;
};
typedef struct OS_HandleArray OS_HandleArray;
struct OS_HandleArray
{
OS_Handle *v;
U64 count;
};
////////////////////////////////
//~ rjf: Process Launch Parameters
typedef struct OS_ProcessLaunchParams OS_ProcessLaunchParams;
struct OS_ProcessLaunchParams
{
String8List cmd_line;
String8 path;
String8List env;
B32 inherit_env;
B32 debug_subprocesses;
B32 consoleless;
OS_Handle stdout_file;
OS_Handle stderr_file;
OS_Handle stdin_file;
};
////////////////////////////////
//~ rjf: Thread Types
typedef void OS_ThreadFunctionType(void *ptr);
////////////////////////////////
//~ rjf: Handle Type Functions (Helpers, Implemented Once)
internal OS_Handle os_handle_zero(void);
internal B32 os_handle_match(OS_Handle a, OS_Handle b);
internal void os_handle_list_push(Arena *arena, OS_HandleList *handles, OS_Handle handle);
internal OS_HandleArray os_handle_array_from_list(Arena *arena, OS_HandleList *list);
////////////////////////////////
//~ rjf: Command Line Argc/Argv Helper (Helper, Implemented Once)
internal String8List os_string_list_from_argcv(Arena *arena, int argc, char **argv);
////////////////////////////////
//~ rjf: Filesystem Helpers (Helpers, Implemented Once)
internal String8 os_data_from_file_path(Arena *arena, String8 path);
internal B32 os_write_data_to_file_path(String8 path, String8 data);
internal B32 os_write_data_list_to_file_path(String8 path, String8List list);
internal B32 os_append_data_to_file_path(String8 path, String8 data);
internal OS_FileID os_id_from_file_path(String8 path);
internal S64 os_file_id_compare(OS_FileID a, OS_FileID b);
internal String8 os_string_from_file_range(Arena *arena, OS_Handle file, Rng1U64 range);
////////////////////////////////
//~ rjf: Process Launcher Helpers
internal OS_Handle os_cmd_line_launch(String8 string);
internal OS_Handle os_cmd_line_launchf(char *fmt, ...);
////////////////////////////////
//~ rjf: @os_hooks System/Process Info (Implemented Per-OS)
internal OS_SystemInfo *os_get_system_info(void);
internal OS_ProcessInfo *os_get_process_info(void);
internal String8 os_get_current_path(Arena *arena);
internal U32 os_get_process_start_time_unix(void);
////////////////////////////////
//~ rjf: @os_hooks Memory Allocation (Implemented Per-OS)
//- rjf: basic
internal void *os_reserve(U64 size);
internal B32 os_commit(void *ptr, U64 size);
internal void os_decommit(void *ptr, U64 size);
internal void os_release(void *ptr, U64 size);
//- rjf: large pages
internal void *os_reserve_large(U64 size);
internal B32 os_commit_large(void *ptr, U64 size);
////////////////////////////////
//~ rjf: @os_hooks Thread Info (Implemented Per-OS)
internal U32 os_tid(void);
internal void os_set_thread_name(String8 string);
////////////////////////////////
//~ rjf: @os_hooks Aborting (Implemented Per-OS)
internal void os_abort(S32 exit_code);
////////////////////////////////
//~ rjf: @os_hooks File System (Implemented Per-OS)
//- rjf: files
internal OS_Handle os_file_open(OS_AccessFlags flags, String8 path);
internal void os_file_close(OS_Handle file);
internal U64 os_file_read(OS_Handle file, Rng1U64 rng, void *out_data);
#define os_file_read_struct(f, off, ptr) os_file_read((f), r1u64((off), (off)+sizeof(*(ptr))), (ptr))
internal U64 os_file_write(OS_Handle file, Rng1U64 rng, void *data);
internal B32 os_file_set_times(OS_Handle file, DateTime time);
internal FileProperties os_properties_from_file(OS_Handle file);
internal OS_FileID os_id_from_file(OS_Handle file);
internal B32 os_file_reserve_size(OS_Handle file, U64 size);
internal B32 os_delete_file_at_path(String8 path);
internal B32 os_copy_file_path(String8 dst, String8 src);
internal B32 os_move_file_path(String8 dst, String8 src);
internal String8 os_full_path_from_path(Arena *arena, String8 path);
internal B32 os_file_path_exists(String8 path);
internal B32 os_folder_path_exists(String8 path);
internal FileProperties os_properties_from_file_path(String8 path);
//- rjf: file maps
internal OS_Handle os_file_map_open(OS_AccessFlags flags, OS_Handle file);
internal void os_file_map_close(OS_Handle map);
internal void * os_file_map_view_open(OS_Handle map, OS_AccessFlags flags, Rng1U64 range);
internal void os_file_map_view_close(OS_Handle map, void *ptr, Rng1U64 range);
//- rjf: directory iteration
internal OS_FileIter *os_file_iter_begin(Arena *arena, String8 path, OS_FileIterFlags flags);
internal B32 os_file_iter_next(Arena *arena, OS_FileIter *iter, OS_FileInfo *info_out);
internal void os_file_iter_end(OS_FileIter *iter);
//- rjf: directory creation
internal B32 os_make_directory(String8 path);
////////////////////////////////
//~ rjf: @os_hooks Shared Memory (Implemented Per-OS)
internal OS_Handle os_shared_memory_alloc(U64 size, String8 name);
internal OS_Handle os_shared_memory_open(String8 name);
internal void os_shared_memory_close(OS_Handle handle);
internal void * os_shared_memory_view_open(OS_Handle handle, Rng1U64 range);
internal void os_shared_memory_view_close(OS_Handle handle, void *ptr, Rng1U64 range);
////////////////////////////////
//~ rjf: @os_hooks Time (Implemented Per-OS)
internal U64 os_now_microseconds(void);
internal U32 os_now_unix(void);
internal DateTime os_now_universal_time(void);
internal DateTime os_universal_time_from_local(DateTime *local_time);
internal DateTime os_local_time_from_universal(DateTime *universal_time);
internal void os_sleep_milliseconds(U32 msec);
////////////////////////////////
//~ rjf: @os_hooks Child Processes (Implemented Per-OS)
internal OS_Handle os_process_launch(OS_ProcessLaunchParams *params);
internal B32 os_process_join(OS_Handle handle, U64 endt_us);
internal void os_process_detach(OS_Handle handle);
////////////////////////////////
//~ rjf: @os_hooks Threads (Implemented Per-OS)
internal OS_Handle os_thread_launch(OS_ThreadFunctionType *func, void *ptr, void *params);
internal B32 os_thread_join(OS_Handle handle, U64 endt_us);
internal void os_thread_detach(OS_Handle handle);
////////////////////////////////
//~ rjf: @os_hooks Synchronization Primitives (Implemented Per-OS)
//- rjf: recursive mutexes
internal OS_Handle os_mutex_alloc(void);
internal void os_mutex_release(OS_Handle mutex);
internal void os_mutex_take(OS_Handle mutex);
internal void os_mutex_drop(OS_Handle mutex);
//- rjf: reader/writer mutexes
internal OS_Handle os_rw_mutex_alloc(void);
internal void os_rw_mutex_release(OS_Handle rw_mutex);
internal void os_rw_mutex_take_r(OS_Handle mutex);
internal void os_rw_mutex_drop_r(OS_Handle mutex);
internal void os_rw_mutex_take_w(OS_Handle mutex);
internal void os_rw_mutex_drop_w(OS_Handle mutex);
//- rjf: condition variables
internal OS_Handle os_condition_variable_alloc(void);
internal void os_condition_variable_release(OS_Handle cv);
// returns false on timeout, true on signal, (max_wait_ms = max_U64) -> no timeout
internal B32 os_condition_variable_wait(OS_Handle cv, OS_Handle mutex, U64 endt_us);
internal B32 os_condition_variable_wait_rw_r(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us);
internal B32 os_condition_variable_wait_rw_w(OS_Handle cv, OS_Handle mutex_rw, U64 endt_us);
internal void os_condition_variable_signal(OS_Handle cv);
internal void os_condition_variable_broadcast(OS_Handle cv);
//- rjf: cross-process semaphores
internal OS_Handle os_semaphore_alloc(U32 initial_count, U32 max_count, String8 name);
internal void os_semaphore_release(OS_Handle semaphore);
internal OS_Handle os_semaphore_open(String8 name);
internal void os_semaphore_close(OS_Handle semaphore);
internal B32 os_semaphore_take(OS_Handle semaphore, U64 endt_us);
internal void os_semaphore_drop(OS_Handle semaphore);
//- rjf: scope macros
#define OS_MutexScope(mutex) DeferLoop(os_mutex_take(mutex), os_mutex_drop(mutex))
#define OS_MutexScopeR(mutex) DeferLoop(os_rw_mutex_take_r(mutex), os_rw_mutex_drop_r(mutex))
#define OS_MutexScopeW(mutex) DeferLoop(os_rw_mutex_take_w(mutex), os_rw_mutex_drop_w(mutex))
#define OS_MutexScopeRWPromote(mutex) DeferLoop((os_rw_mutex_drop_r(mutex), os_rw_mutex_take_w(mutex)), (os_rw_mutex_drop_w(mutex), os_rw_mutex_take_r(mutex)))
////////////////////////////////
//~ rjf: @os_hooks Dynamically-Loaded Libraries (Implemented Per-OS)
internal OS_Handle os_library_open(String8 path);
internal void os_library_close(OS_Handle lib);
internal VoidProc *os_library_load_proc(OS_Handle lib, String8 name);
////////////////////////////////
//~ rjf: @os_hooks Safe Calls (Implemented Per-OS)
internal void os_safe_call(OS_ThreadFunctionType *func, OS_ThreadFunctionType *fail_handler, void *ptr);
////////////////////////////////
//~ rjf: @os_hooks GUIDs (Implemented Per-OS)
internal Guid os_make_guid(void);
////////////////////////////////
//~ rjf: @os_hooks Entry Points (Implemented Per-OS)
// NOTE(rjf): The implementation of `os_core` will define low-level entry
// points if BUILD_ENTRY_DEFINING_UNIT is defined to 1. These will call
// into the standard codebase program entry points, named "entry_point".
#if BUILD_ENTRY_DEFINING_UNIT
internal void entry_point(CmdLine *cmdline);
#endif
#endif // OS_CORE_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,121 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_CORE_WIN32_H
#define OS_CORE_WIN32_H
////////////////////////////////
//~ rjf: Includes / Libraries
#include <windows.h>
#include <windowsx.h>
#include <timeapi.h>
#include <tlhelp32.h>
#include <Shlobj.h>
#include <processthreadsapi.h>
#pragma comment(lib, "user32")
#pragma comment(lib, "winmm")
#pragma comment(lib, "shell32")
#pragma comment(lib, "advapi32")
#pragma comment(lib, "rpcrt4")
#pragma comment(lib, "shlwapi")
#pragma comment(lib, "comctl32")
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") // this is required for loading correct comctl32 dll file
////////////////////////////////
//~ rjf: File Iterator Types
typedef struct OS_W32_FileIter OS_W32_FileIter;
struct OS_W32_FileIter
{
HANDLE handle;
WIN32_FIND_DATAW find_data;
B32 is_volume_iter;
String8Array drive_strings;
U64 drive_strings_iter_idx;
};
StaticAssert(sizeof(Member(OS_FileIter, memory)) >= sizeof(OS_W32_FileIter), file_iter_memory_size);
////////////////////////////////
//~ rjf: Entity Types
typedef enum OS_W32_EntityKind
{
OS_W32_EntityKind_Null,
OS_W32_EntityKind_Thread,
OS_W32_EntityKind_Mutex,
OS_W32_EntityKind_RWMutex,
OS_W32_EntityKind_ConditionVariable,
}
OS_W32_EntityKind;
typedef struct OS_W32_Entity OS_W32_Entity;
struct OS_W32_Entity
{
OS_W32_Entity *next;
OS_W32_EntityKind kind;
union
{
struct
{
OS_ThreadFunctionType *func;
void *ptr;
HANDLE handle;
DWORD tid;
} thread;
CRITICAL_SECTION mutex;
SRWLOCK rw_mutex;
CONDITION_VARIABLE cv;
};
};
////////////////////////////////
//~ rjf: State
typedef struct OS_W32_State OS_W32_State;
struct OS_W32_State
{
Arena *arena;
// rjf: info
OS_SystemInfo system_info;
OS_ProcessInfo process_info;
U64 microsecond_resolution;
// rjf: entity storage
CRITICAL_SECTION entity_mutex;
Arena *entity_arena;
OS_W32_Entity *entity_free;
};
////////////////////////////////
//~ rjf: Globals
global OS_W32_State os_w32_state = {0};
////////////////////////////////
//~ rjf: File Info Conversion Helpers
internal FilePropertyFlags os_w32_file_property_flags_from_dwFileAttributes(DWORD dwFileAttributes);
internal void os_w32_file_properties_from_attribute_data(FileProperties *properties, WIN32_FILE_ATTRIBUTE_DATA *attributes);
////////////////////////////////
//~ rjf: Time Conversion Helpers
internal void os_w32_date_time_from_system_time(DateTime *out, SYSTEMTIME *in);
internal void os_w32_system_time_from_date_time(SYSTEMTIME *out, DateTime *in);
internal void os_w32_dense_time_from_file_time(DenseTime *out, FILETIME *in);
internal U32 os_w32_sleep_ms_from_endt_us(U64 endt_us);
////////////////////////////////
//~ rjf: Entity Functions
internal OS_W32_Entity *os_w32_entity_alloc(OS_W32_EntityKind kind);
internal void os_w32_entity_release(OS_W32_Entity *entity);
////////////////////////////////
//~ rjf: Thread Entry Point
internal DWORD os_w32_thread_entry_point(void *ptr);
#endif // OS_CORE_WIN32_H

View File

@ -0,0 +1,10 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "metagen_os/core/metagen_os_core.c"
#if OS_WINDOWS
# include "metagen_os/core/win32/metagen_os_core_win32.c"
#else
# error OS core layer not implemented for this operating system.
#endif

View File

@ -0,0 +1,19 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef OS_INC_H
#define OS_INC_H
#if !defined(OS_FEATURE_GRAPHICAL)
# define OS_FEATURE_GRAPHICAL 0
#endif
#include "metagen_os/core/metagen_os_core.h"
#if OS_WINDOWS
# include "metagen_os/core/win32/metagen_os_core_win32.h"
#else
# error OS core layer not implemented for this operating system.
#endif
#endif // OS_INC_H

View File

@ -1,3 +1,5 @@
//- GENERATED CODE
char * os_key_display_string[113] = char * os_key_display_string[113] =
{ {
"Null", "Null",

124
src/os/generated/os.meta.h Normal file
View File

@ -0,0 +1,124 @@
//- GENERATED CODE
#ifndef OS_META_H
#define OS_META_H
typedef enum OS_Key
{
OS_Key_Null,
OS_Key_Esc,
OS_Key_F1,
OS_Key_F2,
OS_Key_F3,
OS_Key_F4,
OS_Key_F5,
OS_Key_F6,
OS_Key_F7,
OS_Key_F8,
OS_Key_F9,
OS_Key_F10,
OS_Key_F11,
OS_Key_F12,
OS_Key_F13,
OS_Key_F14,
OS_Key_F15,
OS_Key_F16,
OS_Key_F17,
OS_Key_F18,
OS_Key_F19,
OS_Key_F20,
OS_Key_F21,
OS_Key_F22,
OS_Key_F23,
OS_Key_F24,
OS_Key_Tick,
OS_Key_0,
OS_Key_1,
OS_Key_2,
OS_Key_3,
OS_Key_4,
OS_Key_5,
OS_Key_6,
OS_Key_7,
OS_Key_8,
OS_Key_9,
OS_Key_Minus,
OS_Key_Equal,
OS_Key_Backspace,
OS_Key_Tab,
OS_Key_Q,
OS_Key_W,
OS_Key_E,
OS_Key_R,
OS_Key_T,
OS_Key_Y,
OS_Key_U,
OS_Key_I,
OS_Key_O,
OS_Key_P,
OS_Key_LeftBracket,
OS_Key_RightBracket,
OS_Key_BackSlash,
OS_Key_CapsLock,
OS_Key_A,
OS_Key_S,
OS_Key_D,
OS_Key_F,
OS_Key_G,
OS_Key_H,
OS_Key_J,
OS_Key_K,
OS_Key_L,
OS_Key_Semicolon,
OS_Key_Quote,
OS_Key_Return,
OS_Key_Shift,
OS_Key_Z,
OS_Key_X,
OS_Key_C,
OS_Key_V,
OS_Key_B,
OS_Key_N,
OS_Key_M,
OS_Key_Comma,
OS_Key_Period,
OS_Key_Slash,
OS_Key_Ctrl,
OS_Key_Alt,
OS_Key_Space,
OS_Key_Menu,
OS_Key_ScrollLock,
OS_Key_Pause,
OS_Key_Insert,
OS_Key_Home,
OS_Key_PageUp,
OS_Key_Delete,
OS_Key_End,
OS_Key_PageDown,
OS_Key_Up,
OS_Key_Left,
OS_Key_Down,
OS_Key_Right,
OS_Key_NumLock,
OS_Key_NumSlash,
OS_Key_NumStar,
OS_Key_NumMinus,
OS_Key_NumPlus,
OS_Key_NumPeriod,
OS_Key_Num0,
OS_Key_Num1,
OS_Key_Num2,
OS_Key_Num3,
OS_Key_Num4,
OS_Key_Num5,
OS_Key_Num6,
OS_Key_Num7,
OS_Key_Num8,
OS_Key_Num9,
OS_Key_LeftMouseButton,
OS_Key_MiddleMouseButton,
OS_Key_RightMouseButton,
OS_Key_OS_Key_COUNT,
} OS_Key;
#endif // OS_META_H

View File

@ -1,121 +0,0 @@
typedef enum OS_Key
{
OS_Key_Null,
OS_Key_Esc,
OS_Key_F1,
OS_Key_F2,
OS_Key_F3,
OS_Key_F4,
OS_Key_F5,
OS_Key_F6,
OS_Key_F7,
OS_Key_F8,
OS_Key_F9,
OS_Key_F10,
OS_Key_F11,
OS_Key_F12,
OS_Key_F13,
OS_Key_F14,
OS_Key_F15,
OS_Key_F16,
OS_Key_F17,
OS_Key_F18,
OS_Key_F19,
OS_Key_F20,
OS_Key_F21,
OS_Key_F22,
OS_Key_F23,
OS_Key_F24,
OS_Key_Tick,
OS_Key_0,
OS_Key_1,
OS_Key_2,
OS_Key_3,
OS_Key_4,
OS_Key_5,
OS_Key_6,
OS_Key_7,
OS_Key_8,
OS_Key_9,
OS_Key_Minus,
OS_Key_Equal,
OS_Key_Backspace,
OS_Key_Tab,
OS_Key_Q,
OS_Key_W,
OS_Key_E,
OS_Key_R,
OS_Key_T,
OS_Key_Y,
OS_Key_U,
OS_Key_I,
OS_Key_O,
OS_Key_P,
OS_Key_LeftBracket,
OS_Key_RightBracket,
OS_Key_BackSlash,
OS_Key_CapsLock,
OS_Key_A,
OS_Key_S,
OS_Key_D,
OS_Key_F,
OS_Key_G,
OS_Key_H,
OS_Key_J,
OS_Key_K,
OS_Key_L,
OS_Key_Semicolon,
OS_Key_Quote,
OS_Key_Return,
OS_Key_Shift,
OS_Key_Z,
OS_Key_X,
OS_Key_C,
OS_Key_V,
OS_Key_B,
OS_Key_N,
OS_Key_M,
OS_Key_Comma,
OS_Key_Period,
OS_Key_Slash,
OS_Key_Ctrl,
OS_Key_Alt,
OS_Key_Space,
OS_Key_Menu,
OS_Key_ScrollLock,
OS_Key_Pause,
OS_Key_Insert,
OS_Key_Home,
OS_Key_PageUp,
OS_Key_Delete,
OS_Key_End,
OS_Key_PageDown,
OS_Key_Up,
OS_Key_Left,
OS_Key_Down,
OS_Key_Right,
OS_Key_NumLock,
OS_Key_NumSlash,
OS_Key_NumStar,
OS_Key_NumMinus,
OS_Key_NumPlus,
OS_Key_NumPeriod,
OS_Key_Num0,
OS_Key_Num1,
OS_Key_Num2,
OS_Key_Num3,
OS_Key_Num4,
OS_Key_Num5,
OS_Key_Num6,
OS_Key_Num7,
OS_Key_Num8,
OS_Key_Num9,
OS_Key_LeftMouseButton,
OS_Key_MiddleMouseButton,
OS_Key_RightMouseButton,
OS_Key_COUNT
}
OS_Key;
extern char * os_key_display_string[113];

View File

@ -1,3 +1,3 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ //~
#include "os/generated/os_core.meta.c" #include "os/generated/os.meta.c"

View File

@ -5,7 +5,7 @@
#define OS_MAX_EVENTS 2048 #define OS_MAX_EVENTS 2048
#include "os/generated/os_core.meta.h" #include "os/generated/os.meta.h"
typedef struct OS_Handle OS_Handle; typedef struct OS_Handle OS_Handle;
struct OS_Handle struct OS_Handle

View File

@ -125,15 +125,14 @@ OS_KeyTable:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ Generators //~ Generators
@table_gen_enum OS_Key: @enum OS_Key:
{ {
@expand(OS_KeyTable a) @expand(OS_KeyTable a) `$(a.name)`
` OS_Key_$(a.name),`; OS_Key_COUNT,
` OS_Key_COUNT`
} }
@table_gen_data(`char *`) os_key_display_string: @c_file @data(`char *`)
os_key_display_string:
{ {
@expand(OS_KeyTable a) @expand(OS_KeyTable a) `"$(a.display_string)"`;
`"$(a.display_string)",`;
} }

View File

@ -0,0 +1,16 @@
//- GENERATED CODE
const char *r_d3d12_g_vs_kind_source_table[R_D3D12_VS_Kind_COUNT];
internal void r_d3d12_init_vs_sources()
{
r_d3d12_g_vs_kind_source_table[R_D3D12_VS_Kind_Triangle] = r_d3d12_g_triangle_shader_src;
r_d3d12_g_vs_kind_source_table[R_D3D12_VS_Kind_Test] = r_d3d12_g_test_shader_src;
}
const char *r_d3d12_g_ps_kind_source_table[R_D3D12_PS_Kind_COUNT];
internal void r_d3d12_init_ps_sources()
{
r_d3d12_g_ps_kind_source_table[R_D3D12_PS_Kind_Triangle] = r_d3d12_g_triangle_shader_src;
r_d3d12_g_ps_kind_source_table[R_D3D12_PS_Kind_Test] = r_d3d12_g_test_shader_src;
}

View File

@ -0,0 +1,71 @@
//- GENERATED CODE
#ifndef RENDER_D3D12_META_H
#define RENDER_D3D12_META_H
typedef enum R_D3D12_VS_Kind
{
R_D3D12_VS_Kind_Triangle,
R_D3D12_VS_Kind_Test,
R_D3D12_VS_Kind_COUNT,
} R_D3D12_VS_Kind;
typedef enum R_D3D12_PS_Kind
{
R_D3D12_PS_Kind_Triangle,
R_D3D12_PS_Kind_Test,
R_D3D12_PS_Kind_COUNT,
} R_D3D12_PS_Kind;
read_only global const char* r_d3d12_g_triangle_shader_src =
""
"\n"
"struct PSInput\n"
"{\n"
" float4 position : SV_POSITION;\n"
" float4 color : COLOR;\n"
"};\n"
"\n"
"PSInput vs_main(float4 position : POSITION, float4 color : COLOR)\n"
"{\n"
" PSInput result;\n"
"\n"
" result.position = position;\n"
" result.color = color;\n"
"\n"
" return result;\n"
"}\n"
"\n"
"float4 ps_main(PSInput input) : SV_TARGET\n"
"{\n"
" return input.color;\n"
"}\n"
""
;
read_only global const char* r_d3d12_g_test_shader_src =
""
"\n"
"struct PSInput\n"
"{\n"
" float4 position : SV_POSITION;\n"
" float4 color : COLOR;\n"
"};\n"
"\n"
"PSInput vs_main(float4 position : POSITION, float4 color : COLOR)\n"
"{\n"
" PSInput result;\n"
"\n"
" result.position = position;\n"
" result.color = color;\n"
"\n"
" return result;\n"
"}\n"
"\n"
"float4 ps_main(PSInput input) : SV_TARGET\n"
"{\n"
" return input.color;\n"
"}\n"
""
;
#endif // RENDER_D3D12_META_H

View File

@ -1,17 +1,34 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ //~
#include <d3dcompiler.h>
#include "generated/render_d3d12.meta.c"
#pragma comment(lib, "d3d12.lib") #pragma comment(lib, "d3d12.lib")
#pragma comment(lib, "dxgi.lib") #pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "dxguid.lib") #pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "d3dcompiler.lib")
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ //~
#define D3D12_ERROR(msg, ...) LOG("D3D12 ERROR (anton): "); LOG(msg, __VA_ARGS__); Trap(); #define D3D12_ERROR(msg, ...) \
#define D3D12_CHECK(hr, msg) if(FAILED((hr))) { D3D12_ERROR(msg, __VA_ARGS__) } LOG("D3D12 ERROR (anton): "); \
LOG(msg, __VA_ARGS__); \
Trap();
#define D3D12_CHECK(hr, msg) \
if(FAILED((hr))) \
{ \
D3D12_ERROR(msg, __VA_ARGS__) \
}
#define D3D12_RELEASE(res) if( (res) && (res)->lpVtbl) { (res)->lpVtbl->Release((res)); (res) = 0; } #define D3D12_RELEASE(res) \
if((res) && (res)->lpVtbl) \
{ \
(res)->lpVtbl->Release((res)); \
(res) = 0; \
}
#define D3D12_SHADER_CHECK(hr, err_blob) r_d3d12_shader_check((hr), (err_blob))
global R_D3D12_State *r_d3d12_state = 0; global R_D3D12_State *r_d3d12_state = 0;
global R_D3D12_Command *r_d3d12_cmd = 0; global R_D3D12_Command *r_d3d12_cmd = 0;
@ -20,23 +37,77 @@ global R_D3D12_RenderTarget *r_d3d12_rt = 0;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ //~
internal U32 c_strlen(const char * str)
{
U32 out = 0;
char c = str[0];
while(c != '\0')
{
//LOG("c: %c ", c);
out += 1;
if(out > 1000000)
{
break;
}
c = *(&str[0] + out);
}
return out;
}
internal void internal void
r_init() r_d3d12_shader_check(HRESULT hr, ID3D10Blob *err_blob)
{
if(FAILED(hr))
{
LOG("Shader compilation failed: %.*s\n",
(int)ID3D10Blob_GetBufferSize(err_blob),
(char*)ID3D10Blob_GetBufferPointer(err_blob));
ID3D10Blob_Release(err_blob);
}
}
internal D3D12_SHADER_BYTECODE
r_d3d12_get_shader_bytecode(ID3DBlob *shader_blob)
{
D3D12_SHADER_BYTECODE out = {0};
out.pShaderBytecode = ID3D10Blob_GetBufferPointer(shader_blob);
out.BytecodeLength = ID3D10Blob_GetBufferSize(shader_blob);
return out;
}
internal const char *
r_d3d12_get_vs_shader_source(R_D3D12_VS_Kind kind)
{
return r_d3d12_g_vs_kind_source_table[kind];
}
internal const char *
r_d3d12_get_ps_shader_source(R_D3D12_PS_Kind kind)
{
return r_d3d12_g_ps_kind_source_table[kind];
}
internal void r_init()
{ {
Arena *arena = arena_alloc(); Arena *arena = arena_alloc();
r_d3d12_state = push_array(arena, R_D3D12_State, 1); r_d3d12_state = push_array(arena, R_D3D12_State, 1);
r_d3d12_state->arena = arena; r_d3d12_state->arena = arena;
r_d3d12_state->window_handle = g_win32_window_handle; r_d3d12_state->window_handle = g_win32_window_handle;
r_d3d12_cmd = push_array(arena, R_D3D12_Command, 1); r_d3d12_cmd = push_array(arena,
R_D3D12_Command,
1);
r_d3d12_cmd->arena = arena; r_d3d12_cmd->arena = arena;
r_d3d12_rt = push_array(arena, R_D3D12_RenderTarget, 1); r_d3d12_rt = push_array(arena,
R_D3D12_RenderTarget, 1);
r_d3d12_rt->arena = arena; r_d3d12_rt->arena = arena;
HRESULT hr; HRESULT hr;
hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void**)&r_d3d12_state->debug); hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&r_d3d12_state->debug);
if(SUCCEEDED(hr) && r_d3d12_state->debug ) if(SUCCEEDED(hr) && r_d3d12_state->debug)
{ {
r_d3d12_state->debug->lpVtbl->EnableDebugLayer(r_d3d12_state->debug); r_d3d12_state->debug->lpVtbl->EnableDebugLayer(r_d3d12_state->debug);
} }
@ -44,112 +115,105 @@ r_init()
{ {
D3D12_ERROR("Failed to create D3D12 Debug interface \n"); D3D12_ERROR("Failed to create D3D12 Debug interface \n");
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Create device // Create device
hr = D3D12CreateDevice(0, hr = D3D12CreateDevice(0,
D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_11_0,
&IID_ID3D12Device, &IID_ID3D12Device,
(void **)&r_d3d12_state->device); (void **)&r_d3d12_state->device);
D3D12_CHECK(hr, "Failed to create device\n"); D3D12_CHECK(hr, "Failed to create device\n");
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Create command queue // Create command queue
D3D12_COMMAND_QUEUE_DESC queue_desc = { D3D12_COMMAND_QUEUE_DESC queue_desc = {
.Type = D3D12_COMMAND_LIST_TYPE_DIRECT, .Type = D3D12_COMMAND_LIST_TYPE_DIRECT,
.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL, .Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL,
.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE, .Flags = D3D12_COMMAND_QUEUE_FLAG_NONE,
.NodeMask = 0 .NodeMask = 0
}; };
hr = ID3D12Device_CreateCommandQueue(r_d3d12_state->device, hr = ID3D12Device_CreateCommandQueue(r_d3d12_state->device,
&queue_desc, &queue_desc,
&IID_ID3D12CommandQueue, &IID_ID3D12CommandQueue,
(void**)&r_d3d12_cmd->queue); (void **)&r_d3d12_cmd->queue);
D3D12_CHECK(hr, "Failed to create command queue\n"); D3D12_CHECK(hr, "Failed to create command queue\n");
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Create DXGI factory // Create DXGI factory
IDXGIFactory4 *factory; IDXGIFactory4 *factory;
hr = CreateDXGIFactory1(&IID_IDXGIFactory4, (void**)&factory); hr = CreateDXGIFactory1(&IID_IDXGIFactory4, (void **)&factory);
D3D12_CHECK(hr, "Failed to create factory\n"); D3D12_CHECK(hr, "Failed to create factory\n");
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Create Swapchain // Create Swapchain
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = { DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {.Width = WINDOW_WIDTH_PX,
.Width = WINDOW_WIDTH_PX, .Height = WINDOW_HEIGHT_PX,
.Height = WINDOW_HEIGHT_PX, .Format = DXGI_FORMAT_R8G8B8A8_UNORM,
.Format = DXGI_FORMAT_R8G8B8A8_UNORM, .Stereo = FALSE,
.Stereo = FALSE, .SampleDesc = {1, 0},
.SampleDesc = {1, 0}, .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT, .BufferCount = R_NUM_FRAMES_IN_FLIGHT,
.BufferCount = R_NUM_FRAMES_IN_FLIGHT, .Scaling = DXGI_SCALING_STRETCH,
.Scaling = DXGI_SCALING_STRETCH, .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD, .AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED,
.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED, .Flags = 0};
.Flags = 0
};
// We create a temporary SwapChain1 that is then upgraded to a SwapChain3. // We create a temporary SwapChain1 that is then upgraded to a SwapChain3.
IDXGISwapChain1* temp_swap_chain; IDXGISwapChain1 *temp_swap_chain;
hr = IDXGIFactory4_CreateSwapChainForHwnd(factory, hr = IDXGIFactory4_CreateSwapChainForHwnd(factory,
(IUnknown*)r_d3d12_cmd->queue, (IUnknown *)r_d3d12_cmd->queue,
r_d3d12_state->window_handle, r_d3d12_state->window_handle,
&swap_chain_desc, &swap_chain_desc,
0, 0,
0, 0,
&temp_swap_chain); &temp_swap_chain);
D3D12_CHECK(hr, "Failed to create temp swap chain \n"); D3D12_CHECK(hr, "Failed to create temp swap chain \n");
hr = temp_swap_chain->lpVtbl->QueryInterface(temp_swap_chain, hr = temp_swap_chain->lpVtbl->QueryInterface(temp_swap_chain,
&IID_IDXGISwapChain3, &IID_IDXGISwapChain3,
(void**)&r_d3d12_state->swapchain); (void **)&r_d3d12_state->swapchain);
D3D12_CHECK(hr, "Failed to upgrade to swapchain3\n"); D3D12_CHECK(hr, "Failed to upgrade to swapchain3\n");
temp_swap_chain->lpVtbl->Release(temp_swap_chain); temp_swap_chain->lpVtbl->Release(temp_swap_chain);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Create Render target view (RTV) descriptor heap // Create Render target view (RTV) descriptor heap
D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc = { D3D12_DESCRIPTOR_HEAP_DESC rtv_heap_desc =
.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV, {
.NumDescriptors = R_NUM_FRAMES_IN_FLIGHT, .Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV,
.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE, .NumDescriptors = R_NUM_FRAMES_IN_FLIGHT,
.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE,
.NodeMask = 0 .NodeMask = 0
}; };
hr = ID3D12Device_CreateDescriptorHeap(r_d3d12_state->device,
hr = ID3D12Device_CreateDescriptorHeap(r_d3d12_state->device, &rtv_heap_desc,
&rtv_heap_desc, &IID_ID3D12DescriptorHeap,
&IID_ID3D12DescriptorHeap, (void **)&r_d3d12_rt->heap);
(void**)&r_d3d12_rt->heap);
D3D12_CHECK(hr, "Failed to create descriptor heap\n"); D3D12_CHECK(hr, "Failed to create descriptor heap\n");
r_d3d12_rt->descriptor_size = r_d3d12_state->device->lpVtbl-> r_d3d12_rt->descriptor_size = r_d3d12_state->device->lpVtbl->
GetDescriptorHandleIncrementSize(r_d3d12_state->device, GetDescriptorHandleIncrementSize(r_d3d12_state->device,
D3D12_DESCRIPTOR_HEAP_TYPE_RTV); D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Create render target views // Create render target views
// TODO(anton): This is probably not correct D3D12 API, but I might have an old SDK. // TODO(anton): This is probably not correct D3D12 API, but I might have an old SDK.
D3D12_CPU_DESCRIPTOR_HANDLE handle; D3D12_CPU_DESCRIPTOR_HANDLE handle;
ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(r_d3d12_rt->heap, &handle); ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(r_d3d12_rt->heap, &handle);
for (U32 i = 0; i < R_NUM_FRAMES_IN_FLIGHT; i += 1) for(U32 i = 0; i < R_NUM_FRAMES_IN_FLIGHT; i += 1)
{ {
IDXGISwapChain3_GetBuffer(r_d3d12_state->swapchain, IDXGISwapChain3_GetBuffer(r_d3d12_state->swapchain,
i, i,
&IID_ID3D12Resource, &IID_ID3D12Resource,
(void**)&r_d3d12_rt->targets[i]); (void **)&r_d3d12_rt->targets[i]);
ID3D12Device_CreateRenderTargetView(r_d3d12_state->device, ID3D12Device_CreateRenderTargetView(r_d3d12_state->device, r_d3d12_rt->targets[i], 0, handle);
r_d3d12_rt->targets[i],
0,
handle);
handle.ptr += r_d3d12_rt->descriptor_size; handle.ptr += r_d3d12_rt->descriptor_size;
} }
@ -157,126 +221,279 @@ r_init()
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Command allocator and command list // Command allocator and command list
hr = ID3D12Device_CreateCommandAllocator(r_d3d12_state->device, hr = ID3D12Device_CreateCommandAllocator(r_d3d12_state->device,
D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_LIST_TYPE_DIRECT,
&IID_ID3D12CommandAllocator, &IID_ID3D12CommandAllocator,
(void**)&r_d3d12_cmd->allocator); (void **)&r_d3d12_cmd->allocator);
D3D12_CHECK(hr, "Failed to create command allocator\n"); D3D12_CHECK(hr, "Failed to create command allocator\n");
hr = ID3D12Device_CreateCommandList(r_d3d12_state->device, hr = ID3D12Device_CreateCommandList(r_d3d12_state->device,
0, 0,
D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_LIST_TYPE_DIRECT,
r_d3d12_cmd->allocator, r_d3d12_cmd->allocator,
0, 0,
&IID_ID3D12GraphicsCommandList, &IID_ID3D12GraphicsCommandList,
(void**)&r_d3d12_cmd->list1); (void **)&r_d3d12_cmd->list1);
D3D12_CHECK(hr, "Failed to create command list\n"); D3D12_CHECK(hr, "Failed to create command list\n");
// Put it in a closed state during initialisation. It will be opened at the start of the render function. // Put it in a closed state during initialisation. It will be opened at the start of the render function.
ID3D12GraphicsCommandList_Close(r_d3d12_cmd->list1); ID3D12GraphicsCommandList_Close(r_d3d12_cmd->list1);
// Create the vertex buffer.
{
// Define the geometry for a triangle.
R_D3D12_Vertex triangle_vertices[3];
F32 r_aspect_ratio = (F32)WINDOW_WIDTH_PX / (F32)WINDOW_HEIGHT_PX;
triangle_vertices[0].pos = (Vec3F32){0.0f, 0.25f * r_aspect_ratio, 0.0f};
triangle_vertices[1].pos = (Vec3F32){0.25f, -0.25f * r_aspect_ratio, 0.0f};
triangle_vertices[2].pos = (Vec3F32){-0.25f, -0.25f * r_aspect_ratio, 0.0f};
triangle_vertices[0].color = (Vec4F32){1.0f, 0.0f, 0.0f, 1.0f};
triangle_vertices[1].color = (Vec4F32){0.0f, 1.0f, 0.0f, 1.0f};
triangle_vertices[2].color = (Vec4F32){0.0f, 0.0f, 1.0f, 1.0f};
U32 vertex_buffer_size = 3 * sizeof(R_D3D12_Vertex);
D3D12_RESOURCE_DESC buffer_desc = {
.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
.Alignment = 0,
.Width = vertex_buffer_size,
.Height = 1,
.DepthOrArraySize = 1,
.MipLevels = 1,
.Format = DXGI_FORMAT_UNKNOWN,
.SampleDesc = {.Count = 1, .Quality = 0},
.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
.Flags = D3D12_RESOURCE_FLAG_NONE
};
// Note: using upload heaps to transfer static data like vert buffers is not
// recommended. Every time the GPU needs it, the upload heap will be marshalled
// over. Please read up on Default Heap usage. An upload heap is used here for
// code simplicity and because there are very few verts to actually transfer.
D3D12_HEAP_PROPERTIES heap_props = {
.Type = D3D12_HEAP_TYPE_UPLOAD,
.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN,
.CreationNodeMask = 1,
.VisibleNodeMask = 1
};
ID3D12Device_CreateCommittedResource(r_d3d12_state->device,
&heap_props,
D3D12_HEAP_FLAG_NONE,
&buffer_desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
0,
&IID_ID3D12Resource,
(void **)&r_d3d12_state->vertex_buffer);
// Copy the triangle data to the vertex buffer.
U8 *vtx_data_begin;
D3D12_RANGE read_range = {0, 0}; // We don't intend to read from this resource on the CPU
hr = ID3D12Resource_Map(r_d3d12_state->vertex_buffer,
0, // Subresource index (0 for buffers)
&read_range, // Read range (NULL or {0,0} if not reading)
&vtx_data_begin);
D3D12_CHECK(hr, "Failed to map vertex buffer\n");
memcpy(vtx_data_begin, &triangle_vertices[0], vertex_buffer_size);
ID3D12Resource_Unmap(r_d3d12_state->vertex_buffer, 0, 0);
// Initialize the vertex buffer view.
r_d3d12_state->vtx_buf_view.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(r_d3d12_state->vertex_buffer);
r_d3d12_state->vtx_buf_view.StrideInBytes = sizeof(R_D3D12_Vertex);
r_d3d12_state->vtx_buf_view.SizeInBytes = vertex_buffer_size;
}
// Create shader pipeline
{
r_d3d12_init_vs_sources();
r_d3d12_init_ps_sources();
D3D12_ROOT_SIGNATURE_DESC root_sig_desc = {0};
root_sig_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT;
ID3DBlob *signature = 0;
ID3DBlob *err_blob = 0;
hr = D3D12SerializeRootSignature(&root_sig_desc,
D3D_ROOT_SIGNATURE_VERSION_1,
&signature,
&err_blob);
D3D12_CHECK(hr, "Failed to serialize root signature \n");
hr = ID3D12Device_CreateRootSignature(r_d3d12_state->device,
0,
ID3D10Blob_GetBufferPointer(signature),
ID3D10Blob_GetBufferSize(signature),
&IID_ID3D12RootSignature,
(void **)&r_d3d12_state->root_signature);
D3D12_CHECK(hr, "Failed to create root signature \n");
ID3DBlob *vtx_shader = 0;
ID3DBlob *pxl_shader = 0;
U32 compile_flags = 0; // Can be debug flags D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
const char *vs_source = r_d3d12_get_vs_shader_source(R_D3D12_VS_Kind_Triangle);
hr = D3DCompile(vs_source,
c_strlen(vs_source),
0, // Source name (0 since in memory)
0, // shader macros, not used if 0
0, // include handler, not used if 0
"vs_main", // entry point
"vs_5_0",
compile_flags,
0, // effect falgs
&vtx_shader, // outputblob
&err_blob);
D3D12_SHADER_CHECK(hr, err_blob);
const char *ps_source = r_d3d12_get_ps_shader_source(R_D3D12_PS_Kind_Triangle);
hr = D3DCompile(ps_source,
c_strlen(ps_source),
0, // Source name (0 since in memory)
0, // shader macros, not used if 0
0, // include handler, not used if 0
"ps_main", // entry point
"ps_5_0",
compile_flags,
0, // effect flags
&pxl_shader, // outputblob
&err_blob);
D3D12_SHADER_CHECK(hr, err_blob);
if(pxl_shader)
{
ID3D10Blob_Release(pxl_shader);
}
if(vtx_shader)
{
ID3D10Blob_Release(vtx_shader);
}
if(err_blob)
{
ID3D10Blob_Release(err_blob);
}
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Fence // Fence
hr = ID3D12Device_CreateFence(r_d3d12_state->device, hr = ID3D12Device_CreateFence(r_d3d12_state->device,
0, 0,
D3D12_FENCE_FLAG_NONE, D3D12_FENCE_FLAG_NONE,
&IID_ID3D12Fence, &IID_ID3D12Fence,
(void**)&r_d3d12_state->fence); (void **)&r_d3d12_state->fence);
D3D12_CHECK(hr, "Failed to create fence\n"); D3D12_CHECK(hr, "Failed to create fence\n");
r_d3d12_state->fence_value = 1; r_d3d12_state->fence_value = 1;
r_d3d12_state->fence_event = CreateEvent(0, FALSE, FALSE, 0); r_d3d12_state->fence_event = CreateEvent(0, FALSE, FALSE, 0);
if(!r_d3d12_state->fence_event) if(!r_d3d12_state->fence_event)
{ {
HRESULT fence_hr = HRESULT_FROM_WIN32(GetLastError()); HRESULT fence_hr = HRESULT_FROM_WIN32(GetLastError());
D3D12_CHECK(fence_hr, "Failed to create fence event\n"); D3D12_CHECK(fence_hr, "Failed to create fence event\n");
} }
r_d3d12_wait_for_previous_frame();
r_d3d12_wait_for_previous_frame();
} }
internal void internal void r_d3d12_wait_for_previous_frame()
r_d3d12_wait_for_previous_frame()
{ {
U64 fence_value = r_d3d12_state->fence_value; U64 fence_value = r_d3d12_state->fence_value;
ID3D12CommandQueue_Signal(r_d3d12_cmd->queue, r_d3d12_state->fence, fence_value); ID3D12CommandQueue_Signal(r_d3d12_cmd->queue, r_d3d12_state->fence, fence_value);
r_d3d12_state->fence_value += 1; r_d3d12_state->fence_value += 1;
if(ID3D12Fence_GetCompletedValue(r_d3d12_state->fence) < fence_value) if(ID3D12Fence_GetCompletedValue(r_d3d12_state->fence) < fence_value)
{ {
HRESULT hr = ID3D12Fence_SetEventOnCompletion(r_d3d12_state->fence, fence_value, r_d3d12_state->fence_event); HRESULT hr = ID3D12Fence_SetEventOnCompletion(r_d3d12_state->fence,
fence_value,
r_d3d12_state->fence_event);
D3D12_CHECK(hr, "Failed to set fence event on completion\n"); D3D12_CHECK(hr, "Failed to set fence event on completion\n");
WaitForSingleObject(r_d3d12_state->fence_event, INFINITE); WaitForSingleObject(r_d3d12_state->fence_event, INFINITE);
} }
} }
internal void internal void r_render()
r_render()
{ {
HRESULT hr = ID3D12CommandAllocator_Reset(r_d3d12_cmd->allocator); HRESULT hr = ID3D12CommandAllocator_Reset(r_d3d12_cmd->allocator);
D3D12_CHECK(hr, "Failed to reset command allocator\n"); D3D12_CHECK(hr, "Failed to reset command allocator\n");
hr = ID3D12GraphicsCommandList_Reset(r_d3d12_cmd->list1, r_d3d12_cmd->allocator, 0); hr = ID3D12GraphicsCommandList_Reset(r_d3d12_cmd->list1, r_d3d12_cmd->allocator, 0);
D3D12_CHECK(hr, "Failed to reset command list\n"); D3D12_CHECK(hr, "Failed to reset command list\n");
U32 frame_index = IDXGISwapChain3_GetCurrentBackBufferIndex(r_d3d12_state->swapchain); U32 frame_index = IDXGISwapChain3_GetCurrentBackBufferIndex(r_d3d12_state->swapchain);
D3D12_RESOURCE_BARRIER barrier = { D3D12_RESOURCE_BARRIER barrier =
.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, {
.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE, .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
.Transition = { .Transition = {
.pResource = r_d3d12_rt->targets[frame_index], .pResource = r_d3d12_rt->targets[frame_index],
.StateBefore = D3D12_RESOURCE_STATE_PRESENT, .StateBefore = D3D12_RESOURCE_STATE_PRESENT,
.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET, .StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET,
.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES .Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES
} }
}; };
// Transition from present to render target // Transition from present to render target
ID3D12GraphicsCommandList_ResourceBarrier(r_d3d12_cmd->list1, 1, &barrier); ID3D12GraphicsCommandList_ResourceBarrier(r_d3d12_cmd->list1, 1, &barrier);
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle; D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle;
ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(r_d3d12_rt->heap, &rtv_handle); ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(r_d3d12_rt->heap, &rtv_handle);
rtv_handle.ptr += frame_index * r_d3d12_rt->descriptor_size; rtv_handle.ptr += frame_index * r_d3d12_rt->descriptor_size;
// Clear render target // Clear render target
F32 clearColor[4] = {0.1f, 0.2f, 0.3f, 1.0f}; F32 clearColor[4] = {0.1f, 0.2f, 0.3f, 1.0f};
ID3D12GraphicsCommandList_ClearRenderTargetView(r_d3d12_cmd->list1, rtv_handle, clearColor, 0, NULL); ID3D12GraphicsCommandList_ClearRenderTargetView(r_d3d12_cmd->list1,
rtv_handle,
clearColor,
0,
0);
// Transition render target back to present state
// Transition render target back to present state
barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
ID3D12GraphicsCommandList_ResourceBarrier(r_d3d12_cmd->list1, 1, &barrier); ID3D12GraphicsCommandList_ResourceBarrier(r_d3d12_cmd->list1, 1, &barrier);
// Close command list // Close command list
hr = ID3D12GraphicsCommandList_Close(r_d3d12_cmd->list1); hr = ID3D12GraphicsCommandList_Close(r_d3d12_cmd->list1);
if (FAILED(hr)) return; if(FAILED(hr))
return;
// Execute command list // Execute command list
ID3D12CommandList* commandLists[] = { (ID3D12CommandList*)r_d3d12_cmd->list1 }; ID3D12CommandList *commandLists[] = { (ID3D12CommandList *)r_d3d12_cmd->list1 };
ID3D12CommandQueue_ExecuteCommandLists(r_d3d12_cmd->queue, 1, commandLists);
// Present frame ID3D12CommandQueue_ExecuteCommandLists(r_d3d12_cmd->queue,
1,
commandLists);
// Present frame
IDXGISwapChain3_Present(r_d3d12_state->swapchain, 1, 0); IDXGISwapChain3_Present(r_d3d12_state->swapchain, 1, 0);
r_d3d12_wait_for_previous_frame(); r_d3d12_wait_for_previous_frame();
} }
internal void internal void r_cleanup()
r_cleanup()
{ {
r_d3d12_wait_for_previous_frame(); r_d3d12_wait_for_previous_frame();
U32 do_report_live_objects = 2; // 1 for all, 2 for only after release to check any dangling. U32 do_report_live_objects = 2; // 1 for all, 2 for only after release to check any dangling.
// Report live objects // Report live objects
if(do_report_live_objects == 1) if(do_report_live_objects == 1)
{ {
LOG("D3D12 Reporting live objects before cleanup:\n"); LOG("D3D12 Reporting live objects before cleanup:\n");
if(r_d3d12_state->device) if(r_d3d12_state->device)
{ {
ID3D12DebugDevice *debug_device = 0; ID3D12DebugDevice *debug_device = 0;
HRESULT hr = r_d3d12_state->device->lpVtbl->QueryInterface(r_d3d12_state->device, HRESULT hr = r_d3d12_state->device->lpVtbl->QueryInterface(r_d3d12_state->device, &IID_ID3D12DebugDevice, (void **)&debug_device);
&IID_ID3D12DebugDevice,
(void **)&debug_device);
if(SUCCEEDED(hr) && debug_device) if(SUCCEEDED(hr) && debug_device)
{ {
debug_device->lpVtbl->ReportLiveDeviceObjects(debug_device, debug_device->lpVtbl->ReportLiveDeviceObjects(debug_device,
@ -285,18 +502,20 @@ r_cleanup()
} }
} }
} }
// State // State
D3D12_RELEASE(r_d3d12_state->debug); D3D12_RELEASE(r_d3d12_state->debug);
D3D12_RELEASE(r_d3d12_state->swapchain); D3D12_RELEASE(r_d3d12_state->swapchain);
D3D12_RELEASE(r_d3d12_state->fence); D3D12_RELEASE(r_d3d12_state->fence);
D3D12_RELEASE(r_d3d12_state->vertex_buffer);
// RT // RT
D3D12_RELEASE(r_d3d12_rt->heap); D3D12_RELEASE(r_d3d12_rt->heap);
for(U32 i = 0; i < R_NUM_FRAMES_IN_FLIGHT; i+=1) for(U32 i = 0; i < R_NUM_FRAMES_IN_FLIGHT; i += 1)
{ {
D3D12_RELEASE(r_d3d12_rt->targets[i]); D3D12_RELEASE(r_d3d12_rt->targets[i]);
} }
// Cmd // Cmd
D3D12_RELEASE(r_d3d12_cmd->queue); D3D12_RELEASE(r_d3d12_cmd->queue);
D3D12_RELEASE(r_d3d12_cmd->allocator); D3D12_RELEASE(r_d3d12_cmd->allocator);
@ -304,13 +523,13 @@ r_cleanup()
// Report live objects // Report live objects
if(do_report_live_objects >= 1) if(do_report_live_objects >= 1)
{ {
LOG("D3D12 Reporting live objects after cleanup (should only be device):\n"); LOG("D3D12 Reporting live objects after cleanup (should only be device):\n");
if(r_d3d12_state->device) if(r_d3d12_state->device)
{ {
ID3D12DebugDevice *debug_device = 0; ID3D12DebugDevice *debug_device = 0;
HRESULT hr = r_d3d12_state->device->lpVtbl->QueryInterface(r_d3d12_state->device, HRESULT hr = r_d3d12_state->device->lpVtbl->QueryInterface(r_d3d12_state->device,
&IID_ID3D12DebugDevice, &IID_ID3D12DebugDevice,
(void **)&debug_device); (void **)&debug_device);
if(SUCCEEDED(hr) && debug_device) if(SUCCEEDED(hr) && debug_device)
{ {
@ -323,5 +542,4 @@ r_cleanup()
D3D12_RELEASE(r_d3d12_state->device); D3D12_RELEASE(r_d3d12_state->device);
arena_release(r_d3d12_state->arena); arena_release(r_d3d12_state->arena);
} }

View File

@ -5,6 +5,7 @@
#include <d3d12.h> #include <d3d12.h>
#include <dxgi1_4.h> #include <dxgi1_4.h>
#include "generated/render_d3d12.meta.h"
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ //~
@ -15,6 +16,13 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ //~
typedef struct R_D3D12_Vertex R_D3D12_Vertex;
struct R_D3D12_Vertex
{
Vec3F32 pos;
Vec4F32 color;
};
typedef struct R_D3D12_Command R_D3D12_Command; typedef struct R_D3D12_Command R_D3D12_Command;
struct R_D3D12_Command struct R_D3D12_Command
@ -47,6 +55,10 @@ struct R_D3D12_State
U64 fence_value; U64 fence_value;
U64 frame_fence_values[R_NUM_FRAMES_IN_FLIGHT]; U64 frame_fence_values[R_NUM_FRAMES_IN_FLIGHT];
HANDLE fence_event; HANDLE fence_event;
ID3D12Resource *vertex_buffer;
D3D12_VERTEX_BUFFER_VIEW vtx_buf_view;
ID3D12PipelineState *pipeline;
ID3D12RootSignature *root_signature;
}; };
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,108 @@
////////////////////////////////
// Pipeline Tables
@table(name, source)
R_D3D12_VSTable:
{
{Triangle r_d3d12_g_triangle_shader_src}
{Test r_d3d12_g_test_shader_src}
}
@table(name, source)
R_D3D12_PSTable:
{
{Triangle r_d3d12_g_triangle_shader_src}
{Test r_d3d12_g_test_shader_src}
}
////////////////////////////////
//~ Triangle shader
@embed_string r_d3d12_g_triangle_shader_src:
"""
struct PSInput
{
float4 position : SV_POSITION;
float4 color : COLOR;
};
PSInput vs_main(float4 position : POSITION, float4 color : COLOR)
{
PSInput result;
result.position = position;
result.color = color;
return result;
}
float4 ps_main(PSInput input) : SV_TARGET
{
return input.color;
}
"""
////////////////////////////////
//~ Test shader
@embed_string r_d3d12_g_test_shader_src:
"""
struct PSInput
{
float4 position : SV_POSITION;
float4 color : COLOR;
};
PSInput vs_main(float4 position : POSITION, float4 color : COLOR)
{
PSInput result;
result.position = position;
result.color = color;
return result;
}
float4 ps_main(PSInput input) : SV_TARGET
{
return input.color;
}
"""
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ Generators
@enum R_D3D12_VS_Kind:
{
@expand(R_D3D12_VSTable a) `$(a.name)`,
COUNT,
}
@enum R_D3D12_PS_Kind:
{
@expand(R_D3D12_PSTable a) `$(a.name)`,
COUNT,
}
@gen @c_file
{
`const char *r_d3d12_g_vs_kind_source_table[R_D3D12_VS_Kind_COUNT];`;
`internal void r_d3d12_init_vs_sources()`;
`{`;
@expand(R_D3D12_VSTable a) `r_d3d12_g_vs_kind_source_table[R_D3D12_VS_Kind_$(a.name)] = $(a.source);`
`}`;
``;
}
@gen @c_file
{
`const char *r_d3d12_g_ps_kind_source_table[R_D3D12_PS_Kind_COUNT];`;
`internal void r_d3d12_init_ps_sources()`;
`{`;
@expand(R_D3D12_PSTable a) `r_d3d12_g_ps_kind_source_table[R_D3D12_PS_Kind_$(a.name)] = $(a.source);`
`}`;
``;
}

View File

@ -1,6 +1,29 @@
#ifndef RENDER_CORE_H #ifndef RENDER_CORE_H
#define RENDER_CORE_H #define RENDER_CORE_H
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~
#define BATCH_LIST_SIZE 256
typedef struct R_Batch R_Batch;
struct R_Batch
{
U8 *v; // Pointer to batch data
U64 byte_count;
};
typedef struct R_BatchList R_BatchList;
struct R_BatchList
{
R_Batch batch[BATCH_LIST_SIZE];
U64 batch_count;
U64 byte_count;
U64 byte_per_instance;
};
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//~ //~

2055
src/third_party/stb/stb_sprintf.h vendored Normal file

File diff suppressed because it is too large Load Diff