Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
144863a
[Bugfix] Glass keeps its place against its frame and in a headset (#1…
miogds Oct 10, 2026
99de731
[Feature] Specular anti-aliasing in the model pass (#1332)
miogds Oct 12, 2026
f8f27d2
Release 0.23.0 (#1346)
untoldengine Oct 13, 2026
e8de441
Re-host starter assets on GitHub Releases; promote Untold Editor in R…
untoldengine Oct 13, 2026
60722ce
[Project Layout] Root-level GameData/Resources/Docs project layout (#…
untoldengine Oct 14, 2026
47855dc
Document ComponentKit common failures and component_kit merge status …
untoldengine Oct 14, 2026
c979d86
[Feature] Draw the XR frame through the compositor's rasterization ra…
Oct 10, 2026
160b33a
[Bugfix] Query the frame's drawables with queryDrawables on visionOS 26
Oct 10, 2026
ec4b5cb
[Bugfix] Leave the render target size to the attachments while a rate…
Oct 10, 2026
9f76997
[Feature] Temporal anti-aliasing (.taa and .msaaTaa)
Oct 10, 2026
5921ef8
[Patch] TAA: variance clipping and a history weight that follows the …
Oct 10, 2026
de1513f
[Chores] Log the temporal resolve's parameters once
Oct 10, 2026
07f1919
[Patch] TAA: keep depth-less pixels, Catmull-Rom history, moving-map …
Oct 10, 2026
3fc0e5d
[Bugfix] Light back faces with the normal turned towards the viewer
Oct 10, 2026
e2c10ad
[Chores] TAA: a jitter scale for diagnosis (TAAParams.jitterScale)
Oct 10, 2026
fa88421
[Bugfix] XR: a capture drawable does not advance the temporal history…
Oct 11, 2026
9bc7ac7
[Bugfix] TAA: take the rate ratio on the axis where the history is co…
Oct 11, 2026
cb92585
[Feature] The XR optimisation set: pass skips, TAA into the history, …
Oct 13, 2026
7ee569b
[Chores] Shading-cost experiment switches: flat material, flat lighting
Oct 13, 2026
e130a07
[Chores] Back-face culling experiment switch for the model pass
Oct 13, 2026
264a1cb
[Feature] GPU time per pass, measured by the GPU itself
Oct 13, 2026
6d73d2f
[Feature] Light the indirect part once per fragment, in the G-buffer
Oct 13, 2026
fc3e0d7
[Patch] Build the depth pyramid only for a frame whose depth could hi…
Oct 13, 2026
cd39a84
[Patch] The temporal resolve presents the frame when the output pass …
Oct 13, 2026
d7acd53
[Feature] Light the opaque geometry whole once per fragment; the ligh…
Oct 13, 2026
79dced0
[Feature] The forward pass holds the lit colour alone, and takes only…
Oct 13, 2026
ec2c3b9
[Chores] Time the XR render loop's wait and submission phases in the …
Oct 13, 2026
40c600d
[Chores] Say whether the engine was compiled optimized
Oct 13, 2026
dac6691
[Feature] The sun shadow's filter takes 16, 8 or 4 taps, as the softn…
Oct 13, 2026
e756b1f
[Patch] The temporal resolve reads the maps' local rates from derivat…
Oct 13, 2026
fdde612
[Patch] The look pass presents the frame when the output pass would o…
Oct 13, 2026
94f94e5
[Bugfix] The temporal resolve gives a history with no surface in it n…
Oct 13, 2026
d1ef149
[Patch] The room's estimated light lights an object as bright as the …
Oct 13, 2026
f11a76e
[Feature] The opaque pass writes each pixel's motion, and the tempora…
Oct 13, 2026
3b76d06
[Feature] A tone map of none, for a mixed-immersion space to show the…
Oct 13, 2026
43d6c08
[Bugfix] The culling's compute reads the list and the frustum of its …
Oct 13, 2026
7192f73
[Chores] XR lighting diagnostics name the probe's format and count it…
Oct 13, 2026
3349c1c
[Bugfix] The room probe's faces light the sides of the world they sta…
Oct 14, 2026
c561b2d
[Feature] The look pass can show the scene with the display's P3 prim…
Oct 14, 2026
a9a91e0
[Chores] XR lighting diagnostics report the probe's face luminance
Oct 14, 2026
5bebbd6
[Feature] A render path setting names the deferred, hybrid and forwar…
Oct 14, 2026
ff39fb8
[Bugfix] The newest room probe waits for the prefilter in flight inst…
Oct 14, 2026
a71bb14
[Docs] Why RealityKit shows a mixed-immersion scene with P3 primaries…
Oct 14, 2026
ce26ccc
[Patch] The room light fades from one probe estimate to the next inst…
Oct 14, 2026
471e4bb
[Chores] Retire the shading-cost and back-face culling experiment swi…
Oct 14, 2026
059a93f
[Chores] Two render tests pin the state they read
Oct 14, 2026
eb9a394
[Bugfix] The room probe is convolved, not sampled: no ridges on a whi…
Oct 14, 2026
af3f383
[Chores] The batching tests give the scene channels back their render…
Oct 14, 2026
ecb831f
[Feature] The frame is posed for its predicted presentation time, and…
Oct 14, 2026
5b21622
[Feature] A transform track sampled at the frame's presentation time
Oct 14, 2026
d6d86d2
[Docs] Why a moving object judders on Vision Pro, and the frame-time fix
Oct 14, 2026
d23e858
[Docs] The viewer's frame-time change is drafted, and its summary cou…
Oct 14, 2026
0e67403
[Docs] The viewer's frame-time change as a patch, until this branch r…
Oct 14, 2026
a22c649
[Docs] The frame-time branch stacks on the headset-rendering pull req…
Oct 14, 2026
3eaaf3f
[Docs] The Vision Pro rendering profile, as measured
Oct 14, 2026
c87fc95
[Docs] The profile page says what a Mac rendering for the headset kee…
Oct 14, 2026
621815b
[Chores] Format five files as the pinned SwiftFormat (0.60.1) wants them
Oct 14, 2026
6217ef9
[Bugfix] The pass timing's frame crosses into the completed handler a…
Oct 14, 2026
a1bf79e
[Chores] The renderer tests step gets 60 minutes
Oct 15, 2026
50a1983
[Bugfix] No tile light stage on a paravirtualized GPU, and two tests …
Oct 15, 2026
5cf2229
Warn at build time when Metal kernel sources are stale (#1357)
untoldengine Oct 15, 2026
09365af
[Bugfix] Default Material.heightEnabled to false; auto-enable on heig…
untoldengine Oct 15, 2026
af46a14
Update README logo with new Untold Engine banner (#1362)
untoldengine Oct 15, 2026
7df6a33
[Chores] The XR loop counts the frames whose GPU work finished after …
Oct 15, 2026
6b54291
[Feature] A setting to encode the XR frame without waiting for the op…
Oct 15, 2026
c8d4be1
[Docs] The headset check's results: the hop is the GPU finishing afte…
Oct 15, 2026
3838dfb
[Feature] The XR loop waits for the optimal input time while the fram…
Oct 15, 2026
785ecae
[Chores] The per-second late-frame line says the input-time wait's mo…
Oct 15, 2026
1b595ca
[Chores] A second of frames well past the deadline names the heaviest…
Oct 15, 2026
d029790
[Bugfix] A shadow softness set before the shadow system exists is kep…
Oct 15, 2026
9e978d2
[Feature] The XR loop renders every other refresh, evenly, while a fr…
Oct 15, 2026
0811a77
[Bugfix] The half-rate pacing waits before taking a frame instead of …
Oct 15, 2026
63e43da
[Patch] The half-rate pacing takes its frames on the refresh grid and…
Oct 15, 2026
203e923
[Patch] The pacing's cadence falls only after two seconds that fit it…
Oct 15, 2026
de93a4d
[Patch] The pacing's cadence comes from the typical GPU span, and the…
Oct 15, 2026
455c939
[Bugfix] The paced query snaps to the refresh grid with a tolerance, …
Oct 15, 2026
5513187
[Bugfix] The pacing waits from the instant the compositor handed the …
Oct 15, 2026
118e7b6
[Bugfix] The paced queries fall on a fixed grid, exactly a cadence ap…
Oct 15, 2026
27e121e
[Feature] The half-rate pacing enters on skipped refreshes or frames …
Oct 15, 2026
91de20e
[Patch] The depth pyramid's gate counts objects, not the parts of a m…
Oct 15, 2026
2ac8994
[Patch] No transparency pass for a frame with nothing transparent to …
Oct 15, 2026
f71d96b
[Patch] The scene passes wait for the fence before their fragment sta…
Oct 15, 2026
2394ed0
[Patch] The frame's colour textures without shaderWrite, so the GPU m…
Oct 15, 2026
79b043d
[Patch] The look pass draws into its own descriptor, colour alone
Oct 15, 2026
35dec58
[Patch] No shadow cascades for a sun at zero intensity
Oct 15, 2026
fd081c8
[Feature] The XR line lists every pass of the last frame with their s…
Oct 15, 2026
c3cbb49
[Docs] The frame's cost on the headset: the measurements, the cuts th…
Oct 15, 2026
f82664c
[Feature] The default sun is the configuration's choice, and the head…
Oct 15, 2026
38863d6
[Feature] The headset's Forward pass and temporal resolve do less per…
Oct 15, 2026
5febd7e
[Docs] The shader cuts: what each does, what the reviewers found, the…
Oct 15, 2026
c22424e
[Feature] The sun's cascades are a setting, one on the headset by def…
Oct 15, 2026
ebfa68b
[Bugfix] Skip mass lookup for entities without a kinetic component in…
untoldengine Oct 15, 2026
eac4e3e
[Bugfix] A sun that casts no shadow shadows nothing, and one cascade …
Oct 15, 2026
2f7d17d
[Bugfix] The render path defaults to the platform's: forward on visio…
Oct 15, 2026
4cdf104
[Chores] The hybrid path is drawn by name, and the render tests pin t…
Oct 15, 2026
d14547c
[Chores] The moving box under TAA on the deferred and hybrid paths, a…
Oct 15, 2026
2832d5f
[Chores] The clip positions the vertex hands the velocity are named b…
Oct 15, 2026
c078560
Merge upstream develop: 0.23.0, glass against its frame and in a head…
Oct 15, 2026
bcae617
Merge feature/xr_headset_rendering at 2832d5fb, the head of untoldeng…
Oct 15, 2026
0b6348e
Merge feature/xr_shader_cost at eac4e3e6 (miolabs/UntoldEngine#73, #7…
Oct 15, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
- name: Run renderer tests (non-async suites)
# The sweep takes 26-30 minutes on the hosted macOS runners and grows with
# every render suite; leave headroom so a slow runner is not a red build.
timeout-minutes: 45
timeout-minutes: 60
env:
CI: true
UNTOLD_PSNR_THRESHOLD: "33.5" # tweak per suite/scene
Expand Down
72 changes: 72 additions & 0 deletions BuildChecks/MetalKernelsStaleCheck/checkStaleKernels.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// checkStaleKernels.swift
// MetalKernelsStaleCheck
//
// Copyright (C) Untold Engine Studios
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

// SwiftPM does not compile UntoldEngineKernels.metal; the engine target ships prebuilt
// .metallib resources instead (see buildkernels.sh and Package.swift), so an edit to the
// .metal source has no effect on `swift build` and produces no warning on its own.
// UntoldEngineKernels.metal #includes every file in ../Shaders, so the compiled kernel
// actually depends on all of them too — the hash below covers the kernels file and every
// Shaders/*.metal file for that reason, not just the file SwiftPM excludes by name. This
// plugin runs on every build plan and compares that hash against the one buildkernels.sh
// stamped the last time it rebuilt the metallibs, so a stale shader binary is surfaced
// instead of running silently.

import CryptoKit
import Foundation
import PackagePlugin

@main
struct MetalKernelsStaleCheck: BuildToolPlugin {
/// `Target.directoryURL` needs PackageDescription 6.1; this package is on 6.0, so
/// `.directory` (deprecated `Path`) is the only way to locate the target on this
/// tools version. SwiftPM invokes this method through the `BuildToolPlugin`
/// existential at runtime rather than our code calling it directly, so marking it
/// deprecated silences the unavoidable warning here without anyone needing to call
/// a deprecated entry point themselves.
@available(*, deprecated)
func createBuildCommands(context _: PluginContext, target: Target) async throws -> [Command] {
let engineDir = URL(fileURLWithPath: target.directory.string)
let kernelsDir = engineDir.appending(path: "UntoldEngineKernels")
let shadersDir = engineDir.appending(path: "Shaders")
let metalSource = kernelsDir.appending(path: "UntoldEngineKernels.metal")
let hashFile = kernelsDir.appending(path: ".metalsource.sha256")

guard let kernelsData = try? Data(contentsOf: metalSource) else {
return []
}

let shaderFileNames = (try? FileManager.default.contentsOfDirectory(atPath: shadersDir.path))?
.filter { $0.hasSuffix(".metal") }
.sorted() ?? []

var combined = kernelsData
for name in shaderFileNames {
guard let data = try? Data(contentsOf: shadersDir.appending(path: name)) else { return [] }
combined.append(data)
}

let currentHash = SHA256.hash(data: combined).map { String(format: "%02x", $0) }.joined()
let storedHash = (try? String(contentsOf: hashFile, encoding: .utf8))?
.trimmingCharacters(in: .whitespacesAndNewlines)

if storedHash != currentHash {
Diagnostics.warning("""
UntoldEngineKernels.metal or one of the Shaders/*.metal files it includes has \
changed since the shipped .metallib resources were built (or \
\(hashFile.lastPathComponent) is missing). `swift build` does not recompile \
Metal shaders, so this build is running against stale shader binaries. Run \
./buildkernels.sh from the package root, then commit the updated .metallib \
files and \(hashFile.lastPathComponent).
""")
}

return []
}
}
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,29 @@
# Changelog
## v0.23.0 - 2026-10-12
### 🐞 Fixes
- [Patch] Start a slot of objects as no reference (#1313) (6456434…)
- [Bugfix] Export the material a slot linked to the object shows, not the mesh's default (#1320) (6b7b664…)
- [Bugfix] Test occlusion from the camera the depth pyramid was built with (#1323) (45823f6…)
- [Bugfix] Keep a model's hidden parts hidden at its coarse LOD levels (#1324) (c33fca8…)
- [Bugfix] Detect pip --break-system-packages support in make testrenderer (#1328) (23d66a7…)
- [Bugfix] Use single-bounce reflectance for IBL diffuse energy conservation (#1329) (a2b154d…)
- [Bugfix] Report animation clips that match zero joints on the bound skeleton (#1330) (1085580…)
- [Bugfix] Shade each XR eye from its own position (#1285) (0b2863d…)
- [Bugfix] Glass keeps its place against its frame and in a headset (#1331) (144863a…)
### 📚 Docs
- [Docs] Add a getting-started tutorial for Jolt Physics in XR (#1327) (e544326…)
### 🚀 Features
- [Feature] Bake procedural Blender materials into repeating textures (#1307) (e8ed7ad…)
- [Feature] Glass: a material's transmission shows what is behind it and keeps its reflections (#1308) (da580ee…)
- [Feature] Select LOD levels by the size of the entity on screen (#1322) (6446393…)
- [Feature] Specular anti-aliasing in the model pass (#1332) (99de731…)
### ⚡ Performance
- [Performance] The render passes read the scene through a snapshot (#1321) (8b695d6…)
### ✨ Enhancement
- [Enhancement] Add animation playback helpers for synchronized gameplay (#1316) (ec141a0…)
### 🧹 Chores / Tests
- Remove hardcoded AWS/CloudFront streaming URLs from demos and tests (#1317) (4ae99ca…)
- [Test] Keep the reader of the component release test reading for the whole run (#1312) (9f3c637…)
## v0.22.0 - 2026-10-04
### 🐞 Fixes
- [Patch] Remember failed texture writes per export, not per Blender session (#1263) (fa706bf…)
Expand Down
8 changes: 7 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ let package = Package(
exclude: ["LICENSE.md", "README.md"],
publicHeadersPath: "include"
),
.plugin(
name: "MetalKernelsStaleCheck",
capability: .buildTool(),
path: "BuildChecks/MetalKernelsStaleCheck"
),
.target(
name: "UntoldEngine",
dependencies: ["CShaderTypes"],
Expand Down Expand Up @@ -130,7 +135,8 @@ let package = Package(

// iOS UI stack (only if some targets import UIKit)
.linkedFramework("UIKit", .when(platforms: [.iOS])),
]
],
plugins: ["MetalKernelsStaleCheck"]
),

// The cook's mesh work. A module of its own so that UntoldEngine does not import
Expand Down
56 changes: 30 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

<h1 align="center">
<a href="https://github.com/untoldengine/UntoldEngine">
<img src="docs/images/untoldenginewhite.png" alt="Logo" width="600">
<img src="docs/images/untoldengine-logo.png" alt="Untold Engine Logo" width="600">
</a>
</h1>

Expand Down Expand Up @@ -39,7 +39,7 @@ and input, so you can build your own interactive spatial and XR worlds.
Creator & Lead Developer:
https://www.haroldserrano.com

[![CoolSaber demo](docs/images/coolsaber-thumbnail.png)](https://youtu.be/4JWZf_NFddY)
[![State of the Engine 2026](docs/images/state-of-the-engine-2026-thumbnail.png)](https://youtu.be/AnN2NBRKl8E)

---

Expand All @@ -64,6 +64,21 @@ workflow, product experience, and customer-specific behavior.

---

## Try the Untold Editor

The Untold Editor is a companion app for building scenes visually — placing
entities, setting up lighting and cameras, and authoring `.untoldscene` files
that load straight into your app with `loadUntoldScene(named:)`. It's
optional — nothing in Untold Engine requires it — but it's the fastest way to
get a scene on screen without hand-writing setup code.

The Editor is now stable enough for regular use. Grab the latest build from
the [Releases page](https://github.com/untoldengine/UntoldEditor/releases).

![untold-editor](docs/images/editor-main-highlight.png)

---

## Watch It in Action — Apple Vision Pro Demos

| Demo | Description |
Expand Down Expand Up @@ -100,7 +115,7 @@ Clone the repository and launch the Starter Demo:
```bash
git clone https://github.com/untoldengine/UntoldEngine.git
cd UntoldEngine
git checkout v0.22.0
git checkout v0.23.0
swift run StarterDemo
```

Expand All @@ -122,21 +137,6 @@ Engine as a package dependency.

---

## Try the Untold Editor

The Untold Editor is a companion app for building scenes visually — placing
entities, setting up lighting and cameras, and authoring `.untoldscene` files
that load straight into your app with `loadUntoldScene(named:)`. It's
optional — nothing in Untold Engine requires it — but it's the fastest way to
get a scene on screen without hand-writing setup code.

The Editor is now stable enough for regular use. Grab the latest build from
the [Releases page](https://github.com/untoldengine/UntoldEditor/releases).

![untold-editor](docs/images/editor-main-highlight.png)

---

## Getting Started

To create your own XR, 3D, or spatial visualization app using Untold Engine,
Expand All @@ -159,20 +159,23 @@ start with the documentation path that matches where you are:
If your team is evaluating Untold Engine and needs an engine capability that is
not currently available, see [Commercial Use, Sponsored Features, and Support](COMMERCIAL.md).

## Core Direction
## Our Direction

### Vision — Why We Exist

> An engine that empowers creativity and never stands in its way.

**Vision:** An engine that empowers creativity and never stands in its way.
### Mission — What We Commit To

**Mission:** Build a reliable engine that lets developers focus on creating,
not troubleshooting the tools they depend on.
> Build a reliable engine that lets developers focus on creating, not troubleshooting the tools they depend on.

Untold Engine is built around three focused goals:
### Engineering Principles — How We Operate

- **Spatial Engine First** — Designed for spatial computing applications. LOD, geometry streaming, and static batching exist to support large, real-world-scale environments where presence and performance both matter.
**Reliability · Testing · Simplicity · Compatibility · Developer Experience · Performance**

- **XR / visionOS Support** — Spatial input, AR workflows, and Vision Pro support are functional today and expanding with each release.
These principles guide how we design, build, test, and maintain Untold Engine.

- **Metal-First Architecture** — The rendering layer stays close to Metal to maintain performance, shader control, and pipeline flexibility without abstraction layers getting in the way.
Read our [Engineering Principles](docs/Contributor/EngineeringPrinciples.md) for the standards that guide our development decisions.

---

Expand Down Expand Up @@ -219,6 +222,7 @@ Untold Engine is built around three focused goals:
- [Async Loading](docs/API/UsingAsyncLoading.md)
- [Spatial Input](docs/API/UsingSpatialInput.md)
- [XR Immersion Modes](docs/API/UsingXRImmersionMode.md)
- [The Vision Pro Rendering Profile, As Measured](docs/API/VisionProRenderingProfile.md)
- [Light Portals](docs/API/UsingLightPortals.md)
- [Rendering Extensions](docs/API/UsingRenderingExtensions.md)
- [Rendering Extension Examples](Examples/RenderingExtensions/README.md)
Expand Down
Loading
Loading