mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Past the hard count of released textures, drop the cheapest ones first, in one walk per batch.
Past RELEASED_HARD (16,384) the sweep dropped the first released entry in release order that was overwritten or unsampled for 2 s, render targets included, whose drop vk_texture.c already ties to a black frame, and it walked the list from its head for every drop. At the end of the 2026-11-13 session the count sat at 16,384 for a minute while 12,811 of 15,734 released entries owned no memory. The entries now go by class: overwritten ones, then ones that come back intact and are past their usual take-back gap, then other intact ones, and only then ones whose content only the GPU made; still only unsampled ones, and the one sampled longest ago when all were sampled lately, as before. One walk takes a batch down to 256 under the bound. tex_hard_order = 0 keeps the rule of 0.10.1. The drops past the hard count get a reason of their own, so the texture origins line and the reuse line count them apart from the sweep's drops past RELEASED_MAX, with the classes they came from. The class rules are in vk_tex_release.h and checked by tests/tex_release_check.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -440,6 +440,10 @@
|
||||
# Textures the game released that are kept as they are (1 to 16384); past this count only those that lose nothing
|
||||
# when dropped go. Default: one per 2 MB of the video memory budget, 2048 to 8192. Read at start.
|
||||
# tex_released_max = 4096
|
||||
# Past 16384 kept textures some go whatever they cost. 1: the cheapest first: those drawn over, then those that come
|
||||
# back unchanged from the game's memory (the oldest first), and only then those whose content only the GPU made, which
|
||||
# would come back empty. 0: the first one in release order that nothing showed for 2 seconds, of any kind (0.10.1).
|
||||
# tex_hard_order = 1
|
||||
# Megabytes of this process's commit above which the log says so. Windows counts video memory against the commit;
|
||||
# with a discrete GPU the video memory in use is left out of the comparison, since it is not in RAM. Textures no longer
|
||||
# give way at this bound: dropping one lowers both sides of the comparison alike, so nothing they could do brought the
|
||||
|
||||
@@ -61,7 +61,7 @@ static const char *const known[] = {
|
||||
"SKIN_MOTION", // vk_skin_motion.inc
|
||||
"MVOBJ_ALL", "MVOBJ_BANKS", "MVOBJ_EQUAL", "MVOBJ_EXACT", "MVOBJ_EXEC", "MVOBJ_NOC4", "MVOBJ_ONLY", "MVOBJ_TIMES", "MVPREV_CHECK", "MVPREV_GROW", "MVPREV_SKIP", "MVPREV_TRUST", "MV_CHECK",
|
||||
"MV_SURVEY",
|
||||
"PIPE_SEED_BOOT", "ONE_PER_CORE", "OPERATION_MODE", "OUTPUT_RES", "PAD_LAYOUT", "PACE_HZ", "PASS_CRUMBS", "PATCH_OUTPUT", "PATCH_RES_FIXED", "PATCH_SCENE", "PATCH_WIDE_ASPECT", "PATCH_WIDE_HUD", "PATCH_WIDE_MOVIE", "PATCH_WIDE_OUT", "PATCH_FREECAM", "PATCH_OCCLUSION_OFF", "PATCH_WIDE_SAVE", "PATCH_WIDE_SCENE", "PCSS", "PCSS_K", "PERF_OVERLAY", "PERF_SLOW_MS", "PIPELINE_CACHE", "PIPELINE_CACHE_MAX_MB", "PIPELINE_CACHE_PLAY", "PIPELINE_CACHE_PLAY_MB", "PIPE_BINARIES", "PIPE_BINARIES_MAX_MB", "PIPE_FEEDBACK", "PIPE_MEMO", "PIPE_PREBUILD", "PIPE_SEED", "PIPE_SKIP", "PIPE_SYNC", "PIPE_WAIT", "POWER_FULL", "TEX_SAME", "TEX_SAME_BIG", "TEX_FLAT3D", "TEX_KEEP_TAKEN", "TEX_STALE_KEPT", "PRESENT_HELD", "HELD_SYNC",
|
||||
"PIPE_SEED_BOOT", "ONE_PER_CORE", "OPERATION_MODE", "OUTPUT_RES", "PAD_LAYOUT", "PACE_HZ", "PASS_CRUMBS", "PATCH_OUTPUT", "PATCH_RES_FIXED", "PATCH_SCENE", "PATCH_WIDE_ASPECT", "PATCH_WIDE_HUD", "PATCH_WIDE_MOVIE", "PATCH_WIDE_OUT", "PATCH_FREECAM", "PATCH_OCCLUSION_OFF", "PATCH_WIDE_SAVE", "PATCH_WIDE_SCENE", "PCSS", "PCSS_K", "PERF_OVERLAY", "PERF_SLOW_MS", "PIPELINE_CACHE", "PIPELINE_CACHE_MAX_MB", "PIPELINE_CACHE_PLAY", "PIPELINE_CACHE_PLAY_MB", "PIPE_BINARIES", "PIPE_BINARIES_MAX_MB", "PIPE_FEEDBACK", "PIPE_MEMO", "PIPE_PREBUILD", "PIPE_SEED", "PIPE_SKIP", "PIPE_SYNC", "PIPE_WAIT", "POWER_FULL", "TEX_SAME", "TEX_SAME_BIG", "TEX_FLAT3D", "TEX_KEEP_TAKEN", "TEX_HARD_ORDER", "TEX_STALE_KEPT", "PRESENT_HELD", "HELD_SYNC",
|
||||
"PREPARE_PIPELINES", "PREWARM_FULL", "PREWARM_FULL_S", "PREWARM_WAIT_MS", "PREWARM_XLATE", "PROBE_MOVIE", "PROBE_MOVIE_SHOT", "PROFILE", "PROFILE_CHAINS", "PROFILE_DIR", "PROFILE_FRAMES", "PROFILE_REPLAY", "PROFILE_SLOW",
|
||||
"POOL_FAST", "POOL_GROUP", "POOL_GROUP_CHECK", "POOL_PRESNAP", "POOL_PRESNAP_RUNS",
|
||||
"SUBMIT_FINISH_GPU", "SVC_WAITS", "SNAP_WIDEN", "SUBMIT_FINISH_RESULTS", "SUBMIT_FINISH_KINDS", "FOLIAGE", "MIRROR_STAGE", "FLUSH_SKIP_CLEAN", "MIRROR_HOT", "MESH_EMULATION", "MESH_EMU_MB", "MESH_EMU_TASKS", "MESH_EMU_BATCH", "GUEST_OPT", "LOG_TEX_NEW", "DUMP_KEY", "VEC_INDEX_FIX", "PASS_TIMES",
|
||||
|
||||
@@ -63,6 +63,31 @@ static inline uint64_t trl_keep(uint32_t hold_s, uint64_t cap, int room) {
|
||||
return cap > UINT64_MAX / TRL_ROOM_TIMES ? UINT64_MAX : cap * TRL_ROOM_TIMES;
|
||||
}
|
||||
|
||||
// Past the hard count of released textures (vk_texture.c sweep_hard, RELEASED_HARD) entries go whatever they cost,
|
||||
// cheapest first by class: one drawn over (its content is gone anyway); one that comes back intact from the game's
|
||||
// memory, released longer ago than its usual gap before a take-back and not looked up lately (past_gap); another one
|
||||
// that comes back intact; one whose content only the GPU made, which would come back empty. The last three only when
|
||||
// nothing sampled them lately (TRL_HC_LATELY, kept).
|
||||
enum { TRL_HC_OVER, TRL_HC_INTACT_OLD, TRL_HC_INTACT, TRL_HC_GPU, TRL_HC_LATELY, TRL_HC_N };
|
||||
|
||||
static inline int trl_hard_class(int overwritten, int sampled_lately, int intact, int past_gap) {
|
||||
if (overwritten) return TRL_HC_OVER;
|
||||
if (sampled_lately) return TRL_HC_LATELY;
|
||||
if (!intact) return TRL_HC_GPU;
|
||||
return past_gap ? TRL_HC_INTACT_OLD : TRL_HC_INTACT;
|
||||
}
|
||||
|
||||
// Up to need entries to drop: cls[i] is the class of the i-th released entry in release order, n of them; out gets
|
||||
// their indices, the cheapest class first and in release order within a class. Returns how many; 0 when every entry
|
||||
// was sampled lately (the caller then takes the one sampled longest ago).
|
||||
static inline int trl_hard_pick(const uint8_t *cls, int n, int need, int *out) {
|
||||
int k = 0;
|
||||
for (int c = 0; c < TRL_HC_LATELY && k < need; c++)
|
||||
for (int i = 0; i < n && k < need; i++)
|
||||
if (cls[i] == c) out[k++] = i;
|
||||
return k;
|
||||
}
|
||||
|
||||
// The wall clock of the presents. The cache stamps its textures with present counts (gfx_counters.frames), and the
|
||||
// presents per second change with the scene and the menus: a hold counted in presents would be shorter in wall time
|
||||
// after a fast stretch. The clock notes the present count once a second or so (tick) and answers how many seconds ago
|
||||
|
||||
@@ -750,7 +750,7 @@ static int slot_names(const Tex *e) {
|
||||
// Why an entry was destroyed (drop_tex), in the order of drop_names.
|
||||
enum { DROP_NONE, DROP_MAX, DROP_CAP, DROP_COMMIT, DROP_VRAM, DROP_IDLE_VRAM, DROP_IDLE_COMMIT, DROP_DISUSE,
|
||||
DROP_OVERWRITTEN, DROP_REPLACED, DROP_REUSED, DROP_RANGE, DROP_EXACT, DROP_SHUTDOWN, DROP_COMPACT,
|
||||
DROP_AGE, DROP_N };
|
||||
DROP_AGE, DROP_HARD, DROP_N };
|
||||
static void drop_tex(Tex *e, int why);
|
||||
static void reuse_dropped(const Tex *e, int why); // "reuse of released textures", after vk_texture_census
|
||||
static void reuse_made(const Tex *e);
|
||||
@@ -1951,7 +1951,7 @@ static const char *const drop_names[DROP_N] = {
|
||||
"under VRAM pressure", "idle, under VRAM pressure", "idle, under commit pressure", "unsampled (disuse)",
|
||||
"overwritten at another address", "replaced by another descriptor at its address", "its image went to another texture",
|
||||
"its storage went", "made whole again (texture detail)", "shutdown", "released, in a block a compaction empties",
|
||||
"released and unused for longer than the hold"};
|
||||
"released and unused for longer than the hold", "past RELEASED_HARD released entries"};
|
||||
static const char *const start_names[START_N] = {
|
||||
"started on its own", "filled from storage over a live entry", "content copied from a live entry",
|
||||
"content copied from a released entry", "started empty over a live entry", "started empty over a released entry",
|
||||
@@ -2322,6 +2322,7 @@ __attribute__((noinline)) static void destroy_tex(Tex *e) {
|
||||
static int comes_back_intact(const Tex *e);
|
||||
|
||||
static uint64_t stale_after(const Tex *e);
|
||||
static void sweep_hard(void);
|
||||
|
||||
// The limit of released entries past which the sweep drops those that lose nothing: one per 2 MiB of the video memory
|
||||
// budget, from RELEASED_MAX to RELEASED_MAX_TOP. In Castle Town market on a 16 GB card (RTX 5060 Ti) with 2048 the
|
||||
@@ -2365,20 +2366,81 @@ static void sweep_released(void) {
|
||||
}
|
||||
sweep_at = e;
|
||||
}
|
||||
if (nreleased > RELEASED_HARD) sweep_hard();
|
||||
}
|
||||
|
||||
// Past RELEASED_HARD the count is a hard bound: entries go whatever they cost. The rule was the first entry in release
|
||||
// order that was overwritten or that nothing sampled for SWEEP_UNSAMPLED presents, of whatever kind, render targets
|
||||
// included, whose drop the comment above ties to a black frame; and each drop walked the list from its head again.
|
||||
// Most entries there cost little to make again: at the end of a session of 2026-11-13, when the count sat at 16,384
|
||||
// for a minute, 12,811 of 15,734 released entries owned no memory. So the entries now go cheapest first, by the
|
||||
// classes of vk_tex_release.h trl_hard_class: overwritten ones; ones that come back intact, released longer ago than
|
||||
// their stale_after and not looked up lately; other ones that come back intact; ones whose content only the GPU made;
|
||||
// the last three only when nothing sampled them for SWEEP_UNSAMPLED presents; while every entry was sampled lately, the
|
||||
// one sampled longest ago, as before. Within a class in release order. One walk of the list takes a batch down to
|
||||
// RELEASED_HARD - HARD_SLACK entries, so the next HARD_SLACK releases need no walk. NX2NAVI_TEX_HARD_ORDER=0: the
|
||||
// rule before.
|
||||
#define HARD_SLACK 256
|
||||
#define HARD_BATCH 1024
|
||||
static uint64_t n_hard_by[TRL_HC_N]; // entries dropped past RELEASED_HARD since the last perf line, by class
|
||||
|
||||
static int hard_order(void) {
|
||||
static int on = -1;
|
||||
if (on < 0) on = rt_knob("TEX_HARD_ORDER", "1") == 1;
|
||||
return on;
|
||||
}
|
||||
|
||||
static int e_hard_class(const Tex *e, uint64_t now, uint64_t stale) {
|
||||
int intact = comes_back_intact(e);
|
||||
return trl_hard_class(e->overwritten, now - e->samp_frame < SWEEP_UNSAMPLED, intact,
|
||||
intact && now - e->rel_frame >= stale_after(e) && !used_lately(e, now, stale));
|
||||
}
|
||||
|
||||
static void hard_lru_drop(Tex *lru) {
|
||||
static volatile int32_t n_all_used;
|
||||
log_limited(&n_all_used, "all %d released textures were sampled or drawn into lately: the one longest ago goes "
|
||||
"(%#llx fmt %#x %ux%u, last in frame %llu)", nreleased, (unsigned long long)lru->key.addr,
|
||||
lru->t.d.format, lru->t.d.width, lru->t.d.height, (unsigned long long)lru->samp_frame);
|
||||
n_hard_by[TRL_HC_LATELY]++;
|
||||
drop_tex(lru, DROP_HARD);
|
||||
}
|
||||
|
||||
static void sweep_hard(void) { // tex_lock held
|
||||
static Tex **walk;
|
||||
static uint8_t *cls;
|
||||
static int *out, cap;
|
||||
const uint64_t stale = PRESENTS(120); // STALE_FRAMES
|
||||
while (nreleased > RELEASED_HARD && rel_head) {
|
||||
uint64_t now = frames_now();
|
||||
Tex *gone = NULL, *lru = rel_head;
|
||||
for (Tex *e = rel_head; e && !gone; e = e->rel_next) { // in release order
|
||||
if (e->overwritten || now - e->samp_frame >= SWEEP_UNSAMPLED) gone = e;
|
||||
else if (e->samp_frame < lru->samp_frame) lru = e;
|
||||
Tex *lru = rel_head;
|
||||
if (nreleased > cap && hard_order()) { // room for the whole list; without it the rule before
|
||||
int want = nreleased + HARD_BATCH;
|
||||
Tex **w = realloc(walk, (size_t)want * sizeof *w);
|
||||
if (w) walk = w;
|
||||
uint8_t *c = w ? realloc(cls, (size_t)want) : NULL;
|
||||
if (c) cls = c;
|
||||
int *o = c ? realloc(out, (size_t)want * sizeof *o) : NULL;
|
||||
if (o) out = o, cap = want;
|
||||
}
|
||||
if (!gone) {
|
||||
static volatile int32_t n_all_used;
|
||||
log_limited(&n_all_used, "all %d released textures were sampled or drawn into lately: the one longest ago goes "
|
||||
"(%#llx fmt %#x %ux%u, last in frame %llu)", nreleased, (unsigned long long)lru->key.addr,
|
||||
lru->t.d.format, lru->t.d.width, lru->t.d.height, (unsigned long long)lru->samp_frame);
|
||||
if (!hard_order() || nreleased > cap) {
|
||||
Tex *gone = NULL;
|
||||
for (Tex *e = rel_head; e && !gone; e = e->rel_next) { // in release order
|
||||
if (e->overwritten || now - e->samp_frame >= SWEEP_UNSAMPLED) gone = e;
|
||||
else if (e->samp_frame < lru->samp_frame) lru = e;
|
||||
}
|
||||
if (!gone) hard_lru_drop(lru);
|
||||
else n_hard_by[e_hard_class(gone, now, stale)]++, drop_tex(gone, DROP_HARD);
|
||||
continue;
|
||||
}
|
||||
drop_tex(gone ? gone : lru, DROP_MAX);
|
||||
int n = 0;
|
||||
for (Tex *e = rel_head; e && n < cap; e = e->rel_next) { // in release order
|
||||
walk[n] = e, cls[n] = (uint8_t)e_hard_class(e, now, stale), n++;
|
||||
if (e->samp_frame < lru->samp_frame) lru = e;
|
||||
}
|
||||
int need = nreleased - (RELEASED_HARD - HARD_SLACK);
|
||||
int k = trl_hard_pick(cls, n, need < HARD_BATCH ? need : HARD_BATCH, out);
|
||||
for (int i = 0; i < k; i++) n_hard_by[cls[out[i]]]++, drop_tex(walk[out[i]], DROP_HARD);
|
||||
if (!k) hard_lru_drop(lru);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5134,14 +5196,19 @@ void vk_texture_new_submit(void) {
|
||||
n_commit_back = n_commit_held = 0;
|
||||
n_disuse = n_disuse_bytes = n_disuse_back = n_retire_freed = n_retire_cancel = 0;
|
||||
n_reused = n_reuse_new = n_bind_skips = n_restarts = n_overwritten = n_over_drops = 0;
|
||||
rt_log("perf: frame %llu: texture origins: released entries dropped past RELEASED_MAX %llu, replaced by another "
|
||||
rt_log("perf: frame %llu: texture origins: released entries dropped past RELEASED_MAX %llu, past RELEASED_HARD %llu "
|
||||
"(overwritten %llu, intact past their gap %llu, intact %llu, made by the GPU %llu, all sampled lately %llu), "
|
||||
"replaced by another "
|
||||
"descriptor at their address %llu; entries the CPU does not write made without content at an address it "
|
||||
"can see: on their own %llu, over a released entry %llu, over a live one %llu; marked slots that met an "
|
||||
"entry without content %llu, that show no entry %llu; GPU-drawn entries written back before their drop %llu",
|
||||
(unsigned long long)f * 60, (unsigned long long)n_drop_by[DROP_MAX], (unsigned long long)n_drop_by[DROP_REPLACED],
|
||||
(unsigned long long)f * 60, (unsigned long long)n_drop_by[DROP_MAX], (unsigned long long)n_drop_by[DROP_HARD],
|
||||
(unsigned long long)n_hard_by[TRL_HC_OVER], (unsigned long long)n_hard_by[TRL_HC_INTACT_OLD],
|
||||
(unsigned long long)n_hard_by[TRL_HC_INTACT], (unsigned long long)n_hard_by[TRL_HC_GPU],
|
||||
(unsigned long long)n_hard_by[TRL_HC_LATELY], (unsigned long long)n_drop_by[DROP_REPLACED],
|
||||
(unsigned long long)n_cpu_empty[0], (unsigned long long)n_cpu_empty[1], (unsigned long long)n_cpu_empty[2],
|
||||
(unsigned long long)n_mark_empty, (unsigned long long)n_mark_none, (unsigned long long)n_drop_wb);
|
||||
memset(n_drop_by, 0, sizeof n_drop_by), memset(n_cpu_empty, 0, sizeof n_cpu_empty);
|
||||
memset(n_drop_by, 0, sizeof n_drop_by), memset(n_cpu_empty, 0, sizeof n_cpu_empty), memset(n_hard_by, 0, sizeof n_hard_by);
|
||||
n_mark_empty = n_mark_none = n_drop_wb = 0;
|
||||
rt_log("perf: frame %llu: texture finalizes: slots marked %llu; slots of another live texture with the same "
|
||||
"parameters at the storage, left current %llu (terrain heightmaps %llu); lookups the stale rule gave the "
|
||||
@@ -6554,8 +6621,8 @@ static void reuse_taken_back(const Tex *e) { // tex_lock held; e is being take
|
||||
|
||||
// Drops of an entry the game may still ask for: past a bound, or under a pressure, or unsampled.
|
||||
static int drop_costs(int why) {
|
||||
return why == DROP_MAX || why == DROP_CAP || why == DROP_COMMIT || why == DROP_VRAM || why == DROP_IDLE_VRAM ||
|
||||
why == DROP_IDLE_COMMIT || why == DROP_DISUSE || why == DROP_AGE;
|
||||
return why == DROP_MAX || why == DROP_HARD || why == DROP_CAP || why == DROP_COMMIT || why == DROP_VRAM ||
|
||||
why == DROP_IDLE_VRAM || why == DROP_IDLE_COMMIT || why == DROP_DISUSE || why == DROP_AGE;
|
||||
}
|
||||
|
||||
static void reuse_dropped(const Tex *e, int why) { // tex_lock held
|
||||
@@ -6588,11 +6655,12 @@ void vk_texture_reuse_log(uint64_t frame) {
|
||||
for (int a = 0; a < AGE_N; a++)
|
||||
o += snprintf(line + o, sizeof line - (size_t)o, "%s %s %llu", a ? "," : "", age_names[a], (unsigned long long)ra_n[a]);
|
||||
snprintf(line + o, sizeof line - (size_t)o, "; by the drop: released past the hold %llu, past the size bound "
|
||||
"%llu, past the count %llu, VRAM pressure %llu released and %llu idle, commit %llu released and %llu "
|
||||
"idle, unsampled %llu); dropped past the hold %llu (%.0f MiB); now %d released, %.0f MiB of a bound of "
|
||||
"%.0f MiB, hold %u s",
|
||||
"%llu, past the count %llu (of them past the hard count %llu), VRAM pressure %llu released and %llu idle, "
|
||||
"commit %llu released and %llu idle, unsampled %llu); dropped past the hold %llu (%.0f MiB); now %d "
|
||||
"released, %.0f MiB of a bound of %.0f MiB, hold %u s",
|
||||
(unsigned long long)ra_why[DROP_AGE], (unsigned long long)ra_why[DROP_CAP],
|
||||
(unsigned long long)ra_why[DROP_MAX], (unsigned long long)ra_why[DROP_VRAM],
|
||||
(unsigned long long)(ra_why[DROP_MAX] + ra_why[DROP_HARD]), (unsigned long long)ra_why[DROP_HARD],
|
||||
(unsigned long long)ra_why[DROP_VRAM],
|
||||
(unsigned long long)ra_why[DROP_IDLE_VRAM], (unsigned long long)ra_why[DROP_COMMIT],
|
||||
(unsigned long long)ra_why[DROP_IDLE_COMMIT], (unsigned long long)ra_why[DROP_DISUSE],
|
||||
(unsigned long long)n_age_drops, n_age_bytes / M, nreleased, rel_bytes / M,
|
||||
|
||||
@@ -256,11 +256,46 @@ static void session_checks(void) {
|
||||
(unsigned long long)r150.remade, (unsigned long long)r200.remade, TRL_HOLD_S);
|
||||
}
|
||||
|
||||
// ---- past the hard count (trl_hard_class, trl_hard_pick)
|
||||
static void hard_checks(void) {
|
||||
CHECK(trl_hard_class(1, 1, 0, 0) == TRL_HC_OVER, "an overwritten entry goes first, sampled or not");
|
||||
CHECK(trl_hard_class(0, 1, 1, 1) == TRL_HC_LATELY, "a sampled entry is kept");
|
||||
CHECK(trl_hard_class(0, 0, 0, 0) == TRL_HC_GPU, "GPU-made content");
|
||||
CHECK(trl_hard_class(0, 0, 1, 1) == TRL_HC_INTACT_OLD && trl_hard_class(0, 0, 1, 0) == TRL_HC_INTACT, "intact");
|
||||
// the session's end: release order mixes render targets (GPU), intact entries and a few overwritten ones; the
|
||||
// render targets are the oldest. The rule before took them first.
|
||||
enum { N = 64 };
|
||||
uint8_t cls[N];
|
||||
int out[N];
|
||||
for (int i = 0; i < N; i++)
|
||||
cls[i] = i < 16 ? TRL_HC_GPU : i % 9 == 0 ? TRL_HC_OVER : i % 3 == 0 ? TRL_HC_LATELY : i < 40 ? TRL_HC_INTACT_OLD
|
||||
: TRL_HC_INTACT;
|
||||
int k = trl_hard_pick(cls, N, 20, out);
|
||||
CHECK(k == 20, "picked %d of 20", k);
|
||||
int last = -1, gpu = 0, prev_i = -1;
|
||||
for (int j = 0; j < k; j++) {
|
||||
int c = cls[out[j]];
|
||||
CHECK(c != TRL_HC_LATELY, "a lately sampled entry was picked (%d)", out[j]);
|
||||
CHECK(c >= last, "class order: %d after %d", c, last);
|
||||
if (c == last) CHECK(out[j] > prev_i, "release order within a class");
|
||||
gpu += c == TRL_HC_GPU;
|
||||
last = c, prev_i = out[j];
|
||||
}
|
||||
CHECK(gpu == 0, "%d render targets went while intact entries were left", gpu);
|
||||
// only render targets and sampled ones: the render targets go, in release order; none unsampled: nothing
|
||||
for (int i = 0; i < N; i++) cls[i] = i % 2 ? TRL_HC_GPU : TRL_HC_LATELY;
|
||||
k = trl_hard_pick(cls, N, 5, out);
|
||||
CHECK(k == 5 && out[0] == 1 && out[4] == 9, "render targets in release order: %d, %d..%d", k, out[0], out[4]);
|
||||
for (int i = 0; i < N; i++) cls[i] = TRL_HC_LATELY;
|
||||
CHECK(trl_hard_pick(cls, N, 5, out) == 0, "all sampled lately: none picked");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
clock_checks();
|
||||
rule_checks();
|
||||
taken_back_checks();
|
||||
session_checks();
|
||||
hard_checks();
|
||||
if (fails) printf("%d checks failed\n", fails);
|
||||
else printf("tex_release_check: all checks passed\n");
|
||||
return fails ? 1 : 0;
|
||||
|
||||
Reference in New Issue
Block a user