Repository navigation
feat(): Tony Hawk's Underground - #118
Merged
Merged
Conversation
…APU path Tony Hawk's Pro Skater 3 (XDK 4039) brought up two gaps. Input: the connected ports were refreshed only inside XGetDevices and XGetDeviceChanges for the gamepad type. THPS3 enumerates memory units only and calls XInputOpen straight away, so every open failed and its menus never read the pad (only its attract demo, much later, got one). XInputOpen now refreshes them too, as the console's USB attaches a pad whether or not the title asked. RECOMP_INPUT_LOG also logs the enumeration calls and failed opens, with the caller. Audio: the native path reads buffer settings in the 4134+ layout, so no buffer of an older DirectSound is ever modelled (settings_in_layout). With no RECOMP_AUDIO/audio setting, such a title now starts on the APU path: THPS3 had its music and no sound effects natively, and every buffer heard there.
With every entry bound in the current frame there was no victim, and each texture after the 512th got the white placeholder. Tony Hawk's Underground's in-engine cutscenes bind about 600; its loading screen's sprites are drawn last, so the screen was pure white between scenes. 512 entries are still kept and evicted as before, so a title whose frames fit sees no change, its per-bind scans included. Only a frame with nothing to evict grows the cache, to at most 4096; the byte budget still bounds what they hold. Also names, once per format, a texture refused for a null or out-of-range Data, which was only a count.
From XDK 5xxx a certificate can ask XAPI to check the security sector as well as the security page: with bit 0 of certificate +0x1D8 set it sends READ DVD STRUCTURE for 0xFF02FDFF and accepts the disc only if the sector carries the certificate's timestamp and a SHA-1 of its first 0x4CB bytes, and its signature verifies. The pass-through refused the command, so Tony Hawk's Underground's unmodified default.xbe rebooted into itself three times and quit; only a hard-drive copy with the check patched out started. A disc image has no security sector, so one is made for the running title: its certificate's timestamp and the digest. The signature already passes (XcVerifyPKCS1Signature returns TRUE).
The combiners' fog register took its alpha from the vertex stage's fog output whether or not fog was enabled. The NV2A's fog unit gives a factor of 1 when it is off (xemu: oFog = 1.0), and a final combiner reads the register either way. Tony Hawk's Underground's skater shaders draw with fog off and compute (1 - fog.a) * FC0 as a per-channel fog weight, with FC0 = 0x80008000: read as fully fogged, the green channel went halfway to the fog colour on every skater, in menus, cutscenes and in-level -- magenta skin, purple clothes.
With 2D kept at 4:3 (CENTRE_2D, widescreen_2d = centre), only a whole-screen pass spans the picture: flat colour, up to 4x4 texels, or the frame itself. A full-screen quad through a colour ramp is the same kind of pass, and stretching it distorts nothing. Tony Hawk's Underground tints its paused game through a 256x4 texture; kept in the box, it left the sides of the picture untinted. A texture at most 8 texels across one way now counts; a real picture over the whole screen still stays at 4:3.
A fixed-point depth compare clamps the reference to the format's range (xemu's, through GL's shadow samplers, does the same), so a receiver past the light's far plane compares as the far plane and a cleared map lights it. Unclamped, every ground polygon Tony Hawk's Underground projects its skater's shadow onto that reached past the shadow camera's range failed the test and came out a solid dark band or triangle.
A title can pair one of its own render target textures with the device's depth. The host cannot when their sizes differ (the device depth is supersampled with the scene, a title's render target texture is not), so the pass gets a scratch depth -- which never saw the clear the title made to the device depth at the start of its frame. The scratch now gets the device depth's latest whole clear before it is used, once per clear. Tony Hawk's Underground draws its menus into a 640x480 texture with the device depth during a menu transition and fades it over the old screen. Against the uncleared scratch every 3D draw failed its depth test: the skater and the city panel were missing, half the screen faded to black, and stale pixels from an earlier transition (the old logo, smeared) showed through. RECOMP_HLE_D3D8_DEVZ_STAND_IN=0 for the old behaviour.
…ets supersampled The XDK's antialiasing filter samples the multisampled back buffer (THUG's is 1280x480, 2x across) into the front buffer in Swap's own body. On the console that writes a buffer of its own; here both are the one scene the host presents, so the pass resampled the picture over itself -- a slight softening each frame, and where the title fades each frame over the last (THUG's menu transitions) a blur that compounded into a smear. A draw that samples the frame buffer at an exact multiple of its target's size is that filter and is not drawn (RECOMP_HLE_D3D8_SKIP_AA_FILTER=0 draws it); a pass over the frame at its own size still draws. A colour render-target texture exactly the title's screen size is now drawn at the scene's supersampling: the texture keeps the title's size, its host image is the scene's. A title that renders a whole frame into a texture no longer drops to console resolution while it does. Viewport, scissor and clear follow the target's host size, its depth must match that size, the screen copy into such a texture fills it at its host size, and uploads of the title's size are not written over what the GPU drew. The device depth fits such a target, so it is shared rather than stood in for. RECOMP_D3D8_SCALE_SCENE_TARGETS=0 keeps every texture at the title's size.
The previous commit rewrote the file with LF endings, which made every line of it a change. Only the screen-copy sizing lines differ now.
…ed alone d3d8_CreateTextureImpl now asks the device whether a render-target texture is supersampled with the scene. The smoke tests that link d3d8_resources.c against stub devices had no such symbol and failed to link on all three CI platforms; their stub answers no, as an unscaled device would.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Toolkit changes for Tony Hawk's Underground (EU,
0x4156002D, XDK 5659). The title project lives in its own repository and builds against this branch.On macOS (Apple Silicon), THUG runs from boot into Story Mode gameplay in New Jersey, with the menus, cutscenes and Create-a-Skater on the way. It holds a native 60 fps (presentation interval 1, about 9 ms of work a frame) and uses its own 16:9 mode, which the console widescreen flag switches on. Sound goes through the native DirectSound path. No crashes and no unresolved indirect calls in two-minute runs.
Changes
The texture cache grows for a frame that binds more than 512 textures (
src/hle/hle_d3d8_texture.c)THUG's in-engine cutscenes bind about 600 distinct textures a frame. The cache held 512 entries and can't evict one bound in the current frame. With no victim left, every texture after the 512th got the white placeholder. The loading screen's sprites are drawn last, so the screen was pure white between scenes.
The disc's security sector read is answered (
src/kernel/kernel_bridge.c)From XDK 5xxx a certificate can ask XAPI to check the security sector as well as the security page. The request is bit 0 of the certificate dword at +0x1D8. With it set, XAPI sends READ DVD STRUCTURE for address 0xFF02FDFF and accepts the disc only if:
The pass-through refused that command. So THUG's unmodified
default.xberebooted into itself three times and quit; only a hard-drive copy with the check patched out would start. A disc image has no security sector, so one is now made for the running title, holding its timestamp and the digest. The signature already passes, becauseXcVerifyPKCS1Signaturereturns TRUE. Both the disc's XBE and the patched hard-drive copy now reach gameplay.The fog register reads no fog while fog is disabled (
src/d3d/d3d8_combiners.c)The combiners took the fog register's alpha from the vertex stage's fog output whether or not fog was on. The NV2A gives a factor of 1 (no fog) when it is disabled, as xemu does, and a final combiner reads the register either way. THUG's skater shaders draw with fog off and use (1 - fog.a) * FC0 as a per-channel fog weight, with FC0 = 0x80008000. Read as fully fogged, the green channel was pulled halfway to the fog colour on every skater: magenta skin and purple clothes, in menus, cutscenes and in-level.
A shadow-map compare clamps its reference to the depth range (
src/d3d/d3d8_combiners.c)A fixed-point depth compare clamps the reference to the format's range; xemu's does too, through GL's shadow samplers. Without the clamp, a receiver past the light's far plane read deeper than any stored depth and failed the test. THUG projects its skater's shadow onto the ground polygons around them, and every polygon reaching past the shadow camera's range came out as a solid dark band or triangle. Now such a receiver compares as the far plane, and a cleared map lights it. This affects any title that samples a shadow map.
A stand-in for the device depth gets the device depth's clear (
src/hle/hle_d3d8.c)A title can pair one of its own render-target textures with the device's depth buffer. The host can't when their sizes differ: the device depth is supersampled with the scene, and a title's render-target texture is not. The pass then gets a scratch depth, which never saw the clear the title made to the device depth at the start of its frame. Now the scratch gets the device depth's latest whole clear before it's used, once per clear.
During a menu change, THUG draws the new menu into a 640x480 texture with the device depth and fades it in over the old screen. Against the uncleared scratch, every 3D draw failed its depth test, so the skater and the city panel were missing. Half the screen faded to black, and stale pixels from an earlier transition showed through as a smeared logo.
RECOMP_HLE_D3D8_DEVZ_STAND_IN=0restores the old behaviour.The XDK's antialiasing filter is not drawn over the frame (
src/hle/hle_d3d8.c,src/hle/hle_d3d8_texture.c)With multisampling on, the XDK's Swap filters the back buffer into a separate front buffer. THUG's back buffer is 1280x480, 2x across. On the host, back and front are the same scene, so the filter pass only resampled the picture over itself. A normal frame came out slightly soft. Menu transitions fade each frame over the last, and there the blur compounded until the menu was a smear. The filter is a draw that samples the frame buffer at an exact multiple of its target's size, and it is now skipped. A pass over the frame at its own size, such as a glow or a colour grade, still draws.
RECOMP_HLE_D3D8_SKIP_AA_FILTER=0restores the old behaviour.A screen-sized render target is supersampled with the scene (
src/d3d/d3d8_device.c,src/d3d/d3d8_resources.c,src/d3d/d3d8_screencopy.c)A colour render-target texture exactly the title's screen size now gets a host image at the scene's supersampling, while the texture keeps the title's size. Before, a title that rendered a whole frame into a texture dropped to console resolution while it did. Several things now follow the target's host size:
Uploads at the title's size are not written over what the GPU drew.
RECOMP_D3D8_SCALE_SCENE_TARGETS=0keeps every texture at the title's size.A full-screen colour ramp is a whole-screen pass in centred 2D (
src/d3d/d3d8_device.c)With 2D kept at 4:3 (
CENTRE_2D,widescreen_2d = centre), only whole-screen passes span the picture: flat colour (up to 4x4 texels) or the frame itself. A full-screen quad through a texture at most 8 texels across one way is a colour ramp, which stretching distorts nothing of. THUG tints its paused game through a 256x4 texture, which in the box left the picture's sides untinted. This only affects titles with centred 2D (Gladius among them).Pads open without enumeration; pre-4134 DirectSound on the APU path
This is the THPS3 commit from
title/thps3, cherry-picked because that branch was never merged:XInputOpenrefreshes which ports are connected. THPS3 opens pads without enumerating them.RECOMP_AUDIO=apu.RECOMP_INPUT_LOGalso logs device enumeration and failed opens.THUG relies on neither of these behaviours. If this merges,
title/thps3doesn't need a PR of its own.Testing
default.xbe(SHA-1408756c0…): boots and plays the same route.RECOMP_FPS_CAP=0): 120 fps on a 120 Hz display.