profiler ui wip

This commit is contained in:
jacob 2026-03-30 06:51:40 -05:00
parent 99a0da468a
commit 78f4677cdb
10 changed files with 309 additions and 187 deletions

View File

@ -323,8 +323,8 @@ Enum(G_MemoryFlag)
G_MemoryFlag_AllowTextureRW = (1 << 1),
G_MemoryFlag_AllowTextureDraw = (1 << 2),
G_MemoryFlag_AllowTextureDepthStencil = (1 << 3),
G_MemoryFlag_CpuRead = (1 << 4),
G_MemoryFlag_CpuWrite = (1 << 5),
G_MemoryFlag_HostRead = (1 << 4),
G_MemoryFlag_HostWrite = (1 << 5),
G_MemoryFlag_ForceNoReuse = (1 << 6),
};

View File

@ -587,7 +587,7 @@ void G_Bootstrap(void)
queue->print_readback_buffer = G_PushStructs(
cl, gpu_perm,
u8, queue->print_buffer_size,
.flags = G_MemoryFlag_CpuRead,
.flags = G_MemoryFlag_HostRead,
.name = Lit("Debug print readback buffer")
);
}
@ -1296,11 +1296,11 @@ G_BaseDescriptorIndex G_PushMemory(G_CommandListHandle cl_handle, G_ArenaHandle
{
G_D12_ResourceHeapKind heap_kind = G_D12_ResourceHeapKind_Gpu;
// Heap flags
if (flags & G_MemoryFlag_CpuRead)
if (flags & G_MemoryFlag_HostRead)
{
heap_kind = G_D12_ResourceHeapKind_CpuWriteBack;
}
else if (flags & G_MemoryFlag_CpuWrite)
else if (flags & G_MemoryFlag_HostWrite)
{
heap_kind = G_D12_ResourceHeapKind_CpuWriteCombined;
}
@ -1388,6 +1388,8 @@ G_BaseDescriptorIndex G_PushMemory(G_CommandListHandle cl_handle, G_ArenaHandle
//- Check for reset-resource reusability
// Pop reset resource
u64 old_buffer_stride = 0;
u64 old_buffer_offset = 0;
resource = gpu_arena->reset_resources.first;
if (resource)
{
@ -1398,6 +1400,8 @@ G_BaseDescriptorIndex G_PushMemory(G_CommandListHandle cl_handle, G_ArenaHandle
D3D12_RESOURCE_DESC1 compare_d3d_desc = Zi;
CopyStruct(&reset_d3d_desc, &resource->d3d_desc);
CopyStruct(&compare_d3d_desc, &reset_d3d_desc);
old_buffer_stride = resource->buffer_element_stride;
old_buffer_offset = resource->buffer_element_offset;
// Buffers can be reused if size fits
if (d3d_desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && reset_d3d_desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
@ -1408,9 +1412,30 @@ G_BaseDescriptorIndex G_PushMemory(G_CommandListHandle cl_handle, G_ArenaHandle
}
}
// FIXME: Remove this
if (memory_desc.buffer.stride == 52)
{
DEBUGBREAKABLE;
}
// TODO: Less stringent reuse constraints. We could even create textures as placed resources and reset their underlying heaps.
can_reuse = can_reuse && MatchStruct(&compare_d3d_desc, &d3d_desc);
if (!can_reuse)
b32 can_reuse_descriptor = can_reuse && (!is_buffer || (MaxU64(memory_desc.buffer.stride, 1) == old_buffer_stride));
if (!can_reuse || !can_reuse_descriptor)
{
// Push releasable to command list
{
@ -1432,12 +1457,19 @@ G_BaseDescriptorIndex G_PushMemory(G_CommandListHandle cl_handle, G_ArenaHandle
}
ZeroStruct(release);
SllQueuePush(cl->releases.first, cl->releases.last, release);
release->d3d_resource = resource->d3d_resource;
if (!can_reuse_descriptor)
{
release->descriptor = resource->gpu_descriptor;
resource->gpu_descriptor = 0;
}
if (!can_reuse)
{
release->d3d_resource = resource->d3d_resource;
ZeroStruct(resource);
}
}
}
}
else
{
can_reuse = 0;
@ -1513,6 +1545,7 @@ G_BaseDescriptorIndex G_PushMemory(G_CommandListHandle cl_handle, G_ArenaHandle
Atomic64FetchAdd(&G_D12.cumulative_nonreuse_count, 1);
resource->uid = Atomic64FetchAdd(&G_D12.resource_creation_gen.v, d3d_desc.MipLevels);
}
}
if (is_buffer)
{
@ -1533,13 +1566,12 @@ G_BaseDescriptorIndex G_PushMemory(G_CommandListHandle cl_handle, G_ArenaHandle
{
resource->sampler_desc = memory_desc.sampler;
}
}
if (should_map && !resource->buffer_cpu_address)
{
D3D12_RANGE zero_read_range = Zi;
D3D12_RANGE *read_range_arg = &zero_read_range;
if (memory_desc.buffer.flags & G_MemoryFlag_CpuRead)
if (memory_desc.buffer.flags & G_MemoryFlag_HostRead)
{
// Specify NULL read range to signal to D3D12 that any part of address range may be read by CPU
read_range_arg = 0;
@ -2046,7 +2078,7 @@ G_D12_StagingRegionNode *G_D12_PushStagingRegion(G_D12_CmdList *cl, u64 size)
ring->buffer = G_PushStructs(
G_D12_MakeHandle(G_CommandListHandle, cl), gpu_arena_handle,
u8, new_ring_size,
.flags = G_MemoryFlag_CpuWrite
.flags = G_MemoryFlag_HostWrite
);
ring->base = G_Deref(ring->buffer, u8);
}

View File

@ -84,9 +84,9 @@ Struct(G_D12_Resource)
// Buffer info
D3D12_GPU_VIRTUAL_ADDRESS buffer_gpu_address;
void *buffer_cpu_address;
u64 buffer_element_offset;
u64 buffer_element_stride;
u64 buffer_element_count;
u64 buffer_element_offset;
// Texture info
b32 is_texture;

View File

@ -174,7 +174,7 @@ M_TokenFileList M_TokensFromSrcDirs(Arena *arena, StringList src_dirs)
case TokenizerMode_SingleLineComment:
{
if (t[p] == '\n')
if (t[p] == '\n' || ((p + 1) < l && t[p + 1] == '\n'))
{ // End single line comment
mode = TokenizerMode_None;
p += 1;

View File

@ -15,16 +15,7 @@
//////////////////////////////
//- Resources
// FIXME: Remove this
@ComputeShader V_BlaCS (128, 1)
@ComputeShader V_ProfilerGraphCS (128, 1) // Vertical group dims so that sample access is uniform
@ComputeShader V_PrepareShadeCS (16, 16)
@ComputeShader V_PrepareCellsCS (16, 16)
@ComputeShader V_BackdropDownCS (16, 16)

View File

@ -730,6 +730,7 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2I32 cells_dims = VEC2I32(P_CellsPitch, P_CellsPitch);
//- Init gpu state
G_BufferRef gpu_profiler_frames = Zi;
G_TextureRef gpu_tiles = Zi;
G_BufferRef gpu_particles = Zi;
G_TextureRef gpu_particle_cells[V_ParticleLayer_COUNT];
@ -741,6 +742,14 @@ void V_TickForever(WaveLaneCtx *lane)
{
G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_Direct);
{
//- Init profiler frames buffer
gpu_profiler_frames = G_PushStructs(
cl, gpu_perm,
V_ProfilerFrame, V_ProfilerFramesCap,
.flags = G_MemoryFlag_HostRead | G_MemoryFlag_HostWrite,
.name = Lit("Profiler frames"),
);
V.profiler_frames = G_Deref(gpu_profiler_frames, V_ProfilerFrame);
//- Init tile map texture
gpu_tiles = G_PushTexture2D(
cl, gpu_perm,
@ -900,6 +909,7 @@ void V_TickForever(WaveLaneCtx *lane)
CopyBytes(frame->real_held_buttons, prev_frame->real_held_buttons, sizeof(frame->real_held_buttons));
CopyBytes(frame->held_buttons, prev_frame->held_buttons, sizeof(frame->held_buttons));
frame->palette = prev_frame->palette;
frame->profiler = prev_frame->profiler;
frame->is_editing = prev_frame->is_editing;
frame->ui_debug = prev_frame->ui_debug;
frame->show_console = prev_frame->show_console;
@ -2556,14 +2566,14 @@ void V_TickForever(WaveLaneCtx *lane)
frame->shade_cursor = MulAffineVec2(frame->af.screen_to_shade, frame->screen_cursor);
frame->world_cursor = MulAffineVec2(frame->af.screen_to_world, frame->screen_cursor);
b32 show_editor_ui = TweakBool("Show editor UI", 0);
b32 hide_editor_ui = TweakBool("Hide editor UI", 1);
frame->world_selection_start = frame->world_cursor;
if (frame->is_editing)
{
b32 m1_held = frame->held_buttons[Button_M1];
b32 m2_held = frame->held_buttons[Button_M2];
if (show_editor_ui)
if (!hide_editor_ui)
{
frame->selection_mode = V_SelectionMode_Tile;
}
@ -3636,7 +3646,7 @@ void V_TickForever(WaveLaneCtx *lane)
// FIXME: Remove this
#define V_ShouldSkipPanel(panel) (panel->is_editor_panel && (!frame->is_editing || !show_editor_ui))
#define V_ShouldSkipPanel(panel) (panel->is_editor_panel && (hide_editor_ui || !frame->is_editing))
@ -5390,80 +5400,114 @@ void V_TickForever(WaveLaneCtx *lane)
//- Build profiler
// FIXME: Finalize
V_Profiler *profiler = &frame->profiler;
UI_Key profiler_graph_box = UI_KeyF("graph");
if (TweakBool("Show profiler", 1))
{
profiler->is_showing = 1;
UI_BoxReports profiler_graph_reports = UI_ReportsFromKey(profiler_graph_box);
profiler->graph_dims = DimsFromRng2(profiler_graph_reports.draw.screen_rect);
//- Init test samples
// // FIXME: Remove this
// Struct(SampleState)
// {
// i64 ready_samples_count;
// Arena *samples_arena;
// };
// Struct(Sample)
// {
// u64 zone_id;
// u64 thread_id;
// i64 start_ns;
// i64 end_ns;
// };
// PERSIST i64 sample_states_count = 1;
// PERSIST SampleState *sample_states = 0;
// if (!sample_states)
// {
// sample_states = PushStructs(perm, SampleState, sample_states_count);
// for (i64 sample_state_idx = 0; sample_state_idx < sample_states_count; ++sample_state_idx)
// {
// SampleState *ss = &sample_states[sample_state_idx];
// ss->samples_arena = AcquireArena(Gibi(64));
// ss->ready_samples_count = 128;
// for (i64 sample_idx = 0; sample_idx < ss->ready_samples_count; ++sample_idx)
// {
// Sample *sample = PushStruct(ss->samples_arena, Sample);
// sample->zone_id = (u64)"Hello there";
// sample->thread_id = 0;
// sample->start_ns = NsFromSeconds(sample_idx);
// sample->end_ns = NsFromSeconds(sample_idx + 1);
// }
// }
// }
// FIXME: Remove this
Struct(SampleState)
{
i64 ready_samples_count;
Arena *samples_arena;
};
Struct(Sample)
{
u64 zone_id;
u64 thread_id;
i64 start_ns;
i64 end_ns;
};
PERSIST i64 sample_states_count = 1;
PERSIST SampleState *sample_states = 0;
if (!sample_states)
{
sample_states = PushStructs(perm, SampleState, sample_states_count);
for (i64 sample_state_idx = 0; sample_state_idx < sample_states_count; ++sample_state_idx)
{
SampleState *ss = &sample_states[sample_state_idx];
ss->samples_arena = AcquireArena(Gibi(64));
ss->ready_samples_count = 128;
for (i64 sample_idx = 0; sample_idx < ss->ready_samples_count; ++sample_idx)
{
Sample *sample = PushStruct(ss->samples_arena, Sample);
sample->zone_id = (u64)"Hello there";
sample->thread_id = 0;
sample->start_ns = NsFromSeconds(sample_idx);
sample->end_ns = NsFromSeconds(sample_idx + 1);
}
}
V_ProfilerFrame *pf = &V.profiler_frames[V.profiler_frame_seq % V_ProfilerFramesCap];
V.profiler_frame_seq += 1;
}
//- Build profiler
UI_PushDF(Tag, HashF("profiler"))
UI_Key profiler_box = UI_KeyF("profiler");
UI_PushDF(Tag, profiler_box.v)
UI_PushDF(Parent, profiler_box)
{
UI_Size profiler_height = UI_FNT(20, 1);
UI_Size header_height = UI_FNT(1, 1);
UI_Size header_height = UI_FNT(2, 1);
UI_Size window_padding = UI_FNT(0.5, 1);
Vec4 profiler_color = theme.col.window_bg;
profiler_color.a = 0.75;
UI_Key profiler_key = UI_KeyF("profiler");
UI_SetNext(Parent, vis_ui_box);
UI_SetNext(BackgroundColor, profiler_color);
UI_SetNext(Rounding, UI_RPIX(5 * theme.rounding));
UI_PushDF(Width, UI_GROW(1, 0))
UI_PushDF(Height, profiler_height)
UI_PushDF(Parent, UI_BuildBoxEx(profiler_key))
UI_PushDF(Parent, UI_BuildColumnEx(profiler_box))
UI_PushDF(Parent, UI_BuildRow())
{
UI_BuildSpacer(window_padding, Axis_X);
UI_PushDF(Parent, UI_BuildColumn())
{
//- Header
// UI_PushDF(BackgroundColor, Color_Red)
UI_SetNext(BorderColor, Color_Red);
UI_SetNext(BorderSize, 1);
// UI_SetNext(BorderColor, Color_Red);
// UI_SetNext(BorderSize, 1);
UI_SetNext(ChildAlignment, UI_Region_Center);
UI_PushDF(Height, header_height)
UI_PushDF(Parent, UI_BuildBox())
UI_PushDF(Parent, UI_BuildBoxF("profiler header"))
{
UI_PushDF(TextColor, theme.col.hint)
UI_BuildLabelF("Profiler");
}
UI_BuildDivider(UI_PIX(1, 1), theme.col.divider, Axis_Y);
//- Graph
// UI_SetNext(BackgroundColor, Color_Cyan);
UI_PushDF(Parent, UI_BuildBoxEx(profiler_graph_box))
{
}
}
UI_BuildSpacer(window_padding, Axis_X);
}
}
}
@ -6651,6 +6695,7 @@ void V_TickForever(WaveLaneCtx *lane)
Rng3 screen_viewport = RNG3(VEC3(0, 0, 0), VEC3(frame->screen_dims.x, frame->screen_dims.y, 1));
Rng2 screen_scissor = RNG2(VEC2(screen_viewport.p0.x, screen_viewport.p0.y), VEC2(screen_viewport.p1.x, screen_viewport.p1.y));
b32 should_draw_profiler_graph = profiler->is_showing && profiler->graph_dims.x > 0 && profiler->graph_dims.y > 0;
{
//////////////////////////////
//- GPU upload pass
@ -6708,6 +6753,27 @@ void V_TickForever(WaveLaneCtx *lane)
.name = StringF(frame->arena, "Screen target [%F]", FmtSint(frame->tick))
);
// Profiler graph
// FIXME: Limit dims based on sample buffer capacity
frame->profiler_graph_dims = profiler->graph_dims;
frame->profiler_frame_seq = V.profiler_frame_seq;
if (should_draw_profiler_graph)
{
frame->profiler_graph = G_PushTexture2D(
cl, gpu_frame_arena,
G_TextureLayout_Family,
G_Format_R16G16B16A16_Float,
profiler->graph_dims,
.flags = G_MemoryFlag_AllowTextureRW,
.name = StringF(frame->arena, "Profiler graph [%F]", FmtSint(frame->tick))
);
{
Rng2 uv = RNG2(VEC2(0, 0), VEC2(1, 1));
UI_SetRawTexture(profiler_graph_box, frame->profiler_graph, uv);
}
}
// Albedo texture
frame->albedo = G_PushTexture2D(
cl, gpu_frame_arena,
@ -6819,6 +6885,12 @@ void V_TickForever(WaveLaneCtx *lane)
G_ZoneDF(cl, "Init")
{
// Build profiler graph
if (should_draw_profiler_graph)
{
G_Compute2D(cl, V_ProfilerGraphCS, frame->profiler_graph_dims);
}
// Prepare shade
G_Compute2D(cl, V_PrepareShadeCS, frame->shade_dims);

View File

@ -235,6 +235,15 @@ Struct(V_Palette)
V_TextboxState search_state;
};
////////////////////////////////////////////////////////////
//~ Profiler types
Struct(V_Profiler)
{
Vec2 graph_dims;
b32 is_showing;
};
////////////////////////////////////////////////////////////
//~ Window types
@ -348,6 +357,7 @@ Struct(V_Frame)
Button held_buttons[Button_COUNT]; // User input state captured for gameplay
Button real_held_buttons[Button_COUNT]; // Actual state of user input regardless of keyboard / mouse focus
V_Palette palette;
V_Profiler profiler;
UI_Key text_input_focus;
@ -394,6 +404,9 @@ Struct(V_Ctx)
Atomic32 shutdown;
Fence shutdown_complete;
u64 profiler_frame_seq;
V_ProfilerFrame *profiler_frames;
i64 cur_frame_tick;
V_Frame frames[2];
};

View File

@ -29,75 +29,75 @@
ComputeShader(V_BlaCS)
{
// FIXME: Don't use frame slot
BlaParams params = G_Deref(V_GpuConst_Frame, StructuredBuffer<BlaParams>)[0];
RWTexture2D<Vec4> bla = G_Deref(params.bla_tex, RWTexture2D<Vec4>);
// ComputeShader(V_BlaCS)
// {
// // FIXME: Don't use frame slot
// BlaParams params = G_Deref(V_GpuConst_Frame, StructuredBuffer<BlaParams>)[0];
// RWTexture2D<Vec4> bla = G_Deref(params.bla_tex, RWTexture2D<Vec4>);
i64 samples_count = params.bla_samples_count;
StructuredBuffer<BlaSample> samples = G_Deref(params.bla_samples, StructuredBuffer<BlaSample>);
// i64 samples_count = params.bla_samples_count;
// StructuredBuffer<BlaSample> samples = G_Deref(params.bla_samples, StructuredBuffer<BlaSample>);
Vec2 bla_pos = SV_DispatchThreadID + 0.5;
// Vec2 bla_pos = SV_DispatchThreadID + 0.5;
Vec2 dims = params.bla_dims;
// Vec2 dims = params.bla_dims;
// f32 samp_width = 10 * G_TweakFloat;
f32 samp_width = 3;
// // f32 samp_width = 10 * G_TweakFloat;
// f32 samp_width = 3;
i64 sample_idx = bla_pos.x / samp_width;
// i64 sample_idx = bla_pos.x / samp_width;
if (all(bla_pos < dims) && sample_idx < samples_count)
{
// // Vec4 color = Color_Red;
// Vec4 color = Color_Black;
// color.b = bla_pos.x / dims.x;
// if (MatchFloor(bla_pos, params.cursor))
// {
// G_PrintF("bla_pos: %F, dims: %F, color: %F", G_Fmt(bla_pos), G_Fmt(dims), G_Fmt(color));
// }
// bla[bla_pos] = color;
// if (all(bla_pos < dims) && sample_idx < samples_count)
// {
// // // Vec4 color = Color_Red;
// // Vec4 color = Color_Black;
// // color.b = bla_pos.x / dims.x;
// // if (MatchFloor(bla_pos, params.cursor))
// // {
// // G_PrintF("bla_pos: %F, dims: %F, color: %F", G_Fmt(bla_pos), G_Fmt(dims), G_Fmt(color));
// // }
// // bla[bla_pos] = color;
BlaSample sample = samples[sample_idx];
// f32 ratio0 = (f32)sample.bla0 / (f32)samples_count;
// f32 ratio1 = (f32)sample.bla1 / (f32)samples_count;
// BlaSample sample = samples[sample_idx];
// // f32 ratio0 = (f32)sample.bla0 / (f32)samples_count;
// // f32 ratio1 = (f32)sample.bla1 / (f32)samples_count;
f32 ratio0 = sample.r0;
f32 ratio1 = sample.r1;
// f32 ratio0 = sample.r0;
// f32 ratio1 = sample.r1;
f32 ratio_y = 1.0 - (bla_pos.y / dims.y);
// f32 ratio_y = 1.0 - (bla_pos.y / dims.y);
Vec4 color = 0;
if (ratio_y < ratio0)
{
color = LinearFromSrgb(Vec4(0.82, 0, 0.933, 1));
}
else if (ratio_y < ratio0 + ratio1)
{
color = LinearFromSrgb(Vec4(0.953, 0.467, 0.208, 1));
}
else
{
color = 0;
}
// Vec4 color = 0;
// if (ratio_y < ratio0)
// {
// color = LinearFromSrgb(Vec4(0.82, 0, 0.933, 1));
// }
// else if (ratio_y < ratio0 + ratio1)
// {
// color = LinearFromSrgb(Vec4(0.953, 0.467, 0.208, 1));
// }
// else
// {
// color = 0;
// }
bla[bla_pos] = color;
// bla[bla_pos] = color;
}
}
// }
// }
@ -180,6 +180,33 @@ Vec4 V_ColorFromParticle(V_ParticleDesc desc, u32 particle_idx, u32 density)
return result;
}
////////////////////////////////////////////////////////////
//~ Build profiler graph
ComputeShader(V_ProfilerGraphCS)
{
V_SharedFrame frame = G_Deref(V_GpuConst_Frame, StructuredBuffer<V_SharedFrame>)[0];
RWTexture2D<Vec4> graph = G_Deref(frame.profiler_graph, RWTexture2D<Vec4>);
StructuredBuffer<V_ProfilerFrame> profiler_frames = G_Deref(frame.profiler_frames, StructuredBuffer<V_ProfilerFrame>);
Vec2 graph_dims = frame.profiler_graph_dims;
u32 thread_id = SV_DispatchThreadID.x;
if (thread_id <= frame.profiler_frame_seq && thread_id < graph_dims.x)
{
Vec2 graph_pos = SV_DispatchThreadID + 0.5;
graph_pos.x = graph_dims.x - graph_pos.x;
u64 frame_seq = (frame.profiler_frame_seq - thread_id);
u64 frame_idx = frame_seq % V_ProfilerFramesCap;
V_ProfilerFrame prof_frame = profiler_frames[frame_idx];
Vec4 color = Color_Red;
color.g = Norm24(MixU64(frame_seq));
graph[graph_pos] = color;
}
}
////////////////////////////////////////////////////////////
//~ Prepare frame

View File

@ -50,6 +50,9 @@ Vec4 V_ColorFromParticle(V_ParticleDesc desc, u32 particle_idx, u32 density);
////////////////////////////////////////////////////////////
//~ Shaders
//- Build profiler graph
ComputeShader(V_ProfilerGraphCS);
//- Prepare frame
ComputeShader(V_PrepareShadeCS);
ComputeShader(V_PrepareCellsCS);

View File

@ -213,6 +213,33 @@ Struct(V_DVert)
Vec4 color_lin;
};
////////////////////////////////////////////////////////////
//~ Profiler types
#define V_ProfilerFramesCap Kibi(16)
#define V_ProfilerZonesXList(X) \
X(None, Color_Cyan) \
/* ---------------------------- */
Enum(V_ProfilerZone)
{
#define X(name, ...) CAT(V_ProfilerZone_,name),
V_ProfilerZonesXList(X)
#undef X
V_ProfilerZone_COUNT
};
Struct(V_ProfilerZoneInfo)
{
f32 elapsed_seconds;
};
Struct(V_ProfilerFrame)
{
V_ProfilerZoneInfo zones[V_ProfilerZone_COUNT];
};
////////////////////////////////////////////////////////////
//~ State types
@ -267,55 +294,6 @@ Struct(V_Affines)
Affine tile_to_world;
};
// FIXME: Remove this
Struct(BlaSample)
{
f32 r0;
f32 r1;
};
Struct(BlaParams)
{
// FIXME: Remove this
i64 bla_samples_count;
G_BufferRef bla_samples;
Vec2 cursor;
Vec2 bla_dims;
G_TextureRef bla_tex;
};
Struct(V_SharedFrame)
{
//- Time
@ -406,6 +384,12 @@ Struct(V_SharedFrame)
G_SamplerRef basic_samplers[G_BasicSamplerKind_COUNT];
u64 profiler_frame_seq;
G_BufferRef profiler_frames;
Vec2 profiler_graph_dims;
G_TextureRef profiler_graph;
V_TileDesc tile_descs[P_TileKind_COUNT];
G_TextureRef tiles;