Enhancements plan¶
The design for the Enhancements milestone: what each graphics option means for The Warriors, whether it is possible, how Coney's renderer would do it, what it depends on, a rough cost and the order. The rule stands: the faithful look first. Nothing here is built before graphics research and the faithful renderer are complete. This page only records which choices to make now so that the options stay cheap later. Every option ships off by default and is forced off in test mode, so tests and reference screenshots always show the faithful image.
What the game gives us¶
| Fact | Consequence for enhancements | Source |
|---|---|---|
640 × 448 logical screen, interlaced, 4:3; the original also has a 16:9 mode (device flag 0x04) with its own overlay aspect, HUD and menu layouts and _w legal screens |
Widescreen is partly faithful: the original's 16:9 mode is the reference, and only wider shapes are Coney's | Graphics: video mode, HUD: coordinates |
| 30 frames a second (a flip every second vertical blank); simulation on a fixed 1/30 s step | Above 30 fps, drawing must blend the last two steps. Coney already does this | Graphics: frame rate, Update and render |
World is prelit (a baked vertex colour per vertex) plus a 0.227 grey ambient and a few lamps; 88 of level99's 90 lamps light only objects and humans |
No light sources that match the baked light: shadows, AO and RT can only add to it, never replace it | Lighting: world |
| Humans: vertex lighting from a few lights; one faint blob shadow (alpha 128) | Dynamic shadows are an addition. The Xbox build has a GraphicsShadowXBox.cpp (inferred from its path string), so another shipped version may have had better shadows |
Lighting: humans, Xbox assets |
| Every texture is 4- or 8-bit palettised, up to 512²; only 741 of 42,211 have mipmaps; 20,559 of 20,582 distinct textures wrap | Mip generation is the cheapest big quality gain. Upscalers must handle tiling and palettes | Graphics: texture formats |
| Draw distance 115 m with fog from half of it; the PS2 shrinks it when the frame rate drops | The level's mood is in its fog. A longer view changes the look | World: a frame |
| Screen effects: frame-feedback motion blur (looks), a 512 × 256 blur/glow raster, heat distortion, a 9 × 9 distortion grid | The faithful look already needs a render-to-texture post chain | Graphics: screen effects |
The simulation reads the camera: spawners test Camera_AnyPlayerCanSeePoint and place humans outside half the field of view + 10°; scripts set the field of view |
A wider FOV or aspect must never reach the simulation | AI: off screen, AI: out of sight, Cameras |
Coney's renderer today (repo:src/platform/): librw GL3 (OpenGL 3.3 core) through SDL3. The camera draws
straight into the window's back buffer at the window's size. The 4:3 logical screen is fitted inside the window.
Per-atomic lights go to librw's vertex lighting (scene_lighting). The motion blur stand-in copies the back buffer
into a texture (motion_blur_pass, glCopyTexSubImage2D). The decoupled loop (frame_pacer, FrameClock,
Interpolated<T>) already renders above 30 fps. librw itself offers default-framebuffer MSAA
(DEVICESETMULTISAMPLINGLEVELS, set before the window is made), per-texture anisotropy (Texture::maxAnisotropy),
automatic mipmaps (Raster::AUTOMIPMAP), DXT rasters and camera-texture render targets. It also offers a D3D9
backend. It has no Vulkan, D3D12 or Metal backend.
Foundations¶
These are shared by most options. Each one is worth building for the faithful look anyway.
| Id | Foundation | Needed by | Cost |
|---|---|---|---|
| F1 | Display settings in the debug menus, never the game's own options screens (below): a settings store with presets Faithful (all off), Enhanced and Custom; test mode forces Faithful | every option | S |
| F2 | Scene target and post chain: draw 3D into an offscreen colour + depth texture at the internal resolution, then run post passes (each declared in units of time, not frames), then the 2D overlays at window resolution. The faithful 512 × 256 blur, heat distortion and distortion grid need it too | resolution scale, AA, AO, bloom, grading, DOF, CA, MSAA with blur | M |
| F3 | Coney's own shaders for librw materials (render callbacks of Coney's pipelines, as world_atomic already registers one for uninstancing): extra inputs (shadow map, velocity) and outputs (normals) without patching librw |
shadow maps, TAA velocity, alpha-to-coverage, tessellation | M |
| F4 | Texture resolver: every texture load goes through one lookup, PS2 → Xbox → local pack, keyed by (dictionary resource hash, texture name, hash of the PS2 texels), falling back to the PS2 texture on any mismatch | Xbox textures, upscaled packs, player packs, mip generation | M |
| F5 | Backend switch: a Coney renderer over SDL3's GPU API (Vulkan, D3D12, Metal; already in our SDL3, zlib licence), with librw kept only for reading and decoding streams. Raw Vulkan / D3D12 / Metal only for ray tracing | HDR output, ray tracing, long-term macOS (Apple's OpenGL is deprecated and stops at 4.1) | XL |
Choices to make now, at no cost (they keep every option above open):
- The simulation reads only the game's own camera values (field of view, view window, aspect, draw distance). The display's FOV, aspect and render distance are separate render-side values.
- Everything that moves on screen keeps its last two steps (
Interpolated<T>), including particles, objects, bones, scene cameras and fades. A per-frame effect gives its strength per 1/30 s and converts it per frame (s_frame = 1 - (1 - s)^(30 dt)). Today's motion blur lays the last frame over at a fixed per-frame strength, so its trails shorten as the frame rate rises. - No librw draw call outside the platform renderers, and no window-pixel coordinates in game code: 2D stays in GUI or logical units. Then the backend and the screen shape can change under it.
- Record each HUD and menu element's anchor (left, centre or right) and its 16:9 variant while researching the HUD and the front end (HUD lists the 16:9 overwrites as not yet listed).
- Load every texture through one function, so F4 can be added later in one place.
Where the settings live¶
Maintainer decision: the original's menus stay faithful, so every option on this page is a control in Coney's debug menus, on a Display page in both the pad menus and the developer overlay. None goes into the game's options screens.
- The store. Each option is a named, typed, ranged value with a default, registered like a
tunable in a Display category. It is saved in the same overrides file
(
Display/MSAA samples = 4), and a preset writes a set of values at once. Display values are render-side only: the simulation never reads them, so they need no step-boundary queue. They apply on the next frame. A few (MSAA on the default framebuffer, the backend) take effect at the next start, and the page says so. - Settings so far.
Display/Cutscene letterbox(on by default, the original's look): off, a cutscene plays without its black bars. The letterbox still runs underneath, so the HUD and the spinning icons hide during cutscenes as in the original; only the bars are not drawn. Set it on the Tunables page's Display category in the pad menus or the developer overlay, then Save overrides; or putDisplay/Cutscene letterbox = 0in the overrides file (coney-tunables.iniin your config folder, or the file--tunables FILEnames). - Command line. Flags where they help scripted captures and benchmarks:
--preset faithful|enhanced,--render-scale,--aspect,--fov-offsetand--msaa, beside the existing--fps-capand--vsync. A general--set Display/<name>=<value>covers the rest. - Coney natives. The maintainers plan a FiveM-style script API of Coney's own on top of the game's bindings
(Script mods). The store therefore exposes every option by its stable
Display/<name>key, with its type and range, through one get/set interface. A native can read or set an option later without new plumbing. The natives themselves are designed elsewhere.
Options¶
Feasibility: yes, partly (possible with limits or with a visible trade-off), no. Cost: S about a week of one engineer, M 1-3 weeks, L 1-2 months, XL more. Xbox marks options that need the player's Xbox disc (Xbox assets).
Screen and timing¶
| Option | What it means here | Feasible | Approach | Depends on | Cost |
|---|---|---|---|---|---|
| Internal resolution | The PS2 drew 640 × 448. Coney already draws at window resolution | yes | Render scale 0.5-2× on F2's target (above 1 is supersampling), plus a Faithful 640 × 448 mode with nearest or CRT/interlace output (a shader of our own) | F2 | S after F2 |
| 16:9 widescreen | The original's own 16:9 mode: 3D camera constants, overlay aspect 1.6667 at scale 1.1, HUD and menu layouts, _w screens |
yes (faithful) | Implement the 16:9 mode from research. The logical screen becomes 16:9 instead of 4:3 | research: the 16:9 camera constants (0x00122ca0, 0x00122d38), HUD and menu 16:9 tables |
M |
| 21:9, 32:9, any aspect | Coney's own extension | partly | Hor+ (keep the vertical FOV, widen the horizontal); overlay aspect from the window; HUD elements placed by their recorded anchor inside a chosen safe width; cinematics and movies pillarboxed at 16:9 or 4:3 | 16:9 mode, now-choice 4 | M |
| HUD scaling | GUI space is about [0, 1]² with a built-in 5% safe margin | yes | Scale and inset factors applied in the overlay projection (OverlayCamera), not per widget. Fonts are 2D bitmaps: crisp only with upscaled or SDF font textures (see AI upscaling) |
16:9 mode | S |
| FOV | Player camera 65° on 4:3; scripts set and ease it; scene cameras set their own | yes | A render-side offset or scale on the follow camera only, never on scene or locked cameras (composition); the simulation keeps its value (now-choice 1). Streaming already pulls the far clip in on missing scenery | now-choice 1 | S |
| Frame rate above 30 | Simulation stays at 30 Hz (60 Hz physics tick); drawing blends steps | yes (exists) | Done: decoupled loop, --fps-cap, --vsync. Still to do: audit every drawn thing against now-choice 2 (skinned poses blended per bone as rotations, particles, cameras in scenes, UV scrolls); make the motion blur rate-independent; ignore the PS2's low-fps draw-distance shrink (it measures the display rate) |
now-choice 2 | S-M (audit) |
Anti-aliasing and filtering¶
| Option | What it means here | Feasible | Approach | Depends on | Cost |
|---|---|---|---|---|---|
| MSAA | Low-poly scenery, many long straight edges: MSAA suits it well | yes | librw's multisample levels before the window is made, or a multisampled F2 target. The motion blur's copy must then become a resolve (glBlitFramebuffer), since a multisampled buffer cannot be copied into a texture |
F2 for the blur | S |
| Alpha-to-coverage | Chain-link fences, foliage and railings are alpha cut-outs that MSAA does not smooth | yes | GL_SAMPLE_ALPHA_TO_COVERAGE on alpha-tested materials, with coverage-preserving mips (below) |
F3, MSAA | S |
| FXAA / SMAA | Post-process AA; cheap, a little blur | yes | One post pass. SMAA's reference code is MIT; FXAA 3.11's NVIDIA licence is permissive (check at adoption) | F2 | S |
| TAA | Removes shimmer on fences and thin wires; blurs the already soft 64 × 64 textures and ghosts. Needs jitter, velocity and history | partly | Per-object velocity from the two blended steps (we have them); jittered projection. FSR 2/3 (MIT) can be the TAA and upscaler, and stays the default the build ships. DLSS and XeSS are allowed as optional plug-ins beside the GPL code (Decided) | F2, F3; better after F5 | M |
| Anisotropic filtering | Streets and pavements at grazing angles | yes, but only with mips | Texture::maxAnisotropy per texture |
mip generation | S |
| Mip generation | 98% of textures have no mips, so they alias in the distance at any resolution | yes | Build mips at conversion (AUTOMIPMAP, or Coney's own box filter with alpha coverage kept at the alpha-test reference so fences do not vanish); LOD bias as a setting. Changes the look slightly (less shimmer than the PS2) |
F4 (one load path) | S |
Assets and distance¶
| Option | What it means here | Feasible | Approach | Depends on | Cost |
|---|---|---|---|---|---|
| Xbox textures | 612 of 2,027 matched textures at twice the size; the rest are the same images re-encoded to DXT (block artefacts instead of palette banding) | yes, modest gain | Through F4, keyed by resource hash and position. Take the Xbox texture only when it is larger; keep the PS2 one otherwise | Xbox; F4; open questions 1-3 (names, DXT alpha, world textures) | M |
| Xbox HD movies | 15 of 16 movies at 1280 × 720 | yes | Prefer <name>_hd.bik in the movie player; same Bink revision |
Xbox | S |
| Upscaled textures | See AI upscaling | partly | A local pack made by coney-tools, loaded through F4 |
F4; Xbox optional | L |
| Render distance | 115 m far clip with fog from 57.5 m; levels are built for that view | partly | A fog-distance scale and a far-clip scale, separate (raising only the far clip shows nothing new, because fog is full at the far clip). Expect visible level edges and unbuilt backs. A larger render-side Sector Pool keeps more parts loaded, but streaming decisions stay in the simulation and faithful |
world streaming, now-choice 1 | S-M |
| LOD bias | No geometry LODs are known; humans fade beyond a set distance; the d world (decals, fences) streams separately |
partly | Texture LOD bias (after mip generation); human fade distance as a render-side scale. Geometry LOD: nothing to bias (an open research question) | mips; research on object/human fade (0x0050cc64) |
S |
| Tessellation | Characters are low-poly skinned meshes. PN triangles would round silhouettes but crack at hard edges and UV seams, warp faces and clip held objects | partly, low value | CPU subdivision of the bind pose at load (PN or Phong, skin weights interpolated), characters only: no GL 4 tessellation stage needed. SDL3's GPU API has no tessellation stage | F3 | M |
Lighting and post-processing¶
Realtime shadows, per-pixel lighting and reflections have their own design page: Realtime lighting design. The rows below stay the summary.
| Option | What it means here | Feasible | Approach | Depends on | Cost |
|---|---|---|---|---|---|
| Shadow quality | Faithful: a faint 1 m blob under each human. Upgrade: humans and objects casting onto the world | partly | Keep the blob as Faithful. Enhanced: one shadow map from the level's object directional light (level99: the moonlight), casters = humans and objects, receivers multiply their final colour (the world is prelit, so the shadow darkens baked light, a plausible fake). Lamp shadows are not worth it (lamps light only objects) |
F3; check what GraphicsShadowXBox did (Xbox research, optional) |
M |
| SSAO / HBAO / GTAO | Prelit corners probably already hold baked occlusion (inferred). AO helps where nothing is baked: characters' feet, props, parked cars | yes | GTAO from F2's depth (normals rebuilt from depth), at half resolution, low strength, masked off the sky. A fragment-shader version runs on GL 3.3 and macOS | F2 | M |
| Ray tracing | There is no physical light model to trace: the light is baked colour. What is honest: RT shadows and AO for dynamic objects over the baked light; reflections need roughness or wetness data the game does not have (only MatFX environment-map materials hint at shine) | partly (shadows, AO), no (GI or path tracing that keeps the look) | Vulkan ray queries over a TLAS of scenery and humans, added to the GTAO and shadow-map results. RTX Remix is not a route: it hooks fixed-function D3D9, and librw's D3D9 backend uses shaders (inferred) | F5 plus a raw Vulkan / DXR / Metal path; Windows and Linux first | XL |
| Bloom | The original glows through coronas, propglow halos and the unresearched 512 × 256 glow raster. A threshold bloom would also bloom white walls and text |
yes | Bloom only from what Coney knows glows (coronas, glow sprites, lamp sprites) drawn into a bloom buffer, plus an optional low threshold pass | F2; research on device slot +0x108 |
S |
| Motion blur | The original has one: frame feedback, strength set by looks and scripts. Coney's is a stand-in | yes | Keep the faithful blur, rate-independent (now-choice 2). Optional per-object velocity blur from the blended steps; camera blur from depth reprojection | F2, F3 for velocity | S (rate fix), M (velocity) |
| Chromatic aberration | Not in the original; purely cosmetic | yes | One post pass, off by default | F2 | S |
| Colour grading | The game tints through looks (SetLevelColour, stores) and the brightness option; PCSX2 shows the picture about 70% bright, the game's own colours (Graphics) |
yes | A 3D LUT after the game's own looks; presets Faithful TV (the measured brightness) and Neutral | F2; the display-brightness research | S |
| Depth of field | No focus data. Cutscene cameras have targets, the follow camera has a look-at point 1.4 m above the player | partly | Cutscenes only: focus on the scene camera's target; gameplay off (it would blur the street) | F2 | S-M |
| HDR | Content is LDR on the GS scale (0x80 = 1.0); lit colours clamp at 1.0, so there is no overbright range (measured in the graphics research) | partly, low value | HDR10 / scRGB output with a paper-white setting; only additive effects (glows, coronas, bloom) could go above it. Not on OpenGL portably; SDL3's GPU API has HDR swapchains | F5 | S after F5 |
AI upscaling¶
Legal frame. An upscaled texture is derived from the player's disc, and converting or upscaling it does not
change who owns it. It is made on the player's machine by a tool the player runs, written to a directory the player
chooses outside the repository, and never committed or hosted by the project
(LEGAL). Coney ships the tool and the loader, never a pack. coney-tools may download
a model's weights itself (Decided), but the project does not host them. Sharing follows the pack kinds
in Texture packs.
Pipeline (a new coney-tools textures group beside wad extract, refusing output
paths inside the repository as wad extract does):
- Export: decode every distinct texture (20,582, deduplicated by texel hash) to RGBA, with alpha scaled from 0-128 to 0-255. Record its dictionary hash, name, size, addressing and alpha kind (opaque, cut-out, blended), and its sprite-sheet rectangles if it has any. With the Xbox disc, take the Xbox texture as input where it is larger.
- Prepare: for wrap textures, pad by wrapping (otherwise seams appear) and crop afterwards; for clamp textures, replicate the edge. Spread colour into fully transparent texels (otherwise dark halos appear). For 4-bit palettes, optionally run a 1× de-banding or de-dithering model first; for Xbox input, a 1× DXT-artefact model.
- Upscale colour with the chosen model (2× default, 4× optional). Alpha separately: cut-outs get a smooth upscale and a re-threshold; blended alpha gets a smooth upscale.
- Classify exceptions: below 16 px, use a deterministic pixel-art scaler or skip. Fonts and HUD glyphs are never sent to a GAN: use xBRZ or a signed distance field built from the font atlas. Optionally skip textures with lettering (signs, graffiti) by a curated name list.
- Finish: mips with alpha coverage kept, compression (BC7 needs GL 4.2 or
ARB_texture_compression_bptc; librw knows DXT only, so either Coney uploads BC7 itself or the pack stores DXT5 or PNG), and a manifest keyed as F4 expects. The loader rejects any entry whose PS2 texel hash no longer matches.
Candidates (licences as their projects state them; check at adoption, and the maintainer decides):
| Tool or model | Licence | Fit |
|---|---|---|
| Real-ESRGAN (code and official weights) | BSD-3-Clause | Good general baseline; x4plus over-sharpens flat PS2 surfaces |
| Real-ESRGAN-ncnn-vulkan, waifu2x-ncnn-vulkan | MIT | Standalone Vulkan executables on Windows, Linux and macOS: no Python GPU stack for the player; fixed model set |
| spandrel (loads ESRGAN, SwinIR, HAT, DAT, ... in PyTorch) | MIT; non-commercial architectures are kept in a separate package | Best fit for coney-tools (Python): any community model by file |
| Community models (the OpenModelDB catalogue: game-texture, de-dither and de-DXT models) | per model: CC0 and CC-BY to CC-BY-NC-SA | Highest quality for this content; licences vary, so the player supplies the model file |
| chaiNNer (GUI), Upscayl | GPL-3.0, AGPL-3.0 | For players who want to run their own chains on the exported images |
| xBRZ | GPL-3.0 | Deterministic; fonts, UI and tiny textures |
| ONNX Runtime / PyTorch | MIT / BSD-3-Clause | Runtimes |
| Diffusion upscalers, face restoration (GFPGAN, CodeFormer) | mixed, some non-commercial | Not recommended: invented detail, changed faces |
Quality on this content (from the formats; to be measured on the disc):
| Content | Expectation |
|---|---|
| Tiling scenery (brick, asphalt, pavement; 64 × 64 the most common) | Good at 2-4×, once seams are padded. Risk: invented detail that repeats visibly across tiles |
| Palettised gradients (sky, skin, smoke) | GANs sharpen banding into contours unless de-banded first |
| Cut-out alpha (fences, foliage) | Fine with alpha handled separately; wrong without it (fringes, holes) |
| Character textures (faces a few texels across) | Clothes are fine at 2×; faces turn waxy. Use a conservative model, 2× at most, and an opt-out per texture |
| Sprite sheets, HUD, fonts | Per-rectangle padding stops bleeding between rectangles. GANs smear glyphs; use the deterministic route |
| Text in the world (shop signs, graffiti) | Letters get distorted, and graffiti is the game's art style. Opt-out list |
Size and time (rough): the 2,027 matched texture sets alone hold 128 M PS2 pixels. At 4× that is about 2 G pixels: about 8 GB as RGBA, about 2.7 GB as BC7 with mips. So 2× is the default. A GPU upscales 64 × 64 textures in milliseconds each, so the whole disc takes minutes to an hour; on a CPU it takes hours.
Texture packs¶
Coney imports any pack placed in overrides/, keyed by the original texture's hash. An entry
whose hash matches no texture on the player's disc is ignored. coney-tools makes and exports packs as a folder
with a manifest. There are three kinds:
| Kind | Holds | Sharing |
|---|---|---|
| Original art | textures drawn by the pack's author: no game pixels | shared freely, under the author's licence |
| Recipe (the easy default) | the manifest, the model (or where to download it) and the settings; no textures | shared freely; each player rebuilds the pack from their own disc with coney-tools |
| Pre-made upscale | upscaled game textures | players may import one; the project never hosts or links one |
Order¶
| Phase | When | Items |
|---|---|---|
| 0 | Now, during faithful work | The five choices above; F2 as part of the faithful blur and distortion work |
| 1 | Faithful look at 100% | F1; 16:9 mode; any aspect with HUD anchoring; FOV; frame-rate audit with the motion blur fixed; MSAA and alpha-to-coverage; mip generation and anisotropic filtering; render scale and Faithful 640 × 448 |
| 2 | Then | F4; Xbox textures and HD movies (Xbox); player texture packs (roadmap); the upscaling tool; SMAA or FXAA; colour grading; bloom from glow sprites |
| 3 | Then | F3; shadow maps for humans and objects; GTAO; velocity motion blur; cutscene depth of field; chromatic aberration; fog and render distance scales; TAA or FSR |
| 4 | After the whole game | F5 (SDL3 GPU backend); HDR output; ray-traced shadows and AO on Vulkan; character subdivision |
Decided¶
Maintainer decisions (2026-10-07):
- Model weights:
coney-toolsmay download an upscaling model itself. - Proprietary SDKs: DLSS and XeSS are allowed as optional plug-ins next to the GPL-3.0-or-later code.
- Sharing texture packs: Coney helps players share packs, in the model under Packs. It imports from
overrides/by texture hash and exports withconey-tools; recipe packs are the default; pre-made upscales can be imported but are never hosted or linked by the project.
Questions for the maintainer¶
- Which model licences are acceptable for a download that
coney-toolsstarts itself, and may the docs name non-commercial community models?
Research this plan needs¶
One line each; owned by the graphics and HUD engineers.
- The 16:9 mode's 3D camera constants: done (Graphics: video mode). Still
open: the HUD and menu 16:9 tables (
0x00211ef8,0x001af010,0x001cdc80). - Device slot
+0x108and the heat distortion: done (Graphics: motion blur, heat distortion). - The display brightness: done, the dimness is the game's own colours and the capture is faithful (Graphics: open questions, "Display brightness").
- Human and object fade distances and geometry LOD: done, fades only, no geometry LOD (Graphics: distance).
- What
GraphicsShadowXBox.cppdraws (Xbox, optional).