mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Support adaptive screen masks and native effect presentation
This commit is contained in:
@@ -288,6 +288,9 @@ if(BUILD_TESTING)
|
||||
add_executable(ws_repeat_rect_test tests/test_ws_repeat_rect.c)
|
||||
target_include_directories(ws_repeat_rect_test PRIVATE include)
|
||||
add_test(NAME ws_repeat_rect_test COMMAND ws_repeat_rect_test)
|
||||
add_executable(ws_screen_mask_test tests/test_ws_screen_mask.c)
|
||||
target_include_directories(ws_screen_mask_test PRIVATE include)
|
||||
add_test(NAME ws_screen_mask_test COMMAND ws_screen_mask_test)
|
||||
|
||||
add_executable(mod_gpu_dma_aperture_test
|
||||
tests/test_mod_gpu_dma_aperture.c)
|
||||
|
||||
@@ -393,6 +393,14 @@ void gpu_ws_tag_black_reveal_rect(uint32_t prim);
|
||||
* reveal margins only. The caller verifies the composite's source period.
|
||||
* Original UVs, texel density, and canonical VRAM writes are unchanged. */
|
||||
void gpu_ws_tag_repeat_rect(uint32_t prim, int32_t period);
|
||||
/* Title-identified flat screen mask: extend its off-screen vertical boundary
|
||||
* to the adaptive edge, preserving the interior opening and canonical image.
|
||||
* Packet guarded, cleared on reset/load, inert outside native-wide gameplay. */
|
||||
void gpu_ws_tag_screen_mask_quad(uint32_t prim);
|
||||
/* Pure emulation-thread predicate for a known native-only effect/scene.
|
||||
* Nonzero forces native 4:3; zero defers. No GPU calls or guest writes inside.
|
||||
* Host configuration survives reset/load; classification reads live state. */
|
||||
void gpu_ws_set_native_scene_predicate(int (*predicate)(void));
|
||||
/* Targeted alternative for sprite-heavy 2D games: corner-anchor only primitives
|
||||
* whose ordering-table packet lives in the configured half-open RAM range. */
|
||||
void gpu_ws_set_nw_left_hud_packet_range(uint32_t lo, uint32_t hi);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef PSXRECOMP_WS_SCREEN_MASK_H
|
||||
#define PSXRECOMP_WS_SCREEN_MASK_H
|
||||
#include <stdint.h>
|
||||
|
||||
/* Extend only an off-screen vertical boundary of a title-identified mask.
|
||||
* The interior/slanted light boundary stays at its original coordinates.
|
||||
* Result is wholly outside canonical VRAM; no triangle size limit applies. */
|
||||
typedef struct { int x, y, w, h; } WsScreenMaskBand;
|
||||
static inline int ws_screen_mask_band(const int32_t x[4], const int32_t y[4],
|
||||
int width, int height, int margin,
|
||||
WsScreenMaskBand *out) {
|
||||
if (!out || width <= 0 || height <= 0 || margin <= 0 ||
|
||||
y[0] != y[1] || y[2] != y[3] || y[0] != 0 || y[2] != height)
|
||||
return 0;
|
||||
if (x[0] == x[2] && x[0] <= 0 && x[1] > x[0] && x[3] > x[0] &&
|
||||
x[0] > -margin) {
|
||||
*out = (WsScreenMaskBand){-margin, 0, margin + x[0], height};
|
||||
return 1;
|
||||
}
|
||||
if (x[1] == x[3] && x[1] >= width && x[0] < x[1] && x[2] < x[1] &&
|
||||
x[1] < width + margin) {
|
||||
*out = (WsScreenMaskBand){x[1], 0, width + margin - x[1], height};
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "ws_prepass_guard.h"
|
||||
#include "ws_hud_anchor.h"
|
||||
#include "ws_repeat_rect.h"
|
||||
#include "ws_screen_mask.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -176,6 +177,7 @@ typedef struct { uint32_t key; uint32_t stamp; int32_t anchor_x; } WsTag;
|
||||
static WsTag ws_tags[WS_TAG_BUCKETS];
|
||||
static WsHudAnchorTag ws_hud_anchor_tags[WS_HUD_ANCHOR_TABLE_SIZE];
|
||||
static WsHudAnchorTag ws_reveal_clear_tags[WS_HUD_ANCHOR_TABLE_SIZE];
|
||||
static WsHudAnchorTag ws_screen_mask_tags[WS_HUD_ANCHOR_TABLE_SIZE];
|
||||
static WsRepeatRectTag ws_repeat_rect_tags[WS_REPEAT_RECT_TAG_TABLE_SIZE];
|
||||
static uint32_t ws_last_tag_stamp = (uint32_t)-1000; /* frame of newest tag */
|
||||
static uint32_t ws_last_3d_stamp = (uint32_t)-1000; /* frame of newest shaded prim (diagnostic) */
|
||||
@@ -407,6 +409,7 @@ static void ws_reset_scene_history(void) {
|
||||
static uint32_t s_ws_fmv_frame_cache = 0xFFFFFFFFu;
|
||||
static int s_ws_fmv_cached = 0;
|
||||
static PSXModRetainedScenePredicate s_ws_retained_scene_predicate;
|
||||
static int (*s_ws_native_scene_predicate)(void);
|
||||
static WsSceneHold s_ws_scene_hold;
|
||||
static uint32_t ws_display_origin(void);
|
||||
|
||||
@@ -415,8 +418,14 @@ void psx_mod_set_retained_scene_predicate(PSXModRetainedScenePredicate predicate
|
||||
ws_scene_hold_reset(&s_ws_scene_hold);
|
||||
}
|
||||
|
||||
void gpu_ws_set_native_scene_predicate(int (*predicate)(void)) {
|
||||
s_ws_native_scene_predicate = predicate;
|
||||
}
|
||||
|
||||
int gpu_ws_present_native_43(void) {
|
||||
if (!ws_engaged()) return 0;
|
||||
if (ws_mode == 2 && s_ws_native_scene_predicate && s_ws_native_scene_predicate())
|
||||
return 1;
|
||||
int game_mode = ws_game_mode();
|
||||
if (!game_mode) ws_scene_latch.confirmed = 0;
|
||||
int native_43 = !game_mode || ws_2d_only_scene();
|
||||
@@ -2147,6 +2156,18 @@ void gpu_ws_tag_repeat_rect(uint32_t prim, int32_t period) {
|
||||
period, &guard, (uint32_t)s_frame_count);
|
||||
}
|
||||
|
||||
void gpu_ws_tag_screen_mask_quad(uint32_t prim) {
|
||||
if (!ws_native_wide_configured() || (prim & 3u) || prim > UINT32_MAX - 4u)
|
||||
return;
|
||||
uint32_t words[12], count = 0;
|
||||
if (!ws_hud_command_words(prim + 4u, words, &count) || count != 5u ||
|
||||
((words[0] >> 24) & 0xfdu) != 0x28u) return;
|
||||
WsPrepassPacketGuard guard = ws_prepass_packet_guard(words, count);
|
||||
ws_hud_anchor_insert(ws_screen_mask_tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
(prim + 4u) & 0x1ffffcu, 0, &guard,
|
||||
(uint32_t)s_frame_count);
|
||||
}
|
||||
|
||||
static int ws_nw_explicit_hud_delta(int32_t *out_delta) {
|
||||
if (out_delta) *out_delta = 0;
|
||||
if (!ws_native_wide_active() || gp0_cmd_source_addr == 0xFFFFFFFFu)
|
||||
@@ -3066,6 +3087,7 @@ static void gpu_reset_state(int clear_vram) {
|
||||
gp0_cmd_source_addr = 0xFFFFFFFFu;
|
||||
ws_hud_anchor_clear(ws_hud_anchor_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_hud_anchor_clear(ws_reveal_clear_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_hud_anchor_clear(ws_screen_mask_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_repeat_rect_tag_clear(ws_repeat_rect_tags);
|
||||
polyline_color = 0;
|
||||
polyline_prev_x = polyline_prev_y = 0;
|
||||
@@ -4064,6 +4086,21 @@ static void gp0_exec_mono_quad(void) {
|
||||
int rej_b = psx_gpu_triangle_oversize(vx, vy, 2, 1, 3);
|
||||
if (rej_a && rej_b) return;
|
||||
|
||||
/* Each added band is outside the original screen and executes in the
|
||||
* mask's own OT position/blend mode. Canonical VRAM clips it away. */
|
||||
if (ws_native_wide_active() && gp0_cmd_source_addr != UINT32_MAX &&
|
||||
ws_hud_anchor_lookup(ws_screen_mask_tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
gp0_cmd_source_addr & 0x1ffffcu, gp0_cmd_buf, 5u,
|
||||
(uint32_t)s_frame_count, NULL)) {
|
||||
WsScreenMaskBand band;
|
||||
if (ws_screen_mask_band(vx, vy, ws_disp_w(), ws_disp_h(),
|
||||
draw_area_wide_x_margin(), &band)) {
|
||||
gr_set_semi_transparency(semi_trans, (int)semi_transparency);
|
||||
gr_draw_flat_rect(band.x + draw_offset_x, band.y + draw_offset_y,
|
||||
band.w, band.h, color);
|
||||
}
|
||||
}
|
||||
|
||||
/* Full-screen filters are commonly encoded as an axis-aligned quad. Drawing
|
||||
* a semi-transparent quad as two independent triangles blends their shared
|
||||
* diagonal twice; render the equivalent rectangle once to avoid that seam.
|
||||
@@ -6467,6 +6504,7 @@ int gpu_snapshot_read(const uint8_t *p, uint32_t len) {
|
||||
ws_scene_hold_reset(&s_ws_scene_hold);
|
||||
ws_hud_anchor_clear(ws_hud_anchor_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_hud_anchor_clear(ws_reveal_clear_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_hud_anchor_clear(ws_screen_mask_tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
ws_repeat_rect_tag_clear(ws_repeat_rect_tags);
|
||||
/* Sync renderer clip/scissor to restored GP0(E3/E4); vars alone leave GL
|
||||
* on a stale draw area after savestate load. */
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifdef NDEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include "ws_screen_mask.h"
|
||||
#include "ws_hud_anchor.h"
|
||||
int main(void) {
|
||||
int32_t x[4]={-96,160,-96,68},y[4]={0,0,240,240};
|
||||
WsScreenMaskBand b;
|
||||
for(int margin=0;margin<16000;margin+=53) {
|
||||
int result=ws_screen_mask_band(x,y,320,240,margin,&b);
|
||||
assert(result==(margin>96));
|
||||
if(result) assert(b.x==-margin && b.x+b.w==-96 && b.y==0 && b.h==240);
|
||||
}
|
||||
x[0]=160;x[1]=416;x[2]=308;x[3]=416;
|
||||
assert(ws_screen_mask_band(x,y,320,240,694,&b));
|
||||
assert(b.x==416 && b.w==598);
|
||||
x[3]=415; assert(!ws_screen_mask_band(x,y,320,240,694,&b));
|
||||
x[3]=416;y[3]=239;assert(!ws_screen_mask_band(x,y,320,240,694,&b));
|
||||
/* Same packet address with different geometry/colour must lose its tag. */
|
||||
WsHudAnchorTag tags[WS_HUD_ANCHOR_TABLE_SIZE]={0};
|
||||
uint32_t words[5]={0x2a7b7b7b,0x0000ffa0,0xa0,0xf0ffa0,0xf00044};
|
||||
WsPrepassPacketGuard guard=ws_prepass_packet_guard(words,5);
|
||||
ws_hud_anchor_insert(tags,WS_HUD_ANCHOR_TABLE_SIZE,0xa68b4,0,&guard,20);
|
||||
assert(ws_hud_anchor_lookup(tags,WS_HUD_ANCHOR_TABLE_SIZE,0xa68b4,words,5,20,0));
|
||||
words[2]++;assert(!ws_hud_anchor_lookup(tags,WS_HUD_ANCHOR_TABLE_SIZE,0xa68b4,words,5,20,0));
|
||||
words[2]--;ws_hud_anchor_clear(tags,WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
assert(!ws_hud_anchor_lookup(tags,WS_HUD_ANCHOR_TABLE_SIZE,0xa68b4,words,5,20,0));
|
||||
}
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
uint64_t s_frame_count;
|
||||
static uint32_t state_word;
|
||||
static int world, calls, fmv, hold;
|
||||
static int world, calls, fmv, hold, native_effect;
|
||||
uint32_t psx_read_word(uint32_t address) { (void)address; return state_word; }
|
||||
int mdec_recently_active(uint32_t frames) { (void)frames; return fmv; }
|
||||
static int world_scene(void) { ++calls; return world; }
|
||||
static int retained_scene(void) { return hold; }
|
||||
static int native_scene(void) { return native_effect; }
|
||||
static int native_43(void) { ++s_frame_count; return gpu_ws_present_native_43(); }
|
||||
|
||||
int main(void) {
|
||||
@@ -38,6 +39,12 @@ int main(void) {
|
||||
assert(native_43() == 1); /* 24-bit FMV veto */
|
||||
display_depth = 0;
|
||||
assert(native_43() == 0);
|
||||
gpu_ws_set_native_scene_predicate(native_scene);
|
||||
native_effect=1; assert(native_43()==1); /* Native attack overrides world. */
|
||||
native_effect=0; assert(native_43()==0); /* Recovery restores adaptive. */
|
||||
native_effect=1; ws_mode=0; assert(native_43()==0); /* Off stays inert. */
|
||||
ws_mode=2; gpu_ws_set_native_scene_predicate(NULL);
|
||||
assert(native_43()==0); /* No policy left behind after unregister. */
|
||||
psx_mod_set_retained_scene_predicate(retained_scene);
|
||||
hold = 0;
|
||||
assert(native_43() == 0);
|
||||
|
||||
Reference in New Issue
Block a user