Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions recomp/difftest/overrides.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,13 @@ static long test_ray(int cases) {
/* ---- height ray over a model's node tree ---- */

static uint32_t height_root, height_from, height_to, height_frame;
static int index_lists;
static int index_lists, upright, grid;

static uint32_t make_node(int depth) {
uint32_t node = alloc(0x40), move = alloc(12), turn = alloc(8);
for (int k = 0; k < 3; k++) MF32(move + 4 * k) = rnd(depth ? 4 : 0.5f);
for (int k = 0; k < 3; k++) MF32(move + 4 * k) = grid ? rand() % 3 - 1 : rnd(depth ? 4 : 0.5f);
for (int k = 0; k < 4; k++) M16(turn + 2 * k) = (uint16_t)((rand() % 65536 - 32768) / (rand() % 4 + 1));
if (rand() % 3 == 0) { M16(turn) = 0; M16(turn + 2) = 0; M16(turn + 4) = 0; M16(turn + 6) = 32767; }
if (upright || rand() % 3 == 0) { M16(turn) = 0; M16(turn + 2) = 0; M16(turn + 4) = 0; M16(turn + 6) = 32767; }
int groups = rand() % 4;
uint32_t g = alloc(2 + 7 * groups);
M16(g) = (uint16_t)groups;
Expand All @@ -121,9 +121,11 @@ static long test_height(int cases) {
bump = HEAP + 0x1000;
uint32_t model = alloc(0x40);
int verts = 3 + rand() % 40;
grid = n % 8 >= 6;
upright = n % 4 >= 2;
uint32_t vb = alloc(2 + 12 * verts);
for (int k = 0; k < verts; k++)
for (int j = 0; j < 3; j++) MF32(vb + 2 + 12 * k + 4 * j) = rnd(j == 1 ? 1 : 6);
for (int j = 0; j < 3; j++) MF32(vb + 2 + 12 * k + 4 * j) = grid ? rand() % 5 - 2 : rnd(j == 1 ? 1 : 6);
index_lists = 1 + rand() % 4;
uint32_t lists = make_list(index_lists);
for (int i = 0; i < index_lists; i++) {
Expand All @@ -143,6 +145,8 @@ static long test_height(int cases) {
MF32(height_to) = MF32(height_from) + rnd(n % 2 ? 4 : 0.4f);
MF32(height_to + 4) = MF32(height_from + 4) - 1 + (n % 2 ? rnd(4) : 0);
MF32(height_to + 8) = MF32(height_from + 8) + rnd(0.4f);
if (grid) { MF32(height_from) = rand() % 5 - 2; MF32(height_from + 8) = rand() % 5 - 2; }
if (upright) { MF32(height_to) = MF32(height_from); MF32(height_to + 8) = MF32(height_from + 8); }
M8(FDIV_CHECKED) = (n & 7) ? 1 : 0;
bad += !same(height_setup, f_00489ad0, f_00489ad0_lifted, 0x00489ad0u, 0x00489ce3u, 0);
}
Expand Down
3 changes: 2 additions & 1 deletion recomp/port/render.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ void r_set_render_target(uint32_t surface) {
}
void r_set_transform(uint32_t type, uint32_t addr) {
Mat* m = type == 256 ? &R.world : type == 2 ? &R.view : type == 3 ? &R.proj : (type >= 16 && type < 24) ? &R.tex[type - 16] : 0;
if (m) memcpy(m->m, GPTR(addr), 64);
if (!m || !memcmp(m->m, GPTR(addr), 64)) return;
memcpy(m->m, GPTR(addr), 64);
if (type == 256 || type == 2 || type == 3) wvp_dirty = 1;
}
void r_get_transform(uint32_t type, uint32_t addr) {
Expand Down
56 changes: 32 additions & 24 deletions recomp/port/render_gl.inc
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ EM_JS(int, js_gl_init, (int w, int h), {
const G = self.__gl = { canvas, gl, program, STREAM, vbo, cur: null, textures: new Map(), buffers: [new Map(), new Map()],
vao: gl.createVertexArray(), consts: gl.createTexture(), kring: 0, w, h, last: 0,
direct: canvas === self.pageCanvas };
// Draw constants: 27 blocks of 75 texels per row, and room for two frames' worth so a run never rewrites rows the
// GPU may still be reading.
// Draw constants: rows of 2025 texels, and room for two frames' worth so a run never rewrites rows the GPU may
// still be reading.
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, G.consts);
gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA32F, 27 * 75, 608);
Expand Down Expand Up @@ -285,13 +285,13 @@ EM_JS(void, js_gl_run, (const uint8_t* verts, int nverts, const int32_t* cmds, i
// names its block. A uniform buffer bound per draw cost the D3D11 backend a constant-buffer copy each time.
let kbase = 0;
if (nconsts) {
const rows = Math.ceil(nconsts / 27);
const W = 2025, rows = Math.ceil(nconsts / W);
if (G.kring + rows > 608) G.kring = 0;
kbase = G.kring * 27;
kbase = G.kring;
gl.activeTexture(gl.TEXTURE2);
const full = Math.floor(nconsts / 27), rest = nconsts - full * 27;
if (full) gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, G.kring, 27 * 75, full, gl.RGBA, gl.FLOAT, HEAPF32, consts >> 2);
if (rest) gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, G.kring + full, rest * 75, 1, gl.RGBA, gl.FLOAT, HEAPF32, (consts >> 2) + full * 27 * 300);
const full = Math.floor(nconsts / W), rest = nconsts - full * W;
if (full) gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, G.kring, W, full, gl.RGBA, gl.FLOAT, HEAPF32, consts >> 2);
if (rest) gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, G.kring + full, rest, 1, gl.RGBA, gl.FLOAT, HEAPF32, (consts >> 2) + full * W * 4);
gl.activeTexture(gl.TEXTURE0);
G.kring += rows;
}
Expand Down Expand Up @@ -374,8 +374,8 @@ EM_JS(void, js_gl_run, (const uint8_t* verts, int nverts, const int32_t* cmds, i
}
const vb = G.buffers[0].get(HEAP32[g + 1] >>> 0), ib = HEAP32[g + 2] ? G.buffers[1].get(HEAP32[g + 2] >>> 0) : null;
if (!vb || (HEAP32[g + 2] && !ib)) continue;
const block = kbase + HEAP32[g + 13];
if (G.cur.block !== block) { G.cur.block = block; gl.uniform2i(u.uB, (block % 27) * 75, Math.floor(block / 27)); }
const block = HEAP32[g + 13], row = kbase + Math.floor(block / 2025), col = block % 2025;
if (G.cur.block !== block || G.cur.row !== row) { G.cur.block = block; G.cur.row = row; gl.uniform2i(u.uB, col, row); }
if (!G.vaoOn) { gl.bindVertexArray(G.vao); G.vaoOn = true; A.fill(-1); }
let same = true;
for (let i = 0; i < 11; i++) if (A[i] !== HEAP32[g + 1 + i]) { A[i] = HEAP32[g + 1 + i]; same = false; }
Expand Down Expand Up @@ -595,18 +595,20 @@ static uint32_t gl_texture(uint32_t tex) {
typedef struct { int32_t prim, first, count, st[41], g[16]; } GlCmd;
static GlCmd gl_cmds[MAX_CMDS];
static uint32_t gl_ncmds, gl_nverts;
/* Vertex-shader constants of buffer draws, GL_CONSTS vec4s per block; a draw whose constants match the previous
block's reuses it. */
#define GL_CONSTS 75
static float gl_consts[MAX_CMDS][GL_CONSTS * 4];
static uint32_t gl_nconsts;
/* Vertex-shader constants of buffer draws, packed as vec4s into rows of GL_KROW (the constant texture's width); a
block holds 27 vec4s plus 6 per light and never straddles a row. A draw whose constants match the previous block's
reuses it. */
#define GL_KROW 2025
#define GL_KROWS 300
static float gl_consts[GL_KROWS * GL_KROW * 4];
static uint32_t gl_nconsts, gl_klast, gl_klast_size;

static void gl_flush(void) {
if (!gl_ncmds) return;
double t = prof_mark();
js_gl_run(gl_verts, (int)gl_nverts, (const int32_t*)gl_cmds, (int)gl_ncmds, gl_consts[0], (int)gl_nconsts);
js_gl_run(gl_verts, (int)gl_nverts, (const int32_t*)gl_cmds, (int)gl_ncmds, gl_consts, (int)gl_nconsts);
prof_submit += prof_mark() - t;
gl_ncmds = gl_nverts = gl_nconsts = 0;
gl_ncmds = gl_nverts = gl_nconsts = gl_klast_size = 0;
}

/* The command a draw appends to: the current one when it is a triangle list with the same state, else a new one. */
Expand Down Expand Up @@ -642,7 +644,9 @@ static void log_stage_setup(void) {
static int nseen;
uint32_t key[10] = {R.tss[0][1], R.tss[0][2] << 8 | R.tss[0][3], R.tss[0][4], R.tss[0][11], R.tss[0][24],
R.tss[1][1], R.tss[1][2] << 8 | R.tss[1][3], R.tss[1][4], R.tss[1][11], R.tss[1][24]};
for (int k = 0; k < nseen; k++) if (!memcmp(seen[k], key, sizeof key)) return;
static int last;
if (nseen && !memcmp(seen[last], key, sizeof key)) return;
for (int k = 0; k < nseen; k++) if (!memcmp(seen[k], key, sizeof key)) { last = k; return; }
if (nseen >= 48) return;
memcpy(seen[nseen++], key, sizeof key);
port_log("stage setup: s0 op %u args %04x aop %u tci %x ttff %x | s1 op %u args %04x aop %u tci %x ttff %x | fvf %x",
Expand All @@ -656,7 +660,9 @@ static void log_raster_setup(void) {
uint32_t tex = R.tex_obj[0], s = tex && M32(tex + 8) == 3 ? M32(M32(tex + 40)) : tex;
uint32_t key[6] = {R.rs[27] << 16 | R.rs[19] << 8 | R.rs[20], R.rs[15] << 16 | R.rs[25] << 8 | (R.rs[24] & 255),
R.rs[7] << 16 | R.rs[14] << 8 | R.rs[23], s ? SFMT(s) : 0, R.rs[28], R.rs[22]};
for (int k = 0; k < nseen; k++) if (!memcmp(seen[k], key, sizeof key)) return;
static int last;
if (nseen && !memcmp(seen[last], key, sizeof key)) return;
for (int k = 0; k < nseen; k++) if (!memcmp(seen[k], key, sizeof key)) { last = k; return; }
if (nseen >= 64) return;
memcpy(seen[nseen++], key, sizeof key);
port_log("raster setup: blend %u %u->%u | alpha test %u func %u ref %u | z %u write %u func %u | tex fmt %u | fog %u cull %u",
Expand Down Expand Up @@ -761,10 +767,12 @@ static int gl_buffer_draw(uint32_t prim, uint32_t n, uint32_t first, int32_t bas
if (ib) gl_buffer(ib, 1);
int32_t st[41];
gl_state(st);
if (gl_ncmds == MAX_CMDS) gl_flush();
uint32_t size = 27 + 6 * (uint32_t)nlights, at4 = gl_nconsts;
if (at4 % GL_KROW + size > GL_KROW) at4 += GL_KROW - at4 % GL_KROW;
if (gl_ncmds == MAX_CMDS || at4 + size > GL_KROWS * GL_KROW) { gl_flush(); at4 = 0; }

float* c = gl_consts[gl_nconsts];
memset(c, 0, sizeof gl_consts[0]);
float* c = gl_consts + 4 * at4;
memset(c, 0, 16 * size);
put_mat(c, &wvp); put_mat(c + 16, &wv); put_mat(c + 32, &R.world); put_mat(c + 48, &R.tex[0]); put_mat(c + 64, &R.tex[1]);
c[80] = LY.vx; c[81] = LY.vy; c[82] = LY.vw; c[83] = LY.vh;
c[84] = (float)SW(R.target); c[85] = (float)SH(R.target);
Expand All @@ -779,9 +787,9 @@ static int gl_buffer_draw(uint32_t prim, uint32_t n, uint32_t first, int32_t bas
memcpy(d + 4, L + 1, 12); memcpy(d + 8, L + 9, 12); memcpy(d + 12, L + 13, 12); memcpy(d + 16, L + 16, 12);
memcpy(d + 20, L + 21, 12);
}
uint32_t block = gl_nconsts;
if (block && !memcmp(c, gl_consts[block - 1], sizeof gl_consts[0])) block--;
else gl_nconsts++;
uint32_t block = at4;
if (gl_klast_size == size && !memcmp(c, gl_consts + 4 * gl_klast, 16 * size)) block = gl_klast;
else { gl_klast = at4; gl_klast_size = size; gl_nconsts = at4 + size; }

GlCmd* cmd = &gl_cmds[gl_ncmds++];
static const int32_t gl_prim[7] = {0, 0, 1, 3, 4, 5, 6}; /* POINTS LINES LINE_STRIP TRIANGLES _STRIP _FAN */
Expand Down
11 changes: 11 additions & 0 deletions recomp/runtime/rt_game.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,22 @@ static int height_node(CPU* RT_RESTRICT c, uint32_t node, uint32_t from, uint32_
guest_call(c, f_0045ba60, 0x0045ba60u, 0x00489b58u, diff, dir_n, c->ecx);
Vec3 orig = vload(from_t), dir = vload(dir_n);
uint32_t verts = M32(model + 0x10) + 2, out = M32(frame + 8);
int vertical = dir.x == 0 && dir.z == 0;
for (uint32_t g = 0; g < count; g++) {
uint32_t idx = list_at(lists, (int16_t)M16(groups + 2 + 7 * g + 5));
for (int32_t k = 0; k < (int32_t)M16(idx); k += 3) {
uint32_t tri = idx + 2 + 2 * (uint32_t)k;
float t;
/* The ray is straight up in practice, and then ray_triangle's u is the 2D cross product of the origin and
the second vertex around the first: the same value bit for bit (but the sign of a zero) whenever u is
not NaN, so rejecting here on u < 0 or NaN rejects exactly what it would. */
if (vertical) {
uint32_t p0 = verts + 12 * M16(tri), pa = verts + 12 * M16(tri + 4);
float sx = (float)((double)orig.x - MF32(p0)), sz = (float)((double)orig.z - MF32(p0 + 8));
float px = (float)((double)dir.y * (float)((double)MF32(pa + 8) - MF32(p0 + 8)));
float pz = (float)-((double)dir.y * (float)((double)MF32(pa) - MF32(p0)));
if (!((float)((double)sx * px + (double)sz * pz) >= 0.0)) continue;
}
if (!ray_triangle(orig, dir, vload(verts + 12 * M16(tri)), vload(verts + 12 * M16(tri + 4)),
vload(verts + 12 * M16(tri + 2)), &t))
continue;
Expand Down