| Header | Link |
|---|---|
| Purpose | Purpose |
| Use Cases | Use Cases |
| End-to-End Example | End-to-End Example |
| Quick Map | Quick Map |
| Project Placement | Project Placement |
| Build And Run | Build And Run |
| New Projects And Templates | New Projects And Templates |
| Health And Maintenance | Health And Maintenance |
| Profiling | Profiling |
| Install | Install |
perro is the one command you run at every stage of a project: create it, compile scripts, run a live dev loop, cook a release build, package DLC, import animations, and profile hot code. perro dev loads assets straight from disk for fast edit-run cycles, while perro build bakes assets through the static pipeline for release. The CLI wraps all compiler and setup glue, so your project folder stays plain files with no import database to babysit.
Commands use perro, assuming you ran perro_cli install and reloaded your shell profile, or installed from crates.io when available. --path defaults to the current working directory when omitted.
- Start a new game.
perro new --name MyGamescaffoldsproject.toml,input_map.toml,deps.toml, ares/main.scn, and the.perrocrates. - Fast edit-run loop.
perro devcompiles scripts, builds a project-local dev runner, and runs the game reading assets live from disk, so scene and script edits show up quickly. - Add content without hand-writing boilerplate.
perro new_script,perro new_scene,perro new_animation, andperro new_panimtreedrop templated files intores/(or a DLC) and rebuild. - Cook a shippable build.
perro buildbakes supported assets and links a release executable into.output/;perro build --target webandperro build --target androidexport browser and Android bundles. - Package optional or paid content.
perro dlc --name <name>builds one runtime-loadable.output/dlc/<name>.dlcfromdlcs/<name>/. - Import animation and keep the project healthy.
perro import_animconverts glTF/GLB clips to.panim;perro doctor,clippy,format, andtestcheck refs and script quality;perro bench,perro flamegraph, andperro mem-profileprofile hot paths.
Use check for the shortest script/scene feedback loop, doctor for project
wiring and missing refs, dev for behavior, and build for shipped/static
behavior. Run clippy and test after structural checks pass. A successful
dev run does not replace a release build check because asset and linking paths
differ.
Generated .perro output belongs to the CLI. Fix source under res/, config,
or engine crates; do not patch generated glue as a durable solution.
# 1. Install the `perro` shell command, then open a new shell.
perro_cli install
# 2. Scaffold a new project next to your other games.
perro new --path D:\GameProjects --name MyGame
# 3. Add a behavior script and a 3D scene.
perro new_script --path D:\GameProjects\MyGame --name PlayerController --res /scripts
perro new_scene --path D:\GameProjects\MyGame --name Main --template 3D --res /scenes
# 4. Run the live dev loop with timing overlays while you edit.
perro dev --path D:\GameProjects\MyGame --timings
# 5. Cook the release executable into .output/.
perro build --path D:\GameProjects\MyGameBuild and run:
perro check [--path <project_dir>]
perro test [--path <project_dir>] [-- <cargo_test_args>]
perro dev [--path <project_dir>] [--scene res://path.scn] [--target native|web|android] [--headless] [--timings] [--profile] [--ui-profile] [--release] [--csv-profile [csv_name]] [--sim <spec>] [--host <addr>] [--port <num>]
perro build [--path <project_dir>] [--target native|web|android] [--triple <rust_target> | --universal-macos] [--headless] [--profile] [--console]
perro targets [--host windows|linux|macos]
perro dlc --name <dlc_name> [--path <project_dir>]New projects and templates:
perro new [--path <parent_dir>] [--name <project_name>]
perro new_dlc --name <dlc_name> [--path <project_dir>] [--no-open]
perro new_script --name <script_name> [--path <project_dir>] [--res <res_subdir>] [--dlc <dlc_name>] [--no-open]
perro new_scene --name <scene_name> [--path <project_dir>] [--res <res_subdir>] [--dlc <dlc_name>] [--template 2D|3D] [--no-open]
perro new_animation --name <animation_name> [--path <project_dir>] [--res <res_subdir>] [--dlc <dlc_name>] [--no-open]
perro new_panimtree --name <tree_name> [--path <project_dir>] [--res <res_subdir>] [--dlc <dlc_name>] [--no-open]
perro import_anim <model.glb|model.gltf> --output <clip.panim> [--clip <name|index>] [--fps <fps>] [--skeleton <object_name>] [--retarget-map <map.pretarget>] [--target-rig <model.glb|model.gltf>]Health and maintenance:
perro doctor [--path <project_dir>]
perro test [--path <project_dir>] [-- <cargo_test_args>]
perro format [--path <project_dir>]
perro clippy [--path <project_dir>]
perro clean [--path <project_dir>]Profiling:
perro bench [--path <project_dir>] [--script <hash>] [--method <name>] [--var <name>] [-- <criterion_args>]
perro mem-profile [--path <project_dir>] [--release] [--csv [csv_name]]
perro spec [--path <project_dir>] [--target-fps <fps>]
perro flamegraph [--path <project_dir>] [--profile] [--root]Install:
perro installRecommended workflow:
- Use shipped sample projects under
demos/for repo examples. - Put temporary test/sandbox projects outside this monorepo, for example
D:\GameProjects\MyGame. - Open external project folders directly in VS Code.
Why:
- External projects keep project-local
.vscode/settings.jsonactive. demos/Demo2Danddemos/Demo3Dstay as known-good sample projects.perro check,perro dev, andperro buildwork with any project passed by--path.
Use these commands for normal compile, run, export, and DLC package workflows.
| Command | Main job | Output |
|---|---|---|
check |
Compile project scripts only. | .perro/scripts build output |
test |
Sync project scripts and run their Rust tests. | cargo test result |
dev |
Compile scripts, build dev runner, run project. | running dev app |
build |
Compile scripts, bake static assets, build release project. | .output/ executable + packed assets |
targets |
Show ready, setup-required, and unavailable build targets for a development OS. | support matrix |
dlc |
Build one runtime-loadable DLC package. | .output/dlc/<name>.dlc |
Command:
perro check --path <project_dir>What it does:
- Syncs every
*.rsfile from<project_dir>/res/**into<project_dir>/.perro/scripts/srcas generated*.gen.rs. - Regenerates module exports in
.perro/scripts/src/lib.rsfor all synced Rust files. - Regenerates runtime scripts registry in
.perro/scripts/src/lib.rsfor behavior scripts. - Builds the scripts crate at
<project_dir>/.perro/scripts.
Use this when you only need script compilation/update.
Command:
perro test --path <project_dir> [-- <cargo_test_args>]What it does:
- Syncs every
*.rsfile from<project_dir>/res/**into<project_dir>/.perro/scripts/srcas generated*.gen.rs. - Regenerates module exports and the runtime scripts registry in
.perro/scripts/src/lib.rs. - Refreshes source overrides in
.perro/scripts/Cargo.toml. - Runs
cargo testfrom<project_dir>/.perro/scripts. - Sets
CARGO_TARGET_DIR=<project_dir>/targetso script tests share the project build cache. - Enables the generated scripts crate
steamworksfeature when project Steam support is enabled.
Flags:
-- <cargo_test_args>: forwards remaining args tocargo test.
Examples:
perro test --path D:\GameProjects\MyGame
perro test --path D:\GameProjects\MyGame -- --lib -- --nocapture
perro test --path D:\GameProjects\MyGame -- player_state_testsCommand:
perro dev --path <project_dir> [--scene res://path.scn] [--target native|web|android] [--headless] [--demo] [--timings] [--profile] [--ui-profile] [--release] [--csv-profile [csv_name]] [--sim <spec>] [--host <addr>] [--port <num>]What it does:
- Runs the same scripts build pipeline as
check. - With
--target nativeor no--target, builds the project-local dev runner at<project_dir>/.perro/dev_runner. - With
--target native, launches the generated dev runner binary with your--path. - With
--target web, builds a wasm web bundle from.perro/project. - With
--target web, starts a built-in static server and opens your browser.
Flags:
--target native|web|android: selects native runner, browser wasm bundle, or Android app target. Defaultnative.--scene res://path.scn: boots this scene instead of the project'smain_scene. Forwarded to the runner asPERRO_BOOT_SCENE. Use it to profile a heavy scene directly instead of landing on the project menu.--headless: runs the nativeperro_headlessdev path with no window, input, or GPU render loop. Native only; rejected with--target webor--target android, and cannot combine with--timingsor--ui-profile.--demo: applies[demo]config overrides, skips excluded scripts/assets/scenes, strips tagged node trees, and enablesdemo_exclude!.--timings: prints lightweight native timing averages: sim, gfx, delta, fps.--profile: enables profiling feature for the selected dev target.--ui-profile: enables native dev runnerui_profilefeature.--release: builds release dev target.--csv-profile [csv_name]: writes native dev profile metrics CSV under.output/profiling/.--sim <spec>: runs the dev runner as a weaker machine than this one, for profiling. Forwarded to the runner asPERRO_SIM. Native only; a bad spec fails before the build. See Perf Simulation.--host <addr>: web target only. Static server bind host. Default127.0.0.1.--port <num>: web target only. Static server bind port. Default8000.
Android target notes:
--timings,--ui-profile, and--csv-profileare not supported withperro dev --target androidyet.- Android dev builds require an installed Android SDK/NDK and a running emulator or device.
Web target notes:
--ui-profileis not supported withperro dev --target webyet.--timingsis not supported withperro dev --target webyet.--csv-profileis not supported withperro dev --target webyet.- web output dir:
<project_dir>/.output/web-dev/ - web path uses static embedded wasm runtime, not the native dynamic file-loading dev runner.
- see WASM / Web Target
Use this for local development runs and testing.
The dev runner keeps assets dynamic and reads from normal project files.
Dynamic scene/resource loading is optimized for development.
Perro CLI handles compiler/setup glue so day-to-day workflow stays simple while project structure stays flexible.
For release-like asset loading numbers, run perro build.
See Performance + Flexibility Philosophy.
Command:
perro build --path <project_dir> [--target native|web|android] [--triple <rust_target> | --universal-macos] [--headless] [--profile] [--console] [--demo]--headless use native perro_headless feature path.
- rm
perro_app,perro_graphics,winitfrm final dep graph - kp scripts, scenes, timers, net, CPU physics + water physics
- force CPU particle cfg
- skip window, input device, GPU + rndr loop
- sync new + old
.perro/project+.perro/dev_runnermanifests
Steam-enabled headless builds use Steam GameServer API, not Steam client login.
- anonymous login default
PERRO_STEAM_GSLT-> token loginPERRO_STEAM_GAME_PORT-> game port; default27015PERRO_STEAM_QUERY_PORT-> query port; default27016PERRO_STEAM_SERVER_IP-> bind IPv4; default0.0.0.0PERRO_STEAM_SERVER_NAME-> browser namePERRO_STEAM_MAX_PLAYERS-> browser cap; default64PERRO_STEAM_LISTED=0-> disable browser listingPERRO_STEAM_SECURE=0-> auth w/o VAC-secure mode
Server scripts use steam::game_server for ticket auth, player stats, and server-set achievements.
What it does:
- Runs script compilation, like
check. - Packs
resassets through the static pipeline. - Generates embedded project entry files under
.perro/project. - Optimizes supported assets into match tables and preparsed compile-time statics.
- Packs unsupported/generic assets into
.perro/project/embedded/assets.perro. - Builds the generated project crate in release mode from
.perro/project. - With
--target nativeor no--target, copies the built executable to<project>/.output/. - With
--target web, exports browser bundle files to<project>/.output/web/.
Flags:
--target native|web|android: selects native executable, browser wasm bundle, or Android app target. Defaultnative.--demo: builds only the demo-visible source and applies[demo]config overrides.--triple <rust_target>: cross-compiles a native build for one Rust target triple. The CLI installs the Rust standard-library target when needed. The host still needs the target linker, SDK, and native libraries.--universal-macos: on macOS, buildsaarch64-apple-darwinandx86_64-apple-darwin, then merges the executables withlipo. Per-architecture exports are kept beside the universal export.--profile: enables profile build options for the generated project bundle.--console: enables console build options for generated native project bundle.
Web target notes:
--consoleis not supported withperro build --target web.- web build uses stable
wasm32-unknown-unknown+wasm-bindgen --target web. - web output includes
index.html,boot.js,app.js, andapp_bg.wasm. - see WASM / Web Target
Android target notes:
--consoleis not supported withperro build --target android.- Android builds require an installed Android SDK/NDK; the CLI resolves them from
ANDROID_SDK_ROOT/ANDROID_HOMEandANDROID_NDK_ROOT/ANDROID_NDK_HOME/NDK_HOMEor the default platform location.
Use this to build the final executable into <project>/.output/.
Native cross-build examples:
perro build --triple x86_64-pc-windows-msvc
perro build --triple i686-pc-windows-msvc
perro build --triple aarch64-pc-windows-msvc
perro build --triple x86_64-unknown-linux-gnu
perro build --triple i686-unknown-linux-gnu
perro build --triple aarch64-unknown-linux-gnu
perro build --triple x86_64-apple-darwin
perro build --triple aarch64-apple-darwin
perro build --universal-macos
Windows MSVC architecture cross-builds need the matching Visual Studio C++ tools. Linux cross-builds need the matching GNU or compatible linker and target system libraries. macOS builds need macOS/Xcode tooling; use a Mac for release signing and notarization.
perro targets
perro targets --host windows
perro targets --host linux
perro targets --host macos
Without --host, this shows the current development OS. READY means the host can build the target directly. SETUP means the build is possible after installing the listed linker, SDK, or system libraries. NO means use another development OS.
| Development OS | Windows | Linux | macOS | Web | Android |
|---|---|---|---|---|---|
| Windows | ready/setup by architecture | setup | no | ready | setup |
| Linux | setup with GNU/LLVM target | ready/setup by architecture | no | ready | setup |
| macOS | setup with GNU/LLVM target | setup | ready, including universal | ready | setup |
The static pipeline packs all res assets.
Supported assets, such as scenes, animations, materials, particles, meshes, textures, and CSV tables, are optimized into match tables and preparsed formats as compile-time statics for efficient runtime performance.
This is main Perro trade: author normal files in dev, then let compiler pipeline reshape them for release performance.
Other res files are packed generically into assets.perro.
See Performance + Flexibility Philosophy.
Command:
perro dlc --name <dlc_name> [--path <project_dir>]What it does:
- Reads source from
<project_dir>/dlcs/<dlc_name>/. - Generates DLC scripts crate under
.perro/dlc/<dlc_name>/scripts/. - Generates DLC pack crate under
.perro/dlc/<dlc_name>/pack/. - Builds both runtime-loadable modules.
- Packs manifest, scripts module, pack module, and DLC resources into
<project_dir>/.output/dlc/<dlc_name>.dlc. - Compresses final
.dlcwhen it reduces file size. - Removes temporary
.dlc.stagingfolder after successful pack.
Name rules:
selfis reserved fordlc://self/...and is rejected as a DLC name.
Use these commands to create projects, DLC folders, scripts, scenes, animation clips, and animation trees.
Shared rules:
--pathresolves to a project root for every command exceptnew.new --pathresolves to the parent directory that receives the new project.- Commands with
--dlc <name>targetdlcs/<name>/instead of projectres/. --resacceptsres://...or/...for base game content.--resacceptsdlc://<name>/...or/...for DLC content.--no-opendisables VS Code open for generated files.
Command:
perro new [--path <parent_dir>] [--name <project_name>]What it does:
- Creates a new project directory under
<parent_dir>. - Writes default project files:
project.toml,input_map.toml,deps.toml,res/main.scn, scripts scaffold, and.perrocrates. - Prompts to open the project in VS Code.
Notes:
- If you run this inside a directory you want to contain projects, omit
--path. - Add extra script Rust crates in
deps.tomlunder[dependencies]. - Perro merges
deps.tomlinto.perro/scripts/Cargo.tomloncheck,dev, andbuild.
Examples:
perro new --path D:\GameProjects --name MyGame
perro new --name MyGameCommand:
perro new_dlc --name <dlc_name> [--path <project_dir>] [--no-open]What it does:
- Resolves
<project_dir>. - Creates
<project_dir>/dlcs/<dlc_name>/. - Creates starter directories:
scenes/,scripts/,materials/, andmeshes/. - Creates starter files:
scenes/main.scnandscripts/script.rs. - Uses
dlc://<dlc_name>/scripts/script.rsin starter scene.
Name rules:
selfis reserved fordlc://self/...and is rejected as a DLC name.
Examples:
perro new_dlc --name CosmeticsPack
perro new_dlc --name CosmeticsPack --path D:\GameProjects\MyGameCommand:
perro new_script --name <script_name> [--path <project_dir>] [--res <res_subdir>] [--dlc <dlc_name>] [--no-open]What it does:
- Resolves
<project_dir>. - Resolves target root: project
res/, or projectdlcs/<name>/with--dlc. - Resolves
<res_subdir>relative to target root. - Creates a new
*.rsscript from the empty script template. - Opens the new file in VS Code unless
--no-openis passed. - Rebuilds scripts after file creation.
Notes:
--namecan omit.rs; extension is added automatically.--namemust be a file name only.
Examples:
perro new_script --name PlayerController
perro new_script --name PlayerController --res /scripts
perro new_script --name PlayerController --path D:\GameProjects\MyGame --res res://scripts
perro new_script --name DlcController --path D:\GameProjects\MyGame --dlc ExpansionOne --res /scripts
perro new_script --name DlcController --path D:\GameProjects\MyGame --dlc ExpansionOne --res dlc://ExpansionOne/scripts
perro new_script --name PlayerController --no-openCommand:
perro new_scene --name <scene_name> [--path <project_dir>] [--res <res_subdir>] [--dlc <dlc_name>] [--template 2D|3D] [--no-open]What it does:
- Resolves
<project_dir>. - Resolves target root: project
res/, or projectdlcs/<name>/with--dlc. - Resolves
<res_subdir>relative to target root. - Creates a new
*.scnscene from the selected template. - Opens the new file in VS Code unless
--no-openis passed.
Notes:
--templatedefaults to2D.- Generated scenes use
$root = @main. $rootmarks the scene root and can be reused as a node ref.--namecan omit.scn; extension is added automatically.--namemust be a file name only.
Examples:
perro new_scene --name Main
perro new_scene --name Main3D --template 3D
perro new_scene --name Main --res /scenes
perro new_scene --name Main --path D:\GameProjects\MyGame --res res://scenes --template 2D
perro new_scene --name DlcIntro --path D:\GameProjects\MyGame --dlc ExpansionOne --res /scenes
perro new_scene --name Main --no-openCommand:
perro new_animation --name <animation_name> [--path <project_dir>] [--res <res_subdir>] [--dlc <dlc_name>] [--no-open]What it does:
- Resolves
<project_dir>. - Resolves target root: project
res/, or projectdlcs/<name>/with--dlc. - Resolves
<res_subdir>relative to target root. - Creates a new
*.panimanimation clip from the default animation template. - Opens the new file in VS Code unless
--no-openis passed.
Notes:
- Defaults to
res/animationswhen--resis omitted. --namecan omit.panim; extension is added automatically.--namemust be a file name only.
Examples:
perro new_animation --name CubeMove
perro new_animation --name HeroRun --res /animations
perro new_animation --name HeroRun --path D:\GameProjects\MyGame --res res://animations
perro new_animation --name DlcIdle --path D:\GameProjects\MyGame --dlc ExpansionOne --res /animations
perro new_animation --name HeroRun --no-openCommand:
perro new_panimtree --name <tree_name> [--path <project_dir>] [--res <res_subdir>] [--dlc <dlc_name>] [--no-open]What it does:
- Resolves
<project_dir>. - Resolves target root: project
res/, or projectdlcs/<name>/with--dlc. - Resolves
<res_subdir>relative to target root. - Creates a new
*.panimtreeanimation tree from the default animation tree template. - Opens the new file in VS Code unless
--no-openis passed.
Notes:
- Defaults to
res/animationswhen--resis omitted. --namecan omit.panimtree; extension is added automatically.--namemust be a file name only.
Examples:
perro new_panimtree --name HeroMove
perro new_panimtree --name HeroMove --res /animations
perro new_panimtree --name HeroMove --path D:\GameProjects\MyGame --res res://animations
perro new_panimtree --name DlcMove --path D:\GameProjects\MyGame --dlc ExpansionOne --res /animations
perro new_panimtree --name HeroMove --no-openCommand:
perro import_anim <model.glb|model.gltf> --output <clip.panim> [--clip <name|index>] [--fps <fps>] [--skeleton <object_name>] [--retarget-map <map.pretarget>] [--target-rig <model.glb|model.gltf>]gltf_to_panim and glb_to_panim are aliases.
What it does:
- Loads the glTF document.
- Selects one animation by
--clipname or index. - Converts translation, rotation, and scale channels into
.panimkeyframes. - Writes node tracks as
Node3Dobjects. - Writes skin joint tracks as
Skeleton3Dbone tracks on--skeletonobject. - With
--retarget-map, bakes bone aliases, rest-pose alignment, and translation policy.
Notes:
--clipdefaults to0.--fpsdefaults to60.--skeletondefaults toRig.- Scene or script bindings still map
.panimobject names to actual scene nodes. - Bone names come from glTF node names, for example
bone["Spine"].rotation. - Joint rotations convert from glTF local rotations to Perro rest-relative pose deltas.
--target-rigreads target joint rest poses and needs--retarget-map.- Inline rest poses in the map override glTF rest poses.
- Morph target weights are ignored.
Examples:
perro import_anim res/models/hero.glb --output res/animations/idle.panim --clip Idle
perro import_anim res/models/hero.glb --output res/animations/run.panim --clip 1 --fps 30 --skeleton HeroRig
perro import_anim res/models/mocap.glb --output res/animations/run.panim --retarget-map res/animations/humanoid.pretarget --target-rig res/models/hero.glbRetarget map:
source = Rig
target = HeroRig
keep_unmapped = false
translation = root_only
root_bone = mixamorig:Hips
bone mixamorig:Hips => Hips
bone mixamorig:LeftArm => upper_arm.L
# position | rotation quaternion | scale
source_rest mixamorig:Hips = (0, 0.9, 0) | (0, 0, 0, 1) | (1, 1, 1)
target_rest Hips = (0, 1.02, 0) | (0, 0, 0, 1) | (1, 1, 1)translation values:
all: keep all bone translation tracks; default for old maps.root_only: keep onlyroot_bonetranslation tracks.none: remove all bone translation tracks.
Rest solve maps source-rest position/scale to target-rest position/scale.
Rotation keys stay rest-relative deltas, matching Skeleton3D playback.
Use these commands to check references, run user script tests, format user scripts, lint user scripts, and remove build output.
Command:
perro doctor [--path <project_dir>]What it does:
- Loads
project.toml. - Checks
project.main_scene,project.icon, andproject.startup_splash. - Scans text assets under
res/anddlcs/for quotedres://anddlc://references. - Scans user scripts for likely missing
res://anddlc://load paths. - Warns when
get_var!,set_var!,broadcast_var!,call_method!, or a signal connection references a name not found in any script state ormethods!block, or targets a member that exists but is notpub(no dispatch glue is generated); the warning names the defining file. Also warnsscene var privatewhen a scenescript_varsentry or.panimset_varevent targets a non-pub state field β that value will not apply. - Warns the reverse too: a
pubstate field orpub fnctx method that nothing references dynamically β novar!/func!/method!literal, access-macro string, signal connection, scenescript_varsentry on a node running that script, animation event, or even a plain string literal matching the name anywhere in script code β can droppubto shed its generated glue. Name matching is project-wide and never deduplicates: a shared name with any dynamic use anywhere stays quiet, so only names with zero uses are flagged. Methods without aScriptContextparameter never get glue, sopubon plain helpers is ignored; acall_method!aimed at one of those gets its own "not callable" warning instead. - Warns when those dynamic calls target
ctx.idand a typed self access path is available. - Compiles every
.wgslunderres/anddlcs/against the engine prelude and reports parse/type errors at the shader's own line and column. - Reports missing scene/config references and broken shaders as errors, and script findings as warnings.
Shader checking composes your file exactly like the renderer does, so a custom
material is checked against both the rigid and the skinned prelude, a Sky3D
pass against the sky stack, and a post-process pass against the post prelude:
err: shader res://shaders/sky_horizon_band.wgsl:9:12: [sky pass] no definition in scope for identifier: `undefined_helper`The entry function decides which prelude applies: shade_material (or
shade_vertex) means custom 3D material, sky_shader means Sky3D pass, and
post_process means post-process pass. A .wgsl with none of them cannot be
loaded by the engine, so doctor warns and skips it.
The same check runs during perro build --static, where a broken shader fails
the build instead of the first frame that draws with it.
Command:
perro format --path <project_dir> [--dedup]What it does:
- Resolves your path to that project's
resroot. - Recursively finds format targets under
res/**. - Runs
rustfmton*.rsfiles. - Formats
*.scnand*.furscene files. - Formats key/value resource files:
*.pmat,*.ppart, and*.uistyle. - With
--dedup, creates$varNvalues for large repeated scene values used 3+ times.
Command:
perro clippy --path <project_dir>What it does:
- Resolves your path to that project's
resroot. - Recursively finds all
*.rsfiles underres/**. - Syncs those files into
.perro/scripts. - Runs
cargo clippy --all-targets -- -D warningsfor the generated scripts crate.
Command:
perro clean [--path <project_dir>]What it does:
- Removes the project's
target/directory.
Use these commands to record memory samples or produce flamegraphs from the dev runner.
Answers "how does this run on a machine weaker than mine?" without a second machine.
perro dev --path <project_dir> --sim igpu
perro dev --path <project_dir> --sim half --timings
perro dev --path <project_dir> --sim "igpu,cores=2"Sets PERRO_SIM on the dev runner. Set the env var directly for a shipped build or a bench run:
$env:PERRO_SIM = "low_end"Presets:
| Preset | GPU | Cores |
|---|---|---|
off |
machine default | all |
igpu |
integrated tier + request the LowPower adapter |
all |
low_end |
integrated tier | 4 |
half |
integrated tier | half this machine's |
potato |
integrated tier + LowPower adapter + 720p scene cap |
2 |
Tokens, comma separated, later wins: cores=N, gpu=off|constrained|igpu, pixels=WxH. --sim "half,gpu=off" cuts cores only; --sim "cores=4" skips the GPU tier entirely.
What each axis really does:
- GPU: flips the same low-end quality policy an integrated adapter already trips - 1080p scene cap, MSAA off with FXAA swapped in, SSAO low, small shadow atlas, memory-usage allocator hints.
igpu/potatoalso request theLowPoweradapter, so a hybrid laptop runs on its real integrated GPU. It does not slow the silicon down: a discrete card still renders that tier fast. Use it to check the quality tier and its CPU-side cost, not to predict an iGPU's frame time. - CPU: caps the shared worker pool and every parallel work split to N threads. A real core-count cut, not an injected stall, so parallel scaling and single-thread bottlenecks show up honestly. Per-core clock speed is unchanged.
The runner prints one line at startup when a sim is on:
[perro][sim] PERF SIM ON gpu=(igpu) cores=(4/16) max_scene_pixels=(default) -- timings are NOT this machine
Check that line before trusting any timing CSV: a forgotten PERRO_SIM in your shell poisons every later measurement.
Command:
perro bench --path <project_dir> [--script <hash>] [--method <name>] [--var <name>] [-- <criterion_args>]What it does:
- Syncs project scripts into
<project_dir>/.perro/scripts. - Adds a Criterion bench target for script benches.
- Runs
cargo bench --bench perro_script_benchfrom the generated scripts crate. - Benches script constructor/state creation and lifecycle callbacks.
- Benches methods passed with
--methodusing empty params. - Benches vars passed with
--varthrough generated get/set state paths.
Flags:
--script <hash>: filters to one script registry hash. Repeat for more scripts.--method <name>: benches a generated script method by member name. Repeat for more methods.--var <name>: benches generated state get/set by member name. Repeat for more vars.-- <criterion_args>: forwards remaining args to Criterion.
Examples:
perro bench --path D:\GameProjects\MyGame
perro bench --path D:\GameProjects\MyGame --method tick_ai --method rebuild_path -- --sample-size 20
perro bench --path D:\GameProjects\MyGame --script 529874888977469606 --var healthCommand:
perro mem-profile --path <project_dir> [--release] [--csv [csv_name]]What it does:
- Runs the same scripts build pipeline as
check. - Builds the project-local dev runner with
profilefeature enabled. - Launches dev runner with memory profiling enabled:
PERRO_MEM_PROFILE=1. - Writes batch memory samples CSV in
<project_dir>/.output/profiling/.
Flags:
--release: builds and runs release dev runner binary.--csv [csv_name]: custom output file name under.output/profiling/.
perro spec --path <project_dir> [--scene res://path.scn] [--target-fps <fps>]
Runs the project in release mode and records frame data plus bounded memory
summaries until the game closes. It writes report.json, report.md,
steam.txt, frames.csv, samples.csv, and markers.jsonl under
.output/profiling/spec/.
--scene res://path.scn boots straight into that scene instead of the project's
main_scene, so a capture profiles gameplay rather than the start menu.
frames.csv carries one row per frame with the full per-phase draw breakdown:
CPU phases, GPU prepare/acquire/encode/submit/present spans, GPU timestamp
spans for the main encoder, the water sim, and the shadow depth block, plus
draw-call, batch, triangle and pass-structure counters. The header row names
every column.
Add test-path markers in scripts:
spec_begin!("boss fight");
spec_end!("boss fight");
spec_point!("streaming gate");Marker recording is enabled only for the spec scripts feature. Normal project builds strip marker calls and the marker implementation.
The first report uses CPU-side update/render timings and process RSS. CPU/GPU equivalents are relative to the test PC and remain estimates until a benchmark score database and GPU timestamp queries are available.
Command:
perro flamegraph --path <project_dir> [--profile] [--root]What it does:
- Runs the same scripts build pipeline as
check. - Checks
cargo flamegraphavailability. - Auto-runs
cargo install flamegraphwhen missing. - Runs
cargo flamegraph --releasefrom<project_dir>/.perro/dev_runner. - Sets
CARGO_TARGET_DIR=<project_dir>/target. - Forces debug symbols for release profiling with
CARGO_PROFILE_RELEASE_DEBUG=true. - Passes project path through to dev runner with
-- --path <project_dir>.
Flags:
--profile: enables dev runnerprofilefeature when building/profiling.--root: forwards--roottocargo flamegraph.
Notes:
- Linux: install
perf(linux-toolspackage family). - macOS: install
dtrace/Xcode command line tools. - Windows: CLI asks to relaunch elevated before flamegraph when shell lacks admin rights.
- Windows:
cargo-flamegraphusesblondieand often needs elevated PowerShell/Terminal. - Windows: if error includes
NotAnAdmin, rerun as Administrator. - Windows fallback: prefer WSL/Linux profiling for full flamegraph support.
Examples:
perro flamegraph --path D:\GameProjects\MyGame
perro flamegraph --path D:\GameProjects\MyGame --profileCommand:
perro installWhat it does:
- Adds/updates a
perroshell function in your profile. - On Windows, updates PowerShell profiles.
- On Linux, updates POSIX shell profiles:
~/.profile,~/.bashrc,~/.zshrc. - Function builds source-mode CLI, copies it to temp, then runs args.
After running install, open a new shell or source your updated profile.
Examples:
perro new --path D:\GameProjects --name MyGame
perro check --path D:\GameProjects\MyGame