mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
GPU packet keys keep the enhancement aperture apart from the main RAM it aliases
The UI prepass re-read aperture node payloads through the RAM-folded key, so THPS2's relocated prim arena discarded the HUD squash every frame. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,23 @@ static inline uint32_t psx_mod_gpu_dma_resolve_address_for(
|
||||
return psx_ram_canonical_offset(canonical) & ~3u;
|
||||
}
|
||||
|
||||
/*
|
||||
* Identity of a GPU packet word at `address` (a CPU pointer or a DMA tag):
|
||||
* the allocated enhancement aperture keeps its own physical address, main
|
||||
* RAM folds to its word-aligned live offset. Every source-address key (UI
|
||||
* prepass nodes and items, packet guards, title roles) uses this, so a packet
|
||||
* in the aperture never shares a key with, or reads back, the main RAM its
|
||||
* low bits alias.
|
||||
*/
|
||||
static inline uint32_t psx_gpu_packet_key_for(uint32_t address, uint32_t used) {
|
||||
uint32_t canonical = address & 0x1FFFFFFCu;
|
||||
if (psx_mod_gpu_dma_aperture_offset_for(
|
||||
canonical, 4u, used, (uint32_t *)0))
|
||||
return canonical;
|
||||
return psx_ram_canonical_offset(canonical) & ~3u;
|
||||
}
|
||||
|
||||
uint32_t psx_gpu_packet_key(uint32_t address);
|
||||
uint32_t psx_mod_gpu_dma_memory_alloc(uint32_t size, uint32_t alignment);
|
||||
uint32_t psx_mod_gpu_dma_resolve_address(uint32_t address);
|
||||
uint32_t psx_mod_memory_alloc(uint32_t size, uint32_t alignment);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define PSX_WS_PRIMITIVE_ROLES_H
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "psx_memory.h"
|
||||
#include "mod_memory.h"
|
||||
|
||||
/* Title-supplied packet identity, independent of draw order and sprite tags.
|
||||
* Keys name PsyQ P_TAG headers; GPU lookup names the following command word. */
|
||||
@@ -15,7 +15,7 @@ typedef struct {
|
||||
|
||||
static inline WsPrimitiveRole* ws_role_write(WsPrimitiveRole* tags,
|
||||
uint32_t primitive, uint32_t now) {
|
||||
uint32_t key = psx_ram_canonical_offset(primitive) & ~3u;
|
||||
uint32_t key = psx_gpu_packet_key(primitive);
|
||||
if (!key) return NULL;
|
||||
uint32_t slot = (key >> 2) & (WS_ROLE_BUCKETS - 1u), victim = slot;
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
@@ -36,7 +36,7 @@ static inline WsPrimitiveRole* ws_role_write(WsPrimitiveRole* tags,
|
||||
static inline const WsPrimitiveRole* ws_role_read(const WsPrimitiveRole* tags,
|
||||
uint32_t source, uint32_t now) {
|
||||
if (source == 0xFFFFFFFFu) return NULL;
|
||||
uint32_t key = psx_ram_canonical_offset(source - 4u) & ~3u;
|
||||
uint32_t key = psx_gpu_packet_key(source - 4u);
|
||||
if (!key) return NULL;
|
||||
uint32_t slot = (key >> 2) & (WS_ROLE_BUCKETS - 1u);
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
|
||||
+10
-7
@@ -53,9 +53,10 @@
|
||||
#include <string.h>
|
||||
#include "gpu_timeline.h"
|
||||
|
||||
/* Word-aligned main-RAM key for a primitive/OT address through the live
|
||||
* geometry (retail: the DMAC's 0x1FFFFC fold). */
|
||||
#define GPU_RAM_KEY(a) (psx_ram_canonical_offset(a) & ~3u)
|
||||
/* Packet identity of a primitive/OT address: main RAM through the live
|
||||
* geometry (retail: the DMAC's 0x1FFFFC fold), the enhancement aperture by its
|
||||
* own address (mod_memory.h). */
|
||||
#define GPU_RAM_KEY(a) psx_gpu_packet_key(a)
|
||||
|
||||
extern uint16_t psx_read_half(uint32_t addr);
|
||||
extern uint8_t psx_read_byte(uint32_t addr);
|
||||
@@ -2466,7 +2467,7 @@ void gpu_ws_tag_background_prim(uint32_t prim) {
|
||||
}
|
||||
WsPrepassPacketGuard guard = ws_prepass_packet_guard(words, count);
|
||||
ws_hud_anchor_insert(ws_background_tags, WS_HUD_ANCHOR_TABLE_SIZE,
|
||||
(prim + 4u) & 0x1FFFFCu, 0, &guard,
|
||||
GPU_RAM_KEY((prim + 4u)), 0, &guard,
|
||||
(uint32_t)s_frame_count);
|
||||
ws_background_tags_used = 1;
|
||||
ws_background_tag_frame = (uint32_t)s_frame_count;
|
||||
@@ -6474,8 +6475,8 @@ void gpu_ws_validate_linked_list_header(uint32_t addr, uint32_t header) {
|
||||
void gpu_ws_validate_linked_list_node(uint32_t addr, uint32_t num_words) {
|
||||
if (ws_ui_prepass_count == 0) return;
|
||||
|
||||
uint32_t resolved =
|
||||
GPU_RAM_KEY(psx_mod_gpu_dma_resolve_address(addr));
|
||||
const uint32_t node_addr = psx_mod_gpu_dma_resolve_address(addr);
|
||||
const uint32_t resolved = GPU_RAM_KEY(node_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) {
|
||||
@@ -6489,9 +6490,11 @@ void gpu_ws_validate_linked_list_node(uint32_t addr, uint32_t num_words) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Re-read the payload where the walk reads it (the node's own memory),
|
||||
* never through the key. */
|
||||
uint32_t payload[255];
|
||||
uint32_t first_addr =
|
||||
psx_mod_gpu_dma_resolve_address(resolved + 4u);
|
||||
psx_mod_gpu_dma_resolve_address(node_addr + 4u);
|
||||
for (uint32_t i = 0; i < num_words; i++) {
|
||||
payload[i] = psx_read_word(psx_mod_gpu_dma_resolve_address(
|
||||
first_addr + i * 4u));
|
||||
|
||||
@@ -144,6 +144,10 @@ uint32_t psx_mod_gpu_dma_resolve_address(uint32_t address) {
|
||||
address, mod_gpu_dma_memory_used);
|
||||
}
|
||||
|
||||
uint32_t psx_gpu_packet_key(uint32_t address) {
|
||||
return psx_gpu_packet_key_for(address, mod_gpu_dma_memory_used);
|
||||
}
|
||||
|
||||
/* Exposed for inlined main-RAM load helpers in psx_cyc.h (VLC/decode hot path). */
|
||||
uint8_t *g_psx_ram = ram;
|
||||
/* PSX_LOAD_DELAY gate (default on). −1 = unread; 0/1 after first resolve. */
|
||||
@@ -1355,9 +1359,9 @@ static void mmio_write32(uint32_t addr, uint32_t val) {
|
||||
* 0x80030000, so ROM 0xBFC38B1C executes at 0x80050B1C
|
||||
* (scph1001_relocated_store). */
|
||||
if (debug_cpu_ptr && scph1001_relocated_store(0x80050B1Cu, 0xBFC38B1Cu)) {
|
||||
/* Word-aligned RAM key through the live geometry (retail: the
|
||||
* 0x1FFFFC fold, identical to the DMA/GPU source keys). */
|
||||
src = psx_ram_canonical_offset(debug_cpu_ptr->gpr[4] - 4u) & ~3u;
|
||||
/* The same packet key as the DMA/GPU source keys (retail: the
|
||||
* 0x1FFFFC fold). */
|
||||
src = psx_gpu_packet_key(debug_cpu_ptr->gpr[4] - 4u);
|
||||
}
|
||||
gpu_set_gp0_source(src);
|
||||
gpu_write_gp0(val);
|
||||
|
||||
@@ -54,6 +54,10 @@ int mod_texture_packet_precision(uint32_t source, float q[3], float xy[6]) {
|
||||
}
|
||||
|
||||
static uint32_t test_ram[0x00200000u / 4u];
|
||||
/* The enhancement GPU-DMA aperture (mod_memory.h): a test allocates it by
|
||||
* setting test_aperture_used; reads and keys then follow the real rules. */
|
||||
static uint32_t test_aperture[0x00020000u / 4u];
|
||||
static uint32_t test_aperture_used;
|
||||
|
||||
static struct {
|
||||
int calls;
|
||||
@@ -100,7 +104,9 @@ static inline void gpu_exec_record_triangle(int x0, int y0, int x1, int y1,
|
||||
}
|
||||
|
||||
uint32_t psx_read_word(uint32_t addr) {
|
||||
uint32_t phys = addr & 0x1FFFFFFFu;
|
||||
uint32_t phys = addr & 0x1FFFFFFFu, off;
|
||||
if (psx_mod_gpu_dma_aperture_offset_for(phys, 4u, test_aperture_used, &off))
|
||||
return test_aperture[off / 4u];
|
||||
if (phys <= 0x00200000u - 4u)
|
||||
return test_ram[phys / 4u];
|
||||
return 0;
|
||||
@@ -243,7 +249,12 @@ void color_lut_map555(const ColorLut *lut, uint16_t bgr555,
|
||||
if (g) *g = (uint8_t)(((bgr555 >> 5) & 0x1Fu) << 3);
|
||||
if (b) *b = (uint8_t)(((bgr555 >> 10) & 0x1Fu) << 3);
|
||||
}
|
||||
uint32_t psx_mod_gpu_dma_resolve_address(uint32_t address) { return address; }
|
||||
uint32_t psx_mod_gpu_dma_resolve_address(uint32_t address) {
|
||||
return psx_mod_gpu_dma_resolve_address_for(address, test_aperture_used);
|
||||
}
|
||||
uint32_t psx_gpu_packet_key(uint32_t address) {
|
||||
return psx_gpu_packet_key_for(address, test_aperture_used);
|
||||
}
|
||||
void text_xlate_vram_upload(int x, int y, int w, int h) {
|
||||
(void)x; (void)y; (void)w; (void)h;
|
||||
}
|
||||
|
||||
@@ -250,6 +250,7 @@ void text_xlate_vram_upload(uint16_t x, uint16_t y, uint16_t w, uint16_t h)
|
||||
int ws_cull_should_keep(uint32_t addr) { (void)addr; return 1; }
|
||||
int ws_ui_group_should_keep(uint32_t addr) { (void)addr; return 1; }
|
||||
uint32_t psx_mod_gpu_dma_resolve_address(uint32_t address) { return address; }
|
||||
uint32_t psx_gpu_packet_key(uint32_t address) { return address & 0x001FFFFCu; }
|
||||
void ws_ui_group_assign(WsUiGroupItem *items, size_t count,
|
||||
int32_t display_width, int dense_menu, int in_place)
|
||||
{ (void)items; (void)count; (void)display_width; (void)dense_menu; (void)in_place; }
|
||||
|
||||
@@ -107,6 +107,31 @@ static void add_rear_pieces(const RearPiece *pieces, int count) {
|
||||
test_ram[NODE_GAP / 4u] = NODE_RING;
|
||||
}
|
||||
|
||||
/* The same HUD list in the enhancement GPU-DMA aperture (THPS2's draw-distance
|
||||
* prim arena), with unrelated words in the main RAM its low bits alias. */
|
||||
#define AP_BASE 0x00800000u
|
||||
static void put_node_ap(uint32_t addr, uint32_t next, const uint32_t *words,
|
||||
uint32_t count) {
|
||||
const uint32_t off = (addr - AP_BASE) / 4u;
|
||||
test_aperture[off] = (count << 24) | (next & 0xFFFFFFu);
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
test_aperture[off + 1u + i] = words[i];
|
||||
}
|
||||
static void move_hud_to_aperture(void) {
|
||||
const uint32_t nodes[3] = {NODE_RING, NODE_FILL, NODE_FLAT};
|
||||
test_aperture_used = sizeof test_aperture;
|
||||
memset(test_aperture, 0, sizeof test_aperture);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const uint32_t h = test_ram[nodes[i] / 4u], n = h >> 24;
|
||||
const uint32_t next = (h & 0xFFFFFFu) == 0xFFFFFFu
|
||||
? 0xFFFFFFu : AP_BASE + (h & 0xFFFFFFu);
|
||||
put_node_ap(AP_BASE + nodes[i], next, &test_ram[nodes[i] / 4u + 1u], n);
|
||||
}
|
||||
test_ram[OT_HEAD / 4u] = AP_BASE + NODE_RING;
|
||||
for (uint32_t a = NODE_RING; a < NODE_FLAT + 0x40u; a += 4u)
|
||||
test_ram[a / 4u] = 0xDEAD0000u | a;
|
||||
}
|
||||
|
||||
static void load_packet(uint32_t node, uint32_t count) {
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
gp0_cmd_buf[i] = test_ram[node / 4u + 1u + i];
|
||||
@@ -473,6 +498,39 @@ int main(void) {
|
||||
assert(gpu_exec_triangles.min_x == 50 && gpu_exec_triangles.max_x == 140);
|
||||
}
|
||||
|
||||
/* A list in the aperture validates against its own words at DMA time:
|
||||
* the prepass survives the walk and the fill squashes as in RAM. A RAM
|
||||
* node at the aliased offset is a different node. */
|
||||
reset_state(1);
|
||||
build_hud(60, 128, 64, 120);
|
||||
move_hud_to_aperture();
|
||||
gpu_ws_prepass_linked_list(OT_HEAD);
|
||||
assert(ws_ui_prepass_count == 3);
|
||||
{
|
||||
const uint32_t nodes[3] = {NODE_RING, NODE_FILL, NODE_FLAT};
|
||||
const uint32_t stale = ws_ui_reject.stale;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const uint32_t a = AP_BASE + nodes[i];
|
||||
const uint32_t h = test_aperture[(a - AP_BASE) / 4u];
|
||||
gpu_ws_validate_linked_list_header(a, h);
|
||||
gpu_ws_validate_linked_list_node(a, h >> 24);
|
||||
}
|
||||
assert(ws_ui_reject.stale == stale && ws_ui_prepass_count == 3);
|
||||
gpu_exec_reset_triangles();
|
||||
for (uint32_t i = 0; i < 8; i++)
|
||||
gp0_cmd_buf[i] = test_aperture[(NODE_FILL + 4u) / 4u + i];
|
||||
gp0_cmd_source_addr = AP_BASE + NODE_FILL + 4u;
|
||||
gp0_words_needed = 8;
|
||||
gp0_exec_shaded_quad();
|
||||
const int32_t centre = 60 + (128 - 60) / 2;
|
||||
assert(gpu_exec_triangles.min_x == ws_scale_about(64, centre));
|
||||
assert(gpu_exec_triangles.max_x == ws_scale_about(120, centre));
|
||||
assert(GPU_RAM_KEY(AP_BASE + NODE_FILL) != GPU_RAM_KEY(NODE_FILL));
|
||||
gpu_ws_validate_linked_list_header(NODE_FILL, test_ram[NODE_FILL / 4u]);
|
||||
assert(ws_ui_reject.stale == stale + 1 && ws_ui_stale_why[2] > 0);
|
||||
}
|
||||
test_aperture_used = 0;
|
||||
|
||||
puts("ws_auto_ui_untextured_exec_test: PASS");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user