ui layer testing

This commit is contained in:
jacob 2025-10-15 12:02:38 -05:00
parent 5bee781ede
commit 2fdf891d49
11 changed files with 441 additions and 10 deletions

View File

@ -518,7 +518,7 @@ String TrimWhitespace(String s)
* Example:
* FormatString(arena, Lit("Hello there %F"), FmtString(Lit("George")))
*
* NOTE: FmtEnd must be passed as the last arg in the va_list (This is
* NOTE: FmtEnd must be passed as the last arg in the va_list (this is
* done automatically by the `FormatString` macro).
*
* Format arguments:

View File

@ -69,7 +69,7 @@
#define FLOOD_DEBUG 0
#define GPU_DEBUG 0
#define GPU_DEBUG 1
/* If virtual fibers are enabled, each fiber will get its own OS thread,
* and fiber suspend/resume will be emulated using OS thread primitives.
@ -94,6 +94,6 @@
/* TODO: Move these to user-configurable settings */
#define VSYNC 0
#define VSYNC 1
#define AUDIO_ENABLED 0
#define FPS_LIMIT 300

View File

@ -36,7 +36,7 @@ Struct(D_UiRectParams)
//~ Text types
/* How is text aligned within its area */
Enum( D_TextAlignment)
Enum(D_TextAlignment)
{
DRAW_TEXT_ALIGNMENT_LEFT, /* Default */
DRAW_TEXT_ALIGNMENT_CENTER,

View File

@ -2153,6 +2153,11 @@ i64 GPU_PresentSwapchain(GPU_Swapchain *gpu_swapchain, GPU_Resource *gpu_texture
/* Present */
{
__profn("Present");
if (vsync)
{
/* FIXME: Only flush if windowed mode? */
DwmFlush();
}
HRESULT hr = IDXGISwapChain3_Present(swapchain->swapchain, vsync, present_flags);
if (!SUCCEEDED(hr))
{

View File

@ -2023,9 +2023,116 @@ void UpdateUser(P_Window *window)
GPU_MemoryInfo vram = GPU_QueryMemoryInfo();
//- Draw global debug info
//~ Draw global debug info
/* FIXME: Enable this */
#if 1
#if 1
if (g->debug_draw)
{
__profn("Draw debug info");
UI_Box *dbg_box = UI_BuildBox(0, UI_NilKey);
UI_PushParent(dbg_box);
{
UI_BuildLabelF("blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved));
UI_BuildSeparator();
UI_BuildLabelF("blended world tick: %F", FmtUint(g->ss_blended->tick));
UI_BuildSeparator();
UI_BuildLabelF("blended world time: %F", FmtFloat(SecondsFromNs(g->ss_blended->sim_time_ns)));
UI_BuildSeparator();
UI_BuildSeparator();
UI_BuildLabelF("average local sim publish dt: %F", FmtFloat(SecondsFromNs(g->average_local_to_user_snapshot_publish_dt_ns)));
UI_BuildSeparator();
UI_BuildLabelF("local sim last known tick: %F", FmtUint(g->local_sim_last_known_tick));
UI_BuildSeparator();
UI_BuildLabelF("local sim last known time: %F", FmtFloat(SecondsFromNs(g->local_sim_last_known_time_ns)));
UI_BuildSeparator();
UI_BuildLabelF("local sim predicted time: %F", FmtFloat(SecondsFromNs(g->local_sim_predicted_time_ns)));
UI_BuildSeparator();
UI_BuildLabelF("render time target: %F", FmtFloat(SecondsFromNs(g->render_time_target_ns)));
UI_BuildSeparator();
UI_BuildLabelF("render time: %F", FmtFloat(SecondsFromNs(g->render_time_ns)));
UI_BuildSeparator();
UI_BuildSeparator();
UI_BuildLabelF("local player: [%F]", FmtUid(local_player->id.uid));
UI_BuildSeparator();
UI_BuildSeparator();
Vec2 world_cursor = g->world_cursor;
UI_BuildLabelF("cursor world: %F, %F", FmtFloat(world_cursor.x), FmtFloat(world_cursor.y));
UI_BuildSeparator();
Vec2I32 world_tile_cursor = WorldTileIndexFromPos(world_cursor);
UI_BuildLabelF("cursor world tile: %F, %F", FmtSint(world_tile_cursor.x), FmtSint(world_tile_cursor.y));
UI_BuildSeparator();
Vec2I32 local_tile_cursor = LocalTileIndexFromWorldTileIndex(world_tile_cursor);
UI_BuildLabelF("cursor local tile: %F, %F", FmtSint(local_tile_cursor.x), FmtSint(local_tile_cursor.y));
UI_BuildSeparator();
Vec2I32 tile_chunk_cursor = TileChunkIndexFromWorldTileIndex(world_tile_cursor);
UI_BuildLabelF("cursor tile chunk: %F, %F", FmtSint(tile_chunk_cursor.x), FmtSint(tile_chunk_cursor.y));
UI_BuildSeparator();
UI_BuildSeparator();
UI_BuildLabelF("Network read: %F mbit/s", FmtFloat((f64)g->net_bytes_read.last_second * 8 / 1000 / 1000));
UI_BuildSeparator();
UI_BuildLabelF("Network write: %F mbit/s", FmtFloat((f64)g->net_bytes_sent.last_second * 8 / 1000 / 1000));
UI_BuildSeparator();
UI_BuildLabelF("Ping (real): %F ms", FmtFloat(SecondsFromNs(local_player->player_last_rtt_ns) * 1000));
UI_BuildSeparator();
UI_BuildLabelF("Ping (average): %F ms", FmtFloat(local_player->player_average_rtt_seconds * 1000));
UI_BuildSeparator();
UI_BuildSeparator();
UI_BuildLabelF("Memory committed: %F MiB", FmtFloat((f64)GetGstat(GSTAT_MEMORY_COMMITTED) / 1024 / 1024));
UI_BuildSeparator();
UI_BuildLabelF("Virtual memory reserved: %F TiB", FmtFloat((f64)GetGstat(GSTAT_MEMORY_RESERVED) / 1024 / 1024 / 1024 / 1024));
UI_BuildSeparator();
UI_BuildLabelF("Arenas allocated: %F", FmtUint(GetGstat(GSTAT_NUM_ARENAS)));
UI_BuildSeparator();
UI_BuildSeparator();
UI_BuildLabelF("Video memory (GPU): %F MiB", FmtFloat((f64)vram.local_used / 1024 / 1024));
UI_BuildSeparator();
UI_BuildLabelF("Video memory (shared): %F MiB", FmtFloat((f64)vram.non_local_used / 1024 / 1024));
//UI_BuildLabelF(\n"));
//UI_BuildLabelF(\n"));
#if RtcIsEnabled
UI_BuildSeparator();
UI_BuildSeparator();
UI_BuildLabelF("Debug steps: %F", FmtUint(GetGstat(GSTAT_DEBUG_STEPS)));
//UI_BuildLabelF(\n"));
#endif
//draw_text(g->render_sig, font, pos, StringF(scratch.arena, "blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved)));
//draw_text(g->render_sig, font, pos, text);
#if 0
Vec2 pos = VEC2(10, g->ui_size.y);
D_TextOffsetY offset_y = DRAW_TEXT_OFFSET_Y_BOTTOM;
draw_text(g->render_sig, D_TEXTPARAMS(.font = font, .pos = pos, .str = text, .offset_y = offset_y, .color = ColorWhite));
#endif
}
UI_PopParent();
}
#else
if (g->debug_draw)
{
__profn("Draw debug info");
@ -2130,14 +2237,14 @@ void UpdateUser(P_Window *window)
//draw_text(g->render_sig, font, pos, StringF(temp.arena, "blended world entities: %F/%F", FmtUint(g->ss_blended->num_ents_allocated), FmtUint(g->ss_blended->num_ents_reserved)));
//draw_text(g->render_sig, font, pos, text);
#if 0
Vec2 pos = VEC2(10, g->ui_size.y);
D_TextOffsetY offset_y = DRAW_TEXT_OFFSET_Y_BOTTOM;
draw_text(g->render_sig, D_TEXTPARAMS(.font = font, .pos = pos, .str = text, .offset_y = offset_y, .color = ColorWhite));
#endif
EndTempArena(temp);
}
}
#endif
#endif
{
#if DeveloperIsEnabled
@ -2152,8 +2259,7 @@ void UpdateUser(P_Window *window)
#endif
}
//- Render
//~ Render
{
__profn("Render");
GPU_QueueKind gpu_render_queue = GPU_QueueKind_Direct;
@ -3136,7 +3242,6 @@ JobDef(UpdateSim, UNUSED sig, UNUSED id)
mispredicted_tick = master_ss->tick;
}
u64 step_base_tick = local_client->last_tick;
u64 step_end_tick = step_base_tick + 1;
if (mispredicted_tick > 0)

View File

@ -9,6 +9,7 @@
@Dep mixer
@Dep rendertest
@Dep playback
@Dep ui
//- Api
@IncludeC pp_sim.h

88
src/ui/ui.c Normal file
View File

@ -0,0 +1,88 @@
UI_SharedState UI_shared_state = ZI;
////////////////////////////////
//~ State helpers
UI_FiberState *UI_GetFiberState(void)
{
UI_FiberState *f = UI_shared_state.fiber_states[FiberId()];
if (!f)
{
{
Arena *perm = PermArena();
PushAlign(perm, CachelineSize);
f = PushStruct(perm, UI_FiberState);
UI_shared_state.fiber_states[FiberId()] = f;
PushAlign(perm, CachelineSize);
}
f->build_arena = AcquireArena(Gibi(64));
}
return f;
}
////////////////////////////////
//~ Key helpers
UI_Key UI_KeyFromString(String str)
{
UI_Key key = ZI;
key.hash = HashFnv64(Fnv64Basis, str);
return key;
}
////////////////////////////////
//~ Stack helpers
void UI_PushParent(UI_Box *box)
{
UI_FiberState *f = UI_GetFiberState();
}
UI_Box *UI_PopParent(void)
{
UI_FiberState *f = UI_GetFiberState();
UI_Box *box = 0;
return box;
}
////////////////////////////////
//~ Box helpers
UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key)
{
UI_Box *box = 0;
return box;
}
////////////////////////////////
//~ Text
String UI_DisplayTextFromString(String str)
{
/* Cap string at non-display pattern (e.g. "Hello##hiddentext" becomes "Hello") */
u64 new_len = str.len;
if (str.len > 0)
{
b32 escaped = 0;
for (u64 i = 0; i < str.len - 1 && new_len == str.len; ++i)
{
u8 c = str.text[i];
if (c == '\\')
{
escaped = !escaped;
}
else if (!escaped && c == '#' && str.text[i + 1] == '#')
{
new_len = i;
}
}
}
return STRING(new_len, str.text);
}
void UI_SetDisplayText(UI_Box *box, String str)
{
UI_FiberState *f = UI_GetFiberState();
String text = PushString(f->build_arena, UI_DisplayTextFromString(str));
box->display_text = text;
}

174
src/ui/ui.h Normal file
View File

@ -0,0 +1,174 @@
////////////////////////////////
//~ Key types
#define UI_NilKey ((UI_Key) { 0 })
Struct(UI_Key)
{
u64 hash;
};
////////////////////////////////
//~ Box types
Enum(UI_Flag)
{
UI_Flag_None = 0,
UI_Flag_DrawText = (1 << 0),
};
Struct(UI_Box)
{
UI_Flag flags;
String display_text;
};
////////////////////////////////
//~ State types
Struct(UI_FiberState)
{
Arena *build_arena;
};
Struct(UI_SharedState)
{
UI_FiberState *fiber_states[MaxFibers];
} extern UI_shared_state;
////////////////////////////////
//~ State helpers
UI_FiberState *UI_GetFiberState(void);
////////////////////////////////
//~ Key helpers
UI_Key UI_KeyFromString(String text);
////////////////////////////////
//~ Stack helpers
void UI_PushParent(UI_Box *box);
UI_Box *UI_PopParent(void);
////////////////////////////////
//~ Box
UI_Box *UI_BuildBox(UI_Flag flags, UI_Key key);
////////////////////////////////
//~ Text
String UI_DisplayTextFromString(String str);
void UI_SetDisplayText(UI_Box *box, String str);
/* TODO: Remove this */
#if 0
////////////////////////////////
//~ Rect
Struct(UI_RectParams)
{
Xform xf;
GPU_Resource *texture;
ClipRect clip;
u32 tint;
};
#define UI_RECTPARAMS(...) ((UI_RectParams) { \
.tint = ColorWhite, \
.clip = AllClipped, \
__VA_ARGS__ \
})
////////////////////////////////
//~ Text types
/* How is text aligned within its area */
Enum(UI_TextAlignment)
{
UI_TextAlignment_Left, /* Default */
UI_TextAlignment_Center,
UI_TextAlignment_Right
};
/* How does the specified text position relate to the text area.
* E.g. Bottom & Right means the bottom-right of the text area will snap to
* the specified position. */
Enum(UI_TextOffsetX)
{
UI_TextOffsetX_Left, /* Default */
UI_TextOffsetX_Center,
UI_TextOffsetX_Right
};
Enum(UI_TextOffsetY)
{
UI_TextOffsetY_Top, /* Default */
UI_TextOffsetY_Center,
UI_TextOffsetY_Bottom
};
Struct(UI_TextGlyph)
{
f32 off_x;
f32 off_y;
f32 width;
f32 height;
f32 advance;
ClipRect clip;
};
Struct(UI_TextLine)
{
f32 line_width;
u32 num_glyphs;
UI_TextGlyph *glyphs;
UI_TextLine *next;
};
Struct(UI_TextParams)
{
F_Font *font;
Vec2 pos;
f32 scale;
u32 color;
UI_TextAlignment alignment;
UI_TextOffsetX offset_x;
UI_TextOffsetY offset_y;
String str;
};
#define UI_TEXTPARAMS(...) ((UI_TextParams) { \
.scale = 1.0, \
.alignment = UI_TextAlignment_Left, \
.offset_x = UI_TextOffsetX_Left, \
.offset_y = UI_TextOffsetY_Top, \
.color = ColorWhite, \
__VA_ARGS__ \
})s
////////////////////////////////
//~ State types
Struct(UI_SharedState)
{
i32 _;
} extern UI_shared_state;
#endif

15
src/ui/ui.lay Normal file
View File

@ -0,0 +1,15 @@
@Layer ui
//- Dependencies
@Dep gpu
//- Api
@IncludeC ui.h
@IncludeC ui_widgets.h
//- Impl
@IncludeC ui.c
@IncludeC ui_widgets.c
//- Startup
@startup UI_Startup

32
src/ui/ui_widgets.c Normal file
View File

@ -0,0 +1,32 @@
////////////////////////////////
//~ Label widget
UI_Box *UI_BuildLabel(String text)
{
UI_Key key = UI_KeyFromString(text);
UI_Box *box = UI_BuildBox(UI_Flag_DrawText, key);
UI_SetDisplayText(box, text);
return box;
}
UI_Box *UI_BuildLabelF_(char *fmt_cstr, ...)
{
UI_FiberState *f = UI_GetFiberState();
TempArena scratch = BeginScratchNoConflict();
va_list args;
va_start(args, fmt_cstr);
String text = FormatStringV(scratch.arena, StringFromCstrNoLimit(fmt_cstr), args);
UI_Box *box = UI_BuildLabel(text);
va_end(args);
EndScratch(scratch);
return box;
}
////////////////////////////////
//~ Separator widget
UI_Box *UI_BuildSeparator(void)
{
UI_Box *box = UI_BuildBox(0, UI_NilKey);
return box;
}

11
src/ui/ui_widgets.h Normal file
View File

@ -0,0 +1,11 @@
////////////////////////////////
//~ Label widget
UI_Box *UI_BuildLabel(String text);
UI_Box *UI_BuildLabelF_(char *fmt_cstr, ...);
#define UI_BuildLabelF(fmt_cstr, ...) UI_BuildLabelF_(fmt_cstr, __VA_ARGS__, FmtEnd)
////////////////////////////////
//~ Separator widget
UI_Box *UI_BuildSeparator(void);