Mostly still reviewing and planning... (see description)
Anything considered static can be aggregated into a single VArena. We don't have to worry about ever releasing its memory or it growing "too large". All memory here must be fixed sized. Conservative persistent memory can grow on demand but we would perfer if it could be trimmed or released when no longer dealing with heavy scenarios. Persistent memory should use a slab allocator that is backed by a virtual address space pool allocator instead of pools allocating from a single varena. Chained Arenas can source thier chunks of vmem from the slab which can be utilized for scratch memory. Fonts should be loaded from VSlab. The string cache should use a dedicated varena with 16-byte alignment. All conservative memory should be trimmable by a wipe command which should free all unused blocks. Each block should be a single OS aware reserve of vmem. The Frame can possilby stay as a single varena with scratch allocation utilized on demand. Although it may be more viable for chained varenas to be derived from the main varena via a slab or pool interface. Frame memory should be trimmable on command which should release its committed vmem to its initial value. A dedicated transient varena should not exist. It should be removed when possible. File mappings for now can use a dedicated varena made on demand with a capped reserve size of 4 meg. Any file exceeding this needs the host to support virtual memory mapped I/O for files. The codebase db will use sqlite for the file I/O abstraction. Host might only need to track the first persistent block of vmem, and the rest can be handled by the client (including wrapping that vmem up in a varena). Hot-reload only needs persistent vmem's ref restored on the client module's side. All other references can be resolved from there.
This commit is contained in:
@ -62,7 +62,10 @@ varena_allocator :: proc( arena : ^VArena ) -> ( allocator : Allocator ) {
|
||||
|
||||
// Default growth_policy is nil
|
||||
varena_init :: proc( base_address : uintptr, to_reserve, to_commit : uint,
|
||||
growth_policy : VArena_GrowthPolicyProc, allow_any_resize : b32 = false, dbg_name : string, enable_mem_tracking : b32 = false,
|
||||
growth_policy: VArena_GrowthPolicyProc = nil,
|
||||
allow_any_resize: b32 = false,
|
||||
dbg_name: string = "",
|
||||
enable_mem_tracking: b32 = false,
|
||||
) -> ( arena : VArena, alloc_error : AllocatorError)
|
||||
{
|
||||
page_size := uint(virtual_get_page_size())
|
||||
@ -78,8 +81,8 @@ varena_init :: proc( base_address : uintptr, to_reserve, to_commit : uint,
|
||||
return
|
||||
}
|
||||
|
||||
arena.vmem = vmem
|
||||
arena.commit_used = 0
|
||||
arena.vmem = vmem
|
||||
arena.commit_used = 0
|
||||
|
||||
if growth_policy == nil {
|
||||
arena.growth_policy = varena_default_growth_policy
|
||||
|
@ -71,6 +71,7 @@ import "codebase:grime"
|
||||
import "codebase:sectr"
|
||||
VArena :: sectr.VArena
|
||||
fatal :: sectr.fatal
|
||||
JobSystemContext :: sectr.JobSystemContext
|
||||
Logger :: sectr.Logger
|
||||
logger_init :: sectr.logger_init
|
||||
LogLevel :: sectr.LogLevel
|
||||
@ -106,6 +107,8 @@ RuntimeState :: struct {
|
||||
running : b32,
|
||||
client_memory : ClientMemory,
|
||||
sectr_api : sectr.ModuleAPI,
|
||||
|
||||
job_system: JobSystemContext,
|
||||
}
|
||||
|
||||
ClientMemory :: struct {
|
||||
|
@ -10,8 +10,11 @@ Str_App_State := "App State"
|
||||
|
||||
//region Memory
|
||||
|
||||
// Data segment Memory for sectr module.
|
||||
Memory_App : Memory
|
||||
|
||||
// General memory configuration
|
||||
|
||||
Memory_Base_Address_Persistent :: Terabyte * 1
|
||||
Memory_Base_Address_Frame :: Memory_Base_Address_Persistent + Memory_Reserve_Persistent * 2
|
||||
Memory_Base_Address_Transient :: Memory_Base_Address_Frame + Memory_Reserve_Frame * 2
|
||||
@ -29,13 +32,6 @@ Memory_Commit_Initial_Frame :: 4 * Kilobyte
|
||||
Memory_Commit_Initial_Transient :: 4 * Kilobyte
|
||||
Memory_Commit_Initial_Filebuffer :: 4 * Kilobyte
|
||||
|
||||
MemorySnapshot :: struct {
|
||||
persistent : []u8,
|
||||
frame : []u8,
|
||||
transient : []u8,
|
||||
// files_buffer cannot be restored from snapshot
|
||||
}
|
||||
|
||||
Memory :: struct {
|
||||
persistent : ^VArena,
|
||||
frame : ^VArena,
|
||||
@ -44,15 +40,12 @@ Memory :: struct {
|
||||
|
||||
state : ^State,
|
||||
|
||||
// Should only be used for small memory allocation iterations
|
||||
// Not for large memory env states
|
||||
snapshot : MemorySnapshot,
|
||||
|
||||
replay : ReplayState,
|
||||
logger : Logger,
|
||||
profiler : ^SpallProfiler
|
||||
}
|
||||
|
||||
|
||||
persistent_allocator :: proc() -> Allocator {
|
||||
result := varena_allocator( Memory_App.persistent )
|
||||
return result
|
||||
@ -97,37 +90,6 @@ transient_slab_allocator :: proc() -> Allocator {
|
||||
return result
|
||||
}
|
||||
|
||||
// TODO(Ed) : Implment host memory mapping api
|
||||
save_snapshot :: proc( snapshot : ^MemorySnapshot )
|
||||
{
|
||||
// Make sure the snapshot size is able to hold the current size of the arenas
|
||||
// Grow the files & mapping otherwise
|
||||
{
|
||||
// TODO(Ed) : Implement eventually
|
||||
}
|
||||
|
||||
persistent := Memory_App.persistent
|
||||
mem.copy_non_overlapping( & snapshot.persistent[0], persistent.reserve_start, int(persistent.commit_used) )
|
||||
|
||||
frame := Memory_App.frame
|
||||
mem.copy_non_overlapping( & snapshot.frame[0], frame.reserve_start, int(frame.commit_used) )
|
||||
|
||||
transient := Memory_App.transient
|
||||
mem.copy_non_overlapping( & snapshot.transient[0], transient.reserve_start, int(transient.commit_used) )
|
||||
}
|
||||
|
||||
// TODO(Ed) : Implment host memory mapping api
|
||||
load_snapshot :: proc( snapshot : ^MemorySnapshot ) {
|
||||
persistent := Memory_App.persistent
|
||||
mem.copy_non_overlapping( persistent.reserve_start, & snapshot.persistent[0], int(persistent.commit_used) )
|
||||
|
||||
frame := Memory_App.frame
|
||||
mem.copy_non_overlapping( frame.reserve_start, & snapshot.frame[0], int(frame.commit_used) )
|
||||
|
||||
transient := Memory_App.transient
|
||||
mem.copy_non_overlapping( transient.reserve_start, & snapshot.transient[0], int(transient.commit_used) )
|
||||
}
|
||||
|
||||
// TODO(Ed) : Implement usage of this
|
||||
MemoryConfig :: struct {
|
||||
reserve_persistent : uint,
|
||||
@ -220,6 +182,8 @@ State :: struct {
|
||||
transient_clear_time : f32, // Time in seconds for the usual period to clear transient
|
||||
transient_clear_elapsed : f32, // Time since last clear
|
||||
|
||||
job_system : JobSystemContext,
|
||||
|
||||
string_cache : StringCache,
|
||||
|
||||
input_data : [2]InputState,
|
||||
|
@ -626,6 +626,7 @@ clean_frame :: proc()
|
||||
|
||||
free_all( frame_allocator() )
|
||||
|
||||
// TODO(Ed): Delete this we are no longer using the temp_allocator this way.
|
||||
transient_clear_elapsed += frametime_delta32()
|
||||
if transient_clear_elapsed >= transient_clear_time && ! transinet_clear_lock
|
||||
{
|
||||
|
8
code/sectr/engine/host_api.odin
Normal file
8
code/sectr/engine/host_api.odin
Normal file
@ -0,0 +1,8 @@
|
||||
package sectr
|
||||
|
||||
|
||||
Host_API :: struct {
|
||||
// request_virtual_memory: HostAPI_RequestVirtualMemory,
|
||||
// request_virtual_mapped_io: HostAPI_RequestVirtaulMappedIO,
|
||||
// enqueue_job: ,
|
||||
}
|
75
code/sectr/engine/job_system.odin
Normal file
75
code/sectr/engine/job_system.odin
Normal file
@ -0,0 +1,75 @@
|
||||
package sectr
|
||||
|
||||
ThreadProc :: #type proc(data: rawptr)
|
||||
|
||||
IgnoredThreads :: bit_set[ 0 ..< 64 ]
|
||||
|
||||
JobProc :: #type proc(data: rawptr)
|
||||
|
||||
JobGroup :: struct {
|
||||
counter: u64,
|
||||
}
|
||||
|
||||
JobPriority :: enum {
|
||||
Medium = 0,
|
||||
Low,
|
||||
High,
|
||||
}
|
||||
|
||||
Job :: struct {
|
||||
next: ^Job,
|
||||
cb: JobProc,
|
||||
data: rawptr,
|
||||
group: ^JobGroup,
|
||||
ignored: IgnoredThreads,
|
||||
dbg_lbl: string,
|
||||
}
|
||||
|
||||
JobList :: struct {
|
||||
head: ^Job,
|
||||
mutex: AtomicMutex,
|
||||
}
|
||||
|
||||
JobSystemContext :: struct {
|
||||
job_lists: [JobPriority]JobList,
|
||||
worker_cb: ThreadProc,
|
||||
worker_data: rawptr,
|
||||
counter: int,
|
||||
workers: [] ^ThreadWorkerContext,
|
||||
running: b32,
|
||||
}
|
||||
|
||||
ThreadWorkerContext :: struct {
|
||||
system_ctx: Thread,
|
||||
index: int,
|
||||
}
|
||||
|
||||
// Hard constraint for Windows
|
||||
JOB_SYSTEM_MAX_WORKER_THREADS :: 64
|
||||
|
||||
/*
|
||||
Threads are setup upfront during the client API's startup.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
jobsys_startup :: proc(ctx: ^JobSystemContext, num_workers : int, worker_exec: ThreadProc, worker_data: rawptr) {
|
||||
ctx^ = {
|
||||
worker_cb = worker_exec,
|
||||
worker_data = worker_data,
|
||||
counter = 1,
|
||||
}
|
||||
// Determine number of physical cores
|
||||
// Allocate worker contextes based on number of physical cores - 1 (main thread managed by host included assumed to be index 0)
|
||||
//
|
||||
// num_hw_threads = min(JOB_SYSTEM_MAX_WORKER_THREADS, )
|
||||
// jobsys_worker_make :
|
||||
}
|
||||
|
||||
thread_worker_exec :: proc(_: rawptr) {
|
||||
|
||||
}
|
||||
|
||||
jobsys_shutdown :: proc(ctx: ^JobSystemContext) {
|
||||
|
||||
}
|
@ -126,6 +126,9 @@ import "core:path/filepath"
|
||||
|
||||
import "core:slice"
|
||||
|
||||
import "core:sync"
|
||||
AtomicMutex :: sync.Atomic_Mutex
|
||||
|
||||
import "core:strconv"
|
||||
parse_f32 :: strconv.parse_f32
|
||||
parse_u64 :: strconv.parse_u64
|
||||
@ -147,6 +150,9 @@ import "core:time"
|
||||
time_now :: time.now
|
||||
Time :: time.Time
|
||||
|
||||
import "core:thread"
|
||||
Thread :: thread.Thread
|
||||
|
||||
import "core:unicode"
|
||||
is_white_space :: unicode.is_white_space
|
||||
|
||||
@ -348,6 +354,8 @@ import "codebase:grime"
|
||||
|
||||
varena_allocator :: grime.varena_allocator
|
||||
|
||||
VArena_GrowthPolicyProc :: grime.VArena_GrowthPolicyProc
|
||||
|
||||
//endregion codebase
|
||||
|
||||
//region Procedure overload mappings
|
||||
|
@ -1,163 +1,5 @@
|
||||
package sectr
|
||||
|
||||
ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_LayoutDirection_X, width_ref : ^f32 = nil )
|
||||
{
|
||||
container_width : f32
|
||||
if width_ref != nil {
|
||||
container_width = width_ref ^
|
||||
}
|
||||
else {
|
||||
container_width = container.computed.content.max.x - container.computed.content.min.x
|
||||
}
|
||||
container_height := container.computed.content.max.y - container.computed.content.min.y
|
||||
|
||||
// do layout calculations for the children
|
||||
total_stretch_ratio : f32 = 0.0
|
||||
size_req_children : f32 = 0
|
||||
for child := container.first; child != nil; child = child.next
|
||||
{
|
||||
using child.layout
|
||||
scaled_width_by_height : b32 = b32(.Scale_Width_By_Height_Ratio in flags)
|
||||
if .Scale_Width_By_Height_Ratio in flags
|
||||
{
|
||||
size_req_children += size.min.x * container_height
|
||||
continue
|
||||
}
|
||||
if .Fixed_Width in flags
|
||||
{
|
||||
size_req_children += size.min.x
|
||||
continue
|
||||
}
|
||||
|
||||
size_req_children += size.min.x
|
||||
total_stretch_ratio += anchor.ratio.x
|
||||
}
|
||||
|
||||
avail_flex_space := container_width - size_req_children
|
||||
|
||||
allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space, container_height : f32 ) -> (space_allocated : f32)
|
||||
{
|
||||
using child.layout
|
||||
if .Scale_Width_By_Height_Ratio in flags {
|
||||
size.min.y = container_height
|
||||
space_allocated = size.min.x * container_height
|
||||
}
|
||||
else if ! (.Fixed_Width in flags) {
|
||||
potential_size := anchor.ratio.x * (1 / total_stretch_ratio) * avail_flex_space
|
||||
space_allocated = max(potential_size, size.min.x)
|
||||
size.min.x = space_allocated
|
||||
}
|
||||
else {
|
||||
space_allocated = size.min.x
|
||||
}
|
||||
space_allocated -= margins.left - margins.right
|
||||
size.min.x -= margins.left - margins.right
|
||||
flags |= {.Fixed_Width}
|
||||
return
|
||||
}
|
||||
|
||||
space_used : f32 = 0.0
|
||||
switch direction{
|
||||
case .Left_To_Right:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
using child.layout
|
||||
child_width := allocate_space(child, total_stretch_ratio, avail_flex_space, container_height)
|
||||
anchor = range2({0, anchor.bottom}, {0, anchor.top})
|
||||
alignment = {0, alignment.y}
|
||||
pos.x = space_used
|
||||
space_used += child_width + child.layout.margins.left + child.layout.margins.right
|
||||
}
|
||||
case .Right_To_Left:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
using child.layout
|
||||
child_width := allocate_space(child, total_stretch_ratio, avail_flex_space, container_height)
|
||||
anchor = range2({1, anchor.bottom}, {0, anchor.top})
|
||||
alignment = {1, alignment.y}
|
||||
pos.x = space_used
|
||||
space_used -= child_width + child.layout.margins.left + child.layout.margins.right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui_layout_children_vertically :: proc( container : ^UI_Box, direction : UI_LayoutDirection_Y, height_ref : ^f32 = nil )
|
||||
{
|
||||
container_height : f32
|
||||
if height_ref != nil {
|
||||
container_height = height_ref ^
|
||||
}
|
||||
else {
|
||||
container_height = container.computed.content.max.y - container.computed.content.min.y
|
||||
}
|
||||
container_width := container.computed.content.max.x - container.computed.content.min.x
|
||||
|
||||
// do layout calculations for the children
|
||||
total_stretch_ratio : f32 = 0.0
|
||||
size_req_children : f32 = 0
|
||||
for child := container.first; child != nil; child = child.next
|
||||
{
|
||||
using child.layout
|
||||
scaled_height_by_width : b32 = b32(.Scale_Height_By_Width_Ratio in flags)
|
||||
if scaled_height_by_width {
|
||||
size_req_children += size.min.y * container_width
|
||||
continue
|
||||
}
|
||||
if .Fixed_Height in flags
|
||||
{
|
||||
size_req_children += size.min.y
|
||||
continue
|
||||
}
|
||||
|
||||
size_req_children += size.min.y
|
||||
total_stretch_ratio += anchor.ratio.y
|
||||
}
|
||||
|
||||
avail_flex_space := container_height - size_req_children
|
||||
|
||||
allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space, container_width : f32 ) -> (space_allocated : f32)
|
||||
{
|
||||
using child.layout
|
||||
if .Scale_Height_By_Width_Ratio in flags {
|
||||
size.min.x = container_width
|
||||
space_allocated = size.min.y * container_width
|
||||
}
|
||||
if ! (.Fixed_Height in flags) {
|
||||
potential_size := (anchor.ratio.y * (1 / total_stretch_ratio) * avail_flex_space)
|
||||
space_allocated = max(potential_size, size.min.y)
|
||||
size.min.y = space_allocated
|
||||
}
|
||||
else {
|
||||
space_allocated = size.min.y
|
||||
}
|
||||
space_allocated -= margins.top - margins.bottom
|
||||
size.min.y -= margins.top - margins.bottom
|
||||
flags |= {.Fixed_Height}
|
||||
return
|
||||
}
|
||||
|
||||
space_used : f32 = 0
|
||||
switch direction
|
||||
{
|
||||
case .Top_To_Bottom:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
using child.layout
|
||||
child_height := allocate_space(child, total_stretch_ratio, avail_flex_space, container_width)
|
||||
anchor = range2({anchor.left, 1}, {anchor.right, 0})
|
||||
alignment = {alignment.x, 1}
|
||||
pos.y = space_used
|
||||
space_used -= child_height - child.layout.margins.top - child.layout.margins.bottom
|
||||
}
|
||||
case .Bottom_To_Top:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
using child.layout
|
||||
child_height := allocate_space(child, total_stretch_ratio, avail_flex_space, container_width)
|
||||
anchor = range2({anchor.left,0}, {anchor.right, 0})
|
||||
alignment = {alignment.x, 0}
|
||||
pos.y = space_used
|
||||
space_used += child_height - child.layout.margins.top - child.layout.margins.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui_box_compute_layout :: proc( box : ^UI_Box,
|
||||
dont_mark_fresh : b32 = false,
|
||||
ancestors_layout_required : b32 = false,
|
||||
@ -410,3 +252,161 @@ ui_box_compute_layout_children :: proc( box : ^UI_Box )
|
||||
ui_box_compute_layout( current )
|
||||
}
|
||||
}
|
||||
|
||||
ui_layout_children_horizontally :: proc( container : ^UI_Box, direction : UI_LayoutDirection_X, width_ref : ^f32 = nil )
|
||||
{
|
||||
container_width : f32
|
||||
if width_ref != nil {
|
||||
container_width = width_ref ^
|
||||
}
|
||||
else {
|
||||
container_width = container.computed.content.max.x - container.computed.content.min.x
|
||||
}
|
||||
container_height := container.computed.content.max.y - container.computed.content.min.y
|
||||
|
||||
// do layout calculations for the children
|
||||
total_stretch_ratio : f32 = 0.0
|
||||
size_req_children : f32 = 0
|
||||
for child := container.first; child != nil; child = child.next
|
||||
{
|
||||
using child.layout
|
||||
scaled_width_by_height : b32 = b32(.Scale_Width_By_Height_Ratio in flags)
|
||||
if .Scale_Width_By_Height_Ratio in flags
|
||||
{
|
||||
size_req_children += size.min.x * container_height
|
||||
continue
|
||||
}
|
||||
if .Fixed_Width in flags
|
||||
{
|
||||
size_req_children += size.min.x
|
||||
continue
|
||||
}
|
||||
|
||||
size_req_children += size.min.x
|
||||
total_stretch_ratio += anchor.ratio.x
|
||||
}
|
||||
|
||||
avail_flex_space := container_width - size_req_children
|
||||
|
||||
allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space, container_height : f32 ) -> (space_allocated : f32)
|
||||
{
|
||||
using child.layout
|
||||
if .Scale_Width_By_Height_Ratio in flags {
|
||||
size.min.y = container_height
|
||||
space_allocated = size.min.x * container_height
|
||||
}
|
||||
else if ! (.Fixed_Width in flags) {
|
||||
potential_size := anchor.ratio.x * (1 / total_stretch_ratio) * avail_flex_space
|
||||
space_allocated = max(potential_size, size.min.x)
|
||||
size.min.x = space_allocated
|
||||
}
|
||||
else {
|
||||
space_allocated = size.min.x
|
||||
}
|
||||
space_allocated -= margins.left - margins.right
|
||||
size.min.x -= margins.left - margins.right
|
||||
flags |= {.Fixed_Width}
|
||||
return
|
||||
}
|
||||
|
||||
space_used : f32 = 0.0
|
||||
switch direction{
|
||||
case .Left_To_Right:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
using child.layout
|
||||
child_width := allocate_space(child, total_stretch_ratio, avail_flex_space, container_height)
|
||||
anchor = range2({0, anchor.bottom}, {0, anchor.top})
|
||||
alignment = {0, alignment.y}
|
||||
pos.x = space_used
|
||||
space_used += child_width + child.layout.margins.left + child.layout.margins.right
|
||||
}
|
||||
case .Right_To_Left:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
using child.layout
|
||||
child_width := allocate_space(child, total_stretch_ratio, avail_flex_space, container_height)
|
||||
anchor = range2({1, anchor.bottom}, {0, anchor.top})
|
||||
alignment = {1, alignment.y}
|
||||
pos.x = space_used
|
||||
space_used -= child_width + child.layout.margins.left + child.layout.margins.right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ui_layout_children_vertically :: proc( container : ^UI_Box, direction : UI_LayoutDirection_Y, height_ref : ^f32 = nil )
|
||||
{
|
||||
container_height : f32
|
||||
if height_ref != nil {
|
||||
container_height = height_ref ^
|
||||
}
|
||||
else {
|
||||
container_height = container.computed.content.max.y - container.computed.content.min.y
|
||||
}
|
||||
container_width := container.computed.content.max.x - container.computed.content.min.x
|
||||
|
||||
// do layout calculations for the children
|
||||
total_stretch_ratio : f32 = 0.0
|
||||
size_req_children : f32 = 0
|
||||
for child := container.first; child != nil; child = child.next
|
||||
{
|
||||
using child.layout
|
||||
scaled_height_by_width : b32 = b32(.Scale_Height_By_Width_Ratio in flags)
|
||||
if scaled_height_by_width {
|
||||
size_req_children += size.min.y * container_width
|
||||
continue
|
||||
}
|
||||
if .Fixed_Height in flags
|
||||
{
|
||||
size_req_children += size.min.y
|
||||
continue
|
||||
}
|
||||
|
||||
size_req_children += size.min.y
|
||||
total_stretch_ratio += anchor.ratio.y
|
||||
}
|
||||
|
||||
avail_flex_space := container_height - size_req_children
|
||||
|
||||
allocate_space :: proc( child : ^UI_Box, total_stretch_ratio, avail_flex_space, container_width : f32 ) -> (space_allocated : f32)
|
||||
{
|
||||
using child.layout
|
||||
if .Scale_Height_By_Width_Ratio in flags {
|
||||
size.min.x = container_width
|
||||
space_allocated = size.min.y * container_width
|
||||
}
|
||||
if ! (.Fixed_Height in flags) {
|
||||
potential_size := (anchor.ratio.y * (1 / total_stretch_ratio) * avail_flex_space)
|
||||
space_allocated = max(potential_size, size.min.y)
|
||||
size.min.y = space_allocated
|
||||
}
|
||||
else {
|
||||
space_allocated = size.min.y
|
||||
}
|
||||
space_allocated -= margins.top - margins.bottom
|
||||
size.min.y -= margins.top - margins.bottom
|
||||
flags |= {.Fixed_Height}
|
||||
return
|
||||
}
|
||||
|
||||
space_used : f32 = 0
|
||||
switch direction
|
||||
{
|
||||
case .Top_To_Bottom:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
using child.layout
|
||||
child_height := allocate_space(child, total_stretch_ratio, avail_flex_space, container_width)
|
||||
anchor = range2({anchor.left, 1}, {anchor.right, 0})
|
||||
alignment = {alignment.x, 1}
|
||||
pos.y = space_used
|
||||
space_used -= child_height - child.layout.margins.top - child.layout.margins.bottom
|
||||
}
|
||||
case .Bottom_To_Top:
|
||||
for child := container.first; child != nil; child = child.next {
|
||||
using child.layout
|
||||
child_height := allocate_space(child, total_stretch_ratio, avail_flex_space, container_width)
|
||||
anchor = range2({anchor.left,0}, {anchor.right, 0})
|
||||
alignment = {alignment.x, 0}
|
||||
pos.y = space_used
|
||||
space_used += child_height - child.layout.margins.top - child.layout.margins.bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user