Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,23 @@ jobs:
CARGO_BUILD_JOBS: '2'
run: python tools/ci/setup_windows_perry.py --out "$env:RUNNER_TEMP/bloom-perry" --github-env

- name: Windows / installed native startup
shell: pwsh
env:
CARGO_BUILD_JOBS: '2'
run: |
python -m unittest tools.ci.test_native_package_smoke
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
python tools/ci/native_package_smoke.py

- name: Retain installed native startup evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: windows-native-package-startup
path: target/ci/native-package
if-no-files-found: error

- name: full / example-compile
env:
CARGO_BUILD_JOBS: '2'
Expand Down
60 changes: 60 additions & 0 deletions docs/evidence/windows-installed-native-v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Installed Windows native package

The published Jolt dependency uses `lib/win32-x64`, while the engine's build
script searched for `windows-x64`. A clean install therefore ignored its shipped
archives and fell back to CMake. The long audit-project path then exceeded
MSBuild's path limit. The resolver now uses the published spelling and retains
the older spelling for staged CI archives.

After that correction, Cargo builds the engine but Perry's final link still
requests `Jolt.lib` and `bloom_jolt.lib` from a CMake output directory that does
not exist for a prebuilt install. Rust already bundles those static archives in
`bloom_windows.lib`. Removing the redundant Windows manifest libraries and
source-build `libDirs` allows the installed game to link. A live Jolt simulation
in the startup fixture verifies that the physics implementation is present.

## Local validation

The diagnostic installed-package candidate links and starts on physical Radeon
DX12 and Vulkan. Its 128x128 frame matches all 16,384 expected pixels on each
backend. The fixture creates a dynamic Jolt body, advances it deterministically,
and draws the expected white square only while the valid body has fallen under
gravity. It destroys the body, shape and world after the native loop exits.

The required Windows native-package check now packs and installs the actual
package into a fresh temporary project, compiles that fixture with the pinned
Perry toolchain, starts the native renderer and checks every frame pixel. It
rejects a CMake fallback, compiler/runtime errors, a missing capture and an
incorrect frame. Logs, source/package/compiler/archive hashes and captures are
retained; the temporary package, libraries and executable are removed.

The fresh complete checker passes using Jolt 0.4.1 with no CMake fallback. Its
native compile takes 267.812 seconds; DX12 and Vulkan startup/capture take 6.328
and 3.375 seconds. Both PNG files have SHA-256
`8a509d87d3aa3fab96e0a9e2c67228e187f1bc0853cb4726aa5844799440f30e`.
These are compilation/test durations, not a frame-rate measurement. The tested
installed build script, manifest and fixture match the candidate source.

Blank-frame and physics-failure-color controls are rejected. The full contracts
component, formatting and strict Clippy pass; Clippy also exercises the legacy
`windows-x64` staged-archive lookup. Hosted validation is pending. The runs use
the previously qualified Perry 0.5.1220 source/runtime profile. Local DX12 uses
SDK compiler DLLs from PATH. These results do not claim
stock-Perry usability, packaged DXC, a clean machine or window presentation.

## Separate limitations retained

- Supplying an external `CARGO_TARGET_DIR` lets Cargo build successfully but
Perry then searches its usual crate-local directory for the engine library.
That audit harness override was removed before the normal startup checks.
- Very long native project paths can still fail in MSVC's build-script linker;
the resolver correction does not fix general Windows path-length support.
- Direct-2D mode currently does not service the queued PNG capture. That initial
probe exited without an image and failed. The accepted fixture exercises the
normal scene path; direct-2D capture remains separate work.
- Native headless startup does not prove browser startup or visible native
presentation. #142/#74's complete starter and lifecycle remain open.

Raw diagnostics and successive candidates are retained under
`tools/quality/out/windows-engine-plan/installed-native-startup/`. Earlier failures
are preserved with the changes in harness configuration and source clearly marked.
15 changes: 12 additions & 3 deletions docs/windows-engine-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ first nine-scene Radeon evidence are in draft PR #154. Follow-up work starts at
| #135 / #149 temporal reconstruction | Enforced motion/producer/quality-preset corpus, representative scenes, fractional/native and frozen A/B timing, memory/resize checks, platform evidence | Device/resource, stationary SSGI, and profiler fixes are retained. The surface correction passes original HD startup limits and 154,720 analytic receiver checks on Vulkan, DX12, and hosted Metal; 93 local goldens pass, including lighting recovery. The full Radeon corpus passes twice. Wider representative scenes, frozen A/B performance, memory/resize, and platform acceptance remain open |
| #140 integration gates | Same required local/hosted lanes pass on exact source; release package startup and all-example evidence | #160 fixes silent Windows CI non-execution and MSVC PATH ordering. #161 passes the actual native engine build and all 20 native links locally and in hosted CI. #162 fixes the focused DX12 failures; #163 fixes camera-history reset. #164 passes all 22 hosted Tests jobs using an explicit FXC Windows lane. The underlying WARP/DXIL crash remains open. A separate layered-material correction passes the full local FXC shared suite and all 93 DXC/Vulkan goldens. Release startup/install acceptance remains open |
| #138 capability fallback | Actual constrained-adapter startup and relevant forced-tier corpus, truthful capability outputs | Existing implementation/evidence preserved; physical constrained-limit acceptance still needs proof |
| PR integration | Reviewable changes, passing required checks, full issue evidence, merge-ready rendering branch | #147 and the stacked fixes #154–#164 remain drafts; no merge performed |
| PR integration | Reviewable changes, passing required checks, full issue evidence, merge-ready rendering branch | #147 and the stacked fixes #154–#166 remain drafts; no merge performed |

## Engine work retained in scope

Expand Down Expand Up @@ -74,7 +74,7 @@ audit are saved in `tools/quality/out/windows-engine-plan/plan-requirements.json
exposed four layered-material compiler failures. A [level-zero LUT correction](evidence/windows-fxc-layered-lut-v1.md)
restores the complete local FXC shared component, including all 93 goldens;
all 93 DXC and Vulkan regression goldens also pass. Hosted checks for that
correction are pending. The underlying WARP/DXIL
correction pass all 22 Tests jobs at #165, with [published evidence](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-fxc-layered-20260911). The underlying WARP/DXIL
defect remains open.
2. Finish all-example native linking, real starter/example startup, and clean
Windows installation. The [native example gate](evidence/windows-example-gate-v1.md)
Expand All @@ -89,7 +89,16 @@ audit are saved in `tools/quality/out/windows-engine-plan/plan-requirements.json
and native/browser startup acceptance. The [portable web command](evidence/windows-portable-web-cli-v1.md)
now passes its clean installed help command, nine failure/assembly regression
checks, and a complete installed Perry-plus-engine WASM build on Windows.
Real starter rendering and native startup remain open.
Its asynchronous-copy follow-up also passes the hosted Windows pack/install
check and a fresh local installed web build; remaining hosted jobs are running.
The [native package correction](evidence/windows-installed-native-v1.md)
fixes Jolt directory lookup and redundant final-link metadata. A diagnostic
installed fixture simulates Jolt and renders an exact frame on DX12 and
Vulkan. The complete fresh-package checker also passes both backends with
all 16,384 pixels matching and no CMake fallback. Hosted startup checks are
pending. Browser starter
rendering, visible native presentation, shared lifecycle, general long-path
support and direct-2D frame capture remain open.
3. Complete the wider temporal/geometry, performance, memory, resize, and
capability corpus. The
[HD surface correction](evidence/windows-ssgi-surface-v1.md) and two valid
Expand Down
31 changes: 20 additions & 11 deletions native/shared/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,19 +222,28 @@ fn find_prebuilt_dir(
} else {
""
};
let target_token = format!("{}-{}{}", target_os, arch_token, sim_suffix);
// The published npm package/release matrix uses Node's Windows platform
// name. Keep Rust's spelling as a compatibility alias for staged CI libs.
let package_os = if target_os == "windows" {
"win32"
} else {
target_os
};
let package_token = format!("{}-{}{}", package_os, arch_token, sim_suffix);
let rust_token = format!("{}-{}{}", target_os, arch_token, sim_suffix);
let mut target_tokens = vec![package_token];
if target_tokens[0] != rust_token {
target_tokens.push(rust_token);
}

let candidates = std::iter::empty::<std::path::PathBuf>()
let roots = std::env::var_os("BLOOM_JOLT_PREBUILT_DIR")
.map(std::path::PathBuf::from)
.into_iter()
.chain(
std::env::var_os("BLOOM_JOLT_PREBUILT_DIR")
.map(|v| std::path::PathBuf::from(v).join(&target_token)),
)
.chain(walk_up_for_node_modules(manifest_dir).map(|nm| {
nm.join("@bloomengine")
.join("jolt-prebuilt")
.join("lib")
.join(&target_token)
}));
walk_up_for_node_modules(manifest_dir)
.map(|nm| nm.join("@bloomengine").join("jolt-prebuilt").join("lib")),
);
let candidates = roots.flat_map(|root| target_tokens.iter().map(move |token| root.join(token)));

let (lib_prefix, lib_ext) = if target_os == "windows" {
("", "lib")
Expand Down
5 changes: 5 additions & 0 deletions npm/jolt-prebuilt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,8 @@ Built by `.github/workflows/release.yml` on each tag push — a matrix job per p

This package is versioned independently. `@bloomengine/engine` pins the
compatible prebuilt version in its dependencies.

The Windows package directory is `win32-x64`. The engine also accepts the legacy
`windows-x64` spelling for manually staged archives. Rust bundles the selected
static archives into `bloom_windows.lib`; Perry links that engine library without
requiring an additional Jolt source-build directory.
7 changes: 1 addition & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4499,12 +4499,7 @@
"dxguid",
"xinput",
"opengl32",
"d3dcompiler",
"Jolt",
"bloom_jolt"
],
"libDirs": [
"native/third_party/bloom_jolt/build/windows-x86_64/lib"
"d3dcompiler"
]
},
"linux": {
Expand Down
34 changes: 34 additions & 0 deletions tools/ci/fixtures/native-package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
initWindow, runGame, clearBackground, closeWindow,
captureFrameToPng, isFrameCaptureReady,
} from "@bloomengine/engine/core";
import { drawRect } from "@bloomengine/engine/shapes";
import {
createWorld, destroyWorld, sphereShape, releaseShape, createBody,
destroyBody, bodyCount, isBodyValid, stepVariable, getBodyPosition,
} from "@bloomengine/engine/physics";

// Native installed-package acceptance: a real Jolt body must fall before the
// renderer produces the white square checked by the host. No random inputs.
initWindow(128, 128, "Bloom installed native startup");
const world = createWorld({ gravity: { x: 0, y: -9.81, z: 0 }, maxBodies: 64, numThreads: 1 });
const shape = sphereShape(0.5);
const body = createBody(world, shape, { motionType: 2, position: { x: 0, y: 4, z: 0 } });
let frames = 0;
runGame((_dt) => {
stepVariable(world, 1 / 60, 1);
const y = getBodyPosition(body).y;
const physicsReady = isBodyValid(body) && bodyCount(world) === 1 && y > 0 && y < 4;
clearBackground({ r: 0, g: 0, b: 0, a: 255 });
drawRect(32, 32, 64, 64, {
r: 255, g: physicsReady ? 255 : 0, b: physicsReady ? 255 : 0, a: 255,
});
frames = frames + 1;
if (frames === 8) captureFrameToPng("native-startup.png");
if (frames > 8 && isFrameCaptureReady()) closeWindow();
// A failed capture must terminate and fail the host's image check.
if (frames >= 120) closeWindow();
});
destroyBody(body);
releaseShape(shape);
destroyWorld(world);
Loading
Loading