///////////////////////// //~ 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, ¶ms); 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); }