Skip to content

The ATI and S3 builds ​

2026-10-24, the wrapper's second piece.

The ATI build in a race on Beltane at 1280 × 960

WPODATI.EXE, the ATI 3D Rage build, at the start of a race on Beltane, through our ddraw.dll and ati3dcif.dll, shown at twice its size (here 960 pixels wide).

After the 3dfx build, the next two 3D-card builds were the ATI Rage one (WPODATI.EXE) and the S3 ViRGE one (WPODS3D.EXE). Under Wine neither even started. Wine has no ati3dcif.dll or S3DTKW.dll, and like the software build both stop on DirectDraw's "SetDisplayMode->DDERR_UNSUPPORTED".

lpDD::SetDisplayMode->DDERR_UNSUPPORTED

The ATI build under Wine's own DirectDraw, at its own pixels: the virtual display cannot change mode, so the game stops. (Re-created afterwards for this entry.)

A choice first ​

These builds are not like the 3dfx one. They draw into DirectDraw surfaces at 640 × 480, and the card draws only part of the picture. So there were two roads:

  • make both playable with exact software versions of the card libraries, and scale the finished frame;
  • or replace DirectDraw as well, draw the card's triangles at a higher resolution, and merge in the pixels the game draws itself.

The first road was recommended and taken: it makes both builds playable, and the second road needs a whole DirectDraw replacement for an unknown gain. The question went to the user through the session that coordinates the night's work, and the answer was the first road for both builds, kept apart from the second so the S3 path could be swapped later.

Our own DirectDraw ​

The game asks for an exclusive full-screen 640 × 480 × 16 mode with a back buffer. ddraw.dll grants any mode it asks for and keeps every surface in memory. On each flip it draws the front buffer into the game's window at the size you choose, with GDI. No EXE patch is needed, unlike the software build's run under Wine so far. It is a small COM implementation in Rust. The methods POD calls are real; every other one is a stub that removes its arguments from the stack and says "unsupported", so a wrong guess shows in the log rather than as a crash.

MENU PRINCIPAL from the ATI build at 1280 × 960

The two card libraries ​

A first version of each library only logged what the game gave it and dumped the first vertices. The S3 one failed twice before it logged anything useful:

  • GetState answered 0, so the game took the call as failed and crashed in its clean-up, reading address 0 at 0x4a3926;
  • fixed, the game then stopped with "S3D Virge Library Fail ! S3DTK Error : SetState". Every function in the S3 table must return 1, not 0. The game compares each result with 1, and only S3DTK_InitLib uses 0 for success (observed, the code after each call).

With that, the dumps settled the layouts from the game's own data, without the SDK headers (measured):

  • ATI3DCIF vertices are ten floats (x, y, z, s, t, w, r, g, b, a). The chip draws into the back buffer through a pointer and a pitch of 640 pixels.
  • S3D Toolkit vertices are 32 bytes: four floats, then the colour as four bytes, then three floats. A draw surface is the surface's own address, its width, its height and a format number.
  • The S3 surfaces are 15-bit. The game describes them as S3DTK_VIDEORGB15, the only 16-bit format the ViRGE's 3D engine draws. So for this build our DirectDraw reports 5-5-5 pixels. The game's software renderer reads that and draws 5-5-5 too, and the colours come out right.

Both libraries draw with a small software rasteriser (a crate with its own tests), into the game's surface, as the chips did.

The S3 build in a race on Beltane at 1280 × 960

What the chips actually draw ​

The surprise came from counting. In a race the S3 build made 15,000 TriangleSet calls, every one Gouraud-shaded and none textured, and it never created a texture surface (measured, a counter in the library). The ATI build cannot give its chip a texture at all: it does not import ATI3DCIF_TextureReg (observed). What both chips draw in a race is:

  • the cars' shadows, black shapes at half transparency;
  • the cars' light beams, white fans that fade.

The whole textured track and every car come from the game's own software renderer, the one WINPOD.EXE uses.

The S3 build's light beam beside the car

A light beam from the S3 chip's Gouraud fans, fading to nothing; part of a 1280 × 960 frame, at its own pixels.

The ATI and S3 builds side by side at the start of a race

The ATI build (left) and the S3 build (right) at the start of the same race, each shown at half its 1280 × 960 size. The textured picture is the same software renderer in both.

So the second road, redrawing the chips' work at a higher resolution, would sharpen only shadows and beams. Only the 3dfx build gives its whole scene to the card, so only its scene can be drawn sharper. That finding went back to the user, and the second road was dropped for both builds: scaling the finished frame is the right answer here.

One fix came from comparing the builds side by side. The ATI build first drew a beam as a solid white block.

A solid white block beside the car in the ATI build

Before the fix: the ATI build's light beam as an opaque white shape, which also covers the speed panel. Shown at 960 pixels wide.

The game gives those vertices colours above 255 (an alpha of 717). The S3 build stores colours as bytes and draws the beam fading, so the ATI library now clamps each vertex's colour before blending. That the Rage did the same is inferred. The block went from the next capture, but that frame had no beam in it. Later (2026-10-24, Gold): the clamp does not fix it; the game gives the ATI chip alphas up to 717 for the beams, and what the Rage did with them is open (Every build of Gold).

How it is known ​

  • The calls, their arguments and return values: observed at the call sites.
  • The vertex and surface layouts, the states set, and that the chips draw only untextured faces in a race: measured through the libraries' logs.
  • That both builds reach a race at 1280 × 960 through our libraries, unchanged: measured, the captures above.
  • That the 16-bit surfaces were 5-5-5 on the real ViRGE, and that the Rage clamped vertex colours: inferred.

What is next ​

  • The same ddraw.dll for WINPOD.EXE and PODMMX.EXE, without patch_winpod.py's display patches.
  • When the S3 build would use its textured face kinds, if ever.
  • Comparing a frame with real hardware, or a reference implementation, for the blending.

Prose CC0 1.0, code Apache 2.0. POD: Planet of Death is © Ubi Soft; none of its files are on this site.