mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
Widescreen prepass: validate DMA nodes through an address index, not a scan per node
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 1ba6c41595)
This commit is contained in:
+52
-19
@@ -158,6 +158,18 @@ typedef struct {
|
||||
} WsUiPrepassNode;
|
||||
static WsUiPrepassNode ws_ui_prepass_nodes[WS_UI_PREPASS_NODE_MAX];
|
||||
static uint32_t ws_ui_prepass_node_count;
|
||||
/* The DMA walk validates every node it visits against the prepass record:
|
||||
* thousands of nodes per list, so the lookup is a binary search over an
|
||||
* address-sorted index, built on the first lookup after the record changes
|
||||
* (generation), never a scan of the whole record per node. */
|
||||
static uint16_t ws_ui_prepass_order[WS_UI_PREPASS_NODE_MAX];
|
||||
static uint32_t ws_ui_prepass_gen = 1, ws_ui_prepass_order_gen;
|
||||
static uint32_t ws_ui_prepass_order_count;
|
||||
|
||||
static void ws_ui_prepass_nodes_reset(void) {
|
||||
ws_ui_prepass_node_count = 0;
|
||||
ws_ui_prepass_gen++;
|
||||
}
|
||||
|
||||
/* Why a UI-looking primitive did NOT reach the squash partition.
|
||||
*
|
||||
@@ -256,7 +268,7 @@ void gpu_ws_set_auto_ui_in_place(int on) {
|
||||
void gpu_ws_set_auto_ui_squash(int on) {
|
||||
ws_auto_ui_squash = on ? 1 : 0;
|
||||
ws_ui_prepass_count = 0;
|
||||
ws_ui_prepass_node_count = 0;
|
||||
ws_ui_prepass_nodes_reset();
|
||||
ws_ui_prepass_rank = 0xFFFFu;
|
||||
ws_auto_ui_dense = 0;
|
||||
ws_auto_ui_candidate_count = 0;
|
||||
@@ -2947,7 +2959,7 @@ static int32_t ws_hud_pivot(int32_t x, int32_t w) {
|
||||
* axis-aligned, and gives animated glyphs a shared anchor on their first frame. */
|
||||
static void ws_ui_prepass_invalidate_stale(void) {
|
||||
ws_ui_prepass_count = 0;
|
||||
ws_ui_prepass_node_count = 0;
|
||||
ws_ui_prepass_nodes_reset();
|
||||
ws_ui_prepass_rank = 0xFFFFu;
|
||||
ws_auto_ui_dense = 0;
|
||||
ws_ui_reject.stale++;
|
||||
@@ -6298,7 +6310,7 @@ static int ws_ui_attached(const WsUiPrepassItem *a, const WsUiPrepassItem *b) {
|
||||
|
||||
void gpu_ws_prepass_linked_list(uint32_t start_addr) {
|
||||
ws_ui_prepass_count = 0;
|
||||
ws_ui_prepass_node_count = 0;
|
||||
ws_ui_prepass_nodes_reset();
|
||||
ws_ui_prepass_rank = 0xFFFFu;
|
||||
ws_auto_ui_dense = 0;
|
||||
ws_ui_reject.opcode = ws_ui_reject.not_axis = ws_ui_reject.degenerate =
|
||||
@@ -6319,7 +6331,7 @@ void gpu_ws_prepass_linked_list(uint32_t start_addr) {
|
||||
for (;;) {
|
||||
if (safety++ > max_nodes) {
|
||||
ws_ui_prepass_count = 0;
|
||||
ws_ui_prepass_node_count = 0;
|
||||
ws_ui_prepass_nodes_reset();
|
||||
return;
|
||||
}
|
||||
uint32_t header = psx_read_word(addr);
|
||||
@@ -6327,7 +6339,7 @@ void gpu_ws_prepass_linked_list(uint32_t start_addr) {
|
||||
if (ws_ui_prepass_node_count >= WS_UI_PREPASS_NODE_MAX) {
|
||||
ws_ui_reject.cap++;
|
||||
ws_ui_prepass_count = 0;
|
||||
ws_ui_prepass_node_count = 0;
|
||||
ws_ui_prepass_nodes_reset();
|
||||
return;
|
||||
}
|
||||
uint32_t payload[255];
|
||||
@@ -6338,6 +6350,7 @@ void gpu_ws_prepass_linked_list(uint32_t start_addr) {
|
||||
}
|
||||
WsUiPrepassNode *node =
|
||||
&ws_ui_prepass_nodes[ws_ui_prepass_node_count++];
|
||||
ws_ui_prepass_gen++;
|
||||
node->addr = GPU_RAM_KEY(addr);
|
||||
node->header = header;
|
||||
node->payload_guard =
|
||||
@@ -6546,18 +6559,44 @@ void gpu_ws_prepass_linked_list(uint32_t start_addr) {
|
||||
}
|
||||
}
|
||||
|
||||
static int ws_ui_prepass_order_cmp(const void *x, const void *y) {
|
||||
const uint16_t a = *(const uint16_t *)x, b = *(const uint16_t *)y;
|
||||
const uint32_t aa = ws_ui_prepass_nodes[a].addr, ba = ws_ui_prepass_nodes[b].addr;
|
||||
if (aa != ba) return aa < ba ? -1 : 1;
|
||||
return a < b ? -1 : a > b; /* a repeated address: the first record */
|
||||
}
|
||||
|
||||
/* The first prepass record of `resolved`, as the linear scan found it. */
|
||||
static const WsUiPrepassNode *ws_ui_prepass_find_node(uint32_t resolved) {
|
||||
if (ws_ui_prepass_order_gen != ws_ui_prepass_gen ||
|
||||
ws_ui_prepass_order_count != ws_ui_prepass_node_count) {
|
||||
for (uint32_t i = 0; i < ws_ui_prepass_node_count; i++)
|
||||
ws_ui_prepass_order[i] = (uint16_t)i;
|
||||
qsort(ws_ui_prepass_order, ws_ui_prepass_node_count,
|
||||
sizeof ws_ui_prepass_order[0], ws_ui_prepass_order_cmp);
|
||||
ws_ui_prepass_order_gen = ws_ui_prepass_gen;
|
||||
ws_ui_prepass_order_count = ws_ui_prepass_node_count;
|
||||
}
|
||||
uint32_t lo = 0, hi = ws_ui_prepass_order_count;
|
||||
while (lo < hi) {
|
||||
const uint32_t mid = (lo + hi) / 2;
|
||||
if (ws_ui_prepass_nodes[ws_ui_prepass_order[mid]].addr < resolved) lo = mid + 1;
|
||||
else hi = mid;
|
||||
}
|
||||
if (lo < ws_ui_prepass_order_count &&
|
||||
ws_ui_prepass_nodes[ws_ui_prepass_order[lo]].addr == resolved)
|
||||
return &ws_ui_prepass_nodes[ws_ui_prepass_order[lo]];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void gpu_ws_validate_linked_list_header(uint32_t addr, uint32_t header) {
|
||||
if (ws_ui_prepass_count == 0) return;
|
||||
|
||||
uint32_t resolved =
|
||||
GPU_RAM_KEY(psx_mod_gpu_dma_resolve_address(addr));
|
||||
for (uint32_t i = 0; i < ws_ui_prepass_node_count; i++) {
|
||||
if (ws_ui_prepass_nodes[i].addr != resolved) continue;
|
||||
if (ws_ui_prepass_nodes[i].header != header)
|
||||
ws_ui_prepass_invalidate_stale();
|
||||
return;
|
||||
}
|
||||
ws_ui_prepass_invalidate_stale();
|
||||
const WsUiPrepassNode *node = ws_ui_prepass_find_node(resolved);
|
||||
if (!node || node->header != header)
|
||||
ws_ui_prepass_invalidate_stale();
|
||||
}
|
||||
|
||||
void gpu_ws_validate_linked_list_node(uint32_t addr, uint32_t num_words) {
|
||||
@@ -6565,13 +6604,7 @@ void gpu_ws_validate_linked_list_node(uint32_t addr, uint32_t num_words) {
|
||||
|
||||
uint32_t resolved =
|
||||
GPU_RAM_KEY(psx_mod_gpu_dma_resolve_address(addr));
|
||||
const WsUiPrepassNode *node = NULL;
|
||||
for (uint32_t i = 0; i < ws_ui_prepass_node_count; i++) {
|
||||
if (ws_ui_prepass_nodes[i].addr == resolved) {
|
||||
node = &ws_ui_prepass_nodes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
const WsUiPrepassNode *node = ws_ui_prepass_find_node(resolved);
|
||||
if (!node || node->payload_guard.word_count != num_words) {
|
||||
ws_ui_prepass_invalidate_stale();
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user