diff --git a/runtime/gfx_shader.c b/runtime/gfx_shader.c index 1653b3b7..77be1d9f 100644 --- a/runtime/gfx_shader.c +++ b/runtime/gfx_shader.c @@ -1527,6 +1527,11 @@ static size_t build_request(char *o0, const char *hex, const char *stage, const memcpy(&magic0, control, 4); if (!shared && magic0 == 0x98761234u && !strcmp(stage, "cs")) memcpy(&shared, control + 0x64, 4); o += sprintf(o, "},\"shared\":%u", shared); + // Their local memory, bytes a thread, follows at +0x68. Without it the translator declares its 0x1000-byte + // default: one compute program of the game needs 4,752 bytes a thread and indexed past the end of its array. + uint32_t local = 0; + if (magic0 == 0x98761234u && !strcmp(stage, "cs")) memcpy(&local, control + 0x68, 4); + if (local && local <= (1u << 20)) o += sprintf(o, ",\"local_mem\":%u", local); } if (!strcmp(stage, "cs")) { uint32_t l[3] = {1, 1, 1}; diff --git a/tools/xlate/AmpereLowering.cs b/tools/xlate/AmpereLowering.cs index 9d0bc383..864843d2 100644 --- a/tools/xlate/AmpereLowering.cs +++ b/tools/xlate/AmpereLowering.cs @@ -2536,7 +2536,10 @@ static class AmpereLowering // localBytes: the local memory size the lowered program needs when that changed (spill mode), else 0. Graphics // programs carry it in their header already; compute and task/mesh programs need it from the translator's host // (job "local_mem"). - public static byte[] Lower(byte[] code, int size, bool graphics, out int localBytes, int[] table = null, MeshInfo mesh = null) + // computeLocal: a compute program's own local memory in bytes, where its spill area starts (0: the translator's + // compute default of 0x1000). + public static byte[] Lower(byte[] code, int size, bool graphics, out int localBytes, int[] table = null, MeshInfo mesh = null, + int computeLocal = 0) { localBytes = 0; int header = graphics || mesh != null ? 0x80 : 0; @@ -2555,9 +2558,9 @@ static class AmpereLowering try { lowered = new Lowerer(insns, sph, !graphics, table, mesh) { C1 = C1Bytes }.Run(out branches, out first); } catch (RegisterPressureException) { - // Local memory after the program's own: the header's sizes (translator: low + high + CRS / 32), or the - // translator's compute default (0x1000) for compute programs. - spillAt = 0x1000; + // Local memory after the program's own: the header's sizes (translator: low + high + CRS / 32), or for a + // compute program its own size (computeLocal), the translator's compute default (0x1000) when not given. + spillAt = computeLocal > 0 ? computeLocal : 0x1000; if (header != 0) spillAt = ((int)(BitConverter.ToUInt32(code, 4) & 0xffffff) + (int)(BitConverter.ToUInt32(code, 8) & 0xffffff) + (int)(BitConverter.ToUInt32(code, 12) & 0xffffff) / 32 + 15) & ~15; diff --git a/tools/xlate/Program.cs b/tools/xlate/Program.cs index 840078e6..8360dc9e 100644 --- a/tools/xlate/Program.cs +++ b/tools/xlate/Program.cs @@ -199,8 +199,13 @@ static class SwshXlate } else { - code = AmpereLowering.Lower(code, size, (string)job["stage"] != "cs", out int local, table); + // A compute program's own local memory: "local_mem" from its control section, at least the + // translator's default of 0x1000 bytes; the lowering's spill area goes after it. + bool cs = (string)job["stage"] == "cs"; + int own = cs ? (Math.Max(job["local_mem"] is JsonNode lm ? (int)lm : 0, 0x1000) + 15) & ~15 : 0; + code = AmpereLowering.Lower(code, size, !cs, out int local, table, null, own); if (local != 0) job["local_mem"] = local; + else if (cs) job["local_mem"] = own; } if (job["lowered_out"] is JsonNode lo) File.WriteAllBytes((string)lo, code); }