Files
psxrecomp/runtime/include/ws_radial_screen_mask.h
2026-10-18 21:54:58 -07:00

61 lines
2.8 KiB
C

#ifndef PSXRECOMP_WS_RADIAL_SCREEN_MASK_H
#define PSXRECOMP_WS_RADIAL_SCREEN_MASK_H
#include "ws_hud_anchor.h"
#include <math.h>
/* Explicit title-owned screen-space rings, never a world-geometry heuristic.
* Independent packet guards let adjacent solid and feathered ring segments
* share one scale without allowing an arena's next use to inherit the effect. */
/* Match the bounded HUD/producer metadata budget. A 256-entry cache fills
* with just two 128-quad iris buffers; still-fresh prior packet addresses
* then collide with and evict pieces of the mask being submitted. Keep room
* for both buffers and their short-lived predecessors without weakening the
* packet guards or extending tag lifetime. */
#define WS_RADIAL_MASK_TAG_COUNT 4096u
typedef struct {
WsHudAnchorTag packet;
float scale;
} WsRadialScreenMaskTag;
static inline void ws_radial_mask_clear(WsRadialScreenMaskTag *tags) {
for (unsigned i=0; i<WS_RADIAL_MASK_TAG_COUNT; ++i) tags[i].packet.used=0;
}
static inline void ws_radial_mask_insert(WsRadialScreenMaskTag *tags,
uint32_t key, const WsPrepassPacketGuard *guard, uint32_t frame, float scale) {
if (!guard || !isfinite(scale) || scale < 1.f || scale > 64.f) return;
unsigned slot=(key >> 2) & (WS_RADIAL_MASK_TAG_COUNT-1u), victim=slot;
for (unsigned i=0; i<WS_HUD_ANCHOR_PROBES; ++i) {
unsigned j=(slot+i)&(WS_RADIAL_MASK_TAG_COUNT-1u);
WsHudAnchorTag *p=&tags[j].packet;
if (!p->used || p->command_addr==key) { victim=j; break; }
if (frame-p->frame > WS_HUD_ANCHOR_FRESH_FRAMES) victim=j;
}
tags[victim].packet=(WsHudAnchorTag){.command_addr=key, .frame=frame,
.used=1, .guard=*guard};
tags[victim].scale=scale;
}
static inline int ws_radial_mask_lookup(const WsRadialScreenMaskTag *tags,
uint32_t key, const uint32_t *words, uint32_t count, uint32_t frame,
float *scale) {
unsigned slot=(key >> 2)&(WS_RADIAL_MASK_TAG_COUNT-1u);
for (unsigned i=0; i<WS_HUD_ANCHOR_PROBES; ++i) {
const WsRadialScreenMaskTag *t=&tags[(slot+i)&(WS_RADIAL_MASK_TAG_COUNT-1u)];
if (!t->packet.used || t->packet.command_addr != key) continue;
if (frame<t->packet.frame || frame-t->packet.frame>WS_HUD_ANCHOR_FRESH_FRAMES ||
!ws_prepass_packet_matches(&t->packet.guard,words,count)) return 0;
*scale=t->scale;
return 1;
}
return 0;
}
static inline void ws_radial_mask_transform(int32_t x[4], int32_t y[4],
int width, int height, float scale) {
if (width<=0 || height<=0 || !isfinite(scale) || scale<1.f || scale>64.f) return;
double cx=width*.5, cy=height*.5;
for (unsigned i=0; i<4; ++i) {
x[i]=(int32_t)llround(cx+(x[i]-cx)*scale);
y[i]=(int32_t)llround(cy+(y[i]-cy)*scale);
}
}
#endif