- Rust 96.1%
- GLSL 3.8%
- Shell 0.1%
In-game, objects and characters rendered untextured and the frame rate fell to 14-40 fps. Every upload_texture created a fresh image plus two descriptor sets (REPEAT + CLAMP), but the pool was created without FREE_DESCRIPTOR_SET and destroy_texture never returned them, so after a few thousand uploads every allocation failed. The error was dropped with `let _ =`, leaving nothing at that address, and the next bind fell back to the 1x1 white texture — which, tinted by the level light, is exactly what an untextured surface looks like. Unreal re-downloads constantly (lightmaps refresh every frame), so the pool ran dry within seconds of loading a save. - The descriptor pool allows freeing and destroy_texture frees both sets. - A re-upload of the same address at the same size overwrites the image in place instead of building a new image and descriptors, guarded by a used_frame stamp so it never touches a texture already bound in the frame being recorded (earlier frames are complete; their fence is waited on). - Failed uploads are logged instead of swallowed. - The perf line reports uploads/frame, upload ms/frame and texmiss/frame, and missing binds are logged per address. Measured in the same saved game: texture misses/frame 275 -> 0, uploads/frame 29-119 -> 3-18, upload 4-55 ms -> 0.7-3.8 ms per frame, 14-96 fps -> 102-145 fps. All six differential harnesses still match nGlide and the glide3x probe passes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BP1ZQb5qhUc1Cx18U2eLhk |
||
|---|---|---|
| crates | ||
| docs | ||
| examples | ||
| .gitignore | ||
| Cargo.toml | ||
| README.md | ||
GlideVK
A native Linux 3dfx Glide → Vulkan translation layer. The Glide equivalent
of DXVK / D7VK:
drop-in glide3x.dll / glide2x.dll that translate Glide calls directly to
Vulkan under Wine — no Direct3D or OpenGL hop.
Status: P3 in progress — real games.
glide2x.dll+glide3x.dllshells over a shared core. Deus Ex (Unreal, glide2x) boots, plays its whole intro and shows its main menu with GlideVK: the Eidos and Ion Storm logos, the flying title, the lit metallic emblem and the menu text all render, and in-game resolution changes switch the (Wine-emulated) display mode like a 3dfx driver did (seedocs/game-compatibility.md). Behaviour is checked against a reference Glide DLL (nGlide) withexamples/run_diff.sh, which found five root causes in a row: Glide's s/t span[0,256)on the major axis, not texels; the blend-factor table was permuted; palettes apply at draw time, not bind time; W-buffer depth comes fromoow, notooz; and thegucombine helpers must expand exactly as the SDK'sgu.cdoes. The Wine smoke tests (examples/run_wine_probe.sh [32|64]) still pass on the 32-bit ship target.
Origin: proposed in lutris/dgvoodoo2#5 as the missing FOSS piece for legacy-API coverage on Linux, alongside D7VK, nGlide, and cncdraw.
Layout
crates/
glidevk-core/ Backend-agnostic core: Glide state machine, texture format
conversion, LFB emulation, state-hash -> pipeline/shader cache.
Host-buildable and unit-tested (no Vulkan loader required).
glide3x/ The glide3x.dll shell — Glide 3.x entry points -> core.
docs/
feasibility.md Feasibility study, architecture, and roadmap.
glide2x/ The glide2x.dll shell — Glide 2.x entry points -> core (fixed GrVertex, gu* helpers, inverted texture LOD).
Building
Core (host — for development & tests)
cargo test # runs core + shell unit tests natively
cargo check -p glidevk-core --features vulkan # type-check the ash backend
The core builds on any host so the state machine, texture decode, and LFB packing can be developed and tested without a Vulkan loader.
Differential tests (against a reference Glide DLL)
examples/run_diff.sh # wine_texcoord beside nGlide and beside ours, diffed
GLIDE_REF=/path/glide2x.dll examples/run_diff.sh wine_texcoord wine_blendmode
GLIDE_API=glide3x examples/run_diff.sh wine3_palette # glide3x shell, wine3_* harnesses
Harnesses that read back through grLfbReadRegion run unchanged against any
Glide 2 implementation, so the reference defines correct behaviour. See
docs/game-compatibility.md § Differential testing.
glide3x.dll (Wine target — the shipping artifact)
Glide games are 32-bit, so the primary target is i686-pc-windows-gnu
(x86_64-pc-windows-gnu also builds, for the rare 64-bit consumer):
rustup target add i686-pc-windows-gnu
cargo build -p glide3x --release --target i686-pc-windows-gnu
Exports are forced undecorated via #[export_name] and mirrored in
crates/glide3x/glide3x.def for the MinGW linker.
Vulkan is reached through the in-prefix vulkan-1.dll → winevulkan, exactly as
DXVK does. (x86_64-w64-mingw32-gcc and wine are the runtime prerequisites.)
Install into a Wine prefix (once the backend lands)
cp target/i686-pc-windows-gnu/release/glide3x.dll "$WINEPREFIX/drive_c/windows/system32/"
# then set glide3x to native,builtin as a Wine DLL override
Roadmap
- P0 ✅ — DLL loads under Wine; Vulkan reached via winevulkan;
grSstWinOpenbuilds a Win32 surface + swapchain;grBufferClear/grBufferSwapclear + present. Verified end-to-end (headless probe + 60-frame windowed present). - P1 ✅ — first triangle:
grDrawTriangle/grDrawVertexArray/grDrawVertexArrayContiguous→ Vulkan draw (render pass, passthrough Gouraud pipeline, persistently-mapped vertex buffer,grVertexLayoutparsing, window→clip transform, primitive assembly for list/strip/fan). Verified via center-pixel readback on 32- and 64-bit. - P2 ✅ — textures + combine units.
grTexDownloadMipMap/grTexSourcedecode (RGB565/1555/4444/P8/A8/I8) and upload to GPU images with descriptor sets; a combiner ubershader implements the Glide color/alpha combine units driven by push constants (grColorCombine/grAlphaCombine/grTexCombine,GR_PARAM_ST0). Verified via center-pixel readback on 32- and 64-bit. (Chose an ubershader over per-key GLSL→SPIR-V codegen: identical output, and it dodges a naga 30.0 frontend bug — per-state shader specialization is a deferred P4 perf optimization.) - P3a ✅ — alpha blend (
grAlphaBlendFunction), depth buffer (grDepthBufferMode/Function/grDepthMask,GR_PARAM_Z), alpha test + chroma-key discard. The pipeline cache keysVkPipelines on baked blend/depth state; combine/chroma/ alpha-test are push-constant driven. Verified via center-pixel readback. - P3b (LFB) ✅ —
grLfbLock/grLfbUnlockwrite path: the game's 16/32-bit framebuffer writes are unpacked (RGB565/555/1555/8888) and composited into the presented image on swap. Verified viaexamples/wine_lfb.c(LFB-written yellow overwrites a blue clear). This is the path Diablo II's 2D/menus use. Remaining P3b: fog (per-pixel fog blend + table), read-locks / partial-region LFB preservation, and the offscreen-backbuffer model for finely interleaved LFB↔triangle ordering. - glide2x shell ✅ —
crates/glide2xreusingglidevk-core: fixedGrVertex,grSstQueryHardware, inverted texture LOD, and thegu*combine helpers. Exports cover Deus Ex / POD / Undying'sglide2ximports, and it renders a textured/combined triangle correctly under Wine (verified viaexamples/wine_glide2.c). Seedocs/game-compatibility.md.
Real-game status
glide2x.dll— verified drop-in for the Unreal-engine games, POD, and the 1997–98 catalogue (covers their import tables; renders through the fixedGrVertexpath). Best first real-render target once 64-bit prefixes are in place.glide3x.dll— now exports the complete documented Glide 3.x API surface (every entry inglide3x.defhas a body — the P0–P3 render path wired toglidevk-core, plus state save/restore, screen/origin queries, single-level texture download, tex-mem accounting, clamp-mode select, and theguFogGenerate*table builders; remaining future-milestone calls — fog render, NCC, region LFB — are accepted no-ops). Verified loadable drop-in for Diablo II (all 35D2Glide.dllimports); full-frame LFB (P3b) is done, region-LFB is the last piece for its 2D/menus.- P4 — fidelity (16-bit dither / 22-bit post-filter, NCC textures), performance, HUD/config, Lutris install path.
Licensing
GlideVK code: MIT OR Apache-2.0. The reference material (open 3dfx Glide SDK,
SPDX Glide) is a permissive, source-available license permitting derivative
works; API semantics and export signatures are taken from it. This is a clean,
from-scratch FOSS implementation — the entire point of the originating thread.