mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 09:59:35 -04:00
Require retained-bank OpenGL policy for trusted co-op sessions
This commit is contained in:
@@ -39,6 +39,17 @@ An optional read-only callback contributes canonical host-owned gameplay state
|
||||
to the existing core CRC; digest logs report this as `mod=`. The callback must
|
||||
exclude pointers, padding, rendering caches and local-only input state.
|
||||
|
||||
`requires_retained_texture_banks` opts a trusted profile into retained GL
|
||||
textures during dual-raster netplay. It requires a working OpenGL backend;
|
||||
software/Vulkan selections and an unavailable bank implementation are rejected
|
||||
before guest boot. Software at 1x remains the authoritative VRAM/GPUREAD/CRC
|
||||
source, independent of GL presentation resolution/filter preferences. Retained
|
||||
bank pixels replace textures only on the GL image; the software authority still
|
||||
samples ordinary VRAM for those commands. This policy follows the X6 co-op
|
||||
renderer behavior and does not provide software retained-bank rendering or
|
||||
prove visual equivalence to authoritative VRAM. Validate gameplay state and
|
||||
GL presentation separately. The flag is part of the session content identity.
|
||||
|
||||
For an offline mode whose host state is not serialized, a trusted plugin can
|
||||
call `psx_mod_set_savestate_blocked(1)` and `psx_mod_set_rewind_blocked(1)` during
|
||||
activation. These policies reset before each new session; save blocking also
|
||||
|
||||
@@ -25,6 +25,10 @@ typedef struct PSXModNetplayProfile {
|
||||
unsigned fixed_aspect_mask; /* bit 0=4:3, 1=16:9, 2=21:9; zero=no policy */
|
||||
const char *widescreen_plugin_id; /* Optional forced renderer for wide. */
|
||||
PSXModNetplayStateDigest state_digest;
|
||||
/* Require GL retained texture banks. Netplay retains software @1x VRAM
|
||||
* authority, while GL may use immutable banks for presentation. Software
|
||||
* and unsupported GL launches are refused before guest boot. */
|
||||
int requires_retained_texture_banks;
|
||||
} PSXModNetplayProfile;
|
||||
|
||||
int psx_mod_register_netplay_profile(const PSXModNetplayProfile *profile);
|
||||
@@ -43,6 +47,10 @@ int psx_mod_netplay_set_aspect(int index);
|
||||
int psx_mod_netplay_aspect(void);
|
||||
int psx_mod_netplay_has_state_digest(void);
|
||||
uint32_t psx_mod_netplay_state_digest(void);
|
||||
int psx_mod_netplay_requires_texture_banks(void);
|
||||
/* Runtime renderer index: 0=software, 1=OpenGL, 2=Vulkan. No profile leaves
|
||||
* existing renderer policy unchanged; call only for a netplay launch. */
|
||||
int psx_mod_netplay_renderer_allowed(int renderer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ if(BUILD_TESTING)
|
||||
target_include_directories(mod_netplay_test PRIVATE include)
|
||||
add_test(NAME mod_netplay_test COMMAND mod_netplay_test)
|
||||
add_test(NAME mod_netplay_wide_test COMMAND mod_netplay_test --wide)
|
||||
add_test(NAME mod_netplay_optional_bank_test COMMAND mod_netplay_test --no-banks)
|
||||
add_executable(savestate_title_policy_test tests/test_savestate_title_policy.c
|
||||
src/mod_netplay.c src/psx_sha256.c)
|
||||
target_include_directories(savestate_title_policy_test PRIVATE include)
|
||||
|
||||
@@ -25,6 +25,10 @@ typedef struct PSXModNetplayProfile {
|
||||
unsigned fixed_aspect_mask; /* bit 0=4:3, 1=16:9, 2=21:9; zero=no policy */
|
||||
const char *widescreen_plugin_id; /* Optional forced renderer for wide. */
|
||||
PSXModNetplayStateDigest state_digest;
|
||||
/* Require GL retained texture banks. Netplay retains software @1x VRAM
|
||||
* authority, while GL may use immutable banks for presentation. Software
|
||||
* and unsupported GL launches are refused before guest boot. */
|
||||
int requires_retained_texture_banks;
|
||||
} PSXModNetplayProfile;
|
||||
|
||||
int psx_mod_register_netplay_profile(const PSXModNetplayProfile *profile);
|
||||
@@ -43,6 +47,10 @@ int psx_mod_netplay_set_aspect(int index);
|
||||
int psx_mod_netplay_aspect(void);
|
||||
int psx_mod_netplay_has_state_digest(void);
|
||||
uint32_t psx_mod_netplay_state_digest(void);
|
||||
int psx_mod_netplay_requires_texture_banks(void);
|
||||
/* Runtime renderer index: 0=software, 1=OpenGL, 2=Vulkan. No profile leaves
|
||||
* existing renderer policy unchanged; call only for a netplay launch. */
|
||||
int psx_mod_netplay_renderer_allowed(int renderer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@
|
||||
#include "gpu_gl_renderer.h"
|
||||
#include "gpu_hd_textures.h"
|
||||
#include "mod_texture_banks.h"
|
||||
#include "mod_netplay.h"
|
||||
#include "frame_interpolation.h"
|
||||
#include "render_pass_plan.h"
|
||||
#include "render_pass.h"
|
||||
@@ -5218,7 +5219,13 @@ static int init_gpu_raster(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int gl_renderer_texture_banks_supported(void) { return s_raster_ok && !s_cpu_auth_dual && !s_hd_native_authority; }
|
||||
int gl_renderer_texture_banks_supported(void) {
|
||||
/* A trusted title's bank pixels affect GL presentation only. Keep the
|
||||
* existing software @1x VRAM authority during its delay-sync session. */
|
||||
return s_raster_ok && !s_hd_native_authority &&
|
||||
(!s_cpu_auth_dual || (psx_mod_netplay_is_active() &&
|
||||
psx_mod_netplay_requires_texture_banks()));
|
||||
}
|
||||
|
||||
int gl_renderer_fit_wide_aspect(int disp_w, int *num, int *den) {
|
||||
/* No sync point: pure arithmetic on limits that change only while the
|
||||
|
||||
@@ -18047,6 +18047,11 @@ session_reboot:
|
||||
}
|
||||
memory_init(bios_path_str.c_str());
|
||||
if (g_headless_opengl) g_video_renderer = 1;
|
||||
if (net_cfg.enabled && !psx_mod_netplay_renderer_allowed(g_video_renderer)) {
|
||||
std::fprintf(stderr, "psxrecomp: this title's netplay profile requires OpenGL "
|
||||
"retained textures; select the OpenGL renderer\n");
|
||||
return 1;
|
||||
}
|
||||
#ifndef PSX_HAVE_VULKAN
|
||||
/* Vulkan was not compiled in (PSX_ENABLE_VULKAN=OFF or no SDK found).
|
||||
* Refuse a vulkan request from ANY source (config / CLI / launcher seed)
|
||||
@@ -18884,6 +18889,12 @@ session_reboot:
|
||||
log_present_cadence();
|
||||
}
|
||||
|
||||
if (net_cfg.enabled && psx_mod_netplay_requires_texture_banks() &&
|
||||
!psx_mod_texture_banks_supported()) {
|
||||
std::fprintf(stderr, "psxrecomp: this title's netplay profile requires "
|
||||
"OpenGL retained texture banks; the backend is unavailable\n");
|
||||
return 1;
|
||||
}
|
||||
/* Register vblank presentation callback. */
|
||||
gpu_set_vblank_callback(sdl_vblank_present);
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ int psx_mod_netplay_content_identity(const char *execution_content, char out[65]
|
||||
char normalized[65];
|
||||
size_t n = strlen(input), i;
|
||||
psx_sha256_ctx ctx;
|
||||
uint8_t bytes[32], policy[5];
|
||||
uint8_t bytes[32], policy[6];
|
||||
static const char domain[] = "psx-trusted-netplay-profile-v1";
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
const char *renderer;
|
||||
@@ -75,6 +75,7 @@ int psx_mod_netplay_content_identity(const char *execution_content, char out[65]
|
||||
policy[2] = title_profile->rollback_supported != 0;
|
||||
policy[3] = title_profile->savestates_supported != 0;
|
||||
policy[4] = title_profile->skip_bios_intro != 0;
|
||||
policy[5] = title_profile->requires_retained_texture_banks != 0;
|
||||
psx_sha256_init(&ctx);
|
||||
psx_sha256_update(&ctx, (const uint8_t*)domain, sizeof(domain));
|
||||
psx_sha256_update(&ctx, (const uint8_t*)normalized, n + 1);
|
||||
@@ -105,3 +106,9 @@ int psx_mod_netplay_has_state_digest(void) {
|
||||
uint32_t psx_mod_netplay_state_digest(void) {
|
||||
return psx_mod_netplay_has_state_digest() ? title_profile->state_digest() : 0u;
|
||||
}
|
||||
int psx_mod_netplay_requires_texture_banks(void) {
|
||||
return title_profile && title_profile->requires_retained_texture_banks != 0;
|
||||
}
|
||||
int psx_mod_netplay_renderer_allowed(int renderer) {
|
||||
return !psx_mod_netplay_requires_texture_banks() || renderer == 1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
/* Existing GL fixtures deliberately run without any external pack. The HD
|
||||
* fixture links the real session adapter instead. */
|
||||
/* These renderer fixtures also run without a trusted title profile. */
|
||||
static int test_title_profile_active, test_title_profile_banks;
|
||||
int psx_mod_netplay_is_active(void) { return test_title_profile_active; }
|
||||
int psx_mod_netplay_requires_texture_banks(void) { return test_title_profile_banks; }
|
||||
#ifndef PSX_TEST_HD_TEXTURE_PACK
|
||||
int gpu_hd_textures_acquire_draw(uint16_t tp, uint16_t cx, uint16_t cy,
|
||||
const int limits[4], uint32_t window, int semi, GpuHdTextureImage* image) {
|
||||
|
||||
@@ -526,6 +526,29 @@ int main(int argc,char **argv){
|
||||
for(test_full_composite=0;test_full_composite<2;test_full_composite++){verify_wide_overlay_band();verify_wide_overlay_order();}
|
||||
test_full_composite=0;
|
||||
verify_presentation_capture();
|
||||
/* A trusted retained-bank profile permits GL presentation in delay-sync,
|
||||
* while the software authority still samples the original guest texture. */
|
||||
gl_renderer_sync_cpu();
|
||||
gl_renderer_set_cpu_auth_dual(1);
|
||||
check(!gl_renderer_texture_banks_supported(),"dual banks refused without profile");
|
||||
test_title_profile_banks=1;
|
||||
check(!gl_renderer_texture_banks_supported(),"dual banks refused for inactive profile");
|
||||
test_title_profile_active=1;
|
||||
check(gl_renderer_texture_banks_supported(),"trusted dual retained-bank profile allowed");
|
||||
test_title_profile_banks=0;
|
||||
check(!gl_renderer_texture_banks_supported(),"active profile must explicitly request banks");
|
||||
test_title_profile_banks=1;
|
||||
s_hd_native_authority=1;
|
||||
check(!gl_renderer_texture_banks_supported(),"HD authority still refuses retained banks");
|
||||
s_hd_native_authority=0;
|
||||
glb_set_draw_area(0,0,1023,511);
|
||||
glb_vram_write(0,0,0x7c00);
|
||||
check(gl_renderer_select_texture_bank(7),"trusted dual bank selected");
|
||||
glb_draw_shaded_textured_triangle(100,300,0,0,0x808080,132,300,0,0,0x808080,100,332,0,0,0x808080,0,0,0x100,1);
|
||||
check(gl_renderer_select_texture_bank(0),"reset trusted dual bank");
|
||||
check(glb_vram_read(102,302)==0x7c00,"dual software authority samples ordinary VRAM");
|
||||
test_title_profile_active=test_title_profile_banks=0;
|
||||
gl_renderer_set_cpu_auth_dual(0);
|
||||
printf("checks=%d failures=%d\n",checks,failures);
|
||||
gl_renderer_shutdown();SDL_DestroyWindow(win);SDL_Quit();return failures?1:0;
|
||||
}
|
||||
|
||||
@@ -48,9 +48,10 @@ int main(int argc, char** argv) {
|
||||
NetplayCoreParts baseline, live, changed;
|
||||
PSXModNetplayProfile invalid, duplicate;
|
||||
static PSXModNetplayProfile profile = {
|
||||
"test.simulation", "test-coop-delay-v1", 0, 0, 1, 1u, NULL, gameplay_digest
|
||||
"test.simulation", "test-coop-delay-v1", 0, 0, 1, 1u, NULL, gameplay_digest, 1
|
||||
};
|
||||
const int wide = argc > 1 && strcmp(argv[1], "--wide") == 0;
|
||||
const int no_banks = argc > 1 && strcmp(argv[1], "--no-banks") == 0;
|
||||
uint32_t original_session, profile_session;
|
||||
char original_identity[65], current_identity[65];
|
||||
const char* fingerprint =
|
||||
@@ -60,6 +61,9 @@ int main(int argc, char** argv) {
|
||||
netplay_core_digest_parts(&cpu, &baseline);
|
||||
assert(baseline.mod == 0);
|
||||
assert(psx_mod_netplay_rollback_supported());
|
||||
assert(!psx_mod_netplay_requires_texture_banks());
|
||||
assert(psx_mod_netplay_renderer_allowed(0) && psx_mod_netplay_renderer_allowed(1) &&
|
||||
psx_mod_netplay_renderer_allowed(2));
|
||||
assert(psx_mod_netplay_savestates_supported());
|
||||
assert(strcmp(psx_mod_netplay_version("dev"), "dev") == 0);
|
||||
assert(psx_mod_netplay_session_id(123) == 123);
|
||||
@@ -81,11 +85,17 @@ int main(int argc, char** argv) {
|
||||
invalid.fixed_aspect_mask = 8u;
|
||||
assert(!psx_mod_register_netplay_profile(&invalid));
|
||||
if (wide) profile.fixed_aspect_mask = 6u;
|
||||
if (no_banks) profile.requires_retained_texture_banks = 0;
|
||||
assert(psx_mod_register_netplay_profile(&profile));
|
||||
assert(psx_mod_register_netplay_profile(&profile));
|
||||
duplicate = profile;
|
||||
assert(!psx_mod_register_netplay_profile(&duplicate));
|
||||
assert(!psx_mod_netplay_rollback_supported());
|
||||
assert(psx_mod_netplay_requires_texture_banks() == !no_banks);
|
||||
assert(psx_mod_netplay_renderer_allowed(1));
|
||||
assert(psx_mod_netplay_renderer_allowed(0) == no_banks);
|
||||
assert(psx_mod_netplay_renderer_allowed(2) == no_banks);
|
||||
assert(psx_mod_netplay_renderer_allowed(-1) == no_banks);
|
||||
assert(psx_mod_netplay_savestates_supported());
|
||||
assert(strcmp(psx_mod_netplay_version("override"), "test-coop-delay-v1") == 0);
|
||||
assert(psx_mod_netplay_aspect() == (wide ? 1 : 0));
|
||||
|
||||
@@ -1755,7 +1755,7 @@ int main(int argc, char** argv) {
|
||||
static const PSXModNetplayProfile profile = {
|
||||
missing_profile ? "runtime.missing-simulation" : "runtime.test-vblank",
|
||||
"runtime-coop-delay-v1", 0, 0, 1, 3u,
|
||||
"runtime.missing-renderer", nullptr
|
||||
"runtime.missing-renderer", nullptr, 0
|
||||
};
|
||||
check(psx_mod_register_netplay_profile(&profile), "trusted profile registration");
|
||||
check(PSXRecompV4::mod_runtime_initialize(root, "SLUS-RUNTIME", 0x80002000, {}, &error),
|
||||
|
||||
@@ -57,7 +57,7 @@ int main(void) {
|
||||
CPUState cpu;
|
||||
const uint8_t blob = 1;
|
||||
static const PSXModNetplayProfile profile = {
|
||||
"test.simulation", "test-no-snapshots-v1", 0, 0, 0, 1u, NULL, NULL
|
||||
"test.simulation", "test-no-snapshots-v1", 0, 0, 0, 1u, NULL, NULL, 0
|
||||
};
|
||||
memset(&cpu, 0, sizeof(cpu));
|
||||
s_configured = 1; /* No configure/filesystem needed to test request staging. */
|
||||
|
||||
Reference in New Issue
Block a user