mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
Anchor GTE-projected HUD pieces by translation; expose recent-GTE-projection query
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1027,6 +1027,11 @@ if(BUILD_TESTING)
|
||||
target_include_directories(ws_hud_anchor_test PRIVATE include)
|
||||
add_test(NAME ws_hud_anchor_test COMMAND ws_hud_anchor_test)
|
||||
|
||||
add_executable(ws_gte_sxy_test
|
||||
tests/test_ws_gte_sxy.c)
|
||||
target_include_directories(ws_gte_sxy_test PRIVATE include)
|
||||
add_test(NAME ws_gte_sxy_test COMMAND ws_gte_sxy_test)
|
||||
|
||||
add_executable(gpu_textured_dot_nw_shift_exec_test
|
||||
tests/test_gpu_textured_dot_nw_shift_exec.c
|
||||
src/psx_video_timing.c
|
||||
|
||||
@@ -518,6 +518,18 @@ void gpu_ws_set_adaptive_backdrop_preload(int enabled);
|
||||
* `prim` is the address of the PsyQ P_TAG word; the drawn command starts at
|
||||
* prim+4. anchor: -1 = left, 0 = center, +1 = right. */
|
||||
void gpu_ws_tag_hud_prim(uint32_t prim, int anchor);
|
||||
/* Same, for a primitive whose X the game took from GTE-projected screen
|
||||
* coordinates, which already carry the widescreen X squash about the display
|
||||
* centre. Projection-and-stretch translates it to the edge instead of scaling
|
||||
* it again; native-wide behaves as for gpu_ws_tag_hud_prim. */
|
||||
void gpu_ws_tag_hud_prim_projected(uint32_t prim, int anchor);
|
||||
/* GTE-projection provenance under the widescreen squash (ws_gte_sxy.h): the
|
||||
* GTE reports each SXY it produces; a title plugin asks whether a packet
|
||||
* vertex is one of them, i.e. whether the game built that primitive from
|
||||
* projected geometry (its X is already squashed) rather than authored
|
||||
* screen coordinates. Recorded only while the squash is engaged. */
|
||||
void gpu_ws_note_gte_sxy(int32_t x, int32_t y);
|
||||
int gpu_ws_gte_sxy_recent(int32_t x, int32_t y);
|
||||
/* Stretch a trusted authored background primitive about the display center in
|
||||
* the native-wide output only (GL/SW). prim+4 is the command address, including
|
||||
* for a command embedded inside a longer DMA packet. Only opaque polygons and
|
||||
|
||||
@@ -360,6 +360,17 @@ void psx_mod_tag_hud_primitive(uint32_t primitive, int edge);
|
||||
* immediately before the GP0 colour command; for a compound E1+SPRT packet
|
||||
* this is its E1 word (P_TAG+4). */
|
||||
void psx_mod_anchor_hud_primitive(uint32_t primitive, int edge);
|
||||
/* As psx_mod_anchor_hud_primitive, for a packet whose X coordinates came out
|
||||
* of the GTE (so they already carry the widescreen squash about the display
|
||||
* centre, and the guest wrote them verbatim). Projection-and-stretch moves the
|
||||
* piece to the selected edge by translation, keeping its proportions; scaling
|
||||
* it about the edge again would squash it twice. */
|
||||
void psx_mod_anchor_hud_primitive_projected(uint32_t primitive, int edge);
|
||||
/* Did the GTE project a vertex at screen (x, y) in this or the previous
|
||||
* vblank? Under the widescreen squash a primitive whose every vertex answers
|
||||
* yes was built from projected geometry (already squashed); one that does not
|
||||
* carries authored screen coordinates. Always 0 while the squash is off. */
|
||||
int psx_mod_gte_sxy_recent(int32_t x, int32_t y);
|
||||
/* Screen-space masks identified by their title's producer, P_TAG addresses.
|
||||
* Flat panels extend only their exterior boundaries. Radial flat/Gouraud
|
||||
* quads scale around the display centre for an aspect-aware iris transition.
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef PSXRECOMP_WS_GTE_SXY_H
|
||||
#define PSXRECOMP_WS_GTE_SXY_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Which screen positions did the GTE project recently?
|
||||
*
|
||||
* Under projection-and-stretch widescreen the GTE squashes every projected X
|
||||
* about the display centre. A title that builds HUD from GTE-projected
|
||||
* geometry (vector digits and lettering run through its model renderer) then
|
||||
* writes already-squashed X into its packets, whereas screen-space HUD packets
|
||||
* carry authored X. The two need different corrections, and the packet alone
|
||||
* does not say which it is. The GTE stamps every SXY it produces here with the
|
||||
* vblank it happened in; a packet vertex that matches a recent stamp was
|
||||
* projected.
|
||||
*
|
||||
* "Recent" is WS_GTE_SXY_WINDOW vblanks: games commonly rebuild their packets
|
||||
* every few vblanks and resubmit the same ordering table in between (Army Men
|
||||
* rebuilds every fourth), so a retained packet is still younger than its
|
||||
* projection plus one rebuild period. The cell grid is 1024 x 512, covering
|
||||
* the signed 11-bit GP0 vertex range. A hit is evidence, not proof: with ~1000
|
||||
* projections a frame the chance that a whole polygon matches by accident is
|
||||
* negligible, so require every vertex of a primitive to hit. */
|
||||
#define WS_GTE_SXY_W 1024u
|
||||
#define WS_GTE_SXY_H 512u
|
||||
#define WS_GTE_SXY_CELLS (WS_GTE_SXY_W * WS_GTE_SXY_H)
|
||||
#define WS_GTE_SXY_WINDOW 8u
|
||||
|
||||
typedef struct {
|
||||
uint16_t stamp[WS_GTE_SXY_CELLS]; /* 0x8000 | vblank (15 bits); 0 = never */
|
||||
} WsGteSxy;
|
||||
|
||||
static inline uint32_t ws_gte_sxy_cell(int32_t x, int32_t y) {
|
||||
return (((uint32_t)(y + 256) & (WS_GTE_SXY_H - 1u)) * WS_GTE_SXY_W) +
|
||||
((uint32_t)(x + 512) & (WS_GTE_SXY_W - 1u));
|
||||
}
|
||||
|
||||
static inline void ws_gte_sxy_note(WsGteSxy *s, uint32_t frame,
|
||||
int32_t x, int32_t y) {
|
||||
s->stamp[ws_gte_sxy_cell(x, y)] = (uint16_t)(0x8000u | (frame & 0x7FFFu));
|
||||
}
|
||||
|
||||
static inline int ws_gte_sxy_recent(const WsGteSxy *s, uint32_t frame,
|
||||
int32_t x, int32_t y) {
|
||||
const uint16_t st = s->stamp[ws_gte_sxy_cell(x, y)];
|
||||
if (!st) return 0;
|
||||
return ((frame - st) & 0x7FFFu) <= WS_GTE_SXY_WINDOW;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,11 @@ typedef struct {
|
||||
uint32_t frame;
|
||||
int8_t anchor;
|
||||
uint8_t used;
|
||||
/* The primitive's X came out of the GTE and already carries the
|
||||
* widescreen squash about the display centre (see ws_gte_sxy.h). The
|
||||
* projection path then translates it to the anchor instead of scaling it
|
||||
* about the anchor, and never lets automatic UI layout squash it again. */
|
||||
uint8_t projected;
|
||||
WsPrepassPacketGuard guard;
|
||||
} WsHudAnchorTag;
|
||||
|
||||
@@ -54,10 +59,12 @@ static inline void ws_hud_anchor_clear(WsHudAnchorTag *tags,
|
||||
tags[i].used = 0;
|
||||
}
|
||||
|
||||
static inline void ws_hud_anchor_insert(WsHudAnchorTag *tags, uint32_t count,
|
||||
uint32_t command_addr, int anchor,
|
||||
const WsPrepassPacketGuard *guard,
|
||||
uint32_t frame) {
|
||||
static inline void ws_hud_anchor_insert_stored(WsHudAnchorTag *tags,
|
||||
uint32_t count,
|
||||
uint32_t command_addr,
|
||||
int anchor, int projected,
|
||||
const WsPrepassPacketGuard *guard,
|
||||
uint32_t frame) {
|
||||
if (!tags || !count || !guard) return;
|
||||
uint32_t idx = ws_hud_anchor_slot(command_addr) & (count - 1u);
|
||||
uint32_t victim = idx;
|
||||
@@ -75,9 +82,29 @@ static inline void ws_hud_anchor_insert(WsHudAnchorTag *tags, uint32_t count,
|
||||
tags[victim].command_addr = command_addr;
|
||||
tags[victim].frame = frame;
|
||||
tags[victim].anchor = (int8_t)ws_hud_anchor_clamp(anchor);
|
||||
tags[victim].projected = projected ? 1u : 0u;
|
||||
tags[victim].guard = *guard;
|
||||
}
|
||||
|
||||
static inline void ws_hud_anchor_insert(WsHudAnchorTag *tags, uint32_t count,
|
||||
uint32_t command_addr, int anchor,
|
||||
const WsPrepassPacketGuard *guard,
|
||||
uint32_t frame) {
|
||||
ws_hud_anchor_insert_stored(tags, count, command_addr, anchor, 0, guard,
|
||||
frame);
|
||||
}
|
||||
|
||||
/* Tag a GTE-projected piece (see WsHudAnchorTag.projected). */
|
||||
static inline void ws_hud_anchor_insert_projected(WsHudAnchorTag *tags,
|
||||
uint32_t count,
|
||||
uint32_t command_addr,
|
||||
int edge,
|
||||
const WsPrepassPacketGuard *guard,
|
||||
uint32_t frame) {
|
||||
ws_hud_anchor_insert_stored(tags, count, command_addr, edge, 1, guard,
|
||||
frame);
|
||||
}
|
||||
|
||||
/* Lookup outcome, so callers can keep always-on counters that say WHY a tagged
|
||||
* primitive was or was not honoured (absent, expired, or packet rewritten). */
|
||||
enum {
|
||||
@@ -87,13 +114,14 @@ enum {
|
||||
WS_HUD_ANCHOR_GUARD_MISMATCH = -2
|
||||
};
|
||||
|
||||
static inline int ws_hud_anchor_lookup_result(const WsHudAnchorTag *tags,
|
||||
uint32_t count,
|
||||
uint32_t command_addr,
|
||||
const uint32_t *words,
|
||||
uint32_t word_count,
|
||||
uint32_t frame,
|
||||
int *out_anchor) {
|
||||
static inline int ws_hud_anchor_lookup_result_ex(const WsHudAnchorTag *tags,
|
||||
uint32_t count,
|
||||
uint32_t command_addr,
|
||||
const uint32_t *words,
|
||||
uint32_t word_count,
|
||||
uint32_t frame,
|
||||
int *out_anchor,
|
||||
int *out_projected) {
|
||||
if (!tags || !count || !words) return WS_HUD_ANCHOR_ABSENT;
|
||||
uint32_t idx = ws_hud_anchor_slot(command_addr) & (count - 1u);
|
||||
for (uint32_t i = 0; i < WS_HUD_ANCHOR_PROBES && i < count; i++) {
|
||||
@@ -106,11 +134,23 @@ static inline int ws_hud_anchor_lookup_result(const WsHudAnchorTag *tags,
|
||||
if (!ws_prepass_packet_matches(&tag->guard, words, word_count))
|
||||
return WS_HUD_ANCHOR_GUARD_MISMATCH;
|
||||
if (out_anchor) *out_anchor = tag->anchor;
|
||||
if (out_projected) *out_projected = tag->projected;
|
||||
return WS_HUD_ANCHOR_HIT;
|
||||
}
|
||||
return WS_HUD_ANCHOR_ABSENT;
|
||||
}
|
||||
|
||||
static inline int ws_hud_anchor_lookup_result(const WsHudAnchorTag *tags,
|
||||
uint32_t count,
|
||||
uint32_t command_addr,
|
||||
const uint32_t *words,
|
||||
uint32_t word_count,
|
||||
uint32_t frame,
|
||||
int *out_anchor) {
|
||||
return ws_hud_anchor_lookup_result_ex(tags, count, command_addr, words,
|
||||
word_count, frame, out_anchor, 0);
|
||||
}
|
||||
|
||||
static inline int ws_hud_anchor_lookup(const WsHudAnchorTag *tags,
|
||||
uint32_t count,
|
||||
uint32_t command_addr,
|
||||
|
||||
+66
-16
@@ -43,6 +43,7 @@
|
||||
#include "ws_backdrop_extent.h"
|
||||
#include "ws_prepass_guard.h"
|
||||
#include "ws_hud_anchor.h"
|
||||
#include "ws_gte_sxy.h"
|
||||
#include "ws_repeat_rect.h"
|
||||
#include "ws_feedback_rect.h"
|
||||
#include "ws_screen_mask.h"
|
||||
@@ -2581,7 +2582,18 @@ static void ws_tag_stats_note_lookup(GpuWsTagStats *stats, int result) {
|
||||
}
|
||||
}
|
||||
|
||||
void gpu_ws_tag_hud_prim(uint32_t prim, int anchor) {
|
||||
/* Positions the GTE projected this and the previous vblank (ws_gte_sxy.h).
|
||||
* Only recorded while the squash is engaged: nothing else reads it. */
|
||||
static WsGteSxy ws_gte_sxy;
|
||||
void gpu_ws_note_gte_sxy(int32_t x, int32_t y) {
|
||||
if (!ws_active()) return;
|
||||
ws_gte_sxy_note(&ws_gte_sxy, (uint32_t)s_frame_count, x, y);
|
||||
}
|
||||
int gpu_ws_gte_sxy_recent(int32_t x, int32_t y) {
|
||||
return ws_gte_sxy_recent(&ws_gte_sxy, (uint32_t)s_frame_count, x, y);
|
||||
}
|
||||
|
||||
static void ws_tag_hud_prim(uint32_t prim, int anchor, int projected) {
|
||||
ws_hud_tag_stats.tag_calls++;
|
||||
uint32_t command_addr = prim + 4u;
|
||||
uint32_t words[12];
|
||||
@@ -2595,11 +2607,23 @@ void gpu_ws_tag_hud_prim(uint32_t prim, int anchor) {
|
||||
|
||||
WsPrepassPacketGuard guard =
|
||||
ws_prepass_packet_guard(words, word_count);
|
||||
ws_hud_anchor_insert(ws_hud_anchor_tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
GPU_RAM_KEY(command_addr), anchor, &guard,
|
||||
(uint32_t)s_frame_count);
|
||||
if (projected)
|
||||
ws_hud_anchor_insert_projected(ws_hud_anchor_tags,
|
||||
WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
GPU_RAM_KEY(command_addr), anchor,
|
||||
&guard, (uint32_t)s_frame_count);
|
||||
else
|
||||
ws_hud_anchor_insert(ws_hud_anchor_tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
GPU_RAM_KEY(command_addr), anchor, &guard,
|
||||
(uint32_t)s_frame_count);
|
||||
ws_hud_tag_stats.last_tag_frame = (uint32_t)s_frame_count;
|
||||
}
|
||||
void gpu_ws_tag_hud_prim(uint32_t prim, int anchor) {
|
||||
ws_tag_hud_prim(prim, anchor, 0);
|
||||
}
|
||||
void gpu_ws_tag_hud_prim_projected(uint32_t prim, int anchor) {
|
||||
ws_tag_hud_prim(prim, anchor, 1);
|
||||
}
|
||||
|
||||
void gpu_ws_tag_background_prim(uint32_t prim) {
|
||||
ws_background_tag_stats.tag_calls++;
|
||||
@@ -2730,7 +2754,7 @@ static int ws_nw_radial_mask_transform(int32_t x[4], int32_t y[4]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ws_explicit_hud_anchor(int *out_anchor) {
|
||||
static int ws_explicit_hud_anchor_ex(int *out_anchor, int *out_projected) {
|
||||
if (gp0_cmd_source_addr == 0xFFFFFFFFu)
|
||||
return 0;
|
||||
if (gp0_words_needed <= 0 || gp0_words_needed > 12)
|
||||
@@ -2739,17 +2763,21 @@ static int ws_explicit_hud_anchor(int *out_anchor) {
|
||||
if ((command_addr & 3u) ||
|
||||
(command_addr >= psx_ram_live_bytes() &&
|
||||
!psx_mod_gpu_dma_memory_contains(command_addr, 4u))) return 0;
|
||||
int anchor = 0;
|
||||
int result = ws_hud_anchor_lookup_result(
|
||||
int anchor = 0, projected = 0;
|
||||
int result = ws_hud_anchor_lookup_result_ex(
|
||||
ws_hud_anchor_tags, WS_HUD_ANCHOR_TABLE_SIZE, GPU_RAM_KEY(command_addr),
|
||||
gp0_cmd_buf, (uint32_t)gp0_words_needed, (uint32_t)s_frame_count,
|
||||
&anchor);
|
||||
&anchor, &projected);
|
||||
ws_tag_stats_note_lookup(&ws_hud_tag_stats, result);
|
||||
if (result != WS_HUD_ANCHOR_HIT)
|
||||
return 0;
|
||||
if (out_anchor) *out_anchor = anchor;
|
||||
if (out_projected) *out_projected = projected;
|
||||
return 1;
|
||||
}
|
||||
static int ws_explicit_hud_anchor(int *out_anchor) {
|
||||
return ws_explicit_hud_anchor_ex(out_anchor, NULL);
|
||||
}
|
||||
|
||||
static int ws_nw_explicit_hud_delta(int32_t *out_delta) {
|
||||
if (out_delta) *out_delta = 0;
|
||||
@@ -2764,19 +2792,36 @@ static int ws_nw_explicit_hud_delta(int32_t *out_delta) {
|
||||
/* A title can identify one widget whose pieces arrive in separate DMA lists.
|
||||
* Per-list auto grouping cannot see that ownership. Use the same guarded
|
||||
* edge/centre role as native-wide, expressed as a squash pivot here. */
|
||||
static int ws_projection_hud_anchor(int32_t *out_anchor) {
|
||||
static int ws_projection_hud_anchor_ex(int32_t *out_anchor, int *out_projected) {
|
||||
if (!ws_active()) return 0;
|
||||
int edge;
|
||||
if (!ws_explicit_hud_anchor(&edge)) return 0;
|
||||
int edge, projected = 0;
|
||||
if (!ws_explicit_hud_anchor_ex(&edge, &projected)) return 0;
|
||||
if (out_projected) *out_projected = projected;
|
||||
if (out_anchor)
|
||||
*out_anchor = ws_disp_x() + (edge < 0 ? 0 : edge > 0 ? ws_disp_w() : ws_disp_w() / 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ws_projection_hud_anchor(int32_t *out_anchor) {
|
||||
return ws_projection_hud_anchor_ex(out_anchor, NULL);
|
||||
}
|
||||
|
||||
/* Shift that takes an X already squashed about the display centre to where
|
||||
* scaling it about the anchor would have put it: (anchor - centre) * (1 - s). */
|
||||
static int32_t ws_projected_hud_shift(int32_t anchor) {
|
||||
return ws_scale_about(0, anchor) - ws_scale_about(0, ws_disp_x() + ws_disp_w() / 2);
|
||||
}
|
||||
|
||||
static int ws_projection_hud_transform(int32_t *x, int count) {
|
||||
int32_t anchor;
|
||||
if (!ws_projection_hud_anchor(&anchor)) return 0;
|
||||
for (int i = 0; i < count; i++) x[i] = ws_scale_about(x[i], anchor);
|
||||
int projected = 0;
|
||||
if (!ws_projection_hud_anchor_ex(&anchor, &projected)) return 0;
|
||||
if (projected) {
|
||||
const int32_t shift = ws_projected_hud_shift(anchor);
|
||||
for (int i = 0; i < count; i++) x[i] += shift;
|
||||
} else {
|
||||
for (int i = 0; i < count; i++) x[i] = ws_scale_about(x[i], anchor);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -3279,9 +3324,14 @@ static int ws_auto_ui_transform_quad(int32_t vx[4], int32_t vy[4]) {
|
||||
static int ws_auto_ui_transform_rect(int32_t *x, int32_t *y, int *w, int *h) {
|
||||
if (!x || !y || !w || !h || *w <= 0) return 0;
|
||||
int32_t explicit_anchor;
|
||||
if (ws_projection_hud_anchor(&explicit_anchor)) {
|
||||
*x = ws_scale_about(*x, explicit_anchor);
|
||||
*w = (int)ws_scale_len(*w);
|
||||
int explicit_projected = 0;
|
||||
if (ws_projection_hud_anchor_ex(&explicit_anchor, &explicit_projected)) {
|
||||
if (explicit_projected) {
|
||||
*x += ws_projected_hud_shift(explicit_anchor);
|
||||
} else {
|
||||
*x = ws_scale_about(*x, explicit_anchor);
|
||||
*w = (int)ws_scale_len(*w);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (ws_nw_explicit_hud_delta(NULL) || psx_ws_prim_is_tagged())
|
||||
|
||||
@@ -17,6 +17,7 @@ extern "C" int psx_netplay_active(void);
|
||||
|
||||
extern "C" uint32_t psx_read_word(uint32_t addr);
|
||||
extern "C" int gpu_ws_precise_nclip_enabled(void);
|
||||
extern "C" void gpu_ws_note_gte_sxy(int32_t x, int32_t y);
|
||||
/* overlay_loader.c: nonzero while an overlay shadow diff (interpreter record
|
||||
* plus native replay of one candidate) is running. */
|
||||
extern "C" int psx_overlay_shadow_diff_active(void);
|
||||
@@ -1259,6 +1260,8 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0, uint32_t instr)
|
||||
int64_t sx = sx16 >> 16;
|
||||
int64_t sy = sy16 >> 16;
|
||||
gte->push_sxy(sx, sy);
|
||||
gpu_ws_note_gte_sxy((int16_t)(gte->SXY[2] & 0xFFFF),
|
||||
(int16_t)((uint32_t)gte->SXY[2] >> 16));
|
||||
if (!s_gte_replay_sandbox) {
|
||||
/* Bound the 16.16 transport so an extreme projection cannot wrap an
|
||||
* int32 and masquerade as a valid on-screen shadow. */
|
||||
|
||||
@@ -3469,6 +3469,12 @@ extern "C" void psx_mod_tag_hud_primitive(uint32_t primitive, int edge) {
|
||||
extern "C" void psx_mod_anchor_hud_primitive(uint32_t primitive, int edge) {
|
||||
gpu_ws_tag_hud_prim(primitive, edge);
|
||||
}
|
||||
extern "C" int psx_mod_gte_sxy_recent(int32_t x, int32_t y) {
|
||||
return gpu_ws_gte_sxy_recent(x, y);
|
||||
}
|
||||
extern "C" void psx_mod_anchor_hud_primitive_projected(uint32_t primitive, int edge) {
|
||||
gpu_ws_tag_hud_prim_projected(primitive, edge);
|
||||
}
|
||||
extern "C" void psx_mod_tag_screen_mask_quad(uint32_t primitive) {
|
||||
gpu_ws_tag_screen_mask_quad(primitive);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ extern "C" void gte_test_execute_reference(CPUState *cpu, uint32_t cmd);
|
||||
|
||||
/* gte.cpp runtime dependencies that are irrelevant to register-transfer tests. */
|
||||
extern "C" int gpu_ws_present_native_43(void) { return 0; }
|
||||
extern "C" void gpu_ws_note_gte_sxy(int32_t, int32_t) {}
|
||||
static int g_test_precise_nclip_enabled;
|
||||
static int g_test_render_projective;
|
||||
extern "C" int gpu_render_projective_enabled(void) { return g_test_render_projective; }
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#include "ws_gte_sxy.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void) {
|
||||
WsGteSxy *s = (WsGteSxy *)calloc(1, sizeof *s);
|
||||
assert(s);
|
||||
|
||||
/* Nothing recorded yet, including at the zero cell. */
|
||||
assert(!ws_gte_sxy_recent(s, 10u, 100, 50));
|
||||
assert(!ws_gte_sxy_recent(s, 0u, -512, -256));
|
||||
|
||||
ws_gte_sxy_note(s, 10u, 397, 58);
|
||||
ws_gte_sxy_note(s, 10u, -20, -8);
|
||||
assert(ws_gte_sxy_recent(s, 10u, 397, 58));
|
||||
assert(ws_gte_sxy_recent(s, 10u, -20, -8));
|
||||
assert(!ws_gte_sxy_recent(s, 10u, 398, 58));
|
||||
assert(!ws_gte_sxy_recent(s, 10u, 397, 59));
|
||||
|
||||
/* A packet built in one vblank and resubmitted for the next several
|
||||
* (the ordering table is rebuilt every fourth vblank) still matches. */
|
||||
for (uint32_t f = 10u; f <= 10u + WS_GTE_SXY_WINDOW; ++f)
|
||||
assert(ws_gte_sxy_recent(s, f, 397, 58));
|
||||
assert(!ws_gte_sxy_recent(s, 10u + WS_GTE_SXY_WINDOW + 1u, 397, 58));
|
||||
|
||||
/* A fresh projection of the same position refreshes it. */
|
||||
ws_gte_sxy_note(s, 16u, 397, 58);
|
||||
assert(ws_gte_sxy_recent(s, 20u, 397, 58));
|
||||
|
||||
/* A reader behind the stamp (render thread lag) does not match. */
|
||||
assert(!ws_gte_sxy_recent(s, 3u, 397, 58));
|
||||
|
||||
/* The 15-bit stamp wraps without turning ancient entries into hits. */
|
||||
ws_gte_sxy_note(s, 32767u, 1, 1);
|
||||
assert(ws_gte_sxy_recent(s, 32767u, 1, 1));
|
||||
assert(ws_gte_sxy_recent(s, 32768u + 2u, 1, 1));
|
||||
assert(!ws_gte_sxy_recent(s, 32768u + 100u, 1, 1));
|
||||
|
||||
free(s);
|
||||
puts("ws_gte_sxy_test: PASS");
|
||||
return 0;
|
||||
}
|
||||
@@ -65,6 +65,40 @@ int main(void) {
|
||||
assert(!ws_hud_anchor_lookup(tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
0x80010008u, packet, 4u, 20u, &anchor));
|
||||
|
||||
/* GTE-projected tags keep their edge (native-wide still translates by it)
|
||||
* and carry the projected flag; plain tags do not. */
|
||||
{
|
||||
int projected = 7;
|
||||
ws_hud_anchor_insert_projected(tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
0x80010004u, 5, &guard, 20u);
|
||||
anchor = 0;
|
||||
assert(ws_hud_anchor_lookup_result_ex(
|
||||
tags, WS_HUD_ANCHOR_TABLE_SIZE, 0x80010004u, packet, 4u,
|
||||
20u, &anchor, &projected) == WS_HUD_ANCHOR_HIT);
|
||||
assert(anchor == 1 && projected == 1);
|
||||
ws_hud_anchor_insert_projected(tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
0x80010004u, -3, &guard, 20u);
|
||||
assert(ws_hud_anchor_lookup_result_ex(
|
||||
tags, WS_HUD_ANCHOR_TABLE_SIZE, 0x80010004u, packet, 4u,
|
||||
20u, &anchor, &projected) == WS_HUD_ANCHOR_HIT);
|
||||
assert(anchor == -1 && projected == 1);
|
||||
assert(ws_hud_anchor_native_delta(1, 53, anchor) == -53);
|
||||
/* A projected centre tag is not the same as no tag: it must stop
|
||||
* automatic layout from squashing the piece a second time. */
|
||||
ws_hud_anchor_insert_projected(tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
0x80010004u, 0, &guard, 20u);
|
||||
assert(ws_hud_anchor_lookup_result_ex(
|
||||
tags, WS_HUD_ANCHOR_TABLE_SIZE, 0x80010004u, packet, 4u,
|
||||
20u, &anchor, &projected) == WS_HUD_ANCHOR_HIT);
|
||||
assert(anchor == 0 && projected == 1);
|
||||
ws_hud_anchor_insert(tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
0x80010004u, -7, &guard, 20u);
|
||||
assert(ws_hud_anchor_lookup_result_ex(
|
||||
tags, WS_HUD_ANCHOR_TABLE_SIZE, 0x80010004u, packet, 4u,
|
||||
20u, &anchor, &projected) == WS_HUD_ANCHOR_HIT);
|
||||
assert(anchor == -1 && projected == 0);
|
||||
}
|
||||
|
||||
ws_hud_anchor_clear(tags, WS_HUD_ANCHOR_TABLE_SIZE);
|
||||
assert(!ws_hud_anchor_lookup(tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
0x80010004u, packet, 4u, 20u, &anchor));
|
||||
|
||||
Reference in New Issue
Block a user