263 lines
7.9 KiB
C
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, ¶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);
|
|
} |