Craftax GPU-resident env behind opt-in vecenv device hooks#614
Open
Infatoshi wants to merge 4 commits into
Open
Craftax GPU-resident env behind opt-in vecenv device hooks#614Infatoshi wants to merge 4 commits into
Infatoshi wants to merge 4 commits into
Conversation
Syncs the C env with the craftax.cu reference implementation: lazy floor generation (resets generate floor 0 only, floors 1-8 on first descent, bit-identical maps), CRAFTAX_COMPACT_OBS byte observation support with the matching CraftaxCompactEncoder, and reset pool documentation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds MY_GPU_ENV compile-time hooks to vecenv.h (init/reset/step/ sync_logs/close): an env that defines them steps entirely on-device -- the threadmanager's per-step D2H-actions/OpenMP-step/H2D block is replaced by one async device call on the buffer stream, zero host syncs per step. Requires num_buffers=1; host envs still build so the CPU path remains available in-process for parity testing. ocean/craftax/craftax.cu is a vendored env-only snapshot of github.com/Infatoshi/craftax.cu craftax_full.cu (single-file CUDA port of full Craftax, built with -DCRAFTAX_CU_LIB): game logic and the step/reset/encode kernels verbatim, the standalone repo's own trainer/bench harness stripped by its trim_for_embed.py (9.4k lines vs 14k; trimmed build verified byte-identical trajectory hashes and a bitwise-identical 30-iteration learning trace). binding.cu wraps it behind the hooks, including pool-based device resets matching the host reset_pool_size semantics. Build with GPU_ENV=1 ./build.sh craftax. Stepping the same binary's CPU path and GPU path with identical actions produces bitwise-identical obs/rewards/terminals every step (verified 4096 envs x 300 steps; cross-compiler caveat documented in the parity harness). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Opt-in CRAFTAX_COMPACT_OBS: map cells as uint8 tokens (996 bytes vs 3372) expanded by a fixed lookup in the compiled encoder; scalar tail passed through as raw float bytes. Default float build is unchanged (hash-verified byte-identical). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Steps the same _C.so's CPU path and GPU path with an identical action stream in one process and compares obs/rewards/terminals per step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Infatoshi
marked this pull request as ready for review
July 24, 2026 09:22
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.
What this is
Craftax as PufferLib's first GPU-resident env, behind an opt-in vecenv hook. Companion PR #615 carries the env-agnostic trainer optimizations that were split out of this one; each stands alone. Everything here is additive: the default build is byte-identical in behavior (hash-verified), the device env is behind
GPU_ENV=1 ./build.sh craftax, compact obs behindCRAFTAX_COMPACT_OBS.All numbers: stock 4.0 trainer recipe as-is (single pass, 32 minibatches of 32768, bf16, Muon, V-trace + prio replay, cudagraphs), 8192 envs, MinGRU h256 x 3 layers, horizon 128,
reset_pool_size=1024, RTX PRO 6000 Blackwell — measured on exactly this branch:(With #615 applied on top: 8.73M / 9.85M.)
Design:
MY_GPU_ENVhooks (src/vecenv.h)An env may define five hooks (
my_gpu_init/reset/step/sync_logs/close). If it does, the threadmanager's per-step D2H-actions -> memset -> OpenMPc_step-> H2D block is replaced by a single async device call on the buffer stream — zero host syncs per step. Requiresnum_buffers=1. Host envs still build, so the CPU path stays available in the same process (that's how parity is tested below). Envs that don't define the hooks are completely untouched. This is the same direction as the (currently unconsumed)MY_VEC_STEPmacros in the 5.0 craftax binding — happy to converge on whatever shape 5.0 settles on.The env
ocean/craftax/craftax.cuis a vendored env-only snapshot of craftax.cu (single-file CUDA port of full Craftax,-DCRAFTAX_CU_LIBgates its main) — the device sibling ofcraftax.c/craftax.h. The game logic and step/reset/encode kernels are verbatim; the standalone repo's own trainer/bench harness (~4.6k lines PufferLib never calls) is stripped by a mechanical reachability script that lives upstream, so the snapshot is 9.4k lines and regenerable in one command. The trimmed build was gated the same way as everything else: byte-identical trajectory hashes (512x300, 4096x200, pool-1024 4096x200) and a bitwise-identical 30-iteration learning trace against the untrimmed build.binding.cuwraps it behind the hooks (device sibling ofbinding.c), including device reset pools matching the hostreset_pool_sizesemantics (pre-generated worlds, reset = state copy; index selection matches the host done-reset path).Correctness
_C.so, same seeding (rng=i, seed=seed_offset+i, zero glue), same action stream — obs/rewards/terminals bitwise-identical every step, verified 256x500 and 4096x300 (tests/craftax_gpu_parity.py). Caveat stated plainly: bitwise holds when the C binding is compiled with the reference flags (gcc -O3 -ffast-math); clang -O2 differs by 1-ULP float codegen in ~5 scalar obs floats — the same compiler-relative variation that already exists between clang/gcc builds of the C env itself.Compact observations (opt-in)
Same 843-value packed-ID layout the env already emits — map cells stored as uint8 tokens (996 bytes vs 3372) with a fixed lookup expansion in the compiled encoder (
CraftaxCompactEncoderon the Python side). Besides throughput, it learns better on this recipe: 5 seeds x 5B steps each, compact final score 19.0 +/- 0.35 (all runs monotonic) vs float 11.6 (range 6.6-17.6, with mid-training policy collapse in 3 of 5 seeds). Default stays float32.Limitations
num_buffers=1(nothing to overlap when the env steps on-device).State; a device-resident env there needs a small state bridge (or curriculum off) — noted for the eventual port, not relevant to 4.0.🤖 Generated with Claude Code