Diffs 0 to 15 are counted one by one, then by band 16-31, 32-63, 64-127, 128+. A pass against the lane's own gold with pixels at diff 3+ keeps its PNG, so CompareGold can tell the renders apart and turn them into variants. ForceSaveImageOnSuccess saves the PNG again.
Metadata records the CPU's vector features. CompareGold promote --add-variants adds a render from another CPU as a variant, and promote --source takes a normal CI run's failed renders.
TesselationTest golds are refreshed for current Lavapipe and WARP; its thresholds stay, WARP and Lavapipe differ by a few wireframe pixels between CPU models.
Runtime checks now read `GraphicsDeviceFeatures` instead of comparing `GraphicsProfile` levels. A field says what Stride can use on this device with this backend: `false`/`None` when the backend does not implement it, even if the GPU supports it.
New fields:
- `HasIndex32Bits`
- `HasGeometryShaders`
- `HasPixelShaderUnorderedAccess` (Vulkan: `geometryShader` and `fragmentStoresAndAtomics`, now enabled at device creation when offered)
Vulkan now reports `FormatSupport` per format.
The device features are logged at device creation.
Fixes:
- D3D11 `CheckMipLevels` clamped mips on depth-stencil textures instead of the other textures.
- Vulkan refused 32-bit indices when the requested profile was 9_x.
- `LightShafts` throws a clear error without a shadow map renderer, instead of a `NullReferenceException` every frame.
- `VoxelRenderer` needs compute shaders and pixel-shader UAVs. It logs once and skips instead of throwing, and skips volumes whose voxelization or clipmap update method needs geometry shaders when the device has none.
- `ForwardRenderer` names the reason when it lowers the MSAA level. The iOS MSAA clamp is removed (it was for OpenGL ES).
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
WindowManager treated every hide event as a close. When the main window was hidden, MainWindow was cleared. When it was shown again, it was registered as a modal window, which disabled blocking windows such as progress windows for the rest of the session. In Game Studio, this locked the editor after a failed import.
A hidden main window now stays registered until it is destroyed, and it is not activated while hidden.
The constructor taking the render target formats as a span looped over all eight slots and
read past the end of any shorter span, throwing IndexOutOfRangeException.
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The guard `stepCount < MaxStepPerFrame || MaxStepPerFrame != -1` did not depend
on stepCount: any cap other than -1 never bounded the loop, and -1 never ran a step.
Fixes#3522
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
FindBestDevices skipped every adapter without a display output, to keep WARP and the Basic Render
Driver out. On a hybrid laptop every display is wired to the integrated GPU, so the discrete one -
sorted first by DXGI - had no output and was dropped, and every game ran on the integrated GPU.
The skip now tests what it meant: GraphicsAdapter.IsSoftwareAdapter (the DXGI software flag or the
Microsoft vendor id on Direct3D, the Cpu device type on Vulkan). When nothing survives, the search
runs again without the skip, so a machine with only a software adapter still gets one. Vulkan
enumerates the discrete GPU first, as DXGI's high-performance order already does on Direct3D.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Packages: a package resolves a relative project path and loads its assemblies into the session's container
- Package: resolve a relative project path before loading it as an implicit package
- Packages: a package of a session loads its assemblies into the session's container whoever asks (UpdateAssemblyReferences after a failed first load used the process default one, so a dependent assembly bound to a second copy of its dependency next to itself and the serializer registry rejected it)
* Packages: a package assembly shares the assemblies the host ships and finds the native libraries next to it
- Packages: register the native libraries shipped next to a package assembly when loading it, so they resolve without a file location
- Packages: an assembly the host ships is shared with the packages that load it, never loaded a second time from the package
* Editor: overlapping builds queue, and the reload question waits for an open progress window
- Projects: builds queue on the shared MSBuild BuildManager (one build session at a time) instead of failing with 'a build is already in progress' when project loading, the debugging view's build or assembly reload and a restore overlap; a build canceled while queued does not start
- Editor: a delayed question waits while a progress window or a message box is up, instead of being asked on top of it (the assembly reload question landed on the Creating project window); WindowManager raises MainWindowUnblocked when the last one closes, which flushes the pending questions
* Editor: an assembly reload converts the live objects and rebuilds the scene game content of every assembly it swaps
- Editor: the scene game's content references skip a node that cannot take the value, and content loaded before an assembly reload is built again instead of being pushed into a node of the new assembly; a reloaded type keeps its name but is a different type, so an object still waiting for the change propagator threw 'Can not convert value to the required type' in the middle of the content reload, leaving the assets being reloaded never completed
- Editor: an assembly reload converts the live objects of every assembly it swaps, instead of only those whose recorded path exists on disk; the objects of a skipped assembly kept types of the unloaded copy, and cloning their asset then found no serializer
* Editor: undoing an assembly reload puts back the previous asset, assemblies and their categories
- Editor: replacing a whole asset during an assembly reload is undone by putting the previous asset back (AssetChangeOperation over AssetViewModel.UpdateAsset); it was recorded as a content change on the asset's root node, which holds no value, so undoing the reload of an asset whose own type was reloaded threw a NullReferenceException
- Editor: undoing an assembly reload puts the previous assemblies back into the session's container (AssemblyContainer.RemoveAssembly returns what it knew, RestoreAssembly takes it back); the container resolves what an assembly references through the assembly that asks, so a restored assembly that the container no longer held could not resolve its own dependencies
- Editor: an assembly loaded again by a reload takes back the categories it was registered with (AssemblyRegistry.GetCategories), instead of only the asset one; the Engine category comes from the assembly's module initializer, which runs once, so an assembly put back by an undo lost its content types and their asset pickers became read-only text
* Editor: a project the opened solution does not hold is added to the script workspace on its own (MSBuildWorkspace.OpenProjectAsync), with the projects it references; the workspace opened the solution once, so the projects of a plugin added by Add plugin while a session is open were never found, their packages were never tracked, and editing their code offered no assembly reload until the next Game Studio start
Fixes from `dotnet format analyzers`:
- Add == / != to ColorHSV, NavigationMeshBuildSettings and ContactPoint
- VirtualFileSystemTests reads with ReadExactly
- IndexingDictionaryTests checks both Count and the enumerated pair
Co-authored-by: Virgile Bello <[email protected]>
- Editor: replacing a whole asset during an assembly reload is undone by putting the previous asset back (AssetChangeOperation over AssetViewModel.UpdateAsset); it was recorded as a content change on the asset's root node, which holds no value, so undoing the reload of an asset whose own type was reloaded threw a NullReferenceException
- Editor: undoing an assembly reload puts the previous assemblies back into the session's container (AssemblyContainer.RemoveAssembly returns what it knew, RestoreAssembly takes it back); the container resolves what an assembly references through the assembly that asks, so a restored assembly that the container no longer held could not resolve its own dependencies
- Editor: an assembly loaded again by a reload takes back the categories it was registered with (AssemblyRegistry.GetCategories), instead of only the asset one; the Engine category comes from the assembly's module initializer, which runs once, so an assembly put back by an undo lost its content types and their asset pickers became read-only text
- Editor: the scene game's content references skip a node that cannot take the value, and content loaded before an assembly reload is built again instead of being pushed into a node of the new assembly; a reloaded type keeps its name but is a different type, so an object still waiting for the change propagator threw 'Can not convert value to the required type' in the middle of the content reload, leaving the assets being reloaded never completed
- Editor: an assembly reload converts the live objects of every assembly it swaps, instead of only those whose recorded path exists on disk; the objects of a skipped assembly kept types of the unloaded copy, and cloning their asset then found no serializer
- Projects: builds queue on the shared MSBuild BuildManager (one build session at a time) instead of failing with 'a build is already in progress' when project loading, the debugging view's build or assembly reload and a restore overlap; a build canceled while queued does not start
- Editor: a delayed question waits while a progress window or a message box is up, instead of being asked on top of it (the assembly reload question landed on the Creating project window); WindowManager raises MainWindowUnblocked when the last one closes, which flushes the pending questions
- Packages: register the native libraries shipped next to a package assembly when loading it, so they resolve without a file location
- Packages: an assembly the host ships is shared with the packages that load it, never loaded a second time from the package
- Package: resolve a relative project path before loading it as an implicit package
- Packages: a package of a session loads its assemblies into the session's container whoever asks (UpdateAssemblyReferences after a failed first load used the process default one, so a dependent assembly bound to a second copy of its dependency next to itself and the serializer registry rejected it)