Qualify remaining shaded polygon command variants

(cherry picked from commit 086a513fb1)
This commit is contained in:
Pegasus
2026-09-09 22:30:22 +03:00
committed by Alexandros Mandravillis
parent 942ad0a210
commit 2bf27fe2c1
10 changed files with 38200 additions and 2 deletions
+32
View File
@@ -0,0 +1,32 @@
# Source shaded textured polygon variants
Pepsiman's successive 39 and 31 scope stops expose incomplete flag-family
admission. Alongside the confirmed untextured triangle correction, this
fixture qualifies the remaining shaded textured polygon variants 35/36/37
and 3D/3F, retaining already admitted 34/3C/3E as source controls.
The existing renderer and work projector own raw texture, modulation,
semitransparency, Gouraud interpolation, masking, cache and split-quad work.
The production change only admits the qualified opcodes. It does not change
packet lengths, rendering, timing, movie input, geometry guards or reset
policy. These variants were not each independently encountered in the title;
their admission is supported by the complete authored source matrix.
The CPU/MMIO driver seeds mixed transparent, mask-bit and ordinary texture
and destination pixels, then draws two identical polygons without clearing
the texture cache or framebuffer. It covers eight opcodes, three texture
depths, four blend modes, four mask modes, dithering off/on and disjoint or
overlapping texture targets. The source owns all CPU loads/stores and GPU
dispatches. Declared waits restore draw credit between dispatches.
All 1536 complete final VRAM images match stock Octoshock 2.3. The passive
observer preserves each image and authored program identity, and records
4608 command/cache work values across both repeated triangles or split
quads. O0/O2 native fixtures match every image and work value. Before the
admission change, the native fixture stops at 35 after 192 opcode 34 controls.
The golden manifest binds source lineage a15b31a, stock core, passive observer
and authored driver. No retail data or emulator states are included. Run
CTest -R tas_gpu_shaded_texture_family. This is source-profile qualification;
full unchanged Pepsiman and Tekken replays and the Pepsiman ending witness
remain separate acceptance gates. No whole-game or hardware claim follows.
+25
View File
@@ -0,0 +1,25 @@
# Source shaded-triangle family
Pepsiman qualification 16 clears the shaded-quad admission boundary and
matches all 5269 captured source RAM/clock returns. It then stops when the
source-profile table rejects GP0 31 at clock 2982504064. The diagnostic's
draw mode 0218 is context, not an established draw-mode defect.
The renderer and work projector already implement the shared shaded-triangle
flags. This correction admits opcodes 31/32/33 alongside 30. The low bit is
ignored for these untextured commands; bit 1 selects semitransparency. Packet
length, command dispatch, work calculation and rasterization are unchanged.
Other family and lifecycle guards remain in place.
An authored CPU/MMIO fixture covers all four opcodes, four blend modes, four
mask modes and dithering off/on. Its 128 complete final VRAM images match
stock Octoshock 2.3; the passive observer preserves each image and provides
128 command-work values. Both native O0/O2 builds match every image and work
value. All 64 ignored-bit source alias pairs agree. Before correction the
native fixture stops at opcode 31 after the 32 opcode 30 controls.
The fixture manifest binds source lineage a15b31a, stock core, observer and
authored driver. No retail data or emulator states are included. Run CTest
-R tas_gpu_shaded_triangle. Full unchanged Pepsiman and Tekken regressions
remain separate acceptance gates; this is source compatibility evidence,
not a whole-game or PS1 hardware accuracy claim.
+20
View File
@@ -413,6 +413,26 @@ if(BUILD_TESTING)
add_test(NAME tas_gpu_polygon_order_${_mode} COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/../runtime/tests/test_source_gpu_polygon_order_contracts.py
$<TARGET_FILE:tas_gpu_polygon_order_${_mode}>)
add_executable(tas_gpu_shaded_texture_family_${_mode}
../runtime/tests/test_source_gpu_shaded_texture_family.c
../runtime/src/gpu_sw_renderer.c
../runtime/src/gpu_vram_dirty.c ../runtime/src/psx_sha256.c)
target_include_directories(tas_gpu_shaded_texture_family_${_mode} PRIVATE ../runtime/include)
target_compile_options(tas_gpu_shaded_texture_family_${_mode} PRIVATE -${_mode} -UNDEBUG)
target_link_libraries(tas_gpu_shaded_texture_family_${_mode} PRIVATE m)
add_test(NAME tas_gpu_shaded_texture_family_${_mode} COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/../runtime/tests/test_source_gpu_shaded_texture_family_contracts.py
$<TARGET_FILE:tas_gpu_shaded_texture_family_${_mode}>)
add_executable(tas_gpu_shaded_triangle_${_mode}
../runtime/tests/test_source_gpu_shaded_triangle.c
../runtime/src/gpu_sw_renderer.c
../runtime/src/gpu_vram_dirty.c ../runtime/src/psx_sha256.c)
target_include_directories(tas_gpu_shaded_triangle_${_mode} PRIVATE ../runtime/include)
target_compile_options(tas_gpu_shaded_triangle_${_mode} PRIVATE -${_mode} -UNDEBUG)
target_link_libraries(tas_gpu_shaded_triangle_${_mode} PRIVATE m)
add_test(NAME tas_gpu_shaded_triangle_${_mode} COMMAND ${Python3_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/../runtime/tests/test_source_gpu_shaded_triangle_contracts.py
$<TARGET_FILE:tas_gpu_shaded_triangle_${_mode}>)
add_executable(tas_gpu_shaded_quad_${_mode}
../runtime/tests/test_source_gpu_shaded_quad.c
../runtime/src/gpu_sw_renderer.c
@@ -54,9 +54,9 @@ static inline unsigned source_gpu_polygon_stride(unsigned opcode) {
static inline int source_gpu_polygon_supported(unsigned opcode) {
switch(opcode) {
case 0x20:case 0x21:case 0x22:case 0x23:
case 0x28:case 0x29:case 0x2a:case 0x2b:case 0x30:case 0x38:case 0x39:case 0x3a:case 0x3b:
case 0x28:case 0x29:case 0x2a:case 0x2b:case 0x30:case 0x31:case 0x32:case 0x33:case 0x38:case 0x39:case 0x3a:case 0x3b:
case 0x24:case 0x25:case 0x26:case 0x27:case 0x2c:case 0x2d:case 0x2e:case 0x2f:
case 0x34:case 0x3c:case 0x3e:return 1;
case 0x34:case 0x35:case 0x36:case 0x37:case 0x3c:case 0x3d:case 0x3e:case 0x3f:return 1;
default:return 0;
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,34 @@
#include "source_gpu_command_projection.h"
#include "gpu_sw_renderer.h"
#include "psx_sha256.h"
#include <stdio.h>
#include <stdlib.h>
static uint16_t vram[1024*512];
int g_ws_bd_stretch_on,g_ws_bd_stretch_pct;
int psx_ws_prim_in_backdrop(void){abort();}
static void run(unsigned op,unsigned mode,unsigned blend,unsigned mask,unsigned dither,unsigned feedback) {
memset(vram,0,sizeof(vram));unsigned ty=feedback?0:256,page=(feedback?0:16)|(mode<<7)|(blend<<5);
for(unsigned y=0;y<128;y++)for(unsigned x=0;x<256;x++)vram[(y+ty)*1024+x]=(uint16_t)((x+y)%13==0?0:((x*37+y*113+1)&32767)|((x&1)?32768:0));
for(unsigned i=0;i<256;i++)vram[400*1024+i]=(uint16_t)(i%17==0?0:((i*97+1)&32767)|((i&1)?32768:0));
if(!feedback)for(unsigned y=0;y<32;y++)for(unsigned x=0;x<32;x++)vram[y*1024+x]=(uint16_t)(((x*19+y*57+123)&32767)|((x&1)?32768:0));
int x[]={4,28,8,32},y[]={4,6,20,22};uint32_t colors[]={0xe3942b,0x285eb0,0x76c341,0x8ce921},uv[]={0x0301,0x65f7,0x7d0e,0x165f},words[12];
unsigned vertices=(op&8)?4:3;
for(unsigned i=0;i<vertices;i++){words[i*3]=colors[i]|(i?0:op<<24);words[i*3+1]=(unsigned)x[i]|(unsigned)y[i]<<16;words[i*3+2]=uv[i]|((i==0?400u<<6:i==1?page:0)<<16);}
SourceGPUCommandProjection s;source_gpu_command_cold(&s);s.budget=256;s.clip_x1=1023;s.clip_y1=511;s.mask_bits=mask;s.draw_mode=page|dither<<9;
sw_renderer_init(vram);sw_source_texture_control(0,0);sw_set_draw_area(0,0,1023,511);sw_set_mask_bits(mask&1,!!(mask&2));sw_set_semi_transparency(!!(op&2),blend);
printf("op%02x-m%u-b%u-k%u-d%u-f%u",op,mode,blend,mask,dither,feedback);
for(unsigned pass=0;pass<2;pass++)for(unsigned second=0;second<((op&8)?2u:1u);second++) {
unsigned start=second?9:0,end=second?12:9;
for(unsigned i=start;i<end;i++)if(!source_gpu_command_write(&s,words[i])){fprintf(stderr,"rejected opcode %02X\n",op);exit(1);}
unsigned kind=(op&8)?(second?SOURCE_GPU_DISPATCH_QUAD_SECOND:SOURCE_GPU_DISPATCH_QUAD_FIRST):SOURCE_GPU_DISPATCH_COMMAND;
if(s.dispatch.kind!=kind || s.dispatch.count!=end || s.phase!=((op&8)&&!second?2u:0u) || s.count)abort();
if(!second)sw_source_texture_control(3,page);
SourceGPUTexture texture={0};texture.page=page;texture.clut=400u<<6;texture.raw=!!(op&1);texture.load_clut=!second;
for(unsigned i=0;i<3;i++)texture.uv[i]=uv[i+second];
int extra=-1;if(!sw_draw_source_triangle(x+second,y+second,colors+second,1,dither,0,0,&texture,&extra) || extra<0)abort();
s.budget-=extra;printf(" %d",256-s.budget);
if(!source_gpu_command_update(&s,s.last_update+100000))abort();
}
uint8_t hash[32];psx_sha256_compute((uint8_t*)vram,sizeof(vram),hash);printf(" ");for(unsigned i=0;i<32;i++)printf("%02x",hash[i]);puts("");
}
int main(void){unsigned ops[]={0x34,0x35,0x36,0x37,0x3c,0x3d,0x3e,0x3f};for(unsigned o=0;o<8;o++)for(unsigned m=0;m<3;m++)for(unsigned b=0;b<4;b++)for(unsigned k=0;k<4;k++)for(unsigned d=0;d<2;d++)for(unsigned f=0;f<2;f++)run(ops[o],m,b,k,d,f);return 0;}
@@ -0,0 +1,13 @@
"""Full stock shaded textured polygon images and source command/cache work."""
from pathlib import Path
import json,os,subprocess,sys
fixture=json.loads(Path(__file__).with_name('source_gpu_shaded_texture_family_fixtures.json').read_text())
env={k:v for k,v in os.environ.items() if not k.startswith('PSX_')}
result=subprocess.run([sys.argv[1]],capture_output=True,text=True,env=env,timeout=90)
assert result.returncode==0,(result.returncode,result.stderr)
rows=[line.split() for line in result.stdout.splitlines()]
assert len(rows)==len(fixture['cases'])==1536
for row,case in zip(rows,fixture['cases']):
assert row[0]==case['case'] and [int(x) for x in row[1:-1]]==case['work'] and row[-1]==case['source_vram_sha256'],(row,case)
assert sum(len(x['work']) for x in fixture['cases'])==4608
print('1536 stock complete VRAM images and 4608 command/cache work values PASS')
@@ -0,0 +1,22 @@
#include "source_gpu_command_projection.h"
#include "gpu_sw_renderer.h"
#include "psx_sha256.h"
#include <stdio.h>
#include <stdlib.h>
static uint16_t vram[1024*512];
int g_ws_bd_stretch_on,g_ws_bd_stretch_pct;
int psx_ws_prim_in_backdrop(void){abort();}
static void run(unsigned op,unsigned blend,unsigned mask,unsigned dither) {
memset(vram,0,sizeof(vram));
for(unsigned y=0;y<32;y++)for(unsigned x=0;x<32;x++)vram[y*1024+x]=(uint16_t)(((x*37+y*113+1)&32767)|((x&1)?32768:0));
int x[]={4,28,8},y[]={4,6,20};uint32_t colors[]={0xe3942b,0x285eb0,0x76c341},words[6];
for(unsigned i=0;i<3;i++){words[i*2]=colors[i]|(i?0:op<<24);words[i*2+1]=(unsigned)x[i]|(unsigned)y[i]<<16;}
SourceGPUCommandProjection s;source_gpu_command_cold(&s);s.budget=256;s.clip_x1=1023;s.clip_y1=511;s.mask_bits=mask;s.draw_mode=blend<<5|dither<<9;
for(unsigned i=0;i<6;i++)if(!source_gpu_command_write(&s,words[i])){fprintf(stderr,"rejected opcode %02X\n",op);exit(1);}
if(s.dispatch.kind!=SOURCE_GPU_DISPATCH_COMMAND || s.dispatch.count!=6 || s.phase || s.count)abort();int work=256-s.budget;
sw_renderer_init(vram);sw_source_texture_control(0,0);sw_set_draw_area(0,0,1023,511);sw_set_mask_bits(mask&1,!!(mask&2));sw_set_semi_transparency(!!(op&2),blend);
int extra=-1;if(!sw_draw_source_triangle(x,y,colors,1,dither,0,0,NULL,&extra) || extra)abort();
uint8_t hash[32];psx_sha256_compute((uint8_t*)vram,sizeof(vram),hash);
printf("op%02x-b%u-k%u-d%u %d ",op,blend,mask,dither,work);for(unsigned i=0;i<32;i++)printf("%02x",hash[i]);puts("");
}
int main(void){for(unsigned op=0x30;op<=0x33;op++)for(unsigned b=0;b<4;b++)for(unsigned k=0;k<4;k++)for(unsigned d=0;d<2;d++)run(op,b,k,d);return 0;}
@@ -0,0 +1,12 @@
"""Original-source shaded triangle complete images and command work."""
from pathlib import Path
import json,os,subprocess,sys
fixture=json.loads(Path(__file__).with_name('source_gpu_shaded_triangle_fixtures.json').read_text())
env={k:v for k,v in os.environ.items() if not k.startswith('PSX_')}
result=subprocess.run([sys.argv[1]],capture_output=True,text=True,env=env,timeout=90)
assert result.returncode==0,(result.returncode,result.stderr)
rows=[line.split() for line in result.stdout.splitlines()]
assert len(rows)==len(fixture['cases'])==128
for row,case in zip(rows,fixture['cases']):
assert row[0]==case['case'] and int(row[1])==case['work'] and row[2]==case['source_vram_sha256'],(row,case)
print('128 stock complete VRAM images and 128 command work values PASS')