mirror of
https://github.com/jkdansereau/goldeneye-pc-port.git
synced 2026-10-23 15:52:42 +02:00
port: D397 — fix file-select/gun-barrel comb via RDP scanline-range snap in gfx_draw_rectangle
Root cause H2 confirmed: the gun-barrel / file-select vertical gradient is built from 0.75-native-pixel-tall gSPTextureRectangle row strips (title2.c (<<2)-1 bottom idiom). On the N64 RDP the scan converter floors the U10.2 edges to whole scanlines, so each strip rasterizes to exactly one scanline; gfx_draw_rectangle passed the fractional edges straight to the GPU's continuous rasterizer, aliasing the 0.75px pitch into a periodic 'comb' (fixed ~3-native-row period; window period = 3*scale — confirmed by a 3-scale 640/1024/1280 GE_PCDUMP test). Fix (port-layer, surgical): after the sub-pixel offset, floor the top edge to its scanline and the bottom edge to the last covered scanline (the RDP scanline range [top_scan, bot_scan+1]) before NDC conversion. No-op for whole-pixel rects (edges already 4k/4k-1), so only sub-pixel strips change and normal fills are untouched. Also: remove the temporary GE_D397SH / D397SHM env-gated shader probes (gfx_opengl.cpp, gfx_pc.cpp) now that the root cause is settled. Docs: findings.md D397 -> RESOLVED (+3-scale confirmation + fix record), findings-index.csv regenerated, porting-notes.md section D15 marked resolved with the landed fix. Verified by playtest: gun-barrel + file-select gradient render clean, comb gone.
This commit is contained in:
1 parent
2a60b8a912
commit
a548fc4de6
5 files changed
+86
-51
No files matched your search
@@ -560,36 +560,6 @@ static struct ShaderProgram* gfx_opengl_create_and_load_new_shader(uint64_t shad
|
||||
vs_buf[vs_len] = '\0';
|
||||
fs_buf[fs_len] = '\0';
|
||||
|
||||
#ifdef PORT
|
||||
/* D397 TEMP probe (env-gated): dump the generated texel formula per
|
||||
* shader so the file-select gunbarrel combine can be inspected. */
|
||||
{
|
||||
static int d397_on = -1;
|
||||
if (d397_on < 0) d397_on = getenv("GE_D397SH") != NULL;
|
||||
if (d397_on) {
|
||||
fprintf(stderr,
|
||||
"D397SH: id0=%016llx id1=%08x 2cyc=%d alpha=%d noise=%d edge=%d blur=%d grey=%d "
|
||||
"c0r=(%u,%u,%u,%u) c0a=(%u,%u,%u,%u) c1r=(%u,%u,%u,%u) c1a=(%u,%u,%u,%u) "
|
||||
"single(%d,%d/%d,%d) mult(%d,%d/%d,%d) mix(%d,%d/%d,%d)\n",
|
||||
(unsigned long long)shader_id0, (unsigned)shader_id1,
|
||||
(int)cc_features.opt_2cyc, (int)cc_features.opt_alpha, (int)cc_features.opt_noise,
|
||||
(int)cc_features.opt_texture_edge, (int)cc_features.opt_blur, (int)cc_features.opt_grayscale,
|
||||
cc_features.c[0][0][0], cc_features.c[0][0][1], cc_features.c[0][0][2], cc_features.c[0][0][3],
|
||||
cc_features.c[0][1][0], cc_features.c[0][1][1], cc_features.c[0][1][2], cc_features.c[0][1][3],
|
||||
cc_features.c[1][0][0], cc_features.c[1][0][1], cc_features.c[1][0][2], cc_features.c[1][0][3],
|
||||
cc_features.c[1][1][0], cc_features.c[1][1][1], cc_features.c[1][1][2], cc_features.c[1][1][3],
|
||||
(int)cc_features.do_single[0][0], (int)cc_features.do_single[0][1],
|
||||
(int)cc_features.do_single[1][0], (int)cc_features.do_single[1][1],
|
||||
(int)cc_features.do_multiply[0][0], (int)cc_features.do_multiply[0][1],
|
||||
(int)cc_features.do_multiply[1][0], (int)cc_features.do_multiply[1][1],
|
||||
(int)cc_features.do_mix[0][0], (int)cc_features.do_mix[0][1],
|
||||
(int)cc_features.do_mix[1][0], (int)cc_features.do_mix[1][1]);
|
||||
const char* tf = strstr(fs_buf, "texel = ");
|
||||
if (tf) fprintf(stderr, "D397SHF: %s;\n", tf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const GLchar* sources[2] = { vs_buf, fs_buf };
|
||||
const GLint lengths[2] = { (GLint)vs_len, (GLint)fs_len };
|
||||
GLint success;
|
||||
|
||||
+22
-18
@@ -550,24 +550,6 @@ static void gfx_generate_cc(struct ColorCombiner* comb, const ColorCombinerKey&
|
||||
comb->used_textures[1] = used_textures[1];
|
||||
// comb->prg = gfx_lookup_or_create_shader_program(shader_id0, shader_id1);
|
||||
memcpy(comb->shader_input_mapping, shader_input_mapping, sizeof(shader_input_mapping));
|
||||
#ifdef PORT
|
||||
/* D397 TEMP probe (env-gated, remove before merge): dump which runtime
|
||||
* G_CCMUX inputs this combine feeds as vInput1..N so the file-select
|
||||
* LERP factor source can be settled against the measured census. */
|
||||
if (getenv("GE_D397SH")) {
|
||||
fprintf(stderr,
|
||||
"D397SHM: id0=%016llx cmode=0x%016llx map0=[%u %u %u %u %u %u %u] map1=[%u %u %u %u %u %u %u]\n",
|
||||
(unsigned long long)comb->shader_id0, (unsigned long long)key.combine_mode,
|
||||
(unsigned)comb->shader_input_mapping[0][0], (unsigned)comb->shader_input_mapping[0][1],
|
||||
(unsigned)comb->shader_input_mapping[0][2], (unsigned)comb->shader_input_mapping[0][3],
|
||||
(unsigned)comb->shader_input_mapping[0][4], (unsigned)comb->shader_input_mapping[0][5],
|
||||
(unsigned)comb->shader_input_mapping[0][6],
|
||||
(unsigned)comb->shader_input_mapping[1][0], (unsigned)comb->shader_input_mapping[1][1],
|
||||
(unsigned)comb->shader_input_mapping[1][2], (unsigned)comb->shader_input_mapping[1][3],
|
||||
(unsigned)comb->shader_input_mapping[1][4], (unsigned)comb->shader_input_mapping[1][5],
|
||||
(unsigned)comb->shader_input_mapping[1][6]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct ColorCombiner* gfx_lookup_or_create_color_combiner(const ColorCombinerKey& key) {
|
||||
@@ -3471,6 +3453,28 @@ static void gfx_draw_rectangle(int32_t ulx, int32_t uly, int32_t lrx, int32_t lr
|
||||
uly += rdp.subpixel_ofs_y;
|
||||
lry += rdp.subpixel_ofs_y;
|
||||
|
||||
// D397: the N64 RDP floors the U10.2 top/bottom edges of a texture rect to
|
||||
// whole scanlines before its per-scanline coverage test, so a rect covers
|
||||
// scanlines [floor(uly/4), floor((lry-1)/4]]. A rect whose height is not a
|
||||
// whole number of native pixels therefore rasterizes to a whole number of
|
||||
// scanlines on hardware at every window scale. The gun-barrel / file-select
|
||||
// gradient is built from 0.75px-tall row strips (title2.c: uly=(i+12)<<2,
|
||||
// lry=((i+13)<<2)-1 -> 0.75px tall at 1px pitch); on the RDP each strip is
|
||||
// exactly one scanline. The continuous-NDC conversion below instead passes
|
||||
// the fractional bottom edge straight to the GPU, whose coverage test
|
||||
// aliases the 0.75px pitch into a visible "comb" (H2: period fixed in
|
||||
// native rows, ~3 = the 0.75px-at-1px-pitch beat, window period = 3*scale).
|
||||
// Snap the top/bottom edges to the RDP scanline range so a sub-pixel strip
|
||||
// rasterizes to exactly the scanline it targets. No-op for whole-pixel
|
||||
// rects (edges already multiples of 4) and for the copy-mode +1<<2 edge
|
||||
// (already a whole scanline), so normal fills are unchanged.
|
||||
if (lry > uly) {
|
||||
const int32_t top_scan = uly >> 2;
|
||||
const int32_t bot_scan = (lry - 1) >> 2;
|
||||
uly = top_scan << 2;
|
||||
lry = (bot_scan + 1) << 2;
|
||||
}
|
||||
|
||||
// U10.2 coordinates
|
||||
float ulxf = ulx;
|
||||
float ulyf = uly;
|
||||
|
||||
Reference in new issue
Block a user