Skip to content

Commit

Permalink
Rend resource load limit (#896)
Browse files Browse the repository at this point in the history
* core: Fast path for formatting raw text

* trace: Increase max trace msg size

* rend: Add res create trace scope

* rend: Add resource create load budget

* rend: Fix typo

* rend: Fix typo

* rend: Refactor resource load state machine

* rend: Make rend res unload delay the same as asset unload delay

* rend: Remove a frame's latency from resource load without deps
  • Loading branch information
BastianBlokland committed Jun 20, 2024
1 parent 2227ff3 commit fc03979
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 42 deletions.
1 change: 1 addition & 0 deletions libs/core/include/core_format.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ typedef struct {
typedef enum {
FormatTextFlags_None = 0,
FormatTextFlags_EscapeNonPrintAscii = 1 << 0,
FormatTextFlags_Escape = FormatTextFlags_EscapeNonPrintAscii,
} FormatTextFlags;

/**
Expand Down
24 changes: 17 additions & 7 deletions libs/core/src/format.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,19 @@ void format_write_arg(DynString* str, const FormatArg* arg) {
case FormatArgType_Size:
format_write_size_pretty(str, arg->value_size);
break;
case FormatArgType_Text:
format_write_text(
str,
arg->value_text.size > fmt_txt_len_max ? string_slice(arg->value_text, 0, fmt_txt_len_max)
: arg->value_text,
arg->settings);
case FormatArgType_Text: {
const FormatOptsText* textOpts = arg->settings;
String text = arg->value_text;
if (text.size > fmt_txt_len_max) {
text = string_slice(text, 0, fmt_txt_len_max);
}
if (textOpts->flags & FormatTextFlags_Escape) {
format_write_text(str, text, arg->settings);
} else {
dynstring_append(str, text); // Fast path for raw text.
}
break;
}
case FormatArgType_Char:
format_write_char(str, arg->value_char, arg->settings);
break;
Expand Down Expand Up @@ -517,7 +523,11 @@ void format_write_size_pretty(DynString* str, const usize val) {

void format_write_text(DynString* str, String val, const FormatOptsText* opts) {
diag_assert_msg(val.size <= usize_gibibyte, "Text too big: '{}'", fmt_size(val.size));
mem_for_u8(val, itr) { format_write_char(str, *itr, opts); }
if (opts->flags & FormatTextFlags_Escape) {
mem_for_u8(val, itr) { format_write_char(str, *itr, opts); }
} else {
dynstring_append(str, val); // Fast path for raw text.
}
}

void format_write_text_wrapped(
Expand Down
103 changes: 70 additions & 33 deletions libs/rend/src/resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
#include "core_array.h"
#include "core_diag.h"
#include "core_math.h"
#include "core_path.h"
#include "core_time.h"
#include "ecs_utils.h"
#include "ecs_world.h"
#include "log_logger.h"
#include "rend_register.h"
#include "trace_tracer.h"

#include "platform_internal.h"
#include "reset_internal.h"
Expand All @@ -17,7 +20,12 @@
#include "rvk/shader_internal.h"
#include "rvk/texture_internal.h"

static const u32 g_rendResUnloadUnusedAfterTicks = 480; // NOTE: Less then 2 is not supported.
#define rend_res_max_load_time time_milliseconds(2)

/**
* Amount of frames to delay unloading of resources.
*/
#define rend_res_unload_delay 500

typedef struct {
RvkRepositoryId repoId;
Expand Down Expand Up @@ -65,7 +73,7 @@ typedef enum {
RendResFlags_None = 0,
RendResFlags_Used = 1 << 0,
RendResFlags_Persistent = 1 << 1, // Always considered in-use.
RendResFlags_IgnoreAssetChanges = 1 << 2, // Don't unload when the backend asset changes.
RendResFlags_IgnoreAssetChanges = 1 << 2, // Don't unload when the source asset changes.
} RendResFlags;

typedef enum {
Expand Down Expand Up @@ -350,14 +358,15 @@ static bool rend_res_dependencies_wait(EcsWorld* world, EcsIterator* resourceItr
dependencyRes->flags |= RendResFlags_Used; // Mark the dependencies as still in use.
rend_res_add_dependent(dependencyRes, entity);

if (dependencyRes->state == RendResLoadState_FinishedFailure) {
// Dependency failed to load, also fail this resource.
resComp->state = RendResLoadState_FinishedFailure;
return false;
}
if (dependencyRes->state != RendResLoadState_FinishedSuccess) {
if (ecs_world_has_t(world, *dep, RendResFinishedComp)) {
if (dependencyRes->state == RendResLoadState_FinishedFailure) {
// Dependency failed to load, also fail this resource.
resComp->state = RendResLoadState_FinishedFailure;
return false;
}
diag_assert(dependencyRes->state == RendResLoadState_FinishedSuccess);
} else {
ready = false;
continue;
}
}
return ready;
Expand Down Expand Up @@ -493,41 +502,69 @@ ecs_system_define(RendResLoadSys) {
*/
RvkDevice* device = platform->device;

TimeDuration loadTime = 0;

EcsView* resourceView = ecs_world_view_t(world, ResLoadView);
for (EcsIterator* itr = ecs_view_itr(resourceView); ecs_view_walk(itr);) {
RendResComp* resComp = ecs_view_write_t(itr, RendResComp);
switch (resComp->state) {
case RendResLoadState_AssetAcquire: {
if (rend_res_asset_acquire(world, itr)) {
++resComp->state;
case RendResLoadState_AssetAcquire:
if (!rend_res_asset_acquire(world, itr)) {
break;
}
} break;
case RendResLoadState_AssetWait: {
if (rend_res_asset_wait(world, itr)) {
++resComp->state;
++resComp->state;
break; // NOTE: Cannot fallthrough as asset require takes a frame to take effect.
case RendResLoadState_AssetWait:
if (!rend_res_asset_wait(world, itr)) {
break;
}
} break;
case RendResLoadState_DependenciesAcquire: {
if (rend_res_dependencies_acquire(world, itr)) {
++resComp->state;
++resComp->state;
// Fallthrough.
case RendResLoadState_DependenciesAcquire:
if (!rend_res_dependencies_acquire(world, itr)) {
break;
}
} break;
case RendResLoadState_DependenciesWait: {
if (rend_res_dependencies_wait(world, itr)) {
++resComp->state;
++resComp->state;
if (resComp->dependencies.size) {
break; // NOTE: Cannot fallthrough as dependency acquire takes a frame to take effect.
}
} break;
// Fallthrough.
case RendResLoadState_DependenciesWait:
if (!rend_res_dependencies_wait(world, itr)) {
break;
}
++resComp->state;
// Fallthrough.
case RendResLoadState_Create: {
if (loadTime >= rend_res_max_load_time) {
// Already spend our load budget for this frame; retry next frame.
resComp->state = RendResLoadState_DependenciesWait;
break;
}
#ifdef VOLO_TRACE
const String traceMsg = path_filename(asset_id(ecs_view_read_t(itr, AssetComp)));
#endif
trace_begin_msg("rend_res_create", TraceColor_Blue, "{}", fmt_text(traceMsg));

const TimeSteady loadStart = time_steady_clock();
if (rend_res_create(device, world, itr)) {
++resComp->state;
} else {
diag_assert(resComp->state == RendResLoadState_FinishedFailure);
}
loadTime += time_steady_duration(loadStart, time_steady_clock());

trace_end();
} break;
case RendResLoadState_FinishedSuccess: {
case RendResLoadState_FinishedSuccess:
case RendResLoadState_FinishedFailure:
UNREACHABLE
}

if (resComp->state == RendResLoadState_FinishedSuccess) {
rend_res_finished_success(world, itr);
} break;
case RendResLoadState_FinishedFailure: {
} else if (resComp->state == RendResLoadState_FinishedFailure) {
rend_res_finished_failure(world, itr);
} break;
}
}
}
Expand Down Expand Up @@ -568,7 +605,7 @@ ecs_system_define(RendResUnloadUnusedSys) {
if (UNLIKELY(isUnloading || failed)) {
continue;
}
if (resComp->unusedTicks++ > g_rendResUnloadUnusedAfterTicks) {
if (resComp->unusedTicks++ > rend_res_unload_delay) {
ecs_world_add_t(world, ecs_view_entity(itr), RendResUnloadComp);
}
}
Expand All @@ -583,7 +620,7 @@ ecs_view_define(UnloadChangedView) {
}

/**
* Start unloading resources where the source asset has changed.
* Start unloading resources when the source asset has changed.
*/
ecs_system_define(RendResUnloadChangedSys) {
EcsView* changedAssetsView = ecs_world_view_t(world, UnloadChangedView);
Expand Down Expand Up @@ -735,10 +772,10 @@ bool rend_res_is_persistent(const RendResComp* comp) {
}

u32 rend_res_ticks_until_unload(const RendResComp* comp) {
if (comp->unusedTicks > g_rendResUnloadUnusedAfterTicks) {
if (comp->unusedTicks > rend_res_unload_delay) {
return 0;
}
return g_rendResUnloadUnusedAfterTicks - comp->unusedTicks;
return rend_res_unload_delay - comp->unusedTicks;
}

u32 rend_res_dependents(const RendResComp* comp) { return (u32)comp->dependents.size; }
Expand Down
2 changes: 1 addition & 1 deletion libs/trace/include/trace_tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ typedef enum {
* Pre-condition: id is constant throughout the program (for example a literal).
* Pre-condition: id only consists of ascii characters.
* Pre-condition: msg (and format args) only consist of ascii characters.
* Pre-condition: length of msg (after formatting) is less then 64 bytes.
* Pre-condition: length of msg (after formatting) is less then 256 bytes.
*/
#ifdef VOLO_TRACE
#define trace_begin_msg(_ID_, _COLOR_, _MSG_LIT_, ...) \
Expand Down
2 changes: 1 addition & 1 deletion libs/trace/src/tracer.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include "tracer_internal.h"

#define trace_sinks_max 4
#define trace_message_max 64
#define trace_message_max 256

struct sTracer {
TraceSink* sinks[trace_sinks_max];
Expand Down

0 comments on commit fc03979

Please sign in to comment.