mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
74dadc6004
Post effects now run as chains of full-screen fragment passes on one executor (gl_postfx.inc), as asked in review. A chain is a small .pfx preset (postfx_chain.c, GL-free): passes with a shader (a file next to the preset or builtin:<name>), inputs (source, the optional primitive-derived mask, previous or a named earlier pass), a scale, format, filter and uniforms. The executor owns target allocation and lifetime, program build/cache, uniform binding (u_rcp, u_size, u_org) and GL state save/restore. Stages, in order, each from a fresh copy of the image: the built-in "bloom" chain ([video] bloom), a user chain ([video] post_shader / PSX_POST_SHADER, elective, off by default), and the built-in "fxaa"/"fxaa_hq" chain ([video] antialiasing_mode). FXAA folds in as a one-pass chain and its output is byte-identical to the former fixed pass; it still runs on real frames only. Generated (Smooth motion) frames run bloom and the user chain. The "may glow" mask is an optional pipeline input: draws keep stencil bit 1 only while an active chain reads `mask`, and the window-sized mask texture is built once per present for the chains that use it. Tests: postfx_chain_test (format, built-ins, errors, file import, no GL); gl_bloom_test gains an imported two-pass chain (byte-exact invert), the mask input (HUD unlit, 3D lit), a bad preset, a pass that does not compile, and GL state restored with bloom + a mask chain + FXAA on, all on the caller thread and the render thread. docs/POST_EFFECTS.md documents the format. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
87 lines
3.5 KiB
C
87 lines
3.5 KiB
C
/* postfx_chain.h — post-processing pass chains (docs/POST_EFFECTS.md).
|
|
*
|
|
* A chain is a list of full-screen fragment passes run on the composed game
|
|
* image by the OpenGL executor (gl_postfx.inc). This header is the GL-free
|
|
* part: the preset format and its parser, so it can be unit tested alone.
|
|
*
|
|
* Preset (.pfx) format, one directive per line, '#' comments:
|
|
*
|
|
* pfx 1 # format version (required first)
|
|
* pass <name> # starts a pass; the last pass writes the window
|
|
* shader <file.glsl|builtin:name> # fragment body (see below)
|
|
* input <sampler> <source|mask|previous|<pass name>>
|
|
* scale <f> # target size, fraction of the game rect (default 1)
|
|
* format rgba8|rgba16f # target format (default rgba8)
|
|
* filter linear|nearest # how later passes sample this target (default linear)
|
|
* uniform <name> <f> [f [f [f]]] # float, vec2, vec3 or vec4
|
|
*
|
|
* A shader body is GLSL 330 appended to a header that declares
|
|
* in vec2 v_uv; out vec4 o;
|
|
* uniform vec2 u_rcp; // 1 / this pass's target size in px
|
|
* uniform vec2 u_size; // this pass's target size in px
|
|
* uniform ivec2 u_org; // window origin of the game rect (gl_FragCoord - u_org = px in it)
|
|
* plus one `uniform sampler2D <sampler>;` per input. v_uv is 0..1 over the
|
|
* game rect, bottom-up like the window. */
|
|
#ifndef PSX_POSTFX_CHAIN_H
|
|
#define PSX_POSTFX_CHAIN_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
enum { PFX_MAX_PASSES = 16, PFX_MAX_INPUTS = 4, PFX_MAX_UNIFORMS = 8, PFX_NAME = 32 };
|
|
/* Input sources; a value >= 0 is an earlier pass's index. */
|
|
enum { PFX_IN_SOURCE = -1, PFX_IN_MASK = -2 };
|
|
enum { PFX_FMT_RGBA8 = 0, PFX_FMT_RGBA16F = 1 };
|
|
|
|
typedef struct {
|
|
char sampler[PFX_NAME];
|
|
int from; /* PFX_IN_* or an earlier pass index */
|
|
} PfxInput;
|
|
|
|
typedef struct {
|
|
char name[PFX_NAME];
|
|
int count; /* 1..4 */
|
|
float v[4];
|
|
} PfxUniform;
|
|
|
|
typedef struct {
|
|
char name[PFX_NAME];
|
|
char *shader; /* fragment body (owned) */
|
|
float scale;
|
|
int format, nearest;
|
|
int ninputs, nuniforms;
|
|
PfxInput in[PFX_MAX_INPUTS];
|
|
PfxUniform u[PFX_MAX_UNIFORMS];
|
|
} PfxPass;
|
|
|
|
typedef struct {
|
|
char name[PFX_NAME];
|
|
int npasses;
|
|
int uses_mask; /* some pass reads `mask` */
|
|
PfxPass pass[PFX_MAX_PASSES];
|
|
} PfxChain;
|
|
|
|
/* Resolves `shader` arguments: builtin:<name> and file paths (relative to the
|
|
* preset). Returns a malloc'd body or NULL (err filled). */
|
|
typedef char *(*PfxShaderLoader)(const char *arg, void *user, char *err, int errlen);
|
|
|
|
/* Parse preset text into *out (zeroed first). Returns 1, or 0 with a
|
|
* "line N: ..." message in err. */
|
|
int pfx_chain_parse(const char *text, const char *name, PfxShaderLoader load, void *user,
|
|
PfxChain *out, char *err, int errlen);
|
|
/* Load a preset file; shader files resolve relative to its directory. */
|
|
int pfx_chain_load_file(const char *path, PfxChain *out, char *err, int errlen);
|
|
/* A built-in preset ("bloom", "fxaa", "fxaa_hq"); 0 if unknown. */
|
|
int pfx_chain_builtin(const char *name, PfxChain *out, char *err, int errlen);
|
|
/* A built-in shader body by name, or NULL. */
|
|
const char *pfx_builtin_shader(const char *name);
|
|
/* Set every pass's uniform `name` (count values); returns how many were set. */
|
|
int pfx_chain_set_uniform(PfxChain *c, const char *name, int count, const float *v);
|
|
void pfx_chain_free(PfxChain *c);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|