app_codebase/src/draw/draw.c
Anton Ljungdahl caf9a7f24b hueh
2024-08-08 21:19:46 +02:00

263 lines
7.9 KiB
C

/////////////////////////
//~ Draw globals
global D_State *d_state = 0;
per_thread D_ThreadCtx *d_thread_ctx = 0;
// temp color stuff
global F32 g_text_color[4] = {0.95f, 0.9f, 0.94f, 1.0f};
global F32 g_rect_color[4] = {0.6f, 0.5f, 0.6f, 1.0f};
////////////////////////////////
//~ Stack helper macros
#define D_StackPush(node_type, val_type, name, new_val) \
{\
Arena *arena = D_active_arena();\
D_Bucket *bucket = D_active_bucket();\
node_type *node_v = bucket->name##_free;\
if(node_v != 0) {StackPop(bucket->name##_free);}\
else {node_v = PushArray(arena, node_type, 1);}\
node_v->v = new_val;\
new_val = bucket->name##_stack_top->v;\
StackPush(bucket->name##_stack_top, node_v);\
bucket->last_cmd_stack_gen = bucket->current_stack_gen;\
bucket->current_stack_gen += 1;\
return new_val;\
}
#define D_StackPop(node_type, val_type, name) \
{\
val_type result = d_thread_ctx->name##_nil_stack_top.v;\
D_Bucket *bucket = D_active_bucket();\
if(bucket->name##_stack_top != &d_thread_ctx->name##_nil_stack_top)\
{\
node_type *node = bucket->name##_stack_top;\
result = node->v;\
StackPop(bucket->name##_stack_top);\
StackPush(bucket->name##_free, node);\
bucket->last_cmd_stack_gen = bucket->current_stack_gen;\
bucket->current_stack_gen += 1;\
}\
return result;\
}
#define D_StackTop(node_type, val_type, name) \
{\
D_Bucket *bucket = D_active_bucket();\
val_type result = bucket->name##_stack_top->v;\
return result;\
}
/////////////////////////
//~ "Generated" code
#include "draw_meta.c"
/////////////////////////
//~ Layer init
root_function D_InitReceipt
D_init(R_InitReceipt r_init_receipt, F_InitReceipt f_init_receipt)
{
if(is_main_thread() && d_state == 0)
{
Arena* arena = m_make_arena_reserve(Megabytes(2));
d_state = PushArrayZero(arena, D_State, 1);
d_state->arena = arena;
d_state->font_texture = F_atlas_texture_handle();
D_ensure_thread_initialised();
}
D_InitReceipt receipt = {0};
return receipt;
}
root_function void
D_ensure_thread_initialised(void)
{
if(d_thread_ctx == 0)
{
Arena *arena = m_make_arena_reserve(Megabytes(1));
d_thread_ctx = PushArrayZero(arena, D_ThreadCtx, 1);
d_thread_ctx->arena = arena;
d_thread_ctx->fallback_arena = m_make_arena_reserve(Megabytes(256));
D_InitBucketStacks(&d_thread_ctx->fallback_bucket);
d_thread_ctx->bucket_selection_fallback.arena = d_thread_ctx->fallback_arena;
d_thread_ctx->bucket_selection_fallback.bucket = &d_thread_ctx->fallback_bucket;
d_thread_ctx->bucket_selection_top = &d_thread_ctx->bucket_selection_fallback;
D_InitThreadStackTops;
}
}
/////////////////////////
//~ Draw functions
root_function void
D_frame_begin() {
D_ensure_thread_initialised();
}
root_function void
D_frame_end()
{
}
//- Rect
root_function R_Rect2DInst *
D_rect2D_(Rng2_F32 rect, D_RectParams *rect_params)
{
Arena* arena = D_active_arena();
D_Bucket *bucket = D_active_bucket();
R_Pass *pass = D_pass_from_bucket(arena, bucket, R_PassKind_UI);
R_PassParams_UI *pass_params = pass->params_ui;
R_BatchGroup2DNode *batch_group = pass_params->rects.last;
R_Handle tex = d_state->font_texture;
// If we don't have a batch group yet we initialise it here.
// Eventually we will also check for what different textures we have, since
// we batch by texture. What about transform?
// TODO(anton): understand buckets in rjf codebase.
if(batch_group == 0 || bucket->last_cmd_stack_gen != bucket->current_stack_gen)
{
batch_group = PushArrayZero(arena, R_BatchGroup2DNode, 1);
QueuePush(pass_params->rects.first, pass_params->rects.last, batch_group);
pass_params->rects.count += 1;
batch_group->params.albedo_tex = tex;
batch_group->params.albedo_tex_sample_kind = bucket->tex2d_sample_kind_stack_top->v;
Mat3x3_F32 xform2d = {0.0f};
batch_group->params.xform2d = xform2d;
}
// Here we get the available memory in a batch of memory that can be filled with differen things.
// We get a chunk of that memory of the appropriate size for a R_Rect2DInst and fill it out.
R_Rect2DInst *inst = R_batch_list_push_struct(arena, &batch_group->batches, 256 /* capacity */, R_Rect2DInst);
inst->dst_rect = rect;
inst->src_rect = rect_params->src_rect;
inst->colors[Corner_00] = rect_params->color;//vec4_F32(0.6f, 0.5f, 0.6f, 1.0f);
inst->colors[Corner_01] = rect_params->color;//vec4_F32(0.6f, 0.5f, 0.6f, 1.0f);
inst->colors[Corner_10] = rect_params->color;//vec4_F32(0.6f, 0.5f, 0.6f, 1.0f);
inst->colors[Corner_11] = rect_params->color;//vec4_F32(0.6f, 0.5f, 0.6f, 1.0f);
inst->corner_radii[Corner_00] = rect_params->corner_radius;
inst->corner_radii[Corner_01] = rect_params->corner_radius;
inst->corner_radii[Corner_10] = rect_params->corner_radius;
inst->corner_radii[Corner_11] = rect_params->corner_radius;
inst->softness = rect_params->softness;
inst->border_thickness = rect_params->border_thickness;
inst->omit_texture = rect_params->omit_texture;
return inst;
}
root_function F32
D_text2D(Vec2_F32 position, String8 string, Vec4_F32 color)
{
Arena *arena = D_active_arena();
ArenaTemp scratch = scratch_get(&arena, 1);
F_Run run = F_run_from_string(scratch.arena, string, position);
// The position we get in will be the start of where we want to put the text.
// Then we put a draw rectangle for each glyph in the string, and advance the pos.
// The return value from this function then is the length of the text string.
Vec2_F32 p = position;
for(F_Piece *piece = run.first_piece; piece != 0; piece = piece->next)
{
//break_debugger();
D_RectParams params;
params.src_rect = piece->src_rect;
params.omit_texture = 0;
params.color = color;
Vec2_F32 dst_p0 = piece->dst_p0;
Vec2_F32 dst_p1 = piece->dst_p1;
Rng2_F32 rect = rng2_F32(dst_p0, dst_p1);
D_rect2D_(rect, &params);
p.x += piece->advance;
}
scratch_release(scratch);
F32 result = p.x - position.x;
return result;
}
///////////////////////////////////////////////////
//~ Buckets
root_function D_Bucket *
D_bucket_make(Arena *arena)
{
D_Bucket *bucket = PushArrayZero(arena, D_Bucket, 1);
D_InitBucketStacks(bucket);
String8 name = str8_pushf(arena, "HejBucket");
bucket->bucket_name = name;
return bucket;
}
// TODO(anton): bucket and pass list concat in place when needed
root_function void
D_push_bucket(Arena *arena, D_Bucket *bucket)
{
D_BucketSelectionNode *node = d_thread_ctx->bucket_selection_free;
if(node != 0)
{
StackPop(d_thread_ctx->bucket_selection_free);
}
else
{
node = PushArray(d_thread_ctx->arena, D_BucketSelectionNode, 1);
}
node->arena = arena;
node->bucket = bucket;
StackPush(d_thread_ctx->bucket_selection_top, node);
}
root_function void
D_pop_bucket(void)
{
// Get the top bucket node, pop it and put it on the free list.
// If the top node is the fallback arena we will clear and reinit the fallback.
D_BucketSelectionNode *node = d_thread_ctx->bucket_selection_top;
if(node != &d_thread_ctx->bucket_selection_fallback)
{
StackPop(d_thread_ctx->bucket_selection_top);
StackPush(d_thread_ctx->bucket_selection_free, node);
}
if(d_thread_ctx->bucket_selection_top == &d_thread_ctx->bucket_selection_fallback)
{
m_arena_clear(d_thread_ctx->fallback_arena);
MemoryZeroStruct(&d_thread_ctx->fallback_bucket);
D_InitBucketStacks(&d_thread_ctx->fallback_bucket);
}
}
root_function R_Pass *
D_pass_from_bucket(Arena *arena, D_Bucket *bucket, R_PassKind kind)
{
R_PassNode *node = bucket->passes.last;
R_Pass *pass = 0;
// If we dont have a pass node at the last position, we push the pass to the list.
if(node == 0)
{
pass = R_pass_list_push(arena, &bucket->passes, kind);
}
else
{
pass = &node->v;
}
return pass;
}
root_function Arena *
D_active_arena()
{
return d_thread_ctx->bucket_selection_top->arena;
}
root_function D_Bucket *
D_active_bucket(void)
{
return d_thread_ctx->bucket_selection_top->bucket;
}
root_function void
D_submit(R_Handle window_r, D_Bucket *bucket)
{
R_window_submit(window_r, &bucket->passes);
}