mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
8dbe2165ff
Smooth motion lost most of its generated frames with bloom on (R4 race, 120 Hz: ~30% of bloom-off). Measured and fixed in order of cost: - Depth-stencil blits (CPU milliseconds each on macOS) are gone: generation surfaces no longer copy the marks and are not marked while drawn (generated frames reuse the newer real frame's mask); the native-wide centre copy is colour only again and the mask takes the centre's marks from the hr surface; the mask itself is resolved in the presented surface's own pixels through an R8 target that shares the surface's depth-stencil renderbuffer, then mapped to the window. - Marking needs one pass per batch, not two: 3D batches only set, 2D opaque ones only clear, semi-transparent ones only set; only batches of unknown make (queued replays, lines) still do both. - No full-size copy of the image: `source_down` (a blit straight to the pass's size) feeds the bright pass, and the composite screen-blends onto the window (`blend screen`; unlit pixels are discarded, so they keep their exact value). The debug split/bloom-only views go with the copy. M4, R4 race, render thread + reprojection, PSX_FRAME_GEN_FORCE, a host loaded by other work (absolute rates low): bloom pass on real / generated frames 1.2 / 0.7 ms at 1080p, 1.3 / 0.9 ms at 1440p, 2.6 / 1.6 ms at 4K (glFinish-bracketed); generated frames with bloom 79-80% of bloom-off at all three (was ~30%). A 1/8-resolution blur chain was tried and kept no more frames, so the 1/4 chain (the look) stays. Tests: parser (blend, source_down, bloom preset reads no full copy), GL fixture (source_down + blend screen chain exact; HUD and mask checks unchanged), FXAA unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
5.1 KiB
C
90 lines
5.1 KiB
C
/* Post-effect chain presets (postfx_chain.c): the format, built-in presets,
|
|
* errors and file import, with no GL. */
|
|
#include "postfx_chain.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static int checks, failures;
|
|
static void check(int ok, const char *what) {
|
|
checks++;
|
|
if (!ok) { failures++; printf("FAIL %s\n", what); }
|
|
}
|
|
|
|
static int parse(const char *text, PfxChain *c, char *err) {
|
|
return pfx_chain_parse(text, "t", NULL, NULL, c, err, 256);
|
|
}
|
|
|
|
int main(void) {
|
|
PfxChain c; char err[256];
|
|
|
|
check(pfx_chain_builtin("bloom", &c, err, sizeof err), "bloom preset loads");
|
|
check(c.npasses == 8 && c.uses_mask, "bloom: 8 passes, reads the mask");
|
|
check(c.pass[0].scale == 0.25f && c.pass[0].format == PFX_FMT_RGBA16F, "bloom: quarter-res float bright pass");
|
|
check(c.pass[7].ninputs == 2 && c.pass[7].in[0].from == 6 && c.pass[7].in[1].from == PFX_IN_MASK &&
|
|
c.pass[7].blend == PFX_BLEND_SCREEN, "bloom: composite screen-blends previous under the mask");
|
|
check(c.pass[0].in[0].from == PFX_IN_SOURCE_DOWN, "bloom: bright pass reads the scaled-down image");
|
|
for (int i = 0; i < c.npasses; i++)
|
|
for (int k = 0; k < c.pass[i].ninputs; k++)
|
|
check(c.pass[i].in[k].from != PFX_IN_SOURCE, "bloom: no full-size copy of the image");
|
|
check(c.pass[0].reuse_generated && c.pass[6].reuse_generated && !c.pass[7].reuse_generated,
|
|
"bloom: generated frames reuse bright/blur, run the composite");
|
|
const float k = 1.7f;
|
|
check(pfx_chain_set_uniform(&c, "u_strength", 1, &k) == 1 && c.pass[7].u[0].v[0] == 1.7f, "uniform override");
|
|
pfx_chain_free(&c);
|
|
check(pfx_chain_builtin("fxaa", &c, err, sizeof err) && c.npasses == 1 && !c.uses_mask, "fxaa preset");
|
|
pfx_chain_free(&c);
|
|
check(pfx_chain_builtin("fxaa_hq", &c, err, sizeof err) && c.pass[0].u[0].v[0] == 1.0f, "fxaa_hq preset");
|
|
pfx_chain_free(&c);
|
|
check(!pfx_chain_builtin("nope", &c, err, sizeof err), "unknown preset refused");
|
|
|
|
check(parse("pfx 1\n# c\npass a\n shader builtin:blur\n input t source\n scale 0.5\n filter nearest\n"
|
|
" uniform u_dir 1 2\n generated reuse\npass b\n generated reuse\n shader builtin:blur\n input t a\n input m mask\n", &c, err),
|
|
"custom chain parses");
|
|
check(c.pass[0].nearest && c.pass[0].u[0].count == 2 && c.pass[1].in[0].from == 0 &&
|
|
c.pass[1].scale == 1.0f && c.uses_mask && c.pass[0].reuse_generated &&
|
|
!c.pass[1].reuse_generated, "custom chain fields (the window pass always runs)");
|
|
pfx_chain_free(&c);
|
|
|
|
static const struct { const char *text, *want; } bad[] = {
|
|
{ "pass a\n", "line 1: expected 'pfx 1' first" },
|
|
{ "pfx 2\n", "line 1: expected 'pfx 1' first" },
|
|
{ "pfx 1\n", "line 1: no passes" },
|
|
{ "pfx 1\nshader builtin:blur\n", "line 2: 'shader' outside a pass" },
|
|
{ "pfx 1\npass a\n input t previous\n", "line 3: the first pass has no previous" },
|
|
{ "pfx 1\npass a\n shader builtin:blur\n input t later\n", "line 4: unknown input 'later'" },
|
|
{ "pfx 1\npass a\n scale 9\n", "line 3: scale must be in (0, 4]" },
|
|
{ "pfx 1\npass a\n uniform u 1 x\n", "line 3: bad number 'x'" },
|
|
{ "pfx 1\npass a\n shader builtin:nope\n", "line 3: unknown built-in shader 'nope'" },
|
|
{ "pfx 1\npass a\npass a\n", "line 3: duplicate pass 'a'" },
|
|
{ "pfx 1\npass a\n input t source\n", "pass 'a' has no shader" },
|
|
{ "pfx 1\npass a\n wobble 1\n", "line 3: unknown directive 'wobble'" },
|
|
{ "pfx 1\npass a\n generated maybe\n", "line 3: generated must be reuse or run" },
|
|
{ "pfx 1\npass a\n blend add\n", "line 3: blend must be replace or screen" },
|
|
{ "pfx 1\npass a\n shader builtin:blur\n blend screen\npass b\n shader builtin:blur\n",
|
|
"pass 'a': only the last pass may blend" },
|
|
};
|
|
for (size_t i = 0; i < sizeof bad / sizeof bad[0]; i++) {
|
|
int ok = parse(bad[i].text, &c, err);
|
|
if (ok || strncmp(err, bad[i].want, strlen(bad[i].want))) printf(" got '%s' for case %zu\n", ok ? "(ok)" : err, i);
|
|
check(!ok && !strncmp(err, bad[i].want, strlen(bad[i].want)), "error message");
|
|
}
|
|
|
|
/* File import: shaders resolve next to the preset. */
|
|
FILE *f = fopen("pfx_chain_test.glsl", "wb"); fputs("void main(){ o = vec4(1.0); }\n", f); fclose(f);
|
|
f = fopen("pfx_chain_test.pfx", "wb"); fputs("pfx 1\npass p\n shader pfx_chain_test.glsl\n input t source\n", f); fclose(f);
|
|
check(pfx_chain_load_file("pfx_chain_test.pfx", &c, err, sizeof err) &&
|
|
!strcmp(c.pass[0].shader, "void main(){ o = vec4(1.0); }\n"), "file preset and shader import");
|
|
pfx_chain_free(&c);
|
|
check(!pfx_chain_load_file("pfx_missing.pfx", &c, err, sizeof err) && strstr(err, "cannot open"), "missing preset");
|
|
/* A preset written with CRLF line ends (Windows editors) parses the same. */
|
|
check(parse("pfx 1\r\npass a\r\n shader builtin:blur\r\n input t source\r\n scale 0.5\r\n", &c, err) &&
|
|
c.pass[0].in[0].from == PFX_IN_SOURCE, "CRLF preset");
|
|
pfx_chain_free(&c);
|
|
remove("pfx_chain_test.glsl"); remove("pfx_chain_test.pfx");
|
|
|
|
printf("checks=%d failures=%d\n", checks, failures);
|
|
return failures ? 1 : 0;
|
|
}
|