mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Serve a refused carried translation once the current translator has failed the shader.
The review of the carry refusal (a2de7eb7) found that it also took the old translation away from programs the current
translator cannot translate. Before it, carry_find served the carried translation before any translation ran; after
it, a carried translation with the earlier texture pool length was refused, the translation ran and failed, and get_one
marked the key failed for the session (xlate_worker marked its wait failed), so the shader's draws were skipped, or a
compute dispatch left out, for the whole session, where 0.10.1 drew them with only the textures numbered from 32768 up
wrong. That contradicted docs/architecture.md ("A program the new translator refuses keeps its old translation") and
the carry pass's "its previous translation stays in use".
gfx_shader.c carry_last now runs where a translation failed (get_one, xlate_worker, and get_async_one's synchronous
fallback): carry_find with the new short_ok argument set passes carry_usable by, so the refused translation enters the
memory cache, where the lookups' memory step serves it from then on; the key is not marked failed and the wait is
cleared as for a success. The first four are logged with their key. Every other caller passes short_ok 0.
pool_short_note still counts such a shader for vk_texture_register's warning but no longer logs it as a translator in
use older than the runtime, and the refusal's log lines say that a shader the current translator fails keeps its old
translation. With this, the carry pass's "its previous translation stays in use" is true again for these programs, and
keeping their previous entries (the pass does not prune a failed input's) is what this needs.
Not reproduced before the change. tests/carry_pool_check.py now also cuts carry_last out of gfx_shader.c and checks that
it serves the refused translation into the memory cache, which the memory lookup then finds, serves a usable one
unlogged, returns NULL with none and still refuses another stage, and that pool_short_note counts a carried shader
without logging it; it passes, every runtime file compiles, and gfx_shader.c has the same 7 -Wall -Wextra warnings as
on stability-pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -654,9 +654,10 @@ the header alone, checks the parse of both versions and has `make_game.py` read
|
||||
files, no GPU.
|
||||
|
||||
A change to which translations of a previous translator are served (`runtime/gfx_shader.c` `carry_usable`,
|
||||
`carry_load`, `carry_find_iid`) passes `python tests\carry_pool_check.py`, which cuts the carry lookups out of
|
||||
`gfx_shader.c` and runs them against stand-in caches: a translation whose texture pool array has the earlier length
|
||||
(32768) is freed and the lookup goes on, one with the current length or no pool is served. No game files, no GPU.
|
||||
`carry_load`, `carry_find_iid`, `carry_last`) passes `python tests\carry_pool_check.py`, which cuts the carry lookups
|
||||
out of `gfx_shader.c` and runs them against stand-in caches: a translation whose texture pool array has the earlier
|
||||
length (32768) is freed and the lookup goes on, one with the current length or no pool is served, and once the current
|
||||
translator has failed the shader the refused translation serves from the memory cache. No game files, no GPU.
|
||||
|
||||
A change to how the translator children are read and restarted (`runtime/platform/win32.c` `plat_child_read`,
|
||||
`runtime/gfx_shader.c` `child_exchange_raw`, `svc_request`) passes `python tests\child_timeout_check.py`, which builds
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
// translator thus keeps that translation for the rest of the session, deliberately: no pipeline is built a second
|
||||
// time for it, and the pass below needs no way to swap shaders under the renderer. A shader first met after the pass
|
||||
// translated it gets the new translation. A translation that indexes the texture pool with the earlier translator's
|
||||
// length is never served (carry_usable): its clamp would draw the textures numbered past that length with another.
|
||||
// length is not served (carry_usable): its clamp would draw the textures numbered past that length with another. It
|
||||
// serves only once the current translator has failed the shader (carry_last), as the last resort.
|
||||
// The background pass (gfx_shader_carry_start, after the boot prewarm) translates every stored input that has a
|
||||
// previous translator's entry again under the current identity, on a translator child of its own that runs at idle
|
||||
// priority, outside the pool, and waits while on-demand translations are queued or running. It writes cache files only;
|
||||
@@ -1993,13 +1994,16 @@ static int pool_short(const GfxShader *s) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Shaders that entered the memory cache with a texture pool array of the earlier length (mem_insert). carry_usable
|
||||
// keeps the carried ones out, so these come from a translator in use that is older than the runtime's pool (a dev
|
||||
// build pointed at an old one, or an install whose translator was not rebuilt). Counted, the first logged;
|
||||
// vk_texture.c vk_texture_register names the count when the game registers a texture they cannot reach.
|
||||
static volatile int32_t n_pool_short;
|
||||
// Shaders that entered the memory cache with a texture pool array of the earlier length (mem_insert), all counted;
|
||||
// vk_texture.c vk_texture_register names the count when the game registers a texture they cannot reach. carry_usable
|
||||
// keeps the carried ones out except as the last resort (carry_last, which logs those), so the first one that is not
|
||||
// carried is logged: it comes from a translator in use that is older than the runtime's pool (a dev build pointed at
|
||||
// an old one, or an install whose translator was not rebuilt).
|
||||
static volatile int32_t n_pool_short, pool_short_said;
|
||||
static void pool_short_note(const GfxShader *s) {
|
||||
if (!pool_short(s) || plat_inc32(&n_pool_short) != 1) return;
|
||||
if (!pool_short(s)) return;
|
||||
plat_inc32(&n_pool_short);
|
||||
if (s->carried || plat_inc32(&pool_short_said) != 1) return;
|
||||
rt_log("gfx_shader: a shader of the translator in use indexes the texture pool with %d entries: it reads slot %d "
|
||||
"for every texture numbered from %d up; the translator is older than this runtime (tools/build_xlate.py "
|
||||
"builds the current one)", POOL_TEX_SHORT, POOL_TEX_SHORT - 1, POOL_TEX_SHORT);
|
||||
@@ -2010,24 +2014,26 @@ uint32_t gfx_shader_pool_short_seen(void) { return (uint32_t)__atomic_load_n(&n_
|
||||
// Whether previous identity j's translation s may serve. One that indexes the texture pool with the earlier length is
|
||||
// not used: the lookup goes on to the next previous identity and then to a translation under the current one (the boot
|
||||
// prewarm translates the shaders it names on the boot screen, others are translated at first use). Served, it would
|
||||
// keep the clamp for the whole session (see "Carry" at the top). Counted for gfx_shader_record_flush; the first logged.
|
||||
// keep the clamp for the whole session (see "Carry" at the top). Only when the current translator fails the shader
|
||||
// does it serve after all (carry_last). Counted for gfx_shader_record_flush; the first logged.
|
||||
static int carry_usable(const GfxShader *s, int j) {
|
||||
if (!pool_short(s)) return 1;
|
||||
if (plat_inc64(&C.refused) == 1) {
|
||||
const uint8_t *id = C.prev[j];
|
||||
rt_log("gfx_shader: carry: translations of a previous translator (%02x%02x%02x%02x) that index the texture pool "
|
||||
"with %d entries are not used (textures numbered from %d up would read slot %d); those shaders are "
|
||||
"translated again", id[0], id[1], id[2], id[3], POOL_TEX_SHORT, POOL_TEX_SHORT, POOL_TEX_SHORT - 1);
|
||||
"translated again, and one the current translator fails keeps it", id[0], id[1], id[2], id[3],
|
||||
POOL_TEX_SHORT, POOL_TEX_SHORT, POOL_TEX_SHORT - 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Previous identity j's entry `key` from the disk cache into the memory cache; NULL when the cache has no valid one.
|
||||
// stage (GFX_STAGE_*, -1 any): an entry of another stage than the request's is not used, nor one carry_usable refuses.
|
||||
// Every entry passes disk_load's checks (whole SPIR-V, a .res.json naming its stage, both from the pack or both loose),
|
||||
// which matters most for compute shaders: the replay thread dispatches one the moment it has it (vk_exec_draw.c
|
||||
// shader_for).
|
||||
static GfxShader *carry_load(const uint8_t key[32], const uint8_t iid[32], int stage, int j) {
|
||||
// stage (GFX_STAGE_*, -1 any): an entry of another stage than the request's is not used, nor one carry_usable refuses
|
||||
// unless short_ok (carry_last, after the current translator failed the shader). Every entry passes disk_load's checks
|
||||
// (whole SPIR-V, a .res.json naming its stage, both from the pack or both loose), which matters most for compute
|
||||
// shaders: the replay thread dispatches one the moment it has it (vk_exec_draw.c shader_for).
|
||||
static GfxShader *carry_load(const uint8_t key[32], const uint8_t iid[32], int stage, int j, int short_ok) {
|
||||
GfxShader *s = disk_load(key, iid);
|
||||
if (!s) return NULL;
|
||||
if (stage >= 0 && s->stage != stage) {
|
||||
@@ -2037,7 +2043,7 @@ static GfxShader *carry_load(const uint8_t key[32], const uint8_t iid[32], int s
|
||||
shader_free(s);
|
||||
return NULL;
|
||||
}
|
||||
if (!carry_usable(s, j)) {
|
||||
if (!short_ok && !carry_usable(s, j)) {
|
||||
shader_free(s);
|
||||
return NULL;
|
||||
}
|
||||
@@ -2047,7 +2053,8 @@ static GfxShader *carry_load(const uint8_t key[32], const uint8_t iid[32], int s
|
||||
|
||||
// A previous translator's translation of (code hash, answers) for a stage, newest identity first: from the memory cache
|
||||
// only (disk 0), or from the memory cache and then the disk cache (disk 1). NULL when none has one or carry is off.
|
||||
static GfxShader *carry_find(const uint8_t h[32], const GfxShaderAnswers *a, int stage, int disk) {
|
||||
// short_ok: as carry_load's.
|
||||
static GfxShader *carry_find(const uint8_t h[32], const GfxShaderAnswers *a, int stage, int disk, int short_ok) {
|
||||
if (!carry_on()) return NULL;
|
||||
for (int j = 0; j < C.n_prev; j++) {
|
||||
UseMask m;
|
||||
@@ -2056,7 +2063,7 @@ static GfxShader *carry_find(const uint8_t h[32], const GfxShaderAnswers *a, int
|
||||
iid_of(h, a, &m, iid);
|
||||
key_of_input_id(C.prev_eff[j], iid, key);
|
||||
GfxShader *s = mem_find(key);
|
||||
if (s || (disk && (s = carry_load(key, iid, stage, j)))) return s;
|
||||
if (s || (disk && (s = carry_load(key, iid, stage, j, short_ok)))) return s;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@@ -2073,7 +2080,7 @@ static GfxShader *carry_find_iid(const uint8_t iid[32], int disk, int *owned) {
|
||||
if (s) return s;
|
||||
continue;
|
||||
}
|
||||
if (!owned) s = carry_load(key, iid, -1, j);
|
||||
if (!owned) s = carry_load(key, iid, -1, j, 0);
|
||||
else if ((s = disk_load(key, iid))) {
|
||||
if (carry_usable(s, j)) s->carried = (uint8_t)(j + 1), *owned = 1;
|
||||
else shader_free(s), s = NULL;
|
||||
@@ -2083,6 +2090,22 @@ static GfxShader *carry_find_iid(const uint8_t iid[32], int disk, int *owned) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// The current translator failed the shader of cache key `key` (code hash h): a previous translator's translation that
|
||||
// carry_usable refused serves after all, the last resort. Drawn with the clamp, only the textures numbered from 32768 up
|
||||
// are wrong, where without it every draw of the shader would be skipped, or a compute dispatch left out, for the whole
|
||||
// session. It enters the memory cache, so the lookups' memory step serves it from then on. NULL when none has one.
|
||||
static GfxShader *carry_last(const uint8_t key[32], const uint8_t h[32], const GfxShaderAnswers *a, int stage) {
|
||||
static volatile int32_t n_logged;
|
||||
GfxShader *s = carry_find(h, a, stage, 1, 1);
|
||||
if (s && pool_short(s) && plat_inc32(&n_logged) <= 4) {
|
||||
char hex[65];
|
||||
key_hex(key, hex);
|
||||
rt_log("gfx_shader: carry: the current translator failed %s; its previous translation (texture pool of %d "
|
||||
"entries) serves", hex, POOL_TEX_SHORT);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Whether the cache holds a previous translator's entry of input iid.
|
||||
static int carry_has_iid(const uint8_t iid[32]) {
|
||||
if (!carry_on()) return 0;
|
||||
@@ -2378,18 +2401,19 @@ static int failed_slot(const uint8_t key[32], int insert) {
|
||||
// The lookup order with carry (see "Carry" at the top): the current key in memory, a previous translator's entry in
|
||||
// memory, the current key on disk, a previous translator's entry on disk. A key whose translation failed skips the
|
||||
// last: that lookup found nothing usable before the translation, and a refused entry (carry_usable) would otherwise be
|
||||
// read from the disk again at every call (a compute shader's at every dispatch, vk_exec_draw.c shader_for).
|
||||
// read from the disk again at every call (a compute shader's at every dispatch, vk_exec_draw.c shader_for). When the
|
||||
// translation fails, a refused entry serves after all (carry_last); it is in memory then, so the key is not marked.
|
||||
static const GfxShader *get_one(const Req *r, int stage, const uint8_t *control, const uint8_t *code,
|
||||
const GfxShaderAnswers *a) {
|
||||
GfxShader *s = mem_find(r->key);
|
||||
if (s || (s = carry_find(r->code_hash, a, stage, 0))) return plat_inc64(&n_hits), s;
|
||||
if (s || (s = carry_find(r->code_hash, a, stage, 0, 0))) return plat_inc64(&n_hits), s;
|
||||
paths();
|
||||
if (!cache_dir[0]) return fail("no cache directory", NULL);
|
||||
if (r->final && (s = disk_load(r->key, r->iid))) return plat_inc64(&n_hits), mem_insert(s);
|
||||
if (failed_slot(r->key, 0)) return NULL;
|
||||
if ((s = carry_find(r->code_hash, a, stage, 1))) return plat_inc64(&n_hits), s;
|
||||
if ((s = carry_find(r->code_hash, a, stage, 1, 0))) return plat_inc64(&n_hits), s;
|
||||
const GfxShader *t = translate(r, stage, control, code, a);
|
||||
if (!t) failed_slot(r->key, 1);
|
||||
if (!t && !(t = carry_last(r->key, r->code_hash, a, stage))) failed_slot(r->key, 1);
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -2666,7 +2690,8 @@ static const GfxShader *translate(const Req *r, int stage, const uint8_t *contro
|
||||
// ---- background translation (gfx_shader_get_async)
|
||||
// svc_runtime_limit() workers, one per translator child. A job owns copies of the control section and the code, so the
|
||||
// guest may free or rewrite its program while the job waits. A key stays in X.waits while its job is queued or running,
|
||||
// and for good when the translation failed, so a failing shader is not sent again on every draw.
|
||||
// and for good when the translation failed and no previous translation serves instead (carry_last), so a failing shader
|
||||
// is not sent again on every draw.
|
||||
typedef struct XJob {
|
||||
struct XJob *next;
|
||||
int stage;
|
||||
@@ -2747,14 +2772,15 @@ static void xlate_worker(void *arg) {
|
||||
XJob *j = X.head;
|
||||
if (!(X.head = j->next)) X.tail = NULL;
|
||||
plat_mutex_unlock(&X.lock);
|
||||
const GfxShader *s = translate(&j->r, j->stage, j->control, j->code, &j->a);
|
||||
const GfxShader *t = translate(&j->r, j->stage, j->control, j->code, &j->a), *s = t;
|
||||
if (!s) s = carry_last(j->r.key, j->r.code_hash, &j->a, j->stage); // in memory: get_async_one's first lookup
|
||||
plat_mutex_lock(&X.lock);
|
||||
XWait **w = wait_slot(j->r.key), *e = *w;
|
||||
if (e && s) *w = e->next, free(e);
|
||||
else if (e) e->failed = 1;
|
||||
plat_mutex_unlock(&X.lock);
|
||||
plat_cond_broadcast(&X.done);
|
||||
if (s) plat_inc64(&n_async);
|
||||
if (t) plat_inc64(&n_async);
|
||||
free(j->control), free(j->code), free(j);
|
||||
}
|
||||
return;
|
||||
@@ -2771,7 +2797,7 @@ static void load_worker(void *arg) {
|
||||
if (!(X.lhead = j->next)) X.ltail = NULL;
|
||||
plat_mutex_unlock(&X.lock);
|
||||
GfxShader *d = j->r.final ? disk_load(j->r.key, j->r.iid) : NULL;
|
||||
const GfxShader *s = d ? mem_insert(d) : carry_find(j->r.code_hash, &j->a, j->stage, 1);
|
||||
const GfxShader *s = d ? mem_insert(d) : carry_find(j->r.code_hash, &j->a, j->stage, 1, 0);
|
||||
plat_mutex_lock(&X.lock);
|
||||
if (s) {
|
||||
XWait **w = wait_slot(j->r.key), *e = *w;
|
||||
@@ -2835,7 +2861,7 @@ const GfxShader *gfx_shader_get_async(int stage, const uint8_t *control, const u
|
||||
static const GfxShader *get_async_one(Req r, int stage, const uint8_t *control, const uint8_t *code,
|
||||
const GfxShaderAnswers *a, int *pending) {
|
||||
GfxShader *s = mem_find(r.key);
|
||||
if (s || (s = carry_find(r.code_hash, a, stage, 0))) return plat_inc64(&n_hits), s; // get_one's order
|
||||
if (s || (s = carry_find(r.code_hash, a, stage, 0, 0))) return plat_inc64(&n_hits), s; // get_one's order
|
||||
plat_mutex_lock_shared(&X.lock);
|
||||
XWait *w = *wait_slot(r.key);
|
||||
int failed = w && w->failed;
|
||||
@@ -2847,7 +2873,7 @@ static const GfxShader *get_async_one(Req r, int stage, const uint8_t *control,
|
||||
if (read) {
|
||||
uint64_t t0 = plat_now_ns();
|
||||
GfxShader *d = r.final ? disk_load(r.key, r.iid) : NULL;
|
||||
s = d ? mem_insert(d) : carry_find(r.code_hash, a, stage, 1);
|
||||
s = d ? mem_insert(d) : carry_find(r.code_hash, a, stage, 1, 0);
|
||||
load_spent(plat_now_ns() - t0);
|
||||
if (s) return plat_inc64(&n_hits), s;
|
||||
}
|
||||
@@ -2879,9 +2905,10 @@ static const GfxShader *get_async_one(Req r, int stage, const uint8_t *control,
|
||||
if (raced) return *pending = 1, NULL;
|
||||
if (!read) { // no worker or out of memory: everything here
|
||||
GfxShader *d = r.final ? disk_load(r.key, r.iid) : NULL;
|
||||
if ((s = d ? mem_insert(d) : carry_find(r.code_hash, a, stage, 1))) return plat_inc64(&n_hits), s;
|
||||
if ((s = d ? mem_insert(d) : carry_find(r.code_hash, a, stage, 1, 0))) return plat_inc64(&n_hits), s;
|
||||
}
|
||||
return translate(&r, stage, control, code, a);
|
||||
const GfxShader *t = translate(&r, stage, control, code, a);
|
||||
return t ? t : carry_last(r.key, r.code_hash, a, stage);
|
||||
}
|
||||
|
||||
const GfxShader *gfx_shader_await(int stage, const uint8_t *control, const uint8_t *code, uint64_t code_size,
|
||||
@@ -3395,8 +3422,9 @@ void gfx_shader_record_flush(int force) {
|
||||
if (force && served != __atomic_exchange_n(&C.served_logged, served, __ATOMIC_RELAXED)) // the saves and the exit
|
||||
rt_log("gfx_shader: carry: %lld shaders served from a previous translator this session", (long long)served);
|
||||
if (force && refused != __atomic_exchange_n(&C.refused_logged, refused, __ATOMIC_RELAXED))
|
||||
rt_log("gfx_shader: carry: a previous translator's translation was not used %lld times this session (texture "
|
||||
"pool of %d entries)", (long long)refused, POOL_TEX_SHORT);
|
||||
rt_log("gfx_shader: carry: a previous translator's translation was passed over %lld times this session "
|
||||
"(texture pool of %d entries; it serves only where the current translator failed)", (long long)refused,
|
||||
POOL_TEX_SHORT);
|
||||
if (!RC.on || (!force && now - last < 2000)) return;
|
||||
last = now;
|
||||
// The files are written outside RC.lock, which the render thread takes to record a new answer.
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
"""Check runtime/gfx_shader.c's carry refusal: a previous translator's translation whose texture pool array has the
|
||||
earlier length (32768, vk.h VK_POOL_TEX_CARRIED) is never served, because its clamp draws every texture numbered from
|
||||
32768 up with slot 32767. The check cuts pool_short, carry_usable, carry_load, carry_find and carry_find_iid out of
|
||||
gfx_shader.c, compiles them against stand-ins for the disk and memory caches, and walks the lookups: a refused entry
|
||||
is freed and the lookup goes on to the next previous identity, the owned branch (gfx_shader_load_private) frees it as
|
||||
well, and entries with the current length, the sampler pool or no pool are served as before. pool_short_note, which
|
||||
counts the shaders of the earlier length that enter the memory cache for vk_texture.c's warning, counts those only.
|
||||
earlier length (32768, vk.h VK_POOL_TEX_CARRIED) is not served while the current translator may translate the shader,
|
||||
because its clamp draws every texture numbered from 32768 up with slot 32767. The check cuts pool_short, carry_usable,
|
||||
carry_load, carry_find, carry_find_iid and carry_last out of gfx_shader.c, compiles them against stand-ins for the disk
|
||||
and memory caches, and walks the lookups: a refused entry is freed and the lookup goes on to the next previous identity,
|
||||
the owned branch (gfx_shader_load_private) frees it as well, and entries with the current length, the sampler pool or no
|
||||
pool are served as before. carry_last, which runs once the current translator has failed the shader, serves the refused
|
||||
translation after all and puts it in the memory cache, where the memory lookup finds it. pool_short_note, which counts
|
||||
the shaders of the earlier length that enter the memory cache for vk_texture.c's warning, counts those only, and logs
|
||||
only one that is not carried.
|
||||
|
||||
No GPU, no game files, any Python.
|
||||
|
||||
@@ -75,6 +78,9 @@ static void iid_of(const uint8_t h[32], const GfxShaderAnswers *a, const UseMask
|
||||
static int use_lookup(const uint8_t id[32], const uint8_t h[32], UseMask *m) { return 1; }
|
||||
static GfxShader *mem_find(const uint8_t key[32]) { return mem && mem->keyj == key[0] ? mem : NULL; }
|
||||
static GfxShader *mem_insert(GfxShader *s) { mem = s; return s; }
|
||||
static void key_hex(const uint8_t key[32], char out[65]) {
|
||||
for (int i = 0; i < 32; i++) snprintf(out + 2 * i, 3, "%02x", key[i]);
|
||||
}
|
||||
static void shader_free(GfxShader *s) { frees++, live--; free(s->res[GFX_RES_TEX]); free(s); }
|
||||
static GfxShader *disk_load(const uint8_t key[32], const uint8_t *iid) {
|
||||
int j = key[0];
|
||||
@@ -111,7 +117,7 @@ int main(void) {
|
||||
/* The earlier length: refused, freed, nothing served; counted once, logged once. */
|
||||
reset();
|
||||
disk_len[0] = 32768;
|
||||
assert(!carry_find(h, &a, 1, 1));
|
||||
assert(!carry_find(h, &a, 1, 1, 0));
|
||||
assert(loads == 1 && frees == 1 && live == 0 && !mem);
|
||||
assert(C.refused == 1 && logs == 1 && strstr(last_log, "texture pool"));
|
||||
assert(!carry_find_iid(iid, 1, NULL));
|
||||
@@ -120,14 +126,14 @@ int main(void) {
|
||||
/* The lookup goes on to the next previous identity, whose translation has the current length. */
|
||||
reset();
|
||||
disk_len[0] = 32768, disk_len[1] = 40960;
|
||||
GfxShader *s = carry_find(h, &a, 1, 1);
|
||||
GfxShader *s = carry_find(h, &a, 1, 1, 0);
|
||||
assert(s && s->pool_len == 40960 && s->carried == 2 && mem == s);
|
||||
assert(C.refused == 1 && frees == 1 && live == 1);
|
||||
|
||||
/* The current length, a shader with only the sampler pool and bound textures: served as before. */
|
||||
reset();
|
||||
disk_len[0] = 40960;
|
||||
s = carry_find(h, &a, 1, 1);
|
||||
s = carry_find(h, &a, 1, 1, 0);
|
||||
assert(s && s->carried == 1 && C.refused == 0 && logs == 0);
|
||||
reset();
|
||||
disk_len[0] = -1; /* no texture pool array */
|
||||
@@ -137,7 +143,7 @@ int main(void) {
|
||||
/* A stage mismatch is still refused for its own reason, before the pool is looked at. */
|
||||
reset();
|
||||
disk_len[0] = 32768, disk_stage[0] = 2;
|
||||
assert(!carry_find(h, &a, 1, 1) && C.refused == 0 && frees == 1);
|
||||
assert(!carry_find(h, &a, 1, 1, 0) && C.refused == 0 && frees == 1);
|
||||
|
||||
/* The owned branch (gfx_shader_load_private): refused and freed, or handed over with *owned 1. */
|
||||
reset();
|
||||
@@ -151,10 +157,32 @@ int main(void) {
|
||||
assert(s && owned && s->carried == 2 && !mem && C.refused == 1 && live == 1);
|
||||
shader_free(s);
|
||||
|
||||
/* The last resort (carry_last, after the current translator failed the shader): the refused translation serves
|
||||
and enters the memory cache, from which the memory lookup (disk 0) serves it from then on; logged. */
|
||||
reset();
|
||||
disk_len[0] = 32768;
|
||||
uint8_t key[32] = {0x5a};
|
||||
assert(!carry_find(h, &a, 1, 1, 0) && C.refused == 1 && !mem && live == 0);
|
||||
int logs0 = logs;
|
||||
s = carry_last(key, h, &a, 1);
|
||||
assert(s && s->pool_len == 32768 && s->carried == 1 && mem == s && live == 1 && C.refused == 1);
|
||||
assert(logs == logs0 + 1 && strstr(last_log, "current translator failed"));
|
||||
assert(carry_find(h, &a, 1, 0, 0) == s && loads == 2);
|
||||
/* A usable translation found there is served as well, unlogged; none: NULL; another stage: still refused. */
|
||||
reset();
|
||||
disk_len[0] = 40960;
|
||||
s = carry_last(key, h, &a, 1);
|
||||
assert(s && s->pool_len == 40960 && logs == 0);
|
||||
reset();
|
||||
assert(!carry_last(key, h, &a, 1) && loads == 0);
|
||||
reset();
|
||||
disk_len[0] = 32768, disk_stage[0] = 2;
|
||||
assert(!carry_last(key, h, &a, 1) && frees == 1 && live == 0 && !mem);
|
||||
|
||||
/* Memory only (disk 0) never reads the disk. */
|
||||
reset();
|
||||
disk_len[0] = 32768;
|
||||
assert(!carry_find(h, &a, 1, 0) && !carry_find_iid(iid, 0, NULL) && loads == 0);
|
||||
assert(!carry_find(h, &a, 1, 0, 0) && !carry_find_iid(iid, 0, NULL) && loads == 0);
|
||||
|
||||
/* pool_short_note (what mem_insert calls for every shader entering the memory cache): only shaders with a pool
|
||||
array of the earlier length count, the first is logged, gfx_shader_pool_short_seen gives the count. */
|
||||
@@ -168,13 +196,17 @@ int main(void) {
|
||||
GfxShader *none = disk_load(k, NULL);
|
||||
pool_short_note(cur), pool_short_note(none);
|
||||
assert(gfx_shader_pool_short_seen() == 0 && logs == 0);
|
||||
old->carried = 1; /* served by carry_last, which logs it: counted, not logged as the translator in use */
|
||||
pool_short_note(old);
|
||||
assert(gfx_shader_pool_short_seen() == 1 && logs == 0);
|
||||
old->carried = 0;
|
||||
pool_short_note(old), pool_short_note(old);
|
||||
assert(gfx_shader_pool_short_seen() == 2 && logs == 1 && strstr(last_log, "older than this runtime"));
|
||||
assert(gfx_shader_pool_short_seen() == 3 && logs == 1 && strstr(last_log, "older than this runtime"));
|
||||
shader_free(cur), shader_free(old), shader_free(none);
|
||||
|
||||
reset();
|
||||
printf("carry pool check: earlier-length pool translations refused and freed, the lookup goes on, others served; "
|
||||
"shaders of the earlier length in use counted\n");
|
||||
"a refused one serves once the current translator failed; shaders of the earlier length in use counted\n");
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
|
||||
Reference in New Issue
Block a user