eliminate recursive version of final ui layout position pass

This commit is contained in:
Ryan Fleury
2025-04-25 08:29:44 -07:00
parent fe46e61acb
commit e0ba1155e8
2 changed files with 137 additions and 145 deletions
+132 -140
View File
@@ -1272,11 +1272,11 @@ ui_end_build(void)
root->rect = new_root_rect; root->rect = new_root_rect;
for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis + 1)) for(Axis2 axis = (Axis2)0; axis < Axis2_COUNT; axis = (Axis2)(axis + 1))
{ {
ui_calc_sizes_standalone__in_place_rec(root, axis); ui_calc_sizes_standalone__in_place(root, axis);
ui_calc_sizes_upwards_dependent__in_place_rec(root, axis); ui_calc_sizes_upwards_dependent__in_place(root, axis);
ui_calc_sizes_downwards_dependent__in_place_rec(root, axis); ui_calc_sizes_downwards_dependent__in_place(root, axis);
ui_layout_enforce_constraints__in_place_rec(root, axis); ui_layout_enforce_constraints__in_place(root, axis);
ui_layout_position__in_place_rec(root, axis); ui_layout_position__in_place(root, axis);
} }
} }
} }
@@ -1613,7 +1613,7 @@ ui_end_build(void)
} }
internal void internal void
ui_calc_sizes_standalone__in_place_rec(UI_Box *root, Axis2 axis) ui_calc_sizes_standalone__in_place(UI_Box *root, Axis2 axis)
{ {
ProfBeginFunction(); ProfBeginFunction();
for(UI_Box *b = root; !ui_box_is_nil(b); b = ui_box_rec_df_pre(b, root).next) for(UI_Box *b = root; !ui_box_is_nil(b); b = ui_box_rec_df_pre(b, root).next)
@@ -1637,7 +1637,7 @@ ui_calc_sizes_standalone__in_place_rec(UI_Box *root, Axis2 axis)
} }
internal void internal void
ui_calc_sizes_upwards_dependent__in_place_rec(UI_Box *root, Axis2 axis) ui_calc_sizes_upwards_dependent__in_place(UI_Box *root, Axis2 axis)
{ {
ProfBeginFunction(); ProfBeginFunction();
for(UI_Box *b = root; !ui_box_is_nil(b); b = ui_box_rec_df_pre(b, root).next) for(UI_Box *b = root; !ui_box_is_nil(b); b = ui_box_rec_df_pre(b, root).next)
@@ -1673,7 +1673,7 @@ ui_calc_sizes_upwards_dependent__in_place_rec(UI_Box *root, Axis2 axis)
} }
internal void internal void
ui_calc_sizes_downwards_dependent__in_place_rec(UI_Box *root, Axis2 axis) ui_calc_sizes_downwards_dependent__in_place(UI_Box *root, Axis2 axis)
{ {
ProfBeginFunction(); ProfBeginFunction();
UI_BoxRec rec = {0}; UI_BoxRec rec = {0};
@@ -1710,180 +1710,172 @@ ui_calc_sizes_downwards_dependent__in_place_rec(UI_Box *root, Axis2 axis)
} }
internal void internal void
ui_layout_enforce_constraints__in_place_rec(UI_Box *root, Axis2 axis) ui_layout_enforce_constraints__in_place(UI_Box *root, Axis2 axis)
{ {
ProfBeginFunction(); ProfBeginFunction();
Temp scratch = scratch_begin(0, 0); Temp scratch = scratch_begin(0, 0);
for(UI_Box *box = root; !ui_box_is_nil(box); box = ui_box_rec_df_pre(box, root).next)
// NOTE(rjf): The "layout axis" is the direction in which children
// of some node are intended to be laid out.
//- rjf: fixup children sizes (if we're solving along the *non-layout* axis)
if(axis != root->child_layout_axis && !(root->flags & (UI_BoxFlag_AllowOverflowX << axis)))
{ {
F32 allowed_size = root->fixed_size.v[axis]; //- rjf: fixup children sizes (if we're solving along the *non-layout* axis)
for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next) if(axis != box->child_layout_axis && !(box->flags & (UI_BoxFlag_AllowOverflowX << axis)))
{ {
if(!(child->flags & (UI_BoxFlag_FloatingX<<axis))) F32 allowed_size = box->fixed_size.v[axis];
for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next)
{ {
F32 child_size = child->fixed_size.v[axis]; if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
F32 violation = child_size - allowed_size;
F32 max_fixup = child_size;
F32 fixup = Clamp(0, violation, max_fixup);
if(fixup > 0)
{ {
child->fixed_size.v[axis] -= fixup; F32 child_size = child->fixed_size.v[axis];
F32 violation = child_size - allowed_size;
F32 max_fixup = child_size;
F32 fixup = Clamp(0, violation, max_fixup);
if(fixup > 0)
{
child->fixed_size.v[axis] -= fixup;
}
} }
} }
} }
}
//- rjf: fixup children sizes (in the direction of the layout axis)
if(axis == root->child_layout_axis && !(root->flags & (UI_BoxFlag_AllowOverflowX << axis)))
{
// rjf: figure out total allowed size & total size
F32 total_allowed_size = root->fixed_size.v[axis];
F32 total_size = 0;
F32 total_weighted_size = 0;
for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next)
{
if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
{
total_size += child->fixed_size.v[axis];
total_weighted_size += child->fixed_size.v[axis] * (1-child->pref_size[axis].strictness);
}
}
// rjf: if we have a violation, we need to subtract some amount from all children //- rjf: fixup children sizes (in the direction of the layout axis)
F32 violation = total_size - total_allowed_size; if(axis == box->child_layout_axis && !(box->flags & (UI_BoxFlag_AllowOverflowX << axis)))
if(violation > 0 && total_weighted_size > 0)
{ {
// rjf: figure out how much we can take in totality // rjf: figure out total allowed size & total size
F32 child_fixup_sum = 0; F32 total_allowed_size = box->fixed_size.v[axis];
F32 *child_fixups = push_array(scratch.arena, F32, root->child_count); F32 total_size = 0;
F32 total_weighted_size = 0;
for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next)
{ {
U64 child_idx = 0; if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next, child_idx += 1)
{ {
if(!(child->flags & (UI_BoxFlag_FloatingX<<axis))) total_size += child->fixed_size.v[axis];
{ total_weighted_size += child->fixed_size.v[axis] * (1-child->pref_size[axis].strictness);
F32 fixup_size_this_child = child->fixed_size.v[axis] * (1-child->pref_size[axis].strictness);
fixup_size_this_child = ClampBot(0, fixup_size_this_child);
child_fixups[child_idx] = fixup_size_this_child;
child_fixup_sum += fixup_size_this_child;
}
} }
} }
// rjf: fixup child sizes // rjf: if we have a violation, we need to subtract some amount from all children
F32 violation = total_size - total_allowed_size;
if(violation > 0 && total_weighted_size > 0)
{ {
U64 child_idx = 0; Temp temp = temp_begin(scratch.arena);
for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next, child_idx += 1)
// rjf: figure out how much we can take in totality
F32 child_fixup_sum = 0;
F32 *child_fixups = push_array(temp.arena, F32, box->child_count);
{ {
if(!(child->flags & (UI_BoxFlag_FloatingX<<axis))) U64 child_idx = 0;
for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next, child_idx += 1)
{ {
F32 fixup_pct = (violation / total_weighted_size); if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
fixup_pct = Clamp(0, fixup_pct, 1); {
child->fixed_size.v[axis] -= child_fixups[child_idx] * fixup_pct; F32 fixup_size_this_child = child->fixed_size.v[axis] * (1-child->pref_size[axis].strictness);
fixup_size_this_child = ClampBot(0, fixup_size_this_child);
child_fixups[child_idx] = fixup_size_this_child;
child_fixup_sum += fixup_size_this_child;
}
} }
} }
// rjf: fixup child sizes
{
U64 child_idx = 0;
for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next, child_idx += 1)
{
if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
{
F32 fixup_pct = (violation / total_weighted_size);
fixup_pct = Clamp(0, fixup_pct, 1);
child->fixed_size.v[axis] -= child_fixups[child_idx] * fixup_pct;
}
}
}
temp_end(temp);
}
}
//- rjf: fixup upwards-relative sizes
if(box->flags & (UI_BoxFlag_AllowOverflowX << axis))
{
for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next)
{
if(child->pref_size[axis].kind == UI_SizeKind_ParentPct)
{
child->fixed_size.v[axis] = box->fixed_size.v[axis] * child->pref_size[axis].value;
}
} }
} }
}
//- rjf: enforce clamps
//- rjf: fixup upwards-relative sizes for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next)
if(root->flags & (UI_BoxFlag_AllowOverflowX << axis))
{
for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next)
{ {
if(child->pref_size[axis].kind == UI_SizeKind_ParentPct) child->fixed_size.v[axis] = Max(child->fixed_size.v[axis], child->min_size.v[axis]);
{
child->fixed_size.v[axis] = root->fixed_size.v[axis] * child->pref_size[axis].value;
}
} }
} }
//- rjf: enforce clamps
for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next)
{
child->fixed_size.v[axis] = Max(child->fixed_size.v[axis], child->min_size.v[axis]);
}
//- rjf: recurse
for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next)
{
ui_layout_enforce_constraints__in_place_rec(child, axis);
}
scratch_end(scratch); scratch_end(scratch);
ProfEnd(); ProfEnd();
} }
internal void internal void
ui_layout_position__in_place_rec(UI_Box *root, Axis2 axis) ui_layout_position__in_place(UI_Box *root, Axis2 axis)
{ {
ProfBeginFunction(); ProfBeginFunction();
F32 layout_position = 0; for(UI_Box *box = root; !ui_box_is_nil(box); box = ui_box_rec_df_pre(box, root).next)
//- rjf: lay out children
F32 bounds = 0;
for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next)
{ {
// rjf: grab original position F32 layout_position = 0;
F32 original_position = Min(child->rect.p0.v[axis], child->rect.p1.v[axis]);
// rjf: calculate fixed position & size //- rjf: lay out children
if(!(child->flags & (UI_BoxFlag_FloatingX<<axis))) F32 bounds = 0;
for(UI_Box *child = box->first; !ui_box_is_nil(child); child = child->next)
{ {
child->fixed_position.v[axis] = layout_position; // rjf: grab original position
if(root->child_layout_axis == axis) F32 original_position = Min(child->rect.p0.v[axis], child->rect.p1.v[axis]);
// rjf: calculate fixed position & size
if(!(child->flags & (UI_BoxFlag_FloatingX<<axis)))
{ {
layout_position += child->fixed_size.v[axis]; child->fixed_position.v[axis] = layout_position;
bounds += child->fixed_size.v[axis]; if(box->child_layout_axis == axis)
{
layout_position += child->fixed_size.v[axis];
bounds += child->fixed_size.v[axis];
}
else
{
bounds = Max(bounds, child->fixed_size.v[axis]);
}
}
// rjf: determine final rect for child, given fixed_position & size
if(child->flags & (UI_BoxFlag_AnimatePosX<<axis))
{
if(child->first_touched_build_index == child->last_touched_build_index)
{
child->fixed_position_animated = child->fixed_position;
}
child->rect.p0.v[axis] = box->rect.p0.v[axis] + child->fixed_position_animated.v[axis] - !(child->flags&(UI_BoxFlag_SkipViewOffX<<axis))*floor_f32(box->view_off.v[axis]);
} }
else else
{ {
bounds = Max(bounds, child->fixed_size.v[axis]); child->rect.p0.v[axis] = box->rect.p0.v[axis] + child->fixed_position.v[axis] - !(child->flags&(UI_BoxFlag_SkipViewOffX<<axis))*floor_f32(box->view_off.v[axis]);
} }
child->rect.p1.v[axis] = child->rect.p0.v[axis] + child->fixed_size.v[axis];
child->rect.p0.x = floor_f32(child->rect.p0.x);
child->rect.p0.y = floor_f32(child->rect.p0.y);
child->rect.p1.x = floor_f32(child->rect.p1.x);
child->rect.p1.y = floor_f32(child->rect.p1.y);
// rjf: grab new position
F32 new_position = Min(child->rect.p0.v[axis], child->rect.p1.v[axis]);
// rjf: store position delta
child->position_delta.v[axis] = new_position - original_position;
} }
// rjf: determine final rect for child, given fixed_position & size //- rjf: store view bounds
if(child->flags & (UI_BoxFlag_AnimatePosX<<axis))
{ {
if(child->first_touched_build_index == child->last_touched_build_index) box->view_bounds.v[axis] = bounds;
{
child->fixed_position_animated = child->fixed_position;
}
child->rect.p0.v[axis] = root->rect.p0.v[axis] + child->fixed_position_animated.v[axis] - !(child->flags&(UI_BoxFlag_SkipViewOffX<<axis))*floor_f32(root->view_off.v[axis]);
} }
else
{
child->rect.p0.v[axis] = root->rect.p0.v[axis] + child->fixed_position.v[axis] - !(child->flags&(UI_BoxFlag_SkipViewOffX<<axis))*floor_f32(root->view_off.v[axis]);
}
child->rect.p1.v[axis] = child->rect.p0.v[axis] + child->fixed_size.v[axis];
child->rect.p0.x = floor_f32(child->rect.p0.x);
child->rect.p0.y = floor_f32(child->rect.p0.y);
child->rect.p1.x = floor_f32(child->rect.p1.x);
child->rect.p1.y = floor_f32(child->rect.p1.y);
// rjf: grab new position
F32 new_position = Min(child->rect.p0.v[axis], child->rect.p1.v[axis]);
// rjf: store position delta
child->position_delta.v[axis] = new_position - original_position;
} }
//- rjf: store view bounds
{
root->view_bounds.v[axis] = bounds;
}
//- rjf: recurse
for(UI_Box *child = root->first; !ui_box_is_nil(child); child = child->next)
{
ui_layout_position__in_place_rec(child, axis);
}
ProfEnd(); ProfEnd();
} }
@@ -1891,11 +1883,11 @@ internal void
ui_layout_root(UI_Box *root, Axis2 axis) ui_layout_root(UI_Box *root, Axis2 axis)
{ {
ProfBegin("ui layout pass (%s)", axis == Axis2_X ? "x" : "y"); ProfBegin("ui layout pass (%s)", axis == Axis2_X ? "x" : "y");
ui_calc_sizes_standalone__in_place_rec(root, axis); ui_calc_sizes_standalone__in_place(root, axis);
ui_calc_sizes_upwards_dependent__in_place_rec(root, axis); ui_calc_sizes_upwards_dependent__in_place(root, axis);
ui_calc_sizes_downwards_dependent__in_place_rec(root, axis); ui_calc_sizes_downwards_dependent__in_place(root, axis);
ui_layout_enforce_constraints__in_place_rec(root, axis); ui_layout_enforce_constraints__in_place(root, axis);
ui_layout_position__in_place_rec(root, axis); ui_layout_position__in_place(root, axis);
ProfEnd(); ProfEnd();
} }
+5 -5
View File
@@ -862,11 +862,11 @@ internal UI_Box * ui_box_from_key(UI_Key key);
internal void ui_begin_build(OS_Handle window, UI_EventList *events, UI_IconInfo *icon_info, UI_Theme *theme, UI_AnimationInfo *animation_info, F32 real_dt, F32 animation_dt); internal void ui_begin_build(OS_Handle window, UI_EventList *events, UI_IconInfo *icon_info, UI_Theme *theme, UI_AnimationInfo *animation_info, F32 real_dt, F32 animation_dt);
internal void ui_end_build(void); internal void ui_end_build(void);
internal void ui_calc_sizes_standalone__in_place_rec(UI_Box *root, Axis2 axis); internal void ui_calc_sizes_standalone__in_place(UI_Box *root, Axis2 axis);
internal void ui_calc_sizes_upwards_dependent__in_place_rec(UI_Box *root, Axis2 axis); internal void ui_calc_sizes_upwards_dependent__in_place(UI_Box *root, Axis2 axis);
internal void ui_calc_sizes_downwards_dependent__in_place_rec(UI_Box *root, Axis2 axis); internal void ui_calc_sizes_downwards_dependent__in_place(UI_Box *root, Axis2 axis);
internal void ui_layout_enforce_constraints__in_place_rec(UI_Box *root, Axis2 axis); internal void ui_layout_enforce_constraints__in_place(UI_Box *root, Axis2 axis);
internal void ui_layout_position__in_place_rec(UI_Box *root, Axis2 axis); internal void ui_layout_position__in_place(UI_Box *root, Axis2 axis);
internal void ui_layout_root(UI_Box *root, Axis2 axis); internal void ui_layout_root(UI_Box *root, Axis2 axis);
//////////////////////////////// ////////////////////////////////