<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Unreal Engine CI/CD Blog</title>
    <link>https://speedrun.ci</link>
    <description>Deep dives into Unreal Engine CI/CD, build optimization, DevOps best practices, and lessons from the trenches of game development infrastructure.</description>
    <language>en</language>
    <atom:link href="https://speedrun.ci/rss.xml" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Mon, 12 Oct 2026 07:02:58 GMT</lastBuildDate>
    <item>
      <title>Last Week in Unreal: 5.8.4 Takes Shape, and ue6-main Switched D3D12 Residency to a New Tracker (October 5 to October 11, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-oct-5-11-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-oct-5-11-2026</guid>
      <pubDate>Mon, 12 Oct 2026 07:02:58 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>1,168 commits on ue6-main, the most since we started tracking it in June. A 5.8.4 hotfix candidate was assembled on the public 5.8 branch, D3D12 residency moved to a new tracker by default, Lumen dropped its per-pass HWRT toggles, and the platform I/O dispatcher is now on for game and client builds.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-584-takes-shape-and-ue6-main-switched-d3d12-residency-to-a-new-tracker">Unreal Engine Weekly: 5.8.4 Takes Shape, and ue6-main Switched D3D12 Residency to a New Tracker</h1>
<p><strong>Week of October 5 to October 11, 2026</strong> | <strong>1,168 commits on <code>ue6-main</code>, 92 on <code>ue5-main</code>, 113 on the public <code>5.8</code> branch</strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>If you ship on 5.8, start with the last section of this post. On Monday the public <code>5.8</code> branch was bumped with “Update version for 5.8.4,” a commit carrying a <code>#lockdown</code> tag, and 108 cherry-picked fixes landed alongside it in one block. One of them says outright that it is a “Backport UE-395636 for the UE 5.8.4 hotfix candidate.” There is no 5.8.4 release tag yet (the newest is 5.8.3), so this is a hotfix candidate still being assembled. What is in it matters for build farms: a UBT fix for binaries that were not being relinked, a UAT log stall, a Horde path traversal fix, an iOS lifecycle default change for Xcode 27, and a Vulkan fence default flip that applies to desktop.</p>
<p>On <code>ue6-main</code>, this was a week of defaults. D3D12 residency management moved to a new tracker, opt-in on Monday and default by Thursday (<code>c43ab669</code>). Vulkan bindless now prefers descriptor heaps and shader objects (<code>27c90725</code>). The platform I/O dispatcher, which last week’s post reported as not enabled, is now on by default for game and client targets (<code>b865826a</code>). And the editor auto-starts its MCP server through per-user settings (<code>9fb9185f</code>). Lumen got a new runtime surface cache, LumenWorldCache (<code>c249eca8</code>), and in the same week lost its seven per-subsystem hardware ray tracing CVars (<code>069e277d</code>), so any config that toggled HWRT for one Lumen pass now does nothing.</p>
<p>At 1,168 commits, <code>ue6-main</code> had its busiest week since this briefing started tracking it in June, above the previous high of 1,072 for September 21 to 27 and last week’s 1,065. Among named subsystems, build leads at 99, followed by editor at 81, core at 72, rendering at 71, and platform at 40.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<h3 id="agent-tooling-had-its-biggest-week-yet">Agent tooling had its biggest week yet</h3>
<p><strong>Pattern:</strong> counted the way we counted last week (commits touching the toolset registry, the AI Assistant plugin, the experimental toolsets, <code>AsyncFunctionResult</code>, and <code>ToolsetCore</code>, by path or by name in the message, with mesh modeling toolsets excluded), the cluster came to 64 this week. Including the ModelContextProtocol plugin and a few incidental sweeps brings it to 79. The four prior weeks were 20, 30, 56, and 46. Either way, this is the highest of the five.</p>
<p><strong>Interpretation:</strong> the new work gives agents more reach and more control over what they start. A new <code>ToolCalls</code> toolset lets the agent see and stop calls it backgrounded, with “<code>ListRunning</code>, <code>GetResult</code>, <code>Cancel</code> and <code>CancelAll</code>,” each “scoped to the conversation of the call that invoked it, so one conversation cannot read or stop another’s work.” (<code>0551fdd9</code>). A Knowledge Base plugin adds “a registry of providers with an in-memory SQLite/FTS5 index” (<code>10061736</code>). A FabToolset was added “for searching and installing Fab listings” (<code>66b14bed</code>). Sequencer and animation tools moved onto native scripting APIs, removing several Python toolsets (<code>e4d068b7</code>).</p>
<p>Most teams will notice the MCP server change. One commit, part of a change that starts Python at editor boot, removed the “UEFN MCP Toolsets experimental setting, project-gated Python registration shim, and MCP server auto-start helper” (<code>e0a3c77f</code>). The next day, another enabled “MCP server auto-start by default in the editor and UEFN through editor per-user settings, keeping the C++ default disabled for runtime targets” (<code>9fb9185f</code>). It lands as <code>bAutoStartServer=True</code> in <code>BaseEditorPerProjectUserSettings.ini</code>, and the commit does not say what the server binds to. If you run the editor on build agents or shared machines, decide whether you want it running there and override the setting if not.</p>
<p><strong>Who cares:</strong> tools programmers, pipeline and automation teams, anyone responsible for what runs on editor machines.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="uba-volume-fell-and-authorship-spread-out">UBA volume fell, and authorship spread out</h3>
<p><strong>Pattern:</strong> counting commits whose paths touch <code>UnrealBuildAccelerator</code>, this week came to 13, the lowest of the five weeks we have counted (the priors were 37, 19, 36, and 19). Last week all 19 came from one engineer. This week the 13 came from six engineers, split 6, 3, 1, 1, 1, and 1.</p>
<p><strong>Interpretation:</strong> last week we wrote that UBA was still one engineer’s work, and this week does not fit that reading. The work also leaned toward correctness (a wrong-size CAS entry, a heap overflow, a Linux deadlock), covered under Highlights. One week of broader authorship does not establish a pattern, so we will keep counting.</p>
<p><strong>Who cares:</strong> build and CI engineers, anyone whose build farm depends on UBA.</p>
<p><strong>Signal strength:</strong> Moderate.</p>
<h3 id="land-opt-in-then-flip-the-default-within-days">Land opt-in, then flip the default within days</h3>
<p><strong>Pattern:</strong> three RHIs changed defaults. The D3D12 residency tracker landed as opt-in on Monday (<code>65917c7e</code>), took a startup crash fix on Tuesday (<code>256bd31e</code>), and became the default on Thursday (<code>c43ab669</code>). Vulkan bindless switched its preferred extension and enabled shader objects on Friday (<code>27c90725</code>). Mac enabled “the RHI thread by default at ES3_1” (<code>4c5a115c</code>). Outside the RHIs, the platform I/O dispatcher went default for game targets the same day its deadlock fix landed (<code>a26cc9d9</code>, <code>b865826a</code>).</p>
<p><strong>Interpretation:</strong> this week, Epic did not leave these new paths behind opt-in flags for long. A regression after a sync can come from a default you did not change. The D3D12 and I/O dispatcher commits document a way back, listed under Highlights. The Vulkan and Mac commits do not.</p>
<p><strong>Who cares:</strong> rendering and RHI engineers, anyone running performance or stability passes against <code>ue6-main</code>.</p>
<p><strong>Signal strength:</strong> Moderate.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li><strong>If you ship on 5.8, a 5.8.4 hotfix candidate is being assembled.</strong> The version bump came with 108 cherry-picks. They include a UBT fix for external libs “preventing binaries from being relinked,” a UAT log stall, a Horde path traversal fix, and two default changes, iOS UIScene lifecycle and Vulkan <code>r.Vulkan.CpuWaitForFence</code>. Details are in the 5.8 section below.</li>
<li><strong>D3D12 residency now uses a new tracker by default.</strong> <code>D3D12.ResidencyManagement</code> went from 1 to 2. The commit says “Use D3D12.ResidencyManagement=1 to switch back” (<code>c43ab669</code>). It is read-only, so set it in config.</li>
<li><strong>Per-pass Lumen HWRT CVars are gone.</strong> Seven were removed, and “r.Lumen.HardwareRayTracing is now the only CVar to toggle between SWRT and HWRT” (<code>069e277d</code>). LumenWorldCache arrived as an opt-in replacement for card-based surface caching (<code>c249eca8</code>).</li>
<li><strong>The platform I/O dispatcher is on for game and client builds.</strong> <code>UE_PLATFORM_IO_DISPATCHER_ENABLED</code> “now defaults to UE_GAME instead of 0,” and “-NoPlatformIo disables the platform dispatcher at runtime” (<code>b865826a</code>).</li>
<li><strong>More defaults moved.</strong> Vulkan bindless prefers <code>VK_EXT_descriptor_heap</code> with shader objects on (<code>27c90725</code>). <code>r.ShaderPipelines</code> became read-only (<code>efc31233</code>). <code>r.AVBOIT.SampleBias</code> went from -2 to 0 (<code>602f1317</code>). Nanite visualization modes now need <code>r.ForceDebugViewModes=1</code> outside editor and ODSC shader maps (<code>87860b5e</code>). Low and Medium shadow quality now use Stochastic PCSS for directional VSM (<code>3bf20216</code>).</li>
<li><strong>Expect one-time recooks.</strong> At least four commits announce them, including “one-time full recook” (<code>a14aa680</code>) and “one-time recook of all blueprint packages” (<code>8d5abe19</code>). A multithreaded save race that produced packages “with an empty header” is fixed (<code>7ac3eb85</code>).</li>
<li><strong>UBA served wrong file content on Mac helpers.</strong> A symlink’s size was stored against its target’s hash, so “Mac helpers served 15 bytes” and remote compiles failed (<code>711b4de2</code>). A heap overflow in long sessions is also fixed (<code>5d50dc97</code>).</li>
<li><strong>Check CI wrappers that key on exit codes.</strong> Invalid MRQ arguments now exit with an error code instead of crashing (<code>b097d529</code>), and Mac build-machine crashes exit “1 instead of 3” (<code>4a65fd73</code>). A title-only commit reads “[Mac] Require macOS 15 Sequoia” (<code>8a45c509</code>).</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="d3d12-residency-moved-to-a-new-tracker-opt-in-monday-and-default-by-thursday">D3D12 residency moved to a new tracker, opt-in Monday and default by Thursday</h3>
<p><strong>What changed</strong></p>
<p>On Monday, “New D3D12 GPU residency tracker, replacing D3DX12Residency” landed with the line “Opt in with D3D12.ResidencyManagement=2; the default (1) keeps the existing D3DX12Residency path unchanged.” The commit describes it as “Much cheaper than D3DX12Residency on the submission and RHI threads” and says “The new tracker also covers work graphs, which D3DX12Residency never tracked, and frees their backing memory” (<code>65917c7e</code>). On Tuesday, a startup crash from “a bad robomerge resolve” was fixed (<code>256bd31e</code>). On Thursday: “Use the new D3D12 residency management code path by default,” with “Use D3D12.ResidencyManagement=1 to switch back to the D3DX12Residency library” (<code>c43ab669</code>).</p>
<p>The diff confirms the default moved from 1 to 2, and the help text now marks the RHICore residency tracker as the default. The CVar is read-only, read once at device creation.</p>
<p><strong>Why this is important</strong></p>
<p>A new residency implementation can change hitching, paging, and device-removed behavior without any change in your content, and the commits say nothing about behavior under memory pressure. If a D3D12 build on <code>ue6-main</code> starts stalling or losing the device after this sync, set <code>D3D12.ResidencyManagement=1</code> in config first, since the CVar cannot be changed at runtime.</p>
<p><strong>Who should care</strong></p>
<p>D3D12 rendering engineers, performance teams, anyone triaging GPU memory issues on Windows.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you profile D3D12 memory or see new stalls after syncing. Track otherwise.</p>
<hr>
<h3 id="lumen-added-a-runtime-surface-cache-and-removed-its-per-pass-hwrt-toggles">Lumen added a runtime surface cache and removed its per-pass HWRT toggles</h3>
<p><strong>What changed</strong></p>
<p>LumenWorldCache is described as an “automatic surface cache replacing previous LumenSurfaceCache relying on precomputed mesh cards.” It is “built at runtime by path tracing (by default 1/64 spp) from GBuffer and caching hits in sparse voxel cells” and “Requires ray tracing and hit-lighting (full/material ray tracing mode).” The claimed benefits include “No CPU overhead,” “x10 smaller memory usage,” and skipping the card cooking step. Measured on “FortGPUTestBed, console, 1080p, High scalability,” it came in “0.35ms slower indoors” and “0.4ms faster ourdoors” (sic). The “Missing functionality” list is long, including “Lumen hit-lighting support,” “Ambient Lighting Volume support,” and “Far Field support” (<code>c249eca8</code>).</p>
<p>One detail needs care. The message says to enable it with “r.Lumen.WorldCache.Support 1,” but the diff registers <code>r.Lumen.WorldCache.Supported</code> (default false), plus <code>r.Lumen.WorldCache.Allowed</code> (default true) for scalability and device profiles. Use the name in the diff.</p>
<p>Epic cleared the way in the same week. Two experimental paths, <code>r.LumenScene.GPUDrivenUpdate</code> (<code>d9e01e2a</code>) and <code>r.LumenScene.DirectLighting.Stochastic 1</code> (<code>771669e4</code>), were removed for “fundamental issues” that “weren’t ever solved,” and both commits name LumenWorldCache as “a more promising approach.”</p>
<p>Then the per-subsystem HWRT toggles went. “Those CVars were added during the initial HWRT bringup for debugging but they don’t make much sense anymore (nor they work well),” and “r.Lumen.HardwareRayTracing is now the only CVar to toggle between SWRT and HWRT” (<code>069e277d</code>). The diff removes seven:</p>
<ul>
<li><code>r.Lumen.RadianceCache.HardwareRayTracing</code></li>
<li><code>r.LumenScene.Radiosity.HardwareRayTracing</code></li>
<li><code>r.Lumen.Reflections.HardwareRayTracing</code></li>
<li><code>r.LumenScene.DirectLighting.HardwareRayTracing</code></li>
<li><code>r.Lumen.ScreenProbeGather.HardwareRayTracing</code></li>
<li><code>r.Lumen.TranslucencyVolume.HardwareRayTracing</code></li>
<li><code>r.Lumen.Visualize.HardwareRayTracing</code></li>
</ul>
<p><strong>Why this is important</strong></p>
<p>The CVar removal hits config today. A project that ran HWRT reflections but software tracing for screen probes, or turned HWRT off for one pass on a lower-end device profile, loses that split, and the ini lines stop doing anything. Search your config and device profiles. WorldCache is opt-in, needs full ray tracing, and still has gaps, but the removals show where Lumen is heading. If you have built anything on mesh card data, watch it.</p>
<p><strong>Who should care</strong></p>
<p>Rendering engineers, technical artists shipping Lumen with HWRT, anyone with Lumen CVars in device profiles.</p>
<p><strong>Urgency</strong></p>
<p>Act now if your config sets any of the seven removed CVars. Track WorldCache.</p>
<hr>
<h3 id="defaults-that-moved-this-week">Defaults that moved this week</h3>
<p><strong>What changed</strong></p>
<ul>
<li><strong>Vulkan bindless prefers descriptor heaps and shader objects.</strong> “VulkanRHI: Change default to shader_objects and descriptor_heaps for bindless” (<code>27c90725</code>). The diff changes <code>r.Vulkan.Bindless.PreferredExtension</code> from 0 to 1 (<code>VK_EXT_descriptor_heap</code>) and <code>r.Vulkan.AllowShaderObject</code> from 0 to 1.</li>
<li><strong>The platform I/O dispatcher is on for game and client targets.</strong> “UE_PLATFORM_IO_DISPATCHER_ENABLED now defaults to UE_GAME instead of 0. Game and client targets use the platform I/O dispatcher; server and editor targets keep the existing I/O path.” The commit adds that “-NoPlatformIo disables the platform dispatcher at runtime” (<code>b865826a</code>). It landed right after “Fixed I/O dispatcher deadlock when running with the new platform dispatcher” (<code>a26cc9d9</code>).</li>
<li><strong>The editor auto-starts its MCP server.</strong> Covered under Development Trends (<code>9fb9185f</code>). The C++ default stays disabled for runtime targets.</li>
<li><strong><code>r.ShaderPipelines</code> is read-only.</strong> Material shader pipelines are no longer compiled where the CVar is off, which the commit says includes Windows. “r.ShaderPipelines is now read-only, since toggling it on at runtime would find no pipelines and fall back to the default material” (<code>efc31233</code>). The code default stays 1, but the commit notes Windows runs with <code>r.ShaderPipelines=0</code>.</li>
<li><strong>AVBOIT sample bias went from -2 to 0.</strong> The diff changes <code>r.AVBOIT.SampleBias</code> from -2.0 to 0.0, part of a transmittance fix covered below (<code>602f1317</code>).</li>
<li><strong>Nanite visualization needs a flag in packaged builds.</strong> The raster VISUALIZE permutations now compile only “for editor and ODSC shader maps (or r.ForceDebugViewModes=1),” and the visualizations that need them are disabled elsewhere. Development builds show “an on-screen message” when one is requested but unavailable (<code>87860b5e</code>).</li>
<li><strong>Directional VSM uses Stochastic PCSS at Low and Medium shadow quality.</strong> Set through <code>BaseScalability.ini</code>, covered below (<code>3bf20216</code>).</li>
<li><strong>Win64 links write the PE checksum.</strong> <code>WindowsTargetRules.bSetImageChecksum</code> now defaults “to true for Win64,” for non-incremental links. Covered under the Wine fixes below (<code>62be1807</code>).</li>
</ul>
<p><strong>Why this is important</strong></p>
<p>The Vulkan change switches the descriptor model and the pipeline object model at once, so test it first. The I/O dispatcher affects every packaged game build. If you see new loading hangs, <code>-NoPlatformIo</code> is your first bisect step. QA and performance teams that use Nanite visualize modes in packaged Development builds will need <code>r.ForceDebugViewModes=1</code> for the target platform.</p>
<p><strong>Who should care</strong></p>
<p>RHI engineers, Vulkan teams, anyone shipping packaged builds from <code>ue6-main</code>, QA and performance teams.</p>
<p><strong>Urgency</strong></p>
<p>Act now on Vulkan and the I/O dispatcher if you run either. Track the rest.</p>
<hr>
<h3 id="nanite-culling-was-rewritten-and-translucency-got-correctness-fixes">Nanite culling was rewritten, and translucency got correctness fixes</h3>
<p><strong>What changed</strong></p>
<p>Nanite has a “New faster Nanite multi-pass culling implementation with SIMPLE/COMPLEX shader classification, adaptive fanout and cluster range encoding.” Instances needing expensive features such as skinning or WPO are classified COMPLEX and traversed by a separate permutation. The commit says “This new culling path is now used everywhere and the old persistent culling path has been removed,” and that “Performance tends to be at slightly better or at parity on tiny scene, but significantly better for even moderately large scenes.” New controls: <code>r.Nanite.Culling.Classification (default 1)</code> and <code>r.Nanite.Culling.AdaptiveFanoutThreadBudget</code>, “0 to disable” (<code>ad75acf2</code>).</p>
<p>On translucency, an AVBOIT and MLAB fix corrects three visible problems. The old -2 slice bias “also discarded real occluders - geometry behind one slice of glass kept an untinted highlight,” and now “the bias defaults to 0.” On MLAB’s separate translucency path, the background was attenuated twice, “so a measured green of 0.24 reached the screen as 0.02.” And the combine sample array was resized to cover <code>r.OIT.SortedPixels.MaxSampleCount</code>, “which previously sorted out of bounds at 9 or 16” (<code>602f1317</code>).</p>
<p>For low-end platforms, directional VSM gained a Stochastic PCSS projection, “usually taking less than half the time” of SMRT at reduced quality, with no local light support yet. The CVar’s code default stays SMRT, but the diff sets <code>r.Shadow.Virtual.ProjectionMethodDirectional=1</code> in <code>BaseScalability.ini</code> at Low and Medium shadow quality, so those levels now use SPCSS by default (<code>3bf20216</code>).</p>
<p><strong>Why this is important</strong></p>
<p>There is no fallback to the old Nanite culling path. If your GPU frame time moves after this sync, in either direction, this is a likely place to look, and <code>r.Nanite.Culling.Classification</code> and the fanout budget are the knobs to bisect with. The AVBOIT change alters how translucency looks: anything tuned against the old bias, or against MLAB’s double attenuation, will render differently. And if you ship at Low or Medium shadow quality, directional shadows now use a different, cheaper projection unless your own scalability config sets the CVar back to 0.</p>
<p><strong>Who should care</strong></p>
<p>Graphics programmers, technical artists working with translucency, low-end platform teams.</p>
<p><strong>Urgency</strong></p>
<p>Track. Re-check translucent content if you use AVBOIT or MLAB, and directional shadows if you ship Low or Medium shadow quality.</p>
<hr>
<h3 id="uba-a-wrong-content-cas-entry-a-heap-overflow-and-wine-farm-fixes">UBA: a wrong-content CAS entry, a heap overflow, and Wine farm fixes</h3>
<p><strong>What changed</strong></p>
<p>The UBA fix with the most visible symptom is for Mac helpers. With symlink support on, “a symlink’s size is its target path length.” <code>PopulateCasFromDirsRecursive</code> “hashed the link target’s content but stored the link size, so a CAS entry could advertise the wrong size.” In practice, “Xcode’s usr/include/sched.h -&gt; pthread/sched.h (15 bytes) shares a CAS key with AutoSDK AppleOSS sched.h, so Mac helpers served 15 bytes and remote compiles failed with” an <code>unterminated /* comment</code> error (<code>711b4de2</code>).</p>
<p>Other correctness fixes:</p>
<ul>
<li><strong>Heap overflow.</strong> A fixed 1M-entry array “was written past its end when a directory chain grew longer (one link per mtime change, e.g. frequently touched lock files in a long session)” (<code>5d50dc97</code>).</li>
<li><strong>Linux deadlock.</strong> “Linux: fix opendir/fstat deadlock” (<code>3573b6fc</code>).</li>
<li><strong>ParkingLot race.</strong> A race in <code>TimedWait</code> caused “the later wait to end too early” (<code>e0106d20</code>).</li>
</ul>
<p>For Wine-hosted builds, two large fixes. On hosts with more than 64 logical processors, wineserver’s default affinity “pins every Wine thread, and therefore every spawned cl.exe/UbaObjTool.exe, to CPUs 0-63.” A preloaded shim fixes that, with a caveat: “A wineserver started earlier without the shim (e.g. by running any Wine program first) keeps the 0-63 pinning until it exits; ‘wineserver -k’ before building clears it” (<code>fce0c399</code>). And because editor DLLs carried a zero timestamp and checksum, “Every module load therefore made one server round trip per loaded module, which scales with the square of the module count: about 621k calls during editor startup.” UBT now writes the PE checksum for non-incremental Win64 links (incremental links still skip it), which fixed it. Under Wine 11.7, “Windows_LoadLibrary: 21.6s -&gt; 5.9s (982 loads)” and “Cook wall time: about 110-116s -&gt; 97s.” The commit says “native behavior is unchanged” (<code>62be1807</code>). A related fix for Wine cooks against UBA Horde agents, where “every distributed job failed,” keeps “existing Windows hashes and DDC keys” unchanged on native Windows (<code>9719af0c</code>).</p>
<p>Two protocol changes. <code>SessionNetworkVersion</code> went to 68 with arm64 Wine helper and cross-architecture scheduling fixes (<code>fb715dd6</code>), then to 69 with keyed process reuse, where “processes without a key are not reused” and “The UbaController change that sets the key for ShaderCompileWorker must land with or before this” (<code>6366bbe2</code>). UBT now requests cross-architecture workers (<code>534aa3cd</code>).</p>
<p><strong>Why this is important</strong></p>
<p>If Mac remote compiles have failed on a stray comment error, take the CAS fix. The heap overflow needs a long session with frequently touched files, the example the commit gives. With the network version bumping twice in one week, update hosts and helpers together. The commits do not say how mismatched versions behave. If you build under Wine on large machines, start wineserver through a launcher that preloads the shim (the commit says ushell’s <code>.build --wine</code> does), and run <code>wineserver -k</code> first if one is already running.</p>
<p><strong>Who should care</strong></p>
<p>Build and CI engineers, build-farm owners, anyone running Mac, Linux, arm64, or Wine helpers.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you run UBA with Mac helpers or Wine on more than 64 cores. Track otherwise.</p>
<hr>
<h3 id="cook-and-staging-an-empty-header-race-shader-library-collisions-and-a-run-of-one-time-recooks">Cook and staging: an empty-header race, shader library collisions, and a run of one-time recooks</h3>
<p><strong>What changed</strong></p>
<p>Under multithreaded save, a process-wide guard for unversioned content raced across save threads. “The guard that ends first restores 0 while another thread is still reading its summary; that read stops early and the optimizer produces the package with an empty header (no name map, imports or soft references), logging only a warning.” The allowance is now per package, and “A bad cooked summary is now Fatal, not a warning plus a broken package” (<code>7ac3eb85</code>).</p>
<p>Staging and packaging fixes:</p>
<ul>
<li><strong>Named pakchunks and shader libraries.</strong> Sub-containers of a named pakchunk resolved “to the same shader library as the base container and closing it on unmount.” The mangled id also used “CityHash64 &amp; 0xFF, a 1-in-256 collision that could skip opening a library” (<code>b1794cbc</code>).</li>
<li><strong>Mac and Linux staging.</strong> “On Mac/Linux, DirectoriesToAlwaysStageAs* entries using Windows path separators are never found, so their contents go unstaged.” On one test, “Unable to find directory” warnings dropped “from 21 to 5” (<code>94f3ceb6</code>).</li>
<li><strong>DDC graph forwarding.</strong> <code>-ddc=&lt;graph&gt;</code> “was passed to the cook and run commandlets but never to UnrealPak, so UnrealPak always used the project’s default DDC graph” (<code>931595fd</code>).</li>
</ul>
<p>Plan for recooks. Several commits announce one-time invalidations:</p>
<ul>
<li>“Incremental cook impact: one-time recook of all blueprint packages” (<code>8d5abe19</code>).</li>
<li>“Incremental cook impact: one-time full recook. The global dependencies hash changes for every cook, in every reachability mode” (<code>a14aa680</code>).</li>
<li>“one-time recook of generated packages whose digest included transient or subsystem classes (WP cells with world subsystems)” (<code>c5a81cbb</code>).</li>
<li>Two CookIncrementalVersion bumps, both for a “Native change not picked up” (<code>40579ca1</code>, <code>f10e20cc</code>).</li>
<li>Shader source files are now hashed with <code>FShaderHash</code>, which “bumps shader map cache versions as a safety measure” (<code>6bfb22a7</code>), so expect a shader recompile as well.</li>
</ul>
<p>Two cook behavior changes apply without opting in. Loaded packages kept loader read buffers until GC, “which costs several GB of commit per cook process.” The flush now applies to “every cook-by-the-book cook outside the editor” (<code>2cde43c5</code>). A multithreaded save safety change notes, “This changes load timing for the default single-threaded cook too,” and cooks no longer write <code>GatherableTextData</code> (<code>27b85436</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The empty-header bug produced a broken package with only a warning. If you cook with multithreaded save, take this fix and treat any packages from affected builds as suspect. The shader library collision “could skip opening a library.” On Mac or Linux staging hosts, check whether <code>DirectoriesToAlwaysStageAs*</code> entries with backslashes were ever staged. Schedule the recooks: one full recook, one of every blueprint, and a shader recompile are all in this week’s range.</p>
<p><strong>Who should care</strong></p>
<p>Build and release engineers, platform teams staging on Mac or Linux, anyone running multithreaded save or incremental cooks.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you cook with multithreaded save or stage on Mac or Linux. Plan for the recooks.</p>
<hr>
<h3 id="ci-exit-codes-toolchain-floors-and-horde">CI exit codes, toolchain floors, and Horde</h3>
<p><strong>What changed</strong></p>
<p>Exit codes your wrappers may depend on:</p>
<ul>
<li><strong>Movie Render Queue.</strong> Invalid command line arguments “previously hit a Fatal log (crash, crash report). They now log an Error and exit with the existing MoviePipelineErrorCodes value (NoAsset = 2, NoConfig = 3, Critical = 1)” (<code>b097d529</code>).</li>
<li><strong>Mac build-machine crashes.</strong> “Mac build machines installed a log-only crash handler, so a farm crash left no CrashReport-UE-* folder for Gauntlet to collect.” Now “build-machine crashes write the report folder and skip CrashReportClient, exiting 1 instead of 3” (<code>4a65fd73</code>).</li>
</ul>
<p>Toolchain changes:</p>
<ul>
<li><strong>Enforced minimums.</strong> UBT published its current floors: “MSVC 14.44.35211,” “Windows Clang 20.1.8,” “Linux Clang 20,” “Android NDK r27c, Clang 18.0.3,” and “Xcode 26.1.1, Apple Clang 17 or later, LLVM 19.1.5 or later” (<code>18dae879</code>).</li>
<li><strong>MSVC 14.51.</strong> “UnrealBuildTool: Remove MSVC 14.51 from preferred” (<code>37a2daf1</code>). See the follow-up under Worth Tracking.</li>
<li><strong>macOS 15.</strong> “[Mac] Require macOS 15 Sequoia” (<code>8a45c509</code>). <code>bEnableOSX109Support</code> is now deprecated and ignored (<code>4753eda2</code>).</li>
<li><strong>Clang warnings as errors.</strong> “Clang’s -Wno-error=X overrides -Werror regardless of argument order, so these warnings stayed warnings.” Warning-level <code>CppCompileWarnings</code> now become errors under <code>bWarningsAsErrors</code> (<code>2599812f</code>).</li>
<li><strong>Build.sh.</strong> Mac and Linux now refresh UBT before use and serialize <code>Build.sh</code> per engine: “Concurrent runs wait” (<code>3ccad7e7</code>).</li>
<li><strong>Smaller fixes.</strong> A race writing PGO sidecar files during makefile creation on clean workspaces (<code>551c4523</code>), and a missing space that was “preventing -icf=all from working on Android” (<code>7b9d5661</code>).</li>
</ul>
<p>Horde:</p>
<ul>
<li><strong>Secrets in debug endpoints.</strong> “The config, app settings and environment debug endpoints returned secrets in clear text, also secrets resolved from the secret store.” They now show “[redacted]” (<code>76e3d23a</code>).</li>
<li><strong>Upgrade ordering.</strong> Tool deployments now stay pending until their archive is built. The commit warns: “Hold tool uploads until all servers run this version, because older servers do not know the pending state” (<code>67c42f3a</code>).</li>
</ul>
<p><strong>Why this is important</strong></p>
<p>If you build with warnings as errors on clang, this change can fail builds that previously passed, with no code change on your side. The exit code changes break the opposite way: a script that waited for a crash or matched exit code 3 on Mac will now misread the result. If you self-host Horde, upgrade for the redaction fix, consider whether anyone could reach those debug endpoints, and rotate secrets if so. Then upgrade every server before uploading tools.</p>
<p><strong>Who should care</strong></p>
<p>Build and CI engineers, render farm owners, Horde administrators, Mac and Linux build owners.</p>
<p><strong>Urgency</strong></p>
<p>Act now on Horde secrets and on clang warnings as errors. Check MRQ and Mac crash wrappers at your next sync.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>New BCn realtime encoders.</strong> “New, higher-quality BCn realtime encoders” come in three levels. Level 1 costs “about 15-20% extra compute” over the lowest level. The sRGB entry points now “convert all pixels to encoded space first, which is more expensive but can make a quite significant difference depending on content” (<code>3e4dc512</code>). The commit does not list which call sites use the new encoders, so check any GPU texture compression you rely on.</p>
<p><strong>Ray tracing enablement semantics.</strong> <code>IsRayTracingEnabledForProject</code> and <code>ShouldCompileRayTracingShadersForProject</code> “aren’t affected by runtime settings when running non-editor executable” (<code>b2d94575</code>). <code>IsRayTracingEnabled(EShaderPlatform)</code> “now takes project settings into account,” and passes should use <code>CanUseRayTracingShaders</code> and <code>CanUseInlineRayTracing</code> at runtime (<code>a73d5cc6</code>). Custom ray tracing shaders should no longer use <code>Hit.InstanceID</code> or <code>GetInstanceUserData()</code> directly as a GPU Scene instance id, and should go “through DecodeRayTracingInstanceId(…)” (<code>b083d033</code>). If you maintain custom ray tracing passes, read all three.</p>
<p><strong>PCG Component Lite and UnrealConsole.</strong> PCG Component Lite went from P9 to P11 this week, adding “editor discovery, details actions, preview controls and debugging” (<code>3344fa1a</code>), interactive and manual-edit tools (<code>0087d60c</code>), and editor partitioning (<code>8e742167</code>), plus a “PCG Volume Lite that will instantiate a PCG Component Lite” (<code>5a53d40b</code>). A new switch, <code>ShouldUsePCGVolumeLite()</code>, now chooses between Lite and legacy volumes (<code>f84cac56</code>), but no commit states its default or a migration path. UnrealConsole, the headless editor host we flagged last week, kept moving: <code>ULevelEditorSubsystem</code> methods “now act as if no level editor is open, instead of check()ing or dereferencing null” (<code>d3cf44c5</code>).</p>
<p><strong>Two follow-ups on last week’s post.</strong> Last week we reported that the change enabling the new platform IO dispatcher on all platforms was backed out with no reland, so the new dispatcher was not enabled. This week it is on by default for game and client targets, after a deadlock fix (<code>a26cc9d9</code>, <code>b865826a</code>). Server and editor targets keep the old path. We also covered MSVC 14.51 being made the preferred toolchain and then banned for a miscompile. This week it was removed from the preferred list (<code>37a2daf1</code>), and the diff adds a <code>SuggestedVisualCppVersion</code> so the ban error no longer suggests a banned toolchain. Two other items we flagged last week, the occlusion feedback opt-in and the renamed ray-traced skinning cache CVars, had no new commits.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>This week we ignored most of the 1,168 commits: Verse and UEFN work, scene graph changes, Python API expansion, Niagara editor polish, test automation tooling, Mesh Partition churn, IoStore on-demand container TOC patching, and narrow editor crash fixes. There were 17 commits tagged <code>[Backout]</code>, against 24 last week, inside the normal band for this branch. Most relanded in-window.</p>
<p>Three that did not come back are worth naming, because their titles make them sound shipped. “[Component visualizers] Enable ITF Visualizers by default” (<code>ee128853</code>) was backed out (<code>3dae15ca</code>), so that default did not change. “Niagara - Fix crash when a GPU stage only reads an array data interface that another GPU stage in the same system writes to” (<code>3b713266</code>) was backed out (<code>0639068e</code>), so that crash is still unfixed on this branch. And “Linux: Rectify stacktrace cutoff-frame semantics” (<code>a6a58aff</code>) was backed out (<code>da381317</code>), so Linux stack trimming is unchanged.</p>
<p>A few one-liners. A network ping bug wrote “into freed memory, which surfaced as heap corruption in unrelated systems (e.g. Slate FTextLayout teardown) on the Connecting screen” when two or more ping types were configured as moving averages (<code>3ce03197</code>). Perforce source control disabled the p4api signaler outside Windows, citing “evidence” that “concurrent connections corrupt the heap on Linux in particular” (<code>d574ddfd</code>). “Fixed vulnerability issue in InterchangePSDTranslator,” with no further detail in the message (<code>4310366a</code>). And the legacy Project Launcher was removed (<code>92cf4524</code>), along with its “deprecated Project Launcher and Launcher Services APIs” (<code>70c92023</code>).</p>
<hr>
<h2 id="for-teams-shipping-on-58">For teams shipping on 5.8</h2>
<p>On Monday, October 5, the public <code>5.8</code> branch took “Update version for 5.8.4” (<code>8ef25888</code>), which changes the patch version from 3 to 4 and carries a <code>#lockdown</code> tag. It came with 108 cherry-picked fixes, all dated the same day in one contiguous block of changelists. One of them is explicit: “Backport UE-395636 for the UE 5.8.4 hotfix candidate: prevent stale editor-instance access in UAssetEditorSubsystem::CloseAllEditorsForAsset,” noting “Static compatibility checked against Dev-Release-5.8” (<code>f1190727</code>). There is no 5.8.4 release tag yet. The newest is 5.8.3, so treat this as a candidate that can still change. Of the 113 commits, 4 are Localization Automation and none is a backout. About 30% of them (33 of 108) match the titles of fixes that were on <code>ue5-main</code> or <code>ue6-main</code> last week. About one in five of the rest (16 of 75) carry cherry-pick tags pointing at older changelists.</p>
<p><strong>In the 5.8.4 candidate</strong></p>
<ul>
<li><strong>UBT was not relinking binaries when external libs changed.</strong> The <code>FirstOrDefault</code> check “was not equivalent to Contains’ behavior. This was breaking the timestamp check on external libs and preventing binaries from being relinked” (<code>1fb9a4a0</code>). If you link prebuilt external libraries, incremental builds could have shipped stale binaries. Take this one first.</li>
<li><strong>UAT log output stalled.</strong> A bare <code>=</code> toolchain separator was mistaken “for an ASAN report header,” which “stalls all UAT log output until the LogEventParser 1024-line cap” (<code>451845be</code>). Separately, UAT now starts commandlets with a UTF-8 environment, fixing “garbled output from the commandlets when running UAT on a system that is set to a non-latin codepage (Japanese, Chinese…)” (<code>8c37fa5d</code>). Last week this fix was on <code>ue5-main</code> only.</li>
<li><strong>Horde path traversal.</strong> “Horde: Prevent path traversal when extracting artifact and workspace contents.” Anything that “would resolve outside the target directory now fails closed and aborts the operation instead of writing the file” (<code>b3f8a2e2</code>). A security fix for anyone running Horde on 5.8.</li>
<li><strong>Virtual texture DDC crash after a full disk.</strong> “If the disk was full (or on other IO errors) the temp file was left short but still flagged as available,” and “The existing file validation only hashes the first 32 KB, so a file truncated beyond that passed validation on later editor runs and kept crashing” (<code>6fc486ee</code>). Build machines with tight disks should take this.</li>
<li><strong>iOS UIScene lifecycle is now the default.</strong> “Switch iOS client to use UIScene lifecycle by default to avoid crash on start. When an iOS app is compiled with Xcode27, iOS27 enforces use of UIScene” (<code>915fcb2f</code>). The diff sets <code>bUseSceneBasedLifecycle=True</code> in <code>BaseEngine.ini</code>. The C++ default stays false, so projects that explicitly set it to False in their own config keep the old lifecycle. The header calls it compile-time only, so a rebuild is required after changing it.</li>
<li><strong>iOS finalize on macOS 27.</strong> iOS .app finalize failed with a “Bad file descriptor” error on macOS 27. The build executor spawned actions without stdin, and xcodebuild “then cannot launch /bin/sh for a scheme’s script phases” (<code>269b89a9</code>).</li>
<li><strong>Vulkan fence default flipped.</strong> The message reads “change the default for r.Vulkan.CpuWaitForFence to use semaphores on desktop” (<code>1ad8824e</code>). The diff changes the code default from 1 to 0 with no platform check. Android already set 0 through <code>AndroidEngine.ini</code>, so in practice desktop Vulkan (Windows and Linux) is what changes. The CVar is read-only. To restore the old behavior, set <code>r.Vulkan.CpuWaitForFence=1</code> in config.</li>
<li><strong>D3D12 enhanced barriers.</strong> “Fix enhanced barrier layout selection and synchronization,” a manual integration of three changelists. Queue-specific layouts meant “resources transitioned on one pipe were left in layouts invalid to consume on the other,” and transient aliasing acquires now synchronize “with SYNC_ALL” (<code>9d10f6af</code>). Relevant if you use async compute on D3D12.</li>
<li><strong>Shader preprocessor heap overread.</strong> “a source with an unmatched quote or apostrophe therefore scanned past the NUL terminator into adjacent heap memory,” traced to “material custom node text.” Such files now report “Unclosed string literal.” (<code>b3eb2d65</code>).</li>
<li><strong>Linux crash reporting.</strong> “Stop Linux crash reporting from exhausting the crash allocator’s large pool.” A new <code>unix.CaptureAllThreadStacksMaxChars</code> bounds the all-thread dump (<code>5d863562</code>). The diff sets it to 96 * 1024 characters by default, with 0 meaning unbounded.</li>
<li><strong>RigVM editor-only data in cooked packages.</strong> The editor asset was created <code>RF_Public</code>, so “the editor asset and its model/EdGraph tree were saved into cooked packages, producing missing class errors for ControlRigDeveloper/RigVMDeveloper types on load.” PostLoad now clears the flag “so existing assets are fixed too” (<code>9576cbc6</code>). <code>ue5-main</code> already had its own fix and took only the PostLoad cleanup for older assets, as a “Partial forward port” (<code>049211f1</code>).</li>
<li><strong>DDC-dependent output.</strong> Substrate Toon materials lost a GBuffer write “when fetching from DDC” (<code>74bd88e7</code>), and Mutable’s DDC value changed “from compilation to compilation” (<code>ee0e70f0</code>).</li>
<li><strong>Crashes and regressions.</strong> An XSX “EOS delay-load crash on launch with modern GDK folder layout” (<code>4c0f5eda</code>), alongside an EOSSDK 1.19.2.1 binary update that lists deprecated console SDKs “to be removed in a future release” (<code>5da24655</code>). A Mac editor exit crash whose crash group “reports 37 users and 37 occurrences on 5.8.x” (<code>5561a4c5</code>). A Sequencer world leak that “fatal errors as a world leak” when a level containing a level sequence actor is removed (<code>17f4598e</code>). A Promote To Variable rename bug that “regressed in 5.8” (<code>f7583e0b</code>), and Mover “concurrency-related crashes” (<code>9bb4abbe</code>).</li>
<li><strong>Fixes we flagged earlier.</strong> The landscape edit layers assertion “when two landscape components overlap,” whose message notes “New functions intentionally omit LANDSCAPE_API to make the CL hotfix compatible” (<code>5eb15c8c</code>), is in the candidate. Last week we guessed it was headed for a hotfix. The CEF update that “Fixes editor crashes when the GPU process is reported as unusable” is in too (<code>3d8fd89f</code>).</li>
<li><strong>Editor quality of life.</strong> A Control Rig fix cuts a save stall in Animation Mode that scaled with the number of characters. The repro “goes from 11-17s to 1.7s” (<code>5a62d8e6</code>). A PCG refresh fix adds <code>pcg.ActorTracking.EnablePreviousDataCleanupHotFix</code>, on by default in the diff, as a guard “if it ever has side effects” (<code>3b767ba6</code>).</li>
</ul>
<p><strong>On <code>ue5-main</code> only</strong></p>
<p><code>ue5-main</code> ran 92 commits with three backouts, one of them relanded in-window. None of the items below carries a message saying it is headed for 5.8.x.</p>
<ul>
<li><strong>Force delete crash.</strong> Force-deleting an asset whose editor edits more than one of its objects “closed the editor once per listing, and the first close destroys the toolkit, so the second ran on freed memory” (<code>06cc4cd6</code>).</li>
<li><strong>Control Rig duplicate and paste.</strong> A regression where “every derived field was dropped on duplicate and paste,” so “A duplicated control lost its limits, rotation order and min/max” (<code>d1ba8344</code>).</li>
<li><strong>Cook determinism.</strong> Package access tracking fixes for recording “that depended on load order, GC timing and which process (director or CookWorker) saved the package, which caused spurious and missed incremental recooks.” Expect a “one-time recook of most blueprints” (<code>4425f181</code>).</li>
<li><strong>Sequencer spawnables.</strong> “Spawnables vanish permanently after undoing a space switch compensation” (<code>77f056f7</code>).</li>
<li><strong>Smaller fixes.</strong> A water info LOD gather that “crashed the editor whenever a water zone rendered its info texture” (<code>5263eb77</code>), and a “noisy/black thumbnail when enabling separate translucency” (<code>ae65f725</code>).</li>
<li><strong>Gaussian Splat came over from UE6.</strong> Seven batches merged “//UE6/Main Gaussian Splat work into //UE5/Main,” one of which includes “Enable the GaussianSplat plugin” (<code>d7ec4548</code>).</li>
</ul>
<hr>
<h2 id="closing">Closing</h2>
<p>The 5.8.4 candidate is the part of this week most readers can use right now, and it is still open. On <code>ue6-main</code>, if behavior changes after this sync, check the defaults above before you start bisecting.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine, except in the “For teams shipping on 5.8” section: there, items under “In the 5.8.4 candidate” reference the public <code>5.8</code> branch (the forward port <code>049211f1</code> is on <code>ue5-main</code>), and items under “On <code>ue5-main</code> only” reference <code>ue5-main</code>.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-oct-5-11-2026/og.5b623d5b29.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: Epic Preferred MSVC 14.51, Then Banned It for a Miscompile (September 28 to October 4, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-sep-28-oct-4-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-sep-28-oct-4-2026</guid>
      <pubDate>Mon, 05 Oct 2026 07:49:36 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>1,065 commits on ue6-main. MSVC 14.51 became UBT’s preferred toolchain on Thursday and was banned on Friday for an optimizer miscompile. The ray-traced skinned mesh cache renamed its CVars so old ini overrides are ignored, an occlusion feedback regression fix ships switched off, and incremental cooks got a run of correctness fixes.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-epic-preferred-msvc-1451-then-banned-it-for-a-miscompile">Unreal Engine Weekly: Epic Preferred MSVC 14.51, Then Banned It for a Miscompile</h1>
<p><strong>Week of September 28 to October 4, 2026</strong> | <strong>1,065 commits on <code>ue6-main</code>, 123 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>On Thursday, <code>Windows_SDK.json</code> added “14.51.36231-14.51.99999 as the top preferred MSVC range so UnrealBuildTool selects it when installed” (<code>b9836eed</code>). On Friday, the same file banned it: “Ban MSVC 14.51 due to an optimizer miscompile causing memory corruption and HeadlessChaos cluster assertions. Reproduced with cl 19.51.36260; /d2SSAOptimizer- avoids the failure.” (<code>71b4bbb5</code>). Epic caught a compiler bug in its own build within a day of choosing that compiler. If your build machines picked up 14.51 on their own, check that first.</p>
<p>Much of the rest of the week changes what your build or game does without producing an error. The ray-traced skinned mesh cache renamed its console variables, and the commit says outright that “project ini overrides of the old names are ignored” (<code>95930ecc</code>). PSO precache deferral went on by default (<code>7cc9b379</code>), Mac bindless went on by default (<code>d0841228</code>), and a mesh pass sort key had been returning 0 (<code>69b6a2af</code>). The cooker also got a run of incremental correctness fixes, including one that recooked “every Blueprint deriving from a class with SparseClassDataTypes” on each incremental cook (<code>fd0a139c</code>).</p>
<p>At 1,065 commits, <code>ue6-main</code> came in just under last week’s 1,072, the busiest week since this briefing started tracking it in June. Among named subsystems, build leads at 104, followed by editor at 86, rendering at 69, and core at 67.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<h3 id="agent-tooling-eased-off-its-peak-and-kept-building-out">Agent tooling eased off its peak, and kept building out</h3>
<p><strong>Pattern:</strong> counting commits that touch the toolset registry, the AI Assistant plugin, the experimental toolsets, <code>AsyncFunctionResult</code>, and <code>ToolsetCore</code> (by path or by name in the message, with mesh modeling toolsets excluded), the cluster came to 46 this week, against 56 last week, 30 the week before, and 20 the week before that. It is off last week’s high and still more than double the early September count.</p>
<p><strong>Interpretation:</strong> the work moved from structure to behavior. A new MediaToolset plugin adds “four read-only AI-callable tools: ReadImageFile, GetVideoInfo, SkimVideo and SampleFrames,” with video decoding through Media Foundation “gated on PLATFORM_WINDOWS” (<code>03b4fb0f</code>). ObjectToolset now refuses writes to class default data a Blueprint does not own. Before, “an agent could write to a native CDO like Default__StaticMeshActor, or one of its default subobjects, and change every object of that class created for the rest of the session” (<code>a1e938cf</code>). Agent undo got sturdier: each tool call’s edits now go into their own buffer (<code>7406cee2</code>), and undoing through the EDA “now reverts unconditionally, hand edits included” (<code>c9b057b6</code>). One change did not stick: AllToolsets, EditorToolset, and AnimationAssistantToolset were restricted to desktop platforms early in the week (<code>ff17b2f4</code>), and the restriction was reverted on Friday, “removing SupportedTargetPlatforms from the AllToolsets, AnimationAssistantToolset, and EditorToolset plugins” (<code>258545c3</code>). We are watching UnrealConsole most closely, covered under Worth Tracking.</p>
<p><strong>Who cares:</strong> tools programmers, pipeline and automation teams, anyone scripting the editor through the toolset APIs.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="uba-is-still-one-engineers-work">UBA is still one engineer’s work</h3>
<p><strong>Pattern:</strong> counting commits touching <code>UnrealBuildAccelerator</code> the same way each week: 37 with 29 from one engineer (Sep 7 to 13), 19 with 10 from a second engineer and 7 from the original (Sep 14 to 20), 36 all from the original (Sep 21 to 27), and 19 this week, all from the same engineer behind last week’s 36.</p>
<p><strong>Interpretation:</strong> last week we dropped our handoff reading, and this week supports that. Volume fell to 19, but authorship did not change. The work is mostly Linux and Ninja: Go and static-glibc syscalls routed through UBA’s virtual filesystem (<code>47ff3089</code>, <code>4d7e530e</code>, <code>a8fbc0eb</code>), generated Ninja dependencies kept so “consumers rebuild after changed generators” (<code>a2c1462d</code>), and oversized static command lines that “fail cleanly with E2BIG” when they still do not fit (<code>f987c8ac</code>). The storage proxy fix in Highlights is the one most farms should take.</p>
<p><strong>Who cares:</strong> build and CI engineers, anyone whose build farm depends on UBA.</p>
<p><strong>Signal strength:</strong> Moderate.</p>
<h3 id="cook-correctness-and-dlc-cooking-in-particular">Cook correctness, and DLC cooking in particular</h3>
<p><strong>Pattern:</strong> twelve commits this week open with “Cooker:”, most of them under one DLC ticket, alongside a set of incremental cook fixes and a merge of determinism fixes from Main. The low tier alone carries about 25 cook determinism commits, a series from one engineer among them.</p>
<p><strong>Interpretation:</strong> the commits suggest Epic is making incremental cooks something it trusts for shipping as well as iteration. Several fixes close cases where an incremental cook either did too much work or skipped work it should have done, and the DLC series adds warnings for content a DLC cook depends on but did not cook.</p>
<p><strong>Who cares:</strong> build engineers, release managers, anyone running incremental or DLC cooks.</p>
<p><strong>Signal strength:</strong> Moderate.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li><strong>Check which MSVC your Windows build machines are using.</strong> 14.51 became the top preferred range on Thursday (<code>b9836eed</code>) and was banned on Friday for “an optimizer miscompile causing memory corruption and HeadlessChaos cluster assertions” (<code>71b4bbb5</code>). 14.50 remains available.</li>
<li><strong>The ray-traced skinned mesh cache CVars were renamed, and old names are ignored.</strong> <code>r.Skinning.RayTracingCache</code> is now <code>r.Skinning.Cache.RayTracing</code> (<code>95930ecc</code>). Refits now default to an unlimited budget instead of the old “250k triangles per frame” cap (<code>3bd88216</code>), and a new <code>r.Skinning.Cache.RayTracing.BudgetMB</code> defaults to 256 (<code>98f9acf2</code>).</li>
<li><strong>An occlusion feedback regression fix is off by default.</strong> With <code>r.OcclusionFeedback.Enable=1</code>, spurious visible verdicts kept re-arming skeletal pose ticking and Niagara visibility work. The fix sits behind <code>r.OcclusionFeedback.UntestedAsIndefinite</code>, “(default 0)” (<code>46ea29b0</code>). If you run occlusion feedback, opt in.</li>
<li><strong>Incremental cooks got more correct.</strong> Blueprints with sparse class data were recooked on each incremental cook (<code>fd0a139c</code>), and a comparison bug “led to missed incremental recooks” (<code>2e1c5832</code>). The determinism merge from Main shipped after one backout (<code>78a2556e</code>).</li>
<li><strong>Several defaults moved.</strong> <code>r.PSOPrecache.DeferUntilPostLoad</code> is on (<code>7cc9b379</code>), Mac bindless is on (<code>d0841228</code>), two Chaos user data fixes default on (<code>f5ecfea2</code>, <code>0b2283ee</code>), and the Sequencer landing mode went from 2 to 3 (<code>868a4600</code>).</li>
<li><strong>UBA’s storage proxy could hang fetches “until 10 minute timeout”</strong> and leave helpers looping (<code>94dbca45</code>). Launch On iteration lost about 4 seconds without AutoSDK (<code>d65c5690</code>) and about 2 seconds on Android (<code>d42c6f51</code>).</li>
<li><strong>If you ship on 5.8:</strong> an XSX EOS delay-load crash on launch, garbled UAT commandlet output on non-Latin build agents, a Lumen atlas cleared each frame while a SceneCapture2D renders, and a hotfix-compatible landscape edit layer fix are in the 5.8 section below, with commit references.</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="msvc-1451-was-preferred-then-banned">MSVC 14.51 was preferred, then banned</h3>
<p><strong>What changed</strong></p>
<p>Two commits a day apart, in changelist order. First, “Windows_SDK.json: prefer MSVC 14.51,” adding “14.51.36231-14.51.99999 as the top preferred MSVC range so UnrealBuildTool selects it when installed. 14.50 and 14.44 remain as fallbacks.” (<code>b9836eed</code>). A companion commit fixed 14.51 validation warnings (<code>4a4575f1</code>). Then: “Ban MSVC 14.51 due to an optimizer miscompile causing memory corruption and HeadlessChaos cluster assertions. Reproduced with cl 19.51.36260; /d2SSAOptimizer- avoids the failure. Valid installed toolchains, including 14.50, remain available.” (<code>71b4bbb5</code>).</p>
<p><strong>Why this is important</strong></p>
<p>A miscompile that shows up as memory corruption does not fail the build. It can ship and surface later as a crash that is hard to trace back to the compiler. The message cites HeadlessChaos cluster assertions as the symptom Epic saw. If a Windows build machine installed 14.51 and your engine version was between these two commits, or if you set toolchain preferences yourself, check the compiler version in your build logs. The commit names a workaround flag, <code>/d2SSAOptimizer-</code>, but the engine’s answer is to not use 14.51 at all.</p>
<p><strong>Who should care</strong></p>
<p>Build engineers, CI owners, anyone maintaining Windows build images.</p>
<p><strong>Urgency</strong></p>
<p>Act now if any Windows build machine has MSVC 14.51 installed.</p>
<hr>
<h3 id="the-ray-traced-skinned-mesh-cache-was-rebuilt-and-its-cvars-renamed">The ray-traced skinned mesh cache was rebuilt, and its CVars renamed</h3>
<p><strong>What changed</strong></p>
<p>Four commits from one engineer rebuilt the ray tracing cache for animated instanced skinned meshes (ISKM). Storage became demand-driven: “only active slots own a block and a BLAS, so memory follows active slots and inactive slots are never skinned or built” (<code>781f4415</code>).</p>
<p>The cache then moved onto the shared dynamic geometry budget. The commit flags a “Behavior change with default settings: the manager’s budget is unlimited by default, so every visible cache slot is refit every frame (previously capped at 250k triangles per frame by r.Skinning.RayTracingCache.MaxTrianglesPerFrame).” It measured “cache skinning 0.01 -&gt; 0.21 ms p50” on its perf test level and says “Projects that relied on the old cap should set r.RayTracing.DynamicGeometry.MaxUpdatePrimitivesPerFrame.” It removed <code>r.Skinning.RayTracingCache.MaxTrianglesPerFrame</code> and <code>r.Skinning.RayTracingCache.RebuildInterval</code>, and fixed “a latent cross-queue race with r.RayTracing.AsyncBuild 1” (<code>3bd88216</code>).</p>
<p>The same day, the names changed: “Impact: renamed console variables, so project ini overrides of the old names are ignored.” <code>r.Skinning.RayTracingCache</code> became <code>r.Skinning.Cache.RayTracing</code>, <code>MaxInitialBuildTrianglesPerFrame</code>, <code>PageSizeMB</code>, and <code>PageIdleFrames</code> moved under <code>r.Skinning.Cache.RayTracing.*</code>, and <code>r.Skinning.RayTracingCache.Audit</code> became <code>r.Skinning.Cache.Audit</code> (<code>95930ecc</code>).</p>
<p>Two days later, slots got per-slot LODs and a scene-wide memory budget, <code>r.Skinning.Cache.RayTracing.BudgetMB</code> “(default 256, 0 = unlimited).” Nearby instances now “trace finer LODs (the cache used the coarsest LOD),” and scenes over budget trace their farthest instances with static geometry: “a 10,000 instance crowd: 642 MB -&gt; 256 MB.” Render thread cost of the update is quoted at “0.18 ms for 4,000 instances, 0.50 ms for 10,000 instances (Development)” (<code>98f9acf2</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Two separate changes can bite here. A config file that sets any <code>r.Skinning.RayTracingCache.*</code> variable now sets nothing, with no error. And with default settings, refits are no longer capped, which the commit itself says “costs more GPU with many animations.” If you tuned crowds against the 250k cap, your GPU budget moved. The commit also notes that at a matched 250k budget “GPU cost is unchanged and cache CPU time in the scene update drops 20-88%,” so setting the shared budget explicitly gets you the old cost with the new scheduling.</p>
<p><strong>Who should care</strong></p>
<p>Graphics programmers, technical artists running large animated crowds under ray tracing, anyone with skinning cache CVars in config.</p>
<p><strong>Urgency</strong></p>
<p>Act now if your project config sets any of the old CVar names or relied on the 250k refit cap. Track otherwise.</p>
<hr>
<h3 id="the-chunk-assigner-can-now-be-enabled-from-a-project-setting">The chunk assigner can now be enabled from a project setting</h3>
<p><strong>What changed</strong></p>
<p>Client and game targets can now turn on the oplog chunk assigner with a project setting, <code>[/Script/UnrealEd.ProjectPackagingSettings] bRunChunkAssigner</code>, which the commit adds to BaseGame.ini as “Default False.” DLC reads the plugin’s <code>Config/Game.ini</code> first. “Dedicated servers only run it with -RunChunkAssigner / -ChunkAssigner=.” The commit message reads as if the setting replaces the flag, but the diff shows otherwise: the flag’s help text now says it forces the assigner “for every target, including dedicated servers,” and the setting decides only “Without this flag” (<code>bb4f6b5a</code>). An earlier commit passed the metadata directory through to the assigner (<code>b8279ecf</code>), and a follow-up stages <code>GameFeatureVersePaths.bin</code> explicitly, deleting any previous copy first “so a stale copy is never paked” (<code>a6cdc4f9</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Nothing breaks. Pipelines that pass <code>-RunChunkAssigner</code> keep working, and with the setting defaulting to False, projects that never used the assigner see no change. What is new is that a project can turn the assigner on for client builds in config instead of carrying a command-line flag through every BuildCookRun invocation, and that the message’s “instead of” wording overstates the change if you read it alone.</p>
<p><strong>Who should care</strong></p>
<p>Build and release engineers, anyone using the oplog chunk assigner.</p>
<p><strong>Urgency</strong></p>
<p>Track. Consider moving to the project setting if you already pass the flag for client builds.</p>
<hr>
<h3 id="incremental-cooks-stopped-overcooking-and-undercooking">Incremental cooks stopped overcooking and undercooking</h3>
<p><strong>What changed</strong></p>
<p>Three fixes, failing in different directions. The Blueprint native class dependency hash varied with sparse class data creation order, “which caused every Blueprint deriving from a class with SparseClassDataTypes to be reported as Native Class Changed and recooked on every incremental cook” (<code>fd0a139c</code>). In the other direction, “Algo::CompareAsInt in ARFilter.cpp repeated LessThan(*A, *B), so it never returned +1,” the cook’s asset registry access tracker overwrote records on hash collisions, and “the dropped AR-query build dependencies led to missed incremental recooks” (<code>2e1c5832</code>). And packages importing a class that CoreRedirects declares removed now stay eligible for incremental cooking (<code>760e6ed4</code>).</p>
<p>“Merge cook determinism fixes from Main” covers “cook dependency tracking, deterministic actor naming and ordering, archetype inheritance, soft object path fixup, shader library sorting.” It was backed out once and relanded inside the week (<code>78a2556e</code>).</p>
<p>For DLC, CookImportChecker now runs on DLC cooks, “giving warnings for missing hard imports and adding missing soft imports to the Cook.ReferencesOfUncooked op in the DLC cook’s oplog” (<code>45178bb1</code>). <code>-DlcPluginOnly</code> had a plain bug: “Packages outside of the plugin were still being cooked” (<code>b4a4d9cc</code>). The uncooked references list now works for loose file cooks (<code>02b84132</code>), and UnrealPak and IoStore no longer look for <code>DevelopmentAssetRegistry.bin</code> to find metadata, because “-NoSaveDevAR skips it for performance” (<code>1f7d40c4</code>).</p>
<p>On speed, a material change adds <code>cook.MICParameterLookupCache</code> “(default 0; set by -CookExperimentalOptimizations)” and measures “UpdateParameters 602 s -&gt; 8 s, about -4.8% wall” on a content subset. Two caveats come with it: materials see a “one-time TargetDomainKey change,” and “UMaterialInstance editor layout change (ReentrantFlag removed); editor plugins must rebuild” (<code>8a5911ac</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The sparse class data bug is easy to spot, because incremental cooks do far more work than they should. The comparison bug is worse, because a missed recook can leave stale cooked data in a build with nothing to flag it. If you rely on incremental cooks for anything that goes to players, take <code>2e1c5832</code>. Expect one larger-than-usual incremental cook after <code>8a5911ac</code> because of the key change, and rebuild editor plugins that touch <code>UMaterialInstance</code>.</p>
<p><strong>Who should care</strong></p>
<p>Build engineers, release managers, DLC and plugin content teams, editor plugin authors.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you ship from incremental or DLC cooks. Track otherwise.</p>
<hr>
<h3 id="defaults-that-moved-this-week">Defaults that moved this week</h3>
<p><strong>What changed</strong></p>
<p>None of these announce themselves at build time.</p>
<ul>
<li><strong>PSO precache deferral is on.</strong> <code>r.PSOPrecache.DeferUntilPostLoad (default 1)</code> defers precaching on loaded components until their own PostLoad, because an actor’s PostLoad can change materials first. “Behavior change: on by default. r.PSOPrecache.DeferUntilPostLoad=0 restores the previous behavior.” (<code>7cc9b379</code>).</li>
<li><strong>Mac bindless is on.</strong> “Enable Bindless on all Mac platforms by default.” The diff changes <code>BindlessConfiguration</code> from <code>Disabled</code> to <code>All</code> in <code>BaseMacEngine.ini</code> (<code>d0841228</code>).</li>
<li><strong>Chaos user data, two fixes default on.</strong> <code>p.Chaos.UserDataPT.SkipNullPayloadOnAdd</code>, “default on” (<code>f5ecfea2</code>). And <code>p.Chaos.UserDataPT.SkipInputsDuringResim</code>, “default on,” because a resim replay “stored nulls over live data and repeated old removals” (<code>0b2283ee</code>). Seven commits added eight new collision options alongside, each “default off” in its message (<code>2a3d0def</code>, <code>60a3728a</code>, <code>cb076f03</code>, <code>eb642d3c</code>, <code>dc5169d4</code>, <code>7c340dd7</code>, <code>20f3192e</code>).</li>
<li><strong>Sequencer landing mode.</strong> “Change the default Sequencer.SwitchBoundary.OccupiedLandingMode from 2 to 3 (keep the existing world at the moving boundary and always add a key at the drag start)” (<code>868a4600</code>).</li>
<li><strong>Animation tick ordering.</strong> <code>EndPhysicsTick</code> now waits on the skeletal mesh primary tick, with an opt-out, <code>tick.EndPhysicsTickWaitsForAnimation=0</code> (<code>53d95ea0</code>).</li>
<li><strong>Mesh pass sorting was off.</strong> “Fix TShaderRef::GetSortKey() always returning 0, which left every mesh pass sorted by state bucket id instead of by shader” (<code>69b6a2af</code>). The message does not say when the bug came in.</li>
<li><strong>An occlusion feedback fix you have to opt into.</strong> With <code>r.OcclusionFeedback.Enable=1</code>, spurious visible verdicts re-armed “the 1 second bRecentlyRendered window and all the work keyed on it: skeletal pose ticking (ShouldTickPose, URO, Animation Budget Allocator) and effects.” The fix sits behind <code>r.OcclusionFeedback.UntestedAsIndefinite</code>, “(default 0),” and the diff confirms 0 (<code>46ea29b0</code>).</li>
</ul>
<p><strong>Why this is important</strong></p>
<p>Act on the occlusion feedback item first. The commit calls it “a game thread regression,” but the default leaves the regression in place, so a project running occlusion feedback has to set <code>r.OcclusionFeedback.UntestedAsIndefinite=1</code> (or already run <code>r.OcclusionFeedback.VisibleTestPeriod</code> above 1) to get the fix. The sort key fix is a silent performance change: mesh passes now sort by shader as intended. The message gives no performance numbers, so measure before and after if you profile draw submission closely. One adjacent fix: setting <code>r.CustomDepth</code> to the value it already had reregistered “every component in every world for no change: a large hitch” (<code>1ff8ad7a</code>).</p>
<p><strong>Who should care</strong></p>
<p>Graphics and gameplay programmers, Mac teams, network physics programmers, Sequencer animators, performance engineers.</p>
<p><strong>Urgency</strong></p>
<p>Act now on occlusion feedback if you run it. Track the rest at your next integration.</p>
<hr>
<h3 id="mobile-and-lower-end-gpus-got-more-reach">Mobile and lower-end GPUs got more reach</h3>
<p><strong>What changed</strong></p>
<p>Ambient Lighting Volumes moved to mobile. The commit cuts instance data “from 6 uint4 to 3 uint4,” reduces “the overhead cost from 0.5ms to 0.3ms when simply enabling ALVs on a mobile platform,” notes that “android has been able to cook with ALV enabled,” and concludes “ALV is now enabled by default on mobile platforms” (<code>ee32ab5b</code>). The diff shows no CVar or ini default change, and the new mobile shader path is still gated on the project’s ALV support setting. The day after, ALVs were disabled again in the mobile merged light pass “due to out of smaplers for some game materials” (<code>63a418c0</code>), then re-enabled two days later after a texture slot was saved so they “should fit withing the 16 samplers limit of Android OpenGL” (<code>ca4699fe</code>).</p>
<p>On desktop, a new <code>r.SM6.RequireAtomic64 (default: true)</code> lets otherwise-compatible D3D12 and Mac GPUs select SM6 without 64-bit typed atomics when set to false, while it will “Keep Nanite unavailable on unsupported GPUs” (<code>6b14511d</code>). The engine default is unchanged. Lyra is the only project that sets it false this week, with “Nanite remains disabled on those GPUs” (<code>c9d18676</code>).</p>
<p>For Adreno, a SPIR-V optimizer pass now “stores a null value into every fragment output the shader declares but never writes,” because “Pixels shaders that do not write to all declared outputs may hang Adreno GPUs” (<code>1b9401b7</code>). It replaces an earlier runtime fix for the same GPU failure on the “Galaxy S24 with Adreno 750,” backed out with the note “Shader fix now applied in a ShaderConductor” (<code>ddf3b96f</code>). Separately, debug view markup had “pushed landscape materials over the ES3.1 16 sampler limit” under the new material translator (<code>daeb62f8</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The ALV message says enabled by default on mobile, and the commits back that up as a cost reduction rather than a flag flip. Whether your mobile build uses ALVs still depends on your project’s ALV support setting, so check yours. Three commits this week ran into mobile sampler limits (16 on ES3.1 and Android OpenGL), so expect more of that. The SM6 change opens a path for GPUs without 64-bit atomics, but only if you opt in, and Nanite stays off on those GPUs either way. The Adreno fix lands in the shader compiler, so it applies after a shader rebuild.</p>
<p><strong>Who should care</strong></p>
<p>Mobile rendering engineers, Android teams, technical artists using ALVs, teams supporting older desktop GPUs.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you ship on Adreno and have seen GPU hangs. Track otherwise.</p>
<hr>
<h3 id="uba-fetch-hangs-fixed-and-launch-on-got-faster">UBA fetch hangs fixed, and Launch On got faster</h3>
<p><strong>What changed</strong></p>
<p>UBA’s storage proxy fix covers several ways a fetch could stall. The proxy used to drop the client when the server could not provide a file, which “made the helper get the same proxy again and loop reconnecting forever.” A leaked begin message meant “all later fetches of that file hung until 10 minute timeout.” And “A failed fetch begin is no longer cached forever in the proxy” (<code>94dbca45</code>).</p>
<p>Launch On iteration got two cuts. Without AutoSDK, editor and commandlet startup ran a BuildUBT up-to-date check that “costs ~4s even when nothing changed, while UBT itself takes ~0.7s. This is paid on every cook in Launch On Device.” It is now skipped when UBT is launched with <code>bSkipBuildUBT</code> (<code>d65c5690</code>). On Android, AFS deploy avoids “a second file server start on every iterative deploy (~2s per Launch On)” (<code>d42c6f51</code>), and quick launch incremental deploy was fixed (<code>18b07486</code>). A third change, reusing the cached UATRules assembly instead of recompiling it “(~2s, e.g. every editor quick launch),” was backed out the same day with no reland, so it did not ship (<code>88268823</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The proxy bug showed up as helpers stuck, or processes failing with “missing files that are not missing.” If you run UBA with a storage proxy and have seen intermittent ten-minute stalls, test with this. The Launch On changes are small numbers that add up for anyone iterating on device several times an hour.</p>
<p><strong>Who should care</strong></p>
<p>Build and CI engineers, UBA operators, anyone iterating with Launch On.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you run UBA through a storage proxy. Track otherwise.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>Frame pacing groundwork landed, compiled out.</strong> A new frame pacer measures each pipeline stage from a per-frame task timing graph, “takes the slowest stage’s recurrence as the achievable frame period,” and delays frame start so work lands “just-in-time for the flip minus r.FramePacing.TargetSlack.” The commit ends with “All task timing and frame pacer code is disabled by UE_TASK_TIMING_ENABLE 0 for now.” (<code>c8214148</code>). The next day, the D3D12 RHI removed its PresentOnSubmissionThread paths, which “have been disabled for a while,” because removing them “will make it easier to support the new frame pacing system in D3D12” (<code>2dcea985</code>). Nothing changes for you yet. When <code>UE_TASK_TIMING_ENABLE</code> flips, frame timing behavior changes with it.</p>
<p><strong>UnrealConsole, a headless editor host.</strong> Seven commit messages name it this week. It boots a “curated” startup that was missing two steps “relative to FEngineLoop,” one of which left “any map load” failing <code>CheckForWorldGCLeaks</code> under <code>-minimalengine</code> (<code>8ffa5e75</code>). Saving under “UnrealConsole’s minimal engine” failed (<code>c625ee5a</code>), Slate gained a way to skip editor UI because “UnrealConsole being headless doesn’t want any Slate” (<code>3c28faf2</code>), and toolsets learned to work without it: the agent plugin skips its settings panel “(UnrealConsole headless host)” (<code>dd5f55ec</code>), and the toolset registry tolerates missing editor subsystems “for headless editor hosts” (<code>12efd3d9</code>). No message states the purpose. The commits suggest a headless editor that agent tools can drive without a UI.</p>
<p><strong>PCG Component Lite.</strong> Eight commits this week, counting a backout and its reland, took the series from P4 through P8, including “P6 - add the non-partitioned Lite component” (<code>b2f57825</code>) and a display name of “PCG (Lite)” (<code>a2f7ef33</code>). Last week PCG added a base class to “phase out the old overcrowded component with a new one.” No commit yet states a default or a migration path.</p>
<p><strong>Two follow-ups on last week’s post.</strong> We reported the Iris RPC ownership bypass fix as backed out with no reland. It relanded on Monday as “Re-submit of CL 58175859 (backed out in CL 58581021). Fix Iris RPC ownership bypass and type confusion during RPC processing.” with new tests, and nothing backed it out through Sunday (<code>d42001cf</code>). We also said we would not count the VBench <code>TWriteBarrier</code> change as shipped until the next reland. It also relanded on Monday, with the same “10% speedup on VBench” text, and stayed in through the end of the week (<code>8feb3d14</code>).</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>This week we ignored roughly 830 commits: config and plugin descriptor churn, Verse, UEFN, and scene graph work, animation framework and RigVM polish, AI toolset API surface, virtual production and media plumbing, editor UI, header tool codegen, test and static analysis cleanup, and the usual small fixes. There were 24 commits tagged <code>[Backout]</code>, the same as last week and inside the normal band for this branch. Several relanded in-window, including the cook determinism merge above, PCG Component Lite P5 (<code>2ec4c4e4</code>), and a TEDS column API change (<code>8dbc277a</code>).</p>
<p>Three that did not come back are worth naming, because their titles make them sound shipped. “Enabling new platform IO dispatcher on all platforms until hard-lock to give it some testing” was backed out with no reland (<code>4572de78</code>), so the new dispatcher is not enabled. The UATRules cache reuse mentioned above did not ship (<code>88268823</code>). And a ChaosCloth change to stop rotating reference bone velocity, which would have removed a “fake centripetal acceleration,” was backed out with no reland (<code>d5e43eb6</code>).</p>
<p>A few one-liners. GAS <code>CancelAbilities</code> was cancelling all instances instead of the targeted ones (<code>150de265</code>). A Niagara PSO readiness bug left some effects that “can never be culled,” with kill switches <code>fx.Niagara.RestoreSoloAfterReady=0</code> and <code>fx.Niagara.RegisterScalabilityAfterReady=0</code> (<code>e312517c</code>). With memory-mapped shaders, encrypted IAD chunks reached the shader archive and “Oodle decompression in CreateShader then failed fatally” (<code>16c68d93</code>). And “project-scoped asset module accessibility” is now on by default, which “can cause some ambiguity errors” (<code>c5449c37</code>).</p>
<hr>
<h2 id="for-teams-shipping-on-58">For teams shipping on 5.8</h2>
<p><code>ue5-main</code> ran 123 commits with no backouts, against 196 last week. There was no gather changelist and no hotfix version bump. Of 122 analyzable commits, 111 carry an exact title twin on <code>ue6-main</code>, so most of this list is mirrored there. The traffic is mostly animation framework work (about 45 commits), Control Rig, and Sequencer, with rendering thin at six commits. A handful of the items below are on this branch only, and each says so.</p>
<ul>
<li><strong>XSX EOS delay-load crash on launch.</strong> “Fix EOS delay-load crash on launch with modern GDK folder layout,” with a note to “Remove once EOSSDK ships a modern-layout XSX binary.” It changes build rules only (<code>ef041c2a</code>). This one is on <code>ue5-main</code> only. A separate EOSSDK v1.19.2.1 binary update marks “PS4 10.008.001/10.508.001, PS5 10.00.00.40/11.00.00.40, Switch 18.3.1/19.3.5, XboxOneGDK/XSX 240300-241000” as deprecated and adds “Switch/Switch2 22.2.5” (<code>e0b317d6</code>). Its message does not say whether it ships the modern-layout XSX binary.</li>
<li><strong>UAT output on non-Latin build agents.</strong> UAT now starts commandlets with a UTF-8 environment, fixing “garbled output from the commandlets when running UAT on a system that is set to a non-latin codepage (Japanese, Chinese…)” (<code>97185d08</code>). On <code>ue5-main</code> only. If your build agents run a Japanese or Chinese system locale, take this.</li>
<li><strong>Lumen atlas cleared each frame under SceneCapture2D.</strong> “Fix Lumen indirect lighting atlas being cleared every frame when a SceneCapture2D is rendering” (<code>0fda89fd</code>). Also on <code>ue6-main</code>. A separate Lumen crash when uploading “more instance indices than GPUScene can hold (MAX_INSTANCE_ID)” is also fixed (<code>6014b88a</code>), on both branches, alongside a Vulkan <code>ReadSurfaceData</code> null check (<code>61e2141e</code>).</li>
<li><strong>A landscape fix written to be hotfix compatible.</strong> An assertion in the batched edit layers merge “when two landscape components overlap” is fixed, and the commit notes “New functions intentionally omit LANDSCAPE_API to make the CL hotfix compatible.” (<code>4ff41bbd</code>). It is also on <code>ue6-main</code>. It is the only commit this week that says this, which suggests it is a candidate for a 5.8 hotfix, though no message says so.</li>
<li><strong>Static mesh builds crashed on meshes with no UVs.</strong> “Array index out of bounds: 0 into an array of size 0” in the async static mesh build “whenever bUseMikkTSpace was on, which is the default.” It now falls back to per-vertex tangent averaging, with “No behaviour change for meshes with at least one UV channel” (<code>0c34c4aa</code>). Also on <code>ue6-main</code>.</li>
<li><strong>Mac pak staging for projects outside the engine root.</strong> A GitHub pull request (PR #15101) fixes the <code>CreatePaksUsingChunkManifests</code> lookup path “so that it works for mac platforms and not just windows particularly for projects that live outside the UE root” (<code>01428705</code>). Also on <code>ue6-main</code>.</li>
<li><strong>Editor web views no longer take the editor down.</strong> The CEF v128 update “Fixes editor crashes when the GPU process is reported as unusable,” and the web views now show a spinner instead (<code>7bb6f298</code>). A shutdown crash with <code>-RenderOffScreen</code> is fixed too (<code>1d60c6cb</code>). Both are also on <code>ue6-main</code>. The commits suggest the second matters most on headless build agents.</li>
<li><strong>PIE crash on brushes without a level.</strong> “EXCEPTION_ACCESS_VIOLATION reading address 0x0000000000000028” in <code>ABrush::PostLoad</code>, where “Every reported occurrence is a PIE world duplication.” The fix adds an ensure, and “projects that suppress LogBrush will see the ensure where they previously saw nothing. It fires once per session.” (<code>9396d5b1</code>). Also on <code>ue6-main</code>.</li>
<li><strong>Cook determinism, two fixes.</strong> PoseSearch PCA data varied with CPU cache size, so “a machine with a 32 KB L1 and one with a 48 KB L1” produced different covariances. The fix bumps <code>DatabaseIndexDerivedDataCacheKeyVersion</code> “to rebuild the cached indices” (<code>5cc61f39</code>). A Control Rig anim graph node’s compiled class “no longer depends on cook load order” (<code>e8dcfef1</code>). Both are also on <code>ue6-main</code>. Expect a PoseSearch DDC rebuild.</li>
<li><strong>Sequencer landing mode default changed here too.</strong> <code>Sequencer.SwitchBoundary.OccupiedLandingMode</code> went “from 2 to 3” (<code>de572aff</code>), the same change as on <code>ue6-main</code>.</li>
<li><strong>Visual Studio 2026 solutions.</strong> The source code accessor did not recognize <code>.slnx</code>, so clicking a Blueprint’s native parent class “launched a new devenv on every click,” and the hot reload “save all open Visual Studio documents before compiling” hook was silently disabled (<code>5fc7f720</code>). No twin landed on <code>ue6-main</code> this week. The commit is a cherry-pick of an older changelist.</li>
<li><strong>Animation framework content needs a resave.</strong> An output variables change includes an “asset registry version bump,” and “UAF content needs a resave with or directly after this change” (<code>8b1e4487</code>). Also on <code>ue6-main</code>.</li>
<li><strong>The GC barrier change from last week, one follow-up.</strong> Epic’s own replication test target needed an explicit opt-in: “CIS compile error fix: enabling the GC barrier for replication tests (required by TestGarbageObjectPtrReference)” (<code>acf28db8</code>), also on <code>ue6-main</code>. It is a one-file target change, and it is the pattern to expect in your own non-editor targets if they depend on the barrier. Nothing on this branch touched last week’s AVBOIT, Nanite translucency, Vulkan fence, or RigVM Return node defaults, so last week’s state stands.</li>
<li><strong>Crash fixes worth taking.</strong> A use-after-free in constraint tick prerequisites (<code>6f08631b</code>) and a “GC-related regression from CL 56542014 that could cause crashes in other engine systems like MRQ” (<code>51775abc</code>), both also on <code>ue6-main</code>.</li>
</ul>
<hr>
<h2 id="closing">Closing</h2>
<p>A compiler was chosen and withdrawn inside a day because HeadlessChaos asserted. Epic’s test coverage caught it, and your build logs are where you confirm you are on the right side of it.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless the claim is in the “For teams shipping on 5.8” section, which references <code>ue5-main</code>. Many of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-sep-28-oct-4-2026/og.f0c97f1f2d.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: Epic Ported Its Agent Tools With an LLM (September 21 to September 27, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-sep-21-27-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-sep-21-27-2026</guid>
      <pubDate>Mon, 28 Sep 2026 06:26:30 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>1,072 commits on ue6-main, the busiest week since June. Epic ported most of its editor agent toolsets from Python to C++ with a test-driven LLM process described in the commit, AVBOIT and Nanite translucency went back to off by default on both branches, and MSVC static analysis turned out to have been analyzing nothing on single-file compiles.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-epic-ported-its-agent-tools-with-an-llm">Unreal Engine Weekly: Epic Ported Its Agent Tools With an LLM</h1>
<p><strong>Week of September 21 to September 27, 2026</strong> | <strong>1,072 commits on <code>ue6-main</code>, 196 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>One of the week’s largest commits ports most of Epic’s editor toolsets from Python to C++ (all but the programmatic and Python toolsets), and its message lays out “the general LLM process” it used: port each Python toolset to C++, point the Python tests at it, iterate until they pass, port the tests to C++, iterate again, then “Perform audit for API drift and test coverage regressions” and “Audit until tests pass.” (<code>15fd454a</code>). The tools a model uses to drive the editor were rewritten, in part, by a model, with the test suite as the contract. Earlier in the week the toolset provider interface moved into a new engine module, <code>ToolsetCore</code>, that depends only on Core and CoreUObject (<code>6d7f31bd</code>). The interface agent tools are registered through is moving out of a plugin and into the engine.</p>
<p>Two things we told you last week changed. Adaptive Voxel-Based OIT, which landed on both branches with <code>r.OIT.Method</code> defaulting to it, now defaults off: the value went from 1 to 0 on <code>ue6-main</code> (<code>511c9d7e</code>) and <code>ue5-main</code> alike, “to sort out some ThinTranslucency issues.” Nanite translucency support went off the day before (<code>81b50042</code>), on both branches, and a separate commit quietly switched off its runtime toggle too (<code>4327b52b</code>). And the UBA ownership handoff we described as genuine did not show up this week: all 36 commits touching UnrealBuildAccelerator this week came from the original engineer.</p>
<p>At 1,072 commits this is the busiest week <code>ue6-main</code> has had since this briefing started tracking it in June, clear of last week’s 963. Among named subsystems, build leads at 121, followed by rendering at 71, editor at 64, and core at 56.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<h3 id="agent-tooling-nearly-doubled-and-moved-into-the-engine">Agent tooling nearly doubled, and moved into the engine</h3>
<p><strong>Pattern:</strong> commits touching the toolset registry, the AI Assistant plugin, the experimental toolsets, <code>AsyncFunctionResult</code>, and <code>ToolsetCore</code> came to 56 this week, against 30 last week and 20 the week before, counted the same way each week. It is the largest weekly count we have measured on this cluster, which ran between 19 and 30 a week from the week of August 17 through last week. Two weeks running, the work has produced a new engine module: <code>AsyncFunctionResult</code> last week, <code>ToolsetCore</code> this week (<code>6d7f31bd</code>). And the Python-to-C++ port (<code>15fd454a</code>) says in its own message that it is “in prep for consolidating these toolsets with their related subsystems.”</p>
<p><strong>Interpretation:</strong> the destination is stated: each subsystem exposes its own tools, instead of a plugin wrapping them. <code>ToolsetCore</code> exists so that “a class that already has a base it can’t leave - an engine or editor subsystem, a settings object - can expose AICallable tools by implementing IToolsetProvider instead of inheriting UToolsetDefinition.” The permission model kept pace: five commits hide individual tools and whole toolsets when the host is restricted (<code>05da90e0</code>, <code>8275809e</code>, <code>33602841</code>, <code>97e5f51b</code>, <code>4614680c</code>). Two of them state the rule outright: “a Shipping editor is always restrictive. Roles are not consulted.” (<code>8275809e</code>, <code>33602841</code>). Two others go further: EditorAppToolset’s PIE tools are also withheld from sessions granted the VKCreateUGC role (<code>05da90e0</code>), and CheatsToolset from sessions granted the base creator role, even with restrictive mode off (<code>97e5f51b</code>).</p>
<p><strong>Who cares:</strong> tools programmers, pipeline and automation teams, anyone scripting the editor through the toolset Python API.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="uba-went-back-to-one-engineer">UBA went back to one engineer</h3>
<p><strong>Pattern:</strong> counting commits touching <code>UnrealBuildAccelerator</code> the same way each week: 37 commits with 29 from one engineer (Sep 7 to 13), 19 with 10 from a second engineer and 7 from the original (Sep 14 to 20), and 36 this week, all from the original engineer. The second engineer has no commits of any kind in this week’s data.</p>
<p><strong>Interpretation:</strong> last week we wrote that the split “reads as a real handoff of ownership rather than a week of holiday cover.” One week later, the data does not support that reading. A single week of other work or time off is the likelier explanation, and the concentration we have tracked since August is intact. We are keeping the pattern on the list and dropping the conclusion. The work itself is covered in the highlights below.</p>
<p><strong>Who cares:</strong> build and CI engineers, anyone whose build farm depends on UBA.</p>
<p><strong>Signal strength:</strong> Moderate.</p>
<h3 id="translucency-shipped-then-stepped-back">Translucency shipped, then stepped back</h3>
<p><strong>Pattern:</strong> last week Nanite translucency and AVBOIT landed together. This week Nanite translucency was fixed to cast shadows (<code>07ae0b96</code>), then disabled by default the next day after EngineTests failed to run green (<code>81b50042</code>), with its runtime toggle also defaulted off in a separate commit (<code>4327b52b</code>), and AVBOIT’s method default went to 0 a day after that (<code>511c9d7e</code>). The same disables landed on <code>ue5-main</code>, cited in the 5.8 section below. Nothing re-enabled either one before the week closed.</p>
<p><strong>Interpretation:</strong> the code is still in both branches and still selectable. Epic pulled the defaults back while test and content issues get resolved, which is normal for a large rendering feature meeting a real test suite. If you scheduled an evaluation from last week’s post, you now have to opt in on both. Set <code>r.OIT.Method</code> to 1 on top of the existing <code>r.AVBOIT.Support</code> project opt-in, set the read-only <code>r.Nanite.AllowTranslucency</code> to 1 in config, and set <code>r.Nanite.AllowTranslucentMaterials</code> to 1, which a separate commit also switched off by default (<code>4327b52b</code>).</p>
<p><strong>Who cares:</strong> graphics programmers, technical artists evaluating translucency, anyone who read last week’s AVBOIT section.</p>
<p><strong>Signal strength:</strong> Moderate.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li><strong>AVBOIT and Nanite translucency are off by default again, on both branches.</strong> <code>r.OIT.Method</code> went from 1 to 0 (<code>511c9d7e</code>) and the read-only <code>r.Nanite.AllowTranslucency</code> went from 1 to 0 (<code>81b50042</code>). A separate commit also set the runtime toggle <code>r.Nanite.AllowTranslucentMaterials</code> to 0, without mentioning it in its message (<code>4327b52b</code>). Last week’s post described the earlier default. Nothing re-enabled either before the week closed.</li>
<li><strong>If you run MSVC static analysis in CI, check it analyzed anything.</strong> <code>-StaticAnalyzer=Default</code> on <code>-SingleFile</code> compiles “silently analyzed nothing,” because cl.exe ignores <code>/analyze</code> from a nested response file, and normal compiles would have done the same with <code>bWriteSarif</code> disabled (<code>e5982767</code>). Reproduced on MSVC 14.44, 14.50, and 14.51.</li>
<li><strong>If you ship on 5.8:</strong> a StateStack memory fix whose own message says the hotfix-compatible workaround “dead-ended in 5.8.3,” a Mac editor exit assert affecting “37 users and 37 occurrences on 5.8.x,” and two Xcode 27 and macOS 27 fixes are covered with commit references in the 5.8 section below.</li>
<li><strong>Iris changed its protocol hash, and an RPC ownership bypass fix came back out.</strong> Replication switched from CityHash to XXHash, so “clients and servers must be built from the same revision” (<code>1c98a69c</code>). Separately, a fix from the week before, for “Iris RPC ownership bypass and type confusion,” was backed out with no reland (<code>a10acade</code>).</li>
<li><strong>The <code>TObjectPtr</code> GC barrier is now off by default outside the editor, on both branches.</strong> <code>bWithGCBarrier</code> resolves to true only for Editor targets (<code>b561343d</code>, with the <code>ue5-main</code> commit cited in the 5.8 section). If you run incremental reachability in a cooked game, client, or server build, set it on that target.</li>
<li><strong>UBA’s scheduler was capping Horde help.</strong> Coordinators read the remote process count as total wanted cores, so a 5,000-process run “capped at ~3700 cores” (<code>44ce71cb</code>).</li>
<li><strong><code>FSceneViewState</code> split into game-thread and render-thread halves</strong> (<code>ce0628ef</code>), with several <code>FSceneViewStateInterface</code> methods deprecated and stubbed. Act now if your renderer code or plugins reach into view state.</li>
<li>The editor toolsets were ported from Python to C++ using an LLM-driven process described in the commit (<code>15fd454a</code>), and the toolset provider interface moved into a new engine module (<code>6d7f31bd</code>).</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="avboit-and-nanite-translucency-are-off-by-default">AVBOIT and Nanite translucency are off by default</h3>
<p><strong>What changed</strong></p>
<p>Four commits, in changelist order. First, Nanite translucent materials set to Cast Dynamic Shadow as Masked now cast shadows. The commit explains that “Nanite hid every translucent raster bin in every cull except the AVBOIT extinction cull, so Nanite translucency cast no shadows at all,” and now matches non-Nanite behavior, “an opaque shadow wherever opacity is non-zero,” behind <code>r.Nanite.TranslucentShadows</code> (<code>07ae0b96</code>). Later the same day, a vertex factory cleanup titled “Nanite translucency: only compile the vertex factory for the passes it implements.” also flipped the runtime toggle <code>r.Nanite.AllowTranslucentMaterials</code> from 1 to 0, a change its message does not mention (<code>4327b52b</code>). Next, the following day: “Disabling Nanite Translucency shaders. Some bugs need to be dealt with before enabling again. EngineTests didn’t run green due to this.” The diff changes one line: the default of <code>r.Nanite.AllowTranslucency</code>, a read-only console variable, from 1 to 0 (<code>81b50042</code>). Last, a day later: “Disabling AVBOIT by default to sort out some ThinTranslucency issues.” That diff also changes one line, the default of <code>r.OIT.Method</code> from 1 to 0 (<code>511c9d7e</code>). All three default changes landed on <code>ue5-main</code> as well. Nothing in the window turns any of them back on.</p>
<p><strong>Why this is important</strong></p>
<p>Last week we told you <code>r.OIT.Method</code> defaulted to AVBOIT, with the shader permutations gated behind the off-by-default <code>r.AVBOIT.Support</code>. Both halves were true when written. The first half is no longer true. The practical default was ordinary sorted translucency then and it still is, but an evaluation now needs the method set explicitly as well as the permutations enabled. And because <code>r.Nanite.AllowTranslucency</code> is read-only, turning Nanite translucency support back on means editing config and restarting, with a shader recompile. The runtime toggle, <code>r.Nanite.AllowTranslucentMaterials</code>, now also defaults to 0 (<code>4327b52b</code>) and only matters once support is on at startup.</p>
<p>Look at the stated reasons. Epic pulled the Nanite translucency default because its own test suite went red, and AVBOIT’s to sort out ThinTranslucency issues. Neither commit describes a design change. Expect both back once those bugs are fixed, and read the re-enabling commits for what changed.</p>
<p><strong>Who should care</strong></p>
<p>Graphics programmers, technical artists evaluating translucency, anyone who acted on last week’s AVBOIT section.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you scheduled an AVBOIT evaluation from last week’s post. Track otherwise.</p>
<hr>
<h3 id="msvc-static-analysis-could-run-and-analyze-nothing">MSVC static analysis could run and analyze nothing</h3>
<p><strong>What changed</strong></p>
<p>From the commit: “cl.exe honours most options from a nested response file, but not /analyze: it has to be on the command line or in the top-level response file to enable static analysis”. UBT put <code>/analyze</code> in each module’s shared response file, which every compile’s own response file includes, so analysis was only switched on by the <code>/analyze:log</code> that <code>bWriteSarif</code> adds per file. “-SingleFile compiles don’t get /analyze:log, so -StaticAnalyzer=Default silently analyzed nothing, and normal compiles would have done the same with bWriteSarif disabled.” It reproduces on MSVC 14.44, 14.50, and 14.51. <code>/analyze</code> is now added to the per-file arguments whenever the global arguments go into a shared response file (<code>e5982767</code>).</p>
<p><strong>Why this is important</strong></p>
<p>A static analysis job that silently analyzes nothing reports zero findings, and zero findings looks exactly like clean code. Nothing fails, so nothing prompts anyone to look. If your CI runs <code>-StaticAnalyzer=Default</code> on single-file compiles, or with <code>bWriteSarif</code> disabled, and UBT put the global arguments in a shared response file, those runs analyzed nothing. After this fix they will, so expect findings you have not seen before.</p>
<p>Alongside it, the assert API was reworked with proper <code>[[noreturn]]</code> guarantees and cold-path hints, which “Reduced program size by 11.4 MiB on the tested server target” and “Savings averaged ~1.4% on measured server targets.” The same commit notes <code>FDebug::AssertFailed[V]</code> “is now [[noreturn]], requires the ProgramCounter,” which matters to any fork calling it directly (<code>1947e188</code>).</p>
<p><strong>Who should care</strong></p>
<p>Build engineers, CI owners, anyone running MSVC static analysis, engine-fork maintainers.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you run MSVC static analysis in CI. Check that your last runs actually analyzed files.</p>
<hr>
<h3 id="ubas-scheduler-was-telling-horde-it-was-satisfied-too-early">UBA’s scheduler was telling Horde it was satisfied too early</h3>
<p><strong>What changed</strong></p>
<p><code>Scheduler::GetProcessCountThatCanRunRemotelyNow</code> now includes processes already running on helpers “again,” which marks this as a regression, though the commit does not say when it came in. The consequence it describes: “Coordinators (Horde) treat the value as total wanted cores, so ready-only made them report ‘Satisfied’ once held cores exceeded the ready queue (5000 process UbaCli run capped at ~3700 cores).” (<code>44ce71cb</code>). A companion fix removed a deadlock where <code>SessionServer</code> called scheduler callbacks while holding <code>m_remoteProcessAndSessionLock</code> (<code>f6a17f28</code>).</p>
<p>The rest of UBA’s week came from the same engineer, and much of it was Linux: process lifetime and syscall tracking. A linker-style parent can exit while its child is still writing output, and UBA used to rearm SIGHUP after fork and for every preloaded exec, “so the child is terminated early” (<code>28df7217</code>). “Go symlink/symlinkat syscalls bypassed UBA tracking, so successful creation could be invisible to later reads or absent from published agent outputs” (<code>cc8f9ff8</code>). “Static-glibc symlink and symlinkat calls aborted as unsupported” (<code>0867f702</code>). Orphan watchdogs were reworked (<code>be4f6330</code>, <code>04230c74</code>), and the GCC runtime dependency was removed from the Linux bootstrap (<code>6a18ba11</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The scheduler bug shows up as a ceiling with no error attached. A farm that plateaus below its helper count looks like a sizing problem. If you run large UBA jobs coordinated by Horde and have seen remote help flatten out before your pool is exhausted, this is worth testing against.</p>
<p>Most of these Linux fixes close a case where UBA lost track of work or cut it short, and the rest tighten cleanup and dependencies. If Linux builds under UBA have produced missing outputs or failed symlink calls, retest after taking these.</p>
<p><strong>Who should care</strong></p>
<p>Build and CI engineers, Horde operators, anyone building Linux targets under UBA.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you coordinate large UBA runs through Horde or build Linux targets under UBA. Track otherwise.</p>
<hr>
<h3 id="iris-shifted-its-protocol-identifiers-and-an-ownership-bypass-fix-came-back-out">Iris shifted its protocol identifiers, and an ownership bypass fix came back out</h3>
<p><strong>What changed</strong></p>
<p>Iris switched from CityHash to XXHash, and the commit carries the warning directly: “Note that this shifts every replication protocol identifier, so clients and servers must be built from the same revision.” (<code>1c98a69c</code>).</p>
<p>Separately, a commit from last week, “Fix Iris RPC ownership bypass and type confusion during RPC processing”, was backed out this week (<code>a10acade</code>). The backout gives no reason, and there is no reland in the window. As of the end of the week, that fix is not in <code>ue6-main</code>.</p>
<p><strong>Why this is important</strong></p>
<p>The hash change is a hard compatibility boundary. Any setup where client and server builds can drift, whether staged server rollouts, mixed-revision test farms, or a dedicated server built from a different changelist than the client, cannot mix builds on either side of this commit. Your deployment process has to enforce the same-revision rule.</p>
<p>The backout matters because the title describes an RPC ownership bypass and type confusion fix, and a changelog read on either side of it can easily leave you thinking it shipped. It did not, for now. We do not know why it came out, and we are not going to guess.</p>
<p><strong>Who should care</strong></p>
<p>Network programmers, anyone running Iris, teams with staged or mixed-revision server deployments.</p>
<p><strong>Urgency</strong></p>
<p>Act now on the hash change if you integrate <code>ue6-main</code> and run Iris. Track the RPC fix for a reland.</p>
<hr>
<h3 id="the-renderer-split-view-state-across-threads">The renderer split view state across threads</h3>
<p><strong>What changed</strong></p>
<p>Five commits under one ticket (UE-376381) separate game-thread view state from render-thread view state. <code>FSceneViewGameState</code> is new: it “implements FSceneViewStateInterface and owns the game thread half of the view state,” while “FSceneViewState becomes the render half and no longer implements the interface.” <code>UniqueID</code> was renamed to <code>ViewKey</code> (<code>ce0628ef</code>). Earlier in the week, <code>ResetViewState</code> began queuing deletes to the render thread, and a number of <code>FSceneViewStateInterface</code> methods were “Deprecated and stubbed” (<code>fbd9cb07</code>). Sequencer had been writing <code>SequencerState</code> into <code>FSceneViewState</code> on the game thread “while the renderer reads it off the RT.” It is now passed through <code>FSceneViewInitOptions</code>, the setters and getters are deprecated, and “SequencerState is now Editor only” (<code>7c88cf41</code>).</p>
<p>In the same area, the experimental LumenRef mode was renamed to LumenPT “due to annoying name/CVar autocomplete collisions with r.Lumen.Reflections.*” (<code>72cf5c8e</code>) and gained height fog (<code>5e1d5296</code>), and <code>r.MegaLights.HardwareRayTracing.EndBias</code> and <code>r.LumenPT.LightEndBias</code> were replaced by a shared <code>r.Shadow.RayEndBias</code> (<code>2426acdb</code>).</p>
<p><strong>Why this is important</strong></p>
<p>A deprecated-and-stubbed method still compiles, with only a deprecation warning, and then returns a placeholder (0, false, or null) instead of real data. For a fork, that is quieter than the summer’s scene decomposition work and in some ways worse. A renamed field breaks the build. A stubbed one hands back a placeholder value and keeps going. The renamed console variables are the smaller version of the same problem. A config file that sets <code>r.MegaLights.HardwareRayTracing.EndBias</code> now sets nothing.</p>
<p><strong>Who should care</strong></p>
<p>Graphics programmers, engine-fork maintainers, render plugin authors, anyone with MegaLights or Lumen console variables in project config.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you maintain renderer code that touches view state. Track otherwise.</p>
<hr>
<h3 id="the-agent-tools-moved-into-c-and-into-the-engine">The agent tools moved into C++ and into the engine</h3>
<p><strong>What changed</strong></p>
<p>The editor toolsets, except the programmatic and Python toolsets, were ported from Python to C++ “in prep for consolidating these toolsets with their related subsystems.” The commit describes the method as an LLM process driven by the test suite, ending with “Perform audit for API drift and test coverage regressions” and “Audit until tests pass.” It also notes that <code>AssetTools.FindAsset</code> “is significantly optimized and made async” and that shared utilities were factored out “so that toolsets don’t depend on one another” (<code>15fd454a</code>).</p>
<p>The toolset provider interface moved into a new engine module, <code>ToolsetCore</code>, which “only depends on Core and CoreUObject.” It lets existing engine and editor subsystems expose AI-callable tools by implementing <code>IToolsetProvider</code>, and adds opt-in instance dispatch, “which relaxes the requirement that AICallable functions be static.” It landed, was backed out, and relanded in the window (<code>6d7f31bd</code>). Programmatic toolset scripts now run as coroutines on the editor thread, which the commit says “is about 5x faster than previous” (<code>dbed8596</code>).</p>
<p>Two Python toolset APIs now fail loudly. <code>AssetTools.save_assets</code> was replaced by a single-path <code>save_asset</code> “that raises when a save fails,” and the empty-list “save all dirty assets” mode was removed because it “silently skipped anything the engine failed to mark dirty” (<code>2c893cd8</code>). <code>SceneTools.get_current_level()</code> now raises during PIE “instead of silently returning empty string” (<code>0fdfdb94</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The LLM detail matters for what it says about the process. The contract for the port was the test suite plus an explicit audit for API drift, which is the right way to make a large mechanical rewrite trustworthy regardless of who or what does the typing.</p>
<p>The practical changes are the fail-loud ones. Any script calling <code>save_assets</code> needs porting to the single-path <code>save_asset</code>, and one that relied on the empty-list save-all mode loses it entirely. Reading the current level during PIE now raises.</p>
<p>One commit title reads “Enforce asset reference filter policy in ReferenceConverter so that LLMs can bypass reference restrictions when setting properties.” (<code>d998b1e1</code>). The new tests say otherwise. The ones covering refused references assert that the import fails, most of them with an “asset reference policy” error, and one confirms that a permitted reference still goes through. The change closes the bypass. The title appears to be missing a “not.”</p>
<p><strong>Who should care</strong></p>
<p>Tools programmers, pipeline and automation teams, anyone scripting the editor through the toolset Python API.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you script against <code>AssetTools.save_assets</code> or <code>SceneTools.get_current_level</code>. Track otherwise.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>The <code>TObjectPtr</code> GC barrier is now off by default outside the editor.</strong> <code>UE_WITH_GC_BARRIER</code> decides per target whether the barrier is compiled in, controlled by <code>bWithGCBarrier</code> in the target rules. The message leaves out the default, but the diff has it. The property resolves to true only for Editor targets, and its doc comment says the barriers are “Off for everything but the editor unless a target opts in.” Before this change the barrier was compiled into every target. <code>UE_OBJECT_PTR_GC_BARRIER</code> is deprecated, and enabling incremental reachability in a build without the barrier produces a <code>LogGarbage</code> warning (<code>b561343d</code>). The same change is on <code>ue5-main</code>, cited in the 5.8 section below. If you run <code>gc.AllowIncrementalReachability</code> in a cooked game, client, or server build, set <code>bWithGCBarrier=true</code> on that target, or you will get the warning instead of incremental reachability. Everyone else gets the runtime saving without doing anything.</p>
<p><strong>Hair got volumetric multiple scattering, and it is being hardened quickly.</strong> The new method approximates multiple scattering within hair volumetrically, “based on [Hu et al. 2026],” selected at compile time, with the voxelization step’s per-voxel direction accumulation noted as having “a significant transient memory cost” (<code>1469ffed</code>). A denoising and optimization pass followed two days later, with a bilateral filter that “should be disabled for use with a neural denoiser” (<code>dd82a008</code>), then early termination on transmittance (<code>bc461fb5</code>). Three passes from one engineer between September 21 and September 25. Groom teams should watch the memory note before anything else.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>This week we ignored roughly 850 commits: localization automation, binary manifest drops for internal content pipelines, test infrastructure, Verse and UEFN scene graph work, internal tooling for Epic’s own merge and submit pipelines, and the usual run of small cleanups. There were 24 commits tagged <code>[Backout]</code>, inside the normal band for this branch. Most relanded in-window, including <code>ToolsetCore</code> above and an optional Chaos cached contact manifold that is default off and gated by <code>p.Chaos.Collision.ManifoldCache.Enabled</code> (<code>f2f66d89</code>).</p>
<p>Besides the Iris fix covered above, two that did not come back are worth naming, because each follows up on something we covered. A mechanism for AutoRTFM module defaults, following last week’s <code>ModuleRules</code> migration, was backed out the same day it landed, with no reland (<code>9d4b8559</code>, backed out at <code>28005d1d</code>). And “Make TWriteBarrier and VRestValue AUTORTFM_DISABLE, and remove EWriteMode::Default,” which claimed “a 10% speedup on VBench,” was relanded and then followed by another backout carrying the same description (<code>dcafbb1e</code>, backed out at <code>bc02dce6</code>). The AutoRTFM change is out of the tree at the end of the week, and we would not count the VBench speedup as shipped until the next reland.</p>
<p>Of 1,072 commits, 224 worth reading. Scene graph and typed element work was again the largest identifiable feature cluster at 61 commits, and again almost none of it individually notable. The multiprocess cook load balancer went from “O(n log n) -&gt; O(log n),” a “~6x improvement for cluster size of 1 million,” which applies to the load balancer rather than to total cook time (<code>febb9d11</code>). PCG added a new base class for the PCG Component to “phase out the old overcrowded component with a new one” (<code>690c9f65</code>). And a vulnerability “when loading an FBX file with embedded files” was fixed with no further detail in the commit (<code>cd1a46ca</code>).</p>
<hr>
<h2 id="for-teams-shipping-on-58">For teams shipping on 5.8</h2>
<p><code>ue5-main</code> ran 196 commits with 2 backouts, both relanded. There was no gather changelist this week, and the week was still busy. The volume is a Nanite ray tracing series from one engineer (31 commits tagged [Nanite Ray Tracing], 35 from that engineer in all, counting related HWRT and D3D12 work), a Control Rig burst, and a wide spread of crash and correctness fixes, two of which name 5.8 in their own text.</p>
<ul>
<li><strong>Animation state machine memory could be invalidated, and 5.8.3 only has the workaround.</strong> <code>FEncounteredStateMachineStack::StateStack</code> was “using mem stack allocator when it’s data can persist outside of the stack which resulted in invalidated memory.” The fix also “bumps state stack inline allocation to 8 to avoid more heap allocations,” and the commit states that “A hotfix compatible workaround was previously implemented at 57738922 and dead-ended in 5.8.3.” (<code>65f7b94a</code>). <strong>We rate this the week’s most actionable item for anyone on 5.x.</strong> The full fix is on <code>ue5-main</code>. The commit does not say whether it will reach a 5.8 hotfix.</li>
<li><strong>Mac editors asserted on exit.</strong> “Cocoa can call -[FMetalView hitTest:] while the game thread is inside FEngineLoop::Exit,” after Slate’s widgets are gone, and the hit test “walks sparse-array indices for freed FWidgetData and asserts.” The deferred hit test now bails once engine exit is requested. The commit reports “37 users and 37 occurrences on 5.8.x, every one with an EditorExit activity hint” (<code>2c773ff7</code>).</li>
<li><strong>Two Apple toolchain blockers.</strong> “When an iOS app is compiled with Xcode27, iOS27 enforces use of UIScene,” so the iOS client now uses the UIScene lifecycle by default “to avoid crash on start” (<code>aa25acc1</code>). And iOS <code>.app</code> finalization was failing with “Bad file descriptor” on macOS 27: “The build executor spawns actions without an open descriptor 0. xcodebuild inherits it and then cannot launch /bin/sh for a scheme’s script phases, so app finalization fails and blames the script.” The xcodebuild finalize step now gets an empty stdin (<code>71857dd9</code>). If your Mac build agents are moving to macOS 27, take this before they do.</li>
<li><strong>Linux crash reporting could exhaust the crash allocator’s large pool.</strong> The engine log is now copied “through a fixed 64K buffer rather than IFileManager::Copy, which allocates a scratch buffer of min(1MB, file size),” thread XML is moved rather than copied, and the all-thread dump is bounded by <code>unix.CaptureAllThreadStacksMaxChars</code> (<code>2c030936</code>).</li>
<li><strong>Two DDC correctness fixes.</strong> Mutable’s DDC computed value for customizable objects was “changing from compilation to compilation,” ported from an earlier change (<code>18bc0ec8</code>). And Substrate Toon was missing its GBuffer custom data write “when fetching from DDC” (<code>3e89ad5e</code>). The first is the same class of bug as last week’s determinism gather, arriving on its own.</li>
<li><strong>AVBOIT and Nanite translucency are off by default here too</strong> (<code>b2ec217e</code>, <code>acb2e828</code>, <code>a4bc702e</code>), the same default changes described in the highlights above, including the unannounced <code>r.Nanite.AllowTranslucentMaterials</code> flip. If you read last week’s 5.8 note that AVBOIT was on this branch, it still is, but it now needs <code>r.OIT.Method</code> set explicitly on top of the <code>r.AVBOIT.Support</code> project opt-in.</li>
<li><strong>Skeletal mesh and blend space crashes.</strong> “Stale reference poses could survive a mesh reload and crash on remap” (<code>99598786</code>). A <code>UBlendSpace</code> marker sync assert “caused by a stale leader sample” (<code>28f09fa6</code>), and a blend space grid widget crash “on runtime data that outlives its samples” (<code>57b02e9c</code>).</li>
<li><strong>Sequencer attach track jumped on non-root sockets.</strong> “Compensation was pinned to the root component while evaluation attached to the socket, so the object jumped by the socket’s transform relative to the root,” visible only when the socket’s mesh sits under a default scene root (<code>3c591f15</code>). Binding overrides also asserted “when the player has no shared playback state yet,” for example when overriding bindings from Python or Blueprint before the sequence starts playing (<code>e8cace23</code>).</li>
<li><strong>Nanite streaming, occlusion culling, and foliage fixes.</strong> A mitigation for crashing when <code>r.Nanite.Streaming.StreamingPoolSize</code> is set too high (<code>38986ccc</code>), a workaround for incorrect codegen in <code>FGPUOcclusionPacket::OcclusionCullPrimitive</code> (<code>c82bca23</code>), and stale HISM transforms with <code>foliage.densityscale</code> below 1.0 (<code>fc54f6ff</code>).</li>
<li><strong>Vulkan fence wait default changed.</strong> <code>r.Vulkan.CpuWaitForFence</code> now defaults to 0 (the commit describes this as using semaphores on desktop) instead of 1 (<code>ce723723</code>), alongside an assert fix for invalid window handles (<code>ca2d92c8</code>). The diff changes the default unconditionally, so check it on every Vulkan platform you ship, not only desktop.</li>
<li><strong>The <code>TObjectPtr</code> GC barrier is now off by default for non-editor targets here too</strong> (<code>c92351b9</code>), as described in Worth Tracking above. If you run incremental reachability in a cooked build, set <code>bWithGCBarrier=true</code> on that target before you take this.</li>
<li><strong>MegaLights ignored IES profiles in volumes.</strong> Volumetric fog and lit translucency lighting were ignoring IES profiles under MegaLights (<code>a54423d6</code>).</li>
<li><strong>A RigVM default was flipped and walked back in the same week.</strong> A function’s Return node was made to exit the function, with the new <code>RigVM.Compiler.ReturnNodeExitsFunction</code> defaulting to true (<code>c24c6f3e</code>), then the CVar was defaulted to false because “Existing rigs rely on nodes running after the Return” (<code>c0be2c90</code>). The old behavior is the default. The early exit is opt-in.</li>
</ul>
<hr>
<h2 id="closing">Closing</h2>
<p>The toolsets in the commit we led with were ported by a process its author wrote down, and the most useful sentence in it is the one about auditing for API drift. It tells you how the port was checked, and the method is worth copying.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless the claim is in the “For teams shipping on 5.8” section, which references <code>ue5-main</code>. Many of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-sep-21-27-2026/og.558a6efc46.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: UE5 Stopped Counting (September 14 to September 20, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-sep-14-20-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-sep-14-20-2026</guid>
      <pubDate>Mon, 21 Sep 2026 07:28:49 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>963 commits on ue6-main, and the one that matters most is a single file. A static_assert now makes any further append to the UE5 object version enum fail to compile, handing the engine’s global package version to UE6 in the same serialized slot. The same week, four months of cook determinism work was gathered into the 5.x stream in one changelist, and Epic’s new order-independent transparency implementation landed on both branches.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-ue5-stopped-counting">Unreal Engine Weekly: UE5 Stopped Counting</h1>
<p><strong>Week of September 14 to September 20, 2026</strong> | <strong>963 commits on <code>ue6-main</code>, 172 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>The most consequential commit this week edits one file and changes no behavior. <code>EUnrealEngineObjectUE5Version</code> is now frozen: a <code>static_assert</code> makes any further append to the enum fail to compile, the global package version is edited only from <code>//UE6/Main</code> from here, and <code>EUnrealEngineObjectUE6Version</code> continues the same numbering in the same serialized slot (<code>802d4310</code>). The commit says that if a UE5-only global version bump is ever unavoidable, use the UE4 enum’s slot instead. That is a line being drawn, in code, about which branch the engine’s version number comes from.</p>
<p>The rest of the week reads like the same decision playing out. Four months of cook and DDC determinism work was gathered into <code>//UE5/Main</code> as a single changelist listing 34 incorporated CLs, which is the largest thing to happen to the 5.x stream in weeks and includes fixes this briefing covered on <code>ue6-main</code> back in August. Epic’s implementation of Adaptive Voxel-Based Order Independent Transparency landed on both branches inside the same window. And the AutoRTFM migration that last week’s build-file groundwork promised arrived, rewriting every build file in the tree that set the legacy flags.</p>
<p>At 963 commits the week sits at the top of the band this branch has held since June, three clear of two earlier weeks, so read it as a busy week rather than a record. Build is the top subsystem at 116, followed by rendering at 71, editor at 69, and core at 60.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<h3 id="the-ue5-and-ue6-boundary-stopped-being-implicit">The UE5 and UE6 boundary stopped being implicit</h3>
<p><strong>Pattern:</strong> three separate commits this week define the relationship between the two branches rather than just reflecting it. The UE5 object version enum froze with a compile-time guard and handed numbering to UE6 in the same serialized slot (<code>802d4310</code>, with the identical change landing on the maintenance branch the same day). Four months of determinism fixes were gathered into <code>//UE5/Main</code> in one changelist. AVBOIT landed on <code>ue5-main</code> and on <code>ue6-main</code> the same day (<code>4402e92a</code>). The <code>ue5-main</code> commits behind all three are cited in the 5.8 section below.</p>
<p><strong>Interpretation:</strong> for three months this briefing has described <code>ue5-main</code> as maintenance and <code>ue6-main</code> as where the engine is going. That framing is now too simple. Traffic moves in both directions, and the version freeze is the clearest statement yet of what the split actually means: UE6 owns the format’s forward motion, UE5 keeps a frozen slot and receives backports. If you maintain a fork, the freeze commit is the one to read, because it tells you where your own custom version entries should now live.</p>
<p><strong>Who cares:</strong> engine-fork maintainers, anyone carrying custom object versions, technical directors planning a UE6 merge.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="agent-tooling-grew-by-half-and-gained-its-own-engine-module">Agent tooling grew by half and gained its own engine module</h3>
<p><strong>Pattern:</strong> commits touching the toolset registry, the AI Assistant plugin, the experimental toolsets, and the new async tool-call module came to 30 this week, against 20 in each of the two prior weeks measured the same way, a half-again increase on a cluster that had been flat. The work has moved past capability and permissions into module structure and editor integration: the async tool call result was split into its own engine module, <code>AsyncFunctionResult</code> (<code>3cbf8b56</code>), toolsets backed by a function library are now tracked for validity and retired when their backing class unloads (<code>9adf873e</code>), and an editor-side prompt provider lets tools contribute AI Assistant menu entries without depending on the plugin, first consumer being a right-click “Analyze Crash” on Crash Diagnostics rows (<code>d7b1a871</code>).</p>
<p><strong>Interpretation:</strong> a feature that needs its own engine module has stopped being a plugin. The Crash Diagnostics integration is the more telling half. Epic is not just exposing the editor to an agent, it is wiring the agent into the editor’s own diagnostic surfaces as a default affordance, behind a provider interface so the plugin can stay off by default and the menu entry still appear when it is loaded.</p>
<p><strong>Who cares:</strong> tools programmers, pipeline and automation teams, anyone evaluating model-driven editor automation.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="unrealbuildaccelerators-single-author-concentration-has-genuinely-broken">UnrealBuildAccelerator’s single-author concentration has genuinely broken</h3>
<p><strong>Pattern:</strong> we have tracked this for six weeks. Counting commits touching <code>UnrealBuildAccelerator</code> the same way each week: Aug 31 to Sep 6 was 39 commits with 38 from one engineer; Sep 7 to 13 was 37 with 29 from that engineer and 7 from a second; this week is 19 commits with 10 from the second engineer, 7 from the original, and 2 from a third. Total volume also roughly halved.</p>
<p><strong>Interpretation:</strong> the primary author is no longer the majority of UBA’s commit log, and that took three weeks to happen rather than one, which reads as a real handoff of ownership rather than a week of holiday cover. The work that came with it is not maintenance either. The memory watchdog rework (<code>dd25c6ac</code>) is the week’s most substantial UBA commit by some distance, and it comes from the newer engineer.</p>
<p><strong>Who cares:</strong> build and CI engineers, anyone whose build farm depends on UBA.</p>
<p><strong>Signal strength:</strong> Moderate.</p>
<h3 id="the-cook-determinism-campaign-reached-the-branch-people-ship-from">The cook-determinism campaign reached the branch people ship from</h3>
<p><strong>Pattern:</strong> every week since early August has carried cook or cache determinism work on <code>ue6-main</code>. This week it crossed over. The gather changelist lists 34 incorporated CLs spanning May to September 2026, including the Blueprint RNG fix this briefing covered on <code>ue6-main</code> in the August 10 to 16 edition, material slot name derivation, static mesh lighting GUIDs, audio derived-data keys, and two numbered entries from a seven-part cook-metadata determinism series.</p>
<p><strong>Interpretation:</strong> the campaign has been readable as UE6 hygiene for two months. Its arrival in <code>//UE5/Main</code> changes who it is for. If you ship on 5.8 and your incremental cooks have been invalidating more than they should, this is the changelist that addresses it, and the fixes have had months of soak time on the other branch before getting here.</p>
<p><strong>Who cares:</strong> build and CI engineers on 5.8, anyone running incremental cooks on a farm.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li><code>EUnrealEngineObjectUE5Version</code> is frozen behind a <code>static_assert</code>, and the engine’s global package version is edited only from <code>//UE6/Main</code> from now on, with <code>EUnrealEngineObjectUE6Version</code> continuing the numbering in the same serialized slot (<code>802d4310</code>). If you carry custom object versions in a fork, read this commit.</li>
<li><strong>If you ship on 5.8:</strong> four months of cook and DDC determinism work landed on <code>ue5-main</code> in one changelist listing 34 incorporated CLs, covered with its commit reference in the 5.8 section below. This is the week’s most actionable item for anyone still on 5.x.</li>
<li>UBA’s memory watchdog was comparing both of its thresholds against the commit limit, so on a 274 GB machine the kill threshold sat at 100.9% of physical RAM and could not fire before the machine paged (<code>dd25c6ac</code>). The new modes default to the old behavior, so nothing changes until you configure it.</li>
<li>AutoRTFM’s migration reached the build files (<code>3a3da019</code>), and the legacy module properties were marked obsolete three days later (<code>83b47050</code>). Out-of-tree modules get the deprecation warning now and the removal later.</li>
<li>Adaptive Voxel-Based Order Independent Transparency landed on both branches (<code>4402e92a</code>, and on <code>ue5-main</code> as cited in the 5.8 section). <code>r.OIT.Method</code> defaults to it, but the shader permutations are a project opt-in that is off by default, so nothing changes in your project until you turn it on.</li>
<li>Horde’s Mongo index creation no longer blocks server startup, and a failed build no longer faults a collection for the process lifetime (<code>394690eb</code>). Act now if you self-host Horde.</li>
<li>Ness became something you can switch on: <code>UE_WITH_NESS=1</code> now enables it for any project without editing the target, and Lyra runs in its test job by default (<code>708358b4</code>).</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="the-ue5-object-version-enum-is-frozen">The UE5 object version enum is frozen</h3>
<p><strong>What changed</strong></p>
<p><code>EUnrealEngineObjectUE5Version</code> can no longer be appended to. The commit adds a <code>static_assert</code> so any further entry fails to compile, plus matching comments, and states the rule directly: the global package version is now edited only from <code>//UE6/Main</code>, and <code>EUnrealEngineObjectUE6Version</code> continues this numbering in the same serialized slot. If a UE5-only global version bump is ever unavoidable, the instruction is to use the <code>EUnrealEngineObjectUE4Version</code> slot instead. No code or format change, and the commit notes the added lines are byte-identical to <code>//UE6/Main</code> so the merge is conflict-free (<code>802d4310</code>). The same change landed on <code>ue5-main</code> the same day.</p>
<p><strong>Why this is important</strong></p>
<p>The object version enum is how the engine decides what a serialized package means. Freezing it is the moment the two branches stop being two lines of development that happen to share a format and start being one format with a defined owner. For anyone maintaining a fork, the practical consequence is immediate: a custom entry appended to the UE5 enum will now fail to compile, and the commit tells you where to put it instead. The instruction to fall back to the UE4 slot is also worth reading carefully, because it implies the UE5 slot is considered closed rather than merely full.</p>
<p>This is the kind of change that produces no release note and no behavioral difference, and then explains a class of merge conflict six months later.</p>
<p><strong>Who should care</strong></p>
<p>Engine-fork maintainers, anyone carrying custom object versions or serialization changes, technical directors planning a UE6 merge.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you maintain a fork. Track otherwise.</p>
<hr>
<h3 id="uba-was-throttling-builds-against-a-number-the-machine-was-not-holding">UBA was throttling builds against a number the machine was not holding</h3>
<p><strong>What changed</strong></p>
<p>The memory watchdog applied both of its percentage thresholds to the commit limit rather than to the quantity each threshold came from. The commit gives the consequence precisely: on a 274 GB machine the kill threshold sat at 100.9% of physical RAM and could not fire before the machine paged, and under MSVC, where commit peaks at 256 GB while resident never passes 147 GB, the build was throttled on a number 1.75 times what the machine was actually holding (<code>dd25c6ac</code>).</p>
<p><code>MemThresholdMode</code> now selects how thresholds are derived and defaults to <code>Legacy</code>, so omitting every new key changes nothing. <code>Split</code> gives physical and commit their own wait and kill pairs compared against their own measured quantity, and <code>Headroom</code> uses the same two axes with each threshold set to its base minus a reserve in bytes. The commit states Headroom ships, with wait reserves of 0, meaning measured per tick from how far usage ran past the last published estimate, and kill reserves of 4 GB capped at 10% of their own axis so a fixed few gigabytes cannot become a quarter of a small machine. Dropping the busy-state sample interval from 1000 ms to 250 ms took p99 overshoot from 13.60 GB to 2.10 GB.</p>
<p>The measured result on a large game module against Legacy: clang went from 401.9s to 365.3s at 61.4 to 87.1 mean concurrency with no kills, and MSVC from 431.5s to 392.8s at 50.1 to 63.9 with 8 kills. Structurally, every throttle and kill decision now lives in <code>MemoryWatchdog</code> and reaches the scheduler through callbacks, so the scheduler keeps no memory state and takes no memory lock, and the racing check moved to its own thread, which means enabling the watchdog no longer disables racing.</p>
<p><strong>Why this is important</strong></p>
<p>A kill threshold above physical RAM is a safety mechanism that cannot engage, and the failure it was supposed to prevent shows up as a machine paging rather than as an error anyone attributes to the watchdog. The concurrency numbers are the part to note: the win is not primarily wall clock, it is that the build stops throttling itself on a number that was never the constraint. If you run UBA on machines with large amounts of memory, this is worth measuring rather than assuming.</p>
<p>The MSVC row still reports 8 kills after the fix, which is honest reporting rather than a clean result, and worth knowing before you attribute a killed action to your own configuration.</p>
<p><strong>Who should care</strong></p>
<p>Build engineers, CI and infrastructure teams, anyone running distributed compiles on high-memory agents.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you run UBA on large-memory machines. The defaults are unchanged, so this is an opt-in you have to make.</p>
<hr>
<h3 id="autortfms-migration-reached-every-build-file">AutoRTFM’s migration reached every build file</h3>
<p><strong>What changed</strong></p>
<p>Across 90 build files, every <code>bDisableAutoRTFMInstrumentation</code> and <code>bInjectAutoRTFMAttributeDisables</code> assignment was replaced with <code>AutoRTFM.DefaultMode</code>, and every <code>AutoRTFMExternalMappingFiles.Add</code> call with <code>AutoRTFM.ExternalMappingFiles.Add</code>. The commit titles itself a migration of every <code>.Build.cs</code>, which in practice means every one that set those properties (<code>3a3da019</code>). The commit is explicit that it does not change behavior, that the legacy properties still exist and still work, and that they would be deprecated by the next changelist. Three days later they were: <code>ModuleRules.bDisableAutoRTFMInstrumentation</code>, <code>ModuleRules.bInjectAutoRTFMAttributeDisables</code>, and <code>ModuleRules.AutoRTFMExternalMappingFiles</code> are marked <code>[Obsolete]</code> naming their replacements, with the commit noting nothing in the tree uses them so the build is unaffected, and that they keep working for anything outside the tree until a final step removes them (<code>83b47050</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Last week this was a prediction. The commit that added the <code>ModuleRules.AutoRTFM</code> property kept the legacy flags as facades and explicitly changed no build files, and we said the migration commit it promised was the one that would reach your build files. It did, in the same shape, one week later.</p>
<p>The sequencing is the useful part for anyone maintaining out-of-tree modules. The three steps so far are introduce, migrate in-tree, deprecate, and Epic has stated the fourth is removal. You are currently in the window where your build files still compile and now produce a deprecation warning. That window closes on Epic’s schedule, not yours, and the warning is the only notice you get.</p>
<p><strong>Who should care</strong></p>
<p>Anyone maintaining out-of-tree modules or a plugin distributed as source, build engineers, engine-fork maintainers.</p>
<p><strong>Urgency</strong></p>
<p>Act now. The change is mechanical and the deprecation is already live.</p>
<hr>
<h3 id="order-independent-transparency-landed-and-it-is-off-by-default">Order-independent transparency landed, and it is off by default</h3>
<p><strong>What changed</strong></p>
<p>Epic implemented Adaptive Voxel-Based Order Independent Transparency, citing Drobot 2025 from Advances in Real-Time Rendering at SIGGRAPH 2025, covering both Nanite and non-Nanite translucency. It landed on <code>ue5-main</code> first and on <code>ue6-main</code> the same day, the latter with follow-up fixes squashed in (<code>4402e92a</code>). The <code>ue5-main</code> commits are cited in the 5.8 section below.</p>
<p>The technique approximates, for each event at depth z, the transmittance integral of everything in front of it, using a camera-aligned froxel volume. Occupancy of virtual slices is marked from the view-space depth ranges of visible translucent geometry and a prefix sum compacts them into physical slices, producing a depth-warp lookup table built on the GPU with no CPU readback. Translucent draws then splat extinction into that volume at reduced resolution, 1/8th by default, using <code>InterlockedAdd</code>. The commit is specific that no raster-ordered views are used anywhere and that the whole thing is built on plain UAV atomics, which is what distinguishes it from the existing <code>r.OIT.SortedPixels</code> MLAB path. Fully extinct 16x16 tiles emit depth-write quads so HiZ kills translucent pixels behind fully opaque smoke.</p>
<p>The switches matter more than the algorithm for most readers. <code>r.OIT.Method</code> selects at runtime, with 0 for none, 1 for AVBOIT as the default, and 2 for MLAB, and each falls back to standard sorted translucency where project or platform support is missing. The AVBOIT shader permutations are a project opt-in through <code>r.AVBOIT.Support</code>, which is read-only and defaults to off. Alongside it, <code>r.Nanite.AllowTranslucentMaterials</code> became a runtime toggle with a render state recreate sink, mirroring <code>r.Nanite.AllowMaskedMaterials</code>, where the previous <code>r.Nanite.AllowTranslucency</code> was read-only and needed a restart (<code>ef4c92f9</code>), and shader compile and stability fixes followed the same week (<code>d4125ecf</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Read the two defaults together before you conclude anything shipped. The method selector defaults to AVBOIT, and the shader permutations that make it possible are off unless a project turns them on, so the practical default is unchanged and the fallback is ordinary sorted translucency. This is a feature made available, not a feature switched on.</p>
<p>There is also a content constraint stated plainly in the commit, and it is the sort of thing that costs a week if you find it by experiment: translucent materials must output their own luminance only. Materials that sample scene color self-composite the background, which the resolve then adds again. The commit calls this inherent to every transmittance-integral OIT, and says refraction and distortion are future work. If your translucent materials read scene color, evaluate that before evaluating performance.</p>
<p><strong>Who should care</strong></p>
<p>Graphics programmers, technical artists working on translucency-heavy content, anyone who has been maintaining a bespoke sorted-translucency workaround.</p>
<p><strong>Urgency</strong></p>
<p>Track, and read the content constraint before you schedule an evaluation.</p>
<hr>
<h3 id="hordes-mongo-index-creation-stopped-blocking-startup">Horde’s Mongo index creation stopped blocking startup</h3>
<p><strong>What changed</strong></p>
<p>Index creation previously blocked server startup. The collection wrapper gated every operation on its index build, builds serialized behind a cluster-wide lock, and a build that failed three times faulted the wrapper, failing every subsequent operation on that collection for the process lifetime. Indexes are now built by a background <code>MongoMaintenanceService</code> registered ahead of all other hosted services. Operations are never gated on non-unique indexes; collections with unique indexes wait only until those are confirmed, bounded by a 30 second default. Only worker-mode writable instances build, others poll, and failures retry with capped exponential backoff. A unique build failing on duplicates moves all but the newest duplicate per key into a sibling <code>.DuplicateQuarantine</code> collection, copying before deleting, then retries (<code>394690eb</code>). Follow-ups reported build progress to the database audit log (<code>9c51e0dd</code>) and reordered things so unique indexes build first with the gate confirming them itself (<code>19578c7b</code>).</p>
<p>The same changelist added a diagnostics layer: operations issued during a request are stamped with a comment naming the caller, route template, and trace id, and an operation tracker retains the slowest of the last hour aggregated per user, route, collection, and command, surfaced through new admin-only endpoints and a Database Insights view.</p>
<p><strong>Why this is important</strong></p>
<p>The faulted-wrapper behavior is the one to internalize. A transient index build failure turning into permanent per-collection failure for the lifetime of the process is the kind of bug that presents as “Horde is broken, restart it,” which works, which means nobody diagnoses it. If you self-host Horde, this is a real availability fix, and the new diagnostics are the first time the server will tell you which caller is responsible for database load.</p>
<p><strong>Who should care</strong></p>
<p>Anyone self-hosting Horde, build infrastructure teams, studios running their own CI backplane.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you self-host Horde.</p>
<hr>
<h3 id="the-agent-surface-grew-a-module-and-a-menu">The agent surface grew a module and a menu</h3>
<p><strong>What changed</strong></p>
<p>The async tool call result was split into its own engine module, <code>AsyncFunctionResult</code>. It landed, was backed out, and relanded inside the window, and the relanded version reaches further, into the modular test runner (<code>3cbf8b56</code>). Around it, toolsets backed by a function library are now tracked for validity and retired when the backing class unloads, rather than lingering as registrations pointing at nothing (<code>9adf873e</code>).</p>
<p>The integration that matters is smaller. <code>IEditorAIPromptProvider</code> is a new editor-side interface in <code>EditorFramework</code> that lets tools contribute AI Assistant menu entries without depending on the AI Assistant plugin, which is off by default. The AI Assistant editor module implements it, so the section appears only while the plugin is loaded. The first consumer is Crash Diagnostics, which gained a right-click menu on crash rows with <code>Copy Full Path</code>, <code>Open Containing Folder</code>, and an <code>Analyze Crash</code> entry that opens a conversation about the crash under the cursor (<code>d7b1a871</code>).</p>
<p><strong>Why this is important</strong></p>
<p>A capability that needs its own engine module is no longer a plugin experiment, and a provider interface designed so the menu entry appears only when an off-by-default plugin is loaded is the shape of something Epic expects to be on for a lot of people later. The Crash Diagnostics choice is a deliberate one: it is the editor surface where a developer is already stuck, already has structured data in front of them, and has the least to lose from a model’s first answer being wrong.</p>
<p><strong>Who should care</strong></p>
<p>Tools programmers, pipeline and automation teams, anyone evaluating model-driven editor automation.</p>
<p><strong>Urgency</strong></p>
<p>Track.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>Ness became something you can turn on.</strong> Twelve commits this week, down from last week’s twenty-one, but the shape changed. <code>UE_WITH_NESS=1</code> is now a compile argument that enables Ness for any project without modifying the target directly, and Lyra is enabled by default in the Ness Test job (<code>708358b4</code>). Last week we said the commit messages do not explain what it is for and we were not going to guess. That is still true, and we still are not. What changed is that it now has an opt-in switch and a real game running through its test job, which is what a serialization layer does immediately before people outside the team are expected to try it. Keep it on the list.</p>
<p><strong>A devirtualization option appeared with no discussion attached.</strong> <code>bDevirtualizeFunctionCalls</code> is a new target option enabling C++ virtual call devirtualization on the clang toolchain via <code>-fwhole-program-vtables</code> and <code>-fforce-emit-vtables</code> (<code>7929ed04</code>). The commit body is the title, and it carries four reviewers, which is more than most build options get. Whole-program vtables require link-time optimization to be meaningful, so this reads as connected to the distributed ThinLTO work that has been landing since early September rather than as a standalone flag. No measurements were published with it, which is the reason it is here rather than in the highlights.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>This week we ignored roughly 750 commits: localization automation, binary manifest drops for internal content pipelines, documentation generation, test-only fixes, editor UI polish, and the usual run of small cleanups. There were 17 commits tagged <code>[Backout]</code>, inside the normal band for this branch and not a story on its own. Most relanded in-window, including the <code>AsyncFunctionResult</code> module split covered above and a fix making RigVM function libraries cook deterministically (<code>a39dcf65</code>, relanded at <code>3e145d8a</code>).</p>
<p>Two that did not come back are worth naming, because both are cook-determinism work and this post spends a section arguing that campaign is going well. A cooker change serializing a null <code>SoftObjectPath</code> for paths pointing at objects found not to need cooking was reverted because it produced errors in AssetManager, which expects an array of <code>FSoftObjectPath</code> not to be empty (<code>51aadf70</code>). And a change skipping the GeometryCollectionComponent RestTransforms mismatch reset during cook, so that cooked output would stop depending on whether component registration ran before the save, was backed out on the last day of the window with no reland (<code>46c3e48d</code>). Both were still out of the tree when the week closed.</p>
<p>Of 963 commits, 210 worth reading. Scene graph and typed element work was the largest identifiable feature cluster at 65 commits, almost none of it individually notable, which is the same pattern that cluster has shown since August. Vulkan took a round of structural work including removal of the legacy <code>VULKAN_ENABLE_DUMP_LAYER</code> and <code>VULKAN_ENABLE_WRAP_LAYER</code> paths (<code>2c1455fe</code>) and buffer defragmentation support (<code>cca2d1a4</code>). Android fatal signal handling was reworked so the original faulting context is preserved and handed to the OS crash handler after engine reporting completes, times out, or the forwarded handler itself faults (<code>ad9c3864</code>). And a run of Clang 23 analyzer findings was addressed across the Win64 editor (<code>d97fe6fc</code>), which is worth knowing about if your own static analysis is about to get noisier for the same reason.</p>
<hr>
<h2 id="for-teams-shipping-on-58">For teams shipping on 5.8</h2>
<p><code>ue5-main</code> ran 172 commits with 2 backouts, and this was not a maintenance week. Several items are worth a cherry-pick, and the first is the largest thing to reach this branch in weeks.</p>
<ul>
<li><strong>Four months of cook and DDC determinism work arrived in one changelist.</strong> One changelist gathered cook, cook-metadata, and DDC determinism fixes from May through September 2026 into <code>//UE5/Main</code>, listing 34 incorporated CLs (<code>d360778e</code>). The list is worth reading in full, because the fixes are individually small and collectively describe every way a cook can be a function of something other than its inputs: derived-data key collisions between sound waves sharing a <code>CompressedDataGuid</code>, non-deterministic texture coordinate data in cooked static mesh data, owner-derived shader <code>FriendlyName</code> values baked into content-addressed shader map values for both ComputeKernel and OpenColorIO, an uninitialized padding field serialized into cooked cloth render data, a random <code>StyleRule</code> RuleId minted during cook saves, <code>UBodySetup::BodySetupGuid</code> as a random bumped GUID rather than a content hash, <code>UStaticMesh::LightingGuid</code>, material slot names, hlslcc transcendental constant folding that was not cross-machine deterministic, and Blueprint RNG nodes routed through <code>FCookRand</code>. That last one is the fix this briefing covered on <code>ue6-main</code> in the August 10 to 16 edition. Two of the entries are numbered parts of a seven-part “cook metadata determinism” series, so that series arrives only partially. The expensive cache bugs are the ones that produce correct output, and a cook that invalidates too often is indistinguishable from a project that is simply large. Expect invalidation on the first cook after taking it, for the same reason every determinism fix in this campaign has carried that cost. <strong>This is the week’s most actionable item for anyone on 5.x.</strong></li>
<li><strong>The UE5 object version enum froze here too</strong> (<code>93cb7201</code>), the same change described in the highlights above. If you carry custom object versions on 5.x, the UE4 slot is now where the commit tells you to put them.</li>
<li><strong>HLOD bakes could produce black virtual texture pages that never regenerated.</strong> HLOD captures rendered a fixed number of warmup frames back to back, so virtual texture feedback was rarely read back in time, tile production stayed throttled, and adaptive RVTs refined only one grid cell per frame, meaning textures were captured before the RVT had converged. Separately, in the editor, shaders compile on demand from the first draws that need them and the RVT page pass falls back to the default material until then, so the first bake of a session produced black pages that were never re-produced. Capture view families now disable VT throttling, warmup waits for the GPU, tiles covering the captured bounds are requested explicitly at the captured resolution, and a warmup that triggers shader compilation now finishes it and warms up again. It also fixes a crash when force-building several HLOD actors from the details panel, where the render state recreate context held raw pointers to garbage-collected components (<code>eaf526fb</code>).</li>
<li><strong>Depth of field mixed colors across depth layers.</strong> Tiles containing both foreground and background pixels selected higher mip levels, so heavily out-of-focus areas bled colors from different depth layers into neighboring pixels. Tiles with both now select mip 0. The same commit fixes Diaphragm DOF sampling NaNs during <code>CocFlattenMainCS</code> on some platforms, and a recombine pass whose CoC radius disagreed with earlier passes when internal resolution was reduced, which produced blocky artifacts when upscaling (<code>4676caf0</code>).</li>
<li><strong>Global distance field recaching during streaming was excessive.</strong> Deferred update flags are now cleared after any recache, only one streaming clipmap recapture is allowed per frame, and mesh SDFs too small to meaningfully affect the result no longer trigger a streaming recapture (<code>0a80d473</code>).</li>
<li><strong>Two landscape crashes, both in code paths you hit by accident.</strong> Loading an old landscape that predates edit layers crashed semi-randomly (<code>e26aa9a4</code>), and a race condition crashed the landscape editor following an undo (<code>448bfbab</code>).</li>
<li><strong>PCG crashed iterating destroyed execution sources</strong> in the tracking manager octree. Entries now hold a weak pointer and skip destroyed sources, and unregister from the owning world subsystem on <code>BeginDestroy</code> (<code>ae2a64d1</code>).</li>
<li><strong>Control Rig crashed when switching the control type of a spawned control,</strong> fixed and then improved in the same week (<code>ff852c4d</code>, <code>0599fff5</code>).</li>
<li><strong>Two UAF crashes:</strong> parallel module ticks resolving the same variable reference (<code>5ff5b1a4</code>), and <code>VariableReferencePropertyCustomization</code> used in a context providing no valid property utilities (<code>74168ccd</code>).</li>
<li><strong>Sequencer leaked on level removal.</strong> Removing a level containing a level sequence actor leaked through GC (<code>340ec52c</code>), and dropping an animation asset with the Anim Mixer plugin enabled asserted (<code>906de4c1</code>).</li>
<li><strong>Editor crashed reopening a docked asset editor after reloading the editor layout</strong> (<code>5d4d8082</code>), and Movie Render Pipeline crashed in the graph when rendering a sequence without audio while audio was enabled as an output (<code>a37314fe</code>).</li>
<li><strong>AVBOIT is on this branch too</strong> (<code>a3f91409</code>, <code>669b3d63</code>, <code>a86b5948</code>), with the same project opt-in default described above. Worth knowing it exists on 5.x before someone on your team reads about it and assumes it is UE6-only.</li>
</ul>
<hr>
<h2 id="closing">Closing</h2>
<p>The commit that mattered most this week changes one file, adds a <code>static_assert</code>, and alters no behavior at all. It just decides, permanently, which branch the engine’s version number comes from. Those are the ones worth reading the log for: the changes that cost nothing today and constrain everything afterwards, and that no release note will ever mention because, strictly speaking, nothing happened.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless the claim is in the “For teams shipping on 5.8” section, which references <code>ue5-main</code>. Many of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-sep-14-20-2026/og.7d53dc7895.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: The Cook Never Cached (September 7 to September 13, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-sep-7-13-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-sep-7-13-2026</guid>
      <pubDate>Mon, 14 Sep 2026 05:14:00 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>727 commits on ue6-main, and the two that matter most are four files between them. A class default object minting a GUID in its constructor, and a component writing to itself during serialization, both made native class hashes differ between identical cooks, so dependent blueprints never cached and recooked every time. Meanwhile Epic shipped an agent-facing tool surface into the editor, including a plugin that reads your Codex and Claude config directories, and the renderer deleted a console variable along with legacy HZB occlusion culling.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-the-cook-never-cached">Unreal Engine Weekly: The Cook Never Cached</h1>
<p><strong>Week of September 7 to September 13, 2026</strong> | <strong>727 commits on <code>ue6-main</code>, 127 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>Two small commits this week explain why your incremental cooks have been doing more work than they should. A class default object that mints a GUID in its constructor, and a component that writes to itself while being serialized, both made native class hashes differ between identical cooks, so dependent blueprints never cached and recooked every time. Neither commit is large. Both are the kind of thing you cannot find from the outside, because the symptom is a cook that is merely slower than it should be, not a cook that fails.</p>
<p>Around that, the week’s other through-line is the editor growing a tool surface built for a non-human caller. Epic shipped a native file-operations plugin, a plugin that reads your Codex and Claude configuration directories, an isolated undo buffer for tool calls, and a fix for a Model Context Protocol server that reported a live endpoint it did not have. On the build side, UnrealBuildAccelerator’s Linux port finally got the compressed-intermediates path that made the Windows path cheap. At 727 commits the week sits at the low end of this branch’s usual band, which is week-to-week variance rather than a slowdown.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<h3 id="the-cook-and-cache-determinism-thread-is-now-three-months-long">The cook and cache determinism thread is now three months long</h3>
<p><strong>Pattern:</strong> every week since early August has carried at least one fix for a cache key that depended on something that is not an input. Shader job cache keys dropping at growth boundaries in early August, random numbers baked into packages the week after, cook metadata that did not match its own bytes at the start of September, and this week two separate sources of native class hash instability (<code>25599f8a</code>, <code>8c37fb4f</code>) plus compile-relevant texture state that was missing from material shadermap keys (<code>db1878a8</code>).</p>
<p><strong>Interpretation:</strong> this is not a bug cluster, it is a campaign, and the direction is consistent. Epic is making the cook reproducible by removing hidden inputs one at a time. Each fix carries a one-time invalidation cost and a permanent reduction in redundant work afterward. The pattern also tells you where to look in your own pipeline: the expensive cache bugs are not the ones that produce wrong output, they are the ones that produce correct output too often.</p>
<p><strong>Who cares:</strong> build and CI engineers, anyone running incremental cooks on a farm.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="agent-tooling-stopped-being-a-plugin-and-started-being-infrastructure">Agent tooling stopped being a plugin and started being infrastructure</h3>
<p><strong>Pattern:</strong> in-editor agent tooling has appeared in this report since mid-July, first as a toolset and crash-hardening audit, then as a background tool-call mechanism, then as reflection APIs reshaped around token cost. This week it arrives with a permission model with a restricted mode (<code>f1f0470e</code>), an isolated undo buffer so agent edits stay out of the user’s undo queue (<code>6c091f40</code>), a hardened Model Context Protocol transport (<code>b11e879d</code>), a catalog-caching proxy (<code>8a6f9e96</code>), and modal-popup suppression during tool calls (<code>2ff22fa2</code>).</p>
<p><strong>Interpretation:</strong> the failure modes being fixed are the ones that only matter when a machine is driving. A human notices a modal dialog and dismisses it. An agent hangs on it. The presence of a restricted mode that hides read operations, not just writes, says Epic expects this surface to run against projects where the caller is not fully trusted.</p>
<p><strong>Who cares:</strong> tools programmers, pipeline and automation teams, anyone evaluating model-driven editor automation.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="unrealbuildaccelerators-linux-port-caught-up-and-its-author-concentration-loosened">UnrealBuildAccelerator’s Linux port caught up, and its author concentration loosened</h3>
<p><strong>Pattern:</strong> we have flagged for a month that UBA’s commits came almost entirely from one author. This week it is 32 of 40, with a second author at 7. Last week it was 38 of 39. The work itself moved to Linux: compressed intermediate files (<code>26ad0bae</code>), a native visualizer on both Wayland and X11 (<code>bf02cca5</code>, <code>874ab3f1</code>), and a fix for Ctrl-C not reaching UnrealBuildTool at all on Linux (<code>ebf19945</code>).</p>
<p><strong>Interpretation:</strong> a second name in the commit log is the first movement on this in five weeks of watching it, and it is worth reading as capacity rather than as a handover, because the primary author’s volume did not drop. The Linux focus is the more consequential half. Compressed intermediates are the mechanism that made UBA’s remote-compile path cheap on Windows, and cloud build farms for UE studios are usually Linux.</p>
<p><strong>Who cares:</strong> build and CI engineers, studios running distributed compiles on Linux agents.</p>
<p><strong>Signal strength:</strong> Moderate.</p>
<h3 id="the-renderer-decomposition-started-removing-console-variables-not-just-code">The renderer decomposition started removing console variables, not just code</h3>
<p><strong>Pattern:</strong> the SceneData migration has produced a cluster every week since mid-July. The character keeps escalating. Converting call sites gave way to deleting the old ones at the start of September, and this week legacy primitive HZB occlusion culling was removed along with the <code>r.HZBOcclusion</code> console variable (<code>a89416ed</code>), while <code>GetLumenSceneData</code> moved out of <code>FScene</code> entirely (<code>e9c15aa6</code>).</p>
<p><strong>Interpretation:</strong> a removed function is a compile error you fix in an afternoon. A removed console variable is a shipped configuration that silently stops existing, and anyone carrying it in a device profile or a studio default finds out at runtime. This is the point in a migration where the merge cost stops being mechanical.</p>
<p><strong>Who cares:</strong> graphics programmers maintaining custom renderer code, anyone planning a UE6 merge for an engine fork.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Two commits fixed native class hashes that changed between identical cooks, so dependent blueprints stopped caching and recooked forever (<code>25599f8a</code>, <code>8c37fb4f</code>). Expect a one-time invalidation of every dependent blueprint on the first cook after you take these, then less work permanently.</li>
<li><code>-SkipOnlyEditorOnly=false</code> is no longer supported and now activates a narrower reachability mode (<code>7d0a26b5</code>). Packages that were only ever pulled into your cook incidentally will stop being included. If you rely on that, declare the dependency.</li>
<li>UnrealBuildAccelerator got compressed intermediate files on Linux (<code>26ad0bae</code>), and Ctrl-C now actually cancels UnrealBuildTool there (<code>ebf19945</code>).</li>
<li>Epic shipped an agent-facing tool surface into the editor, including a plugin that discovers skills, agents, and <code>AGENTS.md</code> or <code>CLAUDE.md</code> from configured Codex and Claude directories (<code>dc21275d</code>).</li>
<li>The renderer removed the <code>r.HZBOcclusion</code> console variable along with legacy primitive HZB occlusion culling (<code>a89416ed</code>).</li>
<li>A Verse codegen fast path took script deserialization on a 5000-message repro from 15.5 minutes to 1.4 seconds (<code>ac696db9</code>).</li>
<li><strong>If you ship on 5.8:</strong> a per-bone blend setup fix that only affected cooked builds, a Sequencer duplication bug that overwrote the original’s anim sequence assets on disk, and a Vulkan regression that rendered large TMV plates black. All three are cherry-pick candidates, and they are covered with their commit references in the 5.8 section below.</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="the-cook-has-been-recooking-blueprints-that-never-changed">The cook has been recooking blueprints that never changed</h3>
<p><strong>What changed</strong></p>
<p>Two independent sources of instability in the cook’s native class hash were fixed. <code>HashNativeClass</code> stringifies a class and its class default object to detect when a native class has changed. A default object holding a member whose constructor calls <code>FGuid::NewGuid()</code> hashes differently in every process, so the class reported as changed on every cook and its dependent blueprints never cached. The fix adds an opt-in filter flag for members already tagged as unstable at initialization, and tags three members that were missing it (<code>25599f8a</code>). Separately, <code>USplineComponent::Serialize</code> assigned <code>LastAuthority</code> whenever the archive reported saving, and the dependency hasher stringifies through an archive that reports saving, so hashing a class whose default object holds a spline mutated that object. Whether the stamped value landed in the hashed text depended on what else had serialized the component earlier in the process, which made the hash differ between identical incremental cooks (<code>8c37fb4f</code>).</p>
<p><strong>Why this is important</strong></p>
<p>These are the expensive kind of cache bug, because the output is correct. Nothing fails, nothing looks wrong, and the only symptom is that incremental cooks do full-cook work on blueprints nothing touched. Both commits note that the first cook after the change is a one-time invalidation of every dependent blueprint, so budget for one slow cook and measure the steady state after it.</p>
<p><strong>Who should care</strong></p>
<p>Build and CI engineers, anyone running incremental cooks on a farm, studios where cook time is a scheduling constraint.</p>
<p><strong>Urgency</strong></p>
<p>Act now.</p>
<hr>
<h3 id="the-cookers-reachability-rules-changed-and-packages-will-cook-out">The cooker’s reachability rules changed, and packages will cook out</h3>
<p><strong>What changed</strong></p>
<p><code>-SkipOnlyEditorOnly=false</code> is deprecated, warns when set, and now activates <code>-Cook.RequiredReachability=Editor</code> rather than the full behavior it used to provide. Two consequences are stated in the commit. Packages incidentally loaded while loading and saving a referenced package are no longer included in the cook unless they are declared in AssetRegistry dependencies or actually used in the cooked package. And in MultiPlatform cooking, packages required on one platform are no longer automatically added to the other platforms (<code>7d0a26b5</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Incidental-load reachability is the sort of thing a project depends on without anyone deciding to. If an asset has been arriving in your cooked build because something else happened to load it during a save, it stops arriving, and the failure shows up at runtime as a missing reference rather than at cook time as an error. The MultiPlatform half has the same shape across platforms.</p>
<p><strong>Who should care</strong></p>
<p>Build engineers, anyone cooking for more than one platform in a single invocation, teams with large legacy content sets.</p>
<p><strong>Urgency</strong></p>
<p>Act now.</p>
<hr>
<h3 id="unrealbuildaccelerators-linux-port-stops-paying-full-freight">UnrealBuildAccelerator’s Linux port stops paying full freight</h3>
<p><strong>What changed</strong></p>
<p>Compressed intermediate files landed on Linux, covering <code>.obj</code>, <code>.o</code>, <code>.pch</code>, and <code>.gch</code> (<code>26ad0bae</code>). The implementation hands the compiler process a real file descriptor to a session-owned file mapping, so reads, writes, and seeks go to the kernel rather than through Windows-style emulated file semantics over a shared-memory view. It also adds a clang linker dependency crawler so object files decompress in parallel as the linker opens them rather than serially. The commit states it is not enabled on macOS, because darwin cannot reopen a shared-memory object and file position would end up shared between processes.</p>
<p>The tail of bugs that followed is instructive, and all of it landed in the same week: a cross-device move of a streamed write truncating files to zero bytes, because the copy fallback used the creation size that compressed writers set to zero (<code>c84a58fc</code>); the copy fallback not preserving timestamps, so deferred cache creation re-read and re-hashed the file (<code>bad3d287</code>); and storage not reporting a size for a prefetched cache file, so the detour fell back to a directory-table size produced on a different machine (<code>652609f3</code>, <code>a79c60b6</code>).</p>
<p>Separately, Ctrl-C had stopped cancelling UnrealBuildTool on Linux. <code>SetupOrphanReaper()</code> called <code>setpgid(0,0)</code> without a following <code>tcsetpgrp()</code>, so terminal-generated SIGINT never reached the process. It sits in a startup path UnrealBuildTool calls unconditionally, so the executor you were using did not matter (<code>ebf19945</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Compressed intermediates are the mechanism that made UBA’s remote-compile path cheap on network and disk on Windows. Linux agents have been paying full-size transfers for every object file and precompiled header. For studios whose farms are Linux cloud runners, which is most of them, this changes the transfer economics of distributed compilation. The accompanying bug tail is a fair warning that it arrived by being shipped and exercised.</p>
<p><strong>Who should care</strong></p>
<p>Build engineers, CI and infrastructure teams, anyone running distributed UE compiles on Linux agents.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you are on Linux agents, and benchmark before and after rather than assuming the win.</p>
<hr>
<h3 id="epic-shipped-an-agent-facing-tool-surface-into-the-editor">Epic shipped an agent-facing tool surface into the editor</h3>
<p><strong>What changed</strong></p>
<p>A cluster of work made editor tooling safe and available for a non-human caller. <code>FileSystemToolset</code> is a new plugin exposing nine native file operations, including reading images up to 64 MiB, listing directories, writing and editing text, deleting files, and downloading HTTPS URLs. It hides all nine tools, reads included, when a registered permission provider reports restricted mode, because native paths bypass asset permission rules (<code>f1f0470e</code>). Follow-ups raised the download ceiling (<code>24b5b652</code>), fixed directory listings for reserved Windows filenames (<code>1b05afef</code>), and fixed a crash where probing DNS names through <code>ISocketSubsystem::GetAddressFromString</code> translated whatever socket error happened to be pending and hit a check on Windows (<code>814fef49</code>).</p>
<p><code>AgentPluginToolset</code> is also new, and it discovers Codex and Claude plugin manifests, direct child skills, agents, and <code>AGENTS.md</code> or <code>CLAUDE.md</code> from the current tool working directory, with deterministic capability IDs and rejection of names containing the characters used to delimit capability references (<code>dc21275d</code>). The registry underneath gained a real execution model: tool calls now receive a context carrying a cancellation token and an isolated undo buffer, installed for the duration of the call so agent edits stay out of the user’s undo queue (<code>6c091f40</code>, <code>4b8bace5</code>).</p>
<p>Two fixes show the shape of the risk. <code>FModelContextProtocolServer::Start</code> requested its HTTP router before listeners started, so the server reported a configured endpoint that did not exist, and a second editor could connect to the first through the shared port (<code>b11e879d</code>). And because a comment box is a graph node that stores its label in the same field a node note uses, <code>set_node_note</code> overwrote comment box labels and reported success, which means an agent tidying notes across a graph would have quietly rewritten every comment box it touched (<code>66ef7da5</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The details give away how seriously this is being taken. A restricted mode that hides reads rather than only writes, an undo buffer that isolates a caller’s edits, modal-popup suppression during tool calls (<code>2ff22fa2</code>), and editor screenshots forced to opaque alpha because the images are handed to a vision model that composites against an unknown background (<code>f35c9967</code>). These are not the concerns of a prototype. If you are planning editor automation, this registry is the surface to build against rather than a bespoke bridge.</p>
<p><strong>Who should care</strong></p>
<p>Tools programmers, pipeline and automation teams, anyone evaluating model-driven editor automation.</p>
<p><strong>Urgency</strong></p>
<p>Track.</p>
<hr>
<h3 id="the-renderer-removed-a-console-variable-not-just-code">The renderer removed a console variable, not just code</h3>
<p><strong>What changed</strong></p>
<p>Legacy primitive HZB occlusion culling was removed along with the <code>r.HZBOcclusion</code> console variable, preserving hardware queries, mobile feedback, and feature-specific HZB consumers (<code>a89416ed</code>). <code>GetLumenSceneData</code> moved out of <code>FScene</code> and now returns a reference, so a Lumen-specific extension is no longer included across the whole renderer (<code>e9c15aa6</code>). Deprecated code was removed from Renderer and RenderCore (<code>0ad1a9c8</code>, <code>65641439</code>).</p>
<p>The migration underneath continued at pace. Velocity moved onto scene data and dirty tracking, storing a persistent primitive ID rather than a scene-info pointer, with the registration set becoming a bool data array so freed IDs reset automatically and recycled IDs cannot inherit a stale bit (<code>657a7a7d</code>). Capsule shadows did the same, with the indirect-shadow gather reading proxies and bounds through read views (<code>a589535d</code>). A parallel iteration primitive landed for the whole migration to use (<code>6d2cea0a</code>). Hardware ray tracing is being re-homed the same way, moving primitive registration into a ray tracing scene extension (<code>f76c2e3a</code>) and splitting base and decal instances into separate cached entries for about 0.1ms of CPU time in the MegaLights demo (<code>5ee6b5a9</code>).</p>
<p><strong>Why this is important</strong></p>
<p>A removed function is a compile error. A removed console variable is a shipped configuration that stops existing, and a device profile or studio default carrying <code>r.HZBOcclusion</code> now sets nothing. This is where the merge cost of the SceneData work stops being mechanical, because the question is no longer whether your code compiles but whether it assumed an invariant that is gone.</p>
<p><strong>Who should care</strong></p>
<p>Graphics programmers maintaining custom renderer code, engine-fork maintainers, technical directors planning a UE6 merge.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you maintain a renderer fork.</p>
<hr>
<h3 id="verse-load-times-155-minutes-to-14-seconds">Verse load times: 15.5 minutes to 1.4 seconds</h3>
<p><strong>What changed</strong></p>
<p>Islands with many <code>&lt;localizes&gt;</code> declarations hung on load. Each message initializer emitted roughly 19 property operands plus 5 temporary properties into the shared initialization function, and every operand deserialized through a field-path resolve that linearly scans the owner’s field list, making the whole thing quadratic in message count. A codegen fast path now emits simple message literals as a single <code>MakeMessageLiteral</code> call with 3 operands and 1 temporary. On a 5000-message repro, script deserialization went from 15.5 minutes to 1.4 seconds (<code>ac696db9</code>). The commit notes that other functions and types can hit the same pattern, and that the new VM does not, calling the VM swap the long-term fix.</p>
<p>Alongside it, a Verse interface-field memory corruption was fixed. The compiler’s check for which class controls an interface field’s C++ declaration succeeded for the first class to override the field without considering that a superclass may already have defined it, producing two copies of the field, with the subclass’s binding overwriting the superclass property’s offset (<code>4b146290</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The number is the story. A quadratic in a load path is the kind of thing that is invisible at small content scale and fatal at large content scale, and the repro size where it becomes fatal is well inside what a shipping island can reach. The interface-field bug is the more dangerous of the two in a quieter way, since memory corruption from a compiler-side ownership check does not present as a Verse problem.</p>
<p><strong>Who should care</strong></p>
<p>Teams shipping Verse content at scale, anyone whose editor or cook load times have grown non-linearly with content.</p>
<p><strong>Urgency</strong></p>
<p>Act now if load times are a problem for you.</p>
<hr>
<h3 id="the-windows-link-step-is-in-flux">The Windows link step is in flux</h3>
<p><strong>What changed</strong></p>
<p>Windows distributed ThinLTO backend codegen was corrected to match lld’s default optimization level for all backends and to restore function and data sections. The measured result on a Win64 Test build at the same head: the text section shrank from 525.0 MB to 440.4 MB, section hashes matched the in-process link, all 480,230 text functions matched in size, and backend warning lines went from 19,698 to zero (<code>e2d40c29</code>).</p>
<p>Rad Game Tools’ linker is being stabilized in the same window, with four binary drops in three days and a backout between them. One drop shipped a build carrying two patches, numbered 934 and 935, that the later drops do not (<code>cfae9390</code>), was backed out (<code>6d00de1b</code>), and the three later drops all list a different base and do not carry those two patches, so that specific build did not come back this week. Separately, radobj and radlink are now explicitly disabled when profile-guided optimization is being used, in both the optimize and profile directions (<code>a3a141f6</code>, <code>c89635df</code>).</p>
<p>ASan needed toolchain work on both platforms: forcing ASan libraries to the start of the library list and import table, because atexit table allocations were using unshadowed memory (<code>f27dd11a</code>), and fixing Linux server ASan runtime loading where an architecture suffix added during copy broke the dependency name baked in at link time (<code>0920739a</code>).</p>
<p><strong>Why this is important</strong></p>
<p>If you ship profile-guided optimized Windows builds, the radlink exclusion is a live constraint rather than a note. The ThinLTO numbers are worth having as a baseline: a correctly configured distributed backend should match the in-process link byte for byte, and if yours does not, the warning count is the tell.</p>
<p><strong>Who should care</strong></p>
<p>Build engineers on Windows, console and platform teams, anyone using PGO or sanitizer builds.</p>
<p><strong>Urgency</strong></p>
<p>Act now for PGO users. Track otherwise.</p>
<hr>
<h3 id="ambient-lighting-volumes-leave-mobile">Ambient Lighting Volumes leave mobile</h3>
<p><strong>What changed</strong></p>
<p>Diffuse from capture was extended to the desktop deferred Substrate non-Lumen path, with the renames that go with leaving mobile behind: <code>bMobileDiffuseFromCapture</code> became <code>bDiffuseFromCapture</code>, and <code>r.Mobile.DiffuseFromCapture</code> became <code>r.ReflectionCapture.DiffuseFromCapture</code> (<code>8aca08e0</code>). Tile-culled ALV sampling was added for Lumen screen probes and translucency lighting volume froxels, and the console variable dropped its experimental infix, from <code>r.AmbientLightingVolume.Experimental.EnableForLumen</code> to <code>r.AmbientLightingVolume.EnableForLumen</code>. Measured on PC on a level carrying many volumes, the Lumen probe gather went from 1.9ms without the acceleration structure to a stable 1.5ms with it, matching the cost of hiding all the volumes. World-space probes keep the brute-force path because they can sit outside the view frustum (<code>7b246bea</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Dropping an experimental infix from a console variable name and removing a mobile prefix from a feature flag is a rename, not a documented status change, and it should not be read as a graduation. It is worth noting anyway, because a desktop consumer, a Lumen integration behind a non-experimental variable, and an acceleration structure arriving together is the shape a system takes in the weeks before a default changes.</p>
<p><strong>Who should care</strong></p>
<p>Lighting artists, graphics programmers, mobile and cross-platform titles.</p>
<p><strong>Urgency</strong></p>
<p>Track.</p>
<hr>
<h3 id="the-ci-flakiness-fixes-are-worth-less-than-their-diagnoses">The CI flakiness fixes are worth less than their diagnoses</h3>
<p><strong>What changed</strong></p>
<p>A run of Gauntlet and Horde fixes this week read as a postmortem series. Clearing stale artifacts between reservation-block steps deleted the entire user directory, throwing away the editor’s asset registry cache and pip environment, so every step after the first spent about three minutes re-gathering 22,000 assets and reinstalling dependencies before dispatching a single test, which was long enough for an idle console to be PLM-constrained and the run killed with zero tests reported (<code>ca638a0f</code>). The Android transfer budget was widened after measurement against the slowest device in the pool, and more usefully, a transfer timeout now raises a specific exception rather than a generic device exception that pulled a healthy handset out of the shared pool for every other job (<code>b852b661</code>).</p>
<p>The Horde ref index is eventually consistent and ref reads are not retried internally, which is why artifact retrieval intermittently returned not-found for artifacts that existed while jobs immediately before and after succeeded (<code>0094ee23</code>). A staged CLI binary update notes that the new binaries alone would have made macOS clones exit with an argument-parse error, because the new version removed a flag the caller still defaulted on for macOS (<code>d9c0a9d4</code>). Two more are the same class of bug from opposite ends: an audio device garbage collection race hitting about a third of scheduled runs in one test group (<code>7ec88951</code>), and a use-after-free where a screenshot taker only unbound from a process-global delegate if a game viewport client happened to exist (<code>2f4cefc3</code>).</p>
<p><strong>Why this is important</strong></p>
<p>None of these are Unreal-specific. Eventually consistent reads without retries, a cleanup step that deletes a warm cache, an error type too generic to keep a healthy machine in the pool, and a delegate unbind behind a conditional are all failure modes that exist in any device-based automation. The fixes matter to Epic. The diagnoses are the part worth stealing.</p>
<p><strong>Who should care</strong></p>
<p>Build and QA infrastructure, anyone running automated device testing.</p>
<p><strong>Urgency</strong></p>
<p>Track.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>Something called Ness is being built in <code>Runtime/Ness</code>, and it is reaching into CoreUObject.</strong> Twenty-one commits this week touch <code>NessCore</code>, <code>NessUObject</code>, and a <code>NessEngine</code> plugin: property bindings split per container kind, packets, load plans, diffing, roundtrip tests, an export map describing each export with a saved index and state, and hooks landing inside CoreUObject itself. One crash fix around map property bindings in Lyra reached the high-interest tier (<code>5d187f0f</code>), and the rest sat below it. The commit messages do not explain what it is for, and we are not going to guess. What is worth noting is the shape: a new serialization and binding layer, with its own module split, arriving alongside changes to the default save path. A new layer under CoreUObject is not a plugin experiment, and it is the sort of thing that is obvious in hindsight three releases later. We will keep counting it.</p>
<p><strong>AutoRTFM is becoming per-module policy rather than one global switch.</strong> EpicClang gained a flag taking a mapping file that assigns default modes to directory paths, and an unmapped file path now raises a diagnostic rather than defaulting to enabled (<code>3e96f550</code>). UnrealBuildTool gained a matching <code>ModuleRules.AutoRTFM</code> property with the legacy flags kept as facades, explicitly changing no build files yet and not yet passing the new compiler flag (<code>0d034f4c</code>). The diagnostic on unmapped paths is the part to watch, because it means Epic now expects every file in the tree to have a declared mode. If you maintain out-of-tree modules, the migration commit that <code>0d034f4c</code> promises is the one that will reach your build files.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>This week we ignored roughly 550 commits: localization automation, binary manifest drops for internal facial and PCG content pipelines, test-only fixes, XML documentation comments, editor UI polish, and the usual run of small cleanups. There were 15 commits tagged <code>[Backout]</code>, which is inside the normal band for this branch and not a story on its own. Of them, the ones worth knowing about are covered above or did reland in-window. Two did not come back this week: USD runtime memory overloads (<code>e43099c2</code>, backed out by <code>84d4e02e</code>) and a Composure compile fix (<code>a929b1df</code>, backed out by <code>fd799bbc</code>).</p>
<p>Build is the top subsystem at 97 commits, followed by editor and core at 61 each and rendering at 59. Of 727 commits, 160 worth reading.</p>
<hr>
<h2 id="for-teams-shipping-on-58">For teams shipping on 5.8</h2>
<p><code>ue5-main</code> saw 127 commits this week with no backouts at all, and it is not pure maintenance. Most of the volume is Control Rig, RigVM, Sequencer, and Movie Render Graph authoring polish, but several fixes are worth a cherry-pick if you ship on 5.8 today.</p>
<ul>
<li><strong>A per-bone blend regression that only affects cooked builds.</strong> <code>InitializePerBoneBlend</code> had been moved inside a <code>WITH_EDITOR</code> block, so per-bone blend setup ran in editor builds and silently did nothing in a cooked game (<code>55537993</code>). This is the most consequential fix on the branch this week, and it will not reproduce in PIE.</li>
<li><strong>A Sequencer duplication bug that overwrote assets on disk.</strong> Duplicating a level sequence left the copy sharing the original’s link items, so saving the duplicate re-exported over the original’s linked anim sequences and stole their back-links. Duplicating an anim sequence copied its back-link untouched, leaving two anims claiming the same binding. The fix strips links from the duplicate by default. Note the intentional behavior change: a duplicated sequence no longer drives the original’s anims, so anyone relying on that sharing has to relink (<code>0c2b200c</code>).</li>
<li><strong>A Vulkan regression that rendered large TMV plates black.</strong> The intermediate target hint gated a reserved-resource flag on a global that only says the RHI can create the virtual resource. Vulkan reports true, but only D3D12 has a sparse output backend registered, so the texture got address space and never a committed tile. Only plates whose mip 0 exceeds the sparse-output threshold requested the flag, which is why HD and 4K kept working (<code>4157725d</code>).</li>
<li><strong>Two Control Rig bugs that produce wrong data rather than wrong UI.</strong> Applying multiple poses collapsed the hierarchy to the origin when every pose weight fell below relevance, because the empty influence list fell through to the branch that widens selection to every bone and wrote identity transforms into all of them, with a stale cache keeping it doing so on later frames (<code>398093ba</code>). Separately, baking animation onto a control rig lost timecode attributes on both bake paths (<code>190216d6</code>).</li>
<li><strong>Animation runtime crashes.</strong> A rig hierarchy could not grow past four megabytes, which an AnimSequence bake writing thousands of curve controls hit (<code>ea26c7c5</code>); an unresolved function table was indexed with a bytecode callable index (<code>b9abdf96</code>); and a pose adapter asserted when a Control Rig recompiled while it was ticking (<code>318396f9</code>).</li>
<li><strong>Three compile breaks that only show up on build farms.</strong> A missing editor guard that broke the non-unity build (<code>c3806dae</code>), a mismatched guard in a dynamic property test (<code>978eacc6</code>), and a Composure compile failure from a merge (<code>4f3ecf85</code>). The first matters if your farm builds non-unity for warning cleanliness.</li>
<li><strong>Deprecations, including one naming 5.9.</strong> Composure deprecates two members in 5.9 while retaining compatibility for now (<code>5f2f217f</code>), and a MovieScene decoration accessor is deprecated in favor of a new one as decorations move to instanced structs (<code>9e6c2d04</code>).</li>
<li><strong>A custom version bump on a maintenance branch.</strong> The Anim Sequence Transform Provider gained per-sequence and per-pose GPU instance bounds with readback and a runtime sequence allocation API, and it adds an entry to the stream object versions (<code>1be8db07</code>). Worth reading carefully before merging if you carry local engine changes.</li>
</ul>
<hr>
<h2 id="closing">Closing</h2>
<p>The two commits that mattered most this week are four files between them, and neither one fails anything. They just made the cook do work it had already done. That is the class of problem worth building a habit around: the cache that is always correct and never warm costs you more over a year than the bug that breaks the build, because the bug gets fixed the day it lands.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless the claim is in the “For teams shipping on 5.8” section, which references <code>ue5-main</code>. Many of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-sep-7-13-2026/og.7fdf223b38.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: The Defaults Moved (August 31 to September 6, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-aug-31-sep-6-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-aug-31-sep-6-2026</guid>
      <pubDate>Mon, 07 Sep 2026 07:33:18 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>862 commits on ue6-main, and the two most consequential are one-line commits nobody announced: Live Coding 2 is on by default, and the input thread is enabled on game input platforms. Zen 5.8.21 changes what /health/ready means, so CI probes pointed at it will start reporting healthy servers as down. The renderer’s packed primitive index is gone along with FScene’s sorted-order invariant, which is the week renderer forks feel. And three unrelated systems hit the same worker-starvation deadlock.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-the-defaults-moved">Unreal Engine Weekly: The Defaults Moved</h1>
<p><strong>Week of August 31 to September 6, 2026</strong> | <strong>862 commits on <code>ue6-main</code>, 110 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>The two most consequential changes this week have one-line commit messages and no release note. Live Coding 2 became the default, and the input thread was enabled on game input platforms. Neither was announced, both change how the editor behaves on your machine, and both have commit bodies shorter than their titles. Around them, build and cache infrastructure kept consolidating: distributed ThinLTO landed end to end, the DDC backend inventory started getting cut down to Zen for 6.0, and Zen turned server lifecycle into a documented contract, including one change that breaks health probes. On the rendering side, the SceneData migration stopped converting call sites and started deleting the old ones, which is the week renderer forks feel.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<h3 id="build-is-the-product-surface-in-ue6">Build is the product surface in UE6</h3>
<p><strong>Pattern:</strong> build has been the top high-interest subsystem for eight weeks running, and this week it accounts for 108 of the 197 commits worth reading. Over the same window it has held a narrow band (98, 87, 61, 85, 121, 117, 98, 108) instead of spiking and decaying like a push.</p>
<p><strong>Interpretation:</strong> the composition has shifted. Cache-server firefighting dominated earlier weeks. This week is feature work with contracts attached: a distributed link step, machine-readable lifecycle results from Zen, a deprecation warning wired into <code>BaseEngine.ini</code>. The work has moved from stabilizing the build to deciding what 6.0’s build story is allowed to depend on.</p>
<p><strong>Who cares:</strong> build and CI engineers, anyone maintaining an internal fork of UBT or BuildGraph.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="unreal-build-accelerator-is-still-one-person">Unreal Build Accelerator is still one person</h3>
<p><strong>Pattern:</strong> 38 of this week’s 39 UBA commits carry a single author. Checking the four prior weeks in the same way: 26 of 28, 43 of 50, 37 of 37, and 20 of 20. Five weeks running.</p>
<p><strong>Interpretation:</strong> we have flagged this for a month now and the concentration has not loosened. The work continues to check out as careful, well-reasoned engineering, and this week includes the single best root-cause writeup in the window. The risk here is structural, not a quality problem. A build accelerator that a growing number of studios depend on has one person holding its correctness surface.</p>
<p><strong>Who cares:</strong> studios adopting UBA as core CI infrastructure.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="the-renderer-decomposition-moved-from-converting-to-deleting">The renderer decomposition moved from converting to deleting</h3>
<p><strong>Pattern:</strong> the SceneData and scene-extension migration has produced a fresh cluster every week since mid-July, and this week the character of it changed: <code>FPrimitiveSceneInfo::PackedIndex</code> was removed outright along with the sorted-order machinery in <code>FScene</code>, rather than another round of call sites being converted.</p>
<p><strong>Interpretation:</strong> deprecation shims protect you from a renamed function. They do not protect you from a removed invariant. The prior seven weeks were survivable for a fork by fixing compile errors. This one requires knowing whether your code assumed <code>FScene</code>’s arrays were kept in sorted order.</p>
<p><strong>Who cares:</strong> graphics programmers maintaining custom renderer code or engine forks.</p>
<p><strong>Signal strength:</strong> Strong.</p>
<h3 id="epic-is-shaping-editor-tool-apis-around-language-model-cost">Epic is shaping editor tool APIs around language-model cost</h3>
<p><strong>Pattern:</strong> one commit names token cost as its engineering motivation, in those words, rewriting property addressing because the old shape “routinely cost tens of thousands of tokens.” Two more are shaped by the same pressure without naming it: a log toolset now caps each returned entry so a single engine line cannot swamp a caller’s budget, and typed toolset returns replaced JSON strings so the model reads results directly instead of writing a throwaway script to parse them. A fourth preserves an explicit Claude Code override in the experimental terminal plugin’s keyboard negotiation.</p>
<p><strong>Interpretation:</strong> the in-editor agent work has moved past plumbing. When a team rewrites a reflection API because of what it costs a model to read, the agent has become a consumer the engine API is designed around. One commit saying so plainly is a weaker signal than a trend, which is why this is worth watching rather than concluding.</p>
<p><strong>Who cares:</strong> tools engineers, anyone building editor automation.</p>
<p><strong>Signal strength:</strong> Moderate.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Live Coding 2 is on by default, and so is the input thread on game input platforms. Both landed with a one-line commit message and no announcement, and both change local editor behavior after your next sync.</li>
<li>Zen 5.8.21 changes what <code>/health/ready</code> means. It now answers 503 between route registration and actual readiness, where the catch-all previously answered 200. If your CI treats that path as a liveness probe, it will start reporting a healthy server as down.</li>
<li><code>FPrimitiveSceneInfo::PackedIndex</code> and <code>FScene</code>’s sorted-order maintenance are gone. Renderer forks that index into scene arrays by packed index break here, and the deprecation shim does not cover the removed sort invariant.</li>
<li>Three unrelated systems hit the same worker-starvation deadlock in the same week: AssetRegistry, the shader code archive, and UBA’s macOS process supervision.</li>
<li>If you ship on 5.8 rather than tracking <code>ue6-main</code>, four fixes are worth cherry-picking this week, including a Mac process-tree bug that leaves an orphaned UnrealBuildTool holding the global mutex until you reboot. See the 5.8 section at the end.</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="live-coding-2-and-the-input-thread-are-on-by-default">Live Coding 2 and the input thread are on by default</h3>
<p><strong>What changed:</strong> Live Coding 2 was enabled by default in a commit whose entire body is one bullet point, touching a single build file (<code>b3de9a0e</code>). Supporting work the same week enabled its target build, on-demand cross-process activation, and a compile-readiness query (<code>c8d36548</code>), and skipped the Live Coding hotkey listener thread in commandlets (<code>0ff624e6</code>). Separately, the input thread was turned on for game input platforms, moving input off the game thread, with <code>-NoInputThread</code> as the escape hatch (<code>ee858a9b</code>). Two same-week fixes back it: stuck controller input on disconnect (<code>14a1830b</code>) and a re-entrancy ensure in the async input consumer drain, itself called out as a regression (<code>ac09c713</code>). A third default flip enabled Verse access-specifier authoring and enforcement (<code>40ff6c4e</code>).</p>
<p><strong>Why this is important:</strong> these are the changes most likely to alter what you experience next week, and the least likely to be noticed. Live Coding 2 becoming the default is an iteration-time change for every programmer on Windows. Moving input off the game thread changes timing characteristics for input-sensitive gameplay code, and the two follow-up fixes show it is finding real edges. Downstream, <code>LiveCodingConsole</code> was removed from the installed-engine build because Live Coding 2 brings its own broker (<code>53fc59a2</code>), so an installed engine you produce now ships different artifacts.</p>
<p><strong>Who should care:</strong> everyone. Programmers for the iteration change, gameplay engineers for input timing, and build engineers for the installed-engine artifact difference.</p>
<p><strong>Urgency:</strong> Act now. Not because anything is broken, but because you should know your defaults changed before you debug a timing difference you did not introduce.</p>
<hr>
<h3 id="distributed-thinlto-lands-end-to-end-and-the-linker-keeps-changing-underneath-it">Distributed ThinLTO lands end to end, and the linker keeps changing underneath it</h3>
<p><strong>What changed:</strong> UnrealBuildTool can now hand ThinLTO backend compilations to UBA as distributed jobs instead of running them inside the linker, behind <code>-ThinLTODistributed</code> and off by default (<code>20efe89f</code>). The UBA-side implementation followed: distributor JSON parsing, path conversion, argument quoting, linker temp files registered as virtual inputs without copying, and the waiting linker’s scheduler weight released during backend execution (<code>17ece9ab</code>). Two follow-ups fixed what a real farm exposed. Virtual basenames were normalizing into invalid paths in the job manifest, and because ThinLTO uses the input path as the module identifier, substituting a physical alias broke index lookup and crashed the backend (<code>6ef3b91c</code>). Backends exiting zero had all output except lines containing “error” silently dropped, so distributed warnings were invisible unless something failed (<code>7d9ffd56</code>).</p>
<p><strong>Why this is important:</strong> the link step is often the last serial bottleneck in a shipping build, and this is the first credible path to distributing it. The pace matters as much as the feature: implementation, manifest contract, path-identity fix, and diagnostics fix inside four days. A farm is setting that schedule, not a design document. Underneath it, Epic dropped five Radlinker binary revisions in four days (<code>bcc3b1cd</code>, <code>b9f958b9</code>, <code>e0244a88</code>, <code>deffbde0</code>, <code>b93cddc7</code>), which means a <code>Commit.gitdeps.xml</code> sync is now potentially a linker change.</p>
<p><strong>Who should care:</strong> build engineers, toolchain owners, anyone whose shipping link step is the long pole.</p>
<p><strong>Urgency:</strong> Track, and act now if you already run LTO builds on a UBA farm. It is off by default, so nothing changes until you opt in.</p>
<hr>
<h3 id="ubas-hardest-bugs-this-week-were-on-macos-and-linux">UBA’s hardest bugs this week were on macOS and Linux</h3>
<p><strong>What changed:</strong> 39 high-interest commits carry the UBA tag, and 19 of them are Visualizer work. The new Visualizer was added as a parallel tree, improved twice, then dropped on top of the original directory with files overwritten and reconciled, and the shim target deleted, all on the same day (<code>aabd63fa</code>, <code>31989735</code>, <code>4ec26bb6</code>, <code>ad444118</code>, <code>88a3f651</code>). The important work is elsewhere. On macOS there is no <code>PR_SET_CHILD_SUBREAPER</code>, so <code>waitid</code> on a grandchild returns ECHILD whether it is alive or dead, and UBA treated that plus zero received messages as an exit (<code>13b4f0ea</code>). Since UBT spawns clang++ which spawns cc1 and ld, every compile and link child is a grandchild. The host was destroying a live process’s shared events and returning its communication slot to the allocator, which handed the same slot straight back out. Two more: fixed-address snapshot collisions are now rejected on Mac rather than mapped over live child mappings, and POSIX fatal-signal reporting now exits without RPC cleanup to break a deadlock where clang’s crash handler waits on an RPC while the host waits for clang to exit (<code>cb4a3a1c</code>). Network-layer work covered connection-loss handling during cancellation (<code>0369c997</code>), an agent-listen race with a 15 to 45 second reaction window (<code>84dfad3a</code>), and asynchronous handshakes so a stalled one stops blocking later connections (<code>2bbbd6b2</code>).</p>
<p><strong>Why this is important:</strong> the grandchild fix is the most useful commit in the window to read in full. The failure it describes is invisible in exactly the way distributed build failures usually are: a live process’s slot gets recycled, and the corruption surfaces somewhere unrelated. If you run UBA agents on macOS or Linux and have been writing off intermittent farm failures as flakiness, this is a plausible mechanism. The fixed-address collision commit hedges carefully: the unsafe mapping is a possible initial corruption source, not a proven cause of that particular farm failure. Read it with the hedge intact.</p>
<p><strong>Who should care:</strong> build engineers and CI operators running distributed compile farms, especially cross-platform ones.</p>
<p><strong>Urgency:</strong> Act now if your agents run on macOS or Linux.</p>
<hr>
<h3 id="zen-5821-turns-server-lifecycle-into-a-contract-and-changes-what-a-health-probe-means">Zen 5.8.21 turns server lifecycle into a contract, and changes what a health probe means</h3>
<p><strong>What changed:</strong> a one-line <code>Commit.gitdeps.xml</code> bump carries the longest release note in the window (<code>84ae3c43</code>). <code>zen up</code>, <code>zen status</code>, and <code>zen service status</code> accept <code>--output-path</code> and write a structured result carrying effective port, pid, session id, readiness, and data directory, so callers stop scraping console text. Exit codes now identify causes, and the full contract is documented. The behavior changes matter more than the features. <code>/health/ready</code> answers 200 only once the server can serve requests, 503 before that and again once shutdown begins, where the catch-all previously answered 200 as soon as the health routes existed. And an unrecognized path under <code>/health/</code> now answers 404 instead of 200. Lifecycle semantics tightened too: <code>zen up</code> reuses a server that is already starting rather than racing it, and <code>zen down</code> refuses <code>--pid 0</code> and <code>-p 0</code>, which previously let an unset script variable expanding to zero stop an arbitrary server. There is also a real security fix: the <code>oidc-exe-path</code> request field is now ignored, closing a local privilege-escalation hole where a client could name an executable for the server to launch.</p>
<p><strong>Why this is important:</strong> this release closes the gap between “my script ran zen” and “my script knows what zen did,” and it closes it by making previously permissive responses strict. The health-probe change is the one that bites without warning, because a probe pointed at <code>/health/ready</code> used to succeed for the wrong reason. Probe <code>/health</code> for liveness. The <code>--pid 0</code> refusal is worth checking your own scripts against.</p>
<p><strong>Who should care:</strong> build and CI engineers, DevOps, anyone scripting zenserver lifecycle or cook infrastructure.</p>
<p><strong>Urgency:</strong> Act now if you script zenserver in CI.</p>
<hr>
<h3 id="the-ddc-backend-inventory-is-being-cut-down-for-60">The DDC backend inventory is being cut down for 6.0</h3>
<p><strong>What changed:</strong> eight commits from one engineer, all sharing a synced changelist, systematically retire DDC backends. S3 DDC was deleted outright, with the commit stating that for 6.0 its users can move to Cloud DDC or build on the pak file cache or its forthcoming replacement (<code>e5a7f7b4</code>). The FileSystem cache store now emits a deprecation warning when used, wired into <code>BaseEngine.ini</code> and ShaderCompileWorker’s <code>DefaultEngine.ini</code> (<code>761cd96c</code>). Deprecated graphs and stores were removed from configs (<code>8fb6585c</code>), 5.8-deprecated code removed (<code>2962e510</code>), the DDC IO thread pool is created lazily only when a store needs it (<code>7d6e0e25</code>), and Jupiter access-token races were fixed (<code>31611935</code>).</p>
<p><strong>Why this is important:</strong> the FileSystem cache store is the shared-DDC-on-a-network-share setup plenty of studios still run, and a deprecation warning is the first shot rather than the last. Read alongside the S3 removal and the Zen investment, the practical direction for 6.0 is Zen or Cloud DDC. Nothing this week removes the FileSystem cache store. It warns.</p>
<p><strong>Who should care:</strong> build engineers, studio IT, anyone running a shared DDC.</p>
<p><strong>Urgency:</strong> Track. Start the conversation about what replaces a network-share DDC before it becomes a 6.0 upgrade blocker.</p>
<hr>
<h3 id="the-packed-primitive-index-is-gone-from-the-renderer">The packed primitive index is gone from the renderer</h3>
<p><strong>What changed:</strong> <code>FPrimitiveSceneInfo::PackedIndex</code> was removed, <code>GetIndex()</code> deprecated and redirected to the persistent primitive id, and the machinery that kept <code>FScene</code> in sorted order removed along with the extra copies of added and removed primitives and <code>TScenePrimitiveArray</code> (<code>c281fdce</code>). A companion commit folded <code>FPrimitiveSceneInfo::AddToScene</code> into <code>FScene::ProcessPendingAddToScene</code> (<code>1c348679</code>). Conversions feeding it landed the same week for Nanite custom-depth visibility (<code>c799f3cd</code>), mobile CSM subject primitives (<code>86f9b991</code>), and ray tracing group ids moving into the ray tracing scene extension (<code>8861cfe5</code>). Two real races were fixed on the way through: the uniform-buffer update path was decoupled from GPUScene dirty state, fixing a race where light map allocations raced the update (<code>85df40a2</code>), and dirty-mask marking now validates its access context the same way per-element writes do (<code>b21d5926</code>).</p>
<p><strong>Why this is important:</strong> code that assumed <code>FScene</code>’s arrays were maintained in sorted order will compile and then be wrong. The previous seven weeks of this migration announced themselves as compile errors. A removed invariant does not. A batch of deprecated includes was also removed from RenderCore and Renderer headers the same week (<code>5e2a1be7</code>), so expect merge conflicts on top of the semantic change.</p>
<p><strong>Who should care:</strong> graphics programmers, engine-fork maintainers, anyone with plugins that reach into scene arrays.</p>
<p><strong>Urgency:</strong> Act now if you maintain a renderer fork.</p>
<hr>
<h3 id="nanite-hardware-ray-tracing-stops-guessing-at-memory-and-the-light-paths-are-being-unified">Nanite hardware ray tracing stops guessing at memory, and the light paths are being unified</h3>
<p><strong>What changed:</strong> two Nanite fixes undo the same class of mistake, using a proxy for a number instead of the number. The CLAS address reservation was a worst-case product of BLAS count and resident clusters, which caused out-of-memory failures on large assemblies; it is now a per-part capacity vector fed by readback of the cluster count hierarchy traversal actually emitted, with the base mesh BLAS still using the old value so it is never skipped (<code>f837c054</code>). The BLAS cache-hit test was per-BLAS when only the base BLAS seeds traversal, so a part could miss the cache, get a null address, and have no path to rebuild; a new pass now folds per-slot cache-miss results into a per-geometry rebuild flag (<code>eea475df</code>). Separately, <code>FRTLightingData</code> was removed in favor of <code>FForwardLightData</code>, unifying ray-traced hit lighting with the forward light path (<code>eaff98e0</code>), and LumenRef’s direct lighting now routes through MegaLights material and shading functions, fixing wrong hair appearance and bad subsurface demodulation (<code>f826d845</code>).</p>
<p><strong>Why this is important:</strong> the Nanite memory work makes assemblies with hardware ray tracing materially more usable than a week ago. The commit says the change significantly reduces memory overhead on large assemblies that previously caused out-of-memory issues, and it does not quote a figure, so neither will we. The light-path unification is the theme most likely to produce visual diffs in a fork: lights are no longer skipped from ray tracing when “affect ray traced reflections” is disabled, because they may still be used by GI or ray-traced translucency. That behavior change is deliberate.</p>
<p><strong>Who should care:</strong> graphics programmers shipping hardware ray tracing, lighting and look-dev teams comparing offline reference against realtime.</p>
<p><strong>Urgency:</strong> Track, and benchmark before integrating if you ship Nanite assemblies with hardware ray tracing.</p>
<hr>
<h3 id="worker-starvation-was-the-bug-of-the-week-in-three-unrelated-systems">Worker starvation was the bug of the week, in three unrelated systems</h3>
<p><strong>What changed:</strong> three systems independently hit the same failure. A thread that is itself a task worker blocks waiting on another task, and with enough concurrent waiters no worker remains to run the thing being waited on. AssetRegistry’s mount register and unload paths were converted to continuations instead of waits, with per-provider state replacing a registration critical section and a new join built on task retraction so the synchronous wrappers cannot deadlock when called from a task thread (<code>8070e00e</code>, <code>26cae9d1</code>). <code>FShaderCodeArchive::WaitForPreload</code> was waiting on the task completion event alone, and now waits on the reads and retracts and executes when done, tested at 16 preloads against 6 workers (<code>a86de5f6</code>). UBA’s macOS process supervision, covered above, is the third (<code>13b4f0ea</code>). Alongside them, a set of use-after-free fixes with real root causes: a Slate render-thread use-after-free of cached clip states, with release deferred to the render thread (<code>e7640b2a</code>), a dangling reference when initializing maps from a tuple with converting element types, which touches core container headers and so reaches everything (<code>c76e7d6b</code>), and a FastGeo async render-state job outliving its container where the previous guard was itself the use-after-free, because testing the object read it (<code>9623ab20</code>).</p>
<p><strong>Why this is important:</strong> the pattern is more useful than any individual fix. If your codebase calls a blocking wait from anything that could be running on a task thread, that is this week’s homework, and three separate Epic teams just paid for the lesson. The FastGeo case is the one to internalize: a validity check that dereferences the thing it is validating is not a guard.</p>
<p><strong>Who should care:</strong> everyone touching runtime, editor, or tools code with task-graph usage.</p>
<p><strong>Urgency:</strong> Act now on the pattern, track the individual fixes.</p>
<hr>
<h3 id="cook-determinism-the-bytes-matched-the-metadata-did-not">Cook determinism: the bytes matched, the metadata did not</h3>
<p><strong>What changed:</strong> a numbered seven-part series is landing fixes for non-deterministic cook metadata, meaning the recorded build dependencies rather than the cooked output. Three of the seven appear in this window. An anim sequence sync-marker refresh is now skipped under the cook commandlet, because the package access tracker was attributing blend-space sample reads to whichever asset happened to be post-loading (<code>0b4e3fb9</code>). Shader-symbol config queries were wrapped in an ignore scope (<code>a806e358</code>). Static mesh ray tracing config access was replaced with an explicit per-platform cook dependency function (<code>ea91bf83</code>).</p>
<p><strong>Why this is important:</strong> the commit bodies quantify it from a real oplog diff. 46,377 anim sequence packages, 28 percent of the 168,009 affected, had byte-identical cooked output but different recorded dependencies. Shader symbol settings accounted for another 8,820, and static mesh ray tracing for 13,334, where most meshes never recorded the dependency at all and so would not have been recooked when the setting changed. Recorded dependencies are what decides whether an incremental cook or a shared DDC hit is valid. If you have watched an incremental cook rebuild a third of your content for no visible reason, this is a mechanism that explains it. The static mesh fix also names a specific multi-platform failure, where Linux ray tracing settings bled into Windows client artifacts on shared cook workers because editor config reads are recorded platform-agnostically.</p>
<p><strong>Who should care:</strong> build engineers, anyone running incremental cooks or a shared DDC across platforms.</p>
<p><strong>Urgency:</strong> Track. Four of the seven fixes are not in this window, so the series is not finished.</p>
<hr>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>The Slate render-transform fast path is written, fixed twice, and still switched off.</strong> The optimization landed with its cvar enabled after testing across several titles (<code>27cc168f</code>), an always-clip ancestor edge case followed (<code>9e12c736</code>), and then the cvar was turned back off, described as temporary (<code>87b84a83</code>). Two fixes landed after that: a guard against a non-invertible accumulated transform producing NaN (<code>7ab7a263</code>) and the render-thread use-after-free noted above (<code>e7640b2a</code>). Nothing in this window turns it back on. If it re-enables next week, it is a real Slate invalidation win for heavy HUD and menu hierarchies.</p>
<p><strong>Verse’s move to UnrealHeaderTool did not ship, whatever a changelog implies.</strong> The conversion from Verse’s native interop generator to UHT landed on September 1 (<code>70a84c52</code>) and was backed out on September 4 (<code>adef434b</code>), later by changelist number, with no reland in this window. Worth a line only because the net state is easy to get wrong: the preparation work landed and stayed (<code>3d7c9488</code>, <code>72262635</code>, <code>9bfa3fa9</code>), so the surrounding commits read like a shipped migration. It is not one yet.</p>
<p><strong>An experimental UV parameterization and atlas system is getting optimization passes nobody is discussing.</strong> The JOULE plugin tree carries atlas packing, genus cuts, seam opening, quadric collapse reduction, and a voxel lattice core. Four commits this week, all from one author under one ticket, cover a seam-aware field transfer (<code>c80b5540</code>), quadric collapse optimization with cached edge partners (<code>eb3f8c4a</code>), a caching pass for mesh traversal patterns (<code>2e835f31</code>), and disabling static analysis on the build target (<code>7495b514</code>). That last one is routine for a fast-moving experimental target and should not be read as a quality signal. The reason to watch: the plugin is about four weeks old and already receiving cache-locality work, which is not what a shelved prototype gets. A first-party automatic UV unwrap and atlas generator in the engine would displace a category of DCC and third-party tooling.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>We ignored 427 low-interest commits and 36 filtered as noise. The bulk was routine: localization automation against <code>Commit.gitdeps.xml</code>, template fixes, content drops with no engineering signal, cosmetic robomerge commits, and a long tail of narrow editor crash fixes each affecting one repro. We also set aside 26 commits that touch only <code>Commit.gitdeps.xml</code>, where the change lives in the binary manifest rather than in anything readable. Two were opaque enough to name rather than summarize: a commit enabling divergence emission and one turning off a debug visualization, neither attributable to a named system.</p>
<hr>
<h2 id="for-teams-shipping-on-58">For teams shipping on 5.8</h2>
<p><code>ue5-main</code> saw 110 commits this week, and it was not pure maintenance. Several fixes are worth a cherry-pick if you ship on 5.8 today, and the rest is editor-tooling crash cleanup plus localization and template churn.</p>
<ul>
<li><strong>Two runtime regressions worth cherry-picking.</strong> A per-frame memory leak in Vulkan parallel render passes under dynamic rendering, merged from a community pull request (<code>9f7521b8</code>). And a Nanite LOD scaling regression where HZB sizing fed the temporal upscale factor into LOD selection, and split-screen used the whole view family width instead of the per-view width (<code>37bf9329</code>).</li>
<li><strong>A World Partition data-drift fix.</strong> Actor descriptors now serialize root component absolute transform flags, so saving a parent no longer drifts children using absolute location, rotation, or scale (<code>53fdc035</code>).</li>
<li><strong>Two packaging and build-farm blockers.</strong> iPhonePackager now signs frameworks embedded in the app bundle before the app itself, each with its own bundle identifier and resources, with a flag to fall back (<code>739e7143</code>). On Mac, terminating a process tree walked only one level of children, so a canceled editor package left an orphaned UnrealBuildTool holding the global mutex and every later package failed until reboot (<code>6b110837</code>).</li>
<li><strong>A crash-report sweep.</strong> Eight crash fixes carry adjacent ticket numbers, covering the accumulation depth-of-field viewport preview (<code>0f00e5be</code>), Control Rig keying after teardown (<code>2747b9ce</code>), a RigVM pin lookup (<code>527d596f</code>), two PCG crashes (<code>7a256025</code>, <code>902082dd</code>), skeletal mesh reduction and conversion (<code>d1ab8e1c</code>, <code>4cf4f905</code>), and SVG import (<code>13e5bb9d</code>). Separately, TextureGraph export no longer crashes under <code>-nullrhi</code>, which is how build machines run it (<code>db794640</code>).</li>
<li><strong>One fix took three tries, one change did not ship.</strong> Ordered by changelist trailer: the Sequencer curve editor delete crash landed (<code>8716261c</code>), was backed out (<code>c76fe104</code>), then relanded a day later with a note that the fix has to stay header-defined or the animation tests fail to link (<code>bcb68395</code>). The FK Control Rig quadratic-initialization fix relanded after two prior backouts (<code>884772cd</code>). Control Rig variable overrides landed and were backed out the same day (<code>49d9136f</code>, then <code>dd3e0b00</code>), with no reland in the window, so that one did not ship.</li>
<li><strong>Epic is still landing deprecations in 5.x.</strong> Using the FileSystem DDC cache store now emits a deprecation warning here too (<code>80d04074</code>), which matters if your shared DDC sits on a network share.</li>
</ul>
<hr>
<h2 id="closing">Closing</h2>
<p>The headline changes this week were the ones nobody wrote a release note for. Two defaults flipped, a health probe changed meaning, and a renderer invariant disappeared, all in commits short enough to scroll past. Read the commits. The changelog will not have any of it.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless the claim is in the “For teams shipping on 5.8” section, which references <code>ue5-main</code>. Many of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-aug-31-sep-6-2026/og.b06e4e9bad.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: Same Fix, Different Branch (August 24 to 30, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-aug-24-30-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-aug-24-30-2026</guid>
      <pubDate>Mon, 31 Aug 2026 08:16:42 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>918 commits on ue6-main, and the FScene-to-Scene-Extensions decomposition reached ray tracing specifically for a seventh straight week, with UE_INTERNAL dropped from a key header, a signal the API is stabilizing. One engineer carried Unreal Build Accelerator’s correctness work for a fourth straight week, and a second engineer shipped 23+ commits of Unreal Toolbox investment nobody was tracking. The AI editor assistant’s background tool-call mechanism relanded after repeated backouts. And a Sequencer camera-shake fix shipped successfully on ue5-main after one backout, but stayed reverted on ue6-main with no reland in this week’s window: same patch, opposite outcome by branch.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-same-fix-different-branch">Unreal Engine Weekly: Same Fix, Different Branch</h1>
<p><strong>Week of August 24 - 30, 2026</strong> | <strong>918 commits on <code>ue6-main</code>, 174 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>This was a rendering-and-tooling week more than a rendering-and-crashes week: the FScene-to-Scene-Extensions decomposition reached ray tracing specifically for a second straight arc, LumenRef kept building out its direct-lighting path, and a chunk of legacy rendering code (SSGI among it) got deleted outright. Away from rendering, a single engineer carried Unreal Build Accelerator’s correctness work for a fourth consecutive week, and a second engineer shipped a large, previously untracked campaign on Unreal Toolbox (the internal developer-tool distribution app). The AI editor assistant’s background tool-call mechanism relanded after repeated backouts. Nothing here is an emergency for most teams, but the ray-tracing API surface is visibly stabilizing and worth reading if you maintain a renderer fork.</p>
<h2 id="development-trends">Development Trends</h2>
<h3 id="renderer-decomposition-reaches-ray-tracing-directly-seventh-consecutive-week">Renderer Decomposition Reaches Ray Tracing Directly, Seventh Consecutive Week</h3>
<p><strong>Pattern:</strong> FScene’s split into modular Scene Extensions has produced a fresh cluster every week since reopening July 19. This week the target is explicitly ray tracing: static mesh cache data and machinery moved out of <code>FScene</code>/<code>FPrimitiveSceneInfo</code> into a new <code>FStaticMeshSceneExtension</code> (<code>4a8f93d6</code>), <code>NativeInstances</code> was renamed to <code>TLASInstances</code> (<code>967e1001</code>), <code>FGeometryManagerHandle</code> was refactored “to simplify upcoming changes” (<code>41fadeba</code>), Nanite-tagged ray tracing instance processing moved into <code>Nanite::FRayTracingManager</code> (<code>a34192d5</code>), and <code>PrimitiveRayTracingFlags</code>/<code>PrimitiveRayTracingDatas</code> plus path-tracing invalidation moved into <code>FRayTracingSceneExtension</code> (<code>26233121</code>). <code>UE_INTERNAL</code> was dropped from <code>RayTracingInstanceBufferUtil.h</code> (<code>767182ba</code>), which reads as the API settling into something other teams are meant to build against. The same arc produced a real fix: skeletal meshes were dropping out of the TLAS entirely when <code>r.RayTracing.DynamicGeometry.MaxUpdatePrimitivesPerFrame</code> was enabled (<code>8a51a944</code>).</p>
<p><strong>Interpretation:</strong> seven straight weeks of continuous evidence, with this week narrowing from general scene-storage refactoring to ray tracing specifically. The <code>UE_INTERNAL</code> removal is the clearest signal yet that a chunk of this surface is stabilizing rather than still churning.</p>
<p><strong>Who cares:</strong> renderer and engine-core teams, especially anyone maintaining hardware ray tracing or Lumen HWRT in a fork.</p>
<p><strong>Signal strength:</strong> Strong. Seven weeks of continuous, substantive evidence since reopening July 19.</p>
<h3 id="build-infrastructure-uba-correctness-work-continues-one-engineer-still-carrying-it">Build Infrastructure: UBA Correctness Work Continues, One Engineer Still Carrying It</h3>
<p><strong>Pattern:</strong> Build stayed elevated at 98 commits (down from 121 and 117 the prior two weeks, still well above the pre-July baseline). Unlike the last three weeks’ architecture passes (multi-tenancy, then a three-front rebuild), this week is a return to smaller, numerous correctness fixes, nearly all from the same engineer: a known-inputs streaming feature for the scheduler’s YAML config (<code>f0365f92</code>, <code>e605db48</code>), a compression double-encode fix for RAD OBJ files (<code>29c9b0ef</code>), a COFF-writer padding fix specifically so RAD Linker stops choking on the output (<code>ab784a2a</code>), removable-cache memory-leak diagnostics (<code>31eece04</code>), invisible folders and per-process overlay directories (<code>422e38eb</code>), a POSIX <code>stat</code> fix for leading parent-relative paths (<code>643b90af</code>), and exact cache-entry offset-vector reallocation during maintenance (<code>541ed2ff</code>).</p>
<p><strong>Interpretation:</strong> the same single-engineer concentration flagged the last two weeks continues into a fourth week. The work itself keeps checking out as sound, narrowly-scoped correctness fixes, but the bus-factor risk is now a month-long pattern, not a one-week anomaly.</p>
<p><strong>Who cares:</strong> build and tools teams running distributed builds through UBA.</p>
<p><strong>Signal strength:</strong> Strong. Fourth consecutive week of concentrated single-author UBA activity.</p>
<h3 id="ai-editor-assistant-background-tool-call-mechanism-relands-after-repeated-backouts">AI Editor Assistant: Background Tool-Call Mechanism Relands After Repeated Backouts</h3>
<p><strong>Pattern:</strong> The AI assistant’s mechanism for backgrounding a long-running tool call (running it in a bounded window, then posting the result back to the conversation once it finishes) shipped this week at <code>da58b851ec</code>, citing a prior backout, and again at <code>a515a8f521</code>, citing two prior backouts (tracked under <code>UE-390053</code>). A related crash fix landed cleanly: the test spec backing this feature held its tool promise as a plain member that a background-static destructor tried to fulfill, crashing any process that loaded the plugin without running the test (<code>fc4f1566b5</code>). Separately, <code>AIAssistant</code> and <code>MCPClientToolset</code> were split into standalone <code>EditorAndProgram</code>/editor module pairs (<code>4bff8240d1</code>), and MetaSound’s agent-tooling integration was promoted out of an internal FortTech plugin into a standalone experimental engine toolset registered to load by default (<code>ec883d85aa</code>).</p>
<p><strong>Interpretation:</strong> this is the same Agent Tooling arc that has read as cooling for two weeks, and it isn’t silent. A feature that needed three attempts to land, plus two module splits that widen its footprint into audio tooling, reads as continued, if lower-volume, investment rather than a wind-down.</p>
<p><strong>Who cares:</strong> editor and tools teams following Epic’s in-editor AI agent work.</p>
<p><strong>Signal strength:</strong> Moderate. One concrete cluster after two quieter weeks; worth another week of data before calling it reactivated.</p>
<h3 id="a-new-previously-untracked-story-unreal-toolbox-gets-a-major-investment-week">A New, Previously Untracked Story: Unreal Toolbox Gets a Major Investment Week</h3>
<p><strong>Pattern:</strong> One engineer authored at least 23 commits this week entirely inside Unreal Toolbox, the managed developer-tool distribution and self-update application. The work spans a full self-update UX overhaul with staged versions, deferred updates, and shutdown-race handling, validated with “33 passing tests and clean Development and Analyze builds” by the commit’s own description (<code>b54af79f</code>), structured telemetry with bounded delivery retries and privacy safeguards (<code>53777fd5</code>), deduplicated Horde authentication so marketplace and settings auth share one interactive login instead of racing (<code>1f2a2563</code>), persisted launch profiles with Check Now progress reporting (<code>172a2c8a</code>), and a full branding pass replacing the Unreal Engine logo with a dedicated Toolbox icon across tray, window, and macOS packaging (<code>2e43a4f3</code>).</p>
<p><strong>Interpretation:</strong> this is not part of any previously tracked pattern, and the volume (23+ commits, one engineer, one week) is large enough to flag on its own rather than folding it into general editor and tooling activity. Whether it recurs next week decides if this becomes a sustained story.</p>
<p><strong>Who cares:</strong> anyone whose studio uses Unreal Toolbox for managed tool distribution or UGS integration.</p>
<p><strong>Signal strength:</strong> Single week. Worth watching for a second week of data.</p>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>The FScene-to-Scene-Extensions decomposition reached ray tracing specifically this week (static mesh cache data, <code>NativeInstances</code> renamed to <code>TLASInstances</code>, <code>Nanite::FRayTracingManager</code>), with <code>UE_INTERNAL</code> dropped from a key header, a signal the API is stabilizing. A real bug in the same arc: skeletal meshes could drop out of the ray-tracing TLAS entirely under a common cvar.</li>
<li>Unreal Build Accelerator’s correctness work continues into a fourth straight week, still carried almost entirely by one engineer: a known-inputs streaming feature, a RAD Linker compatibility fix, and several cache and diagnostics fixes.</li>
<li>A Sequencer camera-shake-preview teardown fix shipped successfully on <code>ue5-main</code> this week after one backout, but remains reverted on <code>ue6-main</code>, where it was submitted twice and backed out with no reland in this week’s window. Same patch, opposite outcome by branch.</li>
</ul>
<h2 id="highlights">Highlights</h2>
<h3 id="ray-tracing-gets-its-own-scene-extensions-pass-and-a-real-tlas-bug-gets-fixed">Ray tracing gets its own Scene Extensions pass, and a real TLAS bug gets fixed</h3>
<p><strong>What changed:</strong> Static mesh cache data and machinery moved out of <code>FScene</code>/<code>FPrimitiveSceneInfo</code> into a new <code>FStaticMeshSceneExtension</code> (<code>4a8f93d6</code>). Elsewhere in the same arc: <code>NativeInstances</code> renamed to <code>TLASInstances</code> (<code>967e1001</code>), <code>FGeometryManagerHandle</code> refactored ahead of upcoming changes (<code>41fadeba</code>), Nanite-tagged ray tracing instance processing consolidated into <code>Nanite::FRayTracingManager</code> (<code>a34192d5</code>), <code>PrimitiveRayTracingFlags</code>/<code>PrimitiveRayTracingDatas</code> and path-tracing invalidation moved into <code>FRayTracingSceneExtension</code> (<code>26233121</code>), and <code>UE_INTERNAL</code> was dropped from <code>RayTracingInstanceBufferUtil.h</code> (<code>767182ba</code>). The real bug fix riding along: skeletal meshes were dropping out of the ray tracing TLAS entirely when <code>r.RayTracing.DynamicGeometry.MaxUpdatePrimitivesPerFrame</code> was enabled (<code>8a51a944</code>).</p>
<p><strong>Why this is important:</strong> this is the seventh straight week of the FScene decomposition producing a fresh cluster, and it has now narrowed specifically to ray tracing storage. The <code>UE_INTERNAL</code> removal reads as an API stabilizing rather than still under construction. The TLAS dropout bug is a real correctness issue for anyone using that cvar with skeletal meshes and hardware ray tracing.</p>
<p><strong>Who should care:</strong> renderer and engine-core teams, anyone maintaining a hardware-ray-tracing or Lumen HWRT fork.</p>
<p><strong>Urgency:</strong> Act now if you run <code>r.RayTracing.DynamicGeometry.MaxUpdatePrimitivesPerFrame</code> with skeletal meshes and hardware ray tracing. Track the broader decomposition.</p>
<h3 id="lumenref-keeps-building-toward-a-usable-reference-path">LumenRef keeps building toward a usable reference path</h3>
<p><strong>What changed:</strong> LumenRef (Epic’s in-progress hardware-ray-traced Lumen reference path) picked up Light Function, IES profile, and light-end-bias support (<code>29253461</code>), an initial direct-lighting pass (<code>87a30b90</code>), replaced light-miss shaders with the LF atlas (<code>8c748501</code>), added error-diffusion sampling for screen-space sampling (<code>1138bb46</code>), and consolidated shared code across LumenRef, MegaLights, and hardware-ray-traced lighting generally (<code>08eeb37c</code>). Separately, Lumen’s radiance cache got a hardware-ray-tracing performance win by decoupling two passes that no longer need to run in sequence, saving 0.06ms on console at a 100-probe trace budget (<code>cc537df5</code>).</p>
<p><strong>Why this is important:</strong> this continues the multi-week hardware ray tracing reopening from the prior two weeks, now in its third consecutive week. LumenRef is explicitly still a prototype, not usable yet, but the pace of feature additions (lighting, sampling quality, shared code with production paths) says Epic is actively building it out rather than letting it sit.</p>
<p><strong>Who should care:</strong> graphics and rendering teams tracking hardware-ray-traced Lumen.</p>
<p><strong>Urgency:</strong> Track.</p>
<h3 id="legacy-rendering-code-gets-deleted-not-just-deprecated">Legacy rendering code gets deleted, not just deprecated</h3>
<p><strong>What changed:</strong> Screen Space Global Illumination (SSGI) was removed entirely (<code>5ca9c633</code>). Hair strands lost their dual-scattering roughness override (<code>d38baa8d</code>). Deprecated code was purged from the RHI, focused on Metal (<code>9cd86845</code>). Shader-system deprecated code was removed “past required window” (<code>52861013</code>).</p>
<p><strong>Why this is important:</strong> these are deletions, not deprecation warnings. Anything still calling the removed SSGI path, the hair override, or the purged RHI and shader entry points breaks on sync, not on a future release.</p>
<p><strong>Who should care:</strong> teams maintaining renderer forks, especially anyone still using SSGI instead of Lumen screen-space GI, or the removed hair dual-scattering path.</p>
<p><strong>Urgency:</strong> Act now if you depend on any of the four removed paths. Otherwise ignore.</p>
<h3 id="uba-correctness-work-continues-into-a-fourth-week-still-one-engineer">UBA correctness work continues into a fourth week, still one engineer</h3>
<p><strong>What changed:</strong> Unreal Build Accelerator picked up a known-inputs streaming feature for the scheduler’s YAML config, letting common inputs stream to clients ahead of process payloads (<code>f0365f92</code>, <code>e605db48</code>), a fix for RAD OBJ files getting double-compressed on write (<code>29c9b0ef</code>), a COFF-writer padding fix specifically so RAD Linker stops rejecting the output (<code>ab784a2a</code>), removable-cache memory-leak diagnostics (<code>31eece04</code>), invisible folders and per-process overlay directories (<code>422e38eb</code>), a POSIX <code>stat</code> fix for leading parent-relative paths (<code>643b90af</code>), and exact cache-entry offset-vector reallocation during maintenance, replacing an approximation that over- or under-allocated (<code>541ed2ff</code>). All of the above are from the same engineer.</p>
<p><strong>Why this is important:</strong> this is the fourth straight week UBA’s correctness surface has rested substantially on one person. The work continues to check out as sound, but a month of concentration in one system, one author, is worth flagging as a standing risk independent of the quality of any individual fix.</p>
<p><strong>Who should care:</strong> build and tools teams running distributed builds through UBA, especially anyone tracking RAD Linker and RAD OBJ readiness.</p>
<p><strong>Urgency:</strong> Track.</p>
<h3 id="ai-assistants-background-tool-call-mechanism-relands-after-repeated-backouts-plus-two-module-splits">AI assistant’s background tool-call mechanism relands after repeated backouts, plus two module splits</h3>
<p><strong>What changed:</strong> The AI editor assistant’s mechanism for backgrounding a long-running tool call, letting it keep running past a bounded window while the agent carries on, then posting the result back once it finishes, landed this week at <code>da58b851ec</code> (citing one prior backout) and again at <code>a515a8f521</code> (citing two prior backouts), under <code>UE-390053</code>. A related crash fix landed cleanly: the test spec backing this feature held its tool promise as a plain member, and a file-scope static destructor tried to fulfill an unset promise in any process that loaded the plugin without running the test, crashing on exit (<code>fc4f1566b5</code>). Separately, <code>AIAssistant</code> and <code>MCPClientToolset</code> were split into standalone editor and runtime module pairs (<code>4bff8240d1</code>), and MetaSound’s agent-tooling integration moved out of an internal FortTech plugin into a standalone experimental engine toolset that loads by default (<code>ec883d85aa</code>).</p>
<p><strong>Why this is important:</strong> a feature needing three submission attempts before landing, combined with two module splits widening the toolset’s footprint into audio tooling, says this area is still actively worked, not winding down, even though weekly volume has been lower the last two weeks.</p>
<p><strong>Who should care:</strong> editor and tools teams tracking Epic’s in-editor AI agent work.</p>
<p><strong>Urgency:</strong> Track.</p>
<h3 id="a-crash-guard-fix-ships-to-both-branches-and-a-sequencer-fix-diverges-between-them">A crash-guard fix ships to both branches, and a Sequencer fix diverges between them</h3>
<p><strong>What changed:</strong> An nDisplay crash fix for unresolvable config data, cluster nodes, or viewports (deleting a cluster node could leave a null entry that consumers assumed was always valid) landed, was backed out, and relanded on <code>ue6-main</code> (<code>8f2487ff</code> then backed out <code>bb69dd51</code> then relanded <code>55f805e8</code>), tagged as a temporary fix for 5.8.3. The same fix went through an identical land-backout-reland cycle on <code>ue5-main</code> this week (see the 5.8 section below). Separately, a fix releasing Sequencer camera-shake previews on world teardown (previously nothing dropped the FGCObject references, leaving the outgoing world and its package reachable after a map change) was submitted twice and backed out on <code>ue6-main</code>, with no reland anywhere in this week’s commit data (<code>3ebdd277</code> then <code>ae892dbb6</code> then backed out at <code>ed25dfc9</code>). The same fix landed successfully on <code>ue5-main</code> this week after one backout (see the 5.8 section below).</p>
<p><strong>Why this is important:</strong> the nDisplay fix is confirmed live on both branches as of this week. The camera-shake fix is not live on <code>ue6-main</code> despite two submission attempts, but is live on <code>ue5-main</code>. Anyone checking whether this was fixed needs to check the actual branch, not assume parity between them.</p>
<p><strong>Who should care:</strong> nDisplay and virtual-production teams for the crash fix; Sequencer and animation teams tracking the camera-shake preview leak specifically on <code>ue6-main</code>.</p>
<p><strong>Urgency:</strong> Track. The camera-shake leak is still present on <code>ue6-main</code> as of August 30.</p>
<h2 id="worth-tracking">Worth Tracking</h2>
<ul>
<li><strong>Persistent Storage Manager</strong>, a new quota and budget subsystem tracked under <code>UE-392423</code>, picked up path-conversion fixes for platforms requiring <code>ConvertToAbsolutePathForExternalAppForWrite</code> and CSV-metadata budget stats this week (<code>911d3878</code>, <code>882f652e</code>, <code>519d547f</code>, <code>45fc7d40</code>). Still stabilizing; watch for a broader rollout signal.</li>
<li><strong>A dynamic-logging refactor</strong> continued under <code>UE-392122</code>: tracing support for dynamic logging, a log-category lookup-by-name API, and an atomic-counter-assigned trace log category and log point (<code>5bb0b384</code>, <code>dc6ee08b</code>). Feeds into the multi-week <code>FMsg::Logf</code> and <code>LogV</code> deprecation push from prior weeks.</li>
<li><strong>AutoRTFM and Verse’s <code>VArrayBase</code> hierarchy</strong> went through a same-day (August 28) triple backout and reland loop from one engineer, annotating the type as fully <code>AUTORTFM_DISABLE</code>. Final state as of week’s end: landed (<code>29d26a0b49</code>), after two same-day backouts.</li>
<li><strong>Ness, the still-unexplained serialization runtime</strong>, picked up 26 commits this week, its highest weekly count yet. New this week: a standalone <code>NessRoundtrip</code> continuous-integration test (<code>f3addab8</code>), a “SPOT” table concept with an enumeration API, disabled by default when compiled in (<code>51a9ae20</code>, <code>73bc73a1</code>), and a real crash fix for a <code>FNativeString</code> optimization-induced nullptr in Verse string binding (<code>ef8fde29</code>). The old table-of-contents implementation was deleted as unused (<code>057110cf</code>), which reads as a migration completing rather than starting.</li>
<li><strong>The categorizer’s “other” bucket caught 460 of this week’s 885 analyzable commits (52%)</strong>, including entire real clusters it doesn’t subsystem-tag: Procedural Vegetation Editor (25 commits, a module-move campaign under <code>UE-393874</code>), Mass entity-framework hardening (18 commits), and the Ness serialization work above.</li>
</ul>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>The bulk of this week’s low-interest volume was routine: single-line dependency and localization-automation bumps against <code>Engine/Build/Commit.gitdeps.xml</code>, Gauntlet and AutomationTool test-infrastructure tuning (device timeouts, coverage-target skipping, RPC relay swaps), a long tail of individual Niagara and Control Rig editor crash fixes each affecting a narrow repro case, and UnrealBuildTool dead-define cleanup, seven commits removing unreferenced global defines like <code>USE_VORBIS_FOR_STREAMING</code> and <code>USE_XMA2_FOR_STREAMING</code>. None of these change runtime behavior, build reliability, or upgrade risk for production teams.</p>
<h2 id="for-teams-shipping-on-58">For teams shipping on 5.8</h2>
<p><code>ue5-main</code> saw 174 commits this week (172 analyzable, 35 high-interest). No 5.8.x point release is confirmed as shipping in this week’s commit messages: “5.8.3” appears only as a target label on the nDisplay temporary fix and as a risk note on an unrelated Nanite-landscape material fix, not as a release that has actually gone out. Seven commits carry enough detail to call out:</p>
<ul>
<li><strong>The nDisplay config, cluster, and viewport crash-guard fix</strong> (see above) landed, was backed out, and relanded this week, tagged as a temporary fix for 5.8.3 (<code>74817dcc</code>, <code>6a3ef366</code>, <code>5fb9bb44</code>). Same fix as the <code>ue6-main</code> version.</li>
<li><strong>The Sequencer camera-shake-preview teardown fix shipped successfully on <code>ue5-main</code></strong> after one backout (<code>0422764d</code> then backed out <code>6890eb8a</code> then relanded <code>1262dc72</code>), unlike <code>ue6-main</code> where the same fix remains reverted with no reland this week.</li>
<li><strong>A Curve Table crash on undo</strong> (<code>af9ec00c</code>, CL 57546368): stale pointers to rich curve models after an undo operation now resolve by handle instead, fixing a crash native to this branch.</li>
<li><strong>A Nanite material-preview crash</strong> (<code>46a36579</code>, CL 57538920): selecting an unsupported material domain or blend mode while previewing a Nanite mesh in the material editor no longer crashes.</li>
<li><strong>A Constraints use-after-free during animation evaluation</strong> (<code>00f014cb</code>, CL 57510985): <code>FAnimationEvaluator</code> could be freed while the constraints evaluation graph was invalidated mid-evaluation; now guarded.</li>
<li><strong>Material Parameter Collections now use persistent shared uniform buffers keyed by ID</strong> rather than one buffer per collection instance (<code>0ae1658d</code>, CL 57393741), fixing crashes when the same collection appeared in multiple packages and one was unloaded, leaving stale uniform-buffer references in cached mesh draw commands.</li>
<li><strong>A Chaos memory leak on copying and scaling geometry</strong> was fixed (<code>2ce60b05</code>, CL 57364031).</li>
<li><strong>A Control Rig crash assigning a Pose Asset to a connected Get Pose Cache node</strong> was fixed via an index-swap bug in <code>FRigUnit_PoseAsset</code> (<code>1e883465</code>, CL 57496812).</li>
</ul>
<h2 id="closing">Closing</h2>
<p>A rendering week and a tooling week at the same time: the ray-tracing scene-extension work is visibly heading toward a stable API, and Unreal Build Accelerator and Unreal Toolbox both got a month’s and a week’s worth of concentrated single-author investment respectively. The branch-divergent Sequencer fix is a reminder that “fixed” needs a branch name attached to it.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless the claim is in the “For teams shipping on 5.8” section, which references <code>ue5-main</code>. Some of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-aug-24-30-2026/og.37755401c4.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: Infrastructure Catches Up With Itself (August 17 to 23, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-aug-17-23-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-aug-17-23-2026</guid>
      <pubDate>Mon, 24 Aug 2026 07:24:02 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>960 commits on ue6-main, and Unreal Build Accelerator carried the week again: RAD Linker bring-up, two Wine remote-build correctness fixes, and a storage overhaul all landed in the same seven days, with about a fifth of the week’s high-interest commits from one engineer. AVBOIT shipped as the new default transparency method behind an off-by-default flag, LumenRef’s early-termination pass roughly doubled its performance on NVIDIA hardware, and a batched Mass entity-creation path cut churn-test frame time 18% with real benchmark numbers attached. Two changes, an EnablePlugins build warning and an MVVM crash fix, were submitted and backed out in the same week, so neither is live despite showing up in the commit history. Plus a Zen server bump with a breaking namespace change, a UGS cache that saved 70 minutes of network transfer in one sync, and a silent out-of-bounds write in Mass entity storage that only bites in Test and Shipping builds.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-infrastructure-catches-up-with-itself">Unreal Engine Weekly: Infrastructure Catches Up With Itself</h1>
<p><strong>Week of August 17 - 23, 2026</strong> | <strong>960 commits analyzed on <code>ue6-main</code>, 172 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>This was an infrastructure-heavy week across build tooling, storage, and rendering, with two notable pieces of new tech (AVBOIT and a batched Mass/SceneGraph entity-creation path) landing alongside a mid-flight Unreal Build Accelerator rebuild and a Zen server bump. Two changes were reverted in the same week they shipped, so neither is live despite appearing in the commit history. On <code>ue5-main</code>, weekly volume roughly tripled from 55 to 172 commits, almost entirely cherry-picks of <code>ue6-main</code> fixes rather than native 5.x work.</p>
<h2 id="development-trends">Development Trends</h2>
<h3 id="unreal-build-accelerators-widening-scope-from-cache-correctness-to-full-stack-rebuild">Unreal Build Accelerator’s Widening Scope: From Cache Correctness to Full-Stack Rebuild</h3>
<p><strong>Pattern:</strong> UBA has carried the weekly themes for five of the last six weeks: observability and Zen 5.8.18 (July 13-19), the cache server rewritten under production failure pressure (July 20-26), the cache server dying in production (August 3-9), the cache server becoming multi-tenant (August 10-16), and this week’s three-front rebuild. Build stayed volatile but elevated (0, 3, 98, 87, 61, 85, 121, 117), the second-highest week of the eight-week window. What changed this week is scope: the prior three weeks were a single story (cache server correctness and scale). This week spans three separate workstreams at once: RAD Linker/RAD OBJ bring-up (<code>3c13ab27</code>, <code>daff30cf</code>, <code>9ffd4c20</code>), Wine remote-build correctness fixes for a CPU-spinning retry loop and a corruption bug (<code>9a5984e2</code>, <code>59b4672b</code>), and a storage/CAS architecture pass (<code>81ad2c35</code>, <code>e51f565b</code>, <code>c1d09790</code>). About a fifth of this week’s 200 high-interest commits came from one engineer, all inside UBA.</p>
<p><strong>Interpretation:</strong> cache-server correctness work appears to have graduated into broader platform investment in UBA itself, new backend targets, new host-platform correctness, and storage architecture, rather than staying scoped to patching the cache service. That reads as sustained engineering investment in UBA as core infrastructure, not a one-off hardening sprint. The single-engineer concentration is worth watching: a fifth of a week’s high-interest volume in one system resting on one person is a bus-factor risk even where the work itself is sound.</p>
<p><strong>Who cares:</strong> build and tools teams running distributed builds through UBA, especially Wine-based Linux remote build farms and anyone tracking RAD Linker adoption.</p>
<p><strong>Signal strength:</strong> Strong. Five of six weeks of continuous data, widening rather than narrowing in scope.</p>
<h3 id="renderer-decomposition-reaches-its-sixth-week-since-reopening-still-finding-real-concurrency-bugs">Renderer Decomposition Reaches Its Sixth Week Since Reopening, Still Finding Real Concurrency Bugs</h3>
<p><strong>Pattern:</strong> FScene’s decomposition into modular Scene Extensions has appeared in the weekly themes continuously since July 13-19: modular decomposition, then an API break, then scene data being rebuilt underneath consumers, through August 10-16. This week’s evidence is the SceneData migration closing TSAN-caught races: a fix that removes an unvalidated write path and a stale VSM cache-manager read (<code>bb622ce3</code>), double-buffered dirty masks so a locking consumer can defer late marks instead of asserting (<code>0469e076</code>), and the primitive octree moved into its own SceneData extension (<code>afc02eef</code>).</p>
<p><strong>Interpretation:</strong> six consecutive weeks since reopening on July 19, and the fixes are still genuine, TSAN-confirmed concurrency bugs rather than cosmetic relocation, consistent with the pattern from prior weeks where each extraction phase turned up real defects the old structure was hiding. Merge cost for anyone carrying renderer patches keeps compounding week over week.</p>
<p><strong>Who cares:</strong> renderer and engine-core teams, and any studio maintaining an engine fork with custom renderer code.</p>
<p><strong>Signal strength:</strong> Strong. Six weeks of continuous, substantive evidence since reopening.</p>
<h3 id="hardware-ray-tracing-and-nanite-rt-work-reopens-after-a-quiet-month">Hardware Ray Tracing and Nanite RT Work Reopens After a Quiet Month</h3>
<p><strong>Pattern:</strong> The prior Ray Tracing Productionization trend ran 19 weeks before resolving after July 19, with a note that a single-week revival on August 10-16 (on-demand CLAS reclamation, a LumenRef prototype) would need a second cluster to count as reopened. This week supplies it: AVBOIT ships as the new default OIT method behind an off-by-default support flag (<code>50b9cad9</code>), LumenRef gets an early-termination pass the commit message says roughly doubles its performance on NVIDIA hardware (<code>773bc646</code>), and Nanite ray tracing is restructured for per-instance BLAS support ahead of WPO-driven assembly parts (<code>2eafc68a</code>, <code>e2debb04</code>).</p>
<p><strong>Interpretation:</strong> two consecutive weeks of concrete ray-tracing and Nanite RT work after roughly a month of near-silence is enough to treat this as reopened rather than noise. The current phase reads as productionization groundwork, BLAS restructuring and vendor-specific speedups, rather than a finished, ship-ready feature.</p>
<p><strong>Who cares:</strong> graphics and rendering teams, particularly anyone using Nanite translucency or hardware-ray-traced Lumen.</p>
<p><strong>Signal strength:</strong> Moderate. Two weeks of data, reopening a previously resolved trend.</p>
<h3 id="mass-and-scenegraph-entity-work-returns-with-a-quantified-frame-time-win">Mass and SceneGraph Entity Work Returns With a Quantified Frame-Time Win</h3>
<p><strong>Pattern:</strong> The Actor-Optional Runtime trend (Mass, EntityFramework, and SceneGraph decoupling from AActor) had substantial clusters the weeks of August 2 and August 9 but produced no fresh cluster the week of August 16. This week it returns: six commits over August 19-20 build batched Mass entity creation for the Verse-driven SceneGraph mesh component path, shipping with benchmark numbers attached, an 18% average frame-time drop on a 5,064-entity churn test, a 57% drop in per-entity cost, and a 92% drop in Mass observer-event count (<code>72996da5</code>).</p>
<p><strong>Interpretation:</strong> after a one-week gap, the trend returns with its most concrete evidence yet, a quantified architectural win rather than functional-correctness-only work. That suggests the Verse/SceneGraph/Mass integration is maturing from “works” toward “works at scale,” not stalling out.</p>
<p><strong>Who cares:</strong> teams building on Verse and SceneGraph with Mass-driven mesh components.</p>
<p><strong>Signal strength:</strong> Moderate. A one-week gap breaks strict continuity, but this week’s evidence is stronger than the prior clusters.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Unreal Build Accelerator is running three workstreams at once: RAD Linker/RAD OBJ is explicitly not ready for use, but Wine remote-build correctness fixes and a CAS/storage overhaul are real, and about a fifth of this week’s high-interest commits came from a single engineer.</li>
<li>AVBOIT ships as the new default OIT method (behind an off-by-default support flag), LumenRef gets an early-termination pass that roughly doubles its performance on NVIDIA hardware, and a batched Mass/SceneGraph entity-creation path cuts churn-test frame time 18% with real benchmark numbers attached.</li>
<li>Two changes, an EnablePlugins build warning and an MVVM stale-binding crash fix, were submitted and backed out in the same week: neither is live on <code>ue6-main</code>, and a CL-number discrepancy in both backouts is unresolved.</li>
</ul>
<h2 id="highlights">Highlights</h2>
<h3 id="unreal-build-accelerators-three-front-rebuild-rad-linker-bring-up-wine-correctness-fixes-a-storage-overhaul">Unreal Build Accelerator’s three-front rebuild: RAD Linker bring-up, Wine correctness fixes, a storage overhaul</h3>
<p><strong>What changed:</strong> RAD OBJ/RAD Linker support is under active bring-up: initial write support (<code>3c13ab27</code>, CL 57034075), a new UBT <code>-radobj</code> flag (<code>daff30cf</code>, CL 57176650), diagnostics and robustness work (<code>86d0766a</code>, CL 57239920), keeping cache outputs uncompressed (<code>4f5bf804</code>, CL 57255288), and new RADLINKER binaries the commit message describes as “based on two PRs that over time will evolve into the mainline” (<code>9ffd4c20</code>, CL 57296417). Separately, two Wine correctness bugs were fixed: a missing release-build detour caused Wine to reject a UBA-owned address, which made <code>radlink</code> spin at 100% CPU retrying cache eviction indefinitely (<code>9a5984e2</code>, CL 57226851), and a gap between <code>munmap</code> and <code>mmap</code> could let a concurrent mapping silently corrupt a live one (<code>59b4672b</code>, CL 57215747). And a storage/CAS pass landed: optimized compressed storage and CAS creation (<code>81ad2c35</code>, CL 57152245), parallelized session output prep (<code>8c4fe200</code>, CL 57157524), a fix for cache-entry lifetime and corruption during upload and maintenance with new regression coverage (<code>e51f565b</code>, CL 57170243), and fixed CAS/transfer lifetime leaks (<code>c1d09790</code>, CL 57165605). Process racing is now on by default for UbaCli with a scheduler, though the same commit’s own description admits “force-remote being broken” (<code>077b11aa</code>, CL 57306021).</p>
<p><strong>Why this is important:</strong> these are three different maturity levels of the same system landing in one week. RAD Linker is explicitly not released yet (“radlink.exe with support for this is not released yet”), the Wine fixes are patching live corruption and hang bugs in mixed Windows/Linux remote-build setups, and the storage overhaul is a genuine architecture pass, not incremental cleanup. Roughly 42 of this week’s 200 high-interest <code>ue6-main</code> commits, about a fifth, came from one engineer, all inside UBA: that’s not disqualifying on its own, but it does mean UBA’s correctness surface this week rests on one person’s work.</p>
<p><strong>Who should care:</strong> build/tools teams running distributed builds via UBA, especially Wine-based Linux remote build farms.</p>
<p><strong>Urgency:</strong> Act now for the Wine correctness fixes if you run Wine remote helpers. Track the storage overhaul. Ignore RAD Linker/RAD OBJ until it’s out of bring-up.</p>
<h3 id="zen-ddc-server-jumps-to-v5820-a-relay-role-an-experimental-two-tier-cas-and-a-breaking-namespace-change">Zen (DDC) server jumps to v5.8.20: a relay role, an experimental two-tier CAS, and a breaking namespace change</h3>
<p><strong>What changed:</strong> a single <code>Commit.gitdeps.xml</code> bump (<code>b64b19d3</code>, CL 57118611) carries a large embedded changelog for the Zen server. Two features stand out: a new “relay” server role that turns cache traffic into a durable, replayable S3-backed stream, and an experimental two-tier generational CAS: an NVMe fast-tier read cache that falls back to the primary store, and a separate S3 remote-tier backup mechanism for the primary store. Both are opt-in and fail safe, and the commit does not describe the fast tier as backed by the remote tier, they are independently configured. The same update adds a <code>zen trace stats</code> mode that decodes <code>.utrace</code> files into structured JSON explicitly designed for “tools and LLMs” to consume. It also includes a breaking change: cache requests must now name their namespace explicitly, and the legacy <code>ue4.ddc</code> fallback is rejected with an HTTP 400. Buried in the changelog is a real fix: garbage collection with <code>gc.projectstore.attachment.store</code> enabled could misclassify live project-store attachments as unused and delete them if the oplog changed mid-GC pass; that’s now fixed.</p>
<p><strong>Why this is important:</strong> the namespace change will break any pipeline still relying on the <code>ue4.ddc</code> fallback the moment this version rolls out, and the GC bug was a real data-loss path for anyone running that config flag.</p>
<p><strong>Who should care:</strong> build/platform teams operating shared DDC/Zen infrastructure.</p>
<p><strong>Urgency:</strong> Act now if you rely on the <code>ue4.ddc</code> fallback or the <code>gc.projectstore.attachment.store</code> flag. Track otherwise.</p>
<h3 id="transparency-and-ray-tracing-avboit-ships-lumenref-nearly-doubles-in-speed-nanite-rt-restructures-for-per-instance-blas">Transparency and ray tracing: AVBOIT ships, LumenRef nearly doubles in speed, Nanite RT restructures for per-instance BLAS</h3>
<p><strong>What changed:</strong> AVBOIT (Adaptive Voxel-Based OIT, a SIGGRAPH 2025 technique) is implemented for both Nanite and non-Nanite translucency, built on UAV atomics with no raster-ordered views (<code>50b9cad9</code>, CL 57145936, jira UE-354390). It becomes the numeric default for <code>r.OIT.Method</code>, but its shader permutations are gated behind <code>r.AVBOIT.Support</code>, which defaults off at the project level, so it won’t compile into most projects unopted. Separately, LumenRef (Epic’s in-progress hardware-ray-traced Lumen path) picked up early-terminating paths for rays that won’t contribute to the final pixel, which the commit message says “on average doubles LumenRef performance” while also improving diffuse signal quality (<code>773bc646</code>, CL 57221354); the same commit notes this makes LumenRef “dependent on NV SER extension due to lack of GetClusterID in shader model 6.9,” so the speedup is NVIDIA-only for now. The same author also fixed a correctness bug where world-cache entries were skipped during out-of-order removal (<code>fe7ba585</code>, CL 57298728). And Nanite ray tracing is being restructured for per-instance BLAS support: one commit decouples the BLAS cache from <code>RuntimeResourceId</code>, stating the reason as “we will need to support multiple BLAS per resource (assembly parts / per-instance BLAS when using WPO)” (<code>2eafc68a</code>, CL 57018825), followed by a change moving to one BLAS per assembly-part instance (<code>e2debb04</code>, CL 57025687).</p>
<p><strong>Why this is important:</strong> AVBOIT is a real default-path change to translucency rendering, even though it’s opt-in for now. The LumenRef speedup is significant but currently NVIDIA-specific. The Nanite RT decoupling is groundwork for WPO-driven assembly parts in ray tracing, not a feature that ships this week.</p>
<p><strong>Who should care:</strong> graphics/rendering teams, especially anyone using Nanite translucency or hardware-ray-traced Lumen.</p>
<p><strong>Urgency:</strong> Track.</p>
<h3 id="scenedata-migration-keeps-closing-real-tsan-races-not-just-moving-code-around">SceneData migration keeps closing real TSAN races, not just moving code around</h3>
<p><strong>What changed:</strong> the multi-week migration of core scene state into the tracked-access SceneData system continued. One commit explicitly fixes “TSAN races by tightening root-context access,” removing an unvalidated write path and a stale VSM cache-manager read that TSAN had actually caught (<code>bb622ce3</code>, CL 57261239). Another makes dirty masks double-buffered so a locking consumer can defer late marks instead of asserting (<code>0469e076</code>, CL 57146944). A third moves the primitive octree into its own SceneData extension (<code>afc02eef</code>, CL 56998444).</p>
<p><strong>Why this is important:</strong> these are confirmed race fixes caught by TSAN, not speculative hardening, which suggests the SceneData migration is finding and closing real concurrency bugs as it proceeds rather than just relocating code.</p>
<p><strong>Who should care:</strong> renderer/engine-core teams.</p>
<p><strong>Urgency:</strong> Track.</p>
<h3 id="horde-and-ugs-get-bandwidth-and-reliability-fixes-that-only-bite-at-multi-branch-multi-agent-scale">Horde and UGS get bandwidth and reliability fixes that only bite at multi-branch, multi-agent scale</h3>
<p><strong>What changed:</strong> a new opt-in UGS cross-branch cache lets UGS reuse files already synced in a sibling branch enlistment on the same machine instead of re-fetching identical revisions, matched by Perforce’s own MD5 digest; tested live against FN-Main/UE6-Main it reused 37,694 files (197.71 GB) in one sync with zero mismatches, saving roughly 70 minutes of network transfer at 400 Mbps (<code>f25bb20b</code>, CL 57222891). On the Horde side, pools with AutoSDK disabled were still getting AutoSDK workspaces attached on conform with no view filter, so agents tried to sync the entire AutoSDK depot and failed with an <code>InsufficientSpaceException</code>; the commit notes affected agents “can’t self-heal since the conform that would prune AutoSDK is the one failing” (<code>4702f02c</code>, CL 57222618). Other reliability fixes: full workspace resyncs after Perforce edge-server swaps when only an incremental sync was needed (<code>79deef26</code>, CL 57064448), scheduler parallelization plus protection against a hung Perforce query stalling the whole scheduler (<code>a48474dc</code>, CL 57053336), Redis 5 compatibility restored for Perforce server selection after a Redis-6-only option broke it (<code>d470b800</code>, CL 57061235), and dashboard job-ID queries batched to stop HTTP 414 errors on large pools (<code>b1aaa435</code>), with a follow-up batch-size tuning commit the same day (<code>b7d9f085</code>). A new “Lore” version-control server plugin and job driver for Horde also landed, explicitly flagged as “work in progress and will be changing significantly” (<code>22fd635b</code>, CL 57282454).</p>
<p><strong>Why this is important:</strong> the AutoSDK bug is a genuine self-inflicted-outage pattern: affected agents get stuck and can’t recover on their own because the fix path is the same conform step that’s failing. The UGS cross-branch cache is a meaningful bandwidth win for anyone syncing multiple branch enlistments on the same machine.</p>
<p><strong>Who should care:</strong> build/platform teams running Horde agent fleets or multi-branch Perforce setups.</p>
<p><strong>Urgency:</strong> Act now if you run Horde pools with AutoSDK disabled. Track the UGS cache and other reliability fixes. Ignore the Lore plugin until it stabilizes.</p>
<h3 id="two-changes-shipped-and-reverted-in-the-same-week-neither-is-live">Two changes shipped and reverted in the same week: neither is live</h3>
<p><strong>What changed:</strong> two <code>ue6-main</code> changes were submitted and then backed out within the same week, with no reland visible anywhere later in this week’s window. An EnablePlugins build warning was added (“warn when a EnablePlugins entry is not built,” <code>ef8066fa</code>, CL 57204382), then disabled days later citing “some existing cases that need to be addressed first” (<code>76ae75de</code>, CL 57279538), then fully backed out (<code>d8867032</code>, CL 57323996). Separately, an MVVM stale-binding crash fix that registers Blueprint classes reachable from a widget’s view-binding path as compile dependencies, fixing a real PIE crash on stale property pointers (<code>0a63dd20</code>, CL 57089686), was backed out three CLs later (<code>c4279e47</code>, CL 57092963).</p>
<p><strong>Why this is important:</strong> neither change is live on <code>ue6-main</code> as of the end of this week. The MVVM backout matters most: the underlying PIE crash on stale property pointers is still present upstream, not fixed, despite the initial commit reading like a shipped fix. Both backouts also carry a CL-number discrepancy in their “Original CL Desc” field (57204029 vs. 57204382 for the plugin-warning backout, and 57089520 vs. 57089686 for the MVVM backout): close but non-matching numbers, most likely a Perforce shelve-vs-submit numbering artifact since the file diffs match, but this is unresolved and needs human verification before treating it as settled.</p>
<p><strong>Who should care:</strong> build/UBT teams tracking EnablePlugins warnings, UMG/MVVM teams that saw the crash-fix commit go by.</p>
<p><strong>Urgency:</strong> Track.</p>
<h3 id="scenegraph-batches-mesh-entity-creation-into-mass-with-numbers-to-back-it-up">SceneGraph batches mesh-entity creation into Mass, with numbers to back it up</h3>
<p><strong>What changed:</strong> six commits over August 19-20 build out batched Mass entity creation for the Verse-driven SceneGraph mesh component path. The anchor commit introduces a scoped entity-creation batch that groups scene-add operations and flushes them through one batched creation call instead of paying per-entity archetype moves, and it ships benchmark numbers: on a 5,064-entity churn test, average frame time drops 18% (7.74ms to 6.31ms), per-entity <code>AddedToScene</code> cost drops 57%, and Mass observer-event count drops 92% (4,174 to 334) (<code>72996da5</code>). Follow-on commits add regression tests for teardown/observer edge cases (<code>bf09acfc</code>), bring material/bounds parity with the legacy static render path (<code>95054a1b</code>), fix Mass-rendered meshes freezing at spawn transform under <code>mass.FullyParallel</code> (<code>d5e6f928</code>), and fix signal-ordering correctness during creation (<code>cc28c4c9</code>). A related commit wraps the legacy PSO precache call in <code>AutoRTFM::OnCommit</code> because it’s reached from inside a closed Verse transaction (<code>5b8ad594</code>).</p>
<p><strong>Why this is important:</strong> this is a quantified architectural win, not just a functional-correctness pass. The benchmark table is the clearest evidence this week that Epic’s Verse/SceneGraph/Mass integration is maturing from “works” to “works at scale.”</p>
<p><strong>Who should care:</strong> teams building on Verse/SceneGraph with Mass-driven mesh components.</p>
<p><strong>Urgency:</strong> Track.</p>
<h3 id="metasound-gets-a-live-tunable-parameter-override-system">MetaSound gets a live-tunable parameter-override system</h3>
<p><strong>What changed:</strong> three commits from the same engineer, sharing a sync baseline, build a runtime parameter-override store for MetaSound over August 18-21. The first adds the store so config, hotfix, and console commands can seed default values for a MetaSound source’s transmittable graph inputs before future plays (<code>9efc1e0e</code>, jira UE-387478). The second stops the MetaSound builder from applying those overrides while a graph is being actively edited in the editor, so an artist’s authored defaults aren’t clobbered, and rekeys overrides on the asset’s full path instead of a short object name, so a record targets exactly one asset and mistyped or wildcard paths error instead of silently never applying (<code>c9e632f6</code>). The third splits storage into separate config and console “planes” so reapplying a config override only replaces what config contributed, without stomping console-set values (<code>9ec434e9</code>).</p>
<p><strong>Why this is important:</strong> this reads as infrastructure for live-tuning MetaSound values in a shipping game without republishing assets: config/hotfix for live-ops sweeps, console for local debugging, with each layer isolated from the others.</p>
<p><strong>Who should care:</strong> audio and live-ops teams.</p>
<p><strong>Urgency:</strong> Track.</p>
<h3 id="two-crashes-worth-flagging-a-fabricated-buffer-bug-in-typed-elements-a-silent-out-of-bounds-write-in-mass">Two crashes worth flagging: a fabricated-buffer bug in Typed Elements, a silent out-of-bounds write in Mass</h3>
<p><strong>What changed:</strong> <code>FCommandBuffer::GetQueuedDataColumn</code> was fabricating a pending column from a zero-filled, never-constructed buffer; any member with a non-trivial constructor (FText, for example) ended up with a null internal pointer that crashed the next reader, reproduced via the TEDS “Sort unsorted alerts” path (<code>afa3dc42</code>). Separately, Mass entity storage could run out of page space, assert via <code>AddPage</code>, and then continue anyway: because <code>check</code>/<code>checkf</code> compile out in Test and Shipping configurations, those configurations wrote past the end of the entity-page array while still reporting success (<code>ecddb898</code>).</p>
<p><strong>Why this is important:</strong> the Mass bug is a silent out-of-bounds write specifically in Test and Shipping builds, the exact configurations where the assert that would have caught it is compiled out. That’s a real memory-safety issue in shipped-build configurations, not a debug-only check.</p>
<p><strong>Who should care:</strong> Typed Elements/TEDS users, and any team running Mass at scale in Test or Shipping configurations.</p>
<p><strong>Urgency:</strong> Act now.</p>
<h2 id="worth-tracking">Worth Tracking</h2>
<ul>
<li><strong>Storage/memory-budget hardening, coordinated:</strong> the persistent storage manager’s quota subsystem picked up hotfixable quota CVars (<code>a47df30e</code>), a fix for <code>QuotaMB</code> incorrectly matching as a substring of <code>OptionalQuotaMB</code> (<code>488ec976</code>), overbudget category/stat accessors (<code>aa3e9093</code>), and a debug HUD panel explicitly modeled on the existing IoStoreOnDemand panel (<code>076183e5</code>). Separately, an Elastic IO Block Cache that borrows free memory for disk caching and gives it back under pressure landed (<code>594f21a4</code>), alongside a five-commit IAS/On-Demand cluster the same week, three of which share one sync baseline (<code>b377ad48</code>, <code>80b8233c</code>, <code>49351bcc</code>) and two of which land on their own baselines (<code>2bf1b423</code>, <code>d2795858</code>). A new quota system explicitly copying an existing cache HUD pattern, a new elastic disk cache, and a sprint of IAS hardening landing in the same week reads like coordinated investment in storage/memory-budget tooling, though nothing in this week’s data confirms what platform push it’s aimed at.</li>
<li><strong>Control Rig “Rig Promises”:</strong> a single commit introduces an authored contract describing what a rig will construct before it’s actually constructed, gated behind <code>ControlRig.Promises.Enable</code> (default off) and labeled “first iteration” (<code>972dad21</code>). No user-facing effect yet, but foundational architecture worth remembering before it ships enabled.</li>
<li><strong>RAD Linker/RAD OBJ remains explicitly work in progress</strong> (see the UBA section above): don’t build workflows around it yet.</li>
<li><strong>AI toolset registry keeps expanding:</strong> this week added a diff-based <code>edit_file</code> to replace whole-file writes, because “past the orchestrator output cap the arguments JSON is cut off mid-object” (<code>a6ee1625</code>), a 46-crash telemetry-driven fix rejecting destroyed-object references across every toolset entry point (<code>48951ad8</code>, jira UE-386549), and an asyncio hang fix for a tool call where a user “waited over 90 minutes on a tool call the service had already finished” (<code>4e425d10</code>). The same UE-386549 ticket also shipped as a cherry-pick on <code>ue5-main</code> this week (see the 5.8 section below), so this is an active cross-branch push, not a one-off.</li>
<li><strong>Minor connective tissue:</strong> TEDS adoption continues alongside the SceneGraph/Mass work above: editor viewport piloting moved from a raw actor pointer to a TEDS row handle so non-actor SceneGraph entities can be piloted (<code>84dcfd08</code>), with Outliner tests loosened to match (<code>a54954a5</code>).</li>
</ul>
<h2 id="for-teams-shipping-on-58">For teams shipping on 5.8</h2>
<p><code>ue5-main</code> saw 172 commits this week (171 analyzable, 32 high-interest), up sharply from 55 the prior week (August 10-16). Most of that volume, and most of this week’s high-interest commits, is routine editor-crash-fix maintenance (Sequencer, Curve Editor, landscape, mostly single-line messages with no deeper detail available) and cherry-picks/backports of <code>ue6-main</code> fixes onto 5.8 rather than native 5.x development. No 5.8.x point release is named as shipping in this week’s commit messages, so none should be assumed. Six commits carry enough detail to be worth calling out:</p>
<ul>
<li>A heap-use-after-free bug (caught by ASan) in the Unified Animation Framework’s static teardown path (<code>583d0daa</code>): teardown handlers moved from <code>OnEnginePreExit</code> to <code>OnPreExit</code>, and factory containers plus the abstract-skeleton default-bindings cache are emptied during teardown so UObject references release before the UObject system is purged (CL 57268887).</li>
<li>A landscape editor hotfix (<code>246b4fcd</code>): <code>ULandscapeInfo::GetTargetLayerSettings</code> was const-qualified but implicitly created an undo-buffer entry when none existed, corrupting the undo stack in a specific repro (undo after a landscape resize inside a level instance) and causing a <code>check()</code> crash on <code>PostEditUndo</code>. The fix returns an empty settings struct instead; a full fix (a const pointer return, all callsites updated) isn’t hotfix-compatible, so this minimal version targets 5.8.3 specifically (CL 57265755, jira UE-386585).</li>
<li>A subsystem-collection GC crash (<code>2248b96a</code>): if an owner is garbage-collected without calling <code>Deinitialize()</code>, the fallback deinitialization path could run mid-purge against an object with a reset internal index. The fix skips deinitialization during purge and logs a warning instead of ensuring, deliberately, since an ensure would just report a known condition on a patch release (CL 57235178, jira UE-385129). The same underlying bug class was also fixed on <code>ue6-main</code> this week.</li>
<li>A toolset object-parameter validation fix (<code>bf7d3b85</code>, jira UE-386549): a tool call naming an object by path could resolve an actor already destroyed but not yet garbage-collected, and nothing validated it before a full property write, eventually crashing in <code>UWorld::AddPostProcessVolume</code>. This caused 46 crashes across 45 users on 5.8. The fix rejects any resolved object failing <code>IsValid()</code> across every UToolsetLibrary entry point, with 8 new tests added (CL 57226176). Same jira ticket as the <code>ue6-main</code> toolset-registry fix above, part of the same cross-branch push.</li>
<li>A data layer re-entrancy fatal (<code>b561c015</code>): a data layer track evaluated synchronously from inside <code>UWorld::UpdateLevelStreaming</code> (for example, a Sequence played off an <code>OnLevelAddedToWorld</code> delegate during <code>AddToWorld</code>) could re-enter <code>UpdateLevelStreaming</code> and trip its fatal guard. The fix skips the synchronous flush when already inside <code>UpdateLevelStreaming</code>, gated by a CVar that defaults on (CL 57143995). Cherry-picked from <code>ue6-main</code>.</li>
<li>A Control Rig Constraints UX performance fix (<code>3a5f7678</code>): a details-panel refresh chain was refreshing anim details every tick, adding 8 to 20ms per tick. The fix tries a cached object-id lookup before falling back to the slower resolution path (CL 57090423, jira UE-387626). Cherry-picked from <code>ue6-main</code>.</li>
</ul>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>The bulk of this week’s low-interest volume was routine: binary rebuilds with no source change, single-line dependency bumps for internal Fortnite/UEFN content, test-framework wording and log-verbosity tweaks, and test-automation quality-of-life changes. A handful of commits that initially looked notable turned out to be internal build-packaging plumbing or pure dependency churn (a Linux build fix for an Open PBR Material Horde job with no functional change) and were dropped. Backout and reland pairs remain hard to track systematically; we surfaced the two that mattered above but did not attempt to count the rest.</p>
<h2 id="closing">Closing</h2>
<p>A week of infrastructure catching up with itself: UBA, Zen, Horde, and UGS all moved in the same seven days, and the two backouts are a reminder to check what actually shipped rather than what was merged. The Mass/SceneGraph batching numbers are the clearest signal that the Verse-era entity work is starting to pay off in frame time, not just correctness.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless the claim is in the 5.8 section, which references <code>ue5-main</code>. Some of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-aug-17-23-2026/og.8c570e7a57.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: The Build Farm Stops Blaming the Network (August 10 to 16, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-aug-10-16-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-aug-10-16-2026</guid>
      <pubDate>Mon, 17 Aug 2026 06:50:08 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>847 commits on ue6-main, and the week belongs to build infrastructure. Horde agents on streams with moving Perforce pins were producing builds from a mix of old and new files, and the detector written to catch that stayed dormant. UBA’s cache server went from roughly 500 buckets to roughly 5,000 and grew sweep-budget scaling, automatic bucket retirement, and retention telemetry to survive it. A disk stall detector landed after farm hosts were found holding single filesystem calls for up to 28 minutes while their timeouts got blamed on the network. Plus a material instance cook fix with a measured A/B, a Windows SDK floor bump worth checking before you sync, and an input thread default that flipped on and back off the same day.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-the-build-farm-stops-blaming-the-network">Unreal Engine Weekly: The Build Farm Stops Blaming the Network</h1>
<p><strong>Week of August 10 - 16, 2026</strong> | <strong>847 commits on <code>ue6-main</code>, 55 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>847 commits, 198 worth reading, and the week belongs almost entirely to build infrastructure. Build is the top subsystem at 121, and a single engineer accounts for 47 of those 198.</p>
<p>The through-line is diagnosis. Several long-running build failures got root-caused this week, and in most of them the system had been pointing at the wrong culprit. Farm hosts were freezing whole builds on single filesystem calls that hung for up to 28 minutes, and the resulting timeouts looked like network bugs. Horde agents on streams with moving Perforce pins were producing builds from a mix of old and new files, and the detector written to catch exactly that stayed dormant, because it read pins from a field Perforce never writes <code>@CL</code> revspecs into. A material instance cook fix landed with a six-of-six versus zero-of-six A/B instead of a claim.</p>
<p>The last two weeks were about caches returning wrong answers. This week is about the instruments that name which thing went wrong, and how long a build farm can run without them.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<p><strong>Build has been the top or second subsystem for eight consecutive full-volume weeks, and the work has moved through three distinct phases.</strong> July was modernization: C++23 by default, circular dependencies eliminated, EpicClang versioned, the C# BuildGraph VM deleted. Early August was cache correctness: seven independent defects where a cache key was not a function of its inputs. This week is operations. The UBA cache server got sweep-budget scaling, automatic bucket retirement, retention telemetry, per-bucket metadata, and a web console that scales, because the bucket key gained target type plus branch name for the one heavily used target, taking the count from roughly 500 to roughly 5,000 (<code>6e5e1585</code>, <code>98dccb91</code>). Retirement policy is service work, and Epic is now doing service work on what used to be a build feature.</p>
<p><strong>The UBA cache server has now been hardened under live production failure three weeks running, and the failure class changes each time.</strong> Jul 20-26 was throughput and corruption. Aug 3-9 was process death, with SIGBUS on a full filesystem. This week is scale and misattribution: a worker pool that could enter an absorbing state and freeze a build for roughly 26 minutes (<code>26e9b3dd</code>), a torn read on Apple Silicon that segfaulted inside blake3 (<code>5fb5c544</code>), and a stall detector that reclassifies timeouts caused by local disk (<code>b8a0758b</code>). Three consecutive weeks on the same component means the thing is being operated past the design it shipped with.</p>
<p><strong>The cook-determinism campaign is now in its fifth week and produced its most concrete result yet, though the fixes carry three different reviewers and one jira between them, so they read as unrelated one-offs unless you line them up.</strong> A material instance parameter strip that mutates the live object mid-save (<code>68070a5d</code>), Blueprint RNG nodes drawing from the process-global stream instead of the deterministic cook stream (<code>3bc9b39d</code>), and a shader <code>AssetInfo</code> sort truncating 8-byte hashes to 4 bytes at an estimated 99% collision probability across 200,000 elements (<code>8f43677d</code>). Two of the three present as incremental-cook validation failures, the mechanism that pushes studios into full recooks; the shader hash truncation surfaces as raw cook indeterminism on large projects.</p>
<p><strong>Backout volume stayed high and clustered on the same two areas as last week: 27 commits tagged <code>[Backout]</code>, and five land-backout-reland cycles inside seven days.</strong> Material instance parameter stripping alone burned three commits between two engineers on a single day (<code>364f4a0b</code>, <code>e5601e1b</code>, <code>68eaf5d3</code>), on top of two diagnostic backouts the day before. Cook determinism, dynamic subsystems, an input debug overlay, and the crash reporter statics each completed a full cycle. The input thread default landed and was reverted with no reland. If you track <code>ue6-main</code> closely, mid-week changelists in the material and cook areas are not safe sync points.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Horde agents on streams with moving import CL pins have been intermittently producing builds from a mix of old and new files. The existing view-change detection stayed dormant for pin moves, because Perforce never emits <code>@CL</code> revspecs in the generated view of <code>p4 stream -o -v</code>, so pins surface only in the computed <code>ChangeView</code> field. That field is now captured, hashed, persisted, and diffed on client recreation (<code>61e34d05</code>).</li>
<li>The UBA cache bucket key now includes target type and, for one target, branch name, taking bucket count from roughly 500 to roughly 5,000. A fixed sweep budget of one bucket per poll saturates around 3,600 buckets and the backlog then grows forever, so the budget now scales at one per 500 per poll and idle empty buckets auto-retire (<code>6e5e1585</code>).</li>
<li>A passive disk stall detector landed in UBA after Ntfs event-log history showed farm hosts holding single filesystem calls for 30 seconds to 28 minutes, near daily per affected machine. Timeout warnings whose wait overlapped a detected stall are now downgraded to info with an explanation (<code>b8a0758b</code>).</li>
<li>The minimum Windows SDK is now 10.0.26100.0, with the commit sweeping in-tree references to 22621 (<code>05d13aec</code>). Build images that only carry the older SDK are now below the floor.</li>
<li>Material instance cook non-determinism was traced to <code>CleanUpUnusedParameters()</code> mutating the live object during cook-save, validated with an A/B that reproduced the failure on six of six stripping instances with the fix off and zero of six with it on (<code>68070a5d</code>).</li>
<li><code>FAssetRegistryGenerator::FinalizeChunkIds</code> went from 187s to 1s, taking a 300s stage down to 114s, by fixing pathological <code>TSet</code> reallocations in the package graph (<code>32e19f61</code>).</li>
<li>Restricting ICF defeating to only where AutoRTFM needs it cut a large server binary by 10.5% (<code>1f14b550</code>).</li>
<li>The input thread was enabled by default on August 14 and backed out three hours later. As of this writing <code>input.UseInputThread</code> still defaults to 0 on <code>ue6-main</code> (<code>fbd5d79a</code>, <code>3ba05293</code>).</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="horde-was-building-from-two-changelists-at-once-and-nothing-reported-it">Horde was building from two changelists at once, and nothing reported it</h3>
<p><strong>What changed.</strong> Horde agents on streams with moving import CL pins were intermittently producing builds from a mix of old and new files. When a partitioned client is recreated after an edge-server change, the have table is rebuilt with a flush under today’s stream view. If a pin moved since the workspace was last synced, that flush claims files at the new pin that were never written to disk, and incremental syncs never repair them.</p>
<p>Worth reading twice: why the existing safety net never caught it. A previous changelist added view-change detection to reconcile exactly this case, but it read pins from the generated <code>View</code> of <code>p4 stream -o -v</code>, and the server never emits <code>@CL</code> revspecs there. Pins, including ones inherited from parent stream specs, surface only in the computed <code>ChangeView</code> field. The view hash therefore never changed when a pin moved, and the reconcile stayed dormant. <code>ChangeView</code> is now captured, hashed, persisted in workspace state, and diffed on client recreation, reconciled against what can actually be on disk and capped by both the pin and the last-synced CL: forward moves and pin removal flush to the old snapshot and sync only the delta, while backward moves delete the exact set of formerly-versioned stale files and force-sync the new snapshot, so untracked build artifacts survive (<code>61e34d05</code>). A separate fix stops <code>PerforceConfigSource</code> login failures from stranding one native connection per minute-poll with no finalizer (<code>8cec04cf</code>).</p>
<p><strong>Why this is important.</strong> A build-correctness failure with no error attached to it. The job succeeds, the artifact is wrong, and the wrongness is a mix of two changelists rather than an obviously old build. The commit describes Epic’s own agents, but the shape generalizes to anyone pinning imports across streams and running edge servers. The detection gap is the transferable lesson: a check that reads the wrong field is worse than no check, because it looks like coverage.</p>
<p><strong>Who should care.</strong> Anyone running Horde with pinned stream imports and Perforce edge servers. Release engineers who have ever chased a build that behaved like a stale sync and could not reproduce it.</p>
<p><strong>Urgency: Act now.</strong></p>
<h3 id="the-uba-cache-server-became-a-multi-tenant-service-in-one-week">The UBA cache server became a multi-tenant service, in one week</h3>
<p><strong>What changed.</strong> The cache bucket key changed. Target type (client, server, editor, program, game) is now part of the bucket id, and branch name is added for the win64 modular development editor target, with the commit explicitly avoiding wider use to prevent “a permutation explosion (Epic has lots of branches)” (<code>98dccb91</code>, <code>8af204f5</code>, <code>11fe8b2d</code>). Bucket count goes from roughly 500 to roughly 5,000. Worth noting the order: the server-side scaling work landed earlier in the week, and the changelist that actually put branch name into the bucket id came last, on August 14. Epic built the headroom first.</p>
<p>Sweep budget now scales at one bucket per 500 per poll, eight times that while a force-deleted wave is pending. The commit shows the math: a fixed budget of one saturates around 3,600 buckets, after which the backlog grows forever, and force-deleted waves took bucketCount/8 seconds to consume while serving failed fetches (<code>6e5e1585</code>). Empty idle buckets retire automatically, since per-branch buckets mean dead branches would otherwise accumulate zombie buckets forever, and the idle grace period doubles as the use-after-free guard for erasing a bucket outside the forced path. Bucket indices now come from a counter rather than map size, so retired indices do not alias logs or round-robin.</p>
<p>Retention became measurable rather than assumed. A <code>Retention</code> column reports the longest-unused entry age, an orange <code>trimmed N</code> marks entries that the last full maintenance pass sacrificed which configured expiration would have kept, since only runs holding the bucket exclusively can compact and enforce, the squeeze step moved from a flat one-hour cut to a proportional 10% per iteration, and a starvation bug was fixed where a bucket left dirty by a non-exclusive run never requeued for maintenance (<code>871534b8</code>, <code>13adc404</code>, <code>d05288a0</code>). Two metadata paths landed alongside: per-bucket metadata with dedup and eviction past a default of five entries (<code>0ff4cf28</code>), and server-wide key/value metadata (<code>604510d9</code>), with UBT sending the unhashed cache key string as bucket metadata on first use (<code>11fe8b2d</code>), which should make it possible to tell which bucket belongs to which build configuration. The web console moved sorting, filtering, and truncation server-side, because <code>/api/buckets</code> was shipping roughly 1MB per poll at 5,000 buckets and now ships roughly 15KB for the top 50 (<code>ea085af7</code>). Zen server also moved to v5.8.19 (<code>17032ade</code>), though that commit resolves only to <code>Commit.gitdeps.xml</code>, so only the changelog text is verifiable.</p>
<p><strong>Why this is important.</strong> Operating a UBA cache is now a different job. Cache buckets used to be a coarse partition you could ignore. Now they are per branch and per target type, they retire on their own, and they report when trimming cost you real cache hits. Learn the orange <code>trimmed N</code> marker: it means the expiration you configured was not the expiration you got, and the recommended response is to split the bucket.</p>
<p><strong>Who should care.</strong> Build and CI engineers self-hosting a UBA cache server. Anyone whose cache hit rate has been unexplainably mediocre across many branches.</p>
<p><strong>Urgency: Act now.</strong></p>
<h3 id="your-build-farm-has-been-blaming-the-network">Your build farm has been blaming the network</h3>
<p><strong>What changed.</strong> A passive local disk stall detector landed in UBA, and the commit’s justification is the interesting part. Farm hosts on AWS with EBS storage can hold a single filesystem call for anything from 30 seconds to many minutes while the device otherwise looks healthy, confirmed against Ntfs Operational event 147 history showing Create, Write, and Cleanup calls stuck between 30 seconds and 28 minutes, near daily per affected machine. Those stalls freeze the whole build, and the timeouts they cause downstream looked like network or UBA bugs.</p>
<p>The detector costs two relaxed stores per filesystem call. Wrappers publish the in-flight call in a per-thread slot, and a watchdog scans for calls stuck longer than 15 seconds, logging one line when the stall resolves. The load-bearing part is the reclassification: timeout warnings and errors whose wait overlapped a detected stall are downgraded to info with a suffix naming the stall, covering FetchBegin hangs, <code>WaitForWritten</code>, <code>SendBatchMessages</code>, <code>FetchCompactTable</code>, session ping, and TCP <code>WSAPoll</code> timeouts (<code>b8a0758b</code>).</p>
<p>Two other freezes got root-caused the same way. <code>NetworkServer</code> receive threads could park forever waiting for a worker, and receive threads are where disconnect detection happens, so worker-pool exhaustion was an absorbing state that produced a roughly 26 minute whole-build freeze. <code>PopWorker</code> was changed to wait 10 seconds and then spawn an emergency worker beyond <code>maxWorkerCount</code>, capped at twice the limit, with a warning (<code>26e9b3dd</code>). Do not go looking for that behavior: the same author reverted it hours later, having concluded it would trigger constantly (<code>d97e9372</code>), and shipped a callstack dump for a <code>PopWorker</code> stalled past 15 minutes instead (<code>2ab17b2e</code>). The freeze is diagnosed, not yet fixed. And on Apple Silicon build machines, <code>m_memorySize</code> and <code>m_overlaySize</code> were plain <code>u32</code> published across threads with no barrier, which is safe on x86 total store ordering and not on weakly ordered ARM, so a reader could see the new size before the bytes were visible and hand a torn filename length to a routine that crashed inside <code>blake3_hasher_update</code>. Both fields are now <code>Atomic&lt;u32&gt;</code> with explicit acquire and release, plus a bounds assert so a future torn write fails loudly instead of segfaulting deep in a hash function (<code>5fb5c544</code>).</p>
<p>A related Mac farm link collapse traced to file-mapping handles carrying the creator’s raw file descriptor, so a close-then-recycle served the wrong file to a live fetch. Handles now carry a monotonic mapping id resolved through a live-mapping registry, the descriptor server thread no longer exits on transient accept errors, and descriptor fetches get 10 second socket timeouts (<code>3d4eb7a3</code>). Throughput work stayed measurement-driven: stall sampling showed roughly 380 of 400 workers parked in <code>FetchSegments.Send</code>, so receive-stall thresholds now reserve the upper half of each connection’s send budget as headroom and segment fetch batching auto-sizes to two batches per connection instead of a fixed eight (<code>4cce4d3b</code>, <code>75c07a40</code>, <code>bfafea3b</code>). Cold-page faults inside fetch memcpys got an async readahead, with a POSIX <code>madvise(MADV_WILLNEED)</code> implementation added because, as the commit puts it, the production cache servers run Linux (<code>ba435bd5</code>). Trace buffer exhaustion now logs and saves a truncated trace instead of failing silently, with a configurable reserve size (<code>7d10a313</code>).</p>
<p><strong>Why this is important.</strong> Three separate classes of report that read as flaky networking were local resource starvation, and the fix in each case was a detector that renames the symptom rather than a retry that hides it. If you operate a build farm on cloud block storage, the specific finding is worth checking on your own hardware before you assume it is an Epic-scale problem: multi-minute filesystem stalls on healthy-looking instances, near daily.</p>
<p><strong>Who should care.</strong> Build engineers running distributed builds on AWS or equivalent, anyone with Apple Silicon build machines, anyone who has written off recurring build freezes as network flakiness.</p>
<p><strong>Urgency: Act now.</strong></p>
<h3 id="the-cook-was-baking-fresh-random-numbers-into-your-packages">The cook was baking fresh random numbers into your packages</h3>
<p><strong>What changed.</strong> Three cook-determinism fixes landed.</p>
<p><code>UMaterialInstance::Serialize</code> calls <code>CleanUpUnusedParameters()</code> while cooking, which destructively removes unused entries from the instance’s parameter arrays before <code>Super::Serialize</code> writes them. SavePackage serializes each export more than once, a reference-harvest pass and then an export-write pass, and the cooker can save the same instance more than once in a process. Because the strip mutates the live object and is skipped during the reference-collector pass, a later save harvests a smaller parameter set than the first, and the harvested name table then differs between the two saves. <code>IncrementalValidate</code> reported that as a spurious <code>DeclaredUnmodified_FoundModified</code> false positive, described in the commit as the dominant MaterialInstanceConstant spike. The fix snapshots the seven mutated arrays and restores them after serialization, behind <code>r.Material.RestoreStrippedParametersAfterCookSave</code>, default enabled. It shipped with an A/B: with the restore off, six of six stripping instances emit NameMap-is-different and Indeterministic-header-size; with it on, zero of six (<code>68070a5d</code>).</p>
<p>Blueprint RNG nodes in construction scripts were baking a fresh random value into cooked packages on every cook. <code>AActor::ProcessUserConstructionScript</code> already establishes a deterministic cook-seeded scope, but only <code>KismetGuidLibrary::NewGuid</code> consulted it, so <code>RandomIntegerInRange</code>, <code>RandomFloat</code>, <code>RandomRotator</code>, and array <code>Random</code> and <code>Shuffle</code> still drew from the process-global stream. Those nodes now consult the in-scope cook stream when one is active, gated to editor builds, so shipping behavior is unchanged (<code>3bc9b39d</code>, a resubmit of the first attempt <code>9a961ed9</code> after it was backed out in <code>af23eb62</code> for a Linux and Mac compile error, where <code>RAND_MAX + 1</code> overflowed int because <code>RAND_MAX</code> is <code>MAX_int32</code> outside the MSVC runtime). And the shader <code>AssetInfo</code> sort was clamping 8-byte <code>FShaderHash</code> values to 4 bytes via <code>GetTypeHash</code>, which the commit puts at an estimated 99% collision probability at 200,000 elements, so large projects specifically got indeterminate output (<code>8f43677d</code>).</p>
<p>Not a determinism fix but from the same author and the same cook path: <code>FAssetRegistryGenerator::FinalizeChunkIds</code> had pathological <code>TSet</code> reallocations in the package graph, and the commit reports 187s dropping to 1s, taking the enclosing stage from 300s to 114s, measured on a NullCook performance run rather than a production cook (<code>32e19f61</code>).</p>
<p>The material instance area also produced the week’s worst thrash. A commit disabling MIC unused-parameter stripping outright, because it was causing issues with texture parameters used in runtime virtual texture rendering, was submitted, backed out, and then restored by backing out the backout, all on August 12 across two engineers (<code>364f4a0b</code>, <code>e5601e1b</code>, <code>68eaf5d3</code>).</p>
<p><strong>Why this is important.</strong> Every one of these presents as an incremental cook validation failure rather than as an error, which is the mechanism that converts incremental cooks into full ones. If your team has concluded that incremental cook validation is noisy and started ignoring it, the material instance finding is a concrete reason it was noisy, with a measurement attached.</p>
<p><strong>Who should care.</strong> Anyone running incremental cooks at scale, anyone with a content-addressed artifact cache, technical directors who have stopped trusting cook validation output.</p>
<p><strong>Urgency: Act now if you cook material instances or use Blueprint RNG in construction scripts.</strong></p>
<h3 id="version-floors-moved-and-one-of-them-will-stop-your-build">Version floors moved, and one of them will stop your build</h3>
<p><strong>What changed.</strong> The minimum Windows SDK is now 10.0.26100.0, with the commit noting it fixes all in-tree instances of 22621 found through internal code search (<code>05d13aec</code>). The commit does not spell out the consequence for build machines, but a floor is a floor: an image carrying only the older SDK is now below it. Agility SDK 1.619.5 was added and made the default (<code>90ed17ad</code>, <code>4875ceb2</code>), 1.619.3 was removed (<code>8811b76a</code>), and two debug-layer suppressions were dropped because the validation false positives they covered are fixed in that SDK (<code>ab6243fe</code>). AMD AGS went from 6.0.1 to 6.3.1, adding RDNA3 and RDNA4 asic detection and DX12 shader clock intrinsics, and replacing VS2017 static libraries with VS2026 (<code>cd727840</code>). New DXC binaries shipped alongside a VulkanRHI fix for an invalid <code>OpStore</code> to <code>OpTypeImage</code> from <code>Interlocked*</code> on copied resources, which the commit says was causing crashes on some drivers (<code>9bbbbb5c</code>, <code>90a9fcf7</code>).</p>
<p>One toolchain change is worth stealing. Restricting ICF defeating to only the cases AutoRTFM actually needs, rather than applying it blanket, reduced a large server binary by 10.5%. The commit also documents two behaviors the blanket approach was providing by accident, including AddressSanitizer disabling its fake stack for functions containing inline assembly, which the AutoRTFM runtime depended on (<code>1f14b550</code>).</p>
<p><strong>Why this is important.</strong> SDK floor changes are the least interesting and most disruptive category of build change. They do not appear in release notes as features, and they turn into a morning of red CI for whoever syncs first.</p>
<p><strong>Who should care.</strong> Build engineers, anyone maintaining pinned toolchain images, console and Windows platform teams.</p>
<p><strong>Urgency: Act now.</strong></p>
<h3 id="buildgraphs-c-authoring-api-finished-collapsing">BuildGraph’s C# authoring API finished collapsing</h3>
<p><strong>What changed.</strong> Two weeks after the bytecode VM was deleted, the rest of that layer went with it. <code>BgContext</code>, <code>BgThunkDef</code>, and the remaining expression and VM plumbing are gone, the Def-suffixed hierarchy collapses into direct <code>BgGraph</code>, <code>BgNode</code>, and <code>BgAgent</code> types, graph authoring methods moved from <code>BgGraphDefBuilder</code> onto <code>BgAgent</code> and <code>BgNode</code>, authored node delegates now execute directly instead of through reflection-based thunk binding, and unsupported <code>Task&lt;T&gt;</code> node functions are now rejected outright instead of running with their results silently discarded (<code>36037439</code>). All <code>CustomTask</code> implementations migrated to <code>BgTaskImpl</code> and the deprecated base class was removed (<code>61b23381</code>, <code>cf13ef3e</code>).</p>
<p>Horde also spent the week cutting the cost of its own CI. Analyzers moved inside the Docker compile that produces the shipped binary, so only one build runs per change instead of the entire server graph twice (<code>f5da8afd</code>). Interprocedural security rules moved to an opt-in configuration after costing 33 seconds per change while producing zero diagnostics (<code>86e09fb5</code>). Integration tests moved from creating and dropping a MongoDB database per test to a pooled model (<code>775365c6</code>), and test projects now run in parallel (<code>210d5e5d</code>, <code>c4ca5172</code>, <code>3c1c8e40</code>). Tool deployments now precompute a zstd-compressed tar archive stored as a flat blob, served by presigned redirect straight from the storage backend, so the server spends neither CPU nor bandwidth on it and the artifact extracts with plain <code>tar --zstd -xf</code> while still downloading (<code>db4fe1b3</code>, <code>9c50776b</code>).</p>
<p><strong>Why this is important.</strong> The XML authoring surface looks untouched, which is what most studios actually use, though the commit rewrites XML graph reading and execution onto the consolidated model and <code>CustomTask</code> subclasses have to move to <code>BgTaskImpl</code>. If you invested in C# graph authoring, this is the second breaking pass in three weeks, and the API you migrate to has a different shape as well as a smaller surface. The Horde CI cuts are worth reading as a pattern even if you do not run Horde: two of the three were removing work that produced no output.</p>
<p><strong>Who should care.</strong> Anyone authoring BuildGraph in C#, Horde operators, CI engineers looking for cheap wins in their own pipelines.</p>
<p><strong>Urgency: Act now if you author C# BuildGraph. Track otherwise.</strong></p>
<h3 id="the-renderer-decomposition-took-nanite-material-bins-and-distance-fields">The renderer decomposition took Nanite material bins and distance fields</h3>
<p><strong>What changed.</strong> Two more large migrations moved subsystems off explicit <code>FScene</code> calls and onto dirty-mask tracking resolved inside a defined update window. Nanite material bin lifecycle left <code>FScene</code> and <code>FPrimitiveSceneInfo</code>: <code>PrimitivesNeedingStaticMeshUpdate</code> went from a packed-index <code>TBitArray</code> on <code>FScene</code> to an <code>FDataArrayDirty</code> on <code>FScenePrimitiveDataRenderer</code>, a <code>PostStaticMeshUpdate</code> hook collects the rebuild set, dirty marks can be locked for the remainder of an update, and the mutexes were removed because the update task and the render thread no longer touch the same data (<code>7e6eb874</code>, <code>374879a1</code>).</p>
<p><code>FDistanceFieldSceneData</code> became <code>FDistanceFieldSceneExtension</code>, and that migration carried several fixes that have nothing to do with refactoring. Three worth calling out. <code>ShouldPrepareDistanceFieldScene</code> now mirrors <code>ShouldCompileDistanceFieldShaders</code>, so render prep cannot request shaders that are absent from the global shader map when distance fields are disabled. <code>GetDistanceToNearestSurfaceGlobal</code> returns no-surface-in-range instead of distance 0 when no global signed distance field clipmaps exist, which had been pinning Niagara particles at the emitter. And hidden shadow or GI casters keep their distance field while hidden (<code>39fc371d</code>).</p>
<p>The fallout confirms how load-bearing this work is. A use-after-free race had scene extension task handles stored on the scene data object awaited on a worker thread under RDG parallel destruction, racing the render thread replacing handles for the next frame (<code>fa537262</code>). Out-of-band dirty marking from PSO compilation completion asserted on the locked update array (<code>374879a1</code>). A <code>TAtomic</code> was swapped for a transactionally safe mutex to support in-transaction render proxy initialization (<code>37f10060</code>). Separately, occlusion culling got an element-id cache for the occlusion history set, where the lookup had been 43% of sampled cycles on a gen8 mobile console, plus opt-in visible re-test throttling that is off by default, the two together measured at 0.4ms render thread, 0.3ms game thread, and 0.1 RHI (<code>fede2869</code>), and occlusion bounds moved to sparse custom bounds to cut memory (<code>00cdc259</code>).</p>
<p><strong>Why this is important.</strong> Seven consecutive weeks of the same campaign, and the pattern has not changed: explicit add and remove calls become dirty flags resolved in a window, which is what makes parallel scene update safe. If you carry renderer patches, these are real conflicts, and the distance field fixes are worth pulling on their own merits regardless of the refactor.</p>
<p><strong>Who should care.</strong> Rendering engineers, anyone maintaining an engine fork with renderer changes, anyone using global distance fields with Niagara.</p>
<p><strong>Urgency: Track. Act now if you maintain a renderer fork.</strong></p>
<h3 id="lumen-gained-a-reference-path-and-nanite-ray-tracing-stopped-holding-everything-resident">Lumen gained a reference path, and Nanite ray tracing stopped holding everything resident</h3>
<p><strong>What changed.</strong> A new <code>LumenRef</code> subsystem appeared inside the renderer, with its own world cache, initial sampling, light and material sampling, path resolve, and visualization passes, wired into the deferred shading renderer and scene view state (<code>5e889c76</code>), followed by a world cache freeze visualization fix the same week (<code>209c4705</code>). The commit message is two words, “Initial prototype,” and says nothing more.</p>
<p>The rest is measured performance work. Lumen irradiance field gather now reuses downsampled depth and normal from the stochastic lighting tile classification pass, which itself gains Substrate tile size 16 support and caches translated world position in shared memory. The change also swaps some shared arrays from <code>float3</code> to <code>float2</code> plus <code>float</code> because, per the commit, some platforms pad <code>float3</code> to <code>float4</code>, which increases shared memory usage and reduces occupancy. Measured at 0.51ms saved on console at 900p with the Substrate blendable gbuffer, and 0.88ms with MegaLights enabled (<code>ca72133c</code>). Nanite ray tracing now builds CLAS on demand and reclaims CLAS for unreferenced pages instead of holding everything resident, with streaming requests originating from ray tracing flagged through a previously unused bit and LRU ordering maintained for ray-tracing-referenced pages (<code>e8aa742e</code>), and automatic quality scaling now keys off CLAS pool usage (<code>cbeb9852</code>). Lumen reflections gained optional translucency depth during screen tracing so screen traces hit the frontmost depth-writing translucent layer, at roughly 0.15ms fixed cost at 1080p on console (<code>c7740741</code>). CSM shadow depth reuse gained validity-based invalidation and frustum expansion under <code>r.Shadow.CSMShadowDepthReuse</code>, reusing static and far cascade depth until the light rotates or the cascade footprint leaves the frozen, expanded footprint (<code>50747fde</code>). ACES 2.0 transforms migrated to match OpenColorIO’s adaptive hue and cusp construction, packed into a single 363x1 RGBA32F texture (<code>521db891</code>).</p>
<p><strong>Why this is important.</strong> The CLAS change is the practical one: hardware ray tracing memory pressure on Nanite content stops being a function of everything you loaded and becomes a function of what ray tracing actually referenced. The Lumen gather saving is free if you are on Substrate. <code>LumenRef</code> is worth exactly one line in your notes until it does something.</p>
<p><strong>Who should care.</strong> Rendering engineers, technical artists on GI-heavy or hardware ray tracing projects, console teams.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="a-week-of-crashes-that-all-happen-after-the-useful-work-is-done">A week of crashes that all happen after the useful work is done</h3>
<p><strong>What changed.</strong> An unusual share of this week’s crash fixes are teardown crashes. The clearest one: a fatal error raised from an atexit destructor died on already-destroyed function-local statics before it could be reported, exiting with a crash-reporter-crashed code and producing no report at all. The fix makes the thread manager singleton, the log output device, the log-filename lock, the error and feedback context devices, and the ApplicationWillTerminate and memory-pressure delegate accessors immortal, and with the singleton immortal the runnable thread destructor always unregisters, so shutdown crash reports keep every thread’s name and callstack. Note the landing sequence: it landed, was backed out, and was restored with the note that the backout did not fix the continuous-integration failure it was meant to fix (<code>a6d3dc77</code>, <code>a3eda6f3</code>, <code>cfc74eef</code>).</p>
<p>A sweep fixed existing use-after-free instances across ContentBrowser, the Niagara editor, Slate accessibility, the virtual shadow map cache manager, and the console, and then <code>FText::ToString</code> was marked lifetime-bound so static analysis catches the same bug going forward (<code>3a42e0b6</code>, then <code>b93c6cea</code>). Sweep plus annotation is worth copying into your own codebase for this class of bug.</p>
<p>Elsewhere: Control Rig had two concurrency crashes, including <code>InitializeFromCDO</code> reading the shared source hierarchy while also writing to it with nothing serializing it, so instances initializing on anim worker threads corrupted the listening hierarchies array (<code>85d0e5b2</code>, <code>0e9cee80</code>). Async loading’s state dump moved into a commandlet with its use-after-free fixed by taking references rather than relying on package lifetime (<code>d08fb4ca</code>). IoStore container mapped file handles are now kept alive while mapped regions are outstanding, fixing a fatal assert when a game feature plugin unmounts while memory-mapped animation or sound bulk data is still resident (<code>01bc8239</code>). Audio wave instances outliving their active sound left a dangling back-pointer that the staleness check could not see (<code>f9aa5086</code>). And two that are easy to miss: an out-of-bounds read of the hardcoded-name table on Iris name deserialize (<code>841d1f05</code>), and a data race on a bit-packed Visual Logger flag where a concurrent load-mask-store could tear neighboring flags (<code>1b008a1a</code>).</p>
<p><strong>Why this is important.</strong> Shutdown crashes get deprioritized because the work is already done, and the crash reporter fix explains part of why: some of them were never producing a report to prioritize. If your crash telemetry shows a suspiciously clean shutdown path, that may be the reason.</p>
<p><strong>Who should care.</strong> Engine and tools programmers, anyone shipping plugins that unmount at runtime, anyone triaging shutdown crashes or missing crash reports.</p>
<p><strong>Urgency: Track. The Iris deserialize fix is worth a look if you ship networked builds.</strong></p>
<h3 id="also-worth-knowing">Also worth knowing</h3>
<ul>
<li><strong>All subsystems became dynamic subsystems, and <code>UDynamicSubsystem</code> is deprecated.</strong> The driver is merged modules, where dynamic libraries may carry subsystem definitions without game code opting in everywhere. Subsystems in automatically loaded plugins keep their old behavior, explicitly loaded plugins get dynamic behavior, and a new opt-in restores per-module lifetime. It landed, was backed out, and relanded (<code>c11e8cb7</code>, <code>783fba87</code>, <code>9a17b956</code>). Treat it as a source-compatibility item on merge.</li>
<li><strong>Plugin unmount got several minutes faster.</strong> Package unloading no longer loads data unless a rename is actually being performed, tagged for release notes by Epic (<code>5ccdf834</code>). A related fix stops evicted thumbnail scenes from outliving a plugin unload and keeping its packages referenced (<code>ff42b7c2</code>).</li>
<li><strong>D3D12 enhanced barriers got a correctness pass while the default stays legacy.</strong> Layout selection had been picking queue-type-specific layouts from whichever pipe a transition happened to be recorded on, which the commit calls wrong twice over, and shader-resource, unordered-access, and generic-read layouts are now derived from access alone (<code>dd798781</code>). Automatic selection narrowed so it only picks enhanced barriers when the SDK provides the required layout constant (<code>7e0511ae</code>), and a global was exposed specifically so projects can report barrier adoption through their own telemetry (<code>b84bdb47</code>). Correctness fixes plus adoption instrumentation usually come before a default flip.</li>
<li><strong>CTR encryption landed for plugin and collection encryption</strong>, with a deterministic Blake3-derived initialization vector per block so delta patching still works, and the commit says the foundation should later allow a full-project switchover at a one-time patch size cost (<code>aa47a726</code>).</li>
<li><strong>A black screen in UE Remote is fixed.</strong> Signalling server CORS filtering added earlier had a loopback-only allowed-origins default that rejected every UE Remote client, so no codec was ever negotiated (<code>928808b3</code>).</li>
<li><strong>The CSV profiler changed what it measures by default.</strong> <code>csv.UseLegacyFrameTime</code> was renamed to <code>csv.MeasureFrameTimeAtEndFrame</code> and defaulted on, with Windows opting out via ini. Any cross-platform frame-time capture comparison spanning this change is not comparing the same measurement (<code>c3aac366</code>).</li>
</ul>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>The input thread default flipped on and back off inside three hours.</strong> It was enabled by default on August 14 with <code>input.UseInputThread</code> and <code>-NoInputThread</code> as escape hatches (<code>fbd5d79a</code>), and a backout with the identical description landed the same afternoon (<code>3ba05293</code>). We checked the branch: <code>input.UseInputThread</code> still defaults to 0 on <code>ue6-main</code>, so nothing has changed for you yet. Worth watching, because a default-on input thread changes threading assumptions for anything reading input state off the game thread, and Epic has now tried once.</p>
<p><strong>A new experimental geometry plugin called JOULE appeared</strong> at <code>Engine/Plugins/Experimental/JOULE</code>, with core mesh, grid, atlas packing, and cut-face headers. The author describes it as an initial assisted port and a proof of concept to be matured as it is used, and it landed with no reviewer (<code>f7987d2d</code>). The name is the technique: jointly optimized u(v) lattice embeddings, an approach to continuous parameterization with a jointly optimized volumetric step. Two follow-ups the same week are a static analysis pass and a build fix, both housekeeping. The next few weeks of volume will tell you whether this is the seed of a real parameterization pipeline or something that disappears.</p>
<p><strong>An engine-wide logging migration is underway under one ticket.</strong> Roughly eight commits this week move <code>UE_LOG</code> and <code>UE_CLOG</code> onto narrow-format <code>UE_LOGF</code> and <code>UE_CLOGF</code>, including the public wrapper macros and the Visual Logger wrappers, with the wide <code>UE_VLOG_UELOG</code> and <code>UE_CVLOG_UELOG</code> variants deprecated and several per-system logging macros migrated onto the narrow-format replacements (<code>a42421cd</code>, <code>4f1a1165</code>, <code>28e1dbae</code>, <code>22070966</code>). Broad mechanical churn across public headers plus a deprecation means a removal step is coming and merge pain with it.</p>
<p><strong>The modular test framework continued at roughly 20 commits for a third consecutive week</strong>, adding a standalone hub program that mounts a test plugin with its dependency closure (<code>9a258893</code>), a runner UI with warm-pool visibility and per-worker kill (<code>e37999f9</code>), elision of the reset and travel cycle between suites sharing a resolved environment (<code>4b3252da</code>), an external-session worker mode (<code>d5960e48</code>), and a Verse-facing plugin (<code>b6d2c48f</code>). Three straight weeks at that pace makes this a program.</p>
<hr>
<h2 id="for-teams-shipping-on-58">For teams shipping on 5.8</h2>
<p><code>ue5-main</code> ran 55 commits this week, down from 122, and the mix is narrow: animation work dominates once you count UAF, Control Rig, and PoseSearch together, with the rest split between a procedural vegetation export cluster, localization automation, and isolated fixes.</p>
<p>The one to pull is a heap corruption. <code>UControlRig::InitializeFromCDO</code> reads the shared source hierarchy while also writing to it, registering a listening hierarchy and toggling a cache validity flag, with nothing serializing access, so multiple Control Rig instances of the same class initializing concurrently on anim worker threads corrupted the listening hierarchies array (<code>2da7adde</code>, <code>b3af43db</code>). If you run Control Rig on characters and have ever seen unexplained anim-thread instability at scale, this is a candidate.</p>
<p>Also on the branch: UAF variable refactor phase 1 replaces the binary public and private variable flag with input, internal, and local scopes plus access control, where external callers reach input-scope variables only and inputs are read-only inside UAF (<code>5c3106ac</code>). A PoseSearch fatal check on a database and schema race in completion handling was fixed (<code>d6d94482</code>). Lumen hit-lighting picked up inline ray tracing from ray generation shaders and disabled unused permutations (<code>e3cc02de</code>). Movie Render Queue PIE lifecycle issues were fixed, including a Quick Render crash when a render is aborted because a Blueprint does not compile (<code>420759f4</code>). And a use-after-free from binding a const reference to a temporary returned by <code>FText::ToString()</code> was fixed on the platform text entry path, the same class of bug that got the annotation and sweep treatment on <code>ue6-main</code> (<code>de14038d</code>).</p>
<p>No 5.8.x point release is named in this week’s commits on the branch.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>We passed over roughly 480 commits this week: static analysis and warning cleanup passes, localization automation, formatting, test plumbing, and content asset updates that resolve only to binary manifest blobs. That figure includes the 27 commits tagged <code>[Backout]</code>, whose net effect shows up in the changes they landed on or removed.</p>
<hr>
<h2 id="closing">Closing</h2>
<p>A quiet week for anyone shipping content and a loud one for anyone running the machines that build it. Three separate multi-week failures ended the same way: someone added an instrument that names the cause instead of another retry that hides it.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless the claim is in the 5.8 section, which references <code>ue5-main</code>. Some of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-aug-10-16-2026/og.f4b81e9da8.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: Seven Ways a Build Cache Can Lie (August 3 to 9, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-aug-3-9-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-aug-3-9-2026</guid>
      <pubDate>Mon, 10 Aug 2026 07:31:32 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>784 commits on ue6-main, and the most consequential ones are all the same bug: a cache returned a confident answer that was not a function of its inputs, and nothing logged a complaint. The shader job cache dropped keys at every hash-table growth boundary. DDC record bytes changed based on whether debug dumping was on. The UBA cache server ratcheted its own expiration window from three days to four seconds while crashing with SIGBUS in production. Plus an HDR correctness fix that moves pixels, a lifetime check that went fatal, mimalloc as the desktop editor allocator, and a 5.x sidebar showing 5.8.2 being assembled.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-seven-ways-a-build-cache-can-lie">Unreal Engine Weekly: Seven Ways a Build Cache Can Lie</h1>
<p><strong>Week of August 3 - 9, 2026</strong> | <strong>784 commits on <code>ue6-main</code>, 122 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>784 commits, 174 worth reading, and the ones that matter most are all the same kind of bug. A cache returned the wrong answer, or no answer, and nothing failed. The shader job cache silently lost keys whenever its hash table grew. DDC record bytes changed based on whether debug dumping was on, which is deliberately not part of the key. EpicClang build identifiers differed across machines because of line endings. The UBA cache server ratcheted its own expiration window from three days down to four seconds and kept serving traffic. None of these throw. All of them cost you compile time, cache hit rate, or a shared cache whose records disagree with each other.</p>
<p>The rest of the week ran underneath that: two large decompositions kept moving, HDR output changed for real, and a lifetime check went fatal and took a wave of shutdown crashes with it.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<p><strong>Build is the top subsystem this week at 85 high-interest commits, and for the first time in this run the work is about correctness of the cache rather than the speed of the build.</strong> Build has been rising for weeks (50, 76, 71, 98, 87, 61, 85 across the full-volume weeks). The July arc was modernization: C++23 by default, circular dependencies eliminated, EpicClang versioned, the C# BuildGraph VM deleted. This week the same subsystem produced seven independent defects in which a cache, hash, or identifier was not a function of its inputs. Epic is now paying down the correctness debt of the caching layer it spent the summer speeding up. If you run a shared DDC or a distributed cook, this is the week’s actionable content.</p>
<p><strong>The UBA cache server is being hardened under live production failure for the second time in three weeks.</strong> The week of July 20-26 covered a UBA cache server rewrite driven by production pressure. This week is the sequel, and the failure mode escalated from throughput to process death: repeated SIGBUS crashes on a filesystem at 99% (<code>1e557470</code>, <code>65deda8a</code>), maintenance that never compacted busy buckets (<code>3fa18243</code>), and a container-compatibility rewrite of the memory-sharing transport (<code>682d941e</code>). Two consecutive escalations on the same component means Epic is running this at a scale where the old design does not hold. Anyone self-hosting the same component is running the same design.</p>
<p><strong>The renderer decomposition continues, but the pattern has shifted from carving out extensions to changing how they update.</strong> For six weeks <code>FScene</code> work meant moving data into a new scene extension. This week <code>FLumenSceneExtension</code> replaced explicit Add/Update/Remove calls with dirty-state-driven updates (<code>9f87848a</code>), and <code>FRayTracingSceneExtension</code> replaced a <code>bCachedRaytracingDataDirty</code> bit on <code>FPrimitiveSceneInfo</code> with <code>FDataArrayDirty</code> (<code>164a546c</code>). The interesting one is <code>8ffb925b</code>, which moved <code>ISceneViewFamilyExtention</code> derived types from <code>Fork_GameThread</code> to <code>Fork_AnyThread</code> and states plainly that it is groundwork for constructing <code>SceneRenderer</code> on the render thread. Six weeks of extension carving have been heading there.</p>
<p><strong>Backout volume is high and concentrated: 17 explicit backouts plus four first-line reverts.</strong> The Mass module decoupling landed, was backed out, landed, was backed out, and relanded (<code>a09993fd</code>, <code>cd2dd396</code>, <code>8687b20d</code>, <code>a744a595</code>, <code>ba27d4d6</code>). If you track <code>ue6-main</code> closely, this week is a bad week to sync to an arbitrary changelist.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li><code>FShaderJobDataMap::FindOrAdd</code> probed for a free slot before calling <code>Reserve</code>, and <code>Reserve</code> rehashes on block growth, so the key landed off its own probe path and became unfindable. A lost key means every later job with that input hash misses deduplication and the job cache and recompiles for the lifetime of the process. Keys are lost at power-of-two growth boundaries, so larger projects lose proportionally more (<code>40513cd5</code>).</li>
<li>The UBA cache server crashed repeatedly in production with SIGBUS, because pool files grow sparse and writes allocate blocks at page-fault time with no error path. Extents are now reserved at commit time via <code>fallocate</code> (<code>65deda8a</code>, <code>1e557470</code>). Separately, bucket maintenance never won its exclusivity race on busy buckets, so compaction never ran and expiration collapsed from three days to four seconds (<code>3fa18243</code>).</li>
<li>The filmic HDR remap was decoding against the 100 nit SDR reference while paper white normalized against the BT.2408 203 nit reference. Film white sat low and mid-tone chroma drifted, worst on bright lightly saturated colors. If you have graded HDR against current UE6 output, re-check it (<code>ac66493c</code>).</li>
<li>Delegate lifetime reporting is now fatal on non-shipping builds (<code>a63e30fe</code>). Your CI can start failing on code that has been quietly wrong for years, and the same week produced a dense run of shutdown crash fixes with exactly that shape.</li>
<li>mimalloc v3.3.2 is now enabled on Windows, Mac, and Linux for the Editor and standalone programs (<code>bd92d641</code>). Two files, and every editor memory measurement from here is against a different allocator.</li>
<li>On <code>ue5-main</code>, three commits are visibly shaped to stay hotfix-safe and one names the release outright. A 5.8.2 point release is being assembled (<code>d8a2e8e0</code>, <code>c32fe678</code>, <code>463e23cf</code>).</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="the-shader-job-cache-was-dropping-keys-silently-at-every-growth-boundary">The shader job cache was dropping keys, silently, at every growth boundary</h3>
<p><strong>What changed.</strong> <code>FShaderJobDataMap::FindOrAdd</code> probed for a free slot, then called <code>Reserve</code>, then wrote the item at the slot it found earlier. <code>Reserve</code> rehashes when it adds a block, which changes <code>HashTableMask</code>, so the slot no longer lay on the key’s probe path. The key became unfindable, and if that slot was occupied after the rehash, it displaced the key already living there. The probe now happens after <code>Reserve</code>, and the search index is scoped to its loop. The commit also removes <code>FShaderJobDataMap::Find</code> and <code>FShaderJobCache::Find</code>, which had no callers and the same class of bug (<code>40513cd5</code>).</p>
<p><strong>Why this is important.</strong> The commit spells out the cost: a lost key means every later job with that input hash misses both deduplication and the job cache and recompiles, for the process lifetime. Keys are lost on power-of-two block growth, at 512, 1024, 2048, 4096 unique hashes and up. That means the defect scales with your shader count. A small project might never notice. A large project loses a key at every doubling and pays for it until the process exits. Nothing logs, nothing warns, and the symptom is a shader compile step that is slower than it should be for reasons no profile will explain.</p>
<p><strong>Who should care.</strong> Anyone with a large shader permutation count, anyone benchmarking shader compile throughput, anyone who concluded their job cache was underperforming and could not say why. One file.</p>
<p><strong>Urgency: Act now.</strong></p>
<h3 id="the-uba-cache-server-spent-the-week-dying-in-production">The UBA cache server spent the week dying in production</h3>
<p><strong>What changed.</strong> The largest single cluster of the week is one engineer chasing a repeatedly crashing production cache server, and the fixes are architectural rather than defensive.</p>
<p>When the file-mapping backing ladder falls through to disk files (memfd blocked by seccomp in Docker, <code>/dev/shm</code> too small), pool files grow sparse and writes through the mappings allocate blocks at page-fault time. On a full filesystem that fault is a SIGBUS with no error path. The production box showed a pool file with 1TB apparent size and 4KB actual on a filesystem at 99%. Extents are now reserved at commit time via <code>fallocate</code>, so running out of disk surfaces as a failed map plus a logged error instead of killing the process mid-memcpy (<code>65deda8a</code>, <code>1e557470</code>).</p>
<p>Bucket maintenance was broken in two independent ways. Exclusivity was only attempted after a multi-second delete pass, so zero-clients maintenance always lost the race against reconnecting clients and compaction never ran on busy buckets. Meanwhile the overflow squeeze triggered on raw table size, which is mostly dead offsets that only compaction can remove, so it re-tightened expiration every pass against its own wreckage. Observed effect: an expiration window ratcheting from three days to four seconds while the tables never shrank (<code>3fa18243</code>). Gating that squeeze then exposed a second bug where an overflowing but busy bucket got re-queued by every sweep tick, producing a 250ms no-op maintenance run every second, forever (<code>3451f7f2</code>).</p>
<p>Transport changed too. Downloading a 1GB cas file sent one RPC per 256KB segment, which is 4,000 messages each dispatched to its own server worker. Batched segment fetching replaces that with a reserved id train, default 8 segments per batch, with a version handshake so old and new cache servers interoperate (<code>5f8e11d0</code>). Worker threads no longer stay pinned for the whole wire drain (<code>8bf4c31d</code>).</p>
<p><code>pidfd_getfd</code> is gone entirely, replaced by an abstract Unix domain socket handing out memfd descriptors via <code>SCM_RIGHTS</code>, with a <code>SO_PEERCRED</code> uid check, a creator-pid match, and a <code>/proc/self/fd</code> readlink confirming the descriptor is a memfd. The reason is specific: <code>pidfd_getfd</code> requires <code>PTRACE_MODE_ATTACH</code>, which default Docker and Podman seccomp profiles block. The old path silently degraded to <code>/dev/shm</code> or disk inside a container (<code>682d941e</code>). All POSIX platforms moved to anonymous mapping descriptors and the name-based shared memory machinery is deleted (<code>11837995</code>).</p>
<p><strong>Why this is important.</strong> If you self-host UBA and run it in a container, you may have been running a disk-backed cache while believing it was memory-backed. If you run it near a full filesystem, you have a crash loop with no error message. If your buckets are busy, your expiration window may not be the number you configured. All three fail quietly, which is the theme of the week.</p>
<p><strong>Who should care.</strong> Build engineers and CI infrastructure teams self-hosting UBA cache or storage servers, especially in Docker or Kubernetes.</p>
<p><strong>Urgency: Act now.</strong></p>
<h3 id="cache-keys-stopped-depending-on-things-that-are-not-inputs">Cache keys stopped depending on things that are not inputs</h3>
<p><strong>What changed.</strong> Five independent commits, all the same shape: something that should be a pure function of its inputs was not.</p>
<p><code>FinalShaderSource</code> was serialized into ShaderCompileJob DDC records unconditionally, so enabling debug dumping (which is intentionally excluded from the DDC key) changed the record bytes for a fixed key. It now crosses the worker boundary without entering the cache record, and debug dumping was reworked so cache hits still get debug-compilable source (<code>b0070937</code>). Shader debug dumps also stopped differing between two cooks for reasons unrelated to the shader: defines moved out of inlined source comments into a <code>Defines.csv</code> artifact, and hash markers became empty filename-encoded files (<code>60b432af</code>).</p>
<p>EpicClang now normalizes text file line endings when calculating the build hash, which the commit says plainly should keep build identifiers stable across machines (<code>638c39ee</code>). Editor bulk data identifiers are salted with the lowercased owning package name, because byte-identical duplicate packages were sharing persisted identifiers and cache keys (<code>5538b8d6</code>). Nanite fixed build non-determinism for clusters carrying voxels or curves but no triangles, with a note that DDC versions were not bumped because the non-deterministic dwords were never read (<code>1644f558</code>).</p>
<p>And one commit is not a fix at all: an off-by-default console variable that logs the inputs and outcome of the unused-parameter strip gate for each material instance at cook-save time, described as temporary instrumentation to root-cause non-deterministic cooked headers caused by an async-ordering race in cooked resource availability (<code>b4b96e0e</code>). That race is still open.</p>
<p><strong>Why this is important.</strong> None of these produce a wrong build. They produce a cache that disagrees with itself, and that shows up as hit rate rather than as failure. For a studio on a shared DDC, a key that shifts based on whether an engineer had debug dumping on is a cost that gets absorbed as “the cache is just not that good.” The EpicClang and bulk data fixes are the same story at a different layer.</p>
<p><strong>Who should care.</strong> Anyone running a shared DDC, a distributed cook, or content-hash-based caching. Anyone who has ever tried to explain a cache hit rate.</p>
<p><strong>Urgency: Track, and pull the shader DDC fixes if you run a shared cache.</strong></p>
<h3 id="horde-got-two-ci-reliability-fixes-worth-reading-even-if-you-do-not-run-horde">Horde got two CI reliability fixes worth reading even if you do not run Horde</h3>
<p><strong>What changed.</strong> A race condition in the Horde agent from incorrect concurrent use of a single <code>AesGcm</code> context (<code>2ea64825</code>). The storage <code>HttpClient</code> timeout went from the default 100 seconds to 300, because the 100 second ceiling cut the retry policy short and caused job step failures where a transient network error had already been recovered from (<code>0df89cad</code>). Separately, <code>PerforceMaterializer</code> did not correctly sync streams with <code>@CL</code>-pinned imports after an edge-server swap, leaving files stale or missing (<code>2947e107</code>), and UnrealCloudDDC made compact partition writes lock-free (<code>fdbf163d</code>).</p>
<p><strong>Why this is important.</strong> The timeout one is the useful one to steal. A retry policy nested inside a shorter client timeout means the retries never get to run, and the failure surfaces as a flaky job step rather than as a timeout. That pattern is not specific to Horde, and if you have flaky CI steps that recover on manual retry, it is worth checking whether your own client timeouts are shorter than your retry budget.</p>
<p><strong>Who should care.</strong> CI and build infrastructure teams. Horde users should take all four.</p>
<p><strong>Urgency: Act now if you run Horde, track otherwise.</strong></p>
<h3 id="hdr-output-changed-this-week-and-it-is-a-correctness-fix">HDR output changed this week, and it is a correctness fix</h3>
<p><strong>What changed.</strong> The automatic HDR remap of the filmic tone curve was decoding film image against the 100 nit SDR reference while the scene multiplier normalized paper white against the BT.2408 203 nit reference. Film white sat a stop low and mid-tone chroma drifted, worst on bright lightly saturated colors such as pale skin. The remap is now anchored at HDR reference white, decoding to <code>min(203, peak luminance)</code> nits and inverting an output transform whose peak matches (<code>ac66493c</code>).</p>
<p>The inner LUT shaper moved from normalized-PQ index encoding to gamma 2.2, with numbers quoted in the commit: worst-case grayscale interpolation error drops from about 9.0 to about 2.5 10-bit PQ codes at a 4000 nit peak, and from about 1.7 and 4.2 to about 0.4 and 1.0 codes at 1000 and 2000 nit peaks (<code>5f6131cb</code>). A real multi-view performance bug also got fixed: combine-LUT settings caches and ACES 2.0 transform table contexts were function statics shared across every view, so multiple viewports or scene captures rebaked both LUTs and regenerated transform tables every frame. Both now live on the scene view state (<code>e2e9b2d3</code>). The recurring luminance anchors are now named constants (<code>0d458ead</code>).</p>
<p><strong>Why this is important.</strong> Few rendering changes move pixels in shipped content. This one does. If you have graded HDR against current UE6 output, your grade was compensating for a known-wrong white point. The multi-view fix is separately worth having for anyone running scene captures or split screen.</p>
<p><strong>Who should care.</strong> Graphics engineers, technical artists, cinematics and virtual production teams shipping HDR.</p>
<p><strong>Urgency: Act now if you ship or grade HDR.</strong></p>
<h3 id="a-lifetime-check-went-fatal-and-the-same-week-paid-the-bill">A lifetime check went fatal, and the same week paid the bill</h3>
<p><strong>What changed.</strong> Delegate lifetime reporting is now fatal on non-shipping builds, explicitly so detection is caught in general testing rather than only in dedicated tests (<code>a63e30fe</code>). Alongside it came an unusually dense run of shutdown, exit, and cook-teardown crash fixes with one repeated shape: something holds a raw non-owning pointer, or reads a <code>UClass</code> or <code>UScriptStruct</code> after <code>PurgeAllUObjectsOnExit</code> has already destroyed it.</p>
<p>The canonical case is that <code>FModuleManager::UnloadModulesAtShutdown()</code> runs after <code>PurgeAllUObjectsOnExit</code>, so any <code>ShutdownModule</code> body calling <code>StaticClass()-&gt;GetFName()</code> reads freed memory, including indirectly through <code>TSubclassOf&lt;T&gt;</code> (<code>16a52159</code>, <code>234d7bd9</code>, <code>9a1da123</code>). The dangling-delegate variants follow the same pattern: TraceInsights managers registered with <code>AddRaw</code> into a delegate that outlived them (<code>e4b44368</code>), UEFN never unregistering <code>BrowseToAssetOverrideForClass</code> on editor cleanup (<code>cd365d9b</code>), the Sequencer playback client capability holding a raw pointer never cleared when the client actor was collected (<code>0a003158</code>).</p>
<p>One is not editor-only. A use-after-free of <code>FNamedShaderLibrary</code> where a GFP unmount plus deferred <code>CloseLibrary</code> could destroy a library while an InstallBundleManager worker was inside a concurrent pak-mount callback, reproduced from a retail callstack and introduced by an earlier lock-contention change (<code>d2866b8d</code>). Also a use-after-free and a leaked <code>FRunnableThread</code> in <code>FAsyncQueryLatestServerChunkInfo</code> in StorageServerClient (<code>e9cc8c74</code>).</p>
<p><strong>Why this is important.</strong> A fatal check upstream means CI that was green can go red on code that has been wrong for years. Correct outcome, bad timing if it lands on you unannounced. Plugins that touch statics or delegates in <code>ShutdownModule</code> are the highest-risk surface.</p>
<p><strong>Who should care.</strong> All teams. Especially anyone maintaining plugins, and anyone running non-shipping builds in CI.</p>
<p><strong>Urgency: Track, act now if you run non-shipping builds in CI.</strong></p>
<h3 id="two-decompositions-one-backout-storm">Two decompositions, one backout storm</h3>
<p><strong>What changed.</strong> Lumen data and update logic moved out of <code>FScene</code> and <code>FPrimitiveSceneInfo</code> into <code>FLumenSceneExtension</code>, and the significant part is not the move. Explicit <code>LumenAdd</code>/<code>Update</code>/<code>Remove</code>/<code>InvalidateSurfaceCache</code> calls were replaced by a dirty-state-driven updater, with copy-on-write previous values used to skip no-op changes. One behavior delta is worth knowing: world-origin shifts previously did not update the Lumen scene at all, and now do (<code>9f87848a</code>). The same pattern hit cached ray tracing, where the <code>bCachedRaytracingDataDirty</code> bit was replaced with <code>FDataArrayDirty</code> in <code>FRayTracingSceneExtension</code> and lazy per-view update became eager update during renderer scene update (<code>164a546c</code>). A TSAN race where <code>EnableCopyOnWrite()</code> was called from one extension after another had already spawned a read-only task was fixed by restricting it to the registration phase during <code>FScene</code> construction (<code>595ed7ee</code>).</p>
<p>On the gameplay side, MassEntity’s core types are being decoupled from the Engine module ahead of a move to MassCore, with chunk memory size becoming an ini-settable <code>mass.DefaultChunkMemorySize</code>. It landed, was backed out, landed, was backed out, and relanded, with non-unity include breakage found only by a Linux preflight (<code>a09993fd</code>, <code>cd2dd396</code>, <code>8687b20d</code>, <code>a744a595</code>, <code>ba27d4d6</code>). The commit message states 80 files moved and roughly 650 in-tree consumers migrated to new include paths in a single changelist.</p>
<p><strong>Why this is important.</strong> Both refactors compile clean and then break forks at link or at runtime. The Mass one in particular is a migration event for anyone with a Mass-based gameplay layer, and the double backout says it was not smooth for Epic either.</p>
<p><strong>Who should care.</strong> Rendering engineers and engine-fork maintainers for the <code>FScene</code> work. Gameplay tech teams on Mass for the module split.</p>
<p><strong>Urgency: Track. Act now if you maintain a fork with custom scene proxies or a Mass gameplay layer.</strong></p>
<h3 id="shipping-to-devices-got-easier-if-you-were-blocked">Shipping to devices got easier, if you were blocked</h3>
<p><strong>What changed.</strong> Xcode 27 now passes UBT SDK validation: the Apple MaxVersion was raised to 27.9.0, and the Xcode 27 SDK turning SwiftUI’s <code>@State</code> into a macro is worked around by passing Swift macro plugin search paths explicitly, with platform plugins loaded in-process because <code>swift-plugin-server</code> returns a malformed response under Epic’s build executor. Verified against Xcode 26.1.1, 26.3, 26.6, and 27.0 (<code>5a5295cc</code>).</p>
<p>Two Android packaging bugs that only reproduce in Installed or Launcher builds: <code>Steamworks_APL.xml</code> was never staged so Steam-on-Android packaging failed outright, and the APL’s <code>$S(NDKRoot)</code> and <code>$S(HostPlatform)</code> were never registered so <code>libc++_shared.so</code> was silently skipped (<code>d1c9d5ea</code>). A Main OBB in the assets folder was not added into the APK on basic deploy (<code>fc93d654</code>). AndroidFileClient small-file writes double-optimized the path, so a first-call base-directory change was lost and files could be misrouted while the push still reported success (<code>689e1c6d</code>).</p>
<p>On mobile GPU: a Mali-G68 MP5 crash on the Galaxy A54 fixed by adding <code>--simplify-instructions</code> and <code>--eliminate-dead-code-aggressive</code> to the Android SPIR-V driver-patch scalar-replacement pass (<code>624093bc</code>). An occlusion optimization quoting -0.4ms render thread, -0.3ms game thread, and -0.1 RHI landed, with <code>PrimitiveOcclusionHistorySet::Find</code> measured at 43% of sampled cycles on a gen8 mobile console (<code>ba66ef5f</code>). A commit carrying the identical description was backed out the same day (<code>4ce7efd0</code>), so treat that optimization as in flux.</p>
<p><strong>Why this is important.</strong> The Xcode 27 and Android Installed-build fixes are pure unblocks. The AndroidFileClient one is the interesting bug: it reported success while misrouting files, the same silent wrong answer the caching cluster kept producing.</p>
<p><strong>Who should care.</strong> Platform engineers, mobile teams, release engineering.</p>
<p><strong>Urgency: Act now for iOS and Android teams.</strong></p>
<h3 id="the-editors-agent-layer-got-a-permission-model-and-some-api-honesty">The editor’s agent layer got a permission model and some API honesty</h3>
<p><strong>What changed.</strong> The editor-side agent surface (a <code>ModelContextProtocol</code> plugin with an <code>FModelContextProtocolServer</code>, a <code>Toolsets</code> plugin, an EditorToolset, a <code>ToolsetRegistry</code> that generates tool schemas) got the unglamorous work you only do on something you intend to ship.</p>
<p>Session teardown in the MCP server removed sessions without calling <code>Tool-&gt;CancelAsync</code>, leaking tool-held external state on any session DELETE or server shutdown and leaving the parked HTTP request to time out with no response (<code>a11ec7ae</code>). Two schema-honesty fixes: <code>ToolsetRegistry</code> now rejects optional tool params carrying a non-None default, because the generated schema published <code>null</code> while the implementation substituted a value, and <code>find_assets</code> declared <code>max_results</code> as <code>int | None = 100</code>, telling callers the search was uncapped (<code>a6246539</code>, <code>27c219bb</code>). The 80KB <code>read_file</code> limit is gone, and <code>find_assets</code> dropped a redundant in-memory asset registry query that walked the whole UObject heap duplicating the on-disk enumeration (<code>691183cd</code>, <code>d9fec4bc</code>).</p>
<p>The permission work continued from last week: an <code>IToolsetPermissionProvider</code> with <code>GetToolsToHide</code> (<code>1f6f3012</code>), a project-default tool-access permission mode on the agent environment descriptor (<code>1604c0da</code>), and access-level tagging on the skill management tools (<code>11d652a6</code>). A read-only navmesh query toolset landed that registers in cooked dev and test builds, not just the editor (<code>19f14abe</code>).</p>
<p><strong>Why this is important.</strong> Two schema fixes in one week, both about the published contract lying to the caller, plus request cancellation on teardown. Epic is maintaining this like a shipping API. The permission work is the second consecutive week of access control rather than capability, and a toolset registering in cooked dev builds moves the agent layer out of the editor.</p>
<p><strong>Who should care.</strong> Tools engineers, pipeline TDs, anyone evaluating LLM-driven editor automation.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="mimalloc-is-now-the-editor-allocator-on-all-three-desktop-platforms">mimalloc is now the editor allocator on all three desktop platforms</h3>
<p><strong>What changed.</strong> mimalloc v3.3.2 enabled on Windows, Mac, and Linux for the Editor and standalone programs. Two files, <code>Core.Build.cs</code> and <code>MallocMimalloc.cpp</code>. Test notes list multi-process cooks, editor perf tests, and comprehensive editor tests (<code>bd92d641</code>).</p>
<p><strong>Why this is important.</strong> Two files, and it invalidates your baselines. Every editor memory measurement, fragmentation report, and out-of-memory repro from this changelist forward is against a different allocator. If you are carrying an allocator-sensitive bug or a memory budget you defend in CI, this is the commit that moves the numbers.</p>
<p><strong>Who should care.</strong> Anyone tracking editor memory, anyone with allocator-sensitive tooling, anyone about to file a memory regression against <code>ue6-main</code>.</p>
<p><strong>Urgency: Track.</strong></p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>“Ness”, a serialization runtime nobody is talking about.</strong> A module tree at <code>Engine/Source/Runtime/Ness/NessUObject/</code> is accumulating steady work from three engineers: property binding factories, a linker with <code>FLinkerPropertyBatch</code>, a roundtrip test, a diff implementation, and a <code>-strictsave</code> argument that skips saving and loading UObject payloads with incomplete Ness support while emitting a report of structs that are not yet compatible plus a skipped-object count. Test tags on one commit read “lyra: nesspacks, ness roundtrip.” The expansion of the name appears nowhere in the commit data, so treat it as a literal token. A <code>-strictsave</code> mode, a compatibility report, and validation against Lyra add up to a serialization replacement mid-migration. Helper libraries do not need any of that. If Ness is what it looks like, it eventually touches every cooked package and every DDC key in the engine. Nothing to act on. The struct-compatibility report is the thing to watch week over week (<code>ffb3a7a5</code>, <code>bc0481c8</code>, <code>71f405ca</code>, <code>f22d0ce7</code>, <code>02efb0f5</code>, <code>3e0e8c6b</code>, <code>04b50198</code>, <code>06609cc5</code>).</p>
<p><strong>ModularTestFramework is a second automation stack, and it now has a UI.</strong> Roughly two dozen commits this week in <code>Engine/Plugins/ModularTestFramework/</code> and <code>Engine/Plugins/Editor/ModularTestRunnerEditor/</code>, almost all rated low or medium individually. Together they are a full test runner: dockable per-run tabs that freeze into snapshots on completion, a run-config tab that displays backend consequences, a multi-process backend with a warm pool that pre-spawns client slots from the resolved test set’s declared count rather than a hardcoded command-line knob, per-run overrides for timeout and environment recycling, and a resolver API that mounts and unmounts test companion plugins through per-resolver dispatch. One commit is explicit that it skips “the foreign FAutomation test walk” when dispatching editor runs, so this is running beside the legacy automation framework, not replacing it yet. The multi-process backend with declared client counts is aimed squarely at flaky multiplayer tests. Worth watching for whether it becomes the recommended path (<code>86d33ee8</code>, <code>f486ed2d</code>, <code>cf9d90bf</code>, <code>2f9ded31</code>, <code>caf1ed1f</code>, <code>bc0edb00</code>, <code>de268c78</code>, <code>f6526778</code>, <code>72f13bcd</code>, <code>219b85a8</code>, <code>4cf52b4a</code>, <code>dc53b6ed</code>, <code>b9c3611e</code>, <code>e8291790</code>, <code>6689ab01</code>, <code>e168a4f9</code>).</p>
<p><strong>SceneGraph is starting to work without proxy actors.</strong> The SceneGraph and TEDS commits stopped being infrastructure this week and started being behavior. Duplicate, paste, and alt+drag now work in edit-in-place without proxy actors, and <code>FParticleSystemAssetComponentEditorUtilities</code> dropped its proxy-actor reliance. Proxy actors are the compatibility shim that lets the existing editor manipulate SceneGraph entities. Every commit that removes a dependency on them moves the editor closer to operating on entities natively. That transition eventually changes what a level means for tooling, source control diffing, and anything that walks actors (<code>3a349b8b</code>, <code>ab2e0e3e</code>, <code>10254923</code>, <code>2e1bc001</code>, <code>24bb792b</code>, <code>cfb73504</code>, <code>73fc199f</code>, <code>9346b10f</code>, <code>f41b3299</code>).</p>
<p><strong>One merge hazard to plan around.</strong> A sweeping <code>UE_LOG</code> to <code>UE_LOGF</code> migration landed, described in its own message as only <code>TEXT()</code> removal, <code>%s</code> to <code>%ls</code>, and <code>%hs</code> to <code>%s</code> (<code>11593724</code>). Mechanical does not mean cheap: for any studio carrying engine modifications, a tree-wide macro and format-specifier migration is a merge-conflict event. Schedule it rather than discover it.</p>
<hr>
<h2 id="for-teams-shipping-on-58-the-ue5-main-sidebar">For teams shipping on 5.8: the <code>ue5-main</code> sidebar</h2>
<p>122 commits, 118 after noise filtering, against 784 on <code>ue6-main</code>. 54 of those 118 carry <code>#Synced-CL</code> integration markers, so this branch is still taking integrations rather than sitting frozen.</p>
<p><strong>A 5.8.2 hotfix is being assembled.</strong> Three commits are visibly shaped by hotfix constraints, which is a stronger signal than any of the fixes individually. One names the release outright: “Note that fix is made to be HotFix compatible for 5.8.2 and a more robust fix may be required for future releases,” and carries a <code>#p4v-cherrypick</code> marker (<code>d8a2e8e0</code>). Another states the constraint plainly while removing an ensure when hashing User Defined Structs with unhashable members: “To avoid changing the hash in a hotfix, the real fix will be pushed in Main” (<code>c32fe678</code>). A third is simply titled “Hotfix for modular vehicle crash” (<code>463e23cf</code>). Two engineers independently narrowing their fixes to stay hotfix-safe points the same way.</p>
<p><strong>Bugs that only exist in cooked builds.</strong> The highest-value cluster on this branch, because none of these reproduce in PIE. Anim Mixer suppressed Force Root Lock through <code>FAnimExtractContext::bIgnoreRootLock</code>, which is <code>WITH_EDITOR</code> only, so cooked builds applied the lock during decompression and published the locked constant as root motion. Sections using an anim sequence with both Force Root Lock and Enable Root Motion produced no root motion at all in cooked builds (<code>f3ba9867</code>). A Mover fix is explicitly labeled a regression from 5.7: the stale spring-state reset keys off a blackboard flag only a locally ticking simulation sets, so interpolated sim proxies discarded replicated spring state and restarted every trajectory prediction from zero acceleration, visibly lagging motion matching (<code>4712c3cc</code>). Function references stamped with a stale variant Guid failed to resolve in cooked builds while the editor’s event-name fallback hid it (<code>11a5e03d</code>).</p>
<p><strong>Toolchain and packaging.</strong> Xcode 27 support landed here too (<code>92b559b5</code>), plus Android TargetSDK 36 (<code>20741414</code>), the Main OBB packaging fix (<code>3846da3f</code>), and Steam-on-Android for Installed and Launcher builds (<code>1a077aa5</code>). Two UnrealBuildTool manifest bugs shipped: generated UHT outputs missing from installed builds (<code>f246278c</code>) and UHT input cache files wrongly counted as build products (<code>be3a7b30</code>).</p>
<p><strong>Nanite determinism, on this branch too.</strong> The same fix class as <code>ue6-main</code>: assertion fixes for assemblies with Keep Triangle Percent below 100 or Trim Relative Error above 0, plus a build non-determinism fix for voxel and curve clusters (<code>5257e679</code>). Pull it if you cache cooked Nanite output.</p>
<p><strong>Also worth pulling regardless of hotfix timing:</strong> a quick fix removing <code>SupportsNoOverwriteBuffers()</code> on bone and cloth buffers, which addresses motion blur corruption seen in the wild (<code>b3108674</code>), and a DFAO change that marks sky pixels with negative SceneDepth and rejects them during upsampling, replacing behavior the commit describes as causing a lot of flickering in dark regions (<code>50bf95d2</code>).</p>
<p>Nothing about this branch reads frozen. It keeps taking integrations while a point release gets staged underneath it. New work is landing too: a large editor docking refactor giving every TabManager-managed window a status bar and panel drawer (<code>7cc08fff</code>), a Dynamic Wind tooling series, and a seven-commit Control Rig constraints pass.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>563 of the 737 analyzable commits, plus the 47 filtered as noise before analysis. Mostly localization automation, README additions, tooltip and comment polish, static analysis warning cleanup, test asset repairs, and the ordinary weekly tail of single-file crash fixes in narrow plugins. Also 17 explicit backouts and four first-line reverts, counted in the trends section rather than reported individually. None of it changes runtime behavior, build reliability, or upgrade risk for production teams.</p>
<p>Two from that tail are worth naming anyway because they affect shipped content: a Nanite assembly build crash with Keep Triangle Percent below 100 (<code>39c00366</code>), and a DFAO flicker fix in dark regions (<code>c1cc531e</code>).</p>
<hr>
<h2 id="closing">Closing</h2>
<p>The crashes were the least interesting thing that happened this week. Seven separate places returned a confident answer that was not a function of its inputs, and nothing anywhere logged a complaint. That class of defect never shows up in a crash report or a failing test. It shows up as a build that is slower than it should be, a cache hit rate nobody can explain, and a grade that was compensating for a wrong white point.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless the claim appears in the 5.x sidebar, which references <code>ue5-main</code>. Many of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-aug-3-9-2026/og.9ddfd06039.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: The Week Epic Made Its Failures Talk (July 27 to August 2, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-jul-27-aug-2-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-jul-27-aug-2-2026</guid>
      <pubDate>Mon, 03 Aug 2026 05:50:07 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>822 commits on ue6-main and 77 of the 165 worth reading are crash, hang, deadlock, or teardown fixes. The pattern connecting them is diagnostics: the crash reporter can now report its own death, a commandlet was built to fingerprint Nanite builder output because Epic suspects it is not deterministic, and a test harness turned an 1800-second silent hang into a bounded failure. Plus a live auth bypass in the PixelStreaming2 signalling server, the C# BuildGraph VM deleted outright, Metal 4 as a full second backend, and a 5.x sidebar for studios shipping on 5.8.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-the-week-epic-made-its-failures-talk">Unreal Engine Weekly: The Week Epic Made Its Failures Talk</h1>
<p><strong>Week of July 27 - August 2, 2026</strong> | <strong>822 commits on <code>ue6-main</code>, 254 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>A stabilization week with two large refactors running underneath it. 77 of the 165 commits worth reading on <code>ue6-main</code> are crash, hang, deadlock, or teardown fixes, and what connects them is diagnostics rather than volume. Epic kept taking failures that were silent, misattributed, or undiagnosable and making them loud and specific. The crash reporter learned to report its own death (<code>394ead63</code>). Epic built a commandlet that fingerprints Nanite builder output so two changelists can be diffed, because it suspects the builder is not deterministic (<code>10496625</code>). A test harness converted an 1800-second silent hang into a bounded failure that reports in about 240 seconds (<code>e36e274c</code>). UBT stopped printing only the two endpoints of a broken plugin dependency and started printing the whole chain (<code>cc39075c</code>). The counterexample is a one-file commit nobody would stop on: enabling the HTTP server discarded the connection origin filter, leaving the allowed-origins allowlist unenforced on the PixelStreaming2 players signalling server (<code>a62fabd2</code>). A live auth bypass that existed because a failure was quiet.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<p><strong>FScene decomposition has escalated at every full-volume week since early June, and it is no longer additive.</strong> Last week it broke public API. This week it changed the storage model itself. <code>24640f93</code> converts FScene’s packed parallel arrays (transforms, bounds, occlusion, visibility ids, proxies, scene infos, flags, component ids) to <code>TDataArrayDirect</code> keyed by persistent primitive index inside <code>FScenePrimitiveDataRenderer</code>. Frustum culling now iterates the id-allocator chunk mask, with 64-id chunks mapping to two visibility-map words, and per-view <code>HiddenPrimitivesMap</code> and <code>ShowOnlyPrimitivesMap</code> bit arrays replace per-primitive <code>TSet</code> lookups in the cull loop. Epic carved out two more extensions the same week: reflection environment data into <code>FReflectionEnvironmentSceneExtension</code> (<code>a04f63cf</code>) and the indirect lighting cache plus precomputed light volumes into <code>FIndirectLightingSceneExtension</code>, gated on <code>IsStaticLightingAllowed()</code> (<code>4b44255b</code>). Epic describes the reflection environment move as carrying minimal behavioral change; the indirect lighting move makes no behavior claim either way. The deletions tell you the direction of travel: unused scene proxy APIs (<code>8919180a</code>), unused <code>AddStaticMeshes</code>/<code>UpdateStaticMeshes</code> parameters that “would now lead to unexpected state if used” (<code>8feb62fc</code>), and deprecated HWRT code in <code>FPrimitiveSceneInfo</code> (<code>07f66585</code>).</p>
<p><strong>Build and toolchain work has been running for months: modernization, then correctness under failure, and this week outright deletion of dead layers.</strong> The BuildGraph bytecode VM is gone: the compiler and interpreter plus the graph builder path on July 28 (<code>06683fd7</code>, <code>010bbd83</code>), then the entire expression tree in a 27-file cut two days later (<code>187ddf19</code>). Separately, the bundled DirectX SDK is retired in favor of the Windows SDK across 76 files (<code>9d6d218d</code>). Neither adds capability. Both remove a layer Epic no longer wants to carry, and that only happens late in a modernization program.</p>
<p><strong>The agent toolset arc is now four distinct phases deep: prototype, then crash hardening, then escaping the editor into cooked runtime, and this week a permission model.</strong> Twelve commits across July 30 and 31 tagged the toolset families with an <code>AIAccessMode</code> of read, write, or execute, after the ToolsetRegistry schema gold values were updated for <code>annotations.accessMode</code> days earlier (<code>ccd0297c</code>). Nothing this week enforces those access modes yet. What did land is a separate restriction layer, a shared property-write gate backed by <code>FPropertyEditorPermissionList</code> (<code>98a0cf13</code>), and a per-conversation undo manager with a dedicated transaction buffer (<code>969825c7</code>). Typed access levels and rollback semantics show up when someone expects the toolset pointed at a real project.</p>
<p><strong>On volume: the weeks of June 29 and July 6 were Epic’s summer shutdown, not a slowdown in the program.</strong> Exclude both weeks when you read the multi-week curves, or you will misread a holiday as a deceleration.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>The PixelStreaming2 players signalling server was silently ignoring its <code>AllowedOrigins</code> allowlist whenever the HTTP server was enabled, which is a live auth bypass and the single cheapest thing to fix this week (<code>a62fabd2</code>).</li>
<li><code>FScene</code> converted its packed parallel arrays to persistent-primitive-index keyed storage and removed <code>FSceneInterface::GetPrimitiveSceneProxies</code> and <code>GetScenePrimitiveComponentIds</code>, so renderer forks will not compile without work (<code>24640f93</code>).</li>
<li>The C# BuildGraph expression API is gone: if you wrote C# BuildGraph scripts against the <code>BgExpr</code> family, they are dead on <code>ue6-main</code> (<code>187ddf19</code>, <code>010bbd83</code>). The XML path survives, but it is not untouched. The legacy <code>&lt;Notify&gt;</code> and <code>&lt;Report&gt;</code> elements and the <code>-ReportName</code> and <code>-ShowNotifications</code> switches were removed the same week (<code>301f225b</code>).</li>
<li>Metal 4 landed as a second RHI backend alongside Metal 3, selected once at device init and gated by <code>rhi.Metal.EnableMetal4</code> and <code>rhi.Metal.ForceMetal3</code>, with the third-party bring-up shipping the same week (<code>8a469fb3</code>, <code>51da534b</code>).</li>
<li>On <code>ue5-main</code>, cook validation found that roughly 800 ControlRig and RigVM packages were being re-flagged on every incremental cook, and healed values got re-flagged with new values on the next run (<code>acf33841</code>).</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="a-live-auth-bypass-in-the-pixelstreaming2-signalling-server">A live auth bypass in the PixelStreaming2 signalling server</h3>
<p><strong>What changed.</strong> Enabling the HTTP server discarded the connection origin filter, so the <code>AllowedOrigins</code> allowlist went unenforced on the players signalling server (<code>a62fabd2</code>, UE-386542). The fix is small. The exposure was not. Separately, <code>715723eb</code> hardens the MP4 box parsers against malformed box data across 8 files.</p>
<p><strong>Why this is important.</strong> If you run pixel streaming with a public endpoint and configured an origin allowlist, you may have been trusting a control that was not running. Nothing failed loudly. The filter ran on every connection, and its rejection was discarded before it could take effect.</p>
<p><strong>Who should care.</strong> Anyone shipping PixelStreaming2 with a public or semi-public signalling server. Media pipelines ingesting untrusted MP4 content should pick up <code>715723eb</code> as well.</p>
<p><strong>Urgency: Act now.</strong></p>
<h3 id="fscenes-scene-data-is-being-rebuilt-underneath-you">FScene’s scene data is being rebuilt underneath you</h3>
<p><strong>What changed.</strong> <code>24640f93</code> touches 52 files and converts transforms, bounds, occlusion bounds, visibility ids, proxies, scene infos, flags, and component ids to <code>TDataArrayDirect</code> keyed by persistent primitive index in <code>FScenePrimitiveDataRenderer</code>. Frustum culling iterates the id-allocator chunk mask instead of a dense array. Per-view hidden and show-only bit arrays replace <code>TSet</code> lookups inside the cull loop. <code>FSceneInterface::GetPrimitiveSceneProxies</code> and <code>GetScenePrimitiveComponentIds</code> are removed outright. The change reaches Lumen, Nanite, PCG, HairStrands, and the culling paths. Two scene extensions split out the same week (<code>a04f63cf</code>, <code>4b44255b</code>), <code>76539c2a</code> adds an <code>IsDrawnChanged</code> dirty array explicitly staged for downstream use, and <code>ae5556ad</code> routes transient renderer arrays through <code>SceneRenderingAllocator</code> to cut per-frame malloc churn.</p>
<p>One real correctness fix hides inside the refactor. <code>ea0392bf</code> replaces inherited task-access-context propagation in SceneData with an explicit copyable <code>FTaskAccessHandle</code> captured via <code>CaptureTaskAccess()</code> and opened with <code>FTaskAccessScope</code>. Under the old inherited model, any task launched from a managed task body could transitively capture the external access table and outlive the contexts it pointed at, producing a dangling-context crash in <code>FBaseAccessContext::BeginReadAccess</code>.</p>
<p><strong>Why this is important.</strong> Two removed public accessors on <code>FSceneInterface</code> are enough to break a fork on sync. The storage-model change is deeper than that: if you have custom culling, custom primitive iteration, or anything that walked FScene’s parallel arrays by dense index, the assumption that index N in one array corresponds to index N in another no longer holds the way it did.</p>
<p><strong>Who should care.</strong> Anyone maintaining a renderer fork, custom scene extensions, or engine-side culling modifications. Note that <code>c2f51354</code> moved scene data headers to Engine and RenderCore public to unblock a Renderer refactor and calls the arrangement explicitly temporary, so do not build against it.</p>
<p><strong>Urgency: Act now</strong> if you maintain a renderer fork. Track otherwise.</p>
<h3 id="the-c-buildgraph-vm-is-deleted-and-the-directx-sdk-is-retired">The C# BuildGraph VM is deleted and the DirectX SDK is retired</h3>
<p><strong>What changed.</strong> BuildGraph’s bytecode virtual machine is gone. <code>06683fd7</code> deletes <code>BgCompiler.cs</code> and <code>BgInterpreter.cs</code>. <code>187ddf19</code> removes the entire expression tree across 27 files: <code>BgExpr</code>, <code>BgBool</code>, <code>BgInt</code>, <code>BgString</code>, <code>BgList</code>, <code>BgEnum</code>, <code>BgObject</code>, <code>BgFileSet</code>, <code>BgFunc</code>, <code>BgThunk</code>, <code>BgType</code>, <code>BgAgent</code>, <code>BgNode</code>, <code>BgAggregate</code>, <code>BgLabel</code>, <code>BgAnnotation</code>, <code>BgGraph</code>, plus <code>BgOpcode.cs</code>, <code>BgBytecodeWriter.cs</code>, <code>BgObjectDef.cs</code>, and the bytecode-only serializers. <code>010bbd83</code> removes the <code>BgGraphBuilder</code> path. C# BuildGraph scripts now construct a <code>BgGraphDef</code> directly through <code>IBgGraphDefBuilder.CreateGraphDef()</code>. The XML path survives, but it is not untouched. <code>301f225b</code> removes legacy <code>&lt;Notify&gt;</code> and <code>&lt;Report&gt;</code> support, inert under Horde because <code>ExportForHorde()</code> never serialized them, with node-level <code>NotifyOnWarnings</code> surviving. <code>aaed92dc</code> collapses <code>BgOptionDef</code> to its string variant.</p>
<p>Separately, <code>9d6d218d</code> retires the bundled DirectX SDK in favor of the Windows SDK: 76 files, 117,916 lines, deleting every bundled DirectX import library and the whole <code>Engine/Source/ThirdParty/Windows/DirectX/Include/</code> header tree, switching DX11, DX12, DirectSound, and Text3DEditor to <code>PublicSystemLibraries</code>. <code>Commit.gitdeps.xml</code> drops 77 lines. <code>XInput.lib</code> stays bundled, with a stated reason: SDKs older than 10.0.28000.0 ship an import library whose exported <code>DllMain</code> symbol <code>ld-lld</code> resolves incorrectly. Related cleanup landed in <code>08151c83</code> (DX11Audio third-party module removed), <code>ea46e9da</code> (unused <code>dxerr.lib</code> linkage dropped from Electra media modules), <code>2f39ad72</code>, and <code>921dfcf3</code> (writable DirectX path properties marked obsolete).</p>
<p><strong>Why this is important.</strong> Both change a contract you build against. The BuildGraph deletion breaks source compatibility for anyone who invested in C# graph authoring. The DirectX change alters what a Windows build machine has to have installed, and which version.</p>
<p><strong>Who should care.</strong> Build engineers, anyone maintaining custom BuildGraph automation, and anyone who owns Windows build machine provisioning.</p>
<p><strong>Urgency: Act now</strong> for build engineers.</p>
<h3 id="ubt-and-the-toolchain-keep-tightening-with-a-concrete-compile-time-win-to-copy">UBT and the toolchain keep tightening, with a concrete compile-time win to copy</h3>
<p><strong>What changed.</strong> <code>8a5771cb</code> is the item worth copying directly. Two include-hygiene cuts identified from ClangBuildAnalyzer captures of UnrealGame Win64 Development rebuilds under clang 20.1.8. <code>PBDRigidsSolver.h</code> dropped from 2455ms to 1210ms average parse, a 43 percent machine-normalized improvement across 29 unchanged consumers, and 71.2 seconds to 35.1 seconds in total. <code>PBDRigidsEvolutionGBF.h</code> fell out of the top-ten most expensive headers. Both removed includes stay behind <code>UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_9</code>, the first clear marker of where the next deprecation gate sits.</p>
<p>Around it: target rules moved onto source-generated readonly wrappers (<code>cf9576a9</code>, <code>c5183cac</code>, the second noting Horde preflights were not rebuilding UAT and were “leading to spurious failures”), duplicate target names now detected with a best-fit pick or an early error (<code>98b0f444</code>), full plugin dependency chains reported in sealed, disallowed, and deprecated validation errors instead of just the two endpoints (<code>cc39075c</code>), full paths allowed in <code>ModuleRules.PublicDelayLoadDLLs</code> on Windows (<code>ff421d8d</code>), and the old UBT solution file deleted (<code>a4d3107e</code>). On Linux, <code>a6887a39</code> fixes hot reload failing on the import-library stub response file by routing stub creation through a <code>LinkImportLibrary()</code> override, so the stub becomes a declared module output. <code>a0243626</code> fixes three separate LinuxServer problems: merged modules needing the <code>lib&lt;exe&gt;.so</code> soname form, the merged-module check for EOSSDK missing the Server target type, and a shutdown crash from global destructor ordering.</p>
<p>Cook and packaging picked up two items. <code>67274713</code> adds an <code>IoChunkAssigner</code> encapsulating IoChunk splitting, so game teams can route bulkdata payloads to arbitrary containers without deep IoStore knowledge, and it auto-updates the package header’s <code>BulkDataMap</code>. <code>16646740</code> downloads previous chunk assignments and writes a comparison diff of assets against prior cooks.</p>
<p>The shader preprocessor (not the shader compiler backend, and not the Verse compiler) got real memory-safety fixes in the vendored stb_preprocess C code (<code>0c116a4d</code>): an unbounded reverse whitespace walk that ran off the front of the buffer into the preceding array header, <code>int</code> length and capacity fields against a <code>size_t</code> API producing near-<code>SIZE_MAX</code> allocations, mis-ordered SSE read padding, and <code>copy_argument</code> failing to write back on three EOF paths. <code>5248e080</code> adds unit tests derived from live crash-log asserts, and <code>31a36269</code> dumps the pipeline job input hash alongside the stage hash in pipeline shader debug info.</p>
<p><strong>Why this is important.</strong> The 43 percent header parse reduction is a template for work you can do yourself today with the same tooling. The <code>UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_9</code> gate is advance notice.</p>
<p><strong>Who should care.</strong> Build engineers, anyone with long iteration times on Chaos-dependent modules, and Linux server teams.</p>
<p><strong>Urgency: Act now</strong> on the include hygiene technique. Track the rest.</p>
<h3 id="the-crash-reporter-learned-to-report-its-own-death">The crash reporter learned to report its own death</h3>
<p><strong>What changed.</strong> When the crash reporting thread crashed while handling a crash, every diagnostic for the original crash vanished and exit code 777003 was the only signal you got. <code>394ead63</code> writes a last-resort minidump from the inner exception filter using only Win32 APIs and static storage. Because the original crashing thread is still parked in its own exception filter waiting on the reporting thread, a single dump captures both the original crash and the reason the reporter died. Stack-overflow crashes skip it outright. <code>8cd588f2</code> makes monitor-mode CrashReportClient attach that dump to the synthetic AbnormalShutdown report, only accepting dumps timestamped after the monitor started so stale dumps are never picked up, and translates the exit code to <code>777003 (CrashReporterCrashed)</code>. <code>22378fb0</code> stops the crash-time thread callstack walk from dereferencing threads destroyed during exit, which was itself crashing the crash handler.</p>
<p>Teardown ordering and lock inversion got a matching sweep. <code>ae9683b9</code> fixes a server hang where <code>DumpAllocatorStats</code> grew a 256-element inline array while holding <code>ClassesLocks</code>, letting a layered malloc re-enter and deadlock. The commit notes servers hit this reliably because one size class commonly holds well over a thousand pools. <code>7409fd9a</code> fixes a POSIX PTY shutdown deadlock in the Terminal plugin with a self-pipe wakeup, because on Linux the reader’s own reference keeps the open file alive, so closing the master descriptor signals nothing. <code>723ecc38</code> stops <code>AddConsoleObject()</code> from deleting a replaced console object that a hot-reloaded module’s static <code>FAutoConsoleObject</code> will later pass to <code>UnregisterConsoleObject()</code> at process exit. <code>52736778</code> returns <code>ELoaderType::NotInitialized</code> instead of dereferencing a destroyed async loader during the final purge.</p>
<p>Sequencer got a null-guard sweep across the week from two authors, five of the seven landing on July 30 and 31: Universal Object Locator fragment widgets polling property handles for objects that are gone (<code>0d5ba46e</code>), bound objects with no level where a spawnable template is outered to the <code>UMovieScene</code> (<code>6812bdad</code>), a fatal assert when a track has no registered track editor, seen in the field with a <code>UMovieScenePropertyTrack</code> added through the scripting API (<code>6ac6cecc</code>), <code>AGroupActor::PostLoad</code> dereferencing a null level after conversion to a spawnable (<code>7d8a7f4a</code>), animation with no skeleton (<code>87b69159</code>), keying object-reference array elements read through the enclosing <code>FArrayProperty</code> plus soft object refs being keyed as a null resolved <code>UObject*</code> (<code>ce2539f6</code>), and <code>FValueCache</code> holding its owner as a raw <code>UObject*</code> so every cache built for an actor’s components and instanced subobjects dangled on Blueprint reinstancing (<code>7081461e</code>).</p>
<p><strong>Why this is important.</strong> Exit code 777003 with no dump is the worst crash report you can get. It tells you a crash happened and nothing else. If your telemetry has a bucket of unexplained abnormal shutdowns, this change turns part of that bucket into readable data.</p>
<p><strong>Who should care.</strong> Anyone operating crash telemetry at scale, dedicated server teams, and Sequencer-heavy productions.</p>
<p><strong>Urgency: Track.</strong> Act now if the Sequencer fixes match crashes you are already seeing.</p>
<h3 id="the-ai-editor-toolset-gets-a-permission-model-and-per-conversation-undo">The AI editor toolset gets a permission model and per-conversation undo</h3>
<p><strong>What changed.</strong> Around 30 commits touch the Python <code>editor_toolset</code> tree, the ToolsetRegistry plugin, and an AIAssistant plugin. About a third of them resolve only to <code>Commit.gitdeps.xml</code>, so the Python-side work is invisible in the public tree. The largest single source-visible change is <code>969825c7</code> in <code>Engine/Plugins/Experimental/AIAssistant</code>, which adds <code>FAgentUndoManager</code> with two modes. In global stack mode, agent edits are ordinary transactions and a reject unwinds back to the index at the agent’s first tool call, taking any interleaved user work with it. In per-agent stack mode, each tool call runs inside a capture window with a dedicated transaction buffer swapped over <code>GEditor-&gt;Trans</code>, teeing pre-state into a reinstancing-safe tagged record store, with a per-object guard so objects the user hand-edited afterward are skipped on rollback. The commit explains why it cannot simply unwind the buffer: flip-applying engine records can crash on reinstanced objects. It carries two backout tags, ships 1,117 lines of implementation against 1,003 lines of test, and is wired up in <code>3a4df67e</code>.</p>
<p>The access-mode sweep ran across July 30 and 31 in twelve commits, tagging AI-callable functions with <code>AIAccessMode</code> values of read, write, or execute: EditorToolset (<code>f53a203a</code>), SlateInspectorToolset (<code>ead4ec67</code>), physics and simulation (<code>f0c64976</code>), Gameplay Ability, Tags, and Features (<code>c18941fc</code>), UMG and MVVM (<code>46e75609</code>), PCG (<code>afcdd21c</code>), Niagara (<code>b70033ad</code>), StateTree, WorldConditions, Conversation, and AIModule (<code>8f4d4ba3</code>), diagnostics and testing (<code>7aaaf7cf</code>), Geometry (<code>0dbb7533</code>), and MetaHumanGenerator (<code>a0969299</code>), with a follow-up tagging a new RewindDebuggerToolset function (<code>01667eaf</code>). The ToolsetRegistry schema gold values were updated for <code>annotations.accessMode</code> (<code>ccd0297c</code>). Two commits give those annotations teeth. <code>98a0cf13</code> adds a shared <code>UE::PropertyEditor::CheckCanSetProperty</code> gate with <code>FPropertyEditorPermissionList</code> support and routes both ToolsetRegistry and PythonScriptPlugin property writes through it, and <code>39628b49</code> adds a <code>UToolsetLibrary::IsClassAllowed</code> helper gating UMG and MVVM toolset object creation in UEFN.</p>
<p>The rest is production plumbing. <code>c3107491</code> rejects scripts that use long-running blocking operations, with the stated goal of steering models toward non-blocking scripts, and <code>PythonScriptingTools.execute_python</code> went async for the same reason (<code>c06ac6a8</code>). <code>execute_tool_script</code> now takes a CodeBlock rather than a plain string (<code>b4a6cbae</code>). GLB and GLTF now route through Interchange in <code>StaticMeshTools.import_file</code> instead of failing on FbxFactory (<code>587b0878</code>). Getters and setters for SCS component variables, function locals, and inherited members can be recreated again, and <code>list_variables</code> gained an explicit <code>VariableScope</code> (<code>85d33e3b</code>). <code>9b06e4f4</code> fixes an enum default that broke JSON schema generation at editor startup. And <code>03839cb5</code> fixes four automation tests that keyed a node map on <code>str(guid)</code>, which fell back to a struct repr embedding the wrapper’s heap address, so pass and fail “came down to allocator luck.”</p>
<p>The collateral is instructive. <code>cddc9334</code> backs a no-op guard out of <code>UAssetEditorSubsystem::OpenEditorForAsset</code> and relocates it to the AI toolset entry point, because the subsystem-level guard silently broke Content Browser double-click-to-reload. <code>eb1ad3af</code> fixes a UMG async-load deadlock “when MCP tries to compile and has to sync load data.” <code>486f5f17</code> fixes a UMG Toolset crash renaming widgets with a null parent class. And <code>ae501b76</code> replaces a module-scope <code>import requests</code> with the standard library, after it broke editor Python startup on any machine without a hand-installed package.</p>
<p>Worth noting without speculating: <code>0111a4cd</code> adds a TPS and license entry for Meta Llama 3 8B Instruct under a tag reading “[Kimodo]”, and <code>90d4c03d</code> adds an initial Kimodo plugin for something called the “AnimGenOffline framework.” The commit messages do not say what Kimodo is, and we are not going to guess.</p>
<p>The same week, 15 commits added 58 distinct <code>README.md</code> files: Core and CoreUObject (<code>ca134812</code>), four Chaos modules (<code>98689430</code>), seven MetaSound modules (<code>1f1c36c0</code>), 21 Concert modules (<code>6034ea0f</code>), Gameplay Cameras (<code>f6b9545b</code>), StateTree (<code>5f2cff5f</code>), Enhanced Input (<code>5b4f69ac</code>), and TaskGraph (<code>91d54540</code>), plus an async-loading doc that landed as <code>AsyncLoading.md</code> rather than a README (<code>284b2991</code>). The Concert commit spells out the format: YAML frontmatter with name, purpose, and status, canonical sections, and content that is novel, durable, specific, and actionable. Machine-parseable per-module documentation landing in the same week the agent surface gets typed access levels is context plumbing for models, whatever else it does for humans.</p>
<p><strong>Why this is important.</strong> Access modes, a property-write permission gate, and per-conversation undo are the three things you need before you let a model touch a real project. Their arrival together is a schedule signal.</p>
<p><strong>Who should care.</strong> Tools teams, anyone planning editor automation policy, and UEFN developers, who are already inside the enforcement path via <code>39628b49</code>.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="metal-4-lands-as-a-complete-second-backend">Metal 4 lands as a complete second backend</h3>
<p><strong>What changed.</strong> <code>8a469fb3</code> adds an MTL4 backend alongside the existing Metal 3 path across 73 files. <code>FMetalDevice</code> gains <code>bIsMetal4Device</code>, set during feature enumeration for Apple7+ GPUs on macOS 26 or iOS 26 and newer, gated by <code>rhi.Metal.EnableMetal4</code> and <code>rhi.Metal.ForceMetal3</code> plus the <code>-metal4rhi</code> and <code>-metal3rhi</code> switches. A new <code>IMetalCompiler</code> interface splits into <code>FMetal3Compiler</code> and <code>FMetal4Compiler</code>. Encoding splits into <code>FMetal3CommandEncoder</code> and <code>FMetal4CommandEncoder</code>, both final non-virtual classes over a shared <code>FMetalEncoderCommon</code>. <code>FMetalRHICommandContext</code> becomes an abstract encoder-less base with <code>TMetalRHICommandContext&lt;TEnc&gt;</code> per encoder and <code>if constexpr (TEnc::bIsMetal4)</code> branching. The commit never states the cvar default, and it is not a pure addition either: <code>MetalFence</code> and the dead Intel-Mac paths are deleted outright.</p>
<p>The dependency shipped the same week under a title that gives away nothing. <code>51da534b</code> reads “Metal RHI” with a five-word body, “Upgrading MSC for Metal 4,” and carries 110 files and 24,478 lines: 41 new headers, 34 of them <code>MTL4*.hpp</code> under <code>Engine/Source/ThirdParty/MetalCPP</code>, including <code>MTL4CommandBuffer</code>, <code>MTL4Compiler</code>, <code>MTL4CommandQueue</code>, <code>MTL4AccelerationStructure</code>, and <code>MTL4ArgumentTable</code>. The rest are edits to existing MetalCPP headers. Five more Metal RHI commits landed in the same window: bindless descriptor limits with loud failure on heap exhaustion (<code>0b220fce</code>), a Mac zero-sized viewport backbuffer assert (<code>a87d34fb</code>), an unavailable-drawable assert plus VSM render-target-array validation error (<code>89c6c04b</code>), and re-enabled concurrent dispatch (<code>5211d317</code>).</p>
<p>Mobile plumbing moved too. <code>e7a4fd6d</code> adds archive-based serialization of Vulkan structures with a Python generator (<code>GenerateVulkanArchive.py</code>) that reads the Vulkan registry, using one shared function for serialize and deserialize to guarantee identical ordering and identical feature structures for device creation in both the app and the service, so the Android PSO compilation service needs less hand-written support for each new extension, though the commit calls itself a first draft. <code>1a47b49f</code> wires input-latency telemetry through <code>VK_EXT_present_timing</code> and <code>VK_KHR_present_id</code> with a <code>VK_GOOGLE_display_timing</code> fallback into the Android frame pacer, and enables <code>PLATFORM_SUPPORTS_FLIP_TRACKING</code> on Android and Linux. <code>c0b5f55c</code> forces discard to the very end of the GLES mobile base pass pixel shader to dodge Adreno’s “Bary after KILL” crash on a specific character material. <code>653c7c75</code> binds a depth-stencil sink in <code>DrawTileMesh</code>’s translucent path to fix a MetalRHI assert. On Apple platforms, <code>ab401d17</code> moves LLM <code>SetProgramSize()</code> out of <code>BaseAllocator()</code>, which runs before <code>GMalloc</code> exists, into <code>FApplePlatformMemory::Init()</code>; <code>c0167ef9</code> wraps MarketplaceKit Swift-task completion delegates so game-thread work does not crash; and <code>8ac39446</code> adds iOS GameMode via <code>GCSupportsGameMode</code> and <code>LSSupportsGameMode</code> plist values, enabled by default.</p>
<p><strong>Why this is important.</strong> A second full RHI backend is a multi-release commitment, and the shape of <code>8a469fb3</code> (abstract base, templated context, compile-time branching) says Epic intends to keep both alive rather than migrate and delete. If you have Apple platform RHI modifications, you now have two paths to maintain.</p>
<p><strong>Who should care.</strong> Apple platform teams and anyone with Metal RHI forks. Android teams should read <code>e7a4fd6d</code> and <code>1a47b49f</code>.</p>
<p><strong>Urgency: Act now</strong> for Apple platform engineers. Track otherwise.</p>
<h3 id="nanite-and-megalights-get-shaken-out-against-real-content">Nanite and MegaLights get shaken out against real content</h3>
<p><strong>What changed.</strong> <code>10496625</code> adds <code>DumpNaniteFingerprintCommandlet</code>, which builds Nanite data for a <code>UNaniteDisplacedMesh</code> or <code>UStaticMesh</code> and dumps a deterministic text fingerprint (FResources CRCs, per-page CRCs, layout counts, bounds) for A/B comparison across changelists. It is filed under a Nanite non-determinism investigation (UE-381258). One purpose: make a suspected nondeterminism provable.</p>
<p>CLAS allocation got the same treatment. <code>044f751c</code> makes <code>AllocateClustersCS</code> report allocation results on both success and failure, tracks the actual required blocks per installed page from CLAS build results, and puts the required CLAS buffer size in the on-screen warning. It also fixes the dispatch running one thread per cluster instead of one per page. <code>870d608c</code> auto-raises <code>MaxGeometryIndex</code> and <code>MaxUniqueGeometryCount</code> from uploaded segment mappings and logs a cvar recommendation when the growth causes expensive invalidations. <code>fbb53907</code> renames <code>NumAssemblyParts</code> to <code>NumAssemblyTransforms</code> and fixes an off-by-one in <code>GetMaxEmittedClusters</code>.</p>
<p>On the lighting side, <code>f2050b2f</code> force-enables cast shadows on first-person primitives because MegaLights needs the flag for screen traces to pick up weapon geometry, and the commit itself says “in the future we should fix it properly.” <code>7363fa81</code> adds per-<code>FViewFamily</code> overrides for sample count, guide-by-history, and reference passes for deterministic rendering, opt-in, and byte-identical to before when there is no override and a single view. <code>1959981c</code> relaxes stochastic-upsampling normal weights so fewer Nanite foliage voxel pixels lose lighting on disocclusion, offsetting the cost with ALU and shared-memory tweaks (0.62ms to 0.59ms at 540p on one console, unchanged on another). <code>4ad3bfd7</code> fixes path tracer Substrate refractive slab handling, where IOR of 0 is now treated as IOR of 1, thin-slab transmittance was using mean free path instead of its inverse, and random-walk SSS is now reserved for the opaque case, plus a thin-surface transmittance visualization mode. <code>c8203568</code> makes SkyAtmosphere LUT resources persistent instead of recreating them on every component modification.</p>
<p>Three more rendering changes are easy to skim past. <code>9389850e</code> adds a Substrate <code>Volumetric6AxisBSDF</code> for translucent meshes that works with Lumen and the new material translator, across 19 files including three <code>.ush</code> and one <code>.usf</code>. <code>f57449b7</code> is a 29-file refactor moving runtime reflection captures under the Ambient Lighting Volume component so they coexist with baked captures, fixing black, flickering, and missing captures. <code>54abee34</code> adds a Rendering Quality property (High, Medium, Low) on local lights with per-tier feature fade for mobile deferred (UE-380357).</p>
<p><strong>Why this is important.</strong> CLAS allocation used to just fail. Now it prints the buffer size you needed, which turns a bug report into a config change.</p>
<p><strong>Who should care.</strong> Anyone shipping Nanite at scale, MegaLights adopters, and mobile deferred teams.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="epic-is-building-a-multi-process-test-harness-inside-the-engine">Epic is building a multi-process test harness inside the engine</h3>
<p><strong>What changed.</strong> 31 commits landed in seven days across the ModularTestFramework plugin, its Runner UI, its test plugins, and a new <code>ModularTestHub</code> program, and no single one of them looks like much. Together they are a pre-booted worker pool for networked end-to-end tests: warm slots that drain mid-transition between claims (<code>1768b994</code>), join-in-progress provisioning exempted from the per-test budget with stale connections drained before claim (<code>3f3456c6</code>), per-role wire-flag heartbeats so a wedged slot is visible during a wait rather than after it (<code>97293749</code>), config-driven extra arguments for spawned server workers (<code>800140d8</code>), and async loading thread enabled for server workers (<code>d17619e2</code>). Suites declare and are scheduled by environment (<code>31bf0d6c</code>, <code>b2eeb1db</code>). Test plugin <code>.uplugin</code> files stage into cooked builds with modules denied from Shipping (<code>ae4f1dae</code>). A standalone <code>ModularTestHub</code> program host appeared at <code>Engine/Source/Programs/ModularTestHub/</code> (<code>460d1d90</code>). Live run status, phase events, and wait heartbeats landed as a 26-file change (<code>d6b2364c</code>), with a matching Runner UI (<code>28162a47</code>).</p>
<p>Note the failure-mode language running through it. <code>e36e274c</code> converts an 1800-second silent hang into a bounded reset-reaper failure that reports in roughly 240 seconds.</p>
<p><strong>Why this is important.</strong> Multi-process client and server test harnesses are usually something a studio builds itself in an external automation layer. Epic is putting one in the engine, staged into cooked builds. If you have your own, it is worth knowing what the first-party version will cover before you invest another year in yours.</p>
<p><strong>Who should care.</strong> Automation and QA infrastructure owners on networked titles.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="networking-replication-and-the-one-off-fixes-that-will-bite-specific-teams">Networking, replication, and the one-off fixes that will bite specific teams</h3>
<p><strong>What changed.</strong> <code>89c9f3a6</code> makes Iris NetObjectFactory IDs independent of module load order. Factory registration is now ordered, core factories are pinned to IDs 0 through 2, and the rest are sorted alphabetically, with per-factory logging on the locked table. This matters specifically for anyone registering replication factories from GameFeature plugins, where client and server load order diverges. <code>a0d09021</code> fixes an Iris client GC leak of replicated entity components on level unload. <code>e825a46d</code> fixes Game Feature plugin unmount not cancelling in-flight preloads, where the imported package survived GC via the loader refcount. <code>b7548e9c</code> adds a <code>bEngineInitSafe</code> gate with replay because external RPC route handlers could be serviced before <code>FEngineLoop::Init</code> returned.</p>
<p>Four other one-offs are worth naming. <code>ee2a254a</code> fixes Blueprint uber-graph persistent-frame object arrays being kept as hard references, which pinned a level actor and its World and tripped the world-GC-leak fatal on map teardown. It was a side effect of an earlier changelist that stamped all Blueprint object properties as <code>CPF_TObjectPtr</code>, rerouting the frame arrays to an <code>FObjectPtr*</code> collector overload nobody had overridden. <code>00616fbd</code> moves Material Parameter Collections to persistent shared uniform buffers per ID rather than one per collection instance, fixing crashes when the same collection appears in multiple packages and one gets unloaded, leaving stale references in cached mesh draw commands. <code>62fa0c6b</code> fixes cook not fully recording shadermap-to-asset info for sibling MICs sharing a configuration, which manifests at runtime as “LibraryInstance is null” when a shaderlib unload takes out a leaf shadermap still in use: a cook-time attribution bug with a runtime-only symptom, which console teams tend to find late. <code>1504a004</code> fixes D3D12 reserved-resource LLM tracking to apply the right scope marker on the submission thread and keep reserved buffers on their owner tag instead of RHIMisc and BufferPool, which will change your numbers if you have been chasing unattributed D3D12 memory.</p>
<p>On Control Rig, <code>c47055e4</code> changes override owner-structs to load via version-gated dynamic imports instead of a <code>StaticLoadObject</code> workaround that deadlocked the cook, and adds a re-entrancy guard on <code>RecompileModularRig</code> fixing random heap corruption from nested recompiles. It was re-landed after a backout. <code>7b641d6c</code> fixes <code>ImportBones(USkeletalMesh*)</code> indexing the skeleton-to-mesh lookup table with mesh bone indices, broken for several releases but unreachable until a changelist last week started calling it, “which is why the crash does not reproduce in 5.8.”</p>
<p>Several concurrency fixes landed quietly, none of them phrased in a way that would catch your eye on a skim: two TSan races in TraceLog block pool acquire and release ordering and in PThread priority initialization racing the child thread (<code>f9dda313</code>), an ASAN fix in <code>CExprTupleElement</code> (<code>469f88a2</code>), a Niagara ASAN type registry slot rewrite (<code>ca04b423</code>), Verse race arms continuing to run after the race resolved and registering awaiters nothing cancelled, so a later signal resumed a continuation reading an unset result slot (<code>c1fb5194</code>), and a DerivedDataCache pak writer shutdown race (<code>6569c1fe</code>, UE-388315).</p>
<p>Two more with content impact: <code>d65fcac0</code> fixes an HLOD hash that was order-dependent and dropped caller-accumulated settings, so HLOD setting changes did not trigger rebuilds. Existing HLODs rebuild once (UE-388016). <code>564735b8</code> excludes <code>UE_SERVER</code> from <code>UE_ENABLE_DEBUG_DRAWING</code>, so DebugDraw no longer links Renderer into servers (UE-385930). And <code>a63961db</code> fixes <code>ConcertSyncCore.Build.cs</code> adding SQLiteCore unconditionally against its own uplugin deny-list, silently linking it into game clients.</p>
<p><strong>Why this is important.</strong> These are the fixes that will not show up in a release-notes skim but will match a bug you already have open.</p>
<p><strong>Who should care.</strong> Iris and GameFeature users (act now if you use both together), console teams, and anyone tracking D3D12 memory attribution.</p>
<p><strong>Urgency: Act now</strong> for Iris plus GameFeature plugin users. Track the rest.</p>
<h3 id="horde-test-infrastructure-and-the-proceduralvegetation-plugin">Horde, test infrastructure, and the ProceduralVegetation plugin</h3>
<p><strong>What changed.</strong> <code>91b81d1a</code> stops an issue spanning multiple streams from being marked fix-failed in a stream that has since had a successful run, along with the misleading Slack “Not fixed by” messages that followed. The author is explicit that this is a workaround, not a root-cause fix: doing it properly would mean parsing changelist descriptions for RoboMerge tags or querying RoboMerge directly. <code>606aa83b</code> publishes TestData to a telemetry sink. Outside Horde proper, <code>a4f79d61</code> adds building low-level tests for a given module set through UnrealBuildTool. and <code>b8300dbb</code> restricts cloud artifact publish and retrieve in the Foundation Cooker test graph to build machines, fixing a race in the Gauntlet incremental cook test.</p>
<p>The experimental ProceduralVegetation plugin shipped four crash-fix commits and a 255-file preset library in one week. <code>ce587b30</code> fixes Nanite foliage meshes crashing packaged builds because cooked builds strip the CPU index buffers unless <code>bAllowCPUAccess</code> is set, while <code>GetNumIndices()</code> still returns the cached count, a regression from 5.7.4. <code>5084a78e</code> fixes an open <code>URange</code> bound asserting during UV generation. <code>43b603b7</code> and <code>fa747ac4</code> add two more. <code>8352feda</code> adds 255 growth presets converted by script and explicitly notes they are not yet QA’d.</p>
<p><strong>Why this is important.</strong> A fix-failed flag that was wrong, plus Slack notifications amplifying the wrong state. If your team has learned to distrust Horde’s fix-failed signal, this is why.</p>
<p><strong>Who should care.</strong> Build automation owners running Horde, and anyone maintaining low-level or Gauntlet test graphs. ProceduralVegetation is experimental, and the preset library is explicitly unvalidated, so treat it accordingly.</p>
<p><strong>Urgency: Track.</strong></p>
<hr>
<h2 id="the-5x-stabilization-sidebar">The 5.x Stabilization Sidebar</h2>
<p><code>ue5-main</code> ran 254 commits, 242 of them worth reading after the routine churn comes out, and nearly 50 mention a crash, assert, out-of-bounds, use-after-free, deadlock, or heap corruption. 152 carry an internal <code>#Synced-CL</code> integration tag and 26 explicitly say cherry-pick, so the branch is absorbing fixes from elsewhere far more than it is originating its own work. Commit counts by subsystem understate where the week actually went. Measured by file touches, the week is Control Rig (roughly 370), the experimental UAF plugin (roughly 300), RigVM (over 100), and RoboMerge (85). Two changes landed, were backed out, and re-landed inside the same week (UE-381080 and the RigVM serialization sort), which matters if you sync <code>ue5-main</code> tip. Seven of 242 commits resolve only to <code>Commit.gitdeps.xml</code>.</p>
<p>One caution on sourcing: no commit message on this branch names <code>ue6-main</code> as a source, so we are not calling any of this a backport. The only literal cross-branch statements are <code>3d3ff86f</code> (“Bink: Back merge from Main”), the 26 cherry-pick tags, and <code>1e16ebbe</code>, whose message reads “Avoid usage of UE6 function in cherry picked CL.”</p>
<h3 id="the-cook-and-derived-data-layer-got-audited-and-it-was-worse-than-it-looked">The cook and derived-data layer got audited, and it was worse than it looked</h3>
<p><strong>What changed.</strong> <code>acf33841</code> is the headline. Cook <code>IncrementalValidate</code> was flagging roughly 800 ControlRig and RigVM packages on every single run, because a 4-byte <code>FRigVMCallableInfo::FunctionHash</code> in serialized rig bytecode differed between the on-disk artifact and a fresh save, and freshly-healed values got re-flagged with new values on the next run. Three cooperating defects sit underneath it, one of which is async load-order dependence deciding, per package and per run, whether the cooked bytecode embeds persisted data or data recompiled in-session. Around it: <code>441240aa</code> fixes a rig asset minting a random GUID every cook instead of a deterministic one. <code>06929408</code> and <code>145e603a</code> fix map serialization in raw <code>TMap</code> iteration order, which made byte-identical functions serialize differently across cook processes (<code>1c30e76b</code> is the backout of the first attempt). <code>2c214150</code> fixes cooks failing outright with “Compilation data for public function <X> has no instructions.” <code>1cd565db</code> fixes <code>ComputeHLODHash</code> both dropping settings and being source-component-order dependent, so HLOD setting changes were silently not triggering a rebuild. Existing HLODs rebuild once. <code>d94abc9d</code> fixes a shutdown race in the derived-data pak file cache writer.</p>
<p><strong>Why this is important.</strong> If you have been living with incremental cooks that never feel incremental on a Control Rig heavy project, this is a plausible explanation with a fix attached. It also means your incremental cook validation results before this change were not telling you anything real.</p>
<p><strong>Who should care.</strong> Anyone shipping on 5.8 with Control Rig or RigVM content, and anyone who owns cook determinism.</p>
<p><strong>Urgency: Act now.</strong></p>
<h3 id="crashes-that-only-happen-in-packaged-or-runtime-builds">Crashes that only happen in packaged or runtime builds</h3>
<p><strong>What changed.</strong> <code>47105f70</code> fixes ProceduralVegetationEditor-generated static and skeletal meshes crashing the packaged game on asset load in 5.8 when Nanite Foliage is enabled, explicitly a regression from 5.7.4, caused by reading CPU index buffers that cooked Nanite builds strip unless <code>bAllowCPUAccess</code> is set. It renders fine in the editor, which is why it survived. <code>cc080b6f</code> changes the default memory-mapped bulk data alignment from 1 to 16, because animations are memory-mapped on mobile and alignment of 1 violated the ACL anim sequence codec’s requirement, asserting at decompress time in Debug and Development builds. It touches <code>BaseAndroidEngine.ini</code>, <code>BaseIOSEngine.ini</code>, and <code>SavePackage2.cpp</code>, so it changes cooked layout and adds padding. <code>d4b38151</code> adds a 2GB estimate-based limit to stop ray tracing acceleration structure builds for groom assets too large for scratch memory. <code>c995dddf</code> prevents a runtime crash transitioning into a linked StateTree asset that was recompiled with different parameters after the parent tree compiled. <code>e742b0e0</code> null-guards landscape grass data deserialized from packages saved before the <code>LandscapeGrassSingleArray</code> format change. <code>5473a63e</code> guards GPU crash diagnostic dumps against an invalid diagnostic buffer, so a device-removed event stops replacing your real crash report with a fault inside the crash reporter. That one is speculative with no repro in hand, and the commit says so.</p>
<p><strong>Why this is important.</strong> Every item here is a defect that passes in the editor and fails in a packaged build, which is the worst place on the calendar to find one.</p>
<p><strong>Who should care.</strong> Anyone in cert or approaching it on 5.8. Cherry-pick selectively based on what you actually use, and note that <code>cc080b6f</code> changes cooked layout, so it is not a free pick.</p>
<p><strong>Urgency: Act now, selectively.</strong></p>
<h3 id="a-systematic-editor-crash-sweep-mostly-sequencer-and-the-property-editor">A systematic editor crash sweep, mostly Sequencer and the property editor</h3>
<p><strong>What changed.</strong> A run of near-adjacent Jira tickets (UE-388963, 388964, 388968, 388969, 388971) from two authors over two days, with one pattern throughout: a pointer guaranteed non-null in the common case and not in a Sequencer context. <code>783795fc</code> (bound objects with no level), <code>517d4b0f</code> (a track with no registered track editor turning a handled ensure into a hard crash), <code>966c3146</code> (a group actor converted to a spawnable, therefore no <code>ULevel</code> outer), <code>6ac89a27</code> (animation with no skeleton), <code>e435b5a2</code> (a locator whose property data went away mid-frame). <code>8e2c4fa9</code> is the most structurally interesting: a property change listener held its owner as a raw <code>UObject*</code>, so every cache built for an actor’s components dangled the moment a Blueprint recompile reinstanced them. It is now a <code>TWeakObjectPtr</code>. On Mac, <code>6075bcee</code>, <code>3e629390</code>, and <code>23b2dc0d</code> fix Metal RHI asserts and <code>a129e6f9</code> raises the bindless descriptor limit. <code>346641ec</code> fixes buffer stomps and length underflows in the shader preprocessor’s macro argument handling, including an int against <code>size_t</code> mismatch that turned a bad length into a near-<code>SIZE_MAX</code> allocation. That mirrors <code>0c116a4d</code> on <code>ue6-main</code>.</p>
<p><strong>Why this is important.</strong> These are cheap, low-risk picks for a team spending editor time in Sequencer.</p>
<p><strong>Who should care.</strong> Cinematics teams and Mac-based editor users.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="control-rig-is-still-getting-new-features-on-this-branch">Control Rig is still getting new features on this branch</h3>
<p><strong>What changed.</strong> <code>a11fab2a</code> is a 104-file commit introducing an entirely new constraints editing interface in the animation editor mode, gated behind the <code>ControlRig.UseNewConstraintsWidget</code> cvar with the old interface left unmodified as a fallback. Roughly a dozen follow-up commits landed the same week polishing it (<code>70eafe2a</code>, <code>da94cded</code>, <code>f422f8a3</code>, <code>550013e5</code>), and a parallel rework hit the modular rig editor (<code>d71114f9</code>, <code>7a81b7b4</code>). The UAF plugin accounts for 43 commits and 298 file touches, including Verse exposure (<code>4329276e</code>) and asset registry data versioning (<code>b60200d8</code>).</p>
<p><strong>Why this is important.</strong> Plan around this one. A branch described as post-5.8 maintenance is still receiving a cvar-gated interface replacement and active experimental-plugin development. If your integration policy assumes <code>ue5-main</code> tip equals 5.8 plus safe fixes, that assumption is wrong this week.</p>
<p><strong>Who should care.</strong> Anyone with a standing policy of syncing <code>ue5-main</code> tip, and animation teams evaluating the new constraints widget.</p>
<p><strong>Urgency: Track.</strong></p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>Frame pacing driven by task-graph timing, gated off by default.</strong> <code>9599726e</code> pulls from <code>GRHIFrameTimingQueue</code> inside <code>UEngine::UpdateTimeAndHandleMaxTickRate</code> to derive a next-frame work forecast and a target deadline (the next vblank flip), then throttles the game thread through the same path <code>t.MaxFPS</code> uses, and adds a “Slack” field in stat unit. <code>c5bfef96</code> removes <code>FFrameResult</code> and <code>FRHIFrameTiming</code>, computes work and slack directly on the result node, hands the final node reference back to the game thread, and adds a “continuation” flag on prerequisite links explicitly reserved for separating pipeline stages on the critical path later. <code>d31cd58c</code> adds a DynamicRHI hook for reporting input sample time to platform APIs. The whole game-thread path sits behind <code>UE_TASK_TIMING_ENABLE</code>, disabled by default, and requires platform RHI support (UE-306070). Two of the five commits are build fixes for the enabled case (<code>c1b64712</code>, <code>1a28752c</code>), which means somebody is compiling it regularly. Worth tracking if you own frame pacing or input latency.</p>
<p><strong>The new material translator is in legacy-parity cleanup, which means it is being run against production content.</strong> Six commits landed in <code>Engine/Source/Runtime/Engine/Private/Materials/MIR/</code> and surrounding translator files, all about matching what the legacy translator already does: static switches with disconnected inputs (<code>18014ab9</code>), pixel depth offset (<code>580cbccc</code>), terminating the analytic derivative chain at values with no closed-form derivative (<code>29969f64</code>), collapsing shading models on platforms without per-pixel shading model support (<code>022d7a3e</code>), gating SubsurfaceColor and Refraction on platform-filtered shading models (<code>a684c16d</code>), and Preshader statistics plus op byte count in the material editor, which needed separate stat-gathering code because the new translator uses Preshader2 (<code>bf3822cf</code>). <code>022d7a3e</code> is the one to read: MIR was keeping every shading model the graph reached, which activated extra shading code in the base pass, kept the feeding textures alive, and pushed some character materials past the 16-sampler limit on Android. Parity bugs at that level of specificity do not come from synthetic test content. The commit stream calls it both “New material translator” and “[MIR]”, and the acronym is never expanded anywhere in the data, so we are not expanding it either.</p>
<p><strong>Convergent scaling work landed across the entity and transactional layers in the same week.</strong> <code>2545a03b</code> replaces EntityFramework’s <code>FExecutionManager</code> full Tarjan rebuild on every register and unregister with incremental insert and remove, then got backed out later the same day (<code>315c94f8</code>). <code>ff707682</code> adds optional incremental processing-graph invalidation, where previously every new archetype invalidated all graphs. <code>205e435c</code> replaces processing-queue contention with a lock-free per-chunk 64-bit color mask. <code>58bec238</code> makes Mass <code>IsProcessing()</code> reliable across the async pipeline, where on the legacy parallel path (<code>mass.UseProcessingQueue=false</code>) the counter transiently read false between dependent tasks, so callers were picking direct mutation over the command buffer. It was backed out the next day (<code>7845eb56</code>) with no reland inside the week. On the transactional side, <code>f026373a</code> drops a redundant hit-set merge in AutoRTFM nested commit, removing roughly 44 million redundant hash insertions in a single raytracing benchmark run and taking <code>FTransaction::CommitNested</code> from 11.3 percent to 0.6 percent of samples, for 1.05x to 2.06x on named benchmarks. <code>59a6763d</code> skips AutoRTFM bytecode for single-target client cooks in the Verse compiler, cutting <code>UVerseFunction</code> memory on clients by about 45 percent. And <code>47f88b39</code> splits <code>UE_WITH_DSTM</code> and introduces <code>UE_WITH_TRANSACTIONAL_EXECUTION</code>, re-gating ticks, timers, latent actions, RPCs, the control channel, level streaming volumes, async collision, and physics event dispatch across 27 files in Core, CoreUObject, Engine, Chaos, and Iris. That last one changes what a transactionalized build means, so read it before you assume your build configuration is unchanged.</p>
<p>Also on the radar, at lower confidence: Subsonic shipped 12 commits into <code>Engine/Plugins/Experimental/Subsonic/</code>, building out an event, action, and executor audio model with parameter stores, a custom K2 node for setting executor parameters (<code>cbec3c89</code>), custom source parameter authoring (<code>b1f03a14</code>), collection parameter overrides with a type allow-list (<code>198fc0a7</code>), and editor-tab clipboard shortcuts. That volume of editor polish suggests it is past prototype. The Gaussian Splat plugin split out of Niagara into a standalone <code>Engine/Plugins/GaussianSplat/</code> (<code>7f267510</code>, <code>39c0feb2</code>), exposed <code>bUseQuantizedFormat</code> (<code>167e4661</code>), and added import and allocation error handling (<code>6b109377</code>). And <code>7c096c84</code> is a 92-file, 5,372-line commit exposing UAF systems to Verse and running <code>skeletal_animations</code> as UAF systems, so a <code>uaf_system</code> can now be played as a <code>skeletal_animation</code> from Verse and each <code>skeletal_animation</code> runs as its own system rather than a composited <code>UUAFAnimGraph</code>. It restructures <code>Engine/Plugins/EntityFramework/Source/EntityModifiers/</code> along the way, deleting <code>IEntityModifierInterface.h</code> and adding <code>IEntityModifierImplInterface.h</code>, with reflection gated behind <code>Verse.EnableVerseUAFSystemClassReflection</code>.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>47 commits on <code>ue6-main</code> were pure noise: content syncs, autogenerated data, and mechanical churn with no reader value. Beyond that, we set aside RoboMerge’s submit-approval work, which ran 19 commits in <code>Engine/Extras/RoboMerge/v3</code> including a 39-file restructure of changelist filters supporting approval as well as userlist and usertag filter types (<code>f4f95626</code>), an all-or-nothing filter (<code>674e4413</code>), and user tag caching (<code>2373c52b</code>). It is real work, but it is Epic’s internal integration tooling and it does not change anything on your machine. We also set aside the week’s churn of 13 backouts and 13 relands, several of them pairs within days: interesting as a signal about branch churn, not actionable individually. Two other items are worth naming as deliberate omissions rather than oversights. 22 of the 165 commits we read closely resolve to nothing but <code>Commit.gitdeps.xml</code>, and the entire Python editor-toolset and AI-agent workstream lands that way, so much of that section is read from commit messages rather than code. And <code>af12b293</code> renames “Timing Profiler” to “Timing Insights” and adds a Profile section to the Tools menu, which will confuse exactly one person on your team for exactly one afternoon.</p>
<hr>
<h2 id="closing">Closing</h2>
<p>What matters in a stabilization week is which kind of fixes land, not how many. Epic spent an unusual amount of effort this week on making broken things say so: a crash reporter that survives its own crash, a commandlet whose only output is a fingerprint, an allocator that reports the size it actually needed, a hang converted into a timeout. The PixelStreaming2 origin filter is the argument for why that work matters. One file, no symptom, an allowlist left unenforced on a network-facing server. Nobody was ever going to file a bug against it, because it never said a word.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine except in the 5.x sidebar, where they reference <code>ue5-main</code>. Some of the most interesting entries resolve only to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-jul-27-aug-2-2026/og.bca4df984e.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: The Build Cache Trusted a Version String (July 20 to 26, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-jul-20-26-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-jul-20-26-2026</guid>
      <pubDate>Mon, 27 Jul 2026 07:43:32 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>960 commits on ue6-main, the busiest week since mid-May, and almost none of it is features. Epic’s build cache was silently handing out stale PCHs, a UBA cache server wild-wrote 512 MB past a maintenance block, and twelve commits landed to stop cooked packages from serializing differently between runs. Plus FScene decomposition breaking public API, and a 5.x sidebar for studios shipping on 5.8.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-the-build-cache-trusted-a-version-string">Unreal Engine Weekly: The Build Cache Trusted a Version String</h1>
<p><strong>Week of July 20 - 26, 2026</strong> | <strong>960 commits on <code>ue6-main</code>, 217 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>The busiest week since mid-May, and most of it is repair work. Close to 40% of the week’s notable commits are crash, use-after-free, hang, or assert fixes, the mix you get when a branch is being driven toward a release. The rest concentrates in two places: the renderer, where <code>FScene</code> is being taken apart into scene extensions on what is now clearly a scheduled architectural project, and build infrastructure, where UBA and the EpicClang toolchain absorbed a week of correctness work with the tone of an incident review. One incidental commit makes the framing official: <code>Engine/Build/Build.version</code> had its branch name updated to UE6 (<code>e4af2492</code>).</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<p><em>The weeks of June 29 and July 6 (75 and 64 commits) are Epic’s summer shutdown, not a decline. Week counts below refer to full-volume weeks.</em></p>
<p><strong>FScene decomposition has become a scheduled UE6 project, and this week it broke API.</strong> The scene-extension refactor has escalated at each of its three full-volume appearances: scaffolding in early June, four extractions on July 19, and five more in one coordinated push this week, with rendering activity hitting its highest count since the branch switched to <code>ue6-main</code> in June (66, against a June and July range of 31 to 53). The API break is the new part. <code>FPrimitiveSceneProxy</code>’s level-added and level-removed render-thread hooks left <code>ENGINE_API</code> entirely (<code>97e8f671</code>). Behavior-neutral refactors do not usually take public API with them, which puts a schedule behind this one. The consistent thread across all three weeks is persistent-primitive-ID indexing and change-set-driven lifecycle.</p>
<p><strong>Build and toolchain work shifted from modernization to correctness under failure.</strong> Build has been the longest-running thread since early February, and for two weeks straight it has been the busiest area on the branch. The content changed sharply between the two. July 19 was structural modernization: C++23 as the default standard, the circular-dependency allowlist deleted, UnrealBuildTool moved onto modern .NET. This week is the response to a failure, and the failure was correctness. A cache keyed on a version string instead of on the compiler binary was silently reusing stale PCHs (<code>2699d884</code>). Two quarters of build investment produced a system fast and distributed enough that its bugs now hand you wrong binaries, and Epic is responding by making the whole path fail closed. Banning Clang 21.0.0 through 22.1.3 outright (<code>56b0b3e9</code>) is the version-pin equivalent of the same reflex.</p>
<p><strong>Artifact reproducibility is emerging as a named UE6 shipping requirement.</strong> Determinism fixes have surfaced individually for roughly eight weeks, but this is the first week they arrive as a coherent campaign: twelve commits that exist solely to stop cooked packages from serializing differently between runs of the same cook, ten of them from a single author, five citing the same internal validation tool, with one fix already backed out (<code>391a9237</code>). Read alongside the UBA cache-key and EpicClang work, this is one problem rather than two: distributed build and cook infrastructure only pays off if identical inputs reliably produce identical bytes, and Epic is closing both ends of that contract in the same week. A parallel wall-clock pass from a second author (<code>053e3401</code>, <code>464f299c</code>) says the cook has an owner this quarter.</p>
<p><strong>The editor agent stack is hardening, and starting to leave the editor.</strong> This initiative has been visible for roughly twenty weeks, and the phases line up cleanly: infrastructure buildout in March, toolset proliferation and a first-party MCP server through June, a crash-hardening audit on July 19, and now transactional safety and access control. Two details matter more than the volume: the agent undo manager could not stay landed for four days running, and the new audit verbs exist specifically to repair graph corruption left by previously failed agent writes (<code>afef1d65</code>). Separately, a cluster under a single ticket started moving the toolset and MCP surface out of the editor and into cooked runtime builds, which turns an authoring question into an operational one. Details in Worth Tracking below. The hardening arc is well established; the cooked-runtime direction is one week old and worth confirming before drawing conclusions.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li><strong>If you self-host a UBA cache server, read the cache section first.</strong> A production server wild-wrote up to 512 MB past a maintenance block during compaction, and corrupted entries could turn cache misses into false hits. The bounds-checking and fail-closed fixes are worth pulling regardless of your engine version (<code>92d13a09</code>, <code>d0348830</code>).</li>
<li><strong>EpicClang now generates a build ID from its own source hash and feeds it into the UBA cache key</strong> (<code>2699d884</code>). Before this, a compiler update that changed PCH binary format was invisible to the cache key, so stale intermediates were silently reused. The commit describes the result as compiler crashes and completely UB builds. Clang 21.0.0 through 22.1.3 are now banned outright (<code>56b0b3e9</code>).</li>
<li><strong><code>FScene</code> decomposition accelerated into a five-subsystem push, and it broke public API.</strong> <code>FPrimitiveSceneProxy::OnLevelAddedToWorld_RenderThread</code> and <code>OnLevelRemovedFromWorld_RenderThread</code> left <code>ENGINE_API</code> entirely (<code>97e8f671</code>). Anyone overriding those hooks breaks at compile time.</li>
<li><strong>Epic is treating byte-reproducible cook output as a UE6 shipping requirement.</strong> Ten determinism fixes landed this week from one author working a backlog, plus two more from other authors, alongside a separate wall-clock pass that removed roughly 21% of a 25-minute game cook in a single commit (<code>053e3401</code>).</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="fscene-is-being-dismantled-into-scene-extensions-and-this-one-breaks-api">FScene is being dismantled into scene extensions, and this one breaks API</h3>
<p><strong>What changed</strong></p>
<p>Five separate subsystems were lifted out of <code>FScene</code> into dedicated scene extensions in a single coordinated push, mostly by one author. HLOD hierarchy (<code>FLODSceneTree</code>) moved out of <code>FScene</code> and out of <code>SceneVisibility</code> into <code>FLODHierarchySceneExtension</code> (<code>86fab2e2</code>). Lighting attachment groups moved into <code>FLightingAttachmentGroupSceneExtension</code>, with group membership now stored as persistent primitive IDs (<code>34dcf40b</code>). Nanite’s shading commands, raster pipelines, shading pipelines, visibility, and their per-<code>FPrimitiveSceneInfo</code> bin state all moved into <code>Nanite::FMaterialsSceneExtension</code>, touching 26 files across the deferred renderer, Lumen, and virtual shadow maps (<code>ead01072</code>). Ray tracing data started the same migration into <code>FRayTracingSceneExtension</code> (<code>2142af0d</code>).</p>
<p>Underneath it, <code>SceneData</code> grew a <code>TDataArray&lt;bool&gt;</code> bit-per-ID specialization that dirties only on value change, editor primitive selection was rewritten from a packed-index <code>TBitArray</code> on <code>FScene</code> into <code>FEditorSceneDataExtension</code> (<code>c50ee288</code>), and <code>bPendingAddToScene</code> became a dirty array in the same pattern (<code>b68b7b6f</code>).</p>
<p>The API break is in the level command work. When level handling became <code>FLevelCommandSceneExtension</code>, <code>FPrimitiveSceneProxy::OnLevelAddedToWorld_RenderThread</code> and <code>OnLevelRemovedFromWorld_RenderThread</code> were removed from <code>ENGINE_API</code> entirely, with the extension calling <code>SetForceHidden</code> through friend access and an in-commit TODO to replace that with SceneData-tracked visibility (<code>97e8f671</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The recurring pattern in these commit messages is persistent-primitive-ID indexing and change-set-driven lifecycle. You need both before scene updates can be parallelized or driven end to end on the GPU. This has been running for weeks as steady cleanup; at five subsystems in one push it is a scheduled UE6 project. A public API removal inside a refactor described as behavior-neutral is unusual and worth noting on its own.</p>
<p><strong>Who should care</strong></p>
<p>Rendering engineers, anyone maintaining a modified <code>Renderer</code> module, and anyone with a custom <code>FPrimitiveSceneProxy</code>.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you carry renderer patches. This is the week your merge cost went up, and the <code>ENGINE_API</code> removal is a compile-time break, not a silent behavior change. Track otherwise.</p>
<h3 id="production-failures-forced-a-uba-cache-server-rewrite">Production failures forced a UBA cache server rewrite</h3>
<p><strong>What changed</strong></p>
<p>A production cache server SIGBUS’d during bucket compaction, a Mac client SEGV’d fetching entries, clients spun forever on entries the server could never serve, and large caches took ten-plus minutes to boot. The response was structural rather than a patch.</p>
<p>Maintenance moved from a global stop-the-world pass that kicked clients to per-bucket, event-driven work, triggered on a bucket hitting zero clients with new entries, on expiration, on caskey table overflow, or on web request. Cas liveness is now tracked by two refcounts (in-flight transfers and cache entry references), <code>CacheEntry::inputCasRefs</code> replaces <code>inputsThatAreOutputs</code>, and the database moves to v10 with v9 conversion on load (<code>fc5ca41d</code>). A follow-up made the delete pass run without taking the bucket exclusively, using the same per-cache-key locks as fetch and store, so long-lived client sessions no longer starve maintenance (<code>68497718</code>).</p>
<p>The corruption work is the sharp end. Entries whose caskey and path offsets landed mid-entry passed a range-only scrub and resolved garbage, and compaction traversal then did a wild write up to 512 MB past the maintenance block. <code>CompactPathTable::GetString</code> and <code>CompactCasKeyTable::GetKey</code> are now fully bounds-checked, with an explicitly fail-closed policy (<code>92d13a09</code>). A separate pass added a per-boot offset scrub, disconnected clients requesting table data past the table end, and made <code>FetchCompactTable</code> fail as a miss rather than retry forever (<code>d0348830</code>). Boot time for caches with millions of entries was fixed by dropping the v9-to-v10 input ref synthesis that hit casdb for every input of every entry, and moving refcount rebuild into a parallel post-deserialize pass (<code>01d879de</code>). Separately, memfd handles are now tagged in the handle itself rather than trusted from a process-wide env var, so a stale <code>UBA_FILE_MAPPING_MEMFD=1</code> baked into a Docker image no longer masks the real error as <code>EBADF</code> (<code>74d41354</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The fail-closed policy is the line to read twice: corruption that shrinks an entry’s input set turns cache misses into false hits. It shows up as a flaky product, not as a cache error. Everything else here is availability and boot time, which matter, but the corruption hardening is the part that can hand you a wrong binary.</p>
<p><strong>Who should care</strong></p>
<p>Build engineers, studio infrastructure, and anyone running distributed UE compilation at scale.</p>
<p><strong>Urgency</strong></p>
<p>Act now if you self-host a UBA cache server. The corruption commits are cherry-pick candidates independent of your engine version.</p>
<h3 id="epicclang-stops-being-a-vendored-binary-and-becomes-a-versioned-toolchain">EpicClang stops being a vendored binary and becomes a versioned toolchain</h3>
<p><strong>What changed</strong></p>
<p>A compiler update changed the binary format of PCH files, but UBT and UBA keyed their cache on the LLVM version string rather than the compiler binary, so stale intermediates from an older EpicClang were silently reused. The commit describes the result as compiler crashes and “completely UB builds.”</p>
<p>EpicClang now generates a build ID from the hash of all source layered on top of Clang, errors if you use a PCH built by a different source version, and feeds that build ID into the UBA cache key (<code>2699d884</code>). The day before that fix landed, the mitigation was bumping the patch version to try to invalidate the cache (<code>bfd51aae</code>). Clang 21.0.0 through 22.1.3 are now banned outright in <code>MicrosoftPlatformSDK.cs</code> and <code>Windows_SDK.json</code> due to a compiler crash (<code>56b0b3e9</code>).</p>
<p>The toolchain also got real tooling around it: <code>build.go</code> split into packages with a new <code>symbolize</code> tool that resolves non-symbolized Clang crash stacks, since Epic does not commit the debug symbols, plus a BuildGraph pipeline to acquire, build, test, deploy, codesign, and submit toolchain binaries (<code>31a3b9f0</code>, <code>b3e6cb9a</code>). A <code>MergeClangProfData</code> BuildGraph task now resolves <code>llvm-profdata</code> through the same Windows toolchain search used for compilation, which matters because raw PGO profiles must be merged by a tool matching the linked compiler-rt runtime (<code>a2fa541a</code>). Related for anyone on LTO or PGO with merged-module targets: the LLVM IR object parser unconditionally skipped <code>linkonce_odr</code> globals so they could never become export candidates, breaking dllimported template static data members from Verse interop codegen (<code>77366fe1</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Keying a shared build cache on a version string rather than on the compiler binary is a class of bug that any studio with a shared cache should audit for in its own toolchain wrappers. The version-pin ban is the immediate action item; the build ID is the structural fix.</p>
<p><strong>Who should care</strong></p>
<p>Build engineers, anyone using UBA caching with a custom or updated Clang, and anyone doing PGO on Windows.</p>
<p><strong>Urgency</strong></p>
<p>Act now. Check your toolchain pinning before your next Clang update.</p>
<h3 id="two-authors-are-making-the-cook-byte-reproducible-and-materially-faster-in-parallel">Two authors are making the cook byte-reproducible and materially faster, in parallel</h3>
<p><strong>What changed</strong></p>
<p>Twelve commits exist this week for one reason: to stop cooked packages from serializing differently between runs of the same cook. Five of them cite the same tool, Cook IncrementalValidate, which double-saves and diffs the on-disk artifact against a fresh save. The failures are the classic non-determinism set. Raw <code>TMap</code> iteration order leaked into serialized bytes for RigVM function compilation data (<code>704306bd</code>, backed out, resubmitted as <code>f55e2715</code>) and into the Niagara compile hash, where <code>NestedPropertiesAppendCompileHash</code> hashed values in map order while sorting only the key labels and then mis-paired the two (<code>46be1f8d</code>). RigVM asset variants with no valid <code>FRigVMVariant::Guid</code> minted random GUIDs on load, now fixed by passing the package path so <code>GenerateGUID</code> uses <code>FGuid::NewDeterministicGuid</code> (<code>4debd224</code>). <code>MakeUniqueObjectName</code>’s process-global per-class counter leaked into cooked PCG GPU compute-graph export tables under multiprocess cooking (<code>859468f6</code>). <code>FTextHistory_Base</code> re-keyed text with <code>FGuid::NewGuid()</code> whenever a duplication archive assigned a package localization namespace during cook, randomizing cooked text keys and breaking the localization pipeline’s key match (<code>b9892d57</code>).</p>
<p>The races are more interesting than the ordering bugs. Landscape Nanite meshes created their <code>UBodySetup</code> in the async build’s completion callback rather than at mesh creation, so a package serialized differently depending on whether it was saved before or after that callback ran (<code>e9337bd6</code>). <code>USplineComponent::GetUsedMaterials</code> ignored <code>bGetDebugMaterials</code> and reported editor debug visualization materials unconditionally, so <code>PreSave</code>’s <code>bHasNoStreamableTextures</code> computation flipped depending on whether those async-loaded debug materials had finished loading, which under unversioned property serialization inserted or removed a one-byte payload and shifted every subsequent byte of the export (<code>5974c3af</code>). Material instance cooking ran unused-parameter cleanup against the parent, which is not guaranteed to be cooked first (<code>a9a6a2d3</code>), and the material translator emitted two byte-different but semantically identical HLSL variants under the same MaterialTranslation cache key (<code>5b1bf314</code>). The heaviest is the ControlRig/RigVM fix (<code>dfee7c15</code>), which documents roughly 800 packages flagged on every validation run, traces it to three cooperating defects including a load-order race over whether a consumer embeds persisted or in-session-recompiled function bytecode, and forces <code>RigVMCompiler</code> to always recompile referenced graph functions during cook at a measured ~3% of cook tick time. Cooker DiffOnly diagnostics now also report <code>BulkDataMap</code> differences in the package header (<code>19d769b3</code>). The campaign is still churning: one RigVM determinism fix was backed out (<code>391a9237</code>).</p>
<p>Running alongside it, a different author spent the week on cook wall clock. <code>FLocalizationCookArtifact::StoreDataInOplog</code> recursively scanned the entire project directory twice for <code>*.locres</code> and <code>*.locmeta</code> at cook shutdown, costing 319 seconds of a 25-minute game cook, now fixed by scanning only registered localization paths (<code>053e3401</code>). <code>UChunkDependencyInfo::BuildChunkDependencyGraph</code> was 64 seconds of a full cook and rebuilt whenever the highest seen chunk id grew, and was rewritten to produce an identical tree (<code>464f299c</code>). Shader DDC keys are now built as UTF-8 end to end with byte-identical output, so existing entries stay valid (<code>f70b8ddb</code>); material shader-map DDC key validation, about 30 seconds of a single-process cook in the DDC-load poll path, moved to a worker task joined in <code>FShaderCompilingManager::FinishAllCompilation</code> (<code>28c6fb99</code>); and <code>ConvertToUTF8</code> gained an ASCII-run fast path (<code>f77dc4d9</code>). The determinism author contributed one wall-clock fix of their own: the deterministic static mesh lighting GUID moved off the game thread, where recomputing it forced a synchronous wait on the async build and hung the editor during interactive edits (<code>e8c849ac</code>). On the build side, UAT script module rebuilds went from a ~40 second all-modules storm on any shared <code>.cs</code> timestamp change to a content-hash-validated, reference-assembly-gated path, taking a full rebuild from ~17s to ~10s (<code>d22404fe</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Every one of the determinism bugs silently poisons incremental cooking and shared-DDC reuse. A package that serializes differently on each run defeats hash-based artifact caching, so a CI system doing incremental cooks re-uploads and re-stages content that did not change, and content-addressed hit rates degrade in a way that looks like infrastructure flakiness rather than an engine bug. The volume suggests Epic is working a backlog, not chasing one-offs, which is the strongest available signal that byte-reproducible cook output is a UE6 shipping requirement. The wall-clock work is the same story from the other direction, and both authors are measuring in seconds of real cook time rather than microbenchmarks.</p>
<p><strong>Who should care</strong></p>
<p>Anyone running incremental cooks on CI, anyone operating a shared DDC, and studios with rig-heavy or PCG-heavy content.</p>
<p><strong>Urgency</strong></p>
<p>Track, and act now if your incremental cook hit rates have been unexplainably low. <code>dfee7c15</code> bumps <code>ERigVMCookVersion</code>, so cooked rig packages rebuild once on upgrade.</p>
<h3 id="a-tlsf-gpu-sub-allocator-lands-wired-into-half-the-renderer-shipped-switched-off">A TLSF GPU sub-allocator lands, wired into half the renderer, shipped switched off</h3>
<p><strong>What changed</strong></p>
<p>Epic added an O(1) two-level segregated fit allocator for GPU memory ranges the CPU cannot address, complete with a defrag planner, a GPU-side compute copy pass, a validation layer, and a test suite. <code>FGpuTlsfAllocator</code> lives in <code>RHI</code> with all block metadata in a CPU-side slot pool, so GPU memory is never touched; allocations are opaque handles that survive pool growth and defrag relocation. <code>BuildDefragPlan</code> does true left-compaction behind a write cursor and is capped at 4 GiB per pool because the copy shader addresses bytes with 32-bit offsets. The companion <code>GpuTlsfCopyPass</code> and <code>GpuTlsfCopy.usf</code> execute batched transfers, running overlapping compaction ops as a single group-cooperative forward copy (<code>8db4a063</code>).</p>
<p>Close to twenty existing <code>FSpanAllocator</code> allocators were then converted: GPUScene instance scene data, instance payload, and lightmap allocators, plus the Nanite material and editor hit-proxy buffers, behind <code>GPUSCENE_USE_TLSF_ALLOCATOR</code> and <code>NANITE_MATERIALS_USE_TLSF_ALLOCATOR</code> (<code>68c85fee</code>); skinning bone and transform buffers with the anim-sequence and anim-bank transform providers (<code>9fe7f2f6</code>); and curve skinning rest/deformed allocators plus the SceneCulling cell-chunk-id allocator (<code>bd67821a</code>). Every conversion is behind a compile define defaulting to 0, and the GPUScene defrag is behind <code>r.GPUScene.Defrag.*</code> CVars defaulting to off. Stats overlay registration was generalized to a getter-based seam so both allocator types report into <code>SpanAllocator.Stats.*</code>.</p>
<p><strong>Why this is important</strong></p>
<p>Nothing changes this week. The defrag is the part to understand now: when enabled, it selects the highest-offset persistent primitives each step and injects force-reallocate updates into the live scene update queue, with SceneCulling and Lumen reconciling through the normal <code>FScene::Update</code> path. Nothing else in the engine slides live GPUScene instances toward lower offsets while the scene is updating. Worth knowing before the flip, not after.</p>
<p><strong>Who should care</strong></p>
<p>Rendering engineers on large open worlds, and anyone who has hit GPUScene fragmentation or span-allocator high-water growth.</p>
<p><strong>Urgency</strong></p>
<p>Track. Infrastructure staged for a later flip, not a change you will see today.</p>
<h3 id="the-editors-ai-agent-stack-is-being-hardened-against-its-own-tool-calls">The editor’s AI agent stack is being hardened against its own tool calls</h3>
<p><strong>What changed</strong></p>
<p>The work in <code>AIAssistant</code> and <code>ToolsetRegistry</code> this week is almost entirely defensive: undo semantics, input validation, and non-destructive graph edits. <code>FAgentUndoManager</code> gives per-conversation undo for agent tool calls, with a per-agent mode that is explicitly not a stack unwind. Each tool call runs in a capture window with a dedicated transaction buffer swapped over <code>GEditor-&gt;Trans</code>, teeing pre-state into a reinstancing-safe record store that rollback replays, because flip-applying engine records can crash on reinstanced objects. It also guards objects the user hand-edited after the agent so rollback does not clobber them (<code>75271521</code>). It was backed out (<code>9162cdf4</code>), re-landed with fixes for divergence between the source stream and Main (<code>3fe14476</code>), and backed out again the same day (<code>0679850b</code>).</p>
<p>Enum properties in ToolsetRegistry had no registered JSON converter, so they fell through to <code>FJsonObjectConverter</code>’s numeric import path, which writes an out-of-range integer to the enum with no range check. An agent passing <code>{&quot;shareType&quot;: 7}</code> could drive a tool’s enum switch into a <code>checkNoEntry()</code> path and take down the editor. <code>FToolsetEnumConverter</code> now validates plain enums (range, fractional, int64 overflow, <code>_MAX</code> sentinel, unknown name) and bitmask enums as bitfields (<code>16acb955</code>). <code>write_graph_dsl</code> moved from full-replace to diff-based editing: it re-decompiles the graph, diffs incoming DSL statement by statement, and leaves unchanged statements’ nodes completely untouched including positions, wires, reroute knots, and node ids. It also adds audit verbs (<code>find_disconnected_nodes</code>, <code>find_duplicate_entry_nodes</code>, <code>find_duplicate_graphs</code>) specifically to clean up graph corruption left by previously failed writes (<code>afef1d65</code>). Around the edges: an access-control model with read, write, and execute modes for toolset tools (<code>88cd8fd5</code>), a cross-conversation tool-response contamination fix (<code>8dae6628</code>), a guard against async tool-call completion during exit-time UObject purge (<code>4808a910</code>), and new toolsets for undo history (<code>f87e5a6f</code>), TextureGraph (<code>c2807012</code>), and GeometryTools (<code>03b0e722</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The engineering problem here is reversibility: making it safe for an agent to touch a live editor session and undo what it did. The audit verbs are the most honest detail in the cluster, because Epic is shipping tools to repair graph corruption its own agent left behind. And the undo manager shipping, being backed out, re-landing, and being backed out again inside four days tells you how invasive swapping the editor’s transaction buffer actually is.</p>
<p><strong>Who should care</strong></p>
<p>Tools engineers, technical directors evaluating in-editor agent workflows, and anyone building on ToolsetRegistry.</p>
<p><strong>Urgency</strong></p>
<p>Track. The undo path is not settled, so do not build against it yet.</p>
<h3 id="the-actor-is-becoming-optional">The actor is becoming optional</h3>
<p><strong>What changed</strong></p>
<p>Three separate teams spent the week removing <code>AActor</code> and <code>UActorComponent</code> assumptions from systems that had them baked in. Mover lost its dependency on an actor component in the state machine (<code>71e7bcbf</code>), had <code>MovementMixer</code> converted from an owned UObject-ish thing into a regular C++ class owned by the state machine explicitly to decouple Mover core simulation from the actor framework (<code>4dd1628b</code>), and gained operational mode options so an external system can drive a Mover object and suppress simulation conflict, with attached movement and locally-driven seamless cinematics as the stated use cases (<code>96da392a</code>). ChaosMover picked this up on the physics side: the backend can now take a <code>TScriptInterface&lt;IPhysicsBodyInstanceOwnerResolver&gt;</code> instead of relying on the MoverComponent’s <code>UpdatedComponent</code>, and <code>UNetworkPhysicsComponent::GetController()</code> now walks the owner chain rather than assuming direct ownership by a Controller or Pawn (<code>3cfc7605</code>).</p>
<p>The editor side moved in lockstep. Editor modes can now allow or disallow non-actor selections, with the actor-only <code>IsSelectionAllowed</code> deprecated and unconverted modes defaulting to permitting non-actors behind <code>Editor.ModesAllowNonActorSelectionByDefault</code> (<code>e3d55a9a</code>). <code>IActorPickerMode</code> and <code>FEdModeActorPicker</code> were extended to use <code>HHitProxy::GetElementHandle()</code> so entities, which use <code>HEntityComponentHitProxy</code> instead of <code>HActor</code>, can be picked in the viewport (<code>f6d89ab1</code>). A crash fix confirms this is live rather than aspirational: “Since entity proxy actors were disabled by default, entity element handles go directly into editor selection sets” (<code>eb511c4f</code>). Supporting signals: <code>EntityFrameworkTests</code> and <code>EntityFrameworkEditorTests</code> moved out of NotForLicensees (<code>1b276cf4</code>), SceneGraph per-player custom primitive data callbacks moved off <code>mesh_component</code>, where they allocated two event UObjects per instance, into an opt-in <code>player_primitive_data_component</code> (<code>c3cc53c1</code>), and Mass core continued decoupling from Engine (<code>99c7cab4</code>, <code>01626205</code>). Mass also removed code deprecated in 5.6 across 22 files (<code>73ebccd3</code>) and replaced the now-deprecated <code>CSubsystem</code> with <code>UE::Mass::CSystem</code> (<code>c290a520</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The compatibility shim that let entities masquerade as actors in editor selection is off, which is exactly why the picker and mode-selection APIs had to be widened in the same week. Epic is rewriting gameplay simulation code so it can be hosted on something that is not an actor. Anything a studio has written against <code>IsSelectionAllowed</code>, <code>IActorPickerMode</code>, or Mover’s assumption that there is a MoverComponent on an <code>AActor</code> is on a deprecation clock, and the Mass removals are already hard breaks.</p>
<p><strong>Who should care</strong></p>
<p>Gameplay engineers using Mover or Mass, tools engineers with custom editor modes or pickers, and anyone tracking SceneGraph.</p>
<p><strong>Urgency</strong></p>
<p>Track, and act now if you have custom editor modes or Mass code that was riding 5.6-era deprecations.</p>
<h3 id="mobile-scalable-local-lights-vulkan-modernization-and-the-first-bones-of-a-frame-pacing-dag">Mobile scalable local lights, Vulkan modernization, and the first bones of a frame-pacing DAG</h3>
<p><strong>What changed</strong></p>
<p><code>r.Mobile.ScalableLocalLights</code> (default off) splits local point and spot lights into three tiers on the mobile deferred path: near keeps the existing per-light volume draw with full shading models, IES, shadows, and light functions; medium uses one instanced draw per light type with simplified diffuse and specular; far is diffuse-only. To make it work, Metallic and AO were swapped in the GBuffer so <code>DiffuseColor</code> needs a single <code>GBufferC</code> sample (<code>8687b3ca</code>), with a follow-up making medium and far instanced local lights respect primitive lighting channel settings (<code>97e96c48</code>).</p>
<p>Vulkan added <code>VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ</code> support for dynamic rendering (<code>ab197057</code>) and <code>VK_KHR_pipeline_binary</code> plumbing (<code>1eac9c6b</code>), consolidated image acquire onto either a semaphore or a fence and removed <code>VULKAN_USE_IMAGE_ACQUIRE_FENCES</code> with desktop still on fences (<code>b525937f</code>), and picked up a real correctness fix: textures with <code>StoreAction::EMultisampleResolve</code> that are not memoryless are now written, fixing lost contents when multiple passes shared MSAA targets, such as the clear in editor selection outlines (<code>60421447</code>).</p>
<p>On latency, an experimental frame-timing DAG landed compiled out behind <code>UE_TASK_TIMING_ENABLE</code>. It builds a per-frame DAG of thread work where the shortest path is the frame’s critical path, intended to extend into GPU work via platform RHIs and eventually drive game-thread throttling to cut input latency, and it adds <code>FRHIPresentArgsWithStats</code> (<code>8e606bd7</code>). Enhanced Input now applies continuous and forced input at consume time, removing a +1 frame latency (<code>3c273389</code>), with the game thread polling the input thread fresh instead of waiting for the next poll (<code>48dc2716</code>). Directional VSM projections can now write directly into the shadowmask texture when the projection is the first writer, eliminating the intermediate texture and the fullscreen composite, which mostly helps low-end platforms (<code>0abadf08</code>). Also here: instanced static mesh GPU LOD selection touching virtual shadow maps (<code>4d2ffa93</code>), dithered LOD transitions for GPU-instance-LOD instanced meshes in <code>bRenderStatic</code> mode driven through the instance culling pass rather than <code>GetDynamicMeshElements</code> so mesh draw command caching is preserved (<code>ba525ddc</code>), PSO precache skipping post-process and translucency-lighting-volume material precaching on low-core <code>PCD3D_ES3_1</code> (<code>92dc62b8</code>), net receive processing time limits with optional congestion control (<code>4c47cb82</code>), and a client-side CPU-time circuit breaker for Verse <code>&lt;predicts&gt;</code> with per-execution timeout and loop budgets feeding a leaky-bucket breaker (<code>a11f0644</code>).</p>
<p><strong>Why this is important</strong></p>
<p>The mobile direction is the notable one: reduce per-light cost by distance tier rather than by cutting light counts. The GBuffer channel swap is the catch, and it will bite anyone with custom mobile shading code regardless of whether they turn the feature on. The frame-pacing DAG is not usable yet, but it is the first visible piece of an input-latency story.</p>
<p><strong>Who should care</strong></p>
<p>Mobile and platform engineers, Vulkan backend maintainers, and anyone shipping many-light scenes on mid-tier hardware.</p>
<p><strong>Urgency</strong></p>
<p>Track. Act now only if you maintain custom mobile GBuffer or shading code, in which case check the Metallic and AO swap.</p>
<h3 id="the-stabilization-wave">The stabilization wave</h3>
<p><strong>What changed</strong></p>
<p>Those defect fixes cluster hard: Control Rig (six), Sequencer (six), PCG (five), plus a set of Mac and Linux crashes, one of which had been hiding behind a lucky uninitialized byte on MSVC.</p>
<p>Control Rig is the heaviest. <code>64874c17</code> replaces a <code>StaticLoadObject</code> workaround that deadlocked the cook with version-gated dynamic imports, adds a coalescing re-entrancy guard to <code>RecompileModularRig</code> to fix random heap corruption from nested recompiles tearing down module instances mid-initialization, and bounds-checks override copies against a possibly partially-loaded class layout. It was backed out (<code>44f6ba17</code>) and had not re-landed by week’s end. <code>RigHierarchy</code> topology sharing now uses a dedicated refcount behind a transactionally-safe mutex instead of <code>TSharedPtr::IsUnique()</code>, which is unreliable inside an AutoRTFM transaction and was producing a use-after-free (<code>5e7f6f96</code>).</p>
<p>Two cross-platform fixes are worth reading for the root cause alone. <code>1cc86453</code> makes the <code>WITH_EXTENDED_TLS</code> singleton immortal via placement-new into static storage, because its function-local-static destructor ran at process exit and freed the per-thread TLS backing store while the allocator and lingering threads still used it, crashing only the Mac-host iOS and tvOS cooks with SIGILL. <code>ebcefa8c</code> traces a Linux editor crash to a <code>SLATE_ARGUMENT</code> bool that was never initialized, where MSVC happened to give it 0 and clang gave it garbage that produced an out-of-bounds slot index.</p>
<p>Elsewhere in the same vein: out-of-bounds crashes from unvalidated transform selections in GeometryCollection Dataflow nodes (<code>8e38d6d0</code>), a Mass entity leak plus a tripped <code>SetPuppetHandle</code> assert from dormant pooled actors staying world-registered (<code>5866fc3e</code>), a silent poison leak in the new material translator that triggered an assert (<code>a8f53b37</code>), a TSAN data race on <code>FNiagaraTypeDefinition</code>’s lazy Size and Alignment cache (<code>cc68d4d5</code>), an AutoRTFM race on <code>FContext::Active</code> when reading CurrentThreadID (<code>034c85dd</code>), and a DynamicWind race with the cook step (<code>4b7275ce</code>). <code>MallocStomp2</code> was made platform-agnostic with two virtual address reservation strategies (a ring buffer under 1 TB of VA space, multi-block up to 32 TB otherwise), a fixed underrun mode where the payload is page-aligned against an unmapped guard rather than a committed page, double-free and free-of-decommitted detection, and console commands to trigger each error class (<code>8ff06b1e</code>).</p>
<p>Four infrastructure fixes are act-now if they touch you. Horde’s once-a-minute Perforce health probe had no deadline, so a server accepting TCP but never responding wedged the whole tick under a cluster-wide Redis lock and failed server selection fleet-wide after 7.5 minutes; probes are now capped at 30s (<code>5f6c7776</code>). Linux bundled OpenSSL symbols are isolated via a private version script, fixing a crash on cloud-licensing systems where GPU drivers load their own SSL libraries (<code>0ffae966</code>). Steamworks arm64 Android binaries are now staged in Installed and Launcher builds, where <code>Steamworks.build.cs</code> previously threw when packaging any Android project with OnlineSubsystemSteam enabled (<code>1ab38619</code>). And UBA runtime files that were missing from staged and downloaded builds were restored (<code>45abb63e</code>).</p>
<p>Worth a line each: the Renderer got a private PCH after ClangBuildAnalyzer measured <code>SceneRendering.h</code> at 150.5s and <code>BasePassRendering.h</code> at 89.1s of frontend parse across a Win64 Development rebuild, because ~40 unity TUs each re-parsed the private headers (<code>a8b65242</code>); Gauntlet gained <code>-build=cotf</code>, running a cook-on-the-fly server as an implicit editor session role from loose workspace binaries with no staged build and no upfront cook, verified end to end on Lyra (<code>ad825fcd</code>); Aftermath GPU crash failure classification and a crash fingerprint were added to the crash context (<code>53c2f87a</code>); a new <code>.rhiresourcesnapshot</code> format captures <code>RHIGetTrackedResourceStats()</code> for loading into tools like the Render Resource Viewer (<code>240db376</code>); Android TargetSDK 36 changes landed in <code>UEDeployAndroid.cs</code> (<code>4bf0b050</code>); and <code>TPointHashGrid3</code> now falls back to linear scan when the search region is very large or when modulo binning would bin a distant min and max close together, which is a correctness bug and not only a perf one (<code>8300cbaa</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Individually most of these are unremarkable. Collectively they say the branch is in convergence. The two cross-platform fixes are worth reading because the root causes are patterns you will hit in your own code: a function-local static destructor racing process teardown, and an uninitialized member that only misbehaves under a different compiler.</p>
<p><strong>Who should care</strong></p>
<p>Everyone on ue6-main, specifically animation, Sequencer, and PCG users, Mac and Linux editor users, and self-hosted Horde operators.</p>
<p><strong>Urgency</strong></p>
<p>Act now on the infrastructure four if they apply to you. Track the rest, and pull what matters if you are pinning a build.</p>
<hr>
<h2 id="the-5x-stabilization-sidebar-ue5-main">The 5.x Stabilization Sidebar: ue5-main</h2>
<p>217 commits on ue5-main, 4.5x last week’s 48, but the headline number overstates the engine-facing delta. There is no engine version bump: nothing touched <code>Engine/Build/Build.version</code>, and all five commits titled “Version bump” or “Package bump” (<code>1198f4d6</code>, <code>264f084d</code>, <code>5b7dd791</code>, <code>cdc7c60e</code>, <code>1bd78343</code>) modify <code>Engine/Extras/RoboMerge/v3/package.json</code>, Epic’s internal merge bot. This is bug-fix-and-port work, not release prep.</p>
<p>The largest cluster is engine code rather than tooling: 37 commits touching <code>Engine/Plugins/Experimental/UAF</code> across 185 file touches, including <code>UAF Notify V1: Runtime</code> (<code>3412ea58</code>), initial anim node support for montages (<code>ddc68852</code>), and Proxy Table support (<code>d62e90e8</code>). Two further commits carrying the same UAF integration-stream tag are explicit UE6-to-UE5 cherry-picks into unrelated editor gizmo and Control Rig code (<code>ab3c7edd</code>, <code>f66f093a</code>). Close behind is RoboMerge at 33 commits, 32 from one author, which inflates the count without touching anything a shipping studio consumes.</p>
<p>The 5.8 hotfix line is visibly live in the metadata: <code>df54130f</code> is tagged “(5.8.2 hotfix)”, <code>715a826f</code> notes “Bug introduced in 5.8.1”, and <code>4049e80b</code> was tested in both FNMain and Release 5.8 editors. Worth a cherry-pick if you ship on 5.8: a Mass actor spawning top crash, where <code>MassActorSpawnerSubsystem</code> now recovers instead of asserting when a spawned or pool-retrieved actor’s <code>UMassAgentComponent</code> is in an unexpected registration state, with root cause deferred (<code>df54130f</code>); a blueprint rename and redirector data-loss bug where moving a blueprint onto its own redirector caused a fatal error (<code>afaf8482</code>); a DLC cook failure under <code>-basedonreleaseversion</code> from resolving filenames for WorldPartition generated packages that do not exist on disk (<code>62fe53b4</code>); an AssetRegistry gatherer deadlock (<code>83307182</code>); a StateTree heap use-after-free from recursive <code>SelectStateInternal_LinkedAsset</code> calls invalidating an array element held by reference (<code>a2685056</code>); and a global shader recompile assert from recompiled shaders being removed from ShaderMap Content but left in the CodeResource (<code>51dff0ae</code>). Also credible: <code>4a5b9eaa</code>, <code>d6178fe6</code>, <code>a125ae00</code>, <code>27986b0c</code>, and the 5.8.1 PCG pin validation inversion (<code>715a826f</code>).</p>
<p>The structural finding is the dual landing. 128 of the 213 ue5-main commits we could compare (60%) have a byte-identical first-line commit message on ue6-main, and about 120 of those land on the same calendar day in both branches. Epic is landing these in both branches at once, not backporting them weeks later. Confirmed same-day pairs include the StateTree fix (<code>a2685056</code> / ue6 <code>dcb1b670</code>) and the DLC cook fix (<code>62fe53b4</code> / ue6 <code>f1423aaa</code>). Roughly 60% of the 85 ue5-only commits are the UAF and Proxy Table animation work plus six localization-automation commits; the rest is the 5.8 hotfix traffic covered above. SHAs in this section reference ue5-main unless noted.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>Toolsets and MCP are escaping the editor into cooked runtime builds.</strong> Mostly under a single ticket, <code>PerfToolset</code> was converted from an editor-only plugin to a runtime plugin so its stats and GPU profiler tools can be served from cooked builds (<code>524fc101</code>), the ModelContextProtocol adapter for ToolsetRegistry moved into the base runtime module (<code>7453886e</code>) with a follow-up fixing MCP not seeing ToolsetRegistry due to module load order (<code>683c606a</code>), ToolsetRegistry settings were promoted to a per-project setting explicitly so cooked builds can still use block lists (<code>30c2fb5c</code>), plugin manifest generation moved from writing into the source content directory to generating during cook (<code>0c02acca</code>), and the cvar tool moved out of <code>EditorAppToolset</code> into <code>ConsoleToolset</code>, described as available at cooked runtime (<code>66602199</code>). Most agent commits on ue6-main are editor-facing and easy to file as an authoring feature. This cluster is infrastructure for exposing engine toolsets over MCP from a cooked, shipped build, with a per-project allow and block list as the gate. If the direction holds, the questions are operational: what the block list defaults to, whether the MCP surface is compiled out of Shipping, and what the attack surface looks like on a cooked client.</p>
<p><strong>The derived data cache is being pulled off its process-global singleton.</strong> <code>CreateCache</code> was corrected to return <code>ICacheEx*</code> (<code>f1032c6c</code>), <code>ICacheStore</code> was updated to use the index across more of its API (<code>d4241031</code>), the hierarchy tests were migrated to work on <code>FCache</code> (<code>d2ca136f</code>) while tests that operate on the global cache were removed outright (<code>9c92aa7e</code>), the hierarchy constructor dropped an unnecessary <code>OutOwner</code> (<code>ed509870</code>), <code>StopStore</code> coverage was extended (<code>db0b487a</code>), and derived data loading can now take an optional cache override (<code>b37a9e77</code>). Almost all of it is marked <code>#rnx</code>, so none of it will show up in release notes, and the messages are one-liners that reveal nothing about intent. But “remove tests that operate on the global cache” plus “take an optional cache override when loading derived data” points somewhere specific: more than one cache instance in a process, or derived-data loads scoped to a particular cache. If you run a shared DDC, a cook-server topology, or a custom <code>ICacheStore</code>, this is the API that is about to move.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>53 commits were pure noise and got no further reading. Beyond that, we set aside the usual branch churn (at least 15 backout and revert commits among the routine traffic alone), whitespace fixes, P4VUtils binary bumps, <code>Commit.gitdeps.xml</code>-only submissions, Test Automation Hub dashboard iteration, Epic-internal CI plumbing, test-harness fixes, and UBT naming-convention refactors. RoboMerge and ModularTestFramework are genuinely low-signal on both branches. A long tail of individually routine null guards and bounds checks was counted into the stabilization wave rather than listed. Two clusters were read and deliberately deferred: the new material translator rollout (<code>a8f53b37</code>, <code>bd9a63cc</code>, <code>7377912b</code>, <code>b0c39317</code>, <code>aa759628</code>, <code>6fee7ad4</code>), which is still converging onto legacy behavior and carries no decision this week, and cooked-server dependency trimming that drops the Renderer module for dedicated servers (<code>64414b0a</code>, <code>4fbda163</code>, <code>b5ce96ce</code>), which is worth knowing about if you are chasing server build size and otherwise not.</p>
<hr>
<h2 id="closing">Closing</h2>
<p>The busiest week since mid-May, and almost none of it is features. Epic spent it repairing the machinery that ships the game rather than the game. Pull the build-cache fixes before you find them the hard way.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine except in the 5.x sidebar, where they reference <code>ue5-main</code>.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-jul-20-26-2026/og.184be0869c.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: C++23 Becomes the Default and the Editor’s AI Agent Gets Bolted Down (July 13 to 19, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-jul-13-19-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-jul-13-19-2026</guid>
      <pubDate>Mon, 20 Jul 2026 07:08:11 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>Epic is back from summer shutdown: 789 commits on ue6-main, 48 on ue5-main. Engine modules now compile as C++23 by default, module circular dependencies are fully gone, and a dedicated audit is making the editor’s in-editor AI agent stop crashing the editor when it gets fed bad input. Plus a 5.x stabilization sidebar for studios shipping on 5.8 today.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-the-build-system-grows-up-and-the-agent-gets-bolted-down">Unreal Engine Weekly: The Build System Grows Up and the Agent Gets Bolted Down</h1>
<p><strong>Week of July 13 - 19, 2026</strong> | <strong>789 commits on <code>ue6-main</code>, 48 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>This was a systems-plumbing week more than a feature week, and the graph snapped back from Epic’s summer shutdown: 789 commits on <code>ue6-main</code>, and <code>ue5-main</code> off zero to 48 as 5.x maintenance resumed. The dominant thread is Epic hardening its in-editor LLM agent toolsets and, underneath them, modernizing UnrealBuildTool (C++23 by default, circular dependencies gone) and the distributed build cache. The renderer’s FScene continued its slow decomposition into modular Scene Extensions, and a steady stream of crash-class and data-race fixes landed across Mutable, Iris, audio, and material graphs.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<p><strong>Editor AI tooling shifts from buildout to hardening.</strong> The in-editor LLM agent toolset thread that first earned its own editorial theme in mid-June has not slowed; this week it entered a defensive audit phase, systematically converting agent-callable entry points from crash-on-bad-input to graceful failure. That Epic is spending a dedicated audit making these paths non-fatal is the strongest signal yet that the agent is live enough for bad tool calls to routinely take down the editor. Roughly four months into this initiative, the direction is unambiguous: the editor is becoming a first-party target for an LLM agent, and the plumbing underneath it is being made production-safe.</p>
<p><strong>Build and toolchain modernization is the sustained through-line on ue6-main.</strong> What read as post-cut “polish” earlier in the year has, on ue6-main, become outright modernization: C++23 is now the default standard for engine modules, module circular dependencies are fully eliminated, and UnrealBuildTool’s incremental detection and manifest handling moved onto modern .NET. In parallel, the distributed build cache (UBA observability, Zen 5.8.18 with GC and oplog-import correctness fixes) keeps maturing week over week. This is the most consistent multi-month thread in the data, and its character has moved from cleanup to structural change, so source-build studios should expect real validation work rather than passive upgrades.</p>
<p><strong>Renderer internals are being decomposed into modular Scene Extensions.</strong> A Scene Data refactor that looked like single-week scaffolding in early June has returned as a sustained effort to move state off the monolithic FScene into self-contained, dirty-tracking Scene Extensions. Velocity, runtime virtual texture, capsule shadows, and GPUScene invalidation are all being pulled out along the same pattern, with the stated goal of decoupling input from scene updates so the pipeline can eventually be driven by a state stream. This is invisible to shipping behavior but high-blast-radius for anyone carrying custom renderer patches, and its steady cadence points to an architectural direction rather than a one-off cleanup.</p>
<p><strong>Distributed world-sim substrate is quietly firming up its API.</strong> RemoteObjects/DSTM transactional infrastructure is consolidating out of <code>#ifdef</code> scaffolding into public functions, the usual tell that a subsystem is stabilizing its surface ahead of wider internal use, and networking activity ticked up to a window high alongside Iris replication hardening. Taken together this reads as investment in server-authoritative large-world and multi-server simulation as a forward substrate. It is early and low-volume, but worth tracking for any studio with large-world or distributed-sim ambitions.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Engine modules now compile as C++23 by default (<code>48648dad</code>). If you build UE6 from source or pin an older standard, validate now. This is a real state change, not activity noise.</li>
<li>Epic’s in-editor AI agent toolsets are real enough that bad tool calls were crashing the editor. A systematic audit is converting agent-callable entry points from check()-and-crash to graceful failure, and each fix protects every caller, not just the agent.</li>
<li>Module circular dependencies are fully removed (<code>8a03506c</code>, <code>8bc993b6</code>), a structural milestone that is already producing regressions (Engine to UnrealEd decouple crashed on editor exit, <code>585044e5</code>).</li>
<li>IoStore On-Demand install/streaming got a full week of production hardening. Studios shipping partial-install titles on write-constrained consoles should expect materially different install pacing.</li>
<li>Incremental-cook determinism looks unsettled on this branch (a cluster of “native change not picked up” bumps). Lean on clean cooks for cert builds until the cadence quiets.</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="epics-in-editor-ai-agent-toolsets-and-the-crash-hardening-audit-behind-them">Epic’s in-editor AI agent toolsets and the crash-hardening audit behind them</h3>
<p><strong>What changed:</strong> Epic is exposing the editor to an LLM agent through Python toolsets (SceneTools, MaterialTools, CurveTableTools, BlueprintToolset, UMGToolSet, PluginToolset, AssetTools) plus a reversible Blueprint-graph text DSL that decompiles a Blueprint to text and recompiles it (read_graph_dsl / write_graph_dsl). Most visible this week is a defensive audit tagged “Audit follow-up to CL 55439527 / UE-385503”: every agent-callable entry point that could feed bad input into an engine subsystem is being converted from check()-and-crash to graceful failure. DSL round-trip correctness landed for comment-box geometry on cold loads (<code>25dac488</code>), native member-function calls resolving to a creatable type_id (<code>5f688b16</code>, <code>54be5e06</code>), and multi-output pure nodes like Map/Find binding to the right pin (<code>4e43382e</code>, <code>752be542</code>). Crash-to-non-fatal fixes covered StaticMesh merge with no renderable mesh (<code>7e639551</code>), adding a simple curve to a rich-curve CurveTable (<code>0b346e7e</code>), null material-expression iteration (<code>74f0089a</code>), and find_actors with an interface filter (<code>e54c1a73</code>). New capabilities include containment-checked download_file into a virtual path (<code>b6c25c80</code>) and MaterialTools get_statistics shader-stat exposure (<code>8573def5</code>).
<strong>Why this is important:</strong> The fact that Epic is spending a full audit making these paths non-fatal tells you the agent is live enough that bad tool calls were routinely taking down the editor. Each hardening fix protects every existing caller, including hand-authored Blueprints, not just the agent. Note that much of this lands as Commit.gitdeps.xml-only changes because the Python toolset code is virtualized, so any “build” classification here is misleading.
<strong>Who should care:</strong> Tools engineers, technical designers, and anyone tracking Epic’s LLM-in-editor direction.
<strong>Urgency:</strong> Track. Not something to adopt, but the clearest signal yet of where the editor is heading.</p>
<h3 id="unrealbuildtool-modernization-c23-by-default-circular-dependencies-eliminated">UnrealBuildTool modernization: C++23 by default, circular dependencies eliminated</h3>
<p><strong>What changed:</strong> The default C++ standard for engine modules is now C++23 (<code>48648dad</code>), with C++26 enum staged (<code>626319cb</code>). Epic finished removing module circular dependencies: the CircularDependenciesAllowList is deleted (<code>8a03506c</code>) and the remaining circular-dependency check is gone (<code>8bc993b6</code>). On Windows, the external cl-filter.exe is replaced by inbuilt MSVC timing parsing (<code>f2e0d9a1</code>, removal <code>0ccd35f8</code>), and UBT now serializes precompiled manifests and command-line handling through modern .NET (<code>e6c8239f</code>, <code>958dd158</code>). Incremental detection got smarter: source-file working sets driven by EpicLore indexing, Perforce-opened files, and Horde preflight state (<code>9211ee27</code>, <code>18da622f</code>), with action ordering partially randomized to raise cross-host cache-hit rates (<code>3f5c9c7d</code>). GraphEditor was decoupled from BlueprintGraph (<code>ba1df3c0</code>).
<strong>Why this is important:</strong> The C++23 default is a genuine state change that source builds and older-standard projects need to validate against now. The circular-dependency removal is a structural milestone, but it is already producing regressions: the Engine to UnrealEd decouple caused an editor-exit crash (<code>585044e5</code>). Toolchain wrappers that shell out to cl-filter need to adapt. One instability signal worth noting: UHT input/output caching was disabled, re-enabled, then disabled again within the week (<code>dc083352</code>, <code>709218a9</code>, <code>d4a6c530</code>), so that path is not settled.
<strong>Who should care:</strong> Build engineers and engine programmers, especially anyone on tip-of-tree or building UE6 from source.
<strong>Urgency:</strong> Act now if you build from source. Validate your standard-pinning and any cl-filter-dependent tooling.</p>
<h3 id="distributed-build-cache-grows-up-uba-observability-and-zen-5818">Distributed build cache grows up: UBA observability and Zen 5.8.18</h3>
<p><strong>What changed:</strong> UBA got a week of cache-service stats-page work (hit percentage, per-client TCP/NIC stats, crypto-failure counts with remote IPs, hit-rate graphs, CAS download/delete) alongside reliability fixes: agent crash and recovery when the in-process storage proxy goes bad (<code>abd9dc83</code>), the scheduler no longer over-requesting remote help (<code>e799aa8b</code>), and shutdown crashes (<code>174e6431</code>, <code>751079e9</code>). The Zen storage server jumped to 5.8.18 (<code>67d8080d</code>) with OpenTelemetry throughout, a single-record <code>zen cache drop</code>, and GC and oplog-import correctness fixes that previously could leave a committed op referencing an already-deleted chunk. Per-client bottleneck visibility (<code>81a94db4</code>, <code>b3cbb1bf</code>, <code>3a19570e</code>) and sortable client history (<code>a72416ff</code>) landed too, and Zen hub mode now scales past its ~1024-instance limit.
<strong>Why this is important:</strong> The Zen GC and oplog-import fixes are correctness, not cosmetics: a committed op referencing a deleted chunk is a data-integrity hazard for a shared cache. The observability work makes per-client bottlenecks visible for the first time.
<strong>Who should care:</strong> Build and infra engineers, and studios running shared Zen/DDC or UBA cache farms.
<strong>Urgency:</strong> Track, and act now if you operate a shared cache farm. The Zen bump is worth scheduling.</p>
<h3 id="fscene-decomposition-into-modular-scene-extensions">FScene decomposition into modular Scene Extensions</h3>
<p><strong>What changed:</strong> A sustained renderer refactor continues moving state off the monolithic FScene into self-contained Scene Extensions driven by dirty-tracking rather than the scene-update pass. The core change moves instance data out of FPrimitiveSceneInfo into FScenePrimitiveDataEngine/Renderer arrays and switches GPUScene, VSM cache invalidation, and SceneCulling to dirty-flag-driven updates (<code>fd2022a6</code>). Per-system extractions include velocity data to FVelocitySceneExtension (<code>db26d90c</code>), runtime virtual texture to its own extension (<code>55ddf77d</code>), and capsule-shadow caster primitives to an extension (<code>7da3df1b</code>). Skinning gained provider phases so dynamic wind can skin during the render phase while the normal path skins during scene update (<code>f38e3f39</code>). Measured win: roughly 150us off the critical path in CitySample from avoiding FPrimitiveSceneInfo and proxy hits in GPUScene.
<strong>Why this is important:</strong> This is internal plumbing with no behavior change, but it touches GPUScene, VSM invalidation, SceneCulling, and primitive data layout. The stated goal is decoupling input from scene updates so it can eventually be replaced by a state stream.
<strong>Who should care:</strong> Graphics and engine programmers maintaining custom render paths.
<strong>Urgency:</strong> Track. Anyone carrying renderer patches should expect merge pain.</p>
<h3 id="slate-rounded-box-shader-rewrite">Slate rounded-box shader rewrite</h3>
<p><strong>What changed:</strong> A significant rewrite of the Slate rounded-box shader landed (<code>3154d3b3</code>, PR #14958).
<strong>Why this is important:</strong> The rounded-box shader is the primitive behind nearly every Slate and UMG element with a corner radius: buttons, panels, cards, borders. A shader-level quality and performance change here has a broader blast radius than most individual rendering commits, touching essentially the entire editor and every shipping UMG UI. It arrived flagged as low-interest, which undersells it.
<strong>Who should care:</strong> UI programmers, tools engineers, and anyone shipping custom UMG.
<strong>Urgency:</strong> Track. Low risk, but visually and performance-relevant everywhere, so worth a smoke test on custom UI.</p>
<h3 id="hardware-ray-tracing-and-nanite-rt-continue-maturing">Hardware ray tracing and Nanite RT continue maturing</h3>
<p><strong>What changed:</strong> HWRT hardening continued: the ray-tracing instance buffer builder moved to RDG (<code>ebb0d147</code>), Nanite ray tracing gained assembly support in streaming traversal and stream-out (<code>37b99ff2</code>, <code>e6418a54</code>), and D3D12 opacity micromap support got index-format and index-only-linking improvements (<code>20183afa</code>). Several null and stale-pointer crashes were patched (Nanite RayTracingProxy <code>35c13cd6</code>, GPULightmass <code>5daa01e0</code>).
<strong>Why this is important:</strong> The OMM improvements matter for foliage and alpha-tested geometry cost in RT. Nanite-in-ray-tracing assembly transforms close a correctness gap that otherwise blocks HWRT adoption on instanced and assembly-heavy scenes.
<strong>Who should care:</strong> Graphics programmers and studios evaluating HWRT or Lumen-HWRT.
<strong>Urgency:</strong> Track.</p>
<h3 id="iostore-on-demand-install-and-streaming-hardening">IoStore On-Demand install and streaming hardening</h3>
<p><strong>What changed:</strong> The IAD/IAX subsystem (on-demand container mounting, chunk install/eviction, platform write-throttling) saw a coordinated cluster. Eager-defrag correctness now prevents pending defrag chunks from pinning CAS blocks (<code>e44bc911</code>), a dedup path that failed to locate the right chunk was fixed (<code>e83f0e7b</code>), and installer back-pressure was rewritten so “pressure” is driven by estimated time-to-write the download queue to disk rather than raw item or byte count (<code>4088fc55</code>). Write-budget accounting was simplified so subscribers see the full platform rate against a live budget counter instead of static bias-weighted fractions that starved solo subscribers (<code>0332d1fa</code>), plus a Cycles vs Cycles64 mismatch in throttle-delay math was corrected (<code>cb490d5a</code>). New histogram buckets and persistence analytics (<code>231871e5</code>, <code>5be99bf5</code>) and an iax.DumpMountedContainersToLog diagnostic (<code>009d795c</code>) rounded it out.
<strong>Why this is important:</strong> This is the runtime that decides what installs, what stays resident, and how aggressively the game writes to disk mid-session. The back-pressure rewrite changes install pacing behavior on write-limited targets.
<strong>Who should care:</strong> Studios shipping partial-install or streaming-install titles, especially on write-constrained consoles.
<strong>Urgency:</strong> Track. Worth reading before pinning an engine version for a title heading into cert.</p>
<h3 id="stability-and-crash-class-fixes">Stability and crash-class fixes</h3>
<p><strong>What changed:</strong> Two genuine memory-safety fixes stand out: a data race in the Mutable runtime around Task_Mutable_GetMaterials (<code>81bba34e</code>), and a recursion guard threaded through IsResultMaterialAttributes to prevent a stack-overflow crash on cyclic material graphs (<code>2f015c20</code>), a user-reachable content path. Iris fixed a use-after-free class bug where RPCs referenced dynamic replicated objects destroyed the same frame (<code>ad3ce764</code>). A wave-intrinsics undefined-behavior fix corrected GroupMemoryBarrierWithGroupSync executing in divergent control flow when the wave is narrower than the threadgroup (<code>819dbd72</code>), a correctness landmine that can work on one GPU and corrupt or hang on another. Also landed: a StaticMesh async-build fix eliminating a roughly 219-second cook stall behind DDC fetches (<code>69f0d76d</code>), a thread-safe dynamic-import registry on FLinkerLoad fixing load failures for legacy Control Rig override assets (<code>b5f0409e</code>, UE-381080), an AsyncPhysics RootCounts leak (<code>0ec43389</code>), and audio hardening of TCircularAudioBuffer (<code>669ddbba</code>).
<strong>Why this is important:</strong> The Mutable data race and the cyclic-material-graph stack overflow are both reachable from normal content workflows. The wave-intrinsics fix is the kind of bug that passes on your hardware and fails on a customer’s. The 219-second cook-stall fix is a straight cook-time win.
<strong>Who should care:</strong> Anyone on tip-of-tree, plus graphics programmers writing compute shaders and studios using Mutable or Iris replication.
<strong>Urgency:</strong> Act now if you are pinning a build. These are the fixes you want in.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>Incremental-cook determinism looks unsettled.</strong> Multiple standalone “bump cookincrementalversion / native change not picked up” commits landed this week (<code>325468c1</code>, <code>79de0d09</code>, <code>852c286f</code>, <code>fc2e7259</code>), plus an IoStore DDC version bump (<code>fff1af6c</code>) and an editor-domain digest lock-contention fix (<code>1a845c10</code>). Each is trivial on its own, but a cluster of “native change not invalidating content that uses it” fixes in a single week points at cook-key and invalidation gaps surfacing during parallel-incremental-cook work. On a build farm these produce stale cooked output without an obvious error. Treat incremental-cook determinism on this branch as not-yet-settled and lean on clean cooks for cert builds until the cadence quiets.</p>
<p><strong>RemoteObjects / DSTM transactional infrastructure is stabilizing its API surface.</strong> RemoteExecutor now exposes ConditionalExecuteTransactional so callers stop #ifdef-ing UE_WITH_DSTM (<code>161f9be7</code>), the libpas Remote Object Handle case was removed from CoreUObject because the Remote Heap no longer uses it (<code>e01274f1</code>), and remote-object-id globalization and server-id lifecycle continued tightening (<code>565258b2</code>, <code>e991093a</code>). Consolidating out of #ifdef scaffolding into a public function is the usual tell that a subsystem is firming up its API ahead of wider internal use. This is substrate for large distributed and multi-server world simulation. Track it if server-authoritative large-world sim is on your roadmap.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>Roughly 20 commits were filtered as pure noise (formatting, typo, trivial reverts) before analysis. Beyond that, we set aside several clusters as low-signal for this audience: an animation-runtime allocation-elimination sweep (per-frame heap allocations stripped from anim evaluation, e.g. <code>d0a3ca2d</code>, <code>b0ab9e74</code>) that is a free gain on character-dense scenes with no action needed; a Control Rig Constraints editing UX rewrite gated behind a CVar fallback (<code>f54d5bf1</code>), which is pure UX with the fallback signaling Epic is not yet confident; and a large body of uncategorized Verse, Solaris, and AutoRTFM churn (roughly 37 Verse and 17 AutoRTFM commits), SceneGraph/Entity/Prefab work, and Interchange and Audio Insights activity that did not carry a clear this-week decision for most studios. Skipping these is deliberate.</p>
<hr>
<h2 id="the-5x-stabilization-sidebar-ue5-main">The 5.x Stabilization Sidebar: ue5-main</h2>
<p><code>ue5-main</code> came back from the shutdown at 48 commits, and the character is exactly what a post-final-release maintenance branch should look like. There was no installer version bump this week, so no structural release event to report, and the single largest cluster (roughly 19 commits) was internal revision-control and backout-restore tooling rather than engine code. A handful of fixes are worth a cherry-pick if you ship on 5.8 today:</p>
<ul>
<li><strong>Lumen irradiance field gather</strong> got a real optimization: probe interpolation dropped from 0.93ms to 0.76ms (<code>9c140e9e</code>), alongside a correctness fix for miscomputed probe sample-position offsets (<code>26b898da</code>). Free performance for anyone on the irradiance-field path.</li>
<li><strong>A misaligned read in the stable PSO cache serialization</strong> was fixed, triggered by the FShaderHash change from SHA1 to XXH3 (<code>cb1bef43</code>). If you ship a bundled PSO cache, this is the class of serialization bug that fails quietly.</li>
<li><strong>IOStore chunk install:</strong> <code>-createchunkinstall</code> no longer wrongly includes non-chunk-0 <code>.utoc/.ucas/.sig/.uondemandtoc</code> files (<code>f4567b10</code>), relevant to anyone building chunked installs on 5.8.</li>
<li><strong>FPaths::IsStaged()</strong> had its <code>RequiresCookedData()</code> clause restored (<code>b34bd752</code>), a staged-build path-resolution fix worth noting if you saw oddities there.</li>
<li><strong>Platform fixes:</strong> a corrupted-mouse-image fix from a stride miscalculation in FMacCursor on Apple platforms (<code>5305bce1</code>), and a Linux fix for plugin builds against an installed engine failing to link (<code>0e5b877b</code>).</li>
</ul>
<p>A few changes landed on both branches: the Dynamic Wind work (cardinal batched-animation culling <code>a740c504</code>, reduced low-speed vibration <code>2dbaec56</code>, and skinning provider phases <code>8550ce34</code>, the last mirroring the ue6-main skinning-extension change above). Separately, the Composite editor kept gaining virtual-production tooling on 5.x, including a Transform 2D pass with a viewport gizmo (<code>61515299</code>) and per-layer pass-selection persistence (<code>30baf551</code>). Low, fix-and-trickle volume is exactly what 5.x should be doing now.</p>
<hr>
<h2 id="closing">Closing</h2>
<p>A quiet week on the surface, but the build system moved a real notch forward and a handful of the crash fixes are ones you want in before you pin a build.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine, except the 5.x Stabilization Sidebar, whose SHAs reference <code>ue5-main</code>. Many of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-jul-13-19-2026/og.e563dd1aa9.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: Three People, One Bot, and the Last Wall Comes Down (July 6 to 12, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-jul-6-12-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-jul-6-12-2026</guid>
      <pubDate>Mon, 13 Jul 2026 06:58:59 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>Epic is on its annual two-week summer shutdown. ue6-main ran 64 commits, ue5-main ran zero, and the entire week was written by three humans and a build bot. One of those humans drove the editor’s circular-dependency allowlist from 13 entries to zero, and shipped a live status page for the UBA build cache on the way out.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-the-last-wall-comes-down">Unreal Engine Weekly: The Last Wall Comes Down</h1>
<p><strong>Week of July 6 - 12, 2026</strong> | <strong>64 commits on <code>ue6-main</code>, 0 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>Epic is on its annual company-wide summer shutdown, two weeks off, and the commit graph shows it: 64 commits on <code>ue6-main</code> against a normal week’s ~880, following 75 the week before. One commit message says the quiet part out loud. The author of the Lore rename opens with “You madman! Why are you submitting this during summer break?” (<code>6ba090ed</code>).</p>
<p>Do not read a trend into the volume. This is not a slowdown, not a branch cut, and not a release-cycle signal. It is a holiday, and the graph will snap back.</p>
<p><code>ue5-main</code> saw zero commits. Not a handful, not a trickle: zero. 5.x stabilization is fully paused for the shutdown, so if you are waiting on a 5.x backport, assume nothing moves until Epic returns.</p>
<p>All 64 commits come from three people and a build bot. Henrik Karlsson has 41 of them. Matt Begoghina has 15. Wouter Burgers has 2. The <code>buildmachine</code> bot has 6. And in a week where almost nobody was working, Karlsson finished a campaign we have been tracking for four weeks: he drove the editor’s circular-dependency allowlist from 13 entries to zero, then shipped a live status page for the UBA build cache. If last week was “the walls keep falling,” this week the last one came down.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li><strong>The editor’s circular-dependency allowlist is now empty, down from 13 entries.</strong> UnrealEd, Kismet, BlueprintGraph, GraphEditor, KismetCompiler, and AnimGraph no longer form a link cycle, so they can be built and relinked independently. Editor iteration time drops, and forks and plugins that were getting Blueprint headers for free through UnrealEd’s shared PCH break (<code>dae0d248</code>, <code>e03cbcbd</code>, <code>10e8206a</code>).</li>
<li><strong>UBA’s cache service now serves a live status web page, and <code>UbaCli cacheproxy</code> tunnels a remote cache host’s dashboard to your machine.</strong> Build-cache observability landed in-engine, and it is the biggest new capability this week (<code>5bb6fad7</code>).</li>
<li><strong>A four-line UBT fix repaired nondeterministic shared-PCH cache keys.</strong> If your shared build cache has had an unexplained mediocre hit rate, start here (<code>a18c43ec</code>).</li>
<li><strong>Slate is being annotated for dynamic invalidation, widget batch by widget batch</strong>, including the entire property grid. Nothing changes behavior yet. It is the groundwork for turning invalidation caching on broadly (<code>22432ab7</code>, <code>c5cfe7ab</code>, and thirteen siblings).</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="the-editors-circular-dependency-allowlist-went-to-zero">The editor’s circular-dependency allowlist went to zero</h3>
<p><strong>What changed</strong></p>
<p><code>CircularlyReferencedDependentModules</code> was the long-standing escape hatch that let UnrealEd, Kismet, BlueprintGraph, GraphEditor, KismetCompiler, and AnimGraph reach into each other. It started this week at 13 entries. It ends at zero.</p>
<p>The AnimGraph cut removed all four <code>(&quot;AnimGraph&quot;, *)</code> entries and took the allowlist from 13 to 9 (<code>f78a9d69</code>). That commit is Build.cs-only, because three prep commits had already severed or bridged every reference (<code>2709d889</code>, <code>a3766671</code>, <code>27cd49ad</code>). Note that the prep relocated <code>UAnimPreviewInstance</code> into AnimGraphRuntime behind a class redirect (<code>a3766671</code>), so there is serialized-asset impact.</p>
<p>Then UnrealEd stopped linking Kismet. Kismet moved to <code>DynamicallyLoadedModuleNames</code>, with the DLL kept resident by loading KismetCompiler at UnrealEd startup so the bridge implementations still bind at static-init (<code>dc1f317a</code>). UnrealEd’s link dependency on BlueprintGraph was cut next (<code>2fce5938</code>), followed by UnrealEd to GraphEditor (<code>6474dcc8</code>). The last three entries died in a single 50-file commit that inverted node and pin color lookups, plus roughly 57 context-menu command references, behind new BlueprintGraph-owned interfaces (<code>dae0d248</code>). KismetCompiler was then absorbed into BlueprintGraph, emptying the list (<code>e03cbcbd</code>), and its shim module was deleted in a 74-file cleanup that is mostly Build.cs edits across ControlRig, Chooser, LiveLink, Optimus, and Lyra (<code>6410df6a</code>).</p>
<p>The pattern is consistent and reusable. The lower module owns a pure-virtual interface header (<code>IK2NodeColorProvider</code>, <code>IK2GraphContextCommands</code>, <code>IBlueprintEditorBridge</code>, <code>IAnimBlueprintEditorBridge</code>), the upper module implements it and self-registers at DLL static-init, and the dependency flips from a link edge to a name-loaded module. Providers forward per-call rather than caching, specifically to dodge static-init ordering hazards (<code>dae0d248</code>).</p>
<p>Buried in the KismetCompiler absorption is a small unrelated fix worth a line: <code>EdGraphCompiler.cpp</code>’s <code>FindNodeByClass</code> now skips null entries in <code>Graph-&gt;Nodes</code> instead of dereferencing them (<code>e03cbcbd</code>).</p>
<p><strong>Why this is important</strong></p>
<p>A circular link group cannot be built or relinked independently. Touch one module in the cycle and you drag the whole cluster with it. For the Blueprint editor stack, that cluster is six modules sitting directly under the code most editor programmers touch every day, so the cost showed up as editor iteration time on every incremental build.</p>
<p>The sharper consequence is the shared PCH. BlueprintGraph headers came out of <code>UnrealEdSharedPCH.h</code>, which by Epic’s own count was exporting those types into 1,200-plus inheriting modules (<code>10e8206a</code>). Anything that was transitively picking up BlueprintGraph, Kismet, or AnimGraph headers through UnrealEd or its shared PCH will now fail to compile, and will need explicit Build.cs dependencies. Epic fixed its own code pre-emptively: UnrealEd Kismet2 consumers across roughly 70 modules (<code>934e6b00</code>), AssetTools (<code>52eb36a7</code>), LyraEditor (<code>e4307de0</code>), and Chooser.h (<code>79d80a95</code>). Third-party code gets no such courtesy.</p>
<p>Also worth checking: AnimGraph and BlueprintEditorLibrary are now demand-loaded rather than resident. Any code that assumed they are already loaded at editor startup should be revisited.</p>
<p><strong>Who should care</strong></p>
<p>Anyone maintaining a custom engine fork, anyone shipping an editor plugin, and anyone with a game editor module that includes Blueprint or AnimGraph types. Build engineers too, because the payoff is smaller relink units on editor builds.</p>
<p><strong>Urgency</strong></p>
<p>High if you fork. Do not wait for your first merge conflict to discover this. Grep your editor modules for Blueprint, Kismet, and AnimGraph includes and add explicit Build.cs dependencies now, while the change is fresh and the interface pattern is easy to copy. If you only consume released binaries, this is a “know it is coming” item.</p>
<h3 id="uba-got-build-cache-observability-and-a-real-scheduler-bug-got-fixed">UBA got build-cache observability, and a real scheduler bug got fixed</h3>
<p><strong>What changed</strong></p>
<p>Nine UBA commits landed this week, and they are easy to miss: most carry a bare <code>[UBA]</code> tag and no message body.</p>
<p>The UBA cache service now serves a live status web page via <code>-http=&lt;port&gt;</code>, and a new <code>UbaCli cacheproxy</code> mode tunnels that page over the UBA cache protocol (a new <code>CacheMessageType_HttpRequest</code>) so you can view a remote cache host’s dashboard locally. It is 1,679 lines across 17 files, and it exposes throughput, hit rate, storage, connected clients, maintenance history, and per-bucket controls (<code>5bb6fad7</code>).</p>
<p>Memory-based scheduling had two real defects (<code>0bcfde5d</code>). A memory-throttled queue pop was indistinguishable from an empty queue, so the host repeatedly spawned local duplicates of processes already running remotely instead of picking up queued work. Your build farm loses throughput and never tells you. Separately, an expensive predicted-memory callback ran while holding the process-entries lock. Both failures make a distributed build look randomly slower under load without ever producing an error.</p>
<p>The rest: the host no longer waits on remotes after it has already won the race (<code>1ce40548</code>), detoured processes killed from outside UBA no longer orphan their children (<code>6194406d</code>), and cacheproxy was fixed against a crypto-enabled cache server, with CAS validation de-prioritized so proxies can still pull during maintenance (<code>22801911</code>). Binaries were rebuilt (<code>5fb183a7</code>).</p>
<p>Separately, a four-line UBT change fixed a shared-PCH cache-key bug (<code>a18c43ec</code>). <code>bUseUnity</code> was part of the shared-PCH cache key, but modules sharing a PCH explicitly set <code>bUseUnity = false</code>, so the key for the same shared PCH differed depending on which module created it first. UBT now takes the target’s unity setting instead of the module’s.</p>
<p>Minor, and a footnote at most: <code>WaitMutex</code> is now settable in BuildConfiguration.xml, which helps shared CI hosts running concurrent UBT invocations (<code>5032fd6c</code>).</p>
<p><strong>Why this is important</strong></p>
<p>Nondeterministic cache-key generation is the worst kind of cache miss. It is invisible, it never errors, and it just looks like your cache is mediocre. Four lines fix it. If your shared or accelerated build cache has had a stubbornly poor hit rate with no explanation, this is a candidate cause, and the cheapest thing you will test all month.</p>
<p>The observability work matters for a different reason. Until now, diagnosing a bad cache meant inferring from build times. A hosted status page with hit rate, throughput, and per-bucket controls, viewable through a proxy from a developer machine, turns that inference into a measurement.</p>
<p><strong>Who should care</strong></p>
<p>Build and CI engineers running UBA, anyone operating a shared build cache, and anyone who has been quietly tolerating a distributed build that is fast most days and inexplicably slow on others.</p>
<p><strong>Urgency</strong></p>
<p>Act now. The shared-PCH key fix and the memory-scheduling fix are both cherry-pick candidates today, and both move numbers you can measure. Stand up the cache status page next.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<h3 id="slate-is-being-annotated-for-dynamic-invalidation">Slate is being annotated for dynamic invalidation</h3>
<p>Fifteen commits from Matt Begoghina under one ticket add <code>TWidgetTypeTraits&lt;W&gt;::SupportsInvalidation()</code> specializations across the widget library: core Slate covering docking and tabs (<code>22432ab7</code>), multibox (<code>363cb557</code>), notifications, canvas, and tooltips (<code>344b0e77</code>), popups and title bars (<code>16298b3a</code>), and layout and box widgets (<code>a6595f63</code>); the whole PropertyEditor stack (<code>c5cfe7ab</code>, <code>2f3fc266</code>, <code>0836db2b</code>, <code>e02ac01f</code>); EditorWidgets (<code>2aa42eeb</code>, <code>8f1b07e9</code>); and the Verse EntityPrefabEditor (<code>d3d7391c</code>).</p>
<p>Dynamic invalidation is opt-in per widget type, and a single unmarked widget inside an invalidation panel poisons the cache for that entire subtree, which is why <code>SInvalidationPanel</code> has historically been unusable across most editor UI. Epic is clearing that gate widget by widget, including the property grid, the heaviest Slate surface in the editor.</p>
<p>Nothing changes behavior yet. It only pays off when something downstream turns the caching on broadly. Watch for the commit that enables <code>SInvalidationPanel</code> by default or flips a global invalidation setting. Editor UI framerate moves then, and so does the cost of any custom Slate widget in your project that has not declared <code>SupportsInvalidation</code>: it starts silently forcing repaints of its parent subtree. If you ship editor tooling with custom Slate widgets, add the trait.</p>
<h3 id="skein-and-unreal-revision-control-are-both-lore-now">“Skein” and “Unreal Revision Control” are both “Lore” now</h3>
<p>Epic consolidated both names to Lore (<code>6ba090ed</code>, follow-up <code>138774cd</code>). Nothing technical changed, and both commits are tiny. Treat this as naming confirmation on Epic’s source-control effort, not a feature landing. It is also the commit carrying the summer-break quote.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<ul>
<li>Five “Horde: Updating schema documentation” commits from <code>buildmachine</code>, plus an automated binaries bump. Bot traffic.</li>
<li>A <code>Commit.gitdeps.xml</code> binary-manifest blob bump (<code>9d4036d3</code>). It is a blob bump. There is nothing to read.</li>
<li>Assorted empty-message commits and include-hygiene one-liners, including a spelling fix for <code>InitalizeExtenders</code> (<code>c522f9d2</code>), which we mention only because fixing a typo during your own holiday is a choice.</li>
</ul>
<hr>
<h2 id="closing">Closing</h2>
<p>A quiet week on the calendar, but the allowlist going to zero and a four-line cache-key fix will both cost you time if you find out about them the hard way.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine. Many of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-jul-6-12-2026/og.3adf9dbe7c.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: The Editor Walls Keep Falling Through the July 4 Lull (June 29 to July 5, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-jun-29-jul-5-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-jun-29-jul-5-2026</guid>
      <pubDate>Mon, 06 Jul 2026 07:11:20 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>US holiday week, so ue6-main ran quiet at 75 commits and ue5-main was silent. One engineer kept the UE6 editor-modularization campaign moving: UnrealEd lost its links to MaterialEditor, the Interactive Tools Framework, and AssetTools, a runtime-to-editor layering violation in Landscape got fixed, and the refactor quietly audited itself for bugs along the way.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-the-walls-keep-coming-down-through-the-holiday-lull">Unreal Engine Weekly: The Walls Keep Coming Down Through the Holiday Lull</h1>
<p><strong>Week of June 29 - July 5, 2026</strong> | <strong>75 commits on <code>ue6-main</code>, 0 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>This was the US July 4 holiday week, and the commit graph shows it: 75 commits on <code>ue6-main</code> against the ~880 of a normal week, and <code>ue5-main</code> was completely still. With the offices mostly empty, one engineer kept the lights on, and the week reads as a single coherent story instead of the usual dozen. The UE6 editor-modularization effort we’ve been tracking for three weeks did not pause for the holiday. It came for MaterialEditor, for the Interactive Tools Framework, and for AssetTools. If last week was “an agent going in, the walls coming down,” this week was just the walls, quietly, one load-bearing beam at a time. Nothing here is user-facing or runtime. The audience is build engineers and tools programmers on <code>ue6-main</code>, and anyone maintaining an engine fork who is going to feel these merges.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<p><strong>The UnrealEd decoupling campaign is now a three-week arc, and it’s accelerating.</strong> The theme showed up when <code>ue6-main</code> coverage began, and last week it produced the headline change: <code>Engine</code> stopped linking <code>UnrealEd</code>, a ~176-file inversion through provider interfaces. This week, even at a quarter of normal volume, the campaign came straight for the next targets, and the <code>CircularDependenciesAllowList</code> kept dropping, one entry or pair at a time, across more than a dozen modules (SubobjectDataInterface, LevelEditor, the Sequencer and MovieSceneTools split, Kismet, Documentation, MeshBuilder, and more). The MaterialEditor cut alone ejected a 28-module dependency cycle down to 15. This is not scattered cleanup. It’s a disciplined, repeatable playbook (relocate the code to its natural home, replace the hard link with a name-loaded provider or bridge interface, add CoreRedirects so serialized and script references survive, delete the allowlist entry) applied module by module. The through-line from the UE6 announcement is intact: a modular editor is the precondition for the agentic tooling layer Epic is building on top of it, and this is the unglamorous foundation work that makes that possible.</p>
<p><strong>A new module is being born to catch the falling pieces.</strong> The <code>EditorViewport</code> module started the week essentially empty and has been accreting the viewport core (camera transform and controller, click and cursor and navigation types, hover effects, and now the <code>FEditorViewportClientBase</code> class) so that tool and gizmo code can be typed against a base that sits below UnrealEd. Watch this one: it’s the extraction target the whole viewport story routes through, and it isn’t finished.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Holiday week. 75 commits, almost all one engineer’s editor-decoupling campaign, plus a handful of audio and media fixes from others. <code>ue5-main</code> was silent.</li>
<li><code>UnrealEd</code> no longer links <code>MaterialEditor</code>. The entire MaterialGraph node and schema cluster moved into the MaterialEditor module, collapsing a 28-module dependency cycle to 15 (<code>762b1941</code>).</li>
<li>The Interactive Tools Framework (EITF) was severed from <code>UnrealEd</code>, retyped across ~50 files against a new <code>FEditorViewportClientBase</code> in a new <code>EditorViewport</code> module (<code>fbc487ba</code>, <code>b8139326</code>). This is the highest fork-conflict risk of the week.</li>
<li><code>AssetTools</code> was ejected from the editor dependency cycle, swapping 32 call sites to name-loaded access and relocating a reflected enum (<code>7f47c982</code>). Under-rated: it touches nearly every editor tool and changes a public API.</li>
<li>A runtime layering violation got fixed too: <code>Landscape</code> (a runtime module) no longer reaches into editor-only <code>UnrealEd</code>, routing through Engine-owned provider interfaces instead (<code>a3171f31</code>).</li>
<li>The refactor is landing net bugfixes as it goes: tool-stack lifecycle correctness, multiple null-guards flagged by static analysis, and a same-day fix for a startup crash the work itself introduced (<code>6dbbeaf6</code>).</li>
<li>Underneath the decoupling, a small amount of real feature and fix work: a Quartz timing out-of-bounds regression fix, media encoder and pixel-streaming reliability fixes, and a net-new <code>PySlate</code> module for authoring editor UI in Python.</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="build-tooling-unrealed-loses-three-more-walls">Build &amp; Tooling: UnrealEd Loses Three More Walls</h3>
<p><strong>What changed</strong>
Three of <code>UnrealEd</code>’s biggest dependency edges came down this week. <code>MaterialEditor</code> was cut loose: the whole <code>MaterialGraph</code> node and schema cluster (the <code>UMaterialGraph</code>, <code>UMaterialGraphSchema</code>, and <code>UMaterialGraphNode_*</code> family) relocated out of <code>UnrealEd</code> into the MaterialEditor module, with include paths preserved and ClassRedirects covering script references, ejecting a 28-module link cycle down to 15 (<code>762b1941</code>). The Interactive Tools Framework was severed in two stages: first a prep pass evacuated the reflected, UnrealEd-coupled tool-stack types out of EITF (<code>b8139326</code>), then EITF was retyped across roughly 50 files against a new <code>FEditorViewportClientBase</code> in a new <code>EditorViewport</code> module instead of UnrealEd’s concrete <code>FEditorViewportClient</code> (<code>fbc487ba</code>). And <code>AssetTools</code> was ejected from the cycle: 32 <code>IAssetTools::Get()</code> call sites swapped to name-loaded module access across the core file-and-package tools, with a reflected enum relocated behind an EnumRedirect and a new public <code>GetPackageLengthForCooking</code> API added (<code>7f47c982</code>). Smaller cuts landed in the same spirit, including <code>MeshBuilder</code> (<code>163abb29</code>) and an <code>EditorStyleSettings</code> relocation with a config-defaults fixup (<code>02cc7197</code>, <code>41c663da</code>).</p>
<p><strong>Why this is important</strong>
<code>UnrealEd</code> has been the monolithic module everything links against, and its circular-dependency web is the single biggest driver of editor relink times. Shrinking the strongly-connected component means less of the editor has to rebuild when any one module in the cycle changes. The provider-and-bridge-interface pattern being established here (<code>IMaterialEditorModule</code>, the <code>IEditorXProvider</code> family, <code>ISubobjectDataBridge</code>) is the sanctioned way to reach editor services without taking a hard <code>UnrealEd</code> link. The AssetTools cut is the one to read closely: it sits under nearly the entire content pipeline, it changes call semantics (the module now loads on first menu evaluation), and it moves a reflected enum, so it carries more real regression and fork-conflict risk than the headline MaterialEditor change.</p>
<p><strong>Who should care</strong>
Build and DevOps engineers who own editor compile times. Tools and plugin programmers, especially anyone building on the Interactive Tools Framework, subclassing an editor viewport client, or linking <code>MaterialGraph</code> or <code>AssetTools</code> symbols out of <code>UnrealEd</code>. And anyone maintaining an engine fork: changes at this surface generate painful, wide merge conflicts.</p>
<p><strong>Urgency</strong>
Track. Act now if you maintain a fork touching these module boundaries, or ship custom interactive tools or viewport-client subclasses on <code>ue6-main</code>. The EITF retype changes public header types and adds a new virtual to the viewport-client base, so downstream code will recompile and may need re-fixing.</p>
<hr>
<h3 id="a-runtime-to-editor-layering-violation-gets-fixed">A Runtime-to-Editor Layering Violation Gets Fixed</h3>
<p><strong>What changed</strong>
<code>Landscape</code>, a runtime module, had been reaching into editor-only <code>UnrealEd</code> for its <code>WITH_EDITOR</code> paths: actor selection and deletion, package saving, static-lighting export, name sanitization. That edge is gone, routed through Engine-owned provider interfaces with new verbs added for the editor-time operations (<code>a3171f31</code>). The same pattern reached into the audio runtime, where the <code>Engine</code> to <code>AudioMixer</code> circular dependency was removed (<code>24521a60</code>).</p>
<p><strong>Why this is important</strong>
Runtime modules that link editor code are a recurring source of packaged-build and cook surprises. This is a categorically different (and arguably more valuable) fix than the editor-internal cycle cuts, because it corrects a real layering violation rather than just untangling two editor modules. It’s also a signal: Epic is actively hunting these, so expect more runtime modules to lose their editor edges in the coming weeks. If you have runtime modules with <code>#if WITH_EDITOR</code> blocks calling <code>GUnrealEd</code> or <code>GEditor</code> directly, this is the pattern to copy.</p>
<p><strong>Who should care</strong>
Engine programmers and studios maintaining forks with custom runtime modules that touch editor state.</p>
<p><strong>Urgency</strong>
Track.</p>
<hr>
<h3 id="stability-fixes-the-refactor-is-auditing-itself">Stability &amp; Fixes: The Refactor Is Auditing Itself</h3>
<p><strong>What changed</strong>
The decoupling work is not pure mechanical relocation. It keeps folding in real correctness fixes as it moves code. The EITF prep stage fixed six genuine pre-existing defects in the tool-stack code: a mis-stored identifier that meant editor-scope tool types never unregistered, several unchecked null dereferences in destructors and activation callbacks, <code>.Last()</code> calls on possibly-empty arrays, and a leaked command-list mapping (<code>b8139326</code>). The viewport retype pulled in three more null-guards flagged by static analysis (<code>fbc487ba</code>). And a static-initialization-order crash on editor startup, introduced by the EditorViewport work itself, was caught and fixed the same day in the same synced changelist (<code>6dbbeaf6</code>). Separately, a Quartz timing regression got a fix: an out-of-bounds access in <code>Tick</code> plus a <code>PlayQuantized</code> CoreRedirect resolution (<code>6fd07009</code>).</p>
<p><strong>Why this is important</strong>
This is what a healthy large-scale refactor looks like. Relocations double as bug audits, and the regressions the work introduces surface and get fixed fast. The Quartz out-of-bounds fix is the one non-refactor stability item worth acting on: if you sync recent <code>ue6-main</code> and use quantized audio, confirm you have it.</p>
<p><strong>Who should care</strong>
Editor engineers, anyone who has hit flaky tool-lifecycle or startup issues on <code>ue6-main</code>, and teams using Quartz quantized audio.</p>
<p><strong>Urgency</strong>
Track. Act now on the Quartz fix if you’re on quantized audio.</p>
<hr>
<h3 id="media-streaming-two-narrow-reliability-fixes">Media &amp; Streaming: Two Narrow Reliability Fixes</h3>
<p><strong>What changed</strong>
Two small but real fixes landed in the media stack. The WMF audio encoder was emitting multiple output packets per <code>SendFrame</code> call when the contract expects one, now corrected (<code>2639c6d7</code>). And PixelStreaming2 was throwing errors when a frame was requested before the first one had been pushed into the video capture pipeline, an initialization-ordering bug now guarded (<code>052fc56a</code>).</p>
<p><strong>Why this is important</strong>
Individually minor, but relevant to anyone doing capture-and-encode work or running a pixel-streaming pipeline. They’re also the clearest signal this week that ordinary feature-and-fix work continued underneath the decoupling push.</p>
<p><strong>Who should care</strong>
Teams with media encoding or pixel-streaming pipelines.</p>
<p><strong>Urgency</strong>
Track.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p><strong>A new <code>PySlate</code> module.</strong> A net-new module landed as an “Initial Commit” (<code>0b279ece</code>): a Python-Slate binding surface for authoring editor UI in Python. It’s a single early commit with nothing built on it yet, but a first-party path to scripting editor UI in Python is a genuinely notable capability, and it fits the broader UE6 direction of making the editor more scriptable. Worth watching whether it accretes real surface area in the coming weeks or stays a stub.</p>
<p><strong>The <code>EditorViewport</code> module.</strong> As noted in the trends section, this module is being stood up specifically to host <code>FEditorViewportClient</code>’s base class and the viewport POD types, so tool and interaction code can depend on viewport primitives without linking the editor monolith. Several commits explicitly describe themselves as “prepares” or “stage X of,” and public types are relocating behind CoreRedirects. If you build against viewport internals, the correct include is shifting under you even though forwarding headers keep most code compiling for now.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>We set aside roughly 30 commits this week, most of them the expected fallout of a large refactor: non-unity compile fixes, static-analysis annotations (<code>CA_ASSUME</code>, <code>check(ptr)</code> guards), and a handful of near-identical auto-generated Horde schema documentation updates. None of these affect runtime behavior, performance, or upgrade risk. On a normal week many would not even register; on a 75-commit holiday week they’re a visible fraction of the total, which is itself a fair picture of how quiet the week was.</p>
<hr>
<h2 id="closing">Closing</h2>
<p>A holiday week is a good week to see what a team does when nobody is watching. Epic kept paying down the editor’s oldest architectural debt, carefully, one module at a time. Nothing to act on unless you maintain a fork. Everything to appreciate if you care where the engine is going.</p>
<p>For what it’s worth, this was the shortest read since we started doing this. We keep wanting to say we did something noble with the spare time. Mostly we just enjoyed a quiet inbox and read the diffs slowly for once.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine. Some entries may resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-jun-29-jul-5-2026/og.dd3a6ad0c8.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: UE6 Starts Tearing Down the Editor Walls (June 22 to 28, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-jun-22-28-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-jun-22-28-2026</guid>
      <pubDate>Mon, 29 Jun 2026 06:55:40 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>Week two on ue6-main: 880 commits. Epic is bolting an AI agent into the editor while simultaneously tearing out the circular dependencies and VM layers that would make that hard. The loud story (a first-party agentic toolset framework) sits behind binary blobs we can’t read. The quiet story (Engine no longer links UnrealEd, a Verse VM fuzzer, AutoRTFM hardening) is fully visible in source. Plus the 5.8.1 sidebar for studios shipping today.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-ue6-starts-tearing-down-the-editor-walls">Unreal Engine Weekly: UE6 Starts Tearing Down the Editor Walls</h1>
<p><strong>Week of June 22-28, 2026</strong> | <strong>880 commits on <code>ue6-main</code>, 155 on <code>ue5-main</code></strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>Week two on <code>ue6-main</code> carried 880 commits against <code>ue5-main</code>’s 155, holding the divergence we saw when the branch switched. A week ago this looked like a thin new layer riding on ordinary engine work. This week the layer starts to take a recognizable shape. The story is two halves of the same move: Epic is wiring an AI agent directly into the editor, and at the same time tearing out the circular module dependencies and old VM layers that would make that agent hard to build. The loud half (a first-party agentic toolset framework) is mostly invisible to us, sitting behind binary-manifest blobs. The quiet half (Engine no longer links UnrealEd, a type-directed Verse VM fuzzer, AutoRTFM correctness hardening) is fully readable in source, and that’s where the credibility comes from. None of it is production-actionable for teams shipping today. Early Access is targeted for late 2027. If you ship a game now, you stay on 5.8, and the 5.8.1 maintenance sidebar at the bottom is the part that touches you.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>The biggest storyline (Epic’s agentic in-editor toolset framework) is also the least verifiable: most of those commits resolve to <code>Commit.gitdeps.xml</code> binary blobs. The intent is clear from the messages, the implementation is not.</li>
<li>The agent framework is consolidating from “add a toolset” into shared infrastructure: <code>ToolsetRegistry</code> split into runtime + editor modules and made a singleton so non-editor targets can call it, and MCP server settings enabled in Fortnite/UEFN builds (<code>cee54db2</code>, <code>13a4ae6e</code>).</li>
<li>The most important <em>visible</em> change is structural: <code>Engine</code> no longer links <code>UnrealEd</code>. A ~176-file refactor inverts editor calls through ~28 provider interfaces (<code>3ccf3ec3</code>). This is years-of-payoff plumbing, and it will conflict hard against any custom fork.</li>
<li>The Verse VM got a fuzzer: a type-directed one that generates type-correct programs pushed VM-execution coverage from near-zero to ~99%, running cross-backend with auto-triage (<code>70d06ad5</code>). Usually that comes right before a team flips the default VM.</li>
<li>AutoRTFM and Verse are hardening as one transactional substrate, with commits on each side referencing the other.</li>
<li>A cluster of CI and build-farm reliability fixes is the most act-now material this week: a Horde Dashboard hang, an AssetRegistry gatherer deadlock, a ZenCacheStore shutdown race (<code>ecd1ca21</code>, <code>79564ad3</code>, <code>4a2e16ad</code>).</li>
<li><code>ue5-main</code> bumped to 5.8.1. The sidebar covers the regression and crash fixes worth cherry-picking if you ship on 5.8 today.</li>
</ul>
<hr>
<h2 id="the-through-line-an-agent-going-in-the-walls-coming-down">The Through-Line: An Agent Going In, the Walls Coming Down</h2>
<p>If you read the week as one move instead of a list, it’s this. Epic is bolting an AI agent into the editor and simultaneously removing the architectural debt that would make that agent expensive to maintain. The two efforts are not announced as related, but they fit together. A clean, broadly-callable agent surface is much easier to build once runtime and editor modules stop linking each other in a circle, and once the language the agent reasons about (Verse) has a VM you trust. So the week splits into the loud-but-invisible (the agent) and the quiet-but-visible (the refactors), and the two deserve different grades. We can confirm the refactors from source. We can only read the agent’s intent from commit messages, because the code lives in Epic’s binaries.</p>
<hr>
<h2 id="the-new-layer-ue6-specific">The New Layer (UE6-Specific)</h2>
<h3 id="an-ai-agent-is-going-into-the-editor-toolset-by-toolset">An AI Agent Is Going Into the Editor, Toolset by Toolset</h3>
<p><strong>What changed.</strong> This is no longer one experiment. There’s now a family of “toolsets” being registered for an in-editor agent to call: AssetTools, StateTree, PCG, MetaHuman Generator and a MetaHuman wardrobe toolset, plus a new CrashDiagnostics toolset that lets the assistant fetch and explain prior editor crashes by GUID (<code>1aed1cb3</code>). The plumbing under them matured this week too: <code>ToolsetRegistry</code> was split into runtime and editor modules (<code>fe41b687</code>) and <code>FToolsetRegistry</code> converted to a singleton, explicitly “so we can access it from outside editor in later CLs” (<code>cee54db2</code>), and MCP server settings were enabled in Fortnite/UEFN builds (<code>13a4ae6e</code>). The toolsets themselves are thin Python and Verse wrappers around existing BlueprintCallable APIs, with docstrings written for the agent to discover capabilities and guardrails to keep it from hanging the editor: <code>find_assets</code> gained a <code>max_results</code> cap after agents fired queries returning tens of thousands of results and appeared to hang (<code>c8023dfa</code>), and MetaHuman Generator registration is now gated on optional content because a <code>set_skin_tone</code> call asserted and crashed the editor when that content was absent (<code>c936322e</code>).</p>
<p><strong>Why this is important.</strong> The shift from “add a toolset” to “refactor the shared registry so non-editor targets can call it” is what gives this away. Epic is preparing a first-party agentic layer as a stable, broadly-callable API, not a collection of one-off editor panels. The guardrails point the same direction. Result caps and content-availability gates are exactly what you add when the caller is a model retrying against state it can’t fully see.</p>
<p><strong>Who should care.</strong> Tools and pipeline teams, technical artists, and studios evaluating UE6 automation. Nobody needs to act, but this is the workstream that would reshape how a tools team thinks about editor automation if it ships.</p>
<p><strong>The honest caveat.</strong> Nearly every commit in this theme resolves to a <code>Commit.gitdeps.xml</code> binary-manifest blob. The messages are real and descriptive, but the implementation lives in Epic’s internal binaries we cannot see. Treat the direction as confirmed and the contents as unverifiable.</p>
<p><strong>Urgency</strong> Track.</p>
<hr>
<h3 id="the-engine-to-unrealed-wall-comes-down">The Engine-to-UnrealEd Wall Comes Down</h3>
<p><strong>What changed.</strong> <code>Engine</code> no longer links <code>UnrealEd</code>. The <code>(&quot;Engine&quot;,&quot;UnrealEd&quot;)</code> entry is gone from the circular-dependency allow-list, replaced by roughly 28 <code>IEditorXProvider</code> interfaces that invert the editor-time calls Engine used to reach through UnrealEd for (GEditor, asset tools, transactions, selection, save), across about 176 files (<code>3ccf3ec3</code>). PropertyEditor got the same treatment: ~50 call sites inverted through <code>FPropertyEditorHooks</code>, with <code>FPropertyEditorToolkit</code> extracted into a new module (<code>2f9e23de</code>). Smaller untanglings landed in the same spirit, including NiagaraEditor being severed from the experimental toolset-registry plugin so it no longer force-enables it wherever Niagara ships (<code>472b1e83</code>).</p>
<p><strong>Why this is important.</strong> UnrealEd has been the monolithic module everything links against. Breaking those edges is a precondition for faster editor iteration builds and for letting tooling depend on editor-framework pieces without dragging in the entire editor. This is the fully-visible, unambiguously-real counterweight to the agent story: it ships no feature, and it pays off for years. It also fits the through-line. A clean agent surface is far easier to build on modules that don’t link each other in a circle.</p>
<p><strong>Who should care.</strong> Engine programmers, plugin and module authors, and anyone maintaining a custom fork. Changes at this scale generate painful merge conflicts, so read them before your next integration.</p>
<p><strong>Urgency</strong> Track. Act now if you maintain a fork that touches Engine or PropertyEditor module boundaries.</p>
<hr>
<h3 id="the-verse-vm-gets-a-fuzzer">The Verse VM Gets a Fuzzer</h3>
<p><strong>What changed.</strong> The work around the new Verse VM this week is about confidence, not surface area. A type-directed language fuzzer now generates programs that are type-correct by construction, which pushed VM-execution penetration from effectively zero to around 99%, and runs them across both the legacy BPVM and the new Verse VM with automated crash fingerprinting, delta-debug shrinking, and auto-filed tickets (<code>70d06ad5</code>). Alongside it, a new <code>assert_bytecode</code> facility pins down that specific language constructs compile to the exact expected bytecode (<code>bb02745a</code>), and a <code>toggle_compile_features</code> macro allows per-test compiler-feature opt-in (<code>f2db1e4d</code>). The feature work continued in parallel: a “Verse Animation Phase 2” arc added playback, time, looping, ease-in/out, replication, and async loading (<code>81a5e1bb</code>, <code>362f1cf4</code>), and CoreRedirects for type aliases appeared (<code>62d488c8</code>), a backward-compatibility path you only bother to maintain once content is being authored against the new VM.</p>
<p><strong>Why this is important.</strong> A cross-backend fuzzer and bytecode-equality assertions are what a team builds when it intends to flip the default VM and wants proof the two backends agree first. Read together with the animation phase-2 work and the compatibility redirects, Verse is moving from scaffolding into hardening. If you want to know where UE6’s gameplay-authoring direction actually sits, this is where to look.</p>
<p><strong>Who should care.</strong> Verse and UEFN-ecosystem developers, and language-tooling teams.</p>
<p><strong>Urgency</strong> Track.</p>
<hr>
<h3 id="autortfm-and-verse-harden-as-one-transactional-substrate">AutoRTFM and Verse Harden as One Transactional Substrate</h3>
<p><strong>What changed.</strong> Roughly a dozen AutoRTFM commits landed, almost all correctness hardening: more virtual-function-mode violations fixed, support for unbalanced recursive locking in <code>TTransactionallySafeMutex</code>, and <code>AUTORTFM_IMPLICIT_DISABLE</code> / <code>AUTORTFM_OVERRIDE_MAY_DISABLE</code> annotations applied to <code>AutoRTFM::Open()</code> and <code>UCommandlet::Main()</code>. The cross-references to Verse are now explicit, including a <code>verse-clang</code> change to emit AutoRTFM static-local-initializer calls for non-AutoRTFM-enabled functions, and a Verse-side drop of an <code>AutoRTFM::UnreachableIfClosed()</code> from the Solaris runtime link path. Two real runtime fixes also landed: a latent memory leak in the Verse package cache from an unsafe <code>AUTORTFM_OPEN</code> on a <code>TSharedRef</code> (<code>c306b881</code>), and relinquishing Verse heap access across <code>UE::Tasks::Wait</code> to avoid a GC-handshake deadlock (<code>1ee866b5</code>).</p>
<p><strong>Why this is important.</strong> AutoRTFM provides the transactional-memory guarantees Verse’s gameplay and replication model leans on, and the commits this week are wiring the C++ side (mutexes, virtual-function modes, static initializers) to satisfy that contract. The two systems are advancing as one substrate, and the activity is hardening rather than new scaffolding. That work, plus the EpicClang gating we noted last week, is what “transactionalize C++” actually amounts to so far.</p>
<p><strong>Who should care.</strong> Gameplay programmers evaluating Verse, and engine teams touching threading or transactions.</p>
<p><strong>Urgency</strong> Track.</p>
<hr>
<h3 id="scenegraph-keeps-weaning-off-actor-proxies">SceneGraph Keeps Weaning Off Actor Proxies</h3>
<p><strong>What changed.</strong> The SceneGraph and Entity work this week has one consistent sub-theme: removing the dependency on proxy actors. Commits fixed util and Python-exposed functions that were actor-proxy-reliant, removed an unused proxy-reliant SceneGraphUtil function, and made edit-in-place plus step-in and step-out work without proxy actors (<code>8bdf3f43</code>). Around that, editor integration landed: entity-outliner sorting without GUIDs, duplicate-suffix handling, constrained drag-and-drop, and exposing custom depth and custom-depth-stencil values on mesh entity components.</p>
<p><strong>Why this is important.</strong> SceneGraph is the entity model Epic has named as the eventual replacement for the AActor proxy system. Severing proxy-actor dependencies makes entities stand on their own representation rather than shadowing the legacy actor model, and the simultaneous outliner and prefab work signals Epic wants entities usable through normal editor workflows, not just as a backend. This is the groundwork for deprecating Actors, and it is years ahead of any actual deprecation.</p>
<p><strong>Who should care.</strong> Engine and framework teams, and anyone weighing the long-term actor-versus-entity direction.</p>
<p><strong>Urgency</strong> Track.</p>
<hr>
<h2 id="ordinary-engine-work-that-still-matters">Ordinary Engine Work That Still Matters</h2>
<h3 id="build-ci-reliability">Build &amp; CI Reliability</h3>
<p><strong>What changed.</strong> A cluster of genuine production-reliability fixes landed in the parts of the stack a studio’s build farm actually depends on. The one most teams will feel is a Horde Dashboard hang: a job page would spin forever (HTTP 200, never repaints) when a summary report packed many <code>&lt;br/&gt;</code> tags into one block and triggered catastrophic regex backtracking in markdown-to-jsx (<code>ecd1ca21</code>). DDC Verify can now split into a priming job and a verification job (<code>fc452788</code>). An AssetRegistry gatherer deadlock where all threads could block was fixed by having the gatherer contribute to directory processing when explicitly waited on (<code>79564ad3</code>), and a ZenCacheStore shutdown race in the perf-monitoring thread was closed (<code>4a2e16ad</code>). On the toolchain side, Epic added a direct, non-VM BuildGraph builder API as the foundation for eventually removing the C# BuildGraph VM (<code>28d001c2</code>), and fixed a data race in UnrealBuildTool’s PCH dependency calculation that could produce a wrong dependency list and a build exception (<code>555da914</code>), plus a ~40% speedup on shared-PCH dependency lookup (<code>253b35aa</code>).</p>
<p><strong>Why this is important.</strong> These are the failures that cost a build farm real hours: a dashboard that hangs on a specific report shape, a gatherer that deadlocks, a cache store that races on shutdown. None are dramatic, all are worth pulling into your fork if you run Horde, Zen, or a large DDC at scale. The BuildGraph VM-removal groundwork is worth noting early if you author graphs in C#: the eager-Def API is the future surface, though nothing is deleted yet and the VM stays default.</p>
<p><strong>Who should care.</strong> DevOps, build-farm operators, and CI/CD owners.</p>
<p><strong>Urgency</strong> Act now for studios running Horde/Zen/DDC at scale. Track for everyone else.</p>
<hr>
<h3 id="rendering-nanite-ray-tracing-and-tsr">Rendering: Nanite Ray Tracing and TSR</h3>
<p><strong>What changed.</strong> Nanite ray tracing now keys hierarchy traversal on which pages actually have CLAS allocated, which fixes BLASes with holes when the CLAS pool runs out of memory (<code>fce21cf5</code>), plus a GPU crash fix in the Nanite Voxel rasterizer (<code>a34274e1</code>). TSR split its Guide uncertainty into a dedicated single-channel <code>PF_R16F_FILTER</code> array, removing a redundant full FloatRGBA slice per frame when alpha is enabled (<code>69a9c2b1</code>). A stereo refactor renamed the View uniform buffer (<code>View</code> to <code>VUB</code>) to support a static <code>ViewState</code> alias for instanced stereo, touching about ten files, half of them shaders (<code>1c769f66</code>).</p>
<p><strong>Why this is important.</strong> This is correctness-and-cost tuning on a maturing path, not a feature launch. The CLAS-availability traversal stops intermittent corruption under memory pressure, the failure mode that’s painful to reproduce. The View-to-VUB rename will conflict against any custom shaders, so flag it before integrating.</p>
<p><strong>Who should care.</strong> Rendering engineers, studios on Nanite with hardware ray tracing, and teams using alpha-channel TSR.</p>
<p><strong>Urgency</strong> Track. Note the shader rename if you maintain custom shaders.</p>
<hr>
<h3 id="stability-core-thread-safety-and-the-new-systems">Stability: Core Thread-Safety and the New Systems</h3>
<p><strong>What changed.</strong> A few fixes this week sit below the headline themes but matter because of where they are. A data race was fixed in <code>FNameTokenStore</code> (<code>8190796e</code>), thread-safety in one of the most fundamental engine primitives. The new Verse Animation Framework got a use-after-free fix caught by ASAN, where local stack memory was freed before an update context used it (<code>4b0505d5</code>). The multiserver and RemoteObjects path picked up serialization and shutdown-ordering fixes for the distributed-world tech (<code>80d7dd09</code>, <code>21775ccc</code>).</p>
<p><strong>Why this is important.</strong> The FName data race matters most: that primitive is touched everywhere, so a race there produces rare, unreproducible corruption across unrelated systems. The UAF and multiserver fixes are early-system hardening, which is expected and healthy on a young branch, but worth noting because they sit in the UE6-specific code, not the inherited engine.</p>
<p><strong>Who should care.</strong> Engine programmers, and anyone tracking the maturity of UE6’s new animation and distributed-simulation systems.</p>
<p><strong>Urgency</strong> Track.</p>
<hr>
<h2 id="mass-ecs-gets-layered-into-a-core">Mass ECS Gets Layered Into a Core</h2>
<p><strong>What changed.</strong> About a dozen Mass commits this week were structural reorganization rather than features: element-bitset declarations moved from <code>MassEntity</code> into a new <code>MassCore</code> module, fragment access and presence enums plus the requirement-description and error-reporting helpers pushed down into <code>MassCore</code>, and dead editor-only build dependencies stripped from Mass modules. Test replication into a low-level-test target and README and documentation additions landed alongside.</p>
<p><strong>Why this matters.</strong> Pulling Mass’s foundational pieces down into a lower <code>MassCore</code> layer and stripping editor-only dependencies usually precedes wider reuse across runtime and server targets. Read together with the SceneGraph entity work, Epic is making the entity and ECS stack a first-class, broadly-depended-on engine layer in UE6. The documentation and test work suggests an API stabilizing for external consumption, the kind of cleanup that follows last week’s <code>MassIrisReplication</code> feature push.</p>
<hr>
<h2 id="the-5x-stabilization-sidebar-ue5-main">The 5.x Stabilization Sidebar: ue5-main</h2>
<p><code>ue5-main</code> is now bug-fix and regression maintenance only, and this week it bumped the installer version from 5.8.0 to 5.8.1 (<code>3a421871</code>), confirming the branch is feeding a 5.8.1 hotfix release. At ~155 commits against <code>ue6-main</code>’s 880, it’s doing exactly what a post-final-release branch should. If you ship on 5.8 today, these are the fixes worth cherry-picking:</p>
<ul>
<li><strong>Nanite crash with translucency disabled (default config).</strong> With <code>r.Nanite.AllowTranslucency</code> off (the default), translucent materials still reached translucency and base-pass codepaths and crashed instead of falling back to the default material. Any 5.8 title using Nanite on stock settings could hit this; it now degrades gracefully with a warning (<code>40789343</code>).</li>
<li><strong>Landscape spline tangent regression.</strong> Landscape editor tools were switched back to legacy viewport interactions to fix spline tangent manipulation that broke in 5.8. A real workflow regression for anyone doing landscape, road, or spline authoring (<code>e348bec2</code>).</li>
<li><strong>Vulkan submission-thread deadlock.</strong> Fixes a <code>SubmissionThread</code> deadlock from a recycled “rendering done” semaphore handle, plus a guard that acceleration structures support mutable descriptors before enabling ray tracing. Relevant if you ship Vulkan with RT in the mix (<code>11c1d64a</code>).</li>
<li><strong>Sequencer crash on stale section pointers (UE-385130).</strong> <code>CheckForEdgeInteraction</code> asserted on stale weak pointers when a section was removed before the underlapping-segments list refreshed. An editor-stability fix for heavy cinematics work (<code>256a80f8</code>).</li>
<li><strong>MegaLights + Forward Rendering missing-shader crash.</strong> A compile-versus-use condition mismatch on <code>FFrontLayerTranslucencyClearGBufferPS</code> crashed projects using Forward Rendering with MegaLights enabled. Narrow config, hard crash, mostly the mobile, VR, and forward crowd experimenting with MegaLights (<code>55fc8ae9</code>).</li>
</ul>
<p>Honorable mentions worth a fork cherry-pick: a Metal breadcrumb crash fix on Apple platforms (<code>dd1c69ab</code>), a non-deterministic <code>AnimationSequence</code> DDC fix from uninitialized curve padding that would otherwise spuriously invalidate a team’s shared cache (<code>6d47614c</code>), and a bindless-uniform-buffer texture-reference fix (<code>5f8f0741</code>). The overall character is what you’d expect from a release winding down: regression patching, crash hardening across editor tooling, and routine plumbing, with the 5.8.0-to-5.8.1 bump the only structural event. Low, fix-only volume is exactly what UE5 should be doing now.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p>Two early signals worth a note. First, incremental cook is getting opt-in multithreaded dependency-key computation, precomputing each wave of ready vertices’ per-package keys across worker threads before the serial loop consumes them, with blueprint hashing made thread-safe behind a reader-writer cache lock (<code>3246bde7</code>). It’s behind a new CVar and off by default, with a couple of <code>cookincremental</code> version bumps landing the same week. Cook-key computation is a known serial bottleneck in large CI pipelines, so if that CVar flips to default-on in a future release it’s a direct iteration-time win. Track whether it stays experimental or graduates.</p>
<p>Second, MetaHuman Crowd is being wired into the Mover plugin’s authoritative simulation, with teleport handling overridden so Mover’s state updates alongside the actor transform (<code>95b02b1a</code>), landing next to a “Mass Characters animation pipeline” update (<code>6feac289</code>). The combination of MetaHuman Crowd, Mover as movement authority, and Mass character animation points at Epic converging its crowd and NPC story onto the Mass framework. If you’re choosing a large-population character solution in UE6, this is the stack to watch consolidate.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>We set aside several hundred commits this week, mostly routine crash-fix hardening (Mutable, PVE foliage, Niagara, Sequencer, MetaHuman import), formatting and comment changes, vendor binary bumps, and the usual integration churn including a dozen straight backouts. These don’t affect runtime behavior, performance, build reliability, or upgrade risk for production teams. The interesting work this week was not at the top of the volume chart; the architecture campaigns above were spread thin across many small commits, which is exactly why they’re easy to miss on a fast skim.</p>
<hr>
<h2 id="closing">Closing</h2>
<p>Week two of UE6 is the week the new layer started to resolve into intent: an agent going into the editor, and the walls coming down to make room for it. Most of it is years from anything you’d ship. If you’re shipping today, read the 5.8.1 sidebar and ignore the rest.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine unless noted as <code>ue5-main</code>. Many of the highest-interest entries (notably the agentic toolset work) resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-jun-22-28-2026/og.573b55a512.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>SpeedrunCI Is Now on FAB</title>
      <link>https://speedrun.ci/en/blog/speedrun-ci-now-on-fab</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/speedrun-ci-now-on-fab</guid>
      <pubDate>Fri, 26 Jun 2026 15:03:46 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Product</category>
      <description>Our Unreal Engine build plugin is live on FAB. Trigger cloud builds from inside the editor, no dashboard, no command line, no build wizard required.</description>
      <content:encoded><![CDATA[<p>The SpeedrunCI plugin is now live on FAB. It is free to install, and it puts your build pipeline inside the Unreal Editor.</p>
<p>Here is the short version of why it exists.</p>
<h2 id="fast-builds-were-not-the-whole-problem">Fast builds were not the whole problem</h2>
<p>We build games at <a href="https://ayelet.studio">Ayelet Studio</a>. For a long time, builds were the slowest part of the week. Packaging a build meant running all three platforms sequentially, which cost us 3x the time. Our one build machine was a bottleneck. And producers, QA, and designers had no visibility, so they scrambled for the latest build every morning.</p>
<p>So we built our own CI/CD platform.</p>
<p>That solved the speed and the visibility. It did not solve everything.</p>
<p>But one thing was still broken.</p>
<p>Even with a fast pipeline, starting a build still meant leaving Unreal. You tab out to a web dashboard, or someone opens a terminal and runs the right command with the right flags. So in practice, one person still owned builds. The artists and designers who actually needed a packaged build to test something could not just make one. They had to go find the build wizard. Again.</p>
<p>We had fixed the infrastructure. The workflow still had a person stuck in the middle of it.</p>
<h2 id="what-the-plugin-does">What the plugin does</h2>
<p>The whole point of the plugin is that you never leave the Unreal Editor.</p>
<p>There is a button in the editor toolbar. Click it, and SpeedrunCI runs your build in the cloud, streams your logs, and delivers the finished artifact, all while you keep working. The live status shows up right there in the editor.</p>
<p><img src="https://speedrun.ci/blog-media/speedrun-ci-now-on-fab/pipelines-multiplatform.2cbeec2658.png" alt="The SpeedrunCI panel showing build pipelines for Win64, on-prem, PS5, and Xbox Series, each with its last build status" width="1982" height="1194" loading="lazy" decoding="async"></p>
<p>Every platform pipeline and its last build status sit in one view. Anyone on the team can see what passed and what failed, without a CI dashboard tour.</p>
<p>Underneath, the service is built for how Unreal actually works, not retrofitted from a web app tool:</p>
<ul>
<li>Game-aware caching for cooked assets, shaders, and derived data, so repeat builds stay fast.</li>
<li>A parallel build graph that runs across multiple runners instead of one long queue.</li>
<li>Every UE platform: Windows, Mac, Linux, Android, iOS, and console on Enterprise.</li>
<li>Artifact storage and distribution, so you are done shipping 100GB builds over Google Drive.</li>
</ul>
<p>When a build fails, you do not scroll through 2,000 warnings to find the one error that matters. SpeedrunCI reads the logs and suggests fixes, with the file and line to look at.</p>
<h2 id="what-changes-for-the-team">What changes for the team</h2>
<p>A designer can trigger a build to test a level. A producer can see whether the nightly build passed without asking anyone. The build stops being a thing one person owns and becomes something the whole team can reach.</p>
<p><img src="https://speedrun.ci/blog-media/speedrun-ci-now-on-fab/artifact-download.4c1699ae10.png" alt="A finished Xbox Series X build artifact, 20.6 GB, ready to download from SpeedrunCI" width="1996" height="1184" loading="lazy" decoding="async"></p>
<p>When a build finishes, the artifact is right there to download. A 20 GB console build, no Google Drive link, no waiting on the one person who knows where the builds live.</p>
<h2 id="try-it">Try it</h2>
<p>The plugin is free on FAB. The service has a free trial, so you can run one of your own builds through it without talking to anyone. Install it, connect your account, and run your first Unreal build in minutes:</p>
<ol>
<li>Install the plugin from FAB.</li>
<li>Enable it in your project.</li>
<li>Sign in with your SpeedrunCI account.</li>
<li>Pick a project preset and trigger your first build.</li>
</ol>
<p>If your studio still has one person everyone depends on whenever someone asks “Can you make me a build?”, that is the exact thing we built this to fix. Install it, run a build, and tell us what you think.</p>
<div style="text-align:center;margin:36px 0;">
  <a href="https://www.fab.com/listings/8a93b442-e39e-4ffa-94b6-cd754cd3c34a" style="display:inline-block;background:#059BB4;color:#ffffff;padding:16px 34px;border-radius:8px;font-weight:700;font-size:18px;text-decoration:none;">Get the plugin on FAB</a>
</div>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/speedrun-ci-now-on-fab/og.5877729987.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: UE6 Development Has Begun (June 15 to 21, 2026)</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-jun-15-21-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-jun-15-21-2026</guid>
      <pubDate>Mon, 22 Jun 2026 08:58:45 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>UE 5.8 shipped as the last planned major UE5 release and mainline development moved to ue6-main: 878 commits versus ue5-main’s 113. Week one of UE6 reads as incremental re-platforming of the UE5 codebase, with a thin but real new layer: Verse becoming the gameplay model, a first-party in-editor MCP server, the AutoRTFM transactional toolchain, and Mass ECS gaining replication over Iris.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-ue6-development-has-begun">Unreal Engine Weekly: UE6 Development Has Begun</h1>
<p><strong>Week of June 15-21, 2026</strong> | <strong>878 commits analyzed</strong> | Branch: <code>ue6-main</code></p>
<h2 id="overview">Overview</h2>
<p>UE 5.8 shipped on June 17 as the last planned major UE5 release, and active mainline development has now moved to <code>ue6-main</code>. The branch switch is the news this week. <code>ue6-main</code> carried 878 commits against <code>ue5-main</code>’s 113, the first genuine divergence in our ledger. Read the diff honestly and week one of UE6 looks less like a clean-slate rewrite and more like incremental re-platforming of the UE5 codebase: the bulk of the work is renderer hardening, build and cook plumbing, and async-loading fixes that would look identical on ue5-main. Nothing here is production-actionable. Early Access is targeted for late 2027, with full release 12 to 18 months after that, so this coverage is forward radar, not a migration prompt. We switched branches because this is simply where the engine is being built now.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>UE6 development is live on <code>ue6-main</code>. Week one is mostly ordinary engine work wrapped around a thin, genuinely-new architecture layer.</li>
<li>Verse is becoming the gameplay programming layer: VVM heap-correctness fixes, new language semantics (discriminated unions, bitwise ops, closures), and Verse-to-C++ interop wired through UHT.</li>
<li>A first-party, in-editor MCP server is real and being hardened (chunked streaming, commandlet guards, live URL composition). The AI pillar is shipping as working networked code, not a slide.</li>
<li>Mass ECS gained replication over Iris (the <code>MassIrisReplication</code> arc), the networking backbone for persistent large-scale worlds.</li>
<li>The deprecation everyone is reacting to (Blueprints and Actors) has no commit deprecating either one this week. The replacement foundation (SceneGraph, Entity framework, Verse) is landing, but Blueprints still got a week of active fixes. Years out, not months.</li>
<li>Nothing is production-actionable. This is a 2027+ horizon. If you ship today, you stay on 5.8.</li>
</ul>
<hr>
<h2 id="what-ue6-actually-is-so-far">What UE6 Actually Is, So Far</h2>
<p>The honest read on week one: UE6 is, for now, an incremental re-platforming of the UE5 codebase rather than a clean-slate rewrite. The vast majority of the 878 commits are routine engine maintenance, the kind of renderer, cook, and loader work that has been landing on ue5-main for years. The branch name is new. Most of the work under it is not.</p>
<p>Epic named three initiatives for UE6: Verse as the gameplay programming model, open standards that make content, code, and economies portable across games and engines, and an AI-driven development pipeline (an MCP layer plus in-engine generative AI via diffusion models). The overarching vision above those is the merge of UE5 and UEFN into one engine. It is worth checking the week’s commits against that announced plan rather than assuming they line up.</p>
<p>Two of the three pillars show up clearly. Verse is being hardened into a first-class gameplay layer: roughly three dozen Verse and VVM commits across the VM and the language, plus the access-control machinery that governs a merged UEFN/UE5 monorepo. The AI pipeline is landing as a first-party Model Context Protocol server, four commits this week, hardened as networked code (the generative-AI half of that pillar produced nothing visible this week). The third pillar, open standards and portability, has no commit footprint yet, which is worth stating plainly rather than glossing: it is the one announced initiative we cannot see moving. The UEFN merge itself is not just a slide: Solaris and Fortnite paths are in-repo and UEFN cook and profiling work is landing alongside engine code. Scene Graph, the new high-level gameplay framework Epic announced, also made its first appearance, in asset-digest commits (<code>5dbc77d8</code>). Add the <code>MassIrisReplication</code> arc and the AutoRTFM toolchain wiring, both read from the commits rather than announced as headline features, and that is the whole of the distinctly-new surface. A few dozen commits in total.</p>
<p>The distinctly-UE6 work is directionally important and architecturally load-bearing, but it is a thin layer riding on top of a large body of ordinary engine churn. One caveat: many of the highest-interest commits are <code>Commit.gitdeps.xml</code> binary-manifest blobs. The commit message is real, but the code lives in Epic’s internal binaries we cannot see, so several claims below rest on a message, not on readable source. We flag those where they matter.</p>
<hr>
<h2 id="the-blueprints-and-actors-question">The Blueprints and Actors Question</h2>
<p>The loudest reaction to the UE6 announcement is not Verse or AI. It is deprecation. Epic said Blueprints and Actors are both on the way out: the Actor model gives way to SceneGraph and the Entity framework, and gameplay scripting moves to Verse. For a community whose tutorials, Fab assets, and muscle memory are built on Blueprints, that is the headline, and the question under every thread is the same. How soon do I need to care?</p>
<p>The commits give a calmer answer than the announcement does. The replacement foundation is real and landing now: SceneGraph picked up entity-level work this week (a per-entity <code>CanAffectNavigation</code> toggle, <code>05eb154e</code>, and an asset-digest fix, <code>5dbc77d8</code>), the Entity framework saw activity (<code>e8c51555</code>), and Verse is being hardened into the gameplay layer as described below. That is the machinery that will eventually stand in for Actors and Blueprints.</p>
<p>But nothing this week deprecates either one. Blueprints, in fact, kept getting active maintenance: crash fixes, agent-facing <code>BlueprintTools</code> work (<code>0b5fb5ad</code>), a Blueprint DSL read-write round-trip (<code>9312dfd5</code>), and module-dependency cleanup. Close to twenty commits touched Blueprint code this week, and not one of them deprecated anything. A system Epic was about to pull would not be collecting a week of fixes. Epic’s own roadmap says early UE6 keeps both Actors and Blueprints, deprecates them only once the replacements mature, and ships conversion tools when it does.</p>
<p>So the honest read for anyone shipping on Blueprints today: the transition is real and the foundation is being poured now, but the commit activity says you have years, not months. Early Access is late 2027, and deprecation comes after that. The panic is running ahead of the code.</p>
<hr>
<h2 id="the-new-layer-ue6-specific">The New Layer (UE6-Specific)</h2>
<h3 id="verse-becomes-the-gameplay-layer">Verse Becomes the Gameplay Layer</h3>
<p><strong>What changed.</strong> The Verse VM and language picked up substantive work this week. A heap-correctness fix corrected NewObject-versus-DuplicateObject behavior in the VVM (<code>74971366</code>), the kind of fix that only matters once Verse objects are first-class engine citizens. The language itself gained semantics: discriminated unions are now VVM-exclusive (<code>40cd7231</code>), bitwise math arrived (<code>561a2f0b</code>), closure and <code>Self</code> capture were addressed (<code>771fd59a</code>), and <code>verse::event</code> delegates landed (<code>23c58057</code>). Verse-to-C++ interop is being wired through the toolchain via UHT support for <code>TVerseFunction</code> (<code>556568ff</code>) and decorated <code>FVerseFunction</code> names (<code>b14aa7ed</code>). Compiler diagnostics are maturing (<code>b6ede72f</code>, <code>fd43d560</code>), including folding non-Verse <code>@deprecated</code> into the diagnostics path (<code>61eda997</code>). The <code>@available</code> and <code>@experimental</code> propagation work continued across modules (<code>c892fe75</code>), and Verse tests are migrating into the engine tree (<code>7dd94112</code>). Notably, <code>CanCallerAccessEpicInternal</code> access-control appeared (<code>44c82bea</code>, <code>bccf67ac</code>): a Verse epic-internal trust boundary that reads as the governance layer for a merged UEFN/UE5 monorepo.</p>
<p><strong>Why it matters.</strong> This is the clearest signal of the first pillar. Verse is not being bolted onto the side. The VM is being made heap-correct, the language is gaining the primitives a real gameplay layer needs, and the interop path back to C++ is being formalized. The access-control work, in particular, is what makes one shared codebase across UEFN and the full engine governable.</p>
<p><strong>Who should care.</strong> Studios planning multi-year roadmaps who want to understand whether their gameplay code lives in C++ or Verse by the time UE6 ships.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="autortfm-making-transactionalize-c-real">AutoRTFM: Making “Transactionalize C++” Real</h3>
<p><strong>What changed.</strong> Three small commits, strategically load-bearing. <code>UE_AUTORTFM</code> is now gated behind EpicClang and <code>bUseAutoRTFMCompiler</code> is a build setting (<code>31c208fd</code>), and a <code>ShouldEnableAutoRTFM</code> default was corrected (<code>c8203d63</code>). The transactional substrate (software transactional memory, the mechanism behind the “transactionalize C++” claim) is being wired into the build as a real toolchain dependency.</p>
<p><strong>Why it matters.</strong> This is tiny in line count and large in implication. AutoRTFM depends on Epic’s custom compiler, which means external studios will eventually need that compiler in their toolchain to build UE6 with transactional memory enabled. That is a build-system dependency worth understanding early.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="a-first-party-mcp-server-in-the-editor">A First-Party MCP Server, In the Editor</h3>
<p><strong>What changed.</strong> Four <code>ModelContextProtocol</code> commits, none of them loud, easy to miss in the week’s volume: chunked Transfer-Encoding streaming (<code>e21014e1</code>), skipping auto-start in commandlets (<code>24e0ba98</code>), dropping the <code>Server</code> affix in favor of live URL composition (<code>300da934</code>), and a guard on the HTTP test spec (<code>5ae33186</code>). Alongside the server, the editor tooling is being designed for agent callers: <code>BlueprintTools</code> was redesigned to be idempotent with self-describing errors built for an LLM caller (<code>0b5fb5ad</code>), an <code>ActorTools.set_parent_component</code> crash was fixed (<code>bdeef4bd</code>), and a Mass editor toolset now lets an agent author configs.</p>
<p><strong>Why it matters.</strong> This is the AI pillar shipping as working, networked code rather than a prototype. The detail that matters is the shape of the tooling: idempotent mutations and self-describing errors are what you build when the caller is a model rather than a human. A first-party in-engine MCP server with hardened HTTP transport is a concrete commitment.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="mass-ecs-gains-replication-massirisreplication">Mass ECS Gains Replication (MassIrisReplication)</h3>
<p><strong>What changed.</strong> A seven-commit arc replicating Mass entities over Iris. It includes INI-driven exclusions, a <code>ReplicateOnce</code> path, a quantized transform net serializer, and the usual correctness, safety, and log-hygiene follow-ups. New framework code plus networking, landing together.</p>
<p><strong>Why it matters.</strong> Mass replication over Iris is the backbone for the “persistent large-scale worlds” pitch. Mass already gives you the entity scale; Iris replication is what lets that scale exist on the network. Worth noting: this arc spanned framework and networking changes and was never announced, a fair illustration of how the most important UE6 commits this week sat in the quiet tail, not at the top of the volume chart.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="the-uefn-merge-is-real-in-repo">The UEFN Merge Is Real In-Repo</h3>
<p>A short note, because it underpins both the Verse and MCP sections. The merge is not aspirational this week, it is in the tree: Solaris and Fortnite paths are present, the UEFN CPU Profiler landed (<code>3d908b3b</code>), and there is concrete UEFN cook and performance work, including a <code>ReparentHierarchies</code> improvement from 31 seconds to 18 seconds (<code>7161346e</code>, <code>0c0476ce</code>) and a fix excluding non-native Verse classes from incremental cook (<code>205b0219</code>), which was crashing. Incremental export of metadata (<code>533f10a2</code>) and publishing-mutator descriptors from cook (<code>8366092e</code>) also landed. The real measure of merge maturity will be UEFN cook and incremental stability inside the merged repo, and that is now something we can watch commit by commit.</p>
<hr>
<h2 id="ongoing-engine-work-on-the-new-branch-but-not-new">Ongoing Engine Work (On the New Branch, but Not New)</h2>
<p>Everything in this section would look identical on ue5-main. It is here for completeness and because it dominates the commit count, not because it signals anything UE6-specific.</p>
<h3 id="rendering-performance">Rendering &amp; Performance</h3>
<p>Lumen skylight prefilter mip-bias is now default-on (<code>40fbf7d8</code>). TSR re-enabled 16-bit VALU on modern Nvidia hardware (<code>f85b0521</code>). Opacity Micromap reached part two of its D3D12 RHI work (<code>46a4f3f0</code>), and D3D12 static root signatures landed (<code>bf4b30f7</code>). Beyond the feature work, there was the usual crash and correctness churn, plus a telling process signal: the Nanite “one big thread group” rasterizer change went land, backout, backout, re-land across four commits. A fresh development branch churns like this in its first weeks. Read it as process noise, not instability in any feature you would ship on.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="build-cook-distribution">Build, Cook &amp; Distribution</h3>
<p><code>BulkDataChunkAssigner</code> gained on-demand, optional, and startup containers sourced from the oplog, and cook-time chunking was disabled in that path. DDC-Verify build-versus-build determinism jobs landed, useful for anyone who cares about reproducible cooks. UBA continued its move onto Wine and Linux (an AlmaLinux-9 <code>UbaWine</code> path plus <code>TCP_MAXRT</code> handling), and UBT can now emit action history as JSON. For studios running build-at-scale and reproducibility-sensitive CI, this is the most directly relevant cluster in the report, even though none of it is UE6-specific.</p>
<p><strong>Urgency: Track.</strong></p>
<h3 id="stability-async-loading">Stability &amp; Async Loading</h3>
<p>ZenLoader picked up real fixes: a circular-flush deadlock was addressed at depth greater than one and again for the already-on-stack case (<code>56610334</code>, <code>90923462</code>), and hang diagnostics now dump all loading-thread callstacks (<code>95140162</code>). This is genuine C++ and a useful class of fix for anyone who has chased a load-time deadlock. (One note on counting: UAF here is the new Unreal Animation Framework, easy to mistake for use-after-free by name alone. It is not, so the week’s real crash-fix tally is smaller than a keyword scan suggests.)</p>
<p><strong>Urgency: Track.</strong></p>
<hr>
<h2 id="what-you-ship-on-now-ue-5x-stabilization">What You Ship On Now: UE 5.x Stabilization</h2>
<p><code>ue5-main</code> ran 113 commits this week, and it is not dead. Most of it is stabilization for shipping studios: a MetaHuman fix cluster, Sequencer and Anim Mixer regression fixes, Chaos Cloth, and a batch of crash fixes (several cherry-picked to both branches, so they also appear in the ue6-main work above). There was one status graduation: PC GDK plugins moved out of Beta (<code>25e8cfac</code>). UE 5.8 is the last planned major UE5 release, but Epic continues bug-fix and regression support and has said it may add another release if warranted. If you are shipping a game, you stay on 5.8, so from now on we track <code>ue5-main</code> alongside <code>ue6-main</code>: the new branch for where the engine is heading, the old one for the fixes that actually reach the project you ship this year. The UE6 coverage above is forward radar, not a reason to move.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p>The MCP transport is the cluster to watch. This week it gained hardened HTTP framing and live URL composition. The next steps that would matter are authenticated and scoped access plus formal tool definitions appearing in the tree, which is when an in-engine agent moves from “can connect” to “can be trusted with bounded capability.”</p>
<p>Separately, watch the Verse-as-C++-replacement curve: which runtime systems gain Verse-character parity. Chaos Mover already did this week: a mover for Verse characters landed (<code>adb72968</code>), and a temporary editor <code>VerseStateTree</code> was removed (<code>906d87ba</code>). Each system that crosses into Verse parity is a data point on how much of the gameplay layer Epic intends to live in Verse by ship. And keep watching UEFN cook and incremental stability inside the merged repo. That, more than any feature announcement, is the real measure of how far along the UE5/UEFN merge actually is.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>Roughly 700-plus commits of routine work: build hygiene, DDC-to-LegacyDDC renames, include cleanup, MacOS static-analyzer suppressions, Horde CI plumbing, and the usual Perforce backout and stomp noise (around 11 backouts this week). None of it affects direction. One caution, though: on a fresh branch, low volume is not low importance. The most consequential UE6 work this week (Verse, Mass replication, MCP) was quiet, the kind of thing a fast skim files as noise. It rewards reading every commit, not just the loud ones.</p>
<hr>
<h2 id="closing">Closing</h2>
<p>UE6 is a 2027 story, and from this week on we will follow it commit by commit from week one. We are not leaving ue5-main behind either. As long as studios are shipping on 5.x, we will keep tracking its maintenance branch alongside ue6-main, so this report serves both the game you ship this year and the engine you build on next.</p>
<p><em>Commit SHAs are cited inline, in parentheses, next to each claim. They reference the <code>ue6-main</code> branch of EpicGames/UnrealEngine (GDK is also on <code>ue5-main</code>). Many of the highest-interest entries resolve to <code>Commit.gitdeps.xml</code> binary-manifest blobs: the commit message is real, but the code lives in Epic’s internal binaries, which we cannot see.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-jun-15-21-2026/og.921cf31cd5.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Epic Dropped Lore. The Good, the Bad, and the Hidden Costs.</title>
      <link>https://speedrun.ci/en/blog/what-epic-lore-means-for-your-build-pipeline</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/what-epic-lore-means-for-your-build-pipeline</guid>
      <pubDate>Fri, 19 Jun 2026 12:37:08 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Blog</category>
      <description>Epic open-sourced Lore, its game version control system, at State of Unreal. Here’s a straight read: what’s genuinely good, what’s not ready, the costs nobody mentions, and whether your studio should actually move off Perforce or Diversion.</description>
      <content:encoded><![CDATA[<h1 id="epic-dropped-lore-the-good-the-bad-and-the-hidden-costs">Epic Dropped Lore. The Good, the Bad, and the Hidden Costs.</h1>
<p>Epic open-sourced Lore at State of Unreal. It’s a version control system built for game-scale projects: written in Rust, MIT-licensed, and already running in production as the VCS inside UEFN, where it used to be called Unreal Revision Control.</p>
<p>The headlines wrote themselves. Free, open source, Perforce killer. The reality is more useful than that, and more complicated. Here’s a straight read for studios actually deciding whether to care: the good, the bad, and the costs nobody’s putting in the celebration posts.</p>
<h2 id="the-good">The good</h2>
<p>Start with what’s genuinely strong here.</p>
<ul>
<li><strong>It’s built for binaries.</strong> Files are content-addressed and chunked, so editing a few KB inside a multi-GB asset only moves the changed chunks. That is exactly what Git and Git LFS are bad at, and it’s the core reason studios pay for Perforce in the first place.</li>
<li><strong>It’s free, with no per-seat fee.</strong> MIT-licensed, use it however you want. For a studio paying Perforce by the user account (including the bot and integration accounts that quietly add up), that’s a real line item gone.</li>
<li><strong>Sparse, on-demand checkouts.</strong> You pull only the parts of the repo you need instead of syncing the whole terabyte tree. Good for large projects and faster onboarding.</li>
<li><strong>It works offline.</strong> Branching, diffing, and history inspection all work without the server. Perforce can’t do that.</li>
<li><strong>Access control is built in.</strong> Each repo is its own partition and acts as an access boundary, so contractors and NDA’d work can be scoped cleanly. Knowing a file’s hash isn’t enough to pull it.</li>
<li><strong>It’s Epic, and it’s battle-tested.</strong> It has been running under Fortnite and UEFN at scale for years, so it arrives with a track record most version control launches never get. That pedigree is worth something.</li>
</ul>
<h2 id="the-bad">The bad</h2>
<p>What the launch posts skip.</p>
<ul>
<li><strong>It’s a pre-1.0 release.</strong> Epic says the storage formats are forward-compatible, but the APIs and protocols can still change before 1.0. You’d be adopting a moving target.</li>
<li><strong>Locking is informational only.</strong> This is the big one, and it’s for the exact teams Lore is meant to serve. A lock tells your teammate you’re editing a file. It does not stop them from editing it too. For binary assets you can’t merge, that collision is the whole problem, and Lore doesn’t solve it yet. Enforced locking is on the roadmap, not in the box.</li>
<li><strong>No Unreal Editor plugin until 2027.</strong> Most artists live inside the editor. Today Lore is a CLI and an API, with a VS Code plugin planned. The in-editor workflow your art team needs is over a year out.</li>
<li><strong>Unproven outside Fortnite.</strong> It scales for Epic. Whether it scales for your team size, your asset mix, and your infrastructure is untested in the wild on this open release.</li>
</ul>
<h2 id="the-hidden-costs">The hidden costs</h2>
<p>Here’s what “free and open source” quietly leaves out.</p>
<ul>
<li><strong>Someone has to run the server.</strong> Lore is centralized and self-hosted, with a single source of truth. You provision, scale, secure, and back up that server yourself. If you left Perforce partly to stop babysitting a server and a P4 admin, read that sentence again. You’d be trading a Perforce license fee for the cost of running infrastructure: ops time, a cloud bill, backups, someone on call when it breaks. That cost is real, and plenty of teams underestimate it.</li>
<li><strong>The open version and the Fortnite version aren’t the same yet.</strong> The open-source Lore and the hosted Lore inside UEFN currently use different compression formats. Epic plans to converge them (moving to Zstandard) in 2026. So “the thing Fortnite runs” and “the thing you can self-host today” are not the same build.</li>
<li><strong>Open source doesn’t mean community-run.</strong> This is Epic’s project, on Epic’s roadmap, built for Epic’s priorities, which are Fortnite and UEFN at massive scale. If your studio’s need isn’t on that list, it isn’t arriving because you asked. As one example, the roadmap has no CI/CD or build-integration items on it at all.</li>
<li><strong>Migration is a real project.</strong> Moving a studio’s full history off Perforce or Diversion takes planning and time, and Lore’s model works differently (immutable history, no force-push, no rewriting). Budget for the migration itself, well beyond the install.</li>
</ul>
<h2 id="so-should-you-move">So should you move?</h2>
<p>Depends on where you’re standing.</p>
<ul>
<li><strong>On Perforce and sick of the cost and the upkeep:</strong> worth a pilot on a non-critical project. Not worth migrating production yet. Wait for enforced locking, the editor plugin, and a 1.0, then reassess. Those are the three things to watch.</li>
<li><strong>On Diversion or another managed VCS:</strong> be clear about the trade. Diversion is managed cloud. Lore is self-hosted, do-it-yourself. You’d be swapping a vendor’s operations for your own. For a team that wants that control, it’s a fair deal. For most, it’s a step backward in convenience.</li>
<li><strong>Small team on Git or Git LFS and not actually hurting:</strong> stay. Lore solves a problem you may not have yet.</li>
<li><strong>Already deep in UEFN:</strong> you’re effectively using it already, and the native path is the obvious one.</li>
</ul>
<p>The honest summary: Lore is the most promising open version control to land in game dev in years, and it is not ready to be your studio’s production VCS this week. Start a pilot. Watch the roadmap. Don’t rip out Perforce on launch-week enthusiasm.</p>
<p>One thing worth flagging for anyone who cares about build times, since version control is where every build starts. The most interesting part of Lore long term is quieter than the check-in workflow. A content-addressed system knows, by hash, exactly what changed between any two revisions. That’s the signal a build system needs to rebuild only what changed instead of trusting file timestamps. None of that is wired up today, and Epic hasn’t said it’s coming. But it’s the reason we’re watching Lore closely, and it could matter more for your pipeline down the road than for your daily commits.</p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/what-epic-lore-means-for-your-build-pipeline/og.fcdc72e15d.jpg" type="image/jpeg" length="0"/>
    </item>
    <item>
      <title>Last Week in Unreal: June 8 to 14, 2026</title>
      <link>https://speedrun.ci/en/blog/last-week-in-unreal-jun-8-14-2026</link>
      <guid isPermaLink="true">https://speedrun.ci/en/blog/last-week-in-unreal-jun-8-14-2026</guid>
      <pubDate>Mon, 15 Jun 2026 07:13:48 GMT</pubDate>
      <dc:creator>Venkat S</dc:creator>
      <category>Last Week in Unreal</category>
      <description>770 commits on ue5-main. Epic is assembling a first-party Model Context Protocol layer inside the editor across about 30 commits. UnrealBuildTool gained a full per-warning control surface in BuildConfiguration.xml, and UBA absorbed an AB-BA lock-ordering deadlock fix in its CAS store.</description>
      <content:encoded><![CDATA[<h1 id="unreal-engine-weekly-what-changed-and-what-matters">Unreal Engine Weekly: What Changed and What Matters</h1>
<p><strong>Week of June 8 to 14, 2026</strong> | <strong>770 commits analyzed</strong> | Branch: <code>ue5-main</code></p>
<h2 id="overview">Overview</h2>
<p>This was an infrastructure week, dominated by two themes: Epic building first-party agentic tooling into the editor, and a broad cleanup of the build system. There was no flagship rendering feature, but several quiet correctness fixes in the distributed-build path are more consequential than they look. The 22 backout commits and an active mix of gameplay-systems work suggest a week of churn rather than a clean landing.</p>
<hr>
<h2 id="development-trends">Development Trends</h2>
<h3 id="editor-ai-tooling-from-ai-assistant-to-first-party-mcp-platform">Editor AI Tooling: From AI Assistant to First-Party MCP Platform</h3>
<p><strong>Pattern:</strong> This is the fifteenth consecutive week of editor-AI work, and the first where it shows up as a named editorial theme on its own (“AI &amp; Editor Tooling”). The editor subsystem reversed its multi-week slide, jumping from 40 to 81 commits week-over-week, largely on the back of roughly 30 commits across the Model Context Protocol and editor-toolset layer: MCP servers with stdio transport, an asset-tools layer that now resolves Unreal virtual paths, a CheatsToolset plugin, EditorAppToolset tools, per-toolset analytics with tool-hash mapping, a <code>UnrealConsole -mcp</code> mode, and a pre-release “Unreal Toolbox” app. The AI-in-editor theme also appeared in the Apr 13 and Apr 27 weeks; the line back to the original AI Assistant work is unbroken.</p>
<p><strong>Interpretation:</strong> Epic appears to be productizing LLM-driven editor automation as first-party infrastructure rather than an experiment. Prior weeks were about wiring the AI Assistant into Python and Blueprints. This week the energy moved to a standalone MCP layer with its own transport, analytics, and an external app. The path-resolution and snap-to-ground hardening suggest it is being tested against real editor behavior, not just demoed. Shipping an external Toolbox app on a pre-release channel is how you start preparing something for distribution.</p>
<p><strong>Who cares:</strong> Tools teams, technical artists, studios evaluating in-editor automation, and anyone building their own MCP integrations against the editor.</p>
<p><strong>Signal strength:</strong> Strong. 15 weeks of continuity, with a clear escalation in scope this week.</p>
<h3 id="build-infrastructure-quiet-post-cut-hardening-continues">Build Infrastructure: Quiet Post-Cut Hardening Continues</h3>
<p><strong>Pattern:</strong> Build commits fell again, from 66 to 50, the lowest in the tracked eight-week window and a long way down from the 251 peak the week of May 17. But the character is consistent with the last several weeks: not features, but correctness and policy. UnrealBuildTool gained a full per-toolchain, per-warning control surface in BuildConfiguration.xml; UBA absorbed an AB-BA lock-ordering deadlock fix in its CAS store; and Epic reverted a prior merged-modules “faster build times” change in favor of serial DLL linking. This extends the build infrastructure trend to roughly nineteen weeks.</p>
<p><strong>Interpretation:</strong> Falling volume alongside continued reliability work fits a branch that has already been cut and is now being made reproducible and safe to build at scale. The merged-module revert quietly admits that path had problems. The UBA deadlock fix likely resolves intermittent hangs that distributed-build teams had written off as flaky infrastructure.</p>
<p><strong>Who cares:</strong> Build engineers, CI compile-config owners, warnings-as-errors teams, and anyone running UBA at scale.</p>
<p><strong>Signal strength:</strong> Strong. Sustained for about 19 weeks, now firmly in low-volume polish mode.</p>
<h3 id="ray-tracing-productionization-cross-vendor-and-cross-renderer-reach">Ray Tracing Productionization: Cross-Vendor and Cross-Renderer Reach</h3>
<p><strong>Pattern:</strong> Sixteenth week of the ray-tracing modernization arc. Last week removed the <code>r.RayTracing.EnableOnDemand</code> cvar (RT default-on); this week the work pushed outward in two directions. Chaos Cloth components became ray-tracing-visible, so simulated cloth now contributes to RT reflections, RT shadows, and Lumen HWRT, closing a known gap. And NVIDIA shader-extension headers were made SPIR-V-safe with platforms now able to inject their own RT hit-group functions, plus Opacity Micromap support landed as “Part 1” in the D3D12 RHI.</p>
<p><strong>Interpretation:</strong> After promoting on-demand RT to always-on last week, the effort this week is about reach: making HWRT portable beyond NVIDIA and D3D12 (the SPIR-V and hit-group injection work) and pulling more of the scene into the RT path (cloth). This is consistent with treating RT as production infrastructure rather than a high-end opt-in. Opacity Micromaps remain early.</p>
<p><strong>Who cares:</strong> Graphics engineers shipping HWRT, multi-platform teams on Vulkan/SPIR-V, and teams with dynamic cloth in RT scenes.</p>
<p><strong>Signal strength:</strong> Strong. 16 weeks, though this week’s RT volume is moderate.</p>
<h3 id="pre-release-stability-sweep-steady-crash-hardening-amid-churn">Pre-Release Stability Sweep: Steady Crash Hardening Amid Churn</h3>
<p><strong>Pattern:</strong> Thirteenth week of the named stability theme. A run of genuine crash and safety fixes landed across gameplay and media systems (skin-weight slider, Mass representation traits, StateTree transition null deref, LearningAgents NaN assertion, Media IO interlace switch, PRT NaN/Inf JSON export), plus two thread-safety fixes and two OpenXR swapchain crash fixes. Set against 22 backout commits this week, the picture is a release line still working through instability rather than a clean landing.</p>
<p><strong>Interpretation:</strong> The fixes cluster around active gameplay-systems work (Mass, StateTree, Mover), which suggests those systems are getting attention and a steadier base at the same time. The 22 backouts are the counter-signal: this was a churny week, not a settled one. Consistent with a maturing release line that is still absorbing change.</p>
<p><strong>Who cares:</strong> Teams on Mass, StateTree, Mover, LearningAgents, Media IO, or OpenXR; anyone tracking <code>ue5-main</code> stability before upgrading.</p>
<p><strong>Signal strength:</strong> Strong on continuity (13 weeks), moderate on this week’s specific intensity.</p>
<hr>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Epic is assembling a real Model Context Protocol (MCP) layer inside the editor, around 30 commits across MCP servers, asset tools, a CheatsToolset, an “Unreal Toolbox” app, and per-toolset analytics. This is first-party LLM-driven editor automation, and the velocity says it has moved past prototype.</li>
<li>UnrealBuildTool gained a full compiler-warnings control surface in BuildConfiguration.xml (per-toolchain, per-warning), and UBA absorbed an AB-BA lock-ordering deadlock fix in its CAS store, plus a revert of the merged-module link path. If you run distributed builds at scale, this is the most actionable group of the week.</li>
<li>Nothing this week forces an upgrade. The MCP direction is the thing to watch; the build fixes are the thing to act on if they touch you.</li>
</ul>
<hr>
<h2 id="highlights">Highlights</h2>
<h3 id="rendering-performance">🔥 Rendering &amp; Performance</h3>
<p><strong>What changed:</strong> Substrate Toon shading continued to be built out: matching toon shadow and transmittance across CSM and VSM, streaming-aware ToonProfile textures, cubemap and matcap support, and baking indirect specular into a LUT. A first-pass AmbientLightingVolume system also landed (new shaders, a component visualizer), a tiled-culled ambient lighting primitive that currently only affects diffuse on the non-Lumen path and is explicitly marked for testing. On the ray tracing side, Chaos Cloth components became ray-tracing-visible, so simulated cloth now contributes to RT reflections, RT shadows, and Lumen HWRT. Opacity Micromap support landed as “Part 1” in the D3D12 RHI, and NVIDIA shader-extension headers were made SPIR-V-safe with platforms now able to inject their own RT hit-group functions. Lumen irradiance-field clipmaps bumped to six.</p>
<p><strong>Why this is important:</strong> The Substrate Toon work confirms NPR and stylized rendering as a first-class Substrate target rather than a side path. AmbientLightingVolume is an early-experimental signal of where mobile and non-Lumen GI is heading. The cloth RT change closes a known gap, since dynamic cloth was previously missing from RT scenes. (The cross-vendor RT story is covered in Development Trends above.)</p>
<p><strong>Who should care:</strong> Graphics engineers shipping HWRT, multi-platform teams (especially Vulkan/SPIR-V), and anyone on stylized or NPR projects.</p>
<p><strong>Urgency:</strong> Track. AmbientLightingVolume and Opacity Micromaps are early. The cloth RT change is low risk and worth picking up if you have cloth in RT scenes.</p>
<h3 id="build-tooling">🔧 Build &amp; Tooling</h3>
<p><strong>What changed:</strong> UnrealBuildTool now exposes every compiler warning through BuildConfiguration.xml and the command line. The monolithic warnings config was split into per-toolchain files (Clang, ClangVC, Intel, MSVC, Performance), replacing per-module flags with a single declarative surface. A header-units toggle was added to BuildConfiguration.xml, and an Intel-toolchain <code>-Wno-error=</code> token bug was fixed. New PreCleanSteps and PostCleanSteps hooks plus a <code>-CleanGenerated</code> flag improve CI build hygiene. On the distributed side, UBA absorbed a fix for an AB-BA lock-ordering deadlock in its CAS store (two locks taken in opposite order under heavy load), which had been surfacing as intermittent hangs on large UbaNinja builds. Epic also reverted a prior merged-modules “faster build times” change and forced merged-module DLL links to run serially.</p>
<p><strong>Why this is important:</strong> The warnings overhaul gives build and CI teams one place to define a warnings policy instead of scattering flags across modules. The UBA deadlock fix likely explains hangs that have been written off as flaky infrastructure. And if you turned on merged modules for build speed, the revert is worth knowing about before you upgrade.</p>
<p><strong>Who should care:</strong> Build engineers, CI compile-config owners, warnings-as-errors teams, and anyone running UBA or large distributed builds.</p>
<p><strong>Urgency:</strong> Act now if you maintain a custom warning policy, a CI lint gate, or run UBA at scale. Track otherwise.</p>
<h3 id="ai-editor-tooling">🤖 AI &amp; Editor Tooling</h3>
<p><strong>What changed:</strong> Epic landed coherent work on an in-editor asset-tools layer driven by Model Context Protocol. Asset tools now resolve Unreal virtual paths (<code>/Game/...</code>, <code>/PluginName/...</code>) instead of mis-mapping to filesystem roots, gained a <code>reload_asset</code> tool, and actor placement got snap-to-ground robustness plus a “placement skill” that teaches the agent when to snap. Around that core, the surface accreted across many authors: a CheatsToolset plugin that lists and calls cheat-manager commands, EditorAppToolset tools for notifications, cached thumbnails and viewport show-flags, MCP client and proxy fixes, stdio transport for local MCP servers, an “Unreal Toolbox” app on a pre-release channel, per-toolset analytics with tool-hash mapping, and a UnrealConsole <code>-mcp</code> mode. Verse VM also renamed its <code>llm_*</code> metadata to <code>ai_*</code>.</p>
<p><strong>Why this is important:</strong> The virtual-path resolution and snap-to-ground fixes are the practical part. They mean the agent now addresses assets and places actors the way a human tools engineer would, which is the difference between a demo and something a technical artist can actually drive. (For the multi-week direction read on this workstream, see Development Trends above.)</p>
<p><strong>Who should care:</strong> Tools teams, technical artists, and anyone tracking Epic’s AI-in-editor direction.</p>
<p><strong>Urgency:</strong> Track closely. Nothing to integrate yet, but this is the workstream most likely to change how editor automation looks in upcoming releases.</p>
<h3 id="stability-fixes">🛟 Stability &amp; Fixes</h3>
<p><strong>What changed:</strong> A run of genuine crash and safety fixes landed across gameplay and media systems: a skin-weight slider crash on commit, a Mass representation-traits crash when no representation subsystem is present, a StateTree null deref in transition pin calculation, a LearningAgents NaN assertion in flow-matching inference, a Media IO assertion when switching interlace and progressive, and a PRT export change to write NaN/Inf as null instead of crashing on JSON export. Two thread-safety fixes also landed: Mover concurrency control over its Rollback Blackboard, and an input device hardware delegate now always executing on the game thread. Two OpenXR swapchain crash fixes and an AnimatorKit deformer race-condition fix round it out.</p>
<p><strong>Why this is important:</strong> Each of these quietly removes a reproducible crash from production workflows. The Mass, StateTree, and Mover fixes cluster around active gameplay-systems work this week, so projects on those systems should expect a steadier base.</p>
<p><strong>Who should care:</strong> Teams on Mass, StateTree, Mover, LearningAgents, Media IO, or OpenXR.</p>
<p><strong>Urgency:</strong> Low. Pick these up on your normal integration cadence.</p>
<hr>
<h2 id="worth-tracking">Worth Tracking</h2>
<p>A dedicated input polling thread is taking shape but is off by default. Two commits plus a backout add an <code>input.UseInputThread</code> CVar and a dedicated thread for polling input devices, landing alongside work that removes the game-thread requirement on device connections and adds Unreal Insights CPU trace scopes to the input thread. Adding trace scopes means Epic plans to profile and tune this thread, not throw it away. That makes it a latency play. Watch whether the CVar default flips in a future release.</p>
<p>Two quieter items are worth a line each. A “progressive navdata” navigation cluster (background coverage scan plus per-tile state tracking) uses terms that are not standard shipped UE vocabulary, which suggests an in-progress streaming or adaptive navmesh system rather than maintenance. And the AI tooling trajectory above is the broader thing to keep an eye on: MCP, Unreal Toolbox, AI Assistant, and LearningAgents were all active in the same week, which reads as a sustained workstream rather than a one-off experiment.</p>
<hr>
<h2 id="what-we-ignored">What We Ignored</h2>
<p>This week we ignored roughly 29 commits, mostly related to formatting, minor refactors, editor UI cleanups, and localization. Worth noting for context: the 22 backout commits this week are themselves a signal of an unstable stretch on <code>ue5-main</code>, and a cluster of around 20 “UAF” commits is Unreal Animation Framework work, not memory-safety fixes, despite the acronym.</p>
<hr>
<h2 id="closing">Closing</h2>
<p>No fireworks this week, but the build-system fixes will save someone a debugging afternoon, and the editor AI tooling is the trend worth reading early. We read every commit so you do not have to.</p>
<hr>
<details>
<summary>Commit References</summary>
<table>
<thead>
<tr>
<th>Claim</th>
<th>SHA</th>
</tr>
</thead>
<tbody>
<tr>
<td>UBT exposes every compiler warning through BuildConfiguration.xml</td>
<td><code>307800a2bc</code></td>
</tr>
<tr>
<td>CppCompileWarnings split into per-toolchain files</td>
<td><code>4817a4124c</code></td>
</tr>
<tr>
<td>Intel toolchain <code>-Wno-error=</code> token bug fixed</td>
<td><code>80f9b021f0</code></td>
</tr>
<tr>
<td>Header-units toggle added to BuildConfiguration.xml</td>
<td><code>84c5d52c12</code></td>
</tr>
<tr>
<td>New PreCleanSteps/PostCleanSteps and <code>-CleanGenerated</code> flag</td>
<td><code>72c2a68839</code></td>
</tr>
<tr>
<td>UBA AB-BA lock-ordering deadlock fix in CAS store</td>
<td><code>6507e24916</code></td>
</tr>
<tr>
<td>Merged-modules “faster build times” change reverted</td>
<td><code>bf8f4b326d</code></td>
</tr>
<tr>
<td>Merged-module DLL links forced serial</td>
<td><code>42984161ec</code></td>
</tr>
<tr>
<td>MCP asset tools resolve Unreal virtual paths</td>
<td><code>90d4e4dc56</code></td>
</tr>
<tr>
<td>New <code>reload_asset</code> tool</td>
<td><code>5083e1d6f0</code></td>
</tr>
<tr>
<td>Actor placement snap-to-ground plus placement skill</td>
<td><code>bdc61f390c</code></td>
</tr>
<tr>
<td>MCP proxy fix (no longer consumes first HTTP entry)</td>
<td><code>05ea622e69</code></td>
</tr>
<tr>
<td>CheatsToolset plugin</td>
<td><code>878e534437</code></td>
</tr>
<tr>
<td>EditorAppToolset tools (notifications, thumbnails, show-flags)</td>
<td><code>90d0fba561</code></td>
</tr>
<tr>
<td>MCP stdio transport for local servers</td>
<td><code>dc19d0aa24</code></td>
</tr>
<tr>
<td>UnrealConsole <code>-mcp</code> mode</td>
<td><code>b472e3e41d</code></td>
</tr>
<tr>
<td>Verse VM <code>llm_*</code> metadata renamed to <code>ai_*</code></td>
<td><code>aa5fcb400f</code></td>
</tr>
<tr>
<td>Substrate Toon shadow/transmittance across CSM and VSM</td>
<td><code>eef3f0de5c</code></td>
</tr>
<tr>
<td>First-pass AmbientLightingVolume primitive</td>
<td><code>94560b8a14</code></td>
</tr>
<tr>
<td>Chaos Cloth components become ray-tracing-visible</td>
<td><code>d70b02a3e6</code></td>
</tr>
<tr>
<td>Opacity Micromap support Part 1 in D3D12 RHI</td>
<td><code>98841f07c7</code></td>
</tr>
<tr>
<td>NVIDIA shader-extension headers made SPIR-V-safe</td>
<td><code>b4c5775a20</code></td>
</tr>
<tr>
<td>Nanite RT switches to RootPageIndex over RuntimeResourceID</td>
<td><code>9469789725</code></td>
</tr>
<tr>
<td>Lumen irradiance-field clipmaps bumped to six</td>
<td><code>754d184203</code></td>
</tr>
<tr>
<td>Skin-weight slider crash on commit fixed</td>
<td><code>d4f3d2929b</code></td>
</tr>
<tr>
<td>Mass representation-traits crash fixed</td>
<td><code>5169b57bc4</code></td>
</tr>
<tr>
<td>StateTree null deref in transition pin calc fixed</td>
<td><code>4bdb3f3f0c</code></td>
</tr>
<tr>
<td>LearningAgents NaN assertion in flow-matching inference fixed</td>
<td><code>45f8b8804b</code></td>
</tr>
<tr>
<td>Media IO assertion switching interlace/progressive fixed</td>
<td><code>b262343564</code></td>
</tr>
<tr>
<td>PRT writes NaN/Inf as null instead of crashing on JSON export</td>
<td><code>c666b332d3</code></td>
</tr>
<tr>
<td>Mover concurrency control over Rollback Blackboard</td>
<td><code>addd2d4f79</code></td>
</tr>
<tr>
<td>Input device hardware delegate always executes on game thread</td>
<td><code>7c50c828de</code></td>
</tr>
<tr>
<td>OpenXR swapchain crash fixes</td>
<td><code>1336ec50be</code></td>
</tr>
<tr>
<td>AnimatorKit deformer race-condition fix</td>
<td><code>8c5bc1b90d</code></td>
</tr>
<tr>
<td><code>input.UseInputThread</code> dedicated input polling thread (off by default)</td>
<td><code>9771532fec</code></td>
</tr>
<tr>
<td>“Progressive navdata” navigation cluster (background coverage scan)</td>
<td><code>b9c8981e4c</code></td>
</tr>
</tbody>
</table>
<p>All SHAs reference the <code>ue5-main</code> branch of EpicGames/UnrealEngine.</p>
</details>
<hr>
<p><em>We read every commit on <code>ue5-main</code> so you don’t have to. Last Week in Unreal is published weekly by <a href="https://www.speedrun.ci">SpeedrunCI</a>, CI/CD built for Unreal Engine teams.</em></p>]]></content:encoded>
      <enclosure url="https://speedrun.ci/blog-media/last-week-in-unreal-jun-8-14-2026/og.45f2277ae7.jpg" type="image/jpeg" length="0"/>
    </item>
  </channel>
</rss>
