From 9b495bbc6830c2f8f666adb1f620839ead0e4261 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Thu, 13 Mar 2025 17:18:11 +0100 Subject: [PATCH 001/207] improve how ColorValueBoxes draw transparent colors --- Source/Editor/GUI/Input/ColorValueBox.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Source/Editor/GUI/Input/ColorValueBox.cs b/Source/Editor/GUI/Input/ColorValueBox.cs index 1531266e3..1416ccc58 100644 --- a/Source/Editor/GUI/Input/ColorValueBox.cs +++ b/Source/Editor/GUI/Input/ColorValueBox.cs @@ -129,11 +129,20 @@ namespace FlaxEditor.GUI.Input { base.Draw(); - var style = Style.Current; - var r = new Rectangle(0, 0, Width, Height); + bool isTransparent = _value.A < 1; - Render2D.FillRectangle(r, _value); - Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black); + var style = Style.Current; + var fullRect = new Rectangle(0, 0, Width, Height); + var colorRect = new Rectangle(0, 0, isTransparent ? Width * 0.7f : Width, Height); + + if (isTransparent) + { + var alphaRect = new Rectangle(colorRect.Right, 0, Width - colorRect.Right, Height); + Render2D.FillRectangle(alphaRect, _value); + } + + Render2D.FillRectangle(colorRect, _value with { A = 1 }); + Render2D.DrawRectangle(fullRect, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black); } /// From 051d3633588d5694cc88f6202e8ef2f0f406c642 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Fri, 14 Mar 2025 15:51:36 +0100 Subject: [PATCH 002/207] add alpha grid background --- Source/Editor/EditorAssets.cs | 5 +++++ Source/Editor/GUI/Input/ColorValueBox.cs | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index bdfc7dfa6..d30ca91c0 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -134,6 +134,11 @@ namespace FlaxEditor /// public static string FlaxIconBlueTexture = "Engine/Textures/FlaxIconBlue"; + /// + /// The checkboard material used as a background for displaying transparent colors. + /// + public static string ColorAlphaBackgroundGrid = "Editor/AlphaBackgroundGrid"; + /// /// The icon lists used by editor from the SegMDL2 font. /// diff --git a/Source/Editor/GUI/Input/ColorValueBox.cs b/Source/Editor/GUI/Input/ColorValueBox.cs index 1416ccc58..1893c4124 100644 --- a/Source/Editor/GUI/Input/ColorValueBox.cs +++ b/Source/Editor/GUI/Input/ColorValueBox.cs @@ -14,7 +14,12 @@ namespace FlaxEditor.GUI.Input [HideInEditor] public class ColorValueBox : Control { + private const String ScaleParamName = "Scale"; + // 4.8 is a magic number that makes the grid fit perfect in the color value box + private const float GridScale = 4.8f; + private bool _isMouseDown; + private MaterialBase checkerMaterial; /// /// Delegate function used for the color picker events handling. @@ -101,6 +106,9 @@ namespace FlaxEditor.GUI.Input public ColorValueBox() : base(0, 0, 32, 18) { + checkerMaterial = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.ColorAlphaBackgroundGrid); + checkerMaterial = checkerMaterial.CreateVirtualInstance(); + checkerMaterial.SetParameterValue(ScaleParamName, GridScale); } /// @@ -138,6 +146,7 @@ namespace FlaxEditor.GUI.Input if (isTransparent) { var alphaRect = new Rectangle(colorRect.Right, 0, Width - colorRect.Right, Height); + Render2D.DrawMaterial(checkerMaterial, alphaRect); Render2D.FillRectangle(alphaRect, _value); } From 78dffc9ad11c524799ab8cbdaf76285fee85268f Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 5 Apr 2025 15:24:19 +0300 Subject: [PATCH 003/207] Add override for building CMake project with custom configuration --- Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs | 2 +- Source/Tools/Flax.Build/Deps/Dependency.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs index 7be41423f..9719a7ec2 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs @@ -195,7 +195,7 @@ namespace Flax.Deps.Dependencies RunCmake(cmakeFolder, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + cmakeArgs, envVars); // Run build - BuildCmake(cmakeFolder, envVars); + BuildCmake(cmakeFolder, envVars: envVars); // Deploy binaries var libs = new[] diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index 848f2a2a9..2405cc991 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -251,10 +251,11 @@ namespace Flax.Deps /// Builds the cmake project. /// /// The path. + /// The configuration preset. /// Custom environment variables to pass to the child process. - public static void BuildCmake(string path, Dictionary envVars = null) + public static void BuildCmake(string path, string config = "Release", Dictionary envVars = null) { - Utilities.Run("cmake", "--build . --config Release", null, path, Utilities.RunOptions.DefaultTool, envVars); + Utilities.Run("cmake", $"--build . --config {config}", null, path, Utilities.RunOptions.DefaultTool, envVars); } /// From 643fe639e52b837a5843fee03bfdc8c2c07ea889 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 5 Apr 2025 13:47:42 +0300 Subject: [PATCH 004/207] Update OpenAL to 1.24.3 --- Source/ThirdParty/OpenAL/al.h | 325 +++++---- Source/ThirdParty/OpenAL/alc.h | 113 ++-- Source/ThirdParty/OpenAL/alext.h | 621 +++++++++++++++--- Source/ThirdParty/OpenAL/efx-presets.h | 2 + Source/ThirdParty/OpenAL/efx.h | 134 ++-- .../Flax.Build/Deps/Dependencies/OpenAL.cs | 17 +- 6 files changed, 860 insertions(+), 352 deletions(-) diff --git a/Source/ThirdParty/OpenAL/al.h b/Source/ThirdParty/OpenAL/al.h index 5071fa5e3..a4e3ad515 100644 --- a/Source/ThirdParty/OpenAL/al.h +++ b/Source/ThirdParty/OpenAL/al.h @@ -1,8 +1,40 @@ #ifndef AL_AL_H #define AL_AL_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { + +#ifdef _MSVC_LANG +#define AL_CPLUSPLUS _MSVC_LANG +#else +#define AL_CPLUSPLUS __cplusplus +#endif + +#ifndef AL_DISABLE_NOEXCEPT +#if AL_CPLUSPLUS >= 201103L +#define AL_API_NOEXCEPT noexcept +#else +#define AL_API_NOEXCEPT +#endif +#if AL_CPLUSPLUS >= 201703L +#define AL_API_NOEXCEPT17 noexcept +#else +#define AL_API_NOEXCEPT17 +#endif + +#else /* AL_DISABLE_NOEXCEPT */ + +#define AL_API_NOEXCEPT +#define AL_API_NOEXCEPT17 +#endif + +#undef AL_CPLUSPLUS + +#else /* __cplusplus */ + +#define AL_API_NOEXCEPT +#define AL_API_NOEXCEPT17 #endif #ifndef AL_API @@ -455,220 +487,221 @@ typedef void ALvoid; #ifndef AL_NO_PROTOTYPES /* Renderer State management. */ -AL_API void AL_APIENTRY alEnable(ALenum capability); -AL_API void AL_APIENTRY alDisable(ALenum capability); -AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability); +AL_API void AL_APIENTRY alEnable(ALenum capability) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDisable(ALenum capability) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsEnabled(ALenum capability) AL_API_NOEXCEPT; /* Context state setting. */ -AL_API void AL_APIENTRY alDopplerFactor(ALfloat value); -AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value); -AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value); -AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel); +AL_API void AL_APIENTRY alDopplerFactor(ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDopplerVelocity(ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSpeedOfSound(ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDistanceModel(ALenum distanceModel) AL_API_NOEXCEPT; /* Context state retrieval. */ -AL_API const ALchar* AL_APIENTRY alGetString(ALenum param); -AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values); -AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values); -AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values); -AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param); -AL_API ALint AL_APIENTRY alGetInteger(ALenum param); -AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param); -AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param); +AL_API const ALchar* AL_APIENTRY alGetString(ALenum param) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBooleanv(ALenum param, ALboolean *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetIntegerv(ALenum param, ALint *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFloatv(ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetDoublev(ALenum param, ALdouble *values) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alGetBoolean(ALenum param) AL_API_NOEXCEPT; +AL_API ALint AL_APIENTRY alGetInteger(ALenum param) AL_API_NOEXCEPT; +AL_API ALfloat AL_APIENTRY alGetFloat(ALenum param) AL_API_NOEXCEPT; +AL_API ALdouble AL_APIENTRY alGetDouble(ALenum param) AL_API_NOEXCEPT; /** * Obtain the first error generated in the AL context since the last call to * this function. */ -AL_API ALenum AL_APIENTRY alGetError(void); +AL_API ALenum AL_APIENTRY alGetError(void) AL_API_NOEXCEPT; /** Query for the presence of an extension on the AL context. */ -AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname); +AL_API ALboolean AL_APIENTRY alIsExtensionPresent(const ALchar *extname) AL_API_NOEXCEPT; /** * Retrieve the address of a function. The returned function may be context- * specific. */ -AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname); +AL_API void* AL_APIENTRY alGetProcAddress(const ALchar *fname) AL_API_NOEXCEPT; /** * Retrieve the value of an enum. The returned value may be context-specific. */ -AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename); +AL_API ALenum AL_APIENTRY alGetEnumValue(const ALchar *ename) AL_API_NOEXCEPT; /* Set listener parameters. */ -AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value); -AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value); -AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values); +AL_API void AL_APIENTRY alListenerf(ALenum param, ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListener3f(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListenerfv(ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListeneri(ALenum param, ALint value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListener3i(ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alListeneriv(ALenum param, const ALint *values) AL_API_NOEXCEPT; /* Get listener parameters. */ -AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetListenerf(ALenum param, ALfloat *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListener3f(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListenerfv(ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListeneri(ALenum param, ALint *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListener3i(ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetListeneriv(ALenum param, ALint *values) AL_API_NOEXCEPT; /** Create source objects. */ -AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources); +AL_API void AL_APIENTRY alGenSources(ALsizei n, ALuint *sources) AL_API_NOEXCEPT; /** Delete source objects. */ -AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alDeleteSources(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Verify an ID is for a valid source. */ -AL_API ALboolean AL_APIENTRY alIsSource(ALuint source); +AL_API ALboolean AL_APIENTRY alIsSource(ALuint source) AL_API_NOEXCEPT; /* Set source parameters. */ -AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value); -AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value); -AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values); +AL_API void AL_APIENTRY alSourcef(ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3f(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcefv(ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcei(ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3i(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourceiv(ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT; /* Get source parameters. */ -AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetSourcef(ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3f(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcefv(ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcei(ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3i(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourceiv(ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT; /** Play, restart, or resume a source, setting its state to AL_PLAYING. */ -AL_API void AL_APIENTRY alSourcePlay(ALuint source); +AL_API void AL_APIENTRY alSourcePlay(ALuint source) AL_API_NOEXCEPT; /** Stop a source, setting its state to AL_STOPPED if playing or paused. */ -AL_API void AL_APIENTRY alSourceStop(ALuint source); +AL_API void AL_APIENTRY alSourceStop(ALuint source) AL_API_NOEXCEPT; /** Rewind a source, setting its state to AL_INITIAL. */ -AL_API void AL_APIENTRY alSourceRewind(ALuint source); +AL_API void AL_APIENTRY alSourceRewind(ALuint source) AL_API_NOEXCEPT; /** Pause a source, setting its state to AL_PAUSED if playing. */ -AL_API void AL_APIENTRY alSourcePause(ALuint source); +AL_API void AL_APIENTRY alSourcePause(ALuint source) AL_API_NOEXCEPT; /** Play, restart, or resume a list of sources atomically. */ -AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourcePlayv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Stop a list of sources atomically. */ -AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourceStopv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Rewind a list of sources atomically. */ -AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourceRewindv(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Pause a list of sources atomically. */ -AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources); +AL_API void AL_APIENTRY alSourcePausev(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; /** Queue buffers onto a source */ -AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers); +AL_API void AL_APIENTRY alSourceQueueBuffers(ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT; /** Unqueue processed buffers from a source */ -AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers); +AL_API void AL_APIENTRY alSourceUnqueueBuffers(ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT; /** Create buffer objects */ -AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers); +AL_API void AL_APIENTRY alGenBuffers(ALsizei n, ALuint *buffers) AL_API_NOEXCEPT; /** Delete buffer objects */ -AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers); +AL_API void AL_APIENTRY alDeleteBuffers(ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT; /** Verify an ID is a valid buffer (including the NULL buffer) */ -AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer); +AL_API ALboolean AL_APIENTRY alIsBuffer(ALuint buffer) AL_API_NOEXCEPT; /** * Copies data into the buffer, interpreting it using the specified format and * samplerate. */ -AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate); +AL_API void AL_APIENTRY alBufferData(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT; /* Set buffer parameters. */ -AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value); -AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values); -AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value); -AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); -AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values); +AL_API void AL_APIENTRY alBufferf(ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBuffer3f(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferfv(ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferi(ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBuffer3i(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferiv(ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT; /* Get buffer parameters. */ -AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value); -AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values); -AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value); -AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); -AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values); +AL_API void AL_APIENTRY alGetBufferf(ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBuffer3f(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferfv(ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferi(ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBuffer3i(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferiv(ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT; #endif /* AL_NO_PROTOTYPES */ /* Pointer-to-function types, useful for storing dynamically loaded AL entry * points. */ -typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability); -typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability); -typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability); -typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param); -typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values); -typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values); -typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param); -typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param); -typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param); -typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param); -typedef ALenum (AL_APIENTRY *LPALGETERROR)(void); -typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname); -typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname); -typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename); -typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources); -typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources); -typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources); -typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source); -typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers); -typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers); -typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers); -typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers); -typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer); -typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate); -typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value); -typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3); -typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values); -typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value); -typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3); -typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values); -typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value); -typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3); -typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values); -typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value); -typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3); -typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values); -typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value); -typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value); -typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value); -typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel); +typedef void (AL_APIENTRY *LPALENABLE)(ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISABLE)(ALenum capability) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISENABLED)(ALenum capability) AL_API_NOEXCEPT17; +typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)(ALenum param) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBOOLEANV)(ALenum param, ALboolean *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETINTEGERV)(ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFLOATV)(ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETDOUBLEV)(ALenum param, ALdouble *values) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)(ALenum param) AL_API_NOEXCEPT17; +typedef ALint (AL_APIENTRY *LPALGETINTEGER)(ALenum param) AL_API_NOEXCEPT17; +typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)(ALenum param) AL_API_NOEXCEPT17; +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)(ALenum param) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETERROR)(void) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar *extname) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)(const ALchar *fname) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)(const ALchar *ename) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERF)(ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3F)(ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFV)(ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERI)(ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3I)(ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIV)(ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERF)(ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3F)(ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFV)(ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERI)(ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3I)(ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIV)(ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENSOURCES)(ALsizei n, ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETESOURCES)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISSOURCE)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEF)(ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3F)(ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFV)(ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI)(ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3I)(ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIV)(ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEF)(ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3F)(ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFV)(ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI)(ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3I)(ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIV)(ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)(ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAY)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOP)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWIND)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSE)(ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENBUFFERS)(ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEBUFFERS)(ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISBUFFER)(ALuint buffer) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERDATA)(ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERF)(ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3F)(ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFV)(ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERI)(ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3I)(ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIV)(ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERF)(ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3F)(ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFV)(ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERI)(ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3I)(ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIV)(ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)(ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)(ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)(ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISTANCEMODEL)(ALenum distanceModel) AL_API_NOEXCEPT17; #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_AL_H */ diff --git a/Source/ThirdParty/OpenAL/alc.h b/Source/ThirdParty/OpenAL/alc.h index 6d2103335..d048ca04d 100644 --- a/Source/ThirdParty/OpenAL/alc.h +++ b/Source/ThirdParty/OpenAL/alc.h @@ -1,8 +1,40 @@ #ifndef AL_ALC_H #define AL_ALC_H +/* NOLINTBEGIN */ #ifdef __cplusplus extern "C" { + +#ifdef _MSVC_LANG +#define ALC_CPLUSPLUS _MSVC_LANG +#else +#define ALC_CPLUSPLUS __cplusplus +#endif + +#ifndef AL_DISABLE_NOEXCEPT +#if ALC_CPLUSPLUS >= 201103L +#define ALC_API_NOEXCEPT noexcept +#else +#define ALC_API_NOEXCEPT +#endif +#if ALC_CPLUSPLUS >= 201703L +#define ALC_API_NOEXCEPT17 noexcept +#else +#define ALC_API_NOEXCEPT17 +#endif + +#else /* AL_DISABLE_NOEXCEPT */ + +#define ALC_API_NOEXCEPT +#define ALC_API_NOEXCEPT17 +#endif + +#undef ALC_CPLUSPLUS + +#else /* __cplusplus */ + +#define ALC_API_NOEXCEPT +#define ALC_API_NOEXCEPT17 #endif #ifndef ALC_API @@ -172,34 +204,34 @@ typedef void ALCvoid; /* Context management. */ /** Create and attach a context to the given device. */ -ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist); +ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrlist) ALC_API_NOEXCEPT; /** * Makes the given context the active process-wide context. Passing NULL clears * the active context. */ -ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context); +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context) ALC_API_NOEXCEPT; /** Resumes processing updates for the given context. */ -ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcProcessContext(ALCcontext *context) ALC_API_NOEXCEPT; /** Suspends updates for the given context. */ -ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcSuspendContext(ALCcontext *context) ALC_API_NOEXCEPT; /** Remove a context from its device and destroys it. */ -ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context); +ALC_API void ALC_APIENTRY alcDestroyContext(ALCcontext *context) ALC_API_NOEXCEPT; /** Returns the currently active context. */ -ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void); +ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void) ALC_API_NOEXCEPT; /** Returns the device that a particular context is attached to. */ -ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context); +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *context) ALC_API_NOEXCEPT; /* Device management. */ /** Opens the named playback device. */ -ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename); +ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *devicename) ALC_API_NOEXCEPT; /** Closes the given playback device. */ -ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device); +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device) ALC_API_NOEXCEPT; /* Error support. */ /** Obtain the most recent Device error. */ -ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device); +ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device) ALC_API_NOEXCEPT; /* Extension support. */ @@ -207,24 +239,24 @@ ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device); * Query for the presence of an extension on the device. Pass a NULL device to * query a device-inspecific extension. */ -ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname); +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extname) ALC_API_NOEXCEPT; /** * Retrieve the address of a function. Given a non-NULL device, the returned * function may be device-specific. */ -ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname); +ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcname) ALC_API_NOEXCEPT; /** * Retrieve the value of an enum. Given a non-NULL device, the returned value * may be device-specific. */ -ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname); +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumname) ALC_API_NOEXCEPT; /* Query functions. */ /** Returns information about the device, and error strings. */ -ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param); +ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *device, ALCenum param) ALC_API_NOEXCEPT; /** Returns information about the device and the version of OpenAL. */ -ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); +ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) ALC_API_NOEXCEPT; /* Capture functions. */ @@ -232,43 +264,44 @@ ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum pa * Opens the named capture device with the given frequency, format, and buffer * size. */ -ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) ALC_API_NOEXCEPT; /** Closes the given capture device. */ -ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device); +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device) ALC_API_NOEXCEPT; /** Starts capturing samples into the device buffer. */ -ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device); +ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device) ALC_API_NOEXCEPT; /** Stops capturing samples. Samples in the device buffer remain available. */ -ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device); +ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device) ALC_API_NOEXCEPT; /** Reads samples from the device buffer. */ -ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) ALC_API_NOEXCEPT; #endif /* ALC_NO_PROTOTYPES */ /* Pointer-to-function types, useful for storing dynamically loaded ALC entry * points. */ -typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist); -typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context); -typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context); -typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void); -typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context); -typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename); -typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device); -typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device); -typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname); -typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname); -typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname); -typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param); -typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values); -typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize); -typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device); -typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +typedef ALCcontext* (ALC_APIENTRY *LPALCCREATECONTEXT)(ALCdevice *device, const ALCint *attrlist) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef ALCcontext* (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)(void) ALC_API_NOEXCEPT17; +typedef ALCdevice* (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef ALCdevice* (ALC_APIENTRY *LPALCOPENDEVICE)(const ALCchar *devicename) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)(ALCdevice *device, const ALCchar *extname) ALC_API_NOEXCEPT17; +typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname) ALC_API_NOEXCEPT17; +typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)(ALCdevice *device, ALCenum param) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCGETINTEGERV)(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values) ALC_API_NOEXCEPT17; +typedef ALCdevice* (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)(const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCCAPTURESTART)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) ALC_API_NOEXCEPT17; #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_ALC_H */ diff --git a/Source/ThirdParty/OpenAL/alext.h b/Source/ThirdParty/OpenAL/alext.h index d313a999a..3908e1940 100644 --- a/Source/ThirdParty/OpenAL/alext.h +++ b/Source/ThirdParty/OpenAL/alext.h @@ -1,6 +1,7 @@ #ifndef AL_ALEXT_H #define AL_ALEXT_H +/* NOLINTBEGIN */ #include /* Define int64 and uint64 types */ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || \ @@ -25,6 +26,8 @@ typedef uint64_t _alsoft_uint64_t; extern "C" { #endif +struct _GUID; + #ifndef AL_LOKI_IMA_ADPCM_format #define AL_LOKI_IMA_ADPCM_format 1 #define AL_FORMAT_IMA_ADPCM_MONO16_EXT 0x10000 @@ -141,9 +144,9 @@ extern "C" { #ifndef AL_EXT_STATIC_BUFFER #define AL_EXT_STATIC_BUFFER 1 -typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALuint,ALenum,ALvoid*,ALsizei,ALsizei); +typedef void (AL_APIENTRY*PFNALBUFFERDATASTATICPROC)(const ALuint,ALenum,ALvoid*,ALsizei,ALsizei) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq); +void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT; #endif #endif @@ -159,11 +162,11 @@ void AL_APIENTRY alBufferDataStatic(const ALuint buffer, ALenum format, ALvoid * #ifndef ALC_EXT_thread_local_context #define ALC_EXT_thread_local_context 1 -typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context); -typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void); +typedef ALCboolean (ALC_APIENTRY*PFNALCSETTHREADCONTEXTPROC)(ALCcontext *context) ALC_API_NOEXCEPT17; +typedef ALCcontext* (ALC_APIENTRY*PFNALCGETTHREADCONTEXTPROC)(void) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context); -ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); +ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context) ALC_API_NOEXCEPT; +ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void) ALC_API_NOEXCEPT; #endif #endif @@ -176,9 +179,9 @@ ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void); #define AL_SOFT_buffer_sub_data 1 #define AL_BYTE_RW_OFFSETS_SOFT 0x1031 #define AL_SAMPLE_RW_OFFSETS_SOFT 0x1032 -typedef void (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei); +typedef void (AL_APIENTRY*PFNALBUFFERSUBDATASOFTPROC)(ALuint,ALenum,const ALvoid*,ALsizei,ALsizei) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length); +AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const ALvoid *data,ALsizei offset,ALsizei length) AL_API_NOEXCEPT; #endif #endif @@ -195,12 +198,12 @@ AL_API void AL_APIENTRY alBufferSubDataSOFT(ALuint buffer,ALenum format,const AL #define AL_FOLDBACK_EVENT_STOP 0x4113 #define AL_FOLDBACK_MODE_MONO 0x4101 #define AL_FOLDBACK_MODE_STEREO 0x4102 -typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei); -typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK); -typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void); +typedef void (AL_APIENTRY*LPALFOLDBACKCALLBACK)(ALenum,ALsizei) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTART)(ALenum,ALsizei,ALsizei,ALfloat*,LPALFOLDBACKCALLBACK) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALREQUESTFOLDBACKSTOP)(void) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback); -AL_API void AL_APIENTRY alRequestFoldbackStop(void); +AL_API void AL_APIENTRY alRequestFoldbackStart(ALenum mode,ALsizei count,ALsizei length,ALfloat *mem,LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alRequestFoldbackStop(void) AL_API_NOEXCEPT; #endif #endif @@ -263,15 +266,15 @@ AL_API void AL_APIENTRY alRequestFoldbackStop(void); #define AL_SAMPLE_LENGTH_SOFT 0x200A #define AL_SEC_LENGTH_SOFT 0x200B -typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*); -typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*); -typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*); -typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum); +typedef void (AL_APIENTRY*LPALBUFFERSAMPLESSOFT)(ALuint,ALuint,ALenum,ALsizei,ALenum,ALenum,const ALvoid*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALBUFFERSUBSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,const ALvoid*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFERSAMPLESSOFT)(ALuint,ALsizei,ALsizei,ALenum,ALenum,ALvoid*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY*LPALISBUFFERFORMATSUPPORTEDSOFT)(ALenum) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); -AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data); -AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data); -AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); +AL_API void AL_APIENTRY alBufferSamplesSOFT(ALuint buffer, ALuint samplerate, ALenum internalformat, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alBufferSubSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, const ALvoid *data) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferSamplesSOFT(ALuint buffer, ALsizei offset, ALsizei samples, ALenum channels, ALenum type, ALvoid *data) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format) AL_API_NOEXCEPT; #endif #endif @@ -302,13 +305,13 @@ AL_API ALboolean AL_APIENTRY alIsBufferFormatSupportedSOFT(ALenum format); #define ALC_6POINT1_SOFT 0x1505 #define ALC_7POINT1_SOFT 0x1506 -typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*); -typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum); -typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei); +typedef ALCdevice* (ALC_APIENTRY*LPALCLOOPBACKOPENDEVICESOFT)(const ALCchar*) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCISRENDERFORMATSUPPORTEDSOFT)(ALCdevice*,ALCsizei,ALCenum,ALCenum) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCRENDERSAMPLESSOFT)(ALCdevice*,ALCvoid*,ALCsizei) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName); -ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type); -ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples); +ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName) AL_API_NOEXCEPT; +ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type) AL_API_NOEXCEPT; +ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples) AL_API_NOEXCEPT; #endif #endif @@ -328,31 +331,31 @@ ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffe #define AL_SEC_OFFSET_LATENCY_SOFT 0x1201 typedef _alsoft_int64_t ALint64SOFT; typedef _alsoft_uint64_t ALuint64SOFT; -typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble); -typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble); -typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*); -typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*); -typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT); -typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT); -typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*); -typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*); +typedef void (AL_APIENTRY*LPALSOURCEDSOFT)(ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCE3DSOFT)(ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEDVSOFT)(ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEDSOFT)(ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCE3DSOFT)(ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEDVSOFT)(ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEI64VSOFT)(ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEI64SOFT)(ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCE3I64SOFT)(ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETSOURCEI64VSOFT)(ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value); -AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3); -AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values); -AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value); -AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3); -AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values); -AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value); -AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3); -AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values); -AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value); -AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3); -AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values); +AL_API void AL_APIENTRY alSourcedSOFT(ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3dSOFT(ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcedvSOFT(ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcedSOFT(ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3dSOFT(ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcedvSOFT(ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alSourcei64vSOFT(ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcei64SOFT(ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSource3i64SOFT(ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT; #endif #endif @@ -364,11 +367,11 @@ AL_API void AL_APIENTRY alGetSourcei64vSOFT(ALuint source, ALenum param, ALint64 #ifndef AL_SOFT_deferred_updates #define AL_SOFT_deferred_updates 1 #define AL_DEFERRED_UPDATES_SOFT 0xC002 -typedef void (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void); -typedef void (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void); +typedef void (AL_APIENTRY*LPALDEFERUPDATESSOFT)(void) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPROCESSUPDATESSOFT)(void) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alDeferUpdatesSOFT(void); -AL_API void AL_APIENTRY alProcessUpdatesSOFT(void); +AL_API void AL_APIENTRY alDeferUpdatesSOFT(void) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alProcessUpdatesSOFT(void) AL_API_NOEXCEPT; #endif #endif @@ -400,11 +403,11 @@ AL_API void AL_APIENTRY alProcessUpdatesSOFT(void); #ifndef ALC_SOFT_pause_device #define ALC_SOFT_pause_device 1 -typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device); -typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device); +typedef void (ALC_APIENTRY*LPALCDEVICEPAUSESOFT)(ALCdevice *device) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCDEVICERESUMESOFT)(ALCdevice *device) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device); -ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); +ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device) ALC_API_NOEXCEPT; +ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device) ALC_API_NOEXCEPT; #endif #endif @@ -448,11 +451,11 @@ ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device); #define ALC_NUM_HRTF_SPECIFIERS_SOFT 0x1994 #define ALC_HRTF_SPECIFIER_SOFT 0x1995 #define ALC_HRTF_ID_SOFT 0x1996 -typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index); -typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs); +typedef const ALCchar* (ALC_APIENTRY*LPALCGETSTRINGISOFT)(ALCdevice *device, ALCenum paramName, ALCsizei index) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCRESETDEVICESOFT)(ALCdevice *device, const ALCint *attribs) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index); -ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs); +ALC_API const ALCchar* ALC_APIENTRY alcGetStringiSOFT(ALCdevice *device, ALCenum paramName, ALCsizei index) ALC_API_NOEXCEPT; +ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCint *attribs) ALC_API_NOEXCEPT; #endif #endif @@ -467,9 +470,9 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi #define AL_DEFAULT_RESAMPLER_SOFT 0x1211 #define AL_SOURCE_RESAMPLER_SOFT 0x1212 #define AL_RESAMPLER_NAME_SOFT 0x1213 -typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index); +typedef const ALchar* (AL_APIENTRY*LPALGETSTRINGISOFT)(ALenum pname, ALsizei index) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index); +AL_API const ALchar* AL_APIENTRY alGetStringiSOFT(ALenum pname, ALsizei index) AL_API_NOEXCEPT; #endif #endif @@ -493,9 +496,9 @@ typedef _alsoft_uint64_t ALCuint64SOFT; #define ALC_DEVICE_CLOCK_LATENCY_SOFT 0x1602 #define AL_SAMPLE_OFFSET_CLOCK_SOFT 0x1202 #define AL_SEC_OFFSET_CLOCK_SOFT 0x1203 -typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +typedef void (ALC_APIENTRY*LPALCGETINTEGER64VSOFT)(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values); +ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALsizei size, ALCint64SOFT *values) ALC_API_NOEXCEPT; #endif #endif @@ -552,27 +555,26 @@ ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, #define AL_EVENT_TYPE_SOURCE_STATE_CHANGED_SOFT 0x19A5 #define AL_EVENT_TYPE_DISCONNECTED_SOFT 0x19A6 typedef void (AL_APIENTRY*ALEVENTPROCSOFT)(ALenum eventType, ALuint object, ALuint param, - ALsizei length, const ALchar *message, - void *userParam); -typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable); -typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam); -typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname); -typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values); + ALsizei length, const ALchar *message, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALEVENTCONTROLSOFT)(ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALEVENTCALLBACKSOFT)(ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALGETPOINTERSOFT)(ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVSOFT)(ALenum pname, void **values) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable); -AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam); -AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname); -AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values); +AL_API void AL_APIENTRY alEventControlSOFT(ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEventCallbackSOFT(ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT; +AL_API void* AL_APIENTRY alGetPointerSOFT(ALenum pname) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetPointervSOFT(ALenum pname, void **values) AL_API_NOEXCEPT; #endif #endif #ifndef ALC_SOFT_reopen_device #define ALC_SOFT_reopen_device typedef ALCboolean (ALC_APIENTRY*LPALCREOPENDEVICESOFT)(ALCdevice *device, - const ALCchar *deviceName, const ALCint *attribs); + const ALCchar *deviceName, const ALCint *attribs) ALC_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *deviceName, - const ALCint *attribs); + const ALCint *attribs) ALC_API_NOEXCEPT; #endif #endif @@ -580,16 +582,16 @@ ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *de #define AL_SOFT_callback_buffer #define AL_BUFFER_CALLBACK_FUNCTION_SOFT 0x19A0 #define AL_BUFFER_CALLBACK_USER_PARAM_SOFT 0x19A1 -typedef ALsizei (AL_APIENTRY*ALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numbytes); -typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr); -typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value); -typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3); -typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values); +typedef ALsizei (AL_APIENTRY*ALBUFFERCALLBACKTYPESOFT)(ALvoid *userptr, ALvoid *sampledata, ALsizei numbytes) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALBUFFERCALLBACKSOFT)(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFERPTRSOFT)(ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFER3PTRSOFT)(ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETBUFFERPTRVSOFT)(ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr); -AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr); -AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2); -AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **ptr); +AL_API void AL_APIENTRY alBufferCallbackSOFT(ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferPtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBuffer3PtrSOFT(ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; #endif #endif @@ -640,16 +642,451 @@ AL_API void AL_APIENTRY alGetBufferPtrvSOFT(ALuint buffer, ALenum param, ALvoid #ifndef AL_SOFT_source_start_delay #define AL_SOFT_source_start_delay -typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMESOFT)(ALuint source, ALint64SOFT start_time); -typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMEVSOFT)(ALsizei n, const ALuint *sources, ALint64SOFT start_time); +typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMESOFT)(ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALSOURCEPLAYATTIMEVSOFT)(ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time); -void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time); +void AL_APIENTRY alSourcePlayAtTimeSOFT(ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayAtTimevSOFT(ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT; #endif #endif +#ifndef ALC_EXT_debug +#define ALC_EXT_debug +#define ALC_CONTEXT_FLAGS_EXT 0x19CF +#define ALC_CONTEXT_DEBUG_BIT_EXT 0x0001 +#endif + +#ifndef AL_EXT_debug +#define AL_EXT_debug +#define AL_DONT_CARE_EXT 0x0002 +#define AL_DEBUG_OUTPUT_EXT 0x19B2 +#define AL_DEBUG_CALLBACK_FUNCTION_EXT 0x19B3 +#define AL_DEBUG_CALLBACK_USER_PARAM_EXT 0x19B4 +#define AL_DEBUG_SOURCE_API_EXT 0x19B5 +#define AL_DEBUG_SOURCE_AUDIO_SYSTEM_EXT 0x19B6 +#define AL_DEBUG_SOURCE_THIRD_PARTY_EXT 0x19B7 +#define AL_DEBUG_SOURCE_APPLICATION_EXT 0x19B8 +#define AL_DEBUG_SOURCE_OTHER_EXT 0x19B9 +#define AL_DEBUG_TYPE_ERROR_EXT 0x19BA +#define AL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_EXT 0x19BB +#define AL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_EXT 0x19BC +#define AL_DEBUG_TYPE_PORTABILITY_EXT 0x19BD +#define AL_DEBUG_TYPE_PERFORMANCE_EXT 0x19BE +#define AL_DEBUG_TYPE_MARKER_EXT 0x19BF +#define AL_DEBUG_TYPE_PUSH_GROUP_EXT 0x19C0 +#define AL_DEBUG_TYPE_POP_GROUP_EXT 0x19C1 +#define AL_DEBUG_TYPE_OTHER_EXT 0x19C2 +#define AL_DEBUG_SEVERITY_HIGH_EXT 0x19C3 +#define AL_DEBUG_SEVERITY_MEDIUM_EXT 0x19C4 +#define AL_DEBUG_SEVERITY_LOW_EXT 0x19C5 +#define AL_DEBUG_SEVERITY_NOTIFICATION_EXT 0x19C6 +#define AL_DEBUG_LOGGED_MESSAGES_EXT 0x19C7 +#define AL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_EXT 0x19C8 +#define AL_MAX_DEBUG_MESSAGE_LENGTH_EXT 0x19C9 +#define AL_MAX_DEBUG_LOGGED_MESSAGES_EXT 0x19CA +#define AL_MAX_DEBUG_GROUP_STACK_DEPTH_EXT 0x19CB +#define AL_MAX_LABEL_LENGTH_EXT 0x19CC +#define AL_STACK_OVERFLOW_EXT 0x19CD +#define AL_STACK_UNDERFLOW_EXT 0x19CE +#define AL_CONTEXT_FLAGS_EXT 0x19CF +#define AL_BUFFER_EXT 0x1009 /* Same as AL_BUFFER */ +#define AL_SOURCE_EXT 0x19D0 +#define AL_FILTER_EXT 0x19D1 +#define AL_EFFECT_EXT 0x19D2 +#define AL_AUXILIARY_EFFECT_SLOT_EXT 0x19D3 + +typedef void (AL_APIENTRY*ALDEBUGPROCEXT)(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKEXT)(ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTEXT)(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLEXT)(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPEXT)(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPEXT)(void) AL_API_NOEXCEPT17; +typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGEXT)(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETOBJECTLABELEXT)(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALGETPOINTEREXT)(ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVEXT)(ALenum pname, void **values) AL_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +void AL_APIENTRY alDebugMessageCallbackEXT(ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageInsertEXT(ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageControlEXT(ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alPushDebugGroupEXT(ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alPopDebugGroupEXT(void) AL_API_NOEXCEPT; +ALuint AL_APIENTRY alGetDebugMessageLogEXT(ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; +void AL_APIENTRY alObjectLabelEXT(ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT; +void AL_APIENTRY alGetObjectLabelEXT(ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerEXT(ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervEXT(ALenum pname, void **values) AL_API_NOEXCEPT; +#endif +#endif + +#ifndef ALC_SOFT_system_events +#define ALC_SOFT_system_events +#define ALC_PLAYBACK_DEVICE_SOFT 0x19D4 +#define ALC_CAPTURE_DEVICE_SOFT 0x19D5 +#define ALC_EVENT_TYPE_DEFAULT_DEVICE_CHANGED_SOFT 0x19D6 +#define ALC_EVENT_TYPE_DEVICE_ADDED_SOFT 0x19D7 +#define ALC_EVENT_TYPE_DEVICE_REMOVED_SOFT 0x19D8 +#define ALC_EVENT_SUPPORTED_SOFT 0x19D9 +#define ALC_EVENT_NOT_SUPPORTED_SOFT 0x19DA +typedef void (ALC_APIENTRY*ALCEVENTPROCTYPESOFT)(ALCenum eventType, ALCenum deviceType, + ALCdevice *device, ALCsizei length, const ALCchar *message, void *userParam) ALC_API_NOEXCEPT17; +typedef ALCenum (ALC_APIENTRY*LPALCEVENTISSUPPORTEDSOFT)(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT17; +typedef ALCboolean (ALC_APIENTRY*LPALCEVENTCONTROLSOFT)(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT17; +typedef void (ALC_APIENTRY*LPALCEVENTCALLBACKSOFT)(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +ALCenum ALC_APIENTRY alcEventIsSupportedSOFT(ALCenum eventType, ALCenum deviceType) ALC_API_NOEXCEPT; +ALCboolean ALC_APIENTRY alcEventControlSOFT(ALCsizei count, const ALCenum *events, ALCboolean enable) ALC_API_NOEXCEPT; +void ALC_APIENTRY alcEventCallbackSOFT(ALCEVENTPROCTYPESOFT callback, void *userParam) ALC_API_NOEXCEPT; +#endif +#endif + +#ifndef AL_EXT_direct_context +#define AL_EXT_direct_context +typedef ALCvoid* (ALC_APIENTRY *LPALCGETPROCADDRESS2)(ALCdevice *device, const ALCchar *funcname) AL_API_NOEXCEPT17; + +typedef void (AL_APIENTRY *LPALENABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISABLEDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISENABLEDDIRECT)(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDOPPLERFACTORDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSPEEDOFSOUNDDIRECT)(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDISTANCEMODELDIRECT)(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT17; +typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBOOLEANVDIRECT)(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETINTEGERVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFLOATVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETDOUBLEVDIRECT)(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEANDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALint (AL_APIENTRY *LPALGETINTEGERDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALfloat (AL_APIENTRY *LPALGETFLOATDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLEDIRECT)(ALCcontext *context, ALenum param) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETERRORDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENTDIRECT)(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPROCADDRESSDIRECT)(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUEDIRECT)(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERFVDIRECT)(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALLISTENERIVDIRECT)(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFDIRECT)(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3FDIRECT)(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERFVDIRECT)(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIDIRECT)(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENER3IDIRECT)(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETLISTENERIVDIRECT)(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENSOURCESDIRECT)(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETESOURCESDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISSOURCEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3FDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEFVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3IDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEIVDIRECT)(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEDIRECT)(ALCcontext *context, ALuint source) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCESTOPVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEREWINDVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPAUSEVDIRECT)(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERSDIRECT)(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENBUFFERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEBUFFERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISBUFFERDIRECT)(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERDATADIRECT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3FDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERFVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3IDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERIVDIRECT)(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT17; +/* ALC_EXT_EFX */ +typedef void (AL_APIENTRY *LPALGENEFFECTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEEFFECTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEFFECTDIRECT)(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFVDIRECT)(ALCcontext *context, ALuint effect, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENFILTERSDIRECT)(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEFILTERSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISFILTERDIRECT)(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFVDIRECT)(ALCcontext *context, ALuint filter, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTSDIRECT)(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOTDIRECT)(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALint *values) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFVDIRECT)(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *values) AL_API_NOEXCEPT17; +/* AL_EXT_BUFFER_DATA_STATIC */ +typedef void (AL_APIENTRY *LPALBUFFERDATASTATICDIRECT)(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT17; +/* AL_EXT_debug */ +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECALLBACKDIRECTEXT)(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGEINSERTDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALDEBUGMESSAGECONTROLDIRECTEXT)(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPUSHDEBUGGROUPDIRECTEXT)(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALPOPDEBUGGROUPDIRECTEXT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef ALuint (AL_APIENTRY*LPALGETDEBUGMESSAGELOGDIRECTEXT)(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETOBJECTLABELDIRECTEXT)(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY*LPALGETPOINTERDIRECTEXT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY*LPALGETPOINTERVDIRECTEXT)(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT17; +/* AL_EXT_FOLDBACK */ +typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTARTDIRECT)(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALREQUESTFOLDBACKSTOPDIRECT)(ALCcontext *context) AL_API_NOEXCEPT17; +/* AL_SOFT_buffer_sub_data */ +typedef void (AL_APIENTRY *LPALBUFFERSUBDATADIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT17; +/* AL_SOFT_source_latency */ +typedef void (AL_APIENTRY *LPALSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble,ALdouble,ALdouble) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEDDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3DDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*,ALdouble*,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEDVDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALdouble*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT,ALint64SOFT,ALint64SOFT) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,const ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCE3I64DIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*,ALint64SOFT*,ALint64SOFT*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETSOURCEI64VDIRECTSOFT)(ALCcontext*,ALuint,ALenum,ALint64SOFT*) AL_API_NOEXCEPT17; +/* AL_SOFT_deferred_updates */ +typedef void (AL_APIENTRY *LPALDEFERUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALPROCESSUPDATESDIRECTSOFT)(ALCcontext *context) AL_API_NOEXCEPT17; +/* AL_SOFT_source_resampler */ +typedef const ALchar* (AL_APIENTRY *LPALGETSTRINGIDIRECTSOFT)(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT17; +/* AL_SOFT_events */ +typedef void (AL_APIENTRY *LPALEVENTCONTROLDIRECTSOFT)(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEVENTCALLBACKDIRECTSOFT)(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT17; +typedef void* (AL_APIENTRY *LPALGETPOINTERDIRECTSOFT)(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETPOINTERVDIRECTSOFT)(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT17; +/* AL_SOFT_callback_buffer */ +typedef void (AL_APIENTRY *LPALBUFFERCALLBACKDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERPTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFER3PTRDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **value1, ALvoid **value2, ALvoid **value3) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETBUFFERPTRVDIRECTSOFT)(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **values) AL_API_NOEXCEPT17; +/* AL_SOFT_source_start_delay */ +typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEDIRECTSOFT)(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALSOURCEPLAYATTIMEVDIRECTSOFT)(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT17; +/* EAX */ +typedef ALenum (AL_APIENTRY *LPEAXSETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPEAXGETDIRECT)(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPEAXSETBUFFERMODEDIRECT)(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT17; +typedef ALenum (AL_APIENTRY *LPEAXGETBUFFERMODEDIRECT)(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT17; +#ifdef AL_ALEXT_PROTOTYPES +ALCvoid* ALC_APIENTRY alcGetProcAddress2(ALCdevice *device, const ALCchar *funcName) AL_API_NOEXCEPT; + +void AL_APIENTRY alEnableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; +void AL_APIENTRY alDisableDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsEnabledDirect(ALCcontext *context, ALenum capability) AL_API_NOEXCEPT; + +void AL_APIENTRY alDopplerFactorDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alSpeedOfSoundDirect(ALCcontext *context, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alDistanceModelDirect(ALCcontext *context, ALenum distanceModel) AL_API_NOEXCEPT; + +const ALchar* AL_APIENTRY alGetStringDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBooleanvDirect(ALCcontext *context, ALenum param, ALboolean *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetIntegervDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFloatvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetDoublevDirect(ALCcontext *context, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alGetBooleanDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALint AL_APIENTRY alGetIntegerDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALfloat AL_APIENTRY alGetFloatDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; +ALdouble AL_APIENTRY alGetDoubleDirect(ALCcontext *context, ALenum param) AL_API_NOEXCEPT; + +ALenum AL_APIENTRY alGetErrorDirect(ALCcontext *context) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsExtensionPresentDirect(ALCcontext *context, const ALchar *extname) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetProcAddressDirect(ALCcontext *context, const ALchar *fname) AL_API_NOEXCEPT; +ALenum AL_APIENTRY alGetEnumValueDirect(ALCcontext *context, const ALchar *ename) AL_API_NOEXCEPT; + +void AL_APIENTRY alListenerfDirect(ALCcontext *context, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alListener3fDirect(ALCcontext *context, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alListenerfvDirect(ALCcontext *context, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alListeneriDirect(ALCcontext *context, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alListener3iDirect(ALCcontext *context, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alListenerivDirect(ALCcontext *context, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerfDirect(ALCcontext *context, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListener3fDirect(ALCcontext *context, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerfvDirect(ALCcontext *context, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListeneriDirect(ALCcontext *context, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListener3iDirect(ALCcontext *context, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetListenerivDirect(ALCcontext *context, ALenum param, ALint *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenSourcesDirect(ALCcontext *context, ALsizei n, ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteSourcesDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsSourceDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceivDirect(ALCcontext *context, ALuint source, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcefDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3fDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcefvDirect(ALCcontext *context, ALuint source, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourceiDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3iDirect(ALCcontext *context, ALuint source, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourceivDirect(ALCcontext *context, ALuint source, ALenum param, ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceStopDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceRewindDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePauseDirect(ALCcontext *context, ALuint source) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceStopvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceRewindvDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePausevDirect(ALCcontext *context, ALsizei n, const ALuint *sources) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceQueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, const ALuint *buffers) AL_API_NOEXCEPT; +void AL_APIENTRY alSourceUnqueueBuffersDirect(ALCcontext *context, ALuint source, ALsizei nb, ALuint *buffers) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenBuffersDirect(ALCcontext *context, ALsizei n, ALuint *buffers) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteBuffersDirect(ALCcontext *context, ALsizei n, const ALuint *buffers) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsBufferDirect(ALCcontext *context, ALuint buffer) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferDataDirect(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei size, ALsizei samplerate) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value) AL_API_NOEXCEPT; +void AL_APIENTRY alBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value) AL_API_NOEXCEPT; +void AL_APIENTRY alBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint value1, ALint value2, ALint value3) AL_API_NOEXCEPT; +void AL_APIENTRY alBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, const ALint *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferfDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3fDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferfvDirect(ALCcontext *context, ALuint buffer, ALenum param, ALfloat *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferiDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3iDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *value1, ALint *value2, ALint *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferivDirect(ALCcontext *context, ALuint buffer, ALenum param, ALint *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenEffectsDirect(ALCcontext *context, ALsizei n, ALuint *effects) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteEffectsDirect(ALCcontext *context, ALsizei n, const ALuint *effects) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsEffectDirect(ALCcontext *context, ALuint effect) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectiDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectivDirect(ALCcontext *context, ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectfDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetEffectfvDirect(ALCcontext *context, ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenFiltersDirect(ALCcontext *context, ALsizei n, ALuint *filters) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteFiltersDirect(ALCcontext *context, ALsizei n, const ALuint *filters) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsFilterDirect(ALCcontext *context, ALuint filter) AL_API_NOEXCEPT; +void AL_APIENTRY alFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilteriDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterivDirect(ALCcontext *context, ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterfDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetFilterfvDirect(ALCcontext *context, ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alGenAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT; +void AL_APIENTRY alDeleteAuxiliaryEffectSlotsDirect(ALCcontext *context, ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY alIsAuxiliaryEffectSlotDirect(ALCcontext *context, ALuint effectslot) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +void AL_APIENTRY alAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotiDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotivDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotfDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +void AL_APIENTRY alGetAuxiliaryEffectSlotfvDirect(ALCcontext *context, ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferDataStaticDirect(ALCcontext *context, ALuint buffer, ALenum format, ALvoid *data, ALsizei size, ALsizei freq) AL_API_NOEXCEPT; + +void AL_APIENTRY alDebugMessageCallbackDirectEXT(ALCcontext *context, ALDEBUGPROCEXT callback, void *userParam) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageInsertDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALuint id, ALenum severity, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alDebugMessageControlDirectEXT(ALCcontext *context, ALenum source, ALenum type, ALenum severity, ALsizei count, const ALuint *ids, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alPushDebugGroupDirectEXT(ALCcontext *context, ALenum source, ALuint id, ALsizei length, const ALchar *message) AL_API_NOEXCEPT; +void AL_APIENTRY alPopDebugGroupDirectEXT(ALCcontext *context) AL_API_NOEXCEPT; +ALuint AL_APIENTRY alGetDebugMessageLogDirectEXT(ALCcontext *context, ALuint count, ALsizei logBufSize, ALenum *sources, ALenum *types, ALuint *ids, ALenum *severities, ALsizei *lengths, ALchar *logBuf) AL_API_NOEXCEPT; +void AL_APIENTRY alObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei length, const ALchar *label) AL_API_NOEXCEPT; +void AL_APIENTRY alGetObjectLabelDirectEXT(ALCcontext *context, ALenum identifier, ALuint name, ALsizei bufSize, ALsizei *length, ALchar *label) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerDirectEXT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervDirectEXT(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT; + +void AL_APIENTRY alRequestFoldbackStartDirect(ALCcontext *context, ALenum mode, ALsizei count, ALsizei length, ALfloat *mem, LPALFOLDBACKCALLBACK callback) AL_API_NOEXCEPT; +void AL_APIENTRY alRequestFoldbackStopDirect(ALCcontext *context) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferSubDataDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, const ALvoid *data, ALsizei offset, ALsizei length) AL_API_NOEXCEPT; + +void AL_APIENTRY alSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble value1, ALdouble value2, ALdouble value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALdouble *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcedDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3dDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *value1, ALdouble *value2, ALdouble *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcedvDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALdouble *values) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value) AL_API_NOEXCEPT; +void AL_APIENTRY alSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT value1, ALint64SOFT value2, ALint64SOFT value3) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, const ALint64SOFT *values) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcei64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSource3i64DirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *value1, ALint64SOFT *value2, ALint64SOFT *value3) AL_API_NOEXCEPT; +void AL_APIENTRY alGetSourcei64vDirectSOFT(ALCcontext *context, ALuint source, ALenum param, ALint64SOFT *values) AL_API_NOEXCEPT; + +void AL_APIENTRY alDeferUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; +void AL_APIENTRY alProcessUpdatesDirectSOFT(ALCcontext *context) AL_API_NOEXCEPT; + +const ALchar* AL_APIENTRY alGetStringiDirectSOFT(ALCcontext *context, ALenum pname, ALsizei index) AL_API_NOEXCEPT; + +void AL_APIENTRY alEventControlDirectSOFT(ALCcontext *context, ALsizei count, const ALenum *types, ALboolean enable) AL_API_NOEXCEPT; +void AL_APIENTRY alEventCallbackDirectSOFT(ALCcontext *context, ALEVENTPROCSOFT callback, void *userParam) AL_API_NOEXCEPT; +void* AL_APIENTRY alGetPointerDirectSOFT(ALCcontext *context, ALenum pname) AL_API_NOEXCEPT; +void AL_APIENTRY alGetPointervDirectSOFT(ALCcontext *context, ALenum pname, void **values) AL_API_NOEXCEPT; + +void AL_APIENTRY alBufferCallbackDirectSOFT(ALCcontext *context, ALuint buffer, ALenum format, ALsizei freq, ALBUFFERCALLBACKTYPESOFT callback, ALvoid *userptr) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferPtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBuffer3PtrDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr0, ALvoid **ptr1, ALvoid **ptr2) AL_API_NOEXCEPT; +void AL_APIENTRY alGetBufferPtrvDirectSOFT(ALCcontext *context, ALuint buffer, ALenum param, ALvoid **ptr) AL_API_NOEXCEPT; + +void AL_APIENTRY alSourcePlayAtTimeDirectSOFT(ALCcontext *context, ALuint source, ALint64SOFT start_time) AL_API_NOEXCEPT; +void AL_APIENTRY alSourcePlayAtTimevDirectSOFT(ALCcontext *context, ALsizei n, const ALuint *sources, ALint64SOFT start_time) AL_API_NOEXCEPT; + +ALenum AL_APIENTRY EAXSetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT; +ALenum AL_APIENTRY EAXGetDirect(ALCcontext *context, const struct _GUID *property_set_id, ALuint property_id, ALuint source_id, ALvoid *value, ALuint value_size) AL_API_NOEXCEPT; +ALboolean AL_APIENTRY EAXSetBufferModeDirect(ALCcontext *context, ALsizei n, const ALuint *buffers, ALint value) AL_API_NOEXCEPT; +ALenum AL_APIENTRY EAXGetBufferModeDirect(ALCcontext *context, ALuint buffer, ALint *pReserved) AL_API_NOEXCEPT; +#endif +#endif + +#ifndef AL_SOFT_bformat_hoa +#define AL_SOFT_bformat_hoa +#define AL_UNPACK_AMBISONIC_ORDER_SOFT 0x199D +#endif + #ifdef __cplusplus } #endif +/* NOLINTEND */ #endif diff --git a/Source/ThirdParty/OpenAL/efx-presets.h b/Source/ThirdParty/OpenAL/efx-presets.h index 8539fd517..acd5bf398 100644 --- a/Source/ThirdParty/OpenAL/efx-presets.h +++ b/Source/ThirdParty/OpenAL/efx-presets.h @@ -2,6 +2,7 @@ #ifndef EFX_PRESETS_H #define EFX_PRESETS_H +/* NOLINTBEGIN */ #ifndef EFXEAXREVERBPROPERTIES_DEFINED #define EFXEAXREVERBPROPERTIES_DEFINED @@ -399,4 +400,5 @@ typedef struct { #define EFX_REVERB_PRESET_SMALLWATERROOM \ { 1.0000f, 0.7000f, 0.3162f, 0.4477f, 1.0000f, 1.5100f, 1.2500f, 1.1400f, 0.8913f, 0.0200f, { 0.0000f, 0.0000f, 0.0000f }, 1.4125f, 0.0300f, { 0.0000f, 0.0000f, 0.0000f }, 0.1790f, 0.1500f, 0.8950f, 0.1900f, 0.9920f, 5000.0000f, 250.0000f, 0.0000f, 0x0 } +/* NOLINTEND */ #endif /* EFX_PRESETS_H */ diff --git a/Source/ThirdParty/OpenAL/efx.h b/Source/ThirdParty/OpenAL/efx.h index 5ab64a64d..1e93bf222 100644 --- a/Source/ThirdParty/OpenAL/efx.h +++ b/Source/ThirdParty/OpenAL/efx.h @@ -1,6 +1,7 @@ #ifndef AL_EFX_H #define AL_EFX_H +/* NOLINTBEGIN */ #include #include "alc.h" @@ -204,80 +205,80 @@ extern "C" { /* Effect object function types. */ -typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint); -typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGENEFFECTS)(ALsizei, ALuint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEEFFECTS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISEFFECT)(ALuint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALEFFECTFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETEFFECTFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; /* Filter object function types. */ -typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint); -typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGENFILTERS)(ALsizei, ALuint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEFILTERS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISFILTER)(ALuint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALFILTERFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETFILTERFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; /* Auxiliary Effect Slot object function types. */ -typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*); -typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*); -typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat); -typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*); -typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*); +typedef void (AL_APIENTRY *LPALGENAUXILIARYEFFECTSLOTS)(ALsizei, ALuint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALDELETEAUXILIARYEFFECTSLOTS)(ALsizei, const ALuint*) AL_API_NOEXCEPT17; +typedef ALboolean (AL_APIENTRY *LPALISAUXILIARYEFFECTSLOT)(ALuint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, const ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, const ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTI)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTIV)(ALuint, ALenum, ALint*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTF)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; +typedef void (AL_APIENTRY *LPALGETAUXILIARYEFFECTSLOTFV)(ALuint, ALenum, ALfloat*) AL_API_NOEXCEPT17; #ifdef AL_ALEXT_PROTOTYPES -AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects); -AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects); -AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect); -AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue); -AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues); -AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue); -AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues); -AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue); -AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues); -AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue); -AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues); +AL_API void AL_APIENTRY alGenEffects(ALsizei n, ALuint *effects) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDeleteEffects(ALsizei n, const ALuint *effects) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsEffect(ALuint effect) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffecti(ALuint effect, ALenum param, ALint iValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffectiv(ALuint effect, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffectf(ALuint effect, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alEffectfv(ALuint effect, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffecti(ALuint effect, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffectiv(ALuint effect, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffectf(ALuint effect, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetEffectfv(ALuint effect, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; -AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters); -AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters); -AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter); -AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue); -AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues); -AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue); -AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues); -AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue); -AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues); -AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue); -AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues); +AL_API void AL_APIENTRY alGenFilters(ALsizei n, ALuint *filters) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDeleteFilters(ALsizei n, const ALuint *filters) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsFilter(ALuint filter) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilteri(ALuint filter, ALenum param, ALint iValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilteriv(ALuint filter, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilterf(ALuint filter, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alFilterfv(ALuint filter, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilteri(ALuint filter, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilteriv(ALuint filter, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilterf(ALuint filter, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetFilterfv(ALuint filter, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; -AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots); -AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots); -AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot); -AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue); -AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue); -AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues); +AL_API void AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alDeleteAuxiliaryEffectSlots(ALsizei n, const ALuint *effectslots) AL_API_NOEXCEPT; +AL_API ALboolean AL_APIENTRY alIsAuxiliaryEffectSlot(ALuint effectslot) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint iValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, const ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat flValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, const ALfloat *pflValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSloti(ALuint effectslot, ALenum param, ALint *piValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotiv(ALuint effectslot, ALenum param, ALint *piValues) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotf(ALuint effectslot, ALenum param, ALfloat *pflValue) AL_API_NOEXCEPT; +AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum param, ALfloat *pflValues) AL_API_NOEXCEPT; #endif /* Filter ranges and defaults. */ @@ -758,5 +759,6 @@ AL_API void AL_APIENTRY alGetAuxiliaryEffectSlotfv(ALuint effectslot, ALenum par #ifdef __cplusplus } /* extern "C" */ #endif +/* NOLINTEND */ #endif /* AL_EFX_H */ diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs index f13449192..aa4584135 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs @@ -49,7 +49,9 @@ namespace Flax.Deps.Dependencies public override void Build(BuildOptions options) { var root = options.IntermediateFolder; - var version = "1.23.1"; + var version = "1.24.3"; + string configuration = "Release"; + int concurrency = Math.Min(Math.Max(1, (int)(Environment.ProcessorCount * Configuration.ConcurrencyProcessorScale)), Configuration.MaxConcurrency); var dstIncludePath = Path.Combine(options.ThirdPartyFolder, "OpenAL"); foreach (var platform in options.Platforms) @@ -65,11 +67,9 @@ namespace Flax.Deps.Dependencies "OpenAL32.dll", }; - string configuration = "Release"; - // Get the source CloneGitRepo(root, "https://github.com/kcat/openal-soft.git"); - GitCheckout(root, "master", "d3875f333fb6abe2f39d82caca329414871ae53b"); // 1.23.1 + GitCheckout(root, "master", "dc7d7054a5b4f3bec1dc23a42fd616a0847af948"); // 1.24.3 // Build for Win64 and ARM64 foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) @@ -121,8 +121,9 @@ namespace Flax.Deps.Dependencies }; var envVars = new Dictionary { - { "CC", "clang-7" }, - { "CC_FOR_BUILD", "clang-7" } + { "CC", "clang" }, + { "CC_FOR_BUILD", "clang" }, + { "CMAKE_BUILD_PARALLEL_LEVEL", concurrency.ToString() }, }; var config = "-DALSOFT_REQUIRE_ALSA=ON -DALSOFT_REQUIRE_OSS=ON -DALSOFT_REQUIRE_PORTAUDIO=ON -DALSOFT_REQUIRE_PULSEAUDIO=ON -DALSOFT_REQUIRE_JACK=ON -DALSOFT_EMBED_HRTF_DATA=YES"; @@ -138,8 +139,8 @@ namespace Flax.Deps.Dependencies SetupDirectory(buildDir, true); // Build for Linux - Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DLIBTYPE=STATIC " + config + " ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); + Utilities.Run("cmake", $"-G \"Unix Makefiles\" -DCMAKE_BUILD_TYPE={configuration} -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DLIBTYPE=STATIC {config} ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); + BuildCmake(buildDir, configuration, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); From 36c1909111cf197a77289e2e393a318de08e13c0 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 5 Apr 2025 13:48:16 +0300 Subject: [PATCH 005/207] Build OpenAL with PipeWire backend enabled --- Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs index aa4584135..3bc9611e8 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs @@ -125,7 +125,13 @@ namespace Flax.Deps.Dependencies { "CC_FOR_BUILD", "clang" }, { "CMAKE_BUILD_PARALLEL_LEVEL", concurrency.ToString() }, }; - var config = "-DALSOFT_REQUIRE_ALSA=ON -DALSOFT_REQUIRE_OSS=ON -DALSOFT_REQUIRE_PORTAUDIO=ON -DALSOFT_REQUIRE_PULSEAUDIO=ON -DALSOFT_REQUIRE_JACK=ON -DALSOFT_EMBED_HRTF_DATA=YES"; + var config = $"-DALSOFT_REQUIRE_ALSA=ON " + + $"-DALSOFT_REQUIRE_OSS=ON " + + $"-DALSOFT_REQUIRE_PORTAUDIO=ON " + + $"-DALSOFT_REQUIRE_PULSEAUDIO=ON " + + $"-DALSOFT_REQUIRE_JACK=ON " + + $"-DALSOFT_REQUIRE_PIPEWIRE=ON " + + $"-DALSOFT_EMBED_HRTF_DATA=YES "; // Get the source var packagePath = Path.Combine(root, "package.zip"); From fc96b248cb169ebd012fa37eb84887b767913054 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 5 Apr 2025 13:51:23 +0300 Subject: [PATCH 006/207] Ignore invalid VelocityChanged velocity values in audio backend --- Source/Engine/Audio/AudioBackend.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Audio/AudioBackend.h b/Source/Engine/Audio/AudioBackend.h index ee94e21a3..b7ab87759 100644 --- a/Source/Engine/Audio/AudioBackend.h +++ b/Source/Engine/Audio/AudioBackend.h @@ -4,6 +4,8 @@ #include "Config.h" #include "Types.h" + +#include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Types/BaseTypes.h" /// @@ -83,7 +85,8 @@ public: FORCE_INLINE static void VelocityChanged(const Vector3& velocity) { - Instance->Listener_VelocityChanged(velocity); + if (!velocity.IsNanOrInfinity()) + Instance->Listener_VelocityChanged(velocity); } FORCE_INLINE static void TransformChanged(const Vector3& position, const Quaternion& orientation) From c4c7ee941f2964860a2f93ad159a556dcb713e1a Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 3 Oct 2025 14:46:43 +0200 Subject: [PATCH 007/207] add industry standard shortcut for editor settings --- Source/Editor/Modules/UIModule.cs | 2 +- Source/Editor/Options/InputOptions.cs | 4 ++++ Source/Editor/Windows/EditorWindow.cs | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index fe89026c5..0e482441e 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -669,7 +669,7 @@ namespace FlaxEditor.Modules if (item != null) Editor.ContentEditing.Open(item); }); - cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show()); + cm.AddButton("Editor Options", inputOptions.EditorOptionsWindow, () => Editor.Windows.EditorOptionsWin.Show()); // Scene MenuScene = MainMenu.AddButton("Scene"); diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index af919c1f3..ab473ebed 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -650,6 +650,10 @@ namespace FlaxEditor.Options [EditorDisplay("Windows"), EditorOrder(4020)] public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None); + [DefaultValue(typeof(InputBinding), "Control+Comma")] + [EditorDisplay("Windows"), EditorOrder(4030)] + public InputBinding EditorOptionsWindow = new InputBinding(KeyboardKeys.Comma, KeyboardKeys.Control); + #endregion #region Node Editors diff --git a/Source/Editor/Windows/EditorWindow.cs b/Source/Editor/Windows/EditorWindow.cs index 6d01432ba..03d435efe 100644 --- a/Source/Editor/Windows/EditorWindow.cs +++ b/Source/Editor/Windows/EditorWindow.cs @@ -116,6 +116,11 @@ namespace FlaxEditor.Windows if (InputOptions.WindowShortcutsAvaliable) Editor.Windows.VisualScriptDebuggerWin.FocusOrShow(); }); + InputActions.Add(options => options.EditorOptionsWindow, () => + { + if (InputOptions.WindowShortcutsAvaliable) + Editor.Windows.EditorOptionsWin.FocusOrShow(); + }); // Register Editor.Windows.OnWindowAdd(this); From 7544500be103d4c7ec2d42d726167bcc5af7f7be Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 12 Oct 2025 16:07:20 +0200 Subject: [PATCH 008/207] polish attribute editor - Add hint about what to do when there are no attributes - Swap "Ok" "Cancel" buttons order to match other dialog boxes - Move buttons to the bottom - Increase size of menu - Add some missing "." to add/ remove button tooltips in array editor --- .../CustomEditors/Editors/CollectionEditor.cs | 4 +- Source/Editor/Surface/AttributesEditor.cs | 63 ++++++++++--------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index b977dab63..598b0208e 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -650,7 +650,7 @@ namespace FlaxEditor.CustomEditors.Editors panel.Panel.Size = new Float2(0, 18); panel.Panel.Margin = new Margin(0, 0, Utilities.Constants.UIMargin, 0); - var removeButton = panel.Button("-", "Remove the last item"); + var removeButton = panel.Button("-", "Remove the last item."); removeButton.Button.Size = new Float2(16, 16); removeButton.Button.Enabled = size > _minCount; removeButton.Button.AnchorPreset = AnchorPresets.TopRight; @@ -661,7 +661,7 @@ namespace FlaxEditor.CustomEditors.Editors Resize(Count - 1); }; - var addButton = panel.Button("+", "Add a new item"); + var addButton = panel.Button("+", "Add a new item."); addButton.Button.Size = new Float2(16, 16); addButton.Button.Enabled = (!NotNullItems || size > 0) && size < _maxCount; addButton.Button.AnchorPreset = AnchorPresets.TopRight; diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index 99bf8bd1a..2f1247da9 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -2,11 +2,8 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Loader; -using System.Runtime.Serialization.Formatters.Binary; using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors.Editors; using FlaxEditor.GUI.ContextMenu; @@ -18,6 +15,7 @@ namespace FlaxEditor.Surface class AttributesEditor : ContextMenuBase { private CustomEditorPresenter _presenter; + private Proxy _proxy; private byte[] _oldData; private class Proxy @@ -72,11 +70,11 @@ namespace FlaxEditor.Surface /// Initializes a new instance of the class. /// /// The attributes list to edit. - /// The allowed attribute types to use. - public AttributesEditor(Attribute[] attributes, IList attributeType) + /// The allowed attribute types to use. + public AttributesEditor(Attribute[] attributes, IList attributeTypes) { // Context menu dimensions - const float width = 340.0f; + const float width = 375.0f; const float height = 370.0f; Size = new Float2(width, height); @@ -91,58 +89,65 @@ namespace FlaxEditor.Surface // Buttons float buttonsWidth = (width - 16.0f) * 0.5f; float buttonsHeight = 20.0f; - var cancelButton = new Button(4.0f, title.Bottom + 4.0f, buttonsWidth, buttonsHeight) + var okButton = new Button(4.0f, Bottom - 4.0f - buttonsHeight, buttonsWidth, buttonsHeight) + { + Text = "Ok", + Parent = this + }; + okButton.Clicked += OnOkButtonClicked; + var cancelButton = new Button(okButton.Right + 4.0f, okButton.Y, buttonsWidth, buttonsHeight) { Text = "Cancel", Parent = this }; cancelButton.Clicked += Hide; - var okButton = new Button(cancelButton.Right + 4.0f, cancelButton.Y, buttonsWidth, buttonsHeight) - { - Text = "OK", - Parent = this - }; - okButton.Clicked += OnOkButtonClicked; - // Actual panel + // Actual panel used to display attributes var panel1 = new Panel(ScrollBars.Vertical) { - Bounds = new Rectangle(0, okButton.Bottom + 4.0f, width, height - okButton.Bottom - 2.0f), + Bounds = new Rectangle(0, title.Bottom + 4.0f, width, height - buttonsHeight - title.Height - 14.0f), Parent = this }; var editor = new CustomEditorPresenter(null); editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop; editor.Panel.IsScrollable = true; editor.Panel.Parent = panel1; - editor.Panel.Tag = attributeType; + editor.Panel.Tag = attributeTypes; _presenter = editor; // Cache 'previous' state to check if attributes were edited after operation _oldData = SurfaceMeta.GetAttributesData(attributes); - editor.Select(new Proxy + _proxy = new Proxy { Value = attributes, - }); + }; + editor.Select(_proxy); + + _presenter.Modified += OnPresenterModified; + OnPresenterModified(); + } + + private void OnPresenterModified() + { + if (_proxy.Value.Length == 0) + { + var label = _presenter.Label("No attributes.\nPress the \"+\" button to add a new one and then select an attribute type using the \"Type\" dropdown.", TextAlignment.Center); + label.Label.Wrapping = TextWrapping.WrapWords; + label.Control.Height = 35f; + label.Label.Margin = new Margin(10f); + label.Label.TextColor = label.Label.TextColorHighlighted = Style.Current.ForegroundGrey; + } } private void OnOkButtonClicked() { var newValue = ((Proxy)_presenter.Selection[0]).Value; - for (int i = 0; i < newValue.Length; i++) - { - if (newValue[i] == null) - { - MessageBox.Show("One of the attributes is null. Please set it to the valid object.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); - return; - } - } + newValue = newValue.Where(v => v != null).ToArray(); var newData = SurfaceMeta.GetAttributesData(newValue); if (!_oldData.SequenceEqual(newData)) - { Edited?.Invoke(newValue); - } Hide(); } @@ -183,7 +188,9 @@ namespace FlaxEditor.Surface { _presenter = null; _oldData = null; + _proxy = null; Edited = null; + _presenter.Modified -= OnPresenterModified; base.OnDestroy(); } From 59fb83a4694b9cab9978c4e9d8bc4222e6f40112 Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 12 Oct 2025 16:23:54 +0200 Subject: [PATCH 009/207] correctly center buttons --- Source/Editor/Surface/AttributesEditor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/AttributesEditor.cs b/Source/Editor/Surface/AttributesEditor.cs index 2f1247da9..29d5860c3 100644 --- a/Source/Editor/Surface/AttributesEditor.cs +++ b/Source/Editor/Surface/AttributesEditor.cs @@ -86,8 +86,8 @@ namespace FlaxEditor.Surface Parent = this }; - // Buttons - float buttonsWidth = (width - 16.0f) * 0.5f; + // Ok and Cancel Buttons + float buttonsWidth = (width - 12.0f) * 0.5f; float buttonsHeight = 20.0f; var okButton = new Button(4.0f, Bottom - 4.0f - buttonsHeight, buttonsWidth, buttonsHeight) { From be7871c29206c0df5cdec0c9d382aa54aacc1d70 Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 12 Oct 2025 16:50:31 +0200 Subject: [PATCH 010/207] use Render2D based solution rather than shader for alpha grid https://github.com/FlaxEngine/FlaxEngine/pull/3281#issuecomment-3218049398 --- Source/Editor/EditorAssets.cs | 5 ----- Source/Editor/GUI/Input/ColorValueBox.cs | 28 ++++++++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs index d30ca91c0..bdfc7dfa6 100644 --- a/Source/Editor/EditorAssets.cs +++ b/Source/Editor/EditorAssets.cs @@ -134,11 +134,6 @@ namespace FlaxEditor /// public static string FlaxIconBlueTexture = "Engine/Textures/FlaxIconBlue"; - /// - /// The checkboard material used as a background for displaying transparent colors. - /// - public static string ColorAlphaBackgroundGrid = "Editor/AlphaBackgroundGrid"; - /// /// The icon lists used by editor from the SegMDL2 font. /// diff --git a/Source/Editor/GUI/Input/ColorValueBox.cs b/Source/Editor/GUI/Input/ColorValueBox.cs index 1893c4124..d4bc03fd9 100644 --- a/Source/Editor/GUI/Input/ColorValueBox.cs +++ b/Source/Editor/GUI/Input/ColorValueBox.cs @@ -14,12 +14,7 @@ namespace FlaxEditor.GUI.Input [HideInEditor] public class ColorValueBox : Control { - private const String ScaleParamName = "Scale"; - // 4.8 is a magic number that makes the grid fit perfect in the color value box - private const float GridScale = 4.8f; - private bool _isMouseDown; - private MaterialBase checkerMaterial; /// /// Delegate function used for the color picker events handling. @@ -106,9 +101,6 @@ namespace FlaxEditor.GUI.Input public ColorValueBox() : base(0, 0, 32, 18) { - checkerMaterial = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.ColorAlphaBackgroundGrid); - checkerMaterial = checkerMaterial.CreateVirtualInstance(); - checkerMaterial.SetParameterValue(ScaleParamName, GridScale); } /// @@ -146,7 +138,25 @@ namespace FlaxEditor.GUI.Input if (isTransparent) { var alphaRect = new Rectangle(colorRect.Right, 0, Width - colorRect.Right, Height); - Render2D.DrawMaterial(checkerMaterial, alphaRect); + + // Draw checkerboard pattern to part of the color value box + Render2D.FillRectangle(alphaRect, Color.White); + var smallRectSize = 7.9f; + var numHor = Mathf.CeilToInt(alphaRect.Width / smallRectSize); + var numVer = Mathf.CeilToInt(alphaRect.Height / smallRectSize); + for (int i = 0; i < numHor; i++) + { + for (int j = 0; j < numVer; j++) + { + if ((i + j) % 2 == 0) + { + var rect = new Rectangle(alphaRect.X + smallRectSize * i, alphaRect.Y + smallRectSize * j, new Float2(smallRectSize)); + Render2D.PushClip(alphaRect); + Render2D.FillRectangle(rect, Color.Gray); + Render2D.PopClip(); + } + } + } Render2D.FillRectangle(alphaRect, _value); } From 5a9eedfadb64b16fc050c0dbe55c52f507ffae07 Mon Sep 17 00:00:00 2001 From: Saas Date: Sun, 12 Oct 2025 20:14:43 +0200 Subject: [PATCH 011/207] make old/ new editor dialog boxes nicer to read --- Source/Editor/Editor.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index 04c6eee71..a8ef8f4e9 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -266,8 +266,8 @@ bool Editor::CheckProjectUpgrade() // Check if last version was older else if (lastVersion.Major < FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor < FLAXENGINE_VERSION_MINOR)) { - LOG(Warning, "The project was opened with the older editor version last time"); - const auto result = MessageBox::Show(TEXT("The project was opened with the older editor version last time. Loading it may modify existing data so older editor version won't open it. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question); + LOG(Warning, "The project was last opened with an older editor version"); + const auto result = MessageBox::Show(TEXT("The project was last opened with an older editor version.\nLoading it may modify existing data, which can result in older editor versions being unable to open it.\n\nDo you want to perform a backup before or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question); if (result == DialogResult::Yes) { if (BackupProject()) @@ -289,8 +289,8 @@ bool Editor::CheckProjectUpgrade() // Check if last version was newer else if (lastVersion.Major > FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor > FLAXENGINE_VERSION_MINOR)) { - LOG(Warning, "The project was opened with the newer editor version last time"); - const auto result = MessageBox::Show(TEXT("The project was opened with the newer editor version last time. Loading it may fail and corrupt existing data. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning); + LOG(Warning, "The project was last opened with a newer editor version"); + const auto result = MessageBox::Show(TEXT("The project was last opened with a newer editor version.\nLoading it may fail and corrupt existing data.\n\nDo you want to perform a backup before loading or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning); if (result == DialogResult::Yes) { if (BackupProject()) From d7a528cdbc65e9c47d6e09595322b962e67bf761 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 12 Oct 2025 22:38:11 +0200 Subject: [PATCH 012/207] Update assets --- Content/Shaders/Reflections.flax | 4 ++-- Content/Shaders/SSR.flax | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Content/Shaders/Reflections.flax b/Content/Shaders/Reflections.flax index a4605e048..d5ac73593 100644 --- a/Content/Shaders/Reflections.flax +++ b/Content/Shaders/Reflections.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5c99a81c70ccb7e9b2e1e4bc5f271c2fe7b36630489fbb269adfaa34de2cc80 -size 3183 +oid sha256:28c80074ac502d5a931b91089fea7bc0c9d573e0bdd317623577833ff328aa8a +size 3107 diff --git a/Content/Shaders/SSR.flax b/Content/Shaders/SSR.flax index 79085c297..1d5932820 100644 --- a/Content/Shaders/SSR.flax +++ b/Content/Shaders/SSR.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9602d25ef9300636aa23112b3ba3a4f45bf1c4c99b1484a86074da5a787abfc -size 11085 +oid sha256:e1a51391e285346f0a67a75ab03b8262cae02d02dddf0d625a6814a51a3b7394 +size 11007 From 7dfc37f65276eae7f9889c454055b59db3b8cd79 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 12 Oct 2025 23:24:34 +0200 Subject: [PATCH 013/207] Adjustments for #3338 --- Source/Engine/Audio/AudioBackend.h | 5 +-- Source/Engine/Audio/AudioListener.cpp | 4 +-- .../Flax.Build/Deps/Dependencies/NvCloth.cs | 2 +- .../Flax.Build/Deps/Dependencies/OpenAL.cs | 35 ++++++++++++------- Source/Tools/Flax.Build/Deps/Dependency.cs | 15 ++++++++ Source/Tools/Flax.Build/Deps/Downloader.cs | 18 ++++++++-- 6 files changed, 58 insertions(+), 21 deletions(-) diff --git a/Source/Engine/Audio/AudioBackend.h b/Source/Engine/Audio/AudioBackend.h index d0cf42fb1..7d3d087a7 100644 --- a/Source/Engine/Audio/AudioBackend.h +++ b/Source/Engine/Audio/AudioBackend.h @@ -4,8 +4,6 @@ #include "Config.h" #include "Types.h" - -#include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Types/BaseTypes.h" /// @@ -85,8 +83,7 @@ public: FORCE_INLINE static void VelocityChanged(const Vector3& velocity) { - if (!velocity.IsNanOrInfinity()) - Instance->Listener_VelocityChanged(velocity); + Instance->Listener_VelocityChanged(velocity); } FORCE_INLINE static void TransformChanged(const Vector3& position, const Quaternion& orientation) diff --git a/Source/Engine/Audio/AudioListener.cpp b/Source/Engine/Audio/AudioListener.cpp index 1921bd373..9c5898b4a 100644 --- a/Source/Engine/Audio/AudioListener.cpp +++ b/Source/Engine/Audio/AudioListener.cpp @@ -23,11 +23,11 @@ void AudioListener::Update() { // Update the velocity const Vector3 pos = GetPosition(); - const float dt = Time::Update.UnscaledDeltaTime.GetTotalSeconds(); + const float dt = Math::Max(Time::Update.UnscaledDeltaTime.GetTotalSeconds(), 0.00001f); const auto prevVelocity = _velocity; _velocity = (pos - _prevPos) / dt; _prevPos = pos; - if (_velocity != prevVelocity) + if (_velocity != prevVelocity && !_velocity.IsNanOrInfinity()) { AudioBackend::Listener::VelocityChanged(_velocity); } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs index 545ef7169..699e40f72 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs @@ -195,7 +195,7 @@ namespace Flax.Deps.Dependencies RunCmake(cmakeFolder, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + cmakeArgs, envVars); // Run build - BuildCmake(cmakeFolder, envVars: envVars); + BuildCmake(cmakeFolder, envVars); // Deploy binaries var libs = new[] diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs index 0f1140583..447b91498 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs @@ -50,8 +50,7 @@ namespace Flax.Deps.Dependencies { var root = options.IntermediateFolder; var version = "1.24.3"; - string configuration = "Release"; - int concurrency = Math.Min(Math.Max(1, (int)(Environment.ProcessorCount * Configuration.ConcurrencyProcessorScale)), Configuration.MaxConcurrency); + var configuration = "Release"; var dstIncludePath = Path.Combine(options.ThirdPartyFolder, "OpenAL"); foreach (var platform in options.Platforms) @@ -123,7 +122,7 @@ namespace Flax.Deps.Dependencies { { "CC", "clang-" + Configuration.LinuxClangMinVer }, { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, - { "CMAKE_BUILD_PARALLEL_LEVEL", concurrency.ToString() }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; var config = $"-DALSOFT_REQUIRE_ALSA=ON " + $"-DALSOFT_REQUIRE_OSS=ON " + @@ -158,7 +157,11 @@ namespace Flax.Deps.Dependencies { "libopenal.a", }; - var config = "-DALSOFT_REQUIRE_OBOE=OFF -DALSOFT_REQUIRE_OPENSL=ON -DALSOFT_EMBED_HRTF_DATA=YES"; + var envVars = new Dictionary + { + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, + }; + var config = " -DALSOFT_REQUIRE_OBOE=OFF -DALSOFT_REQUIRE_OPENSL=ON -DALSOFT_EMBED_HRTF_DATA=YES"; // Get the source var packagePath = Path.Combine(root, "package.zip"); @@ -181,8 +184,8 @@ namespace Flax.Deps.Dependencies SetupDirectory(buildDir, true); // Build - RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - BuildCmake(buildDir); + RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=" + configuration + config, envVars); + BuildCmake(buildDir, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -194,7 +197,11 @@ namespace Flax.Deps.Dependencies { "libopenal.a", }; - var config = "-DALSOFT_REQUIRE_COREAUDIO=ON -DALSOFT_EMBED_HRTF_DATA=YES"; + var envVars = new Dictionary + { + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, + }; + var config = " -DALSOFT_REQUIRE_COREAUDIO=ON -DALSOFT_EMBED_HRTF_DATA=YES"; // Get the source var packagePath = Path.Combine(root, "package.zip"); @@ -210,8 +217,8 @@ namespace Flax.Deps.Dependencies foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { SetupDirectory(buildDir, true); - RunCmake(buildDir, platform, architecture, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - BuildCmake(buildDir); + RunCmake(buildDir, platform, architecture, ".. -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=" + configuration + config, envVars); + BuildCmake(buildDir, envVars); var depsFolder = GetThirdPartyFolder(options, platform, architecture); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); @@ -224,7 +231,11 @@ namespace Flax.Deps.Dependencies { "libopenal.a", }; - var config = "-DALSOFT_REQUIRE_COREAUDIO=ON -DALSOFT_EMBED_HRTF_DATA=YES"; + var envVars = new Dictionary + { + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, + }; + var config = " -DALSOFT_REQUIRE_COREAUDIO=ON -DALSOFT_EMBED_HRTF_DATA=YES"; // Get the source var packagePath = Path.Combine(root, "package.zip"); @@ -240,8 +251,8 @@ namespace Flax.Deps.Dependencies // Build for iOS SetupDirectory(buildDir, true); - RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_SYSTEM_NAME=iOS -DALSOFT_OSX_FRAMEWORK=ON -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=Release " + config); - BuildCmake(buildDir); + RunCmake(buildDir, platform, TargetArchitecture.ARM64, ".. -DCMAKE_SYSTEM_NAME=iOS -DALSOFT_OSX_FRAMEWORK=ON -DLIBTYPE=STATIC -DCMAKE_BUILD_TYPE=" + configuration + config, envVars); + BuildCmake(buildDir, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.ARM64); foreach (var file in binariesToCopy) Utilities.FileCopy(Path.Combine(buildDir, file), Path.Combine(depsFolder, file)); diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index 79d39f2a6..a79a968a3 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -248,6 +248,21 @@ namespace Flax.Deps Utilities.Run("git", "reset --hard", null, path, Utilities.RunOptions.DefaultTool); } + /// + /// Gets the maximum concurrency level for a cmake command. See CMAKE_BUILD_PARALLEL_LEVEL or -j docs. + /// + public static string CmakeBuildParallel => Math.Min(Math.Max(1, (int)(Environment.ProcessorCount * Configuration.ConcurrencyProcessorScale)), Configuration.MaxConcurrency).ToString(); + + /// + /// Builds the cmake project. + /// + /// The path. + /// Custom environment variables to pass to the child process. + public static void BuildCmake(string path, Dictionary envVars) + { + BuildCmake(path, "Release", envVars); + } + /// /// Builds the cmake project. /// diff --git a/Source/Tools/Flax.Build/Deps/Downloader.cs b/Source/Tools/Flax.Build/Deps/Downloader.cs index 4b8c77da0..57da4a2ee 100644 --- a/Source/Tools/Flax.Build/Deps/Downloader.cs +++ b/Source/Tools/Flax.Build/Deps/Downloader.cs @@ -14,6 +14,7 @@ namespace Flax.Deps /// static class Downloader { + private static bool IgnoreSSL = false; private const string GoogleDriveDomain = "drive.google.com"; private const string GoogleDriveDomain2 = "https://drive.google.com"; @@ -54,7 +55,7 @@ namespace Flax.Deps { if (httpClient == null) { - using (httpClient = new HttpClient()) + using (httpClient = GetHttpClient()) { return DownloadFileFromUrlToPathRaw(url, path, httpClient); } @@ -130,7 +131,7 @@ namespace Flax.Deps // You can comment the statement below if the provided url is guaranteed to be in the following format: // https://drive.google.com/uc?id=FILEID&export=download url = GetGoogleDriveDownloadLinkFromUrl(url); - using (var httpClient = new HttpClient()) + using (var httpClient = GetHttpClient()) { FileInfo downloadedFile; @@ -209,5 +210,18 @@ namespace Flax.Deps return string.Format("https://drive.google.com/uc?id={0}&export=download", url.Substring(index, closingIndex - index)); } + + private static HttpClient GetHttpClient() + { + if (IgnoreSSL) + { + Log.Warning("Accessing HTTP with SSL certificate validation disabled!"); + var handler = new HttpClientHandler(); + handler.ClientCertificateOptions = ClientCertificateOption.Manual; + handler.ServerCertificateCustomValidationCallback = (httpRequestMessage, cert, cetChain, policyErrors) => true; + return new HttpClient(handler); + } + return new HttpClient(); + } } } From 212abe7d9061b4b14766e716a58fcea08051642d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 12 Oct 2025 23:25:17 +0200 Subject: [PATCH 014/207] Update OpenAL for Windows and Android #3338 --- .../Platforms/Android/Binaries/ThirdParty/ARM64/libopenal.a | 4 ++-- Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.dll | 4 ++-- Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.lib | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libopenal.a b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libopenal.a index 5c5eebbb9..6bdec7f7a 100644 --- a/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libopenal.a +++ b/Source/Platforms/Android/Binaries/ThirdParty/ARM64/libopenal.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7179291d2efd8d3c8c8cd494a2c65e5b506e52929b962223f0af9faf2ba09382 -size 20142390 +oid sha256:4743608626b06af2df45dfb3af6b5ca97ecf08fd35d2a7dd14ac9f19d3aadd9e +size 34362142 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.dll index e64bb6cd7..c270e6da5 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a54bca64e921d32525ed58c52ce802b4ca2c3f8a92c342296da48c55ec1ea58d -size 1112064 +oid sha256:084675cd25a1090424ae7a31a93d69dd52d2752a9940e28104759e9f87c573b4 +size 1407488 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.lib index ae9715a8b..9acd14199 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/OpenAL32.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6a4ac5af61abb19624dc2ae23bc16af09a062655f3eab89fedf6c0ead6cd935a +oid sha256:b58f1412018cc3618ab579e280bf5fc167479494655dcf440d0239948e44a65d size 37562 From b62f331b08342f405df88406e9c6d9dfdccc673f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 12 Oct 2025 17:04:40 -0500 Subject: [PATCH 015/207] Fix crash when deleting collision data. --- Source/Engine/Physics/Colliders/Collider.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index f93a9bd88..6980fc4e6 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -244,10 +244,14 @@ void Collider::UpdateGeometry() if (actor) { const auto rigidBody = dynamic_cast(GetParent()); - if (_staticActor != nullptr || (rigidBody && CanAttach(rigidBody))) + if (rigidBody && CanAttach(rigidBody)) { Attach(rigidBody); } + else if (_staticActor != nullptr) + { + PhysicsBackend::AttachShape(_shape, actor); + } else { // Be static triangle mesh From df28b0d97775fe74cd1afd9e92648b765889acb2 Mon Sep 17 00:00:00 2001 From: Saas Date: Mon, 13 Oct 2025 11:31:53 +0200 Subject: [PATCH 016/207] fix Content Panel navigation bar scroll bar colors Same concept as in #2581 --- Source/Editor/Windows/ContentWindow.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 3d2ec4a66..41382e60c 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -142,6 +142,7 @@ namespace FlaxEditor.Windows { Title = "Content"; Icon = editor.Icons.Folder32; + var style = Style.Current; FlaxEditor.Utilities.Utils.SetupCommonInputActions(this); @@ -164,6 +165,8 @@ namespace FlaxEditor.Windows _navigationBar = new NavigationBar { Parent = _toolStrip, + ScrollbarTrackColor = style.Background, + ScrollbarThumbColor = style.ForegroundGrey, }; // Split panel @@ -179,7 +182,7 @@ namespace FlaxEditor.Windows var headerPanel = new ContainerControl { AnchorPreset = AnchorPresets.HorizontalStretchTop, - BackgroundColor = Style.Current.Background, + BackgroundColor = style.Background, IsScrollable = false, Offsets = new Margin(0, 0, 0, 18 + 6), }; From bd0daf7580eabe972e0b285cbe7ef9accbbd9645 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Mon, 13 Oct 2025 21:49:05 -0500 Subject: [PATCH 017/207] Fix tree arrowing up not selecting children in tree. --- Source/Editor/GUI/Tree/Tree.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Tree/Tree.cs b/Source/Editor/GUI/Tree/Tree.cs index 5530a1738..3f60572f6 100644 --- a/Source/Editor/GUI/Tree/Tree.cs +++ b/Source/Editor/GUI/Tree/Tree.cs @@ -447,8 +447,8 @@ namespace FlaxEditor.GUI.Tree // Select previous parent child var select = nodeParent.GetChild(myIndex - 1) as TreeNode; - // Select last child if is valid and expanded and has any children - if (select != null && select.IsExpanded && select.HasAnyVisibleChild) + // Get bottom most child node + while (select != null && select.IsExpanded && select.HasAnyVisibleChild) { select = select.GetChild(select.ChildrenCount - 1) as TreeNode; } From 7377bad7217620a106e55eeb6a030fb385c7804e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 14 Oct 2025 21:20:22 -0500 Subject: [PATCH 018/207] Fix check method for type editor not working in a collection. --- Source/Editor/CustomEditors/Editors/TypeEditor.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/TypeEditor.cs b/Source/Editor/CustomEditors/Editors/TypeEditor.cs index 1ed5d6cf8..462902181 100644 --- a/Source/Editor/CustomEditors/Editors/TypeEditor.cs +++ b/Source/Editor/CustomEditors/Editors/TypeEditor.cs @@ -104,7 +104,7 @@ namespace FlaxEditor.CustomEditors.Editors public event Action TypePickerValueChanged; /// - /// The custom callback for types validation. Cane be used to implement a rule for types to pick. + /// The custom callback for types validation. Can be used to implement a rule for types to pick. /// public Func CheckValid; @@ -353,7 +353,13 @@ namespace FlaxEditor.CustomEditors.Editors } if (!string.IsNullOrEmpty(typeReference.CheckMethod)) { - var parentType = ParentEditor.Values[0].GetType(); + var parentEditor = ParentEditor; + // Find actual parent editor if parent editor is collection editor + while (parentEditor.GetType().IsAssignableTo(typeof(CollectionEditor))) + parentEditor = parentEditor.ParentEditor; + + var parentType = parentEditor.Values[0].GetType(); + var method = parentType.GetMethod(typeReference.CheckMethod, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { From d323b1c7e2f55eab221d7328896c0153bd7357a2 Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 16 Oct 2025 20:28:34 +0200 Subject: [PATCH 019/207] move skylight and environment probe bake buttons into dedicated groups --- .../CustomEditors/Dedicated/EnvironmentProbeEditor.cs | 8 +++++--- Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs b/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs index 8f2173a4e..7649da514 100644 --- a/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/EnvironmentProbeEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. using FlaxEngine; +using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Dedicated { @@ -11,7 +12,7 @@ namespace FlaxEditor.CustomEditors.Dedicated [CustomEditor(typeof(EnvironmentProbe)), DefaultEditor] public class EnvironmentProbeEditor : ActorEditor { - private FlaxEngine.GUI.Button _bake; + private Button _bake; /// public override void Initialize(LayoutElementsContainer layout) @@ -20,8 +21,9 @@ namespace FlaxEditor.CustomEditors.Dedicated if (Values.HasDifferentTypes == false) { - layout.Space(10); - _bake = layout.Button("Bake").Button; + var group = layout.Group("Bake"); + group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2); + _bake = group.Button("Bake").Button; _bake.Clicked += BakeButtonClicked; } } diff --git a/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs b/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs index 0a38e0dfe..ee3f2a504 100644 --- a/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/SkyLightEditor.cs @@ -1,6 +1,7 @@ // Copyright (c) Wojciech Figat. All rights reserved. using FlaxEngine; +using FlaxEngine.GUI; namespace FlaxEditor.CustomEditors.Dedicated { @@ -19,8 +20,9 @@ namespace FlaxEditor.CustomEditors.Dedicated if (Values.HasDifferentTypes == false) { // Add 'Bake' button - layout.Space(10); - var button = layout.Button("Bake"); + var group = layout.Group("Bake"); + group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2); + var button = group.Button("Bake"); button.Button.Clicked += BakeButtonClicked; } } From a9fc5f720dde33f3cfe09ff5b1ab9335cfa45f03 Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 16 Oct 2025 20:35:53 +0200 Subject: [PATCH 020/207] break up EnvironmentProbe properties into groups --- Source/Engine/Level/Actors/EnvironmentProbe.h | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Level/Actors/EnvironmentProbe.h b/Source/Engine/Level/Actors/EnvironmentProbe.h index 09f771437..4a77a811e 100644 --- a/Source/Engine/Level/Actors/EnvironmentProbe.h +++ b/Source/Engine/Level/Actors/EnvironmentProbe.h @@ -42,38 +42,38 @@ public: /// /// The reflections texture resolution. /// - API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(0), EditorDisplay(\"Quality\")") ProbeCubemapResolution CubemapResolution = ProbeCubemapResolution::UseGraphicsSettings; + /// + /// The probe update mode. + /// + API_FIELD(Attributes = "EditorOrder(10), EditorDisplay(\"Quality\")") + ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual; + /// /// The reflections brightness. /// - API_FIELD(Attributes="EditorOrder(10), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(0), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")") float Brightness = 1.0f; /// /// The probe rendering order. The higher values are render later (on top). /// - API_FIELD(Attributes = "EditorOrder(25), EditorDisplay(\"Probe\")") + API_FIELD(Attributes = "EditorOrder(20), EditorDisplay(\"Probe\")") int32 SortOrder = 0; - /// - /// The probe update mode. - /// - API_FIELD(Attributes="EditorOrder(30), EditorDisplay(\"Probe\")") - ProbeUpdateMode UpdateMode = ProbeUpdateMode::Manual; - /// /// The probe capture camera near plane distance. /// - API_FIELD(Attributes="EditorOrder(30), Limit(0, float.MaxValue, 0.01f), EditorDisplay(\"Probe\")") + API_FIELD(Attributes="EditorOrder(25), Limit(0, float.MaxValue, 0.01f), EditorDisplay(\"Probe\")") float CaptureNearPlane = 10.0f; public: /// /// Gets the probe radius. /// - API_PROPERTY(Attributes="EditorOrder(20), DefaultValue(3000.0f), Limit(0), EditorDisplay(\"Probe\")") + API_PROPERTY(Attributes="EditorOrder(15), DefaultValue(3000.0f), Limit(0), EditorDisplay(\"Probe\")") float GetRadius() const; /// From e4bb8001b2ebd6b1f934fb80d22692c7b041cb30 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 18 Oct 2025 18:23:33 -0500 Subject: [PATCH 021/207] Fix not having undo for items dragged into scene tree into empty space. --- Source/Editor/Windows/SceneTreeWindow.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 75a3723cf..9ead85786 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -504,7 +504,9 @@ namespace FlaxEditor.Windows } var actor = item.OnEditorDrop(this); actor.Name = item.ShortName; - Level.SpawnActor(actor); + var scene = Level.Scenes[0]; + if (scene != null) + Editor.SceneEditing.Spawn(actor, scene); var graphNode = Editor.Scene.GetActorNode(actor.ID); if (graphNode != null) graphNodes.Add(graphNode); @@ -527,7 +529,9 @@ namespace FlaxEditor.Windows continue; } actor.Name = item.Name; - Level.SpawnActor(actor); + var scene = Level.Scenes[0]; + if (scene != null) + Editor.SceneEditing.Spawn(actor, scene); Editor.Scene.MarkSceneEdited(actor.Scene); } result = DragDropEffect.Move; @@ -549,7 +553,9 @@ namespace FlaxEditor.Windows Control = control, Name = item.Name, }; - Level.SpawnActor(uiControl); + var scene = Level.Scenes[0]; + if (scene != null) + Editor.SceneEditing.Spawn(uiControl, scene); Editor.Scene.MarkSceneEdited(uiControl.Scene); } result = DragDropEffect.Move; @@ -571,7 +577,9 @@ namespace FlaxEditor.Windows continue; } actor.Name = actorType.Name; - Level.SpawnActor(actor); + var scene = Level.Scenes[0]; + if (scene != null) + Editor.SceneEditing.Spawn(actor, scene); var graphNode = Editor.Scene.GetActorNode(actor.ID); if (graphNode != null) graphNodes.Add(graphNode); From 91c0ba19864d285b079150d7774923cda83c6d20 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 19 Oct 2025 22:13:40 +0300 Subject: [PATCH 022/207] Enforce pointer alignment for InlinedAllocation AssetReferences stored in inlined allocation needs to be aligned to pointer sized boundary due to atomic operations having strict requirements for such. Unaligned access seems to only crash on Windows on ARM systems when trying to allocate TextRender draw chunks. --- Source/Engine/Core/Memory/Allocation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Core/Memory/Allocation.h b/Source/Engine/Core/Memory/Allocation.h index d6958d2c3..ffd51d96c 100644 --- a/Source/Engine/Core/Memory/Allocation.h +++ b/Source/Engine/Core/Memory/Allocation.h @@ -208,7 +208,7 @@ public: typedef typename FallbackAllocation::template Data FallbackData; bool _useFallback = false; - byte _data[Capacity * sizeof(T)]; + alignas(sizeof(void*)) byte _data[Capacity * sizeof(T)]; FallbackData _fallback; public: From 6b9c727a6a538a1fda40053ce0fc908c66c449c9 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 19 Oct 2025 22:16:30 +0300 Subject: [PATCH 023/207] Fix compiler warning --- Source/Engine/Scripting/ScriptingType.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/ScriptingType.h b/Source/Engine/Scripting/ScriptingType.h index cd7b05c73..b9735a5af 100644 --- a/Source/Engine/Scripting/ScriptingType.h +++ b/Source/Engine/Scripting/ScriptingType.h @@ -563,8 +563,8 @@ FORCE_INLINE int32 GetVTableIndex(void** vtable, int32 entriesCount, void* func) offset = ((*op & 0x3FFC00) >> 10) * ((*op & 0x40000000) != 0 ? 8 : 4); return offset / sizeof(void*); } - CRASH; } + CRASH; #elif defined(__clang__) // On Clang member function pointer represents the offset from the vtable begin. return (int32)(intptr)func / sizeof(void*); From be5dbbb95f5b7ae0ad55bab61a70bd576f482e8c Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 19 Oct 2025 15:55:57 -0500 Subject: [PATCH 024/207] Fix duplicate option for collections being grayed out if nothing is in clipboard. --- Source/Editor/CustomEditors/Editors/CollectionEditor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index b977dab63..69bacee1a 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors menu.AddButton("Copy", linkedEditor.Copy); var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index)); - b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize; + b.Enabled = !Editor._readOnly && Editor._canResize; b = menu.AddButton("Paste", linkedEditor.Paste); b.Enabled = linkedEditor.CanPaste && !Editor._readOnly; @@ -407,7 +407,7 @@ namespace FlaxEditor.CustomEditors.Editors menu.AddButton("Copy", linkedEditor.Copy); var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index)); - b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize; + b.Enabled = !Editor._readOnly && Editor._canResize; var paste = menu.AddButton("Paste", linkedEditor.Paste); paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly; From 8467315a1e8ddf15141889c8e3efba4286653460 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 20 Oct 2025 18:08:54 +0200 Subject: [PATCH 025/207] Fix motion vector stability on Large World origin changes #3745 --- Content/Shaders/MotionBlur.flax | 4 ++-- Source/Engine/Renderer/MotionBlurPass.cpp | 4 ++++ Source/Shaders/MotionBlur.shader | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Content/Shaders/MotionBlur.flax b/Content/Shaders/MotionBlur.flax index b713e814b..8a8bb4e4a 100644 --- a/Content/Shaders/MotionBlur.flax +++ b/Content/Shaders/MotionBlur.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af63c3e954919a968e21f19313af1a704c2afa42c5a02744200e1dbf132dc88f -size 9418 +oid sha256:0e8fb3a9a966969ab731455c1d50b280cba0703163cffca93a51fd504262e77e +size 9497 diff --git a/Source/Engine/Renderer/MotionBlurPass.cpp b/Source/Engine/Renderer/MotionBlurPass.cpp index 3077e4cdb..f00df522c 100644 --- a/Source/Engine/Renderer/MotionBlurPass.cpp +++ b/Source/Engine/Renderer/MotionBlurPass.cpp @@ -30,6 +30,9 @@ GPU_CB_STRUCT(Data { Float2 Input0SizeInv; Float2 Input2SizeInv; + + Float3 PrevWorldOriginOffset; + float Dummy1; }); MotionBlurPass::MotionBlurPass() @@ -194,6 +197,7 @@ void MotionBlurPass::RenderMotionVectors(RenderContext& renderContext) Matrix::Transpose(renderContext.View.ViewProjection(), data.CurrentVP); Matrix::Transpose(renderContext.View.PrevViewProjection, data.PreviousVP); data.TemporalAAJitter = renderContext.View.TemporalAAJitter; + data.PrevWorldOriginOffset = renderContext.View.Origin - renderContext.View.PrevOrigin; auto cb = _shader->GetShader()->GetCB(0); context->UpdateCB(cb, &data); context->BindCB(0, cb); diff --git a/Source/Shaders/MotionBlur.shader b/Source/Shaders/MotionBlur.shader index 153b14bba..7041ec743 100644 --- a/Source/Shaders/MotionBlur.shader +++ b/Source/Shaders/MotionBlur.shader @@ -21,6 +21,8 @@ int MaxBlurSamples; uint VariableTileLoopCount; float2 Input0SizeInv; float2 Input2SizeInv; +float3 PrevWorldOriginOffset; +float Dummy1; META_CB_END DECLARE_GBUFFERDATA_ACCESS(GBuffer) @@ -39,7 +41,7 @@ float4 PS_CameraMotionVectors(Quad_VS2PS input) : SV_Target GBufferData gBufferData = GetGBufferData(); float4 worldPos = float4(GetWorldPos(gBufferData, input.TexCoord, deviceDepth), 1); - float4 prevClipPos = mul(worldPos, PreviousVP); + float4 prevClipPos = mul(worldPos + float4(PrevWorldOriginOffset, 0), PreviousVP); float4 curClipPos = mul(worldPos, CurrentVP); float2 prevHPos = prevClipPos.xy / prevClipPos.w; float2 curHPos = curClipPos.xy / curClipPos.w; From d1774cac28988b46bd1de87aec26745ae71fcb48 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 22 Oct 2025 16:14:05 +0200 Subject: [PATCH 026/207] Go back to `SSE4.2` on Windows as minimum requirement instead of `AVX2` for better user coverage by default #3732 --- Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs index 5154cdbf9..fddbdb3de 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs @@ -20,7 +20,7 @@ namespace Flax.Build /// Specifies the minimum CPU architecture type to support (on x86/x64). /// [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (om x86/x64).")] - public static CpuArchitecture WindowsCpuArch = CpuArchitecture.AVX2; // 94.48% support on PC according to Steam Hardware & Software Survey: May 2025 (https://store.steampowered.com/hwsurvey/) + public static CpuArchitecture WindowsCpuArch = CpuArchitecture.SSE4_2; // 99.78% support on PC according to Steam Hardware & Software Survey: September 2025 (https://store.steampowered.com/hwsurvey/) } } From 32b4efc17562ab3fe43957004236b0b3d974670c Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 24 Oct 2025 23:20:07 +0300 Subject: [PATCH 027/207] Add missing .NET installation architecture info to error messages --- Source/Engine/Scripting/Runtime/DotNet.cpp | 52 +++++++++++-------- .../Flax.Build/Build/DotNet/DotNetSdk.cs | 2 +- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index f69e0c060..671b6b31b 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -1798,6 +1798,33 @@ bool InitHostfxr() get_hostfxr_params.dotnet_root = dotnetRoot.Get(); } #endif + String platformStr; + switch (PLATFORM_TYPE) + { + case PlatformType::Windows: + case PlatformType::UWP: + if (PLATFORM_ARCH == ArchitectureType::x64) + platformStr = "Windows x64"; + else if (PLATFORM_ARCH == ArchitectureType::ARM64) + platformStr = "Windows ARM64"; + else + platformStr = "Windows x86"; + break; + case PlatformType::Linux: + platformStr = PLATFORM_ARCH_ARM64 ? "Linux ARM64" : PLATFORM_ARCH_ARM ? "Linux Arm32" : PLATFORM_64BITS ? "Linux x64" : "Linux x86"; + break; + case PlatformType::Mac: + platformStr = PLATFORM_ARCH_ARM || PLATFORM_ARCH_ARM64 ? "macOS ARM64" : PLATFORM_64BITS ? "macOS x64" : "macOS x86"; + break; + default: + if (PLATFORM_ARCH == ArchitectureType::x64) + platformStr = "x64"; + else if (PLATFORM_ARCH == ArchitectureType::ARM64) + platformStr = "ARM64"; + else + platformStr = "x86"; + } + char_t hostfxrPath[1024]; size_t hostfxrPathSize = sizeof(hostfxrPath) / sizeof(char_t); int rc = get_hostfxr_path(hostfxrPath, &hostfxrPathSize, &get_hostfxr_params); @@ -1810,9 +1837,9 @@ bool InitHostfxr() Platform::OpenUrl(TEXT("https://dotnet.microsoft.com/en-us/download/dotnet")); #endif #if USE_EDITOR - LOG(Fatal, "Missing .NET 8 or later SDK installation required to run Flax Editor."); + LOG(Fatal, "Missing .NET 8 or later SDK installation for {0} is required to run Flax Editor.", platformStr); #else - LOG(Fatal, "Missing .NET 8 or later Runtime installation required to run this application."); + LOG(Fatal, "Missing .NET 8 or later Runtime installation for {0} is required to run this application.", platformStr); #endif return true; } @@ -1870,27 +1897,6 @@ bool InitHostfxr() hostfxr_close(handle); if (rc == 0x80008096) // FrameworkMissingFailure { - String platformStr; - switch (PLATFORM_TYPE) - { - case PlatformType::Windows: - case PlatformType::UWP: - if (PLATFORM_ARCH == ArchitectureType::x64) - platformStr = "Windows x64"; - else if (PLATFORM_ARCH == ArchitectureType::ARM64) - platformStr = "Windows ARM64"; - else - platformStr = "Windows x86"; - break; - case PlatformType::Linux: - platformStr = PLATFORM_ARCH_ARM64 ? "Linux ARM64" : PLATFORM_ARCH_ARM ? "Linux Arm32" : PLATFORM_64BITS ? "Linux x64" : "Linux x86"; - break; - case PlatformType::Mac: - platformStr = PLATFORM_ARCH_ARM || PLATFORM_ARCH_ARM64 ? "macOS ARM64" : PLATFORM_64BITS ? "macOS x64" : "macOS x86"; - break; - default:; - platformStr = ""; - } LOG(Fatal, "Failed to resolve compatible .NET runtime version in '{0}'. Make sure the correct platform version for runtime is installed ({1})", platformStr, String(init_params.dotnet_root)); } else diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index bf3c61f7e..d12e4b8df 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -115,7 +115,7 @@ namespace Flax.Build /// Init with a proper message. /// public MissingException() - : base(string.IsNullOrEmpty(Configuration.Dotnet) ? $"Missing .NET SDK {MinimumVersion} (or higher)." : $"Missing .NET SDK {Configuration.Dotnet}.") + : base(string.IsNullOrEmpty(Configuration.Dotnet) ? $"Missing .NET SDK {MinimumVersion} (or higher) for {Platform.BuildTargetPlatform} {Platform.BuildTargetArchitecture}." : $"Missing .NET SDK {Configuration.Dotnet}.") { } } From 0d140c4f39da982c76e9ad6f0b711b33a853caee Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 24 Oct 2025 23:21:46 +0300 Subject: [PATCH 028/207] Fix Windows dotnet version lookup without installed SDK Having only x86 dotnet SDK installed while expecting x64 version fails prematurely. --- Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index d12e4b8df..7e55377fd 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -218,8 +218,8 @@ namespace Flax.Build using RegistryKey runtimeKey = baseKey.OpenSubKey(@$"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sharedfx\Microsoft.NETCore.App"); using RegistryKey hostKey = baseKey.OpenSubKey(@$"SOFTWARE\dotnet\Setup\InstalledVersions\{arch}\sharedhost"); dotnetPath = (string)hostKey.GetValue("Path"); - dotnetSdkVersions = sdkVersionsKey.GetValueNames(); - dotnetRuntimeVersions = runtimeKey.GetValueNames(); + dotnetSdkVersions = sdkVersionsKey?.GetValueNames() ?? Enumerable.Empty(); + dotnetRuntimeVersions = runtimeKey?.GetValueNames() ?? Enumerable.Empty(); } #pragma warning restore CA1416 break; From 280035e54f15a248c2c9e590b233638957cdce89 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 24 Oct 2025 23:23:39 +0300 Subject: [PATCH 029/207] Use stderr for Flax.Build error messages This turns the error messages from Flax.Build to red in Flax Editor console output. --- Source/Tools/Flax.Build/Log.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Log.cs b/Source/Tools/Flax.Build/Log.cs index fbc96e576..834e51768 100644 --- a/Source/Tools/Flax.Build/Log.cs +++ b/Source/Tools/Flax.Build/Log.cs @@ -65,7 +65,10 @@ namespace Flax.Build if (ApplyConsoleColors) Console.ForegroundColor = color; - Console.WriteLine(Indent + message); + if (color != ConsoleColor.Red) + Console.WriteLine(Indent + message); + else + Console.Error.WriteLine(Indent + message); if (ApplyConsoleColors) Console.ResetColor(); From c1439c646e86e66d112111f1e078ac847d03ce45 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 25 Oct 2025 11:34:48 -0500 Subject: [PATCH 030/207] Fix default for prefab canvas size index being 0 instead of -1. --- Source/Editor/Viewport/PrefabWindowViewport.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 362aeacf6..a22b4042f 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -74,7 +74,7 @@ namespace FlaxEditor.Viewport private PrefabUIEditorRoot _uiRoot; private bool _showUI = false; - private int _defaultScaleActiveIndex = 0; + private int _defaultScaleActiveIndex = -1; private int _customScaleActiveIndex = -1; private ContextMenuButton _uiModeButton; private ContextMenuChildMenu _uiViewOptions; From 2841ac22df83cf80ef9ea71b46cabb658b1d8da7 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 25 Oct 2025 22:35:48 +0300 Subject: [PATCH 031/207] Bump the maximum supported .NET SDK version to 10 --- Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index 7e55377fd..f8e215320 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -135,7 +135,7 @@ namespace Flax.Build /// /// The maximum SDK version. /// - public static Version MaximumVersion => new Version(9, 0); + public static Version MaximumVersion => new Version(10, 0); /// public override TargetPlatform[] Platforms From 755702b65ac2a4f2917db5e3fedabecb44ebe5c8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 25 Oct 2025 22:52:41 +0200 Subject: [PATCH 032/207] Improve #3747 to check for loaded levels in drag drop validation --- Source/Editor/Windows/SceneTreeWindow.cs | 25 +++++++++--------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index 9ead85786..d9ba67aa3 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -319,22 +319,22 @@ namespace FlaxEditor.Windows { if (assetItem.IsOfType()) return true; - return assetItem.OnEditorDrag(this); + return assetItem.OnEditorDrag(this) && Level.IsAnySceneLoaded; } private static bool ValidateDragActorType(ScriptType actorType) { - return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType); + return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType) && Level.IsAnySceneLoaded; } private static bool ValidateDragControlType(ScriptType controlType) { - return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType); + return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType) && Level.IsAnySceneLoaded; } private static bool ValidateDragScriptItem(ScriptItem script) { - return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null; + return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null && Level.IsAnySceneLoaded; } /// @@ -490,6 +490,7 @@ namespace FlaxEditor.Windows if (result == DragDropEffect.None) { _isDropping = true; + // Drag assets if (_dragAssets != null && _dragAssets.HasValidDrag) { @@ -504,9 +505,7 @@ namespace FlaxEditor.Windows } var actor = item.OnEditorDrop(this); actor.Name = item.ShortName; - var scene = Level.Scenes[0]; - if (scene != null) - Editor.SceneEditing.Spawn(actor, scene); + Editor.SceneEditing.Spawn(actor); var graphNode = Editor.Scene.GetActorNode(actor.ID); if (graphNode != null) graphNodes.Add(graphNode); @@ -529,9 +528,7 @@ namespace FlaxEditor.Windows continue; } actor.Name = item.Name; - var scene = Level.Scenes[0]; - if (scene != null) - Editor.SceneEditing.Spawn(actor, scene); + Editor.SceneEditing.Spawn(actor); Editor.Scene.MarkSceneEdited(actor.Scene); } result = DragDropEffect.Move; @@ -553,9 +550,7 @@ namespace FlaxEditor.Windows Control = control, Name = item.Name, }; - var scene = Level.Scenes[0]; - if (scene != null) - Editor.SceneEditing.Spawn(uiControl, scene); + Editor.SceneEditing.Spawn(uiControl); Editor.Scene.MarkSceneEdited(uiControl.Scene); } result = DragDropEffect.Move; @@ -577,9 +572,7 @@ namespace FlaxEditor.Windows continue; } actor.Name = actorType.Name; - var scene = Level.Scenes[0]; - if (scene != null) - Editor.SceneEditing.Spawn(actor, scene); + Editor.SceneEditing.Spawn(actor); var graphNode = Editor.Scene.GetActorNode(actor.ID); if (graphNode != null) graphNodes.Add(graphNode); From 2ee2c97f1eb8f5ab6c1be6ba11f925ccbf3e29c0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 26 Oct 2025 21:48:49 +0100 Subject: [PATCH 033/207] Update docs for Ubuntu 24 and Clang 4 --- README.md | 4 ++-- Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8e9de0424..f4cc82ee2 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ Follow the instructions below to compile and run the engine from source. * Fedora: `sudo dnf install dotnet-sdk-8.0` * Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host` * Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/)) - * Ubuntu: `sudo apt install vulkan-sdk` + * Ubuntu: `sudo apt install vulkan-sdk` (deprecated, follow official docs) * Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers` * Arch: `sudo pacman -S vulkan-headers vulkan-tools vulkan-validation-layers` * Install Git with LFS @@ -60,7 +60,7 @@ Follow the instructions below to compile and run the engine from source. * Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev` * Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel` * Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib` -* Install Clang compiler (version 6 or later): +* Install Clang compiler (version 14 or later): * Ubuntu: `sudo apt-get install clang lldb lld` * Fedora: `sudo dnf install clang llvm lldb lld` * Arch: `sudo pacman -S clang lldb lld` diff --git a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs index 174b2bcee..6d017806d 100644 --- a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs @@ -14,7 +14,7 @@ namespace Flax.Build /// Specifies the minimum Clang compiler version to use on Linux (eg. 10). /// [CommandLine("linuxClangMinVer", "", "Specifies the minimum Clang compiler version to use on Linux (eg. 10).")] - public static string LinuxClangMinVer = "13"; + public static string LinuxClangMinVer = "14"; } } From a0689f3f00790607fe1a45e39e31466be0877b90 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 27 Oct 2025 00:01:36 +0100 Subject: [PATCH 034/207] Update all Linux deps with Clang 14 on Ubuntu 24 --- .../ThirdParty/x64/libGenericCodeGen.a | 4 ++-- .../Linux/Binaries/ThirdParty/x64/libHLSL.a | 2 +- .../ThirdParty/x64/libMachineIndependent.a | 4 ++-- .../Binaries/ThirdParty/x64/libNvCloth.a | 4 ++-- .../Binaries/ThirdParty/x64/libOGLCompiler.a | 4 ++-- .../Binaries/ThirdParty/x64/libOSDependent.a | 4 ++-- .../libPhysXCharacterKinematic_static_64.a | 4 ++-- .../ThirdParty/x64/libPhysXCommon_static_64.a | 4 ++-- .../x64/libPhysXCooking_static_64.a | 2 +- .../x64/libPhysXExtensions_static_64.a | 4 ++-- .../x64/libPhysXFoundation_static_64.a | 4 ++-- .../ThirdParty/x64/libPhysXPvdSDK_static_64.a | 4 ++-- .../x64/libPhysXVehicle2_static_64.a | 4 ++-- .../x64/libPhysXVehicle_static_64.a | 4 ++-- .../ThirdParty/x64/libPhysX_static_64.a | 4 ++-- .../ThirdParty/x64/libSPIRV-Tools-opt.a | 4 ++-- .../Binaries/ThirdParty/x64/libSPIRV-Tools.a | 4 ++-- .../Linux/Binaries/ThirdParty/x64/libSPIRV.a | 4 ++-- .../Linux/Binaries/ThirdParty/x64/libassimp.a | 4 ++-- .../Linux/Binaries/ThirdParty/x64/libcurl.a | 4 ++-- .../Binaries/ThirdParty/x64/libfreetype.a | 4 ++-- .../Binaries/ThirdParty/x64/libglslang.a | 4 ++-- .../Linux/Binaries/ThirdParty/x64/libopenal.a | 4 ++-- .../Linux/Binaries/ThirdParty/x64/libvorbis.a | 4 ++-- .../Binaries/ThirdParty/x64/libvorbisenc.a | 4 ++-- .../Binaries/ThirdParty/x64/libvorbisfile.a | 4 ++-- .../Flax.Build/Deps/Dependencies/Assimp.cs | 7 ++++--- .../Flax.Build/Deps/Dependencies/NvCloth.cs | 3 +++ .../Flax.Build/Deps/Dependencies/OpenAL.cs | 1 + .../Flax.Build/Deps/Dependencies/PhysX.cs | 8 +++++--- .../Flax.Build/Deps/Dependencies/curl.cs | 19 ++++++++++--------- .../Flax.Build/Deps/Dependencies/freetype.cs | 17 ++++++++++------- .../Flax.Build/Deps/Dependencies/nethost.cs | 6 +++--- .../Flax.Build/Deps/Dependencies/vorbis.cs | 8 +++++--- Source/Tools/Flax.Build/Deps/Dependency.cs | 2 +- 35 files changed, 92 insertions(+), 79 deletions(-) diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libGenericCodeGen.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libGenericCodeGen.a index 1a8237027..c5745f338 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libGenericCodeGen.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libGenericCodeGen.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c6add4697d7513883ea5583587a50b3b01ff0156d06e2ccc48070e23c415bf4 -size 31902 +oid sha256:794ce94a3f89c080a59889c7d49f98a5e3ce004c8c5fea533370e6dc9a388396 +size 30214 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libHLSL.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libHLSL.a index 3ce822729..5e9764557 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libHLSL.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libHLSL.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:254756fe20ea96e73bd82a9a491c1f18c852aae33dd8bf9d6648c91d0247c7eb +oid sha256:0fd14ac5c4b03136ad2734a3b866c6524cf817afa1e6294c8f1408cac44f5b24 size 1068 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libMachineIndependent.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libMachineIndependent.a index 0235bd398..56ac04634 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libMachineIndependent.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libMachineIndependent.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9e79710c30285e0c5e4fe2ff1e9c47d06d9457428c177c6580e1ec8966dd079 -size 5287654 +oid sha256:1d3af5ec52f4d1aa80ed28e41a40bd53f3162f2c42965e162a0fca8329d479a6 +size 5370940 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libNvCloth.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libNvCloth.a index 573dfb245..51e71d359 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libNvCloth.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libNvCloth.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6234ee8e0cea08be467f8d13994fee8b94dfa2ed2147460a3268dbe6cfeb87a6 -size 553518 +oid sha256:62687b3036959f1b873623202dbfef3706c154ceb3fa26e87dea6702d9bab657 +size 553390 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOGLCompiler.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOGLCompiler.a index 7ac2e3cc5..ca8f14082 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOGLCompiler.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOGLCompiler.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b78a2a48c4c17a066164b7d151260d836e01ddb0d4087f25a6266aafc220eb5 -size 3332 +oid sha256:90b0951e9d0fcbcd63d53c60981d342bcd5d3c40f04983ee281fb45821f84ab0 +size 3308 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOSDependent.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOSDependent.a index ce233ff4f..b9dad7646 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOSDependent.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libOSDependent.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:67afb8e80b768c785905821f4ab4e4491598ab3a69f3fd514d4a9dadf6f80d36 -size 4966 +oid sha256:c5725e2cfe85e4c8a03f407562098306d3d1cc6715c4349288433cde0efcceb2 +size 4918 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCharacterKinematic_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCharacterKinematic_static_64.a index 98a4a8286..7e702f665 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCharacterKinematic_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCharacterKinematic_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9cf1125a7e1d0f2fb1a12b6f04537228cd37e3322cf818696a2d3446f97b953 -size 278626 +oid sha256:9929fabaa9ca418b8e48ea1081652fa61180fe0bec0518f5603966f6b4918e5c +size 278562 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCommon_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCommon_static_64.a index 242c33ca5..6b596b38b 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCommon_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCommon_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19d0245e6dce80bb596ed10a154a5e18a5902df6c3e95c087e856fa6ca25f6f6 -size 4596892 +oid sha256:8b8f2a44b37ac65ea3eaee9ad090f471be3848490017d06980e6224b443a2218 +size 4595548 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCooking_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCooking_static_64.a index e7d99ad1f..851680c01 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCooking_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXCooking_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3d44653d8526acb4fd631cf007941a656ab054552b9406cf54199abf7bfab98c +oid sha256:a1126331d2ba6933b1fa70d8d735c766e67785d58c243d960d6c0225f207bdf3 size 28284 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXExtensions_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXExtensions_static_64.a index 150fe2099..5b547830d 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXExtensions_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXExtensions_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0667c4d6515bb6c95c9ce5072edc56da7da45b6691eba2d3332f6b1db27c7511 -size 4081244 +oid sha256:d516b0b54861a86ed413f7a0e3358d8ea74952b5ef84871332453f758f0d8920 +size 4081602 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXFoundation_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXFoundation_static_64.a index 94622de79..8d0a04c50 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXFoundation_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXFoundation_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db06448b20f3d0b14780730e7e884cb18be4b546dc228b84c0ad8d33b088dcd4 -size 121148 +oid sha256:ee3df94bba27db3a77864816c460c76f82c58a98fd1b3055554fb773232b3fef +size 121028 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXPvdSDK_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXPvdSDK_static_64.a index e49794ebc..c22a96c31 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXPvdSDK_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXPvdSDK_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:419c688357cf3c4b781fa3cae601906be8e46d1de9d1238f8acb482166b5f837 -size 503210 +oid sha256:c22c955f6cf0d5b55033d0acdcae44b6e17e07eaaec884fa5ad80dcef8ddc15d +size 503146 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle2_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle2_static_64.a index 2da3cad95..5163432ac 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle2_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle2_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5559f869db603d979003f6d8d3d35179db5193974f39481bc2ee821af27faf3 -size 195454 +oid sha256:12d23592318c74bd3d5aed5134bf8be50fa32c78e66e59236d25620aed7316e9 +size 195302 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle_static_64.a index abcca2a9a..488e90c95 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysXVehicle_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:427613612343bd0ea40d523ea0c7458a6ed927357fe4a277428d4ee0228bad95 -size 1209552 +oid sha256:bd1af110b345dc5548cf07885ea4f0e95bd8f83b4f9c041bfc962a6e72d8cc71 +size 1209456 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysX_static_64.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysX_static_64.a index 12db8e2e6..c4e817356 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysX_static_64.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libPhysX_static_64.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2b606e398be2f9772da1c0dd0f1b9aead20974cf03eeaa7a2df1a477eb6caff9 -size 6360866 +oid sha256:7d2e9591a28c5d0bf0aee830e340c6e9a1e978aa7f80cdbda002c5fa1fac7196 +size 6359770 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools-opt.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools-opt.a index 5c0736d35..fca25c5e9 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools-opt.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools-opt.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d101fdbbdb514937ef3ccdbb2a0791542fdb880b0331a59599e1cab3fdaba8d0 -size 8949560 +oid sha256:5205664eaa07b9161d5c9daa4ca7a2e224a7e8d082d13390bd3743e7cd9937cf +size 8105308 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools.a index 3732827d0..7f73d4824 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV-Tools.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dafaa04b6d0896fdb33738ea7104fa47ad150f287d046bd752d7f343efb6bc1a -size 2515558 +oid sha256:17699265b59441501b4837cef88d6f82d7404201f2d05cf2eb35e083b4b63c14 +size 2394830 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV.a index 2c9b04e3d..622011e31 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libSPIRV.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e8ee83a8a956e1e700aa5599107a5710d9c17f350a7d9025765b588146272c7 -size 1057884 +oid sha256:e5bdbd77d71f37080617b261aa92b589ea62a2bc0e567755367733535ddf9d60 +size 1071492 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libassimp.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libassimp.a index 92b3c5545..dd916929d 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libassimp.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libassimp.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3510d6c5585f08fc9fcbf2044bb0dc0238e2501c1914e3b98aef36bc8cd2711 -size 10303430 +oid sha256:d9e8469042e4734543f142f9559347bd1e36bc897b8962ae4f58fcec985e70ba +size 10261802 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libcurl.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libcurl.a index f722e3a16..f08214054 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libcurl.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libcurl.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b7f66f58d5e7dabe0a0ec7e01ae5dd3683c2ae474ac6dbc133393b2be715bbf -size 574086 +oid sha256:ab21e8f683649d34388dbdddafb33ac4ba152e0e0fb1d631e03641e3bfc484c3 +size 767214 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libfreetype.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libfreetype.a index 5d8a644ec..a77930866 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libfreetype.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libfreetype.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:678c906be4431bbf7b1a5e915e587f0e1f4f0c14cd2dc30a90e44e669d7f690c -size 1123598 +oid sha256:0885cd644e330c5b622e1312395125c2f14ac78deaf9ca58b66fdcfa074d28bb +size 1136190 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libglslang.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libglslang.a index 0e82c48ea..9f830b8e1 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libglslang.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libglslang.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5f1fad7d2bc3a7768ea1e8a075315c371064cfd5cfcc64f9d4a55837e304b3a -size 32314 +oid sha256:9a1f4379e8262064813ab001df54d5759077d5c19191ad464374a07073d7db8e +size 34130 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libopenal.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libopenal.a index 2a0348bcd..b88eb07e6 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libopenal.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libopenal.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:762b5fd6fe17dc4a99fd49488b32486e6744aef278a44c9843bdda05972e1287 -size 1565506 +oid sha256:3e51434c5af3558b73ced5051a239b33077bce527d25e5c71602e7b27b65c80c +size 3506588 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbis.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbis.a index 0feb18fa5..c606738e6 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbis.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbis.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3257e82c3740aec7b15d065ef0b89064b971f77db4251c220fd277b3a96ac19d -size 294326 +oid sha256:fdc79150a98f6f589d5a339dc50e4ad628f92767aa194c8ac4a202ebd78cec1d +size 296926 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisenc.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisenc.a index 59ef26a02..1fc00718c 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisenc.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisenc.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50c8f8977fece27302c967c7d65b4a64b1c07f538b54edb0852169bba0bca91b -size 778766 +oid sha256:6d8cbdad00f1a7f0a59f500d6687a64fbcdfc3db7c3acc05a65f68b7ea6566cb +size 782150 diff --git a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisfile.a b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisfile.a index 9e6ebdd68..41abc9b37 100644 --- a/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisfile.a +++ b/Source/Platforms/Linux/Binaries/ThirdParty/x64/libvorbisfile.a @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc394079fd2b794b637e879357494d0db3e1a8e9690949d7708766ccf360d5eb -size 38204 +oid sha256:1b8377b841a4a871487ff0f591970ada56306087b1f4e3f09a8fb457f6811ac4 +size 38804 diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs b/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs index f72928bf8..bb1c4fa3c 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/Assimp.cs @@ -127,11 +127,12 @@ namespace Flax.Deps.Dependencies { "CC", "clang-" + Configuration.LinuxClangMinVer }, { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, { "CXX", "clang++-" + Configuration.LinuxClangMinVer }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; // Build for Linux RunCmake(root, platform, TargetArchitecture.x64, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + globalConfig, envVars); - Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool, envVars); configHeaderFilePath = Path.Combine(root, "include", "assimp", "config.h"); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); Utilities.FileCopy(Path.Combine(root, "lib", "libassimp.a"), Path.Combine(depsFolder, "libassimp.a")); @@ -143,11 +144,11 @@ namespace Flax.Deps.Dependencies foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 }) { RunCmake(root, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + globalConfig); - Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool); configHeaderFilePath = Path.Combine(root, "include", "assimp", "config.h"); var depsFolder = GetThirdPartyFolder(options, platform, architecture); Utilities.FileCopy(Path.Combine(root, "lib", "libassimp.a"), Path.Combine(depsFolder, "libassimp.a")); - Utilities.Run("make", "clean", null, root, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("make", "clean", null, root, Utilities.RunOptions.DefaultTool); } break; } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs index 699e40f72..1120a94f8 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/NvCloth.cs @@ -115,6 +115,7 @@ namespace Flax.Deps.Dependencies var buildFolder = Path.Combine(nvCloth, "compiler", platform.ToString() + '_' + architecture.ToString()); var envVars = new Dictionary(); envVars["GW_DEPS_ROOT"] = root; + envVars["CMAKE_BUILD_PARALLEL_LEVEL"] = CmakeBuildParallel; switch (platform) { case TargetPlatform.Windows: @@ -166,6 +167,8 @@ namespace Flax.Deps.Dependencies cmakeArgs += " -DTARGET_BUILD_PLATFORM=linux"; cmakeName = "linux"; binariesPrefix = "lib"; + envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer); + envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer); break; default: throw new InvalidPlatformException(platform); } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs index 034b2a4b8..319ad70b3 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/OpenAL.cs @@ -123,6 +123,7 @@ namespace Flax.Deps.Dependencies { { "CC", "clang-" + Configuration.LinuxClangMinVer }, { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CXX", "clang++-" + Configuration.LinuxClangMinVer }, { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; var config = $"-DALSOFT_REQUIRE_ALSA=ON " + diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs index 32e2d2f38..39f7ad975 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/PhysX.cs @@ -238,6 +238,8 @@ namespace Flax.Deps.Dependencies case TargetPlatform.Linux: envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer); envVars.Add("CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer); + envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer); + envVars.Add("CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel); break; case TargetPlatform.Mac: break; default: throw new InvalidPlatformException(BuildPlatform); @@ -258,7 +260,7 @@ namespace Flax.Deps.Dependencies Log.Info("Building PhysX version " + File.ReadAllText(Path.Combine(root, "physx", "version.txt")) + " to " + binariesSubDir); // Generate project files - Utilities.Run(projectGenPath, preset, null, projectGenDir, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run(projectGenPath, preset, null, projectGenDir, Utilities.RunOptions.DefaultTool, envVars); switch (targetPlatform) { @@ -304,10 +306,10 @@ namespace Flax.Deps.Dependencies } break; case TargetPlatform.Linux: - Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.ConsoleLogOutput, envVars); break; case TargetPlatform.Mac: - Utilities.Run("xcodebuild", "-project PhysXSDK.xcodeproj -alltargets -configuration " + configuration, null, Path.Combine(projectGenDir, "compiler", preset), Utilities.RunOptions.ConsoleLogOutput); + Utilities.Run("xcodebuild", "-project PhysXSDK.xcodeproj -alltargets -configuration " + configuration, null, Path.Combine(projectGenDir, "compiler", preset), Utilities.RunOptions.ConsoleLogOutput, envVars); break; default: throw new InvalidPlatformException(BuildPlatform); } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs b/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs index a474b9566..447f573a7 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/curl.cs @@ -107,13 +107,14 @@ namespace Flax.Deps.Dependencies { { "CC", "clang-" + Configuration.LinuxClangMinVer }, { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; var buildDir = Path.Combine(root, "build"); SetupDirectory(buildDir, true); - Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); - Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("make", "install", null, root, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.DefaultTool, envVars); + Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run("make", "install", null, root, Utilities.RunOptions.DefaultTool); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); var filename = "libcurl.a"; Utilities.FileCopy(Path.Combine(buildDir, "lib", filename), Path.Combine(depsFolder, filename)); @@ -153,11 +154,11 @@ namespace Flax.Deps.Dependencies }; var buildDir = Path.Combine(root, "build"); SetupDirectory(buildDir, true); - Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("chmod", "+x install-sh", null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --host=" + archName + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); - Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("make", "install", null, root, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run("chmod", "+x install-sh", null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --host=" + archName + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.DefaultTool, envVars); + Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool); + Utilities.Run("make", "install", null, root, Utilities.RunOptions.DefaultTool); var depsFolder = GetThirdPartyFolder(options, platform, architecture); var filename = "libcurl.a"; Utilities.FileCopy(Path.Combine(buildDir, "lib", filename), Path.Combine(depsFolder, filename)); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs index ac0079401..89ed09a72 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/freetype.cs @@ -114,19 +114,22 @@ namespace Flax.Deps.Dependencies } case TargetPlatform.Linux: { - var envVars = new Dictionary + var envVars = new Dictionary { { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer } + { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; // Fix scripts - Utilities.Run("sed", "-i -e \'s/\r$//\' autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); - Utilities.Run("sed", "-i -e \'s/\r$//\' configure", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run("dos2unix", "autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run("dos2unix", "configure", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + //Utilities.Run("sed", "-i -e \'s/\r$//\' autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); + //Utilities.Run("sed", "-i -e \'s/\r$//\' configure", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); Utilities.Run("chmod", "+x autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError); Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.Default, envVars); + Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); // Disable using libpng even if it's found on the system var cmakeFile = Path.Combine(root, "CMakeLists.txt"); @@ -140,8 +143,8 @@ namespace Flax.Deps.Dependencies // Build for Linux SetupDirectory(buildDir, true); var toolchain = UnixToolchain.GetToolchainName(platform, TargetArchitecture.x64); - Utilities.Run("cmake", string.Format("-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_TARGET={0} ..", toolchain), null, buildDir, Utilities.RunOptions.ThrowExceptionOnError, envVars); - Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run("cmake", string.Format("-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_TARGET={0} ..", toolchain), null, buildDir, Utilities.RunOptions.DefaultTool, envVars); + Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.DefaultTool, envVars); var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64); Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName)); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index 11ab39f8d..1991aa605 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -303,9 +303,9 @@ namespace Flax.Deps.Dependencies } // Ensure to have dependencies installed - Utilities.Run("ninja", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("cmake", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError); - Utilities.Run("python", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError); + Utilities.Run("ninja", "--version", null, null, Utilities.RunOptions.DefaultTool); + Utilities.Run("cmake", "--version", null, null, Utilities.RunOptions.DefaultTool); + Utilities.Run("python", "--version", null, null, Utilities.RunOptions.DefaultTool); // Get the source if (!Directory.Exists(Path.Combine(root, ".git"))) diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs index 45adc9188..d22f8696f 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/vorbis.cs @@ -366,15 +366,17 @@ namespace Flax.Deps.Dependencies var envVars = new Dictionary { { "CC", "clang-" + Configuration.LinuxClangMinVer }, - { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer } + { "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }, + { "CXX", "clang++-" + Configuration.LinuxClangMinVer }, + { "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel }, }; var buildDir = Path.Combine(root, "build"); - Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.Default, envVars); + Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.DefaultTool, envVars); // Build for Linux var toolchain = UnixToolchain.GetToolchainName(platform, TargetArchitecture.x64); - Utilities.Run(Path.Combine(root, "configure"), string.Format("--host={0}", toolchain), null, root, Utilities.RunOptions.Default, envVars); + Utilities.Run(Path.Combine(root, "configure"), string.Format("--host={0}", toolchain), null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars); SetupDirectory(buildDir, true); Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars); diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index a79a968a3..43cdcc146 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -456,7 +456,7 @@ namespace Flax.Deps case TargetPlatform.Mac: break; default: throw new InvalidPlatformException(BuildPlatform); } - Utilities.Run(path, args, null, workspace, Utilities.RunOptions.ThrowExceptionOnError, envVars); + Utilities.Run(path, args, null, workspace, Utilities.RunOptions.DefaultTool, envVars); } internal bool GetMsBuildForPlatform(TargetPlatform targetPlatform, out VisualStudioVersion vsVersion, out string msBuildPath) From c944bd9bed476f64f74bbd879e0c4c23bd5a74d1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 27 Oct 2025 00:32:29 +0100 Subject: [PATCH 035/207] Fix missing Vulkan feature activation for host query reset --- .../GraphicsDevice/Vulkan/GPUContextVulkan.cpp | 2 +- .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 18 ++++++++++++++++++ .../GraphicsDevice/Vulkan/GPUDeviceVulkan.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 00af99154..c374bbeed 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -114,7 +114,7 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue) #if GPU_ENABLE_TRACY #if VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset && !PLATFORM_SWITCH // Use calibrated timestamps extension - if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT) + if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT && _device->PhysicalDeviceFeatures12.hostQueryReset) { _tracyContext = tracy::CreateVkContext(_device->Adapter->Gpu, _device->Device, vkResetQueryPoolEXT, vkGetPhysicalDeviceCalibrateableTimeDomainsEXT, vkGetCalibratedTimestampsEXT); } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 7c54be973..e96f7b3b3 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1568,7 +1568,15 @@ bool GPUDeviceVulkan::Init() vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queueCount, QueueFamilyProps.Get()); // Query device features + RenderToolsVulkan::ZeroStruct(PhysicalDeviceFeatures12, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES); vkGetPhysicalDeviceFeatures(gpu, &PhysicalDeviceFeatures); + if (vkGetPhysicalDeviceFeatures2) + { + VkPhysicalDeviceFeatures2 features2; + RenderToolsVulkan::ZeroStruct(features2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2); + features2.pNext = &PhysicalDeviceFeatures12; + vkGetPhysicalDeviceFeatures2(gpu, &features2); + } // Get extensions and layers Array deviceExtensions; @@ -1671,6 +1679,16 @@ bool GPUDeviceVulkan::Init() VulkanPlatform::RestrictEnabledPhysicalDeviceFeatures(PhysicalDeviceFeatures, enabledFeatures); deviceInfo.pEnabledFeatures = &enabledFeatures; +#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset + VkPhysicalDeviceHostQueryResetFeatures resetFeatures; + if (PhysicalDeviceFeatures12.hostQueryReset) + { + RenderToolsVulkan::ZeroStruct(resetFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES); + resetFeatures.hostQueryReset = VK_TRUE; + deviceInfo.pNext = &resetFeatures; + } +#endif + // Create the device VALIDATE_VULKAN_RESULT(vkCreateDevice(gpu, &deviceInfo, nullptr, &Device)); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h index ae265100c..09fa93f3e 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.h @@ -496,6 +496,7 @@ public: /// The physical device enabled features. /// VkPhysicalDeviceFeatures PhysicalDeviceFeatures; + VkPhysicalDeviceVulkan12Features PhysicalDeviceFeatures12; Array TimestampQueryPools; Array OcclusionQueryPools; From cd66d0021965a487c86d9ed38ed009fa11a82b66 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 27 Oct 2025 16:48:24 +0100 Subject: [PATCH 036/207] Add checking hardware instruction set on Windows earlier on #3732 --- .../Engine/Platform/Windows/WindowsPlatform.cpp | 17 ++++++++++++----- Source/ThirdParty/tracy/client/TracyAlloc.cpp | 8 ++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 116cb3deb..e40ae1ca7 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -543,11 +543,9 @@ void WindowsPlatform::ReleaseMutex() } } -void WindowsPlatform::PreInit(void* hInstance) +PRAGMA_DISABLE_OPTIMIZATION; +void CheckInstructionSet() { - ASSERT(hInstance); - Instance = hInstance; - #if PLATFORM_ARCH_X86 || PLATFORM_ARCH_X64 // Check the minimum vector instruction set support int32 cpuInfo[4] = { -1 }; @@ -597,10 +595,19 @@ void WindowsPlatform::PreInit(void* hInstance) { // Not supported CPU CPUBrand cpu; - Error(String::Format(TEXT("Cannot start program due to lack of CPU feature {}.\n\n{}"), missingFeature, String(cpu.Buffer))); + Platform::Error(String::Format(TEXT("Cannot start program due to lack of CPU feature {}.\n\n{}"), missingFeature, String(cpu.Buffer))); exit(-1); } #endif +} +PRAGMA_ENABLE_OPTIMIZATION; + +void WindowsPlatform::PreInit(void* hInstance) +{ + ASSERT(hInstance); + Instance = hInstance; + + CheckInstructionSet(); // Disable the process from being showing "ghosted" while not responding messages during slow tasks DisableProcessWindowsGhosting(); diff --git a/Source/ThirdParty/tracy/client/TracyAlloc.cpp b/Source/ThirdParty/tracy/client/TracyAlloc.cpp index 545a6062b..923c92494 100644 --- a/Source/ThirdParty/tracy/client/TracyAlloc.cpp +++ b/Source/ThirdParty/tracy/client/TracyAlloc.cpp @@ -6,6 +6,10 @@ #include "../common/TracyYield.hpp" +#if PLATFORM_WINDOWS +extern void CheckInstructionSet(); +#endif + namespace tracy { @@ -18,6 +22,10 @@ tracy_no_inline static void InitRpmallocPlumbing() const auto done = RpInitDone.load( std::memory_order_acquire ); if( !done ) { +#if PLATFORM_WINDOWS + // Check instruction set before executing any code (Tracy init static vars before others) + CheckInstructionSet(); +#endif int expected = 0; while( !RpInitLock.compare_exchange_weak( expected, 1, std::memory_order_release, std::memory_order_relaxed ) ) { expected = 0; YieldThread(); } const auto done = RpInitDone.load( std::memory_order_acquire ); From 38a48cf1d4de3db84f7d5416bd2bd951ba9c3883 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 27 Oct 2025 16:48:52 +0100 Subject: [PATCH 037/207] Update build number --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 284bc0652..9b7c32c8b 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 11, "Revision": 0, - "Build": 6801 + "Build": 6802 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.", From 22515c37b57733a7ae21b097160f599301d084ba Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 27 Oct 2025 22:38:47 +0100 Subject: [PATCH 038/207] Fix crash regression in D3D12 UAV bindings code --- Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 64c384447..a26584486 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -491,7 +491,7 @@ void GPUContextDX12::flushUAVs() ASSERT(uaCount <= GPU_MAX_UA_BINDED); // Fill table with source descriptors - DxShaderHeader& header = _currentCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header; + DxShaderHeader& header = _isCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header; D3D12_CPU_DESCRIPTOR_HANDLE srcDescriptorRangeStarts[GPU_MAX_UA_BINDED]; for (uint32 i = 0; i < uaCount; i++) { From 5a587a858267b331784e992308670f64a0afcf31 Mon Sep 17 00:00:00 2001 From: Mofasa Date: Tue, 28 Oct 2025 15:09:15 +0800 Subject: [PATCH 039/207] Update HintPaths for project references Fixed error CS0234: The type or namespace name 'C odeAnalysis' does not exist in the namespace 'Microsoft' --- Source/Tools/Flax.Build/Flax.Build.csproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Tools/Flax.Build/Flax.Build.csproj b/Source/Tools/Flax.Build/Flax.Build.csproj index d19d931ce..4b22d6924 100644 --- a/Source/Tools/Flax.Build/Flax.Build.csproj +++ b/Source/Tools/Flax.Build/Flax.Build.csproj @@ -37,22 +37,22 @@ - ..\..\..\Source\Platforms\DotNet\Ionic.Zip.Reduced.dll + ..\..\Platforms\DotNet\Ionic.Zip.Reduced.dll - ..\..\..\Source\Platforms\DotNet\System.Text.Encoding.CodePages.dll + ..\..\Platforms\DotNet\System.Text.Encoding.CodePages.dll - ..\..\..\Source\Platforms\DotNet\Mono.Cecil.dll + ..\..\Platforms\DotNet\Mono.Cecil.dll - ..\..\..\Source\Platforms\DotNet\Microsoft.VisualStudio.Setup.Configuration.Interop.dll + ..\..\Platforms\DotNet\Microsoft.VisualStudio.Setup.Configuration.Interop.dll - ..\..\..\Source\Platforms\DotNet\Microsoft.CodeAnalysis.CSharp.dll + ..\..\Platforms\DotNet\Microsoft.CodeAnalysis.CSharp.dll - ..\..\..\Source\Platforms\DotNet\Microsoft.CodeAnalysis.dll + ..\..\Platforms\DotNet\Microsoft.CodeAnalysis.dll From 0ca60062d9d31dfe675f7459dc2cf97edae1e0d2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 28 Oct 2025 16:17:48 +0100 Subject: [PATCH 040/207] Fix crash on leftover physic contact collision processing --- Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 8d61e9ba5..18929ab73 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -1969,6 +1969,7 @@ void PhysicsBackend::EndSimulateScene(void* scene) scenePhysX->EventsCallback.SendTriggerEvents(); scenePhysX->EventsCallback.SendCollisionEvents(); scenePhysX->EventsCallback.SendJointEvents(); + scenePhysX->EventsCallback.Clear(); } // Clear delta after simulation ended @@ -4466,14 +4467,14 @@ void PhysicsBackend::FlushRequests(void* scene) } if (scenePhysX->RemoveColliders.HasItems()) { - for (int32 i = 0; i < scenePhysX->RemoveColliders.Count(); i++) - scenePhysX->EventsCallback.OnColliderRemoved(scenePhysX->RemoveColliders[i]); + for (PhysicsColliderActor* e : scenePhysX->RemoveColliders) + scenePhysX->EventsCallback.OnColliderRemoved(e); scenePhysX->RemoveColliders.Clear(); } if (scenePhysX->RemoveJoints.HasItems()) { - for (int32 i = 0; i < scenePhysX->RemoveJoints.Count(); i++) - scenePhysX->EventsCallback.OnJointRemoved(scenePhysX->RemoveJoints[i]); + for (Joint* e : scenePhysX->RemoveJoints) + scenePhysX->EventsCallback.OnJointRemoved(e); scenePhysX->RemoveJoints.Clear(); } From 802e2ac7c2236dce547d41ecec1ef7c6515b34ad Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 28 Oct 2025 21:25:30 +0100 Subject: [PATCH 041/207] Fix compiling `stb` texture tool on Windows --- Source/Engine/Tools/TextureTool/TextureTool.stb.cpp | 2 ++ Source/ThirdParty/detex/detex.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp index b88bc4ef3..7c74ef3f2 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp @@ -65,6 +65,8 @@ #endif +#undef MessageBox + static void stbWrite(void* context, void* data, int size) { auto file = (FileWriteStream*)context; diff --git a/Source/ThirdParty/detex/detex.h b/Source/ThirdParty/detex/detex.h index adbeac512..ec61d7495 100644 --- a/Source/ThirdParty/detex/detex.h +++ b/Source/ThirdParty/detex/detex.h @@ -72,7 +72,11 @@ __BEGIN_DECLS #include #include +#if defined(_MSC_VER) +#define DETEX_INLINE_ONLY __forceinline +#else #define DETEX_INLINE_ONLY __attribute__((always_inline)) inline +#endif #define DETEX_RESTRICT __restrict /* Maximum uncompressed block size in bytes. */ From 70c9dd6608f4dfe8e5c3d0ea02e6a69da12bfde5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 28 Oct 2025 21:36:28 +0100 Subject: [PATCH 042/207] Fix eye adaptation in Manual mode --- Source/Engine/Renderer/EyeAdaptationPass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Renderer/EyeAdaptationPass.cpp b/Source/Engine/Renderer/EyeAdaptationPass.cpp index 3cf44af1b..b539cb212 100644 --- a/Source/Engine/Renderer/EyeAdaptationPass.cpp +++ b/Source/Engine/Renderer/EyeAdaptationPass.cpp @@ -98,6 +98,7 @@ void EyeAdaptationPass::Render(RenderContext& renderContext, GPUTexture* colorBu if (mode == EyeAdaptationMode::Manual) { // Apply fixed manual exposure + context->ResetSR(); context->SetRenderTarget(*colorBuffer); context->SetViewportAndScissors((float)colorBuffer->Width(), (float)colorBuffer->Height()); context->SetState(_psManual); From c4fcaa999c421802387351c362ef14ada48a6efd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 28 Oct 2025 22:23:15 +0100 Subject: [PATCH 043/207] Fix asset thumbnails to be stable with FXAA instead of TAA --- Source/Editor/Content/Proxy/AssetProxy.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Editor/Content/Proxy/AssetProxy.cs b/Source/Editor/Content/Proxy/AssetProxy.cs index b96b90383..06de5369a 100644 --- a/Source/Editor/Content/Proxy/AssetProxy.cs +++ b/Source/Editor/Content/Proxy/AssetProxy.cs @@ -130,6 +130,11 @@ namespace FlaxEditor.Content eyeAdaptation.Mode = EyeAdaptationMode.None; eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode; preview.PostFxVolume.EyeAdaptation = eyeAdaptation; + + var antiAliasing = preview.PostFxVolume.AntiAliasing; + antiAliasing.Mode = AntialiasingMode.FastApproximateAntialiasing; + antiAliasing.OverrideFlags |= AntiAliasingSettingsOverride.Mode; + preview.PostFxVolume.AntiAliasing = antiAliasing; } } } From 05a8c841da9067bed66f09a7ad1bfc8cf27e8b9f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 28 Oct 2025 23:19:51 +0100 Subject: [PATCH 044/207] Fix color grading lut to be refreshed when shader gets reloaded --- Content/Shaders/ColorGrading.flax | 4 ++-- Source/Engine/Renderer/ColorGradingPass.cpp | 20 ++++++++++++++++++++ Source/Engine/Renderer/ColorGradingPass.h | 7 ++----- Source/Shaders/ColorGrading.shader | 7 +++---- Source/Shaders/GammaCorrectionCommon.hlsl | 7 ++----- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/Content/Shaders/ColorGrading.flax b/Content/Shaders/ColorGrading.flax index 15f723620..716e18593 100644 --- a/Content/Shaders/ColorGrading.flax +++ b/Content/Shaders/ColorGrading.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ad1d9597dd930f0b63358dbe8326d131661f9cc2251181eeec0cb44ffc530d7a -size 12311 +oid sha256:bce57f0ccf6d808985f4d79cc4e15d85cae999bee598d8e93ff5b1b126bc42b8 +size 12310 diff --git a/Source/Engine/Renderer/ColorGradingPass.cpp b/Source/Engine/Renderer/ColorGradingPass.cpp index bbb45ef4c..d6e164622 100644 --- a/Source/Engine/Renderer/ColorGradingPass.cpp +++ b/Source/Engine/Renderer/ColorGradingPass.cpp @@ -47,6 +47,9 @@ public: Data CachedData; ToneMappingMode Mode = ToneMappingMode::None; Texture* LutTexture = nullptr; +#if COMPILE_WITH_DEV_ENV + uint64 FrameRendered = 0; +#endif ~ColorGradingCustomBuffer() { @@ -54,6 +57,17 @@ public: } }; +#if COMPILE_WITH_DEV_ENV + +void ColorGradingPass::OnShaderReloading(Asset* obj) +{ + _psLut.Release(); + invalidateResources(); + _reloadedFrame = Engine::FrameCount; +} + +#endif + String ColorGradingPass::ToString() const { return TEXT("ColorGradingPass"); @@ -194,6 +208,9 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) // Check if LUT parameter hasn't been changed since the last time if (Platform::MemoryCompare(&colorGradingBuffer.CachedData , &data, sizeof(Data)) == 0 && colorGradingBuffer.Mode == toneMapping.Mode && +#if COMPILE_WITH_DEV_ENV + colorGradingBuffer.FrameRendered > _reloadedFrame && +#endif Engine::FrameCount > 30 && // Skip caching when engine is starting TODO: find why this hack is needed colorGradingBuffer.LutTexture == lutTexture) { @@ -203,6 +220,9 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) colorGradingBuffer.CachedData = data; colorGradingBuffer.Mode = toneMapping.Mode; colorGradingBuffer.LutTexture = lutTexture; +#if COMPILE_WITH_DEV_ENV + colorGradingBuffer.FrameRendered = Engine::FrameCount; +#endif // Render LUT PROFILE_GPU("Color Grading LUT"); diff --git a/Source/Engine/Renderer/ColorGradingPass.h b/Source/Engine/Renderer/ColorGradingPass.h index 612940e76..dd17e7786 100644 --- a/Source/Engine/Renderer/ColorGradingPass.h +++ b/Source/Engine/Renderer/ColorGradingPass.h @@ -25,11 +25,8 @@ public: private: #if COMPILE_WITH_DEV_ENV - void OnShaderReloading(Asset* obj) - { - _psLut.Release(); - invalidateResources(); - } + uint64 _reloadedFrame = 0; + void OnShaderReloading(Asset* obj); #endif public: diff --git a/Source/Shaders/ColorGrading.shader b/Source/Shaders/ColorGrading.shader index 9d1d71697..fe2ca4c91 100644 --- a/Source/Shaders/ColorGrading.shader +++ b/Source/Shaders/ColorGrading.shader @@ -164,16 +164,14 @@ float3 TonemapACES(float3 linearColor) // The code was originally written by Stephen Hill (@self_shadow). // sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT - static const float3x3 ACESInputMat = - { + static const float3x3 ACESInputMat = { {0.59719, 0.35458, 0.04823}, {0.07600, 0.90834, 0.01566}, {0.02840, 0.13383, 0.83777} }; // ODT_SAT => XYZ => D60_2_D65 => sRGB - static const float3x3 ACESOutputMat = - { + static const float3x3 ACESOutputMat = { { 1.60475, -0.53108, -0.07367}, {-0.10208, 1.10813, -0.00605}, {-0.00327, -0.07276, 1.07602} @@ -188,6 +186,7 @@ float3 TonemapACES(float3 linearColor) color = a / b; color = mul(ACESOutputMat, color); + return saturate(color); } diff --git a/Source/Shaders/GammaCorrectionCommon.hlsl b/Source/Shaders/GammaCorrectionCommon.hlsl index b687f9166..e410400ad 100644 --- a/Source/Shaders/GammaCorrectionCommon.hlsl +++ b/Source/Shaders/GammaCorrectionCommon.hlsl @@ -37,7 +37,7 @@ float4 FastTonemapInvert(float4 c) return float4(FastTonemapInvert(c.rgb), c.a); } -float LinearToSrgbChannel(float linearColor) +float LinearToSrgb(float linearColor) { if (linearColor < 0.00313067) return linearColor * 12.92; @@ -46,10 +46,7 @@ float LinearToSrgbChannel(float linearColor) float3 LinearToSrgb(float3 linearColor) { - return float3( - LinearToSrgbChannel(linearColor.r), - LinearToSrgbChannel(linearColor.g), - LinearToSrgbChannel(linearColor.b)); + return float3(LinearToSrgb(linearColor.r), LinearToSrgb(linearColor.g), LinearToSrgb(linearColor.b)); } float3 sRGBToLinear(float3 color) From 2c8c9b163734d99f53ff24bc929b6e48f482b2b0 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 29 Oct 2025 08:26:39 +0200 Subject: [PATCH 045/207] Fix .NET version preprocessor definitions --- Source/Engine/Scripting/Scripting.Build.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/Scripting.Build.cs b/Source/Engine/Scripting/Scripting.Build.cs index d15b460c9..206513259 100644 --- a/Source/Engine/Scripting/Scripting.Build.cs +++ b/Source/Engine/Scripting/Scripting.Build.cs @@ -32,7 +32,7 @@ public class Scripting : EngineModule options.ScriptingAPI.Defines.Add("NET"); AddFrameworkDefines("NET{0}_{1}", dotnetSdk.Version.Major, 0); // "NET7_0" for (int i = 5; i <= dotnetSdk.Version.Major; i++) - AddFrameworkDefines("NET{0}_{1}_OR_GREATER", dotnetSdk.Version.Major, 0); // "NET7_0_OR_GREATER" + AddFrameworkDefines("NET{0}_{1}_OR_GREATER", i, 0); // "NET7_0_OR_GREATER" options.ScriptingAPI.Defines.Add("NETCOREAPP"); AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 3, 1); // "NETCOREAPP3_1_OR_GREATER" AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 2, 2); From 053e52c91f4d087d41930b3e83095d977f8172af Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 29 Oct 2025 22:18:01 +0100 Subject: [PATCH 046/207] Fix compilation for bindings only without scripting internal host defined --- Source/Engine/Engine/NativeInterop.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 2d8f8db84..209eb54e2 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -425,6 +425,8 @@ namespace FlaxEngine.Interop var fieldOffsetPtr = (IntPtr*)field.FieldHandle.Value; // Pointer to MonoClassField fieldOffsetPtr += 3; // Skip three pointers (type, name, parent_and_flags) return *(int*)fieldOffsetPtr - IntPtr.Size * 2; // Load the value of a pointer (4 bytes, int32), then subtracting 16 bytes from it (2 pointers for vtable and threadsync) +#else + throw new NotImplementedException(); #endif } From 114828adcbbce683498bcdfdc9d7528151fb5667 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 30 Oct 2025 17:12:49 +0100 Subject: [PATCH 047/207] Refactor NetworkReplicator update into separate function for cleaner code --- .../Engine/Networking/NetworkReplicator.cpp | 437 +++++++++--------- 1 file changed, 226 insertions(+), 211 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index fba916891..9fdaf7a97 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -589,7 +589,7 @@ void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkCli msg.WriteNetworkId(objectId); if (NetworkManager::IsClient()) { - NetworkManager::Peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg); + peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg); } else { @@ -598,6 +598,227 @@ void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkCli } } +void SendDespawn(DespawnItem& e) +{ + NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object ID={}", e.Id.ToString()); + NetworkMessageObjectDespawn msgData; + Guid objectId = e.Id; + { + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(objectId, &objectId); + } + auto peer = NetworkManager::Peer; + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteNetworkId(objectId); + BuildCachedTargets(NetworkManager::Clients, e.Targets); + if (NetworkManager::IsClient()) + peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg); + else + peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg, CachedTargets); +} + +void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients) +{ + auto it = Objects.Find(obj->GetID()); + if (it.IsEnd()) + return; + auto& item = it->Item; + const bool isClient = NetworkManager::IsClient(); + + // Skip serialization of objects that none will receive + if (!isClient) + { + BuildCachedTargets(item, targetClients); + if (CachedTargets.Count() == 0) + return; + } + + if (item.AsNetworkObject) + item.AsNetworkObject->OnNetworkSerialize(); + + // Serialize object + NetworkStream* stream = CachedWriteStream; + stream->Initialize(); + const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true); + if (failed) + { + //NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString()); + return; + } + const uint32 size = stream->GetPosition(); + if (size > MAX_uint16) + { + LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16); + return; + } + +#if USE_NETWORK_REPLICATOR_CACHE + // Process replication cache to skip sending object data if it didn't change + if (item.RepCache.Data.Length() == size && + item.RepCache.Mask == targetClients && + Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0) + { + return; + } + item.RepCache.Mask = targetClients; + item.RepCache.Data.Copy(stream->GetBuffer(), size); +#endif + // TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state) + constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable; + + // Send object to clients + NetworkMessageObjectReplicate msgData; + msgData.OwnerFrame = NetworkManager::Frame; + Guid objectId = item.ObjectId, parentId = item.ParentId; + { + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(objectId, &objectId); + IdsRemappingTable.KeyOf(parentId, &parentId); + } + NetworkPeer* peer = NetworkManager::Peer; + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteNetworkId(objectId); + msg.WriteNetworkId(parentId); + msg.WriteNetworkName(obj->GetType().Fullname); + NetworkMessageObjectReplicatePayload msgDataPayload; + msgDataPayload.DataSize = size; + const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid); + const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectReplicatePayload); + const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart) - networkKeyIdWorstCaseSize; + uint32 partsCount = 1; + uint32 dataStart = 0; + uint32 msgDataSize = size; + if (size > msgMaxData) + { + // Send msgMaxData within first message + msgDataSize = msgMaxData; + dataStart += msgMaxData; + + // Send rest of the data in separate parts + partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData); + } + else + dataStart += size; + ASSERT(partsCount <= MAX_uint8); + msgDataPayload.PartsCount = partsCount; + msgDataPayload.PartSize = msgDataSize; + msg.WriteStructure(msgDataPayload); + msg.WriteBytes(stream->GetBuffer(), msgDataSize); + uint32 dataSize = msgDataSize, messageSize = msg.Length; + if (isClient) + peer->EndSendMessage(repChannel, msg); + else + peer->EndSendMessage(repChannel, msg, CachedTargets); + + // Send all other parts + for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) + { + NetworkMessageObjectReplicatePart msgDataPart; + msgDataPart.OwnerFrame = msgData.OwnerFrame; + msgDataPart.DataSize = msgDataPayload.DataSize; + msgDataPart.PartsCount = msgDataPayload.PartsCount; + msgDataPart.PartStart = dataStart; + msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData); + msg = peer->BeginSendMessage(); + msg.WriteStructure(msgDataPart); + msg.WriteNetworkId(objectId); + msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize); + messageSize += msg.Length; + dataSize += msgDataPart.PartSize; + dataStart += msgDataPart.PartSize; + if (isClient) + peer->EndSendMessage(repChannel, msg); + else + peer->EndSendMessage(repChannel, msg, CachedTargets); + } + ASSERT_LOW_LAYER(dataStart == size); + +#if COMPILE_WITH_PROFILER + // Network stats recording + if (NetworkInternal::EnableProfiling) + { + const Pair name(obj->GetTypeHandle(), StringAnsiView::Empty); + auto& profileEvent = NetworkInternal::ProfilerEvents[name]; + profileEvent.Count++; + profileEvent.DataSize += dataSize; + profileEvent.MessageSize += messageSize; + profileEvent.Receivers += isClient ? 1 : CachedTargets.Count(); + } +#endif +} + +void SendRpc(RpcItem& e) +{ + ScriptingObject* obj = e.Object.Get(); + if (!obj) + return; + auto it = Objects.Find(obj->GetID()); + if (it == Objects.End()) + { +#if !BUILD_RELEASE + if (!DespawnedObjects.Contains(obj->GetID())) + LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), String(e.Name.Second), obj->GetID()); +#endif + return; + } + auto& item = it->Item; + const NetworkManagerMode mode = NetworkManager::Mode; + NetworkPeer* peer = NetworkManager::Peer; + + // Send RPC message + //NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}::{} object ID={}", e.Name.First.ToString(), String(e.Name.Second), item.ToString()); + NetworkMessageObjectRpc msgData; + Guid msgObjectId = item.ObjectId; + Guid msgParentId = item.ParentId; + { + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(msgObjectId, &msgObjectId); + IdsRemappingTable.KeyOf(msgParentId, &msgParentId); + } + msgData.ArgsSize = (uint16)e.ArgsData.Length(); + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteNetworkId(msgObjectId); + msg.WriteNetworkId(msgParentId); + msg.WriteNetworkName(obj->GetType().Fullname); + msg.WriteNetworkName(e.Name.First.GetType().Fullname); + msg.WriteNetworkName(e.Name.Second); + msg.WriteBytes(e.ArgsData.Get(), e.ArgsData.Length()); + uint32 dataSize = e.ArgsData.Length(), messageSize = msg.Length, receivers = 0; + NetworkChannelType channel = (NetworkChannelType)e.Info.Channel; + if (e.Info.Server && mode == NetworkManagerMode::Client) + { + // Client -> Server +#if USE_NETWORK_REPLICATOR_LOG + if (e.Targets.Length() != 0) + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString()); +#endif + peer->EndSendMessage(channel, msg); + receivers = 1; + } + else if (e.Info.Client && (mode == NetworkManagerMode::Server || mode == NetworkManagerMode::Host)) + { + // Server -> Client(s) + BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId); + peer->EndSendMessage(channel, msg, CachedTargets); + receivers = CachedTargets.Count(); + } + +#if COMPILE_WITH_PROFILER + // Network stats recording + if (NetworkInternal::EnableProfiling && receivers) + { + auto& profileEvent = NetworkInternal::ProfilerEvents[e.Name]; + profileEvent.Count++; + profileEvent.DataSize += dataSize; + profileEvent.MessageSize += messageSize; + profileEvent.Receivers += receivers; + } +#endif +} + void DeleteNetworkObject(ScriptingObject* obj) { // Remove from the mapping table @@ -1652,9 +1873,6 @@ void NetworkInternal::NetworkReplicatorUpdate() if (Objects.Count() == 0) return; const bool isClient = NetworkManager::IsClient(); - const bool isServer = NetworkManager::IsServer(); - const bool isHost = NetworkManager::IsHost(); - NetworkPeer* peer = NetworkManager::Peer; if (!isClient && NewClients.Count() != 0) { @@ -1694,22 +1912,7 @@ void NetworkInternal::NetworkReplicatorUpdate() PROFILE_CPU_NAMED("DespawnQueue"); for (DespawnItem& e : DespawnQueue) { - // Send despawn message - NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object ID={}", e.Id.ToString()); - NetworkMessageObjectDespawn msgData; - Guid objectId = e.Id; - { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(objectId, &objectId); - } - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteNetworkId(objectId); - BuildCachedTargets(NetworkManager::Clients, e.Targets); - if (isClient) - peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg); - else - peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg, CachedTargets); + SendDespawn(e); } DespawnQueue.Clear(); } @@ -1871,136 +2074,11 @@ void NetworkInternal::NetworkReplicatorUpdate() PROFILE_CPU_NAMED("Replication"); if (CachedWriteStream == nullptr) CachedWriteStream = New(); - NetworkStream* stream = CachedWriteStream; - stream->SenderId = NetworkManager::LocalClientId; + CachedWriteStream->SenderId = NetworkManager::LocalClientId; // TODO: use Job System when replicated objects count is large for (auto& e : CachedReplicationResult->_entries) { - ScriptingObject* obj = e.Object; - auto it = Objects.Find(obj->GetID()); - if (it.IsEnd()) - continue; - auto& item = it->Item; - - // Skip serialization of objects that none will receive - if (!isClient) - { - BuildCachedTargets(item, e.TargetClients); - if (CachedTargets.Count() == 0) - continue; - } - - if (item.AsNetworkObject) - item.AsNetworkObject->OnNetworkSerialize(); - - // Serialize object - stream->Initialize(); - const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true); - if (failed) - { - //NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString()); - continue; - } - const uint32 size = stream->GetPosition(); - if (size > MAX_uint16) - { - LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16); - continue; - } - -#if USE_NETWORK_REPLICATOR_CACHE - // Process replication cache to skip sending object data if it didn't change - if (item.RepCache.Data.Length() == size && - item.RepCache.Mask == e.TargetClients && - Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0) - { - continue; - } - item.RepCache.Mask = e.TargetClients; - item.RepCache.Data.Copy(stream->GetBuffer(), size); -#endif - // TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state) - constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable; - - // Send object to clients - NetworkMessageObjectReplicate msgData; - msgData.OwnerFrame = NetworkManager::Frame; - Guid objectId = item.ObjectId, parentId = item.ParentId; - { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(objectId, &objectId); - IdsRemappingTable.KeyOf(parentId, &parentId); - } - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteNetworkId(objectId); - msg.WriteNetworkId(parentId); - msg.WriteNetworkName(obj->GetType().Fullname); - NetworkMessageObjectReplicatePayload msgDataPayload; - msgDataPayload.DataSize = size; - const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid); - const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectReplicatePayload); - const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart) - networkKeyIdWorstCaseSize; - uint32 partsCount = 1; - uint32 dataStart = 0; - uint32 msgDataSize = size; - if (size > msgMaxData) - { - // Send msgMaxData within first message - msgDataSize = msgMaxData; - dataStart += msgMaxData; - - // Send rest of the data in separate parts - partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData); - } - else - dataStart += size; - ASSERT(partsCount <= MAX_uint8); - msgDataPayload.PartsCount = partsCount; - msgDataPayload.PartSize = msgDataSize; - msg.WriteStructure(msgDataPayload); - msg.WriteBytes(stream->GetBuffer(), msgDataSize); - uint32 dataSize = msgDataSize, messageSize = msg.Length; - if (isClient) - peer->EndSendMessage(repChannel, msg); - else - peer->EndSendMessage(repChannel, msg, CachedTargets); - - // Send all other parts - for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) - { - NetworkMessageObjectReplicatePart msgDataPart; - msgDataPart.OwnerFrame = msgData.OwnerFrame; - msgDataPart.DataSize = msgDataPayload.DataSize; - msgDataPart.PartsCount = msgDataPayload.PartsCount; - msgDataPart.PartStart = dataStart; - msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData); - msg = peer->BeginSendMessage(); - msg.WriteStructure(msgDataPart); - msg.WriteNetworkId(objectId); - msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize); - messageSize += msg.Length; - dataSize += msgDataPart.PartSize; - dataStart += msgDataPart.PartSize; - if (isClient) - peer->EndSendMessage(repChannel, msg); - else - peer->EndSendMessage(repChannel, msg, CachedTargets); - } - ASSERT_LOW_LAYER(dataStart == size); - -#if COMPILE_WITH_PROFILER - // Network stats recording - if (EnableProfiling) - { - const Pair name(obj->GetTypeHandle(), StringAnsiView::Empty); - auto& profileEvent = ProfilerEvents[name]; - profileEvent.Count++; - profileEvent.DataSize += dataSize; - profileEvent.MessageSize += messageSize; - profileEvent.Receivers += isClient ? 1 : CachedTargets.Count(); - } -#endif + SendReplication(e.Object, e.TargetClients); } } @@ -2009,70 +2087,7 @@ void NetworkInternal::NetworkReplicatorUpdate() PROFILE_CPU_NAMED("Rpc"); for (auto& e : RpcQueue) { - ScriptingObject* obj = e.Object.Get(); - if (!obj) - continue; - auto it = Objects.Find(obj->GetID()); - if (it == Objects.End()) - { -#if USE_EDITOR || !BUILD_RELEASE - if (!DespawnedObjects.Contains(obj->GetID())) - LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), String(e.Name.Second), obj->GetID()); -#endif - continue; - } - auto& item = it->Item; - - // Send RPC message - //NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}::{} object ID={}", e.Name.First.ToString(), String(e.Name.Second), item.ToString()); - NetworkMessageObjectRpc msgData; - Guid msgObjectId = item.ObjectId; - Guid msgParentId = item.ParentId; - { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(msgObjectId, &msgObjectId); - IdsRemappingTable.KeyOf(msgParentId, &msgParentId); - } - msgData.ArgsSize = (uint16)e.ArgsData.Length(); - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteNetworkId(msgObjectId); - msg.WriteNetworkId(msgParentId); - msg.WriteNetworkName(obj->GetType().Fullname); - msg.WriteNetworkName(e.Name.First.GetType().Fullname); - msg.WriteNetworkName(e.Name.Second); - msg.WriteBytes(e.ArgsData.Get(), e.ArgsData.Length()); - uint32 dataSize = e.ArgsData.Length(), messageSize = msg.Length, receivers = 0; - NetworkChannelType channel = (NetworkChannelType)e.Info.Channel; - if (e.Info.Server && isClient) - { - // Client -> Server -#if USE_NETWORK_REPLICATOR_LOG - if (e.Targets.Length() != 0) - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString()); -#endif - peer->EndSendMessage(channel, msg); - receivers = 1; - } - else if (e.Info.Client && (isServer || isHost)) - { - // Server -> Client(s) - BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId); - peer->EndSendMessage(channel, msg, CachedTargets); - receivers = CachedTargets.Count(); - } - -#if COMPILE_WITH_PROFILER - // Network stats recording - if (EnableProfiling && receivers) - { - auto& profileEvent = ProfilerEvents[e.Name]; - profileEvent.Count++; - profileEvent.DataSize += dataSize; - profileEvent.MessageSize += messageSize; - profileEvent.Receivers += receivers; - } -#endif + SendRpc(e); } RpcQueue.Clear(); } From 35d6e5fd21b5afd8fb2eff071952af7e5c7fe869 Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 30 Oct 2025 20:02:15 +0100 Subject: [PATCH 048/207] fix still being able to open/ close script editor with no fields Introduces "CanOpenClose" to DropPanel. If false, will ignore the user clicking on the header (or the arrow) to open or collapse the panel --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 2 ++ Source/Engine/UI/GUI/Panels/DropPanel.cs | 17 ++++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index 356ae5ee4..954599347 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -914,9 +914,11 @@ namespace FlaxEditor.CustomEditors.Dedicated // Remove drop down arrows and containment lines if no objects in the group if (group.Children.Count == 0) { + group.Panel.Close(); group.Panel.ArrowImageOpened = null; group.Panel.ArrowImageClosed = null; group.Panel.EnableContainmentLines = false; + group.Panel.CanOpenClose = false; } // Scripts arrange bar diff --git a/Source/Engine/UI/GUI/Panels/DropPanel.cs b/Source/Engine/UI/GUI/Panels/DropPanel.cs index 0bfa799c2..de80f9fc5 100644 --- a/Source/Engine/UI/GUI/Panels/DropPanel.cs +++ b/Source/Engine/UI/GUI/Panels/DropPanel.cs @@ -104,13 +104,20 @@ namespace FlaxEngine.GUI /// /// Gets or sets a value indicating whether enable drop down icon drawing. /// - [EditorOrder(1)] + [EditorOrder(2)] public bool EnableDropDownIcon { get; set; } + /// + /// Get or sets a value indicating whether the panel can be opened or closed via the user interacting with the ui. + /// Changing the open/ closed state from code or the Properties panel will still work regardless. + /// + [EditorOrder(1)] + public bool CanOpenClose { get; set; } = true; + /// /// Gets or sets a value indicating whether to enable containment line drawing, /// - [EditorOrder(2)] + [EditorOrder(3)] public bool EnableContainmentLines { get; set; } = false; /// @@ -369,7 +376,7 @@ namespace FlaxEngine.GUI } // Header - var color = _mouseOverHeader ? HeaderColorMouseOver : HeaderColor; + var color = _mouseOverHeader && CanOpenClose ? HeaderColorMouseOver : HeaderColor; if (color.A > 0.0f) { Render2D.FillRectangle(new Rectangle(0, 0, Width, HeaderHeight), color); @@ -510,7 +517,7 @@ namespace FlaxEngine.GUI if (button == MouseButton.Left && _mouseButtonLeftDown) { _mouseButtonLeftDown = false; - if (_mouseOverHeader) + if (_mouseOverHeader && CanOpenClose) Toggle(); return true; } @@ -540,7 +547,7 @@ namespace FlaxEngine.GUI if (button == MouseButton.Left && _mouseButtonLeftDown) { _mouseButtonLeftDown = false; - if (_mouseOverHeader) + if (_mouseOverHeader && CanOpenClose) Toggle(); return true; } From fbae93b53226fd6f9a33fa5fffdfa284e22a0ce9 Mon Sep 17 00:00:00 2001 From: Saas Date: Thu, 30 Oct 2025 20:28:37 +0100 Subject: [PATCH 049/207] update default engine version in issue bug template to 1.11 --- .github/ISSUE_TEMPLATE/1-bug.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/1-bug.yaml b/.github/ISSUE_TEMPLATE/1-bug.yaml index 2e2c65485..a75003f63 100644 --- a/.github/ISSUE_TEMPLATE/1-bug.yaml +++ b/.github/ISSUE_TEMPLATE/1-bug.yaml @@ -31,7 +31,7 @@ body: - '1.10' - '1.11' - master branch - default: 2 + default: 3 validations: required: true - type: textarea From 7c3c4f1a63123d602afe05481d6fc25389146f94 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 30 Oct 2025 22:40:23 +0100 Subject: [PATCH 050/207] Add Network RPC messages splitting for large arguments payloads #3776 --- Source/Engine/Networking/NetworkInternal.h | 4 +- Source/Engine/Networking/NetworkManager.cpp | 1 + .../Engine/Networking/NetworkReplicator.cpp | 354 +++++++++++------- 3 files changed, 213 insertions(+), 146 deletions(-) diff --git a/Source/Engine/Networking/NetworkInternal.h b/Source/Engine/Networking/NetworkInternal.h index 7eb4f7d52..2aade3811 100644 --- a/Source/Engine/Networking/NetworkInternal.h +++ b/Source/Engine/Networking/NetworkInternal.h @@ -8,7 +8,7 @@ #endif // Internal version number of networking implementation. Updated once engine changes serialization or connection rules. -#define NETWORK_PROTOCOL_VERSION 4 +#define NETWORK_PROTOCOL_VERSION 5 // Enables encoding object ids and typenames via uint32 keys rather than full data send. #define USE_NETWORK_KEYS 1 @@ -29,6 +29,7 @@ enum class NetworkMessageIDs : uint8 ObjectDespawn, ObjectRole, ObjectRpc, + ObjectRpcPart, MAX, }; @@ -48,6 +49,7 @@ public: static void OnNetworkMessageObjectDespawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer); static void OnNetworkMessageObjectRole(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer); static void OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer); + static void OnNetworkMessageObjectRpcPart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer); #if COMPILE_WITH_PROFILER diff --git a/Source/Engine/Networking/NetworkManager.cpp b/Source/Engine/Networking/NetworkManager.cpp index 784bbf51e..36ff94c5c 100644 --- a/Source/Engine/Networking/NetworkManager.cpp +++ b/Source/Engine/Networking/NetworkManager.cpp @@ -391,6 +391,7 @@ namespace NetworkInternal::OnNetworkMessageObjectDespawn, NetworkInternal::OnNetworkMessageObjectRole, NetworkInternal::OnNetworkMessageObjectRpc, + NetworkInternal::OnNetworkMessageObjectRpcPart, }; } diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 9fdaf7a97..c584d3526 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -55,14 +55,14 @@ PACK_STRUCT(struct NetworkMessageObjectReplicate uint32 OwnerFrame; }); -PACK_STRUCT(struct NetworkMessageObjectReplicatePayload +PACK_STRUCT(struct NetworkMessageObjectPartPayload { uint16 DataSize; uint16 PartsCount; uint16 PartSize; }); -PACK_STRUCT(struct NetworkMessageObjectReplicatePart +PACK_STRUCT(struct NetworkMessageObjectPart { NetworkMessageIDs ID = NetworkMessageIDs::ObjectReplicatePart; uint32 OwnerFrame; @@ -111,7 +111,7 @@ PACK_STRUCT(struct NetworkMessageObjectRole PACK_STRUCT(struct NetworkMessageObjectRpc { NetworkMessageIDs ID = NetworkMessageIDs::ObjectRpc; - uint16 ArgsSize; + uint32 OwnerFrame; }); struct NetworkReplicatedObject @@ -182,13 +182,14 @@ struct Serializer void* Tags[2]; }; -struct ReplicateItem +struct PartsItem { ScriptingObjectReference Object; Guid ObjectId; uint16 PartsLeft; uint32 OwnerFrame; uint32 OwnerClientId; + const void* Tag; Array Data; }; @@ -220,7 +221,7 @@ struct DespawnItem DataContainer Targets; }; -struct RpcItem +struct RpcSendItem { ScriptingObjectReference Object; NetworkRpcName Name; @@ -233,11 +234,12 @@ namespace { CriticalSection ObjectsLock; HashSet Objects; - Array ReplicationParts; + Array ReplicationParts; + Array RpcParts; Array SpawnParts; Array SpawnQueue; Array DespawnQueue; - Array RpcQueue; + Array RpcQueue; Dictionary IdsRemappingTable; NetworkStream* CachedWriteStream = nullptr; NetworkStream* CachedReadStream = nullptr; @@ -251,6 +253,7 @@ namespace #endif Array DespawnedObjects; uint32 SpawnId = 0; + uint32 RpcId = 0; #if USE_EDITOR void OnScriptsReloading() @@ -505,6 +508,76 @@ void SetupObjectSpawnMessageItem(SpawnItem* e, NetworkMessage& msg) msg.WriteStructure(msgDataItem); } +void SendInParts(NetworkPeer* peer, NetworkChannelType channel, const byte* data, const uint16 dataSize, NetworkMessage& msg, const NetworkRpcName& name, bool toServer, const Guid& objectId, uint32 ownerFrame, NetworkMessageIDs partId) +{ + NetworkMessageObjectPartPayload msgDataPayload; + msgDataPayload.DataSize = dataSize; + const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid); + const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectPartPayload); + const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectPart) - networkKeyIdWorstCaseSize; + uint32 partsCount = 1; + uint32 dataStart = 0; + uint32 msgDataSize = dataSize; + if (dataSize > msgMaxData) + { + // Send msgMaxData within first message + msgDataSize = msgMaxData; + dataStart += msgMaxData; + + // Send rest of the data in separate parts + partsCount += Math::DivideAndRoundUp(dataSize - dataStart, partMaxData); + + // TODO: promote channel to Ordered when using parts? + } + else + dataStart += dataSize; + ASSERT(partsCount <= MAX_uint8); + msgDataPayload.PartsCount = partsCount; + msgDataPayload.PartSize = msgDataSize; + msg.WriteStructure(msgDataPayload); + msg.WriteBytes(data, msgDataSize); + uint32 messageSize = msg.Length; + if (toServer) + peer->EndSendMessage(channel, msg); + else + peer->EndSendMessage(channel, msg, CachedTargets); + + // Send all other parts + for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) + { + NetworkMessageObjectPart msgDataPart; + msgDataPart.ID = partId; + msgDataPart.OwnerFrame = ownerFrame; + msgDataPart.DataSize = msgDataPayload.DataSize; + msgDataPart.PartsCount = msgDataPayload.PartsCount; + msgDataPart.PartStart = dataStart; + msgDataPart.PartSize = Math::Min(dataSize - dataStart, partMaxData); + msg = peer->BeginSendMessage(); + msg.WriteStructure(msgDataPart); + msg.WriteNetworkId(objectId); + msg.WriteBytes(data + msgDataPart.PartStart, msgDataPart.PartSize); + messageSize += msg.Length; + dataStart += msgDataPart.PartSize; + if (toServer) + peer->EndSendMessage(channel, msg); + else + peer->EndSendMessage(channel, msg, CachedTargets); + } + ASSERT_LOW_LAYER(dataStart == dataSize); + +#if COMPILE_WITH_PROFILER + // Network stats recording + if (NetworkInternal::EnableProfiling) + { + auto& profileEvent = NetworkInternal::ProfilerEvents[name]; + profileEvent.Count++; + profileEvent.DataSize += dataSize; + profileEvent.MessageSize += messageSize; + profileEvent.Receivers += toServer ? 1 : CachedTargets.Count(); + } +#endif +} + void SendObjectSpawnMessage(const SpawnGroup& group, const Array& clients) { PROFILE_CPU(); @@ -682,74 +755,11 @@ void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients) msg.WriteNetworkId(objectId); msg.WriteNetworkId(parentId); msg.WriteNetworkName(obj->GetType().Fullname); - NetworkMessageObjectReplicatePayload msgDataPayload; - msgDataPayload.DataSize = size; - const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid); - const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectReplicatePayload); - const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart) - networkKeyIdWorstCaseSize; - uint32 partsCount = 1; - uint32 dataStart = 0; - uint32 msgDataSize = size; - if (size > msgMaxData) - { - // Send msgMaxData within first message - msgDataSize = msgMaxData; - dataStart += msgMaxData; - - // Send rest of the data in separate parts - partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData); - } - else - dataStart += size; - ASSERT(partsCount <= MAX_uint8); - msgDataPayload.PartsCount = partsCount; - msgDataPayload.PartSize = msgDataSize; - msg.WriteStructure(msgDataPayload); - msg.WriteBytes(stream->GetBuffer(), msgDataSize); - uint32 dataSize = msgDataSize, messageSize = msg.Length; - if (isClient) - peer->EndSendMessage(repChannel, msg); - else - peer->EndSendMessage(repChannel, msg, CachedTargets); - - // Send all other parts - for (uint32 partIndex = 1; partIndex < partsCount; partIndex++) - { - NetworkMessageObjectReplicatePart msgDataPart; - msgDataPart.OwnerFrame = msgData.OwnerFrame; - msgDataPart.DataSize = msgDataPayload.DataSize; - msgDataPart.PartsCount = msgDataPayload.PartsCount; - msgDataPart.PartStart = dataStart; - msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData); - msg = peer->BeginSendMessage(); - msg.WriteStructure(msgDataPart); - msg.WriteNetworkId(objectId); - msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize); - messageSize += msg.Length; - dataSize += msgDataPart.PartSize; - dataStart += msgDataPart.PartSize; - if (isClient) - peer->EndSendMessage(repChannel, msg); - else - peer->EndSendMessage(repChannel, msg, CachedTargets); - } - ASSERT_LOW_LAYER(dataStart == size); - -#if COMPILE_WITH_PROFILER - // Network stats recording - if (NetworkInternal::EnableProfiling) - { - const Pair name(obj->GetTypeHandle(), StringAnsiView::Empty); - auto& profileEvent = NetworkInternal::ProfilerEvents[name]; - profileEvent.Count++; - profileEvent.DataSize += dataSize; - profileEvent.MessageSize += messageSize; - profileEvent.Receivers += isClient ? 1 : CachedTargets.Count(); - } -#endif + const NetworkRpcName name(obj->GetTypeHandle(), StringAnsiView::Empty); + SendInParts(peer, repChannel, stream->GetBuffer(), size, msg, name, isClient, objectId, msgData.OwnerFrame, NetworkMessageIDs::ObjectReplicatePart); } -void SendRpc(RpcItem& e) +void SendRpc(RpcSendItem& e) { ScriptingObject* obj = e.Object.Get(); if (!obj) @@ -759,64 +769,60 @@ void SendRpc(RpcItem& e) { #if !BUILD_RELEASE if (!DespawnedObjects.Contains(obj->GetID())) - LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), String(e.Name.Second), obj->GetID()); + LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID()); #endif return; } auto& item = it->Item; + if (e.ArgsData.Length() > MAX_uint16) + { + LOG(Error, "Too much data for object RPC method '{}.{}' on object '{}' ({} bytes provided while limit is {}).", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID(), e.ArgsData.Length(), MAX_uint16); + return; + } const NetworkManagerMode mode = NetworkManager::Mode; NetworkPeer* peer = NetworkManager::Peer; - // Send RPC message - //NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}::{} object ID={}", e.Name.First.ToString(), String(e.Name.Second), item.ToString()); - NetworkMessageObjectRpc msgData; - Guid msgObjectId = item.ObjectId; - Guid msgParentId = item.ParentId; - { - // Remap local client object ids into server ids - IdsRemappingTable.KeyOf(msgObjectId, &msgObjectId); - IdsRemappingTable.KeyOf(msgParentId, &msgParentId); - } - msgData.ArgsSize = (uint16)e.ArgsData.Length(); - NetworkMessage msg = peer->BeginSendMessage(); - msg.WriteStructure(msgData); - msg.WriteNetworkId(msgObjectId); - msg.WriteNetworkId(msgParentId); - msg.WriteNetworkName(obj->GetType().Fullname); - msg.WriteNetworkName(e.Name.First.GetType().Fullname); - msg.WriteNetworkName(e.Name.Second); - msg.WriteBytes(e.ArgsData.Get(), e.ArgsData.Length()); - uint32 dataSize = e.ArgsData.Length(), messageSize = msg.Length, receivers = 0; - NetworkChannelType channel = (NetworkChannelType)e.Info.Channel; + bool toServer; if (e.Info.Server && mode == NetworkManagerMode::Client) { // Client -> Server #if USE_NETWORK_REPLICATOR_LOG if (e.Targets.Length() != 0) - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString()); + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}.{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString()); #endif - peer->EndSendMessage(channel, msg); - receivers = 1; + toServer = true; } else if (e.Info.Client && (mode == NetworkManagerMode::Server || mode == NetworkManagerMode::Host)) { // Server -> Client(s) BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId); - peer->EndSendMessage(channel, msg, CachedTargets); - receivers = CachedTargets.Count(); + if (CachedTargets.IsEmpty()) + return; + toServer = false; } + else + return; -#if COMPILE_WITH_PROFILER - // Network stats recording - if (NetworkInternal::EnableProfiling && receivers) + // Send RPC message + //NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}.{} object ID={}", e.Name.First.ToString(), e.Name.Second.ToString(), item.ToString()); + NetworkMessageObjectRpc msgData; + msgData.OwnerFrame = ++RpcId; + Guid objectId = item.ObjectId; + Guid parentId = item.ParentId; { - auto& profileEvent = NetworkInternal::ProfilerEvents[e.Name]; - profileEvent.Count++; - profileEvent.DataSize += dataSize; - profileEvent.MessageSize += messageSize; - profileEvent.Receivers += receivers; + // Remap local client object ids into server ids + IdsRemappingTable.KeyOf(objectId, &objectId); + IdsRemappingTable.KeyOf(parentId, &parentId); } -#endif + NetworkMessage msg = peer->BeginSendMessage(); + msg.WriteStructure(msgData); + msg.WriteNetworkId(objectId); + msg.WriteNetworkId(parentId); + msg.WriteNetworkName(obj->GetType().Fullname); + msg.WriteNetworkName(e.Name.First.GetType().Fullname); + msg.WriteNetworkName(e.Name.Second); + NetworkChannelType channel = (NetworkChannelType)e.Info.Channel; + SendInParts(peer, channel, e.ArgsData.Get(), e.ArgsData.Length(), msg, e.Name, toServer, objectId, msgData.OwnerFrame, NetworkMessageIDs::ObjectRpcPart); } void DeleteNetworkObject(ScriptingObject* obj) @@ -929,38 +935,43 @@ FORCE_INLINE void DirtyObjectImpl(NetworkReplicatedObject& item, ScriptingObject Hierarchy->DirtyObject(obj); } -ReplicateItem* AddObjectReplicateItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId) +PartsItem* AddPartsItem(Array& items, NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId) { // Reuse or add part item - ReplicateItem* replicateItem = nullptr; - for (auto& e : ReplicationParts) + PartsItem* item = nullptr; + for (auto& e : items) { if (e.OwnerFrame == ownerFrame && e.Data.Count() == dataSize && e.ObjectId == objectId) { // Reuse - replicateItem = &e; + item = &e; break; } } - if (!replicateItem) + if (!item) { // Add - replicateItem = &ReplicationParts.AddOne(); - replicateItem->ObjectId = objectId; - replicateItem->PartsLeft = partsCount; - replicateItem->OwnerFrame = ownerFrame; - replicateItem->OwnerClientId = senderClientId; - replicateItem->Data.Resize(dataSize); + item = &items.AddOne(); + item->ObjectId = objectId; + item->PartsLeft = partsCount; + item->OwnerFrame = ownerFrame; + item->OwnerClientId = senderClientId; + item->Data.Resize(dataSize); } // Copy part data - ASSERT(replicateItem->PartsLeft > 0); - replicateItem->PartsLeft--; - ASSERT(partStart + partSize <= replicateItem->Data.Count()); + ASSERT(item->PartsLeft > 0); + item->PartsLeft--; + ASSERT(partStart + partSize <= item->Data.Count()); const void* partData = event.Message.SkipBytes(partSize); - Platform::MemoryCopy(replicateItem->Data.Get() + partStart, partData, partSize); + Platform::MemoryCopy(item->Data.Get() + partStart, partData, partSize); - return replicateItem; + return item; +} + +FORCE_INLINE PartsItem* AddObjectReplicateItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId) +{ + return AddPartsItem(ReplicationParts, event, ownerFrame, partsCount, dataSize, objectId, partStart, partSize, senderClientId); } void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, byte* data, uint32 dataSize, uint32 senderClientId) @@ -1008,6 +1019,24 @@ void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, b DirtyObjectImpl(item, obj); } +FORCE_INLINE PartsItem* AddObjectRpcItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId) +{ + return AddPartsItem(RpcParts, event, ownerFrame, partsCount, dataSize, objectId, partStart, partSize, senderClientId); +} + +void InvokeObjectRpc(const NetworkRpcInfo* info, byte* data, uint32 dataSize, uint32 senderClientId, ScriptingObject* obj) +{ + // Setup message reading stream + if (CachedReadStream == nullptr) + CachedReadStream = New(); + NetworkStream* stream = CachedReadStream; + stream->SenderId = senderClientId; + stream->Initialize(data, dataSize); + + // Execute RPC + info->Execute(obj, stream, info->Tag); +} + void InvokeObjectSpawn(const NetworkMessageObjectSpawn& msgData, const Guid& prefabId, const NetworkMessageObjectSpawnItem* msgDataItems) { ScopeLock lock(ObjectsLock); @@ -2033,6 +2062,7 @@ void NetworkInternal::NetworkReplicatorUpdate() } } + // TODO: remove items from RpcParts after some TTL to reduce memory usage // TODO: remove items from SpawnParts after some TTL to reduce memory usage // Replicate all owned networked objects with other clients or server @@ -2100,7 +2130,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo { PROFILE_CPU(); NetworkMessageObjectReplicate msgData; - NetworkMessageObjectReplicatePayload msgDataPayload; + NetworkMessageObjectPartPayload msgDataPayload; Guid objectId, parentId; StringAnsiView objectTypeName; event.Message.ReadStructure(msgData); @@ -2110,7 +2140,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo event.Message.ReadStructure(msgDataPayload); ScopeLock lock(ObjectsLock); if (DespawnedObjects.Contains(objectId)) - return; // Skip replicating not-existing objects + return; // Skip replicating non-existing objects NetworkReplicatedObject* e = ResolveObject(objectId, parentId, objectTypeName); if (!e) return; @@ -2129,7 +2159,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo else { // Add to replication from multiple parts - ReplicateItem* replicateItem = AddObjectReplicateItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId); + PartsItem* replicateItem = AddObjectReplicateItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId); replicateItem->Object = e->Object; } } @@ -2137,13 +2167,13 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo void NetworkInternal::OnNetworkMessageObjectReplicatePart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) { PROFILE_CPU(); - NetworkMessageObjectReplicatePart msgData; + NetworkMessageObjectPart msgData; Guid objectId; event.Message.ReadStructure(msgData); event.Message.ReadNetworkId(objectId); ScopeLock lock(ObjectsLock); if (DespawnedObjects.Contains(objectId)) - return; // Skip replicating not-existing objects + return; // Skip replicating non-existing objects const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId; AddObjectReplicateItem(event, msgData.OwnerFrame, msgData.PartsCount, msgData.DataSize, objectId, msgData.PartStart, msgData.PartSize, senderClientId); @@ -2303,14 +2333,16 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie { PROFILE_CPU(); NetworkMessageObjectRpc msgData; - Guid msgObjectId, msgParentId; + NetworkMessageObjectPartPayload msgDataPayload; + Guid objectId, parentId; StringAnsiView objectTypeName, rpcTypeName, rpcName; event.Message.ReadStructure(msgData); - event.Message.ReadNetworkId(msgObjectId); - event.Message.ReadNetworkId(msgParentId); + event.Message.ReadNetworkId(objectId); + event.Message.ReadNetworkId(parentId); event.Message.ReadNetworkName(objectTypeName); event.Message.ReadNetworkName(rpcTypeName); event.Message.ReadNetworkName(rpcName); + event.Message.ReadStructure(msgDataPayload); ScopeLock lock(ObjectsLock); // Find RPC info @@ -2320,11 +2352,11 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(name); if (!info) { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(rpcTypeName), String(rpcName), msgObjectId); + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(rpcTypeName), String(rpcName), objectId); return; } - NetworkReplicatedObject* e = ResolveObject(msgObjectId, msgParentId, objectTypeName); + NetworkReplicatedObject* e = ResolveObject(objectId, parentId, objectTypeName); if (e) { auto& item = *e; @@ -2344,18 +2376,50 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie return; } - // Setup message reading stream - if (CachedReadStream == nullptr) - CachedReadStream = New(); - NetworkStream* stream = CachedReadStream; - stream->SenderId = client ? client->ClientId : NetworkManager::ServerClientId; - stream->Initialize(event.Message.Buffer + event.Message.Position, msgData.ArgsSize); - - // Execute RPC - info->Execute(obj, stream, info->Tag); + const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId; + if (msgDataPayload.PartsCount == 1) + { + // Call RPC + InvokeObjectRpc(info, event.Message.Buffer + event.Message.Position, msgDataPayload.DataSize, senderClientId, obj); + } + else + { + // Add to RPC from multiple parts + PartsItem* rpcItem = AddObjectRpcItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId); + rpcItem->Object = e->Object; + rpcItem->Tag = info; + } } else if (info->Channel != static_cast(NetworkChannelType::Unreliable) && info->Channel != static_cast(NetworkChannelType::UnreliableOrdered)) { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", msgObjectId, String(rpcTypeName), String(rpcName)); + NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", objectId, String(rpcTypeName), String(rpcName)); + } +} + +void NetworkInternal::OnNetworkMessageObjectRpcPart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer) +{ + PROFILE_CPU(); + NetworkMessageObjectPart msgData; + Guid objectId; + event.Message.ReadStructure(msgData); + event.Message.ReadNetworkId(objectId); + ScopeLock lock(ObjectsLock); + if (DespawnedObjects.Contains(objectId)) + return; // Skip replicating non-existing objects + + const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId; + PartsItem* rpcItem = AddObjectRpcItem(event, msgData.OwnerFrame, msgData.PartsCount, msgData.DataSize, objectId, msgData.PartStart, msgData.PartSize, senderClientId); + if (rpcItem && rpcItem->PartsLeft == 0) + { + // Got all parts so invoke RPC + ScriptingObject* obj = rpcItem->Object.Get(); + if (obj) + { + InvokeObjectRpc((const NetworkRpcInfo*)rpcItem->Tag, rpcItem->Data.Get(), rpcItem->Data.Count(), rpcItem->OwnerClientId, obj); + } + + // Remove item + int32 partIndex = (int32)((RpcParts.Get() - rpcItem) / sizeof(rpcItem)); + RpcParts.RemoveAt(partIndex); } } From 1f592ba1a1413c8b71db9ec8c97d04f11126a9ef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 30 Oct 2025 23:36:51 +0100 Subject: [PATCH 051/207] Fix game viewport scaling when using custom aspect or resolution to simulate actual logic #3699 --- Source/Editor/Editor.cs | 2 +- Source/Editor/Windows/GameWindow.cs | 142 +++++++++++++++++--- Source/Engine/UI/GUI/RenderOutputControl.cs | 17 +-- 3 files changed, 127 insertions(+), 34 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 85e579ec9..66328926d 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1587,7 +1587,7 @@ namespace FlaxEditor if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null) result = dockedTo.SelectedTab.Size; else - result = gameWin.Viewport.Size; + result = gameWin.Viewport.ContentSize; result *= root.DpiScale; result = Float2.Round(result); diff --git a/Source/Editor/Windows/GameWindow.cs b/Source/Editor/Windows/GameWindow.cs index 0088ddd71..872e78509 100644 --- a/Source/Editor/Windows/GameWindow.cs +++ b/Source/Editor/Windows/GameWindow.cs @@ -10,17 +10,117 @@ using FlaxEditor.Modules; using FlaxEditor.Options; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEngine.Json; namespace FlaxEditor.Windows { + /// + /// Render output control with content scaling support. + /// + public class ScaledRenderOutputControl : RenderOutputControl + { + /// + /// Custom scale. + /// + public float ContentScale = 1.0f; + + /// + /// Actual bounds size for content (incl. scale). + /// + public Float2 ContentSize => Size / ContentScale; + + /// + public ScaledRenderOutputControl(SceneRenderTask task) + : base(task) + { + } + + /// + public override void Draw() + { + DrawSelf(); + + // Draw children with scale + var scaling = new Float3(ContentScale, ContentScale, 1); + Matrix3x3.Scaling(ref scaling, out Matrix3x3 scale); + Render2D.PushTransform(scale); + if (ClipChildren) + { + GetDesireClientArea(out var clientArea); + Render2D.PushClip(ref clientArea); + DrawChildren(); + Render2D.PopClip(); + } + else + { + DrawChildren(); + } + + Render2D.PopTransform(); + } + + /// + public override void GetDesireClientArea(out Rectangle rect) + { + // Scale the area for the client controls + rect = new Rectangle(Float2.Zero, Size / ContentScale); + } + + /// + public override bool IntersectsContent(ref Float2 locationParent, out Float2 location) + { + // Skip local PointFromParent but use base code + location = base.PointFromParent(ref locationParent); + return ContainsPoint(ref location); + } + + /// + public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation) + { + location /= ContentScale; + return base.IntersectsChildContent(child, location, out childSpaceLocation); + } + + /// + public override bool ContainsPoint(ref Float2 location, bool precise = false) + { + if (precise) // Ignore as utility-only element + return false; + return base.ContainsPoint(ref location, precise); + } + + /// + public override bool RayCast(ref Float2 location, out Control hit) + { + var p = location / ContentScale; + if (RayCastChildren(ref p, out hit)) + return true; + return base.RayCast(ref location, out hit); + } + + /// + public override Float2 PointToParent(ref Float2 location) + { + var result = base.PointToParent(ref location); + result *= ContentScale; + return result; + } + + /// + public override Float2 PointFromParent(ref Float2 location) + { + var result = base.PointFromParent(ref location); + result /= ContentScale; + return result; + } + } + /// /// Provides in-editor play mode simulation. /// /// public class GameWindow : EditorWindow { - private readonly RenderOutputControl _viewport; + private readonly ScaledRenderOutputControl _viewport; private readonly GameRoot _guiRoot; private bool _showGUI = true, _editGUI = true; private bool _showDebugDraw = false; @@ -77,7 +177,7 @@ namespace FlaxEditor.Windows /// /// Gets the viewport. /// - public RenderOutputControl Viewport => _viewport; + public ScaledRenderOutputControl Viewport => _viewport; /// /// Gets or sets a value indicating whether show game GUI in the view or keep it hidden. @@ -295,7 +395,7 @@ namespace FlaxEditor.Windows var task = MainRenderTask.Instance; // Setup viewport - _viewport = new RenderOutputControl(task) + _viewport = new ScaledRenderOutputControl(task) { AnchorPreset = AnchorPresets.StretchAll, Offsets = Margin.Zero, @@ -396,11 +496,8 @@ namespace FlaxEditor.Windows { if (v == null) return; - if (v.Size.Y <= 0 || v.Size.X <= 0) - { return; - } if (string.Equals(v.Label, "Free Aspect", StringComparison.Ordinal) && v.Size == new Int2(1, 1)) { @@ -448,15 +545,7 @@ namespace FlaxEditor.Windows private void ResizeViewport() { - if (!_freeAspect) - { - _windowAspectRatio = Width / Height; - } - else - { - _windowAspectRatio = 1; - } - + _windowAspectRatio = _freeAspect ? 1 : Width / Height; var scaleWidth = _viewportAspectRatio / _windowAspectRatio; var scaleHeight = _windowAspectRatio / _viewportAspectRatio; @@ -468,6 +557,24 @@ namespace FlaxEditor.Windows { _viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height); } + + if (_viewport.KeepAspectRatio) + { + var resolution = _viewport.CustomResolution.HasValue ? (Float2)_viewport.CustomResolution.Value : Size; + if (scaleHeight < 1) + { + _viewport.ContentScale = _viewport.Width / resolution.X; + } + else + { + _viewport.ContentScale = _viewport.Height / resolution.Y; + } + } + else + { + _viewport.ContentScale = 1; + } + _viewport.SyncBackbufferSize(); PerformLayout(); } @@ -907,6 +1014,7 @@ namespace FlaxEditor.Windows return child.OnNavigate(direction, Float2.Zero, this, visited); } } + return null; } @@ -957,7 +1065,7 @@ namespace FlaxEditor.Windows else _defaultScaleActiveIndex = 0; } - + if (_customScaleActiveIndex != -1) { var options = Editor.UI.CustomViewportScaleOptions; diff --git a/Source/Engine/UI/GUI/RenderOutputControl.cs b/Source/Engine/UI/GUI/RenderOutputControl.cs index 9025cbc04..8dc6707bf 100644 --- a/Source/Engine/UI/GUI/RenderOutputControl.cs +++ b/Source/Engine/UI/GUI/RenderOutputControl.cs @@ -180,7 +180,7 @@ namespace FlaxEngine.GUI } /// - public override void Draw() + public override void DrawSelf() { var bounds = new Rectangle(Float2.Zero, Size); @@ -205,21 +205,6 @@ namespace FlaxEngine.GUI Render2D.DrawTexture(buffer, bounds, color); else Render2D.FillRectangle(bounds, Color.Black); - - // Push clipping mask - if (ClipChildren) - { - GetDesireClientArea(out var clientArea); - Render2D.PushClip(ref clientArea); - } - - DrawChildren(); - - // Pop clipping mask - if (ClipChildren) - { - Render2D.PopClip(); - } } /// From a70d7cf1f9e3589fddda5f22bc4a7bdbbf8aba32 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 30 Oct 2025 23:37:20 +0100 Subject: [PATCH 052/207] Update build number --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 9b7c32c8b..6a37b7a85 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 11, "Revision": 0, - "Build": 6802 + "Build": 6803 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.", From 3fc1895b5658750e23101e6bc8c189fad0868baa Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 18 Oct 2025 04:11:56 +0300 Subject: [PATCH 053/207] Fix compiler error and wrong CPU architecture warnings on WoA --- Source/Engine/Scripting/ScriptingType.h | 3 ++ .../Platforms/Windows/WindowsToolchain.cs | 33 +++++++++++-------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Scripting/ScriptingType.h b/Source/Engine/Scripting/ScriptingType.h index b9735a5af..e1fb3dc04 100644 --- a/Source/Engine/Scripting/ScriptingType.h +++ b/Source/Engine/Scripting/ScriptingType.h @@ -5,6 +5,9 @@ #include "Types.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/Guid.h" +#if PLATFORM_ARCH_ARM64 +#include "Engine/Core/Core.h" +#endif class MMethod; class BinaryModule; diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs index fddbdb3de..0c2d44c5d 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchain.cs @@ -19,7 +19,7 @@ namespace Flax.Build /// /// Specifies the minimum CPU architecture type to support (on x86/x64). /// - [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (om x86/x64).")] + [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (on x86/x64).")] public static CpuArchitecture WindowsCpuArch = CpuArchitecture.SSE4_2; // 99.78% support on PC according to Steam Hardware & Software Survey: September 2025 (https://store.steampowered.com/hwsurvey/) } } @@ -76,22 +76,27 @@ namespace Flax.Build.Platforms options.LinkEnv.InputLibraries.Add("oleaut32.lib"); options.LinkEnv.InputLibraries.Add("delayimp.lib"); - if (options.Architecture == TargetArchitecture.ARM64) + options.CompileEnv.CpuArchitecture = Configuration.WindowsCpuArch; + + if (options.Architecture == TargetArchitecture.x64) + { + if (_minVersion.Major <= 7 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX2) + { + // Old Windows had lower support ratio for latest CPU features + options.CompileEnv.CpuArchitecture = CpuArchitecture.AVX; + } + if (_minVersion.Major >= 11 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX) + { + // Windows 11 has hard requirement on SSE4.2 + options.CompileEnv.CpuArchitecture = CpuArchitecture.SSE4_2; + } + } + else if (options.Architecture == TargetArchitecture.ARM64) { options.CompileEnv.PreprocessorDefinitions.Add("USE_SOFT_INTRINSICS"); options.LinkEnv.InputLibraries.Add("softintrin.lib"); - } - - options.CompileEnv.CpuArchitecture = Configuration.WindowsCpuArch; - if (_minVersion.Major <= 7 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX2) - { - // Old Windows had lower support ratio for latest CPU features - options.CompileEnv.CpuArchitecture = CpuArchitecture.AVX; - } - if (_minVersion.Major >= 11 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX) - { - // Windows 11 has hard requirement on SSE4.2 - options.CompileEnv.CpuArchitecture = CpuArchitecture.SSE4_2; + if (options.CompileEnv.CpuArchitecture != CpuArchitecture.None) + options.CompileEnv.CpuArchitecture = CpuArchitecture.NEON; } } From 594c0fb8e7b00e86224c6c48d37a9191386b8ad0 Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 31 Oct 2025 19:23:42 +0100 Subject: [PATCH 054/207] add comment around asset from which asset reference graph originates --- .../Windows/AssetReferencesGraphWindow.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Windows/AssetReferencesGraphWindow.cs b/Source/Editor/Windows/AssetReferencesGraphWindow.cs index bc1437d97..d383d1249 100644 --- a/Source/Editor/Windows/AssetReferencesGraphWindow.cs +++ b/Source/Editor/Windows/AssetReferencesGraphWindow.cs @@ -140,8 +140,9 @@ namespace FlaxEditor.Windows } private string _cacheFolder; - private Guid _assetId; + private AssetItem _item; private Surface _surface; + private AssetNode _rootAssetNode; private Label _loadingLabel; private CancellationTokenSource _token; private Task _task; @@ -163,13 +164,13 @@ namespace FlaxEditor.Windows public AssetReferencesGraphWindow(Editor editor, AssetItem assetItem) : base(editor, false, ScrollBars.None) { - Title = assetItem.ShortName + " References"; + _item = assetItem; + Title = _item.ShortName + " References"; _tempFolder = StringUtils.NormalizePath(Path.GetDirectoryName(Globals.TemporaryFolder)); _cacheFolder = Path.Combine(Globals.ProjectCacheFolder, "References"); if (!Directory.Exists(_cacheFolder)) Directory.CreateDirectory(_cacheFolder); - _assetId = assetItem.ID; _surface = new Surface(this) { AnchorPreset = AnchorPresets.StretchAll, @@ -194,6 +195,10 @@ namespace FlaxEditor.Windows _nodesAssets.Add(assetId); var node = new AssetNode((uint)_nodes.Count + 1, _surface.Context, GraphNodes[0], GraphGroups[0], assetId); _nodes.Add(node); + + if (assetId == _item.ID) + _rootAssetNode = node; + return node; } @@ -392,8 +397,7 @@ namespace FlaxEditor.Windows _nodesAssets = new HashSet(); var searchLevel = 4; // TODO: make it as an option (somewhere in window UI) // TODO: add option to filter assets by type (eg. show only textures as leaf nodes) - var assetNode = SpawnNode(_assetId); - // TODO: add some outline or tint color to the main node + var assetNode = SpawnNode(_item.ID); BuildGraph(assetNode, searchLevel, false); ArrangeGraph(assetNode, false); BuildGraph(assetNode, searchLevel, true); @@ -402,6 +406,13 @@ namespace FlaxEditor.Windows return; _progress = 100.0f; + if (_rootAssetNode != null) + { + var commentRect = _rootAssetNode.EditorBounds; + commentRect.Expand(80f); + _surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green); + } + // Update UI FlaxEngine.Scripting.InvokeOnUpdate(() => { From 1091bc6e2c48c220a0e18d47dab67fa5edc2508d Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 31 Oct 2025 20:47:23 +0100 Subject: [PATCH 055/207] only show comment edit buttons when surface can be edited --- Source/Editor/Surface/SurfaceComment.cs | 31 ++++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index 0138a1b66..10e9fc776 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -214,22 +214,25 @@ namespace FlaxEditor.Surface if (!_isRenaming) Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center); - // Close button - Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - - // Color button - Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - - // Check if is resizing - if (_isResizing) + if (Surface.CanEdit) { - // Draw overlay - Render2D.FillRectangle(_resizeButtonRect, style.Selection); - Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); - } + // Close button + Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); - // Resize button - Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); + // Color button + Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); + + // Check if is resizing + if (_isResizing) + { + // Draw overlay + Render2D.FillRectangle(_resizeButtonRect, style.Selection); + Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder); + } + + // Resize button + Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey); + } // Selection outline if (_isSelected) From 93f12b73d8e318f19a025c20d1c71a2d4c07372b Mon Sep 17 00:00:00 2001 From: Saas Date: Fri, 31 Oct 2025 23:16:37 +0100 Subject: [PATCH 056/207] less code is more better --- Source/Editor/Windows/AssetReferencesGraphWindow.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Windows/AssetReferencesGraphWindow.cs b/Source/Editor/Windows/AssetReferencesGraphWindow.cs index d383d1249..dcaf98462 100644 --- a/Source/Editor/Windows/AssetReferencesGraphWindow.cs +++ b/Source/Editor/Windows/AssetReferencesGraphWindow.cs @@ -142,7 +142,6 @@ namespace FlaxEditor.Windows private string _cacheFolder; private AssetItem _item; private Surface _surface; - private AssetNode _rootAssetNode; private Label _loadingLabel; private CancellationTokenSource _token; private Task _task; @@ -196,9 +195,6 @@ namespace FlaxEditor.Windows var node = new AssetNode((uint)_nodes.Count + 1, _surface.Context, GraphNodes[0], GraphGroups[0], assetId); _nodes.Add(node); - if (assetId == _item.ID) - _rootAssetNode = node; - return node; } @@ -406,12 +402,9 @@ namespace FlaxEditor.Windows return; _progress = 100.0f; - if (_rootAssetNode != null) - { - var commentRect = _rootAssetNode.EditorBounds; - commentRect.Expand(80f); - _surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green); - } + var commentRect = assetNode.EditorBounds; + commentRect.Expand(80f); + _surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green); // Update UI FlaxEngine.Scripting.InvokeOnUpdate(() => From 036d4b2f4b4ce89498b7394cc819d7dde7663a27 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 3 Nov 2025 23:21:09 +0100 Subject: [PATCH 057/207] Fix error when asset refs picker uses different types --- Source/Editor/CustomEditors/Editors/AssetRefEditor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs index 4fae716f1..b407d9a3c 100644 --- a/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs +++ b/Source/Editor/CustomEditors/Editors/AssetRefEditor.cs @@ -123,6 +123,8 @@ namespace FlaxEditor.CustomEditors.Editors { base.Refresh(); + if (Picker == null) + return; var differentValues = HasDifferentValues; Picker.DifferentValues = differentValues; if (!differentValues) From 5d17d2509d0daf5c3790d1c3d8b26c9968e117a6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 4 Nov 2025 12:42:08 +0100 Subject: [PATCH 058/207] Fix Global SDF gradient at borders --- Source/Shaders/GlobalSignDistanceField.hlsl | 89 ++++++++++++--------- 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index a2f09de68..8075c081d 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -11,6 +11,7 @@ #define GLOBAL_SDF_WORLD_SIZE 60000.0f #define GLOBAL_SDF_MIN_VALID 0.9f #define GLOBAL_SDF_CHUNK_MARGIN_SCALE 4.0f +#define GLOBAL_SDF_SAMPLER SamplerLinearClamp // Global SDF data for a constant buffer struct GlobalSDFData @@ -74,6 +75,15 @@ void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldP textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // Cascades are placed next to each other on X axis } +// Clamps Global SDF cascade UV to ensure it can be sued for gradient sampling (clamps first and last pixels). +void ClampGlobalSDFTextureGradientUV(const GlobalSDFData data, uint cascade, float texelOffset, inout float3 textureUV) +{ + float cascadeSizeUV = 1.0f / data.CascadesCount; + float cascadeUVStart = cascadeSizeUV * cascade + texelOffset; + float cascadeUVEnd = cascadeUVStart + cascadeSizeUV - texelOffset * 3; + textureUV.x = clamp(textureUV.x, cascadeUVStart, cascadeUVEnd); +} + // Gets the Global SDF cascade index for the given world location. uint GetGlobalSDFCascade(const GlobalSDFData data, float3 worldPosition) { @@ -96,7 +106,7 @@ float SampleGlobalSDFCascade(const GlobalSDFData data, Texture3D te float voxelSize = data.CascadeVoxelSize[cascade]; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1)) distance = distanceTex; return distance; @@ -115,7 +125,7 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex, floa float voxelSize = data.CascadeVoxelSize[cascade]; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0); if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1)) { distance = distanceTex * maxDistanceTex; @@ -140,12 +150,12 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex, Text float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; - float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0); + float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0); if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1)) { distance = distanceMip * maxDistanceMip; float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; if (distanceTex < chunkMargin) distance = distanceTex; break; @@ -169,16 +179,17 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D float voxelSize = data.CascadeVoxelSize[cascade]; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0); + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0); if (distanceTex < chunkMargin && all(cascadeUV > 0) && all(cascadeUV < 1)) { float texelOffset = 1.0f / data.Resolution; - float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV); + float xp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex; distance = distanceTex * maxDistanceTex; break; @@ -203,33 +214,35 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; - float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip; + float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceMip; if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1)) { float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; if (distanceTex < chunkMargin) { distance = distanceTex; float texelOffset = 1.0f / data.Resolution; - float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV); + float xp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceTex; } else { distance = distanceMip; float texelOffset = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR / data.Resolution; - float xp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = mip.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV); + float xp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceMip; } break; @@ -253,15 +266,14 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D float4 cascadePosDistance = data.CascadePosDistance[cascade]; float voxelSize = data.CascadeVoxelSize[cascade]; float voxelExtent = voxelSize * 0.5f; - float3 worldPosition = trace.WorldPosition; // Skip until cascade that contains the start location - if (any(abs(worldPosition - cascadePosDistance.xyz) > cascadePosDistance.w)) + if (any(abs(trace.WorldPosition - cascadePosDistance.xyz) > cascadePosDistance.w)) continue; // Hit the cascade bounds to find the intersection points float traceStartBias = voxelSize * cascadeTraceStartBias; - float2 intersections = LineHitBox(worldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www); + float2 intersections = LineHitBox(trace.WorldPosition, traceEndPosition, cascadePosDistance.xyz - cascadePosDistance.www, cascadePosDistance.xyz + cascadePosDistance.www); intersections.xy *= traceMaxDistance; intersections.x = max(intersections.x, traceStartBias); intersections.x = max(intersections.x, nextIntersectionStart); @@ -280,18 +292,18 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D LOOP for (; step < 250 && stepTime < intersections.y && hit.HitTime < 0.0f; step++) { - float3 stepPosition = worldPosition + trace.WorldDirection * stepTime; + float3 stepPosition = trace.WorldPosition + trace.WorldDirection * stepTime; float stepScale = trace.StepScale; // Sample SDF float stepDistance, voxelSizeScale = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR; float3 cascadeUV, textureUV; GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeUV, textureUV); - float distanceMip = mip.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceMip; + float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceMip; if (distanceMip < chunkSize) { stepDistance = distanceMip; - float distanceTex = tex.SampleLevel(SamplerLinearClamp, textureUV, 0) * maxDistanceTex; + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; if (distanceTex < chunkMargin) { stepDistance = distanceTex; @@ -301,7 +313,7 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D } else { - // Assume no SDF nearby so perform a jump tto the next chunk + // Assume no SDF nearby so perform a jump to the next chunk stepDistance = chunkSize; voxelSizeScale = 1.0f; } @@ -318,12 +330,13 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D { // Calculate hit normal from SDF gradient float texelOffset = 1.0f / data.Resolution; - float xp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = tex.SampleLevel(SamplerLinearClamp, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV); + float xp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; + float xn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; + float yp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; + float yn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; + float zp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; + float zn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn)); } } From 85b134b7be0411fcda366ee0ce6a969cdc978b7d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 4 Nov 2025 13:27:21 +0100 Subject: [PATCH 059/207] Add improved Global SDF quality and precision of rasterization --- Content/Editor/Primitives/Cube.flax | 4 ++-- Source/Engine/Tools/ModelTool/ModelTool.cpp | 2 ++ Source/Shaders/SDF.shader | 2 ++ Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl | 8 ++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Content/Editor/Primitives/Cube.flax b/Content/Editor/Primitives/Cube.flax index 6328a7bbc..b12f75e05 100644 --- a/Content/Editor/Primitives/Cube.flax +++ b/Content/Editor/Primitives/Cube.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b5a18bf58e0b93c8bba9459fd77a92898f0fae373b58d3acbcb9e36f66c89cd7 -size 23537 +oid sha256:8e8d210a74ae373793eaee1ddab1372a6a50a000c489f97b2258a09cd93cc2d0 +size 5031 diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 0ae1e0639..78a3fe515 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -366,9 +366,11 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, return true; ModelBase::SDFData sdf; sdf.WorldUnitsPerVoxel = METERS_TO_UNITS(0.1f) / Math::Max(resolutionScale, 0.0001f); // 1 voxel per 10 centimeters +#if 0 const float boundsMargin = sdf.WorldUnitsPerVoxel * 0.5f; // Add half-texel margin around the mesh bounds.Minimum -= boundsMargin; bounds.Maximum += boundsMargin; +#endif const Float3 size = bounds.GetSize(); Int3 resolution(Float3::Ceil(Float3::Clamp(size / sdf.WorldUnitsPerVoxel, 4, 256))); Float3 uvwToLocalMul = size; diff --git a/Source/Shaders/SDF.shader b/Source/Shaders/SDF.shader index ba2626ff9..2239e2dbe 100644 --- a/Source/Shaders/SDF.shader +++ b/Source/Shaders/SDF.shader @@ -142,8 +142,10 @@ void CS_RasterizeTriangle(uint3 DispatchThreadId : SV_DispatchThreadID) int voxelIndex = GetVoxelIndex(voxelCoord); float3 voxelPos = GetVoxelPos(voxelCoord); float distance = SignedDistancePointToTriangle(voxelPos, v0, v1, v2); +#if 0 if (distance < -10.0f) // TODO: find a better way to reject negative distance from degenerate triangles that break SDF shape distance = abs(distance); +#endif InterlockedMin(SDF[voxelIndex], FloatFlip3(distance)); } } diff --git a/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl b/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl index 13c28f16d..7128ba9ca 100644 --- a/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl +++ b/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl @@ -52,6 +52,14 @@ uint IFloatFlip3(uint f2) float DistancePointToEdge(float3 p, float3 x0, float3 x1, out float3 n) { + // Hack to swap to ensure the order is correct (.x only for simplicity) + if (x0.x > x1.x) + { + float3 temp = x0; + x0 = x1; + x1 = temp; + } + float3 x10 = x1 - x0; float t = dot(x1 - p, x10) / dot(x10, x10); From 387c3ea2f4a517d609718080d8abc887da8efd03 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 4 Nov 2025 13:27:47 +0100 Subject: [PATCH 060/207] Add better debug view for Global SDF to include surface hit normal --- Source/Shaders/GlobalSignDistanceField.shader | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Shaders/GlobalSignDistanceField.shader b/Source/Shaders/GlobalSignDistanceField.shader index f3a51addd..461dba08d 100644 --- a/Source/Shaders/GlobalSignDistanceField.shader +++ b/Source/Shaders/GlobalSignDistanceField.shader @@ -311,6 +311,7 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target float3 viewRay = lerp(lerp(ViewFrustumWorldRays[3], ViewFrustumWorldRays[0], input.TexCoord.x), lerp(ViewFrustumWorldRays[2], ViewFrustumWorldRays[1], input.TexCoord.x), 1 - input.TexCoord.y).xyz; viewRay = normalize(viewRay - ViewWorldPos); trace.Init(ViewWorldPos, viewRay, ViewNearPlane, ViewFarPlane); + trace.NeedsHitNormal = true; GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace); // Debug draw @@ -321,9 +322,14 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target else { // Debug draw SDF normals - float dst; - color.rgb = normalize(SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, hit.GetHitPosition(trace), dst)) * 0.5f + 0.5f; + color.rgb = normalize(hit.HitNormal) * 0.5f + 0.5f; } +#elif 1 + else + { + // Composite with SDF normals + color.rgb *= saturate(normalize(hit.HitNormal) * 0.5f + 0.7f) + 0.1f; + } #endif return float4(color, 1); } From 0f701ec08e94f41718af188aefad3407b989946b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 4 Nov 2025 13:30:18 +0100 Subject: [PATCH 061/207] Add force Mesh SDF rebuild when holiding `F` key and using Build All Meshes SDF optino in Editor menu --- Source/Editor/Editor.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 66328926d..58466e35d 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1390,6 +1390,7 @@ namespace FlaxEditor public void BuildAllMeshesSDF() { var models = new List(); + var forceRebuild = Input.GetKey(KeyboardKeys.F); Scene.ExecuteOnGraph(node => { if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel) @@ -1399,7 +1400,7 @@ namespace FlaxEditor model != null && !models.Contains(model) && !model.IsVirtual && - model.SDF.Texture == null) + (forceRebuild || model.SDF.Texture == null)) { models.Add(model); } @@ -1412,7 +1413,17 @@ namespace FlaxEditor { var model = models[i]; Log($"[{i}/{models.Count}] Generating SDF for {model}"); - if (!model.GenerateSDF()) + float resolutionScale = 1.0f, backfacesThreshold = 0.6f; + int lodIndex = 6; + bool useGPU = true; + var sdf = model.SDF; + if (sdf.Texture != null) + { + // Preserve options set on this model + resolutionScale = sdf.ResolutionScale; + lodIndex = sdf.LOD; + } + if (!model.GenerateSDF(resolutionScale, lodIndex, true, backfacesThreshold, useGPU)) model.Save(); } }); From 5ec860015d76b4874d178dc284f61e5de2cbd889 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 6 Nov 2025 21:00:16 +0100 Subject: [PATCH 062/207] Add minor adjustments --- Source/Editor/Modules/UIModule.cs | 1 + Source/Engine/Core/Math/CollisionsHelper.cpp | 9 ++------- Source/Engine/Core/Math/Ray.cpp | 5 ----- Source/Engine/Core/Math/Ray.h | 5 ++++- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/Source/Editor/Modules/UIModule.cs b/Source/Editor/Modules/UIModule.cs index 0e482441e..c02992041 100644 --- a/Source/Editor/Modules/UIModule.cs +++ b/Source/Editor/Modules/UIModule.cs @@ -714,6 +714,7 @@ namespace FlaxEditor.Modules _menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", inputOptions.BuildCSG, Editor.BuildCSG); _menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", inputOptions.BuildNav, Editor.BuildNavMesh); _menuToolsBuildAllMeshesSDF = cm.AddButton("Build all meshes SDF", inputOptions.BuildSDF, Editor.BuildAllMeshesSDF); + _menuToolsBuildAllMeshesSDF.LinkTooltip("Generates Sign Distance Field texture for all meshes used in loaded scenes. Use with 'F' key pressed to force rebuild SDF for meshes with existing one."); cm.AddSeparator(); cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow); _menuToolsCancelBuilding = cm.AddButton("Cancel building game", () => GameCooker.Cancel()); diff --git a/Source/Engine/Core/Math/CollisionsHelper.cpp b/Source/Engine/Core/Math/CollisionsHelper.cpp index 5a40fae6e..dee415e4a 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.cpp +++ b/Source/Engine/Core/Math/CollisionsHelper.cpp @@ -620,14 +620,9 @@ bool Collision::RayIntersectsTriangle(const Ray& ray, const Vector3& a, const Ve Real rayDistance = edge2.X * distanceCrossEdge1.X + edge2.Y * distanceCrossEdge1.Y + edge2.Z * distanceCrossEdge1.Z; rayDistance *= inverseDeterminant; - // Check if the triangle is behind the ray origin - if (rayDistance < 0.0f) - { - return false; - } - + // Check if the triangle is in front the ray origin distance = rayDistance; - return true; + return rayDistance >= 0.0f; } bool CollisionsHelper::RayIntersectsTriangle(const Ray& ray, const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3, Real& distance, Vector3& normal) diff --git a/Source/Engine/Core/Math/Ray.cpp b/Source/Engine/Core/Math/Ray.cpp index 414814c4a..5bee8c2c4 100644 --- a/Source/Engine/Core/Math/Ray.cpp +++ b/Source/Engine/Core/Math/Ray.cpp @@ -12,11 +12,6 @@ String Ray::ToString() const return String::Format(TEXT("{}"), *this); } -Vector3 Ray::GetPoint(Real distance) const -{ - return Position + Direction * distance; -} - Ray Ray::GetPickRay(float x, float y, const Viewport& viewport, const Matrix& vp) { Vector3 nearPoint(x, y, 0.0f); diff --git a/Source/Engine/Core/Math/Ray.h b/Source/Engine/Core/Math/Ray.h index cb2aa6419..65e9f1512 100644 --- a/Source/Engine/Core/Math/Ray.h +++ b/Source/Engine/Core/Math/Ray.h @@ -79,7 +79,10 @@ public: /// /// The distance from ray origin. /// The calculated point. - Vector3 GetPoint(Real distance) const; + FORCE_INLINE Vector3 GetPoint(Real distance) const + { + return Position + Direction * distance; + } /// /// Determines if there is an intersection between ray and a point. From 3a5bb81d3968593155b5609892b0444aade7e64d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 6 Nov 2025 21:00:29 +0100 Subject: [PATCH 063/207] Add a new splash screen quote --- Source/Editor/Windows/SplashScreen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Windows/SplashScreen.cpp b/Source/Editor/Windows/SplashScreen.cpp index 7e2889ca1..54a1a7418 100644 --- a/Source/Editor/Windows/SplashScreen.cpp +++ b/Source/Editor/Windows/SplashScreen.cpp @@ -137,6 +137,7 @@ const Char* SplashScreenQuotes[] = TEXT("Good Luck Have Fun"), TEXT("GG Well Played"), TEXT("Now with documentation."), + TEXT("We do this not because it is easy,\nbut because we thought it would be easy"), }; SplashScreen::~SplashScreen() From 59643b2fb9dd5564f7406d57116e2574f945defa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 6 Nov 2025 21:01:02 +0100 Subject: [PATCH 064/207] Add improved local-light shadow raytracing by starting ray from light, not surface --- Source/Shaders/GI/GlobalSurfaceAtlas.shader | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Shaders/GI/GlobalSurfaceAtlas.shader b/Source/Shaders/GI/GlobalSurfaceAtlas.shader index 2efa638f0..387620c5c 100644 --- a/Source/Shaders/GI/GlobalSurfaceAtlas.shader +++ b/Source/Shaders/GI/GlobalSurfaceAtlas.shader @@ -165,11 +165,19 @@ float4 PS_Lighting(AtlasVertexOutput input) : SV_Target BRANCH if (NoL > 0) { +#if RADIAL_LIGHT + // Shot a ray from light to the texel to see if there is any occluder + GlobalSDFTrace trace; + trace.Init(Light.Position, -L, bias, toLightDst); + GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace, 1.0f); + shadowMask = hit.IsHit() && hit.HitTime < toLightDst - bias * 3 ? LightShadowsStrength : 1; +#else // Shot a ray from texel into the light to see if there is any occluder GlobalSDFTrace trace; trace.Init(gBuffer.WorldPos + gBuffer.Normal * shadowBias, L, bias, toLightDst - bias); GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace, 2.0f); shadowMask = hit.IsHit() ? LightShadowsStrength : 1; +#endif } else { From cc851b29fc71c3e315d484b46ba5076b5d63c46b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 2 Nov 2025 22:12:53 +0100 Subject: [PATCH 065/207] Fix animation state transition inputs when using other surface context --- Source/Editor/Surface/Archetypes/Animation.StateMachine.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 7facfc807..e66f38398 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -726,7 +726,7 @@ namespace FlaxEditor.Surface.Archetypes private void OnSurfaceMouseUp(ref Float2 mouse, MouseButton buttons, ref bool handled) { - if (handled) + if (handled || Surface.Context != Context) return; // Check click over the connection @@ -751,7 +751,7 @@ namespace FlaxEditor.Surface.Archetypes private void OnSurfaceMouseDoubleClick(ref Float2 mouse, MouseButton buttons, ref bool handled) { - if (handled) + if (handled || Surface.Context != Context) return; // Check double click over the connection From c0b73375b147218050de259141198313f68d4b53 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 7 Nov 2025 21:31:04 +0100 Subject: [PATCH 066/207] Fix invoking asset load event if it's referenced directly #3782 --- Source/Engine/Content/Asset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index cb711013b..9fd4cee9c 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -666,7 +666,7 @@ void Asset::onLoaded() { onLoaded_MainThread(); } - else if (OnLoaded.IsBinded()) + else if (OnLoaded.IsBinded() || _references.HasItems()) { Function action; action.Bind(this); From ca500548a3b7774b3fb802697dddfa09683a21e9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 7 Nov 2025 21:31:13 +0100 Subject: [PATCH 067/207] Bump up build number --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 6a37b7a85..5123e5b8f 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 11, "Revision": 0, - "Build": 6803 + "Build": 6804 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.", From d84cef0c189684e524d0e05e9901041f7698ca5b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 9 Nov 2025 22:15:41 +0100 Subject: [PATCH 068/207] Fix crash due to async content data streaming Properly checks for asset data unloading before taking lock on asset chunks. #3358 #3085 #3515 --- Source/Engine/Content/Storage/FlaxChunk.h | 8 +++- Source/Engine/Content/Storage/FlaxStorage.cpp | 48 +++++++++++++++---- Source/Engine/Content/Storage/FlaxStorage.h | 6 +-- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Content/Storage/FlaxChunk.h b/Source/Engine/Content/Storage/FlaxChunk.h index 6e9887574..1d3bdce1a 100644 --- a/Source/Engine/Content/Storage/FlaxChunk.h +++ b/Source/Engine/Content/Storage/FlaxChunk.h @@ -87,6 +87,10 @@ public: /// double LastAccessTime = 0.0; + /// + /// Flag set to indicate that chunk is during loading (atomic access to sync multiple reading threads). + /// + int64 IsLoading = 0; /// /// The chunk data. /// @@ -146,7 +150,7 @@ public: /// FORCE_INLINE bool IsLoaded() const { - return Data.IsValid(); + return Data.IsValid() && Platform::AtomicRead(&IsLoading) == 0; } /// @@ -154,7 +158,7 @@ public: /// FORCE_INLINE bool IsMissing() const { - return Data.IsInvalid(); + return !IsLoaded(); } /// diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index ed8b623ae..1c6be4971 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -5,6 +5,7 @@ #include "FlaxPackage.h" #include "ContentStorageManager.h" #include "Engine/Core/Log.h" +#include "Engine/Core/ScopeExit.h" #include "Engine/Core/Types/TimeSpan.h" #include "Engine/Platform/File.h" #include "Engine/Profiler/ProfilerCPU.h" @@ -246,6 +247,7 @@ FlaxStorage::~FlaxStorage() ASSERT(IsDisposed()); CHECK(_chunksLock == 0); CHECK(_refCount == 0); + CHECK(_isUnloadingData == 0); ASSERT(_chunks.IsEmpty()); #if USE_EDITOR @@ -261,6 +263,22 @@ FlaxStorage::~FlaxStorage() #endif } +void FlaxStorage::LockChunks() +{ +RETRY: + Platform::InterlockedIncrement(&_chunksLock); + if (Platform::AtomicRead(&_isUnloadingData) != 0) + { + // Someone else is closing file handles or freeing chunks so wait for it to finish and retry + Platform::InterlockedDecrement(&_chunksLock); + do + { + Platform::Sleep(1); + } while (Platform::AtomicRead(&_isUnloadingData) != 0); + goto RETRY; + } +} + FlaxStorage::LockData FlaxStorage::LockSafe() { auto lock = LockData(this); @@ -689,7 +707,6 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data) return true; } - // Load header return LoadAssetHeader(e, data); } @@ -699,7 +716,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk) ASSERT(IsLoaded()); ASSERT(chunk != nullptr && _chunks.Contains(chunk)); - // Check if already loaded + // Protect against loading the same chunk from multiple threads at once + while (Platform::InterlockedCompareExchange(&chunk->IsLoading, 1, 0) != 0) + Platform::Sleep(1); + SCOPE_EXIT{ Platform::AtomicStore(&chunk->IsLoading, 0); }; if (chunk->IsLoaded()) return false; @@ -776,12 +796,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk) // Raw data chunk->Data.Read(stream, size); } - ASSERT(chunk->IsLoaded()); chunk->RegisterUsage(); } UnlockChunks(); - return failed; } @@ -1420,10 +1438,12 @@ FileReadStream* FlaxStorage::OpenFile() bool FlaxStorage::CloseFileHandles() { + // Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end + Platform::InterlockedIncrement(&_isUnloadingData); + SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); }; + if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0) - { - return false; - } + return false; // Early out when no files are opened PROFILE_CPU(); PROFILE_MEM(ContentFiles); @@ -1496,9 +1516,21 @@ void FlaxStorage::Tick(double time) { auto chunk = _chunks.Get()[i]; const bool wasUsed = (time - chunk->LastAccessTime) < unusedDataChunksLifetime; - if (!wasUsed && chunk->IsLoaded() && EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory)) + if (!wasUsed && + chunk->IsLoaded() && + EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory) && + Platform::AtomicRead(&chunk->IsLoading) == 0) { + // Guard the unloading so if other thread wants to lock the chunks will need to wait for this to end + Platform::InterlockedIncrement(&_isUnloadingData); + if (Platform::AtomicRead(&_chunksLock) != 0 || Platform::AtomicRead(&chunk->IsLoading) != 0) + { + // Someone started loading so skip ticking + Platform::InterlockedDecrement(&_isUnloadingData); + return; + } chunk->Unload(); + Platform::InterlockedDecrement(&_isUnloadingData); } wasAnyUsed |= wasUsed; } diff --git a/Source/Engine/Content/Storage/FlaxStorage.h b/Source/Engine/Content/Storage/FlaxStorage.h index 450de9808..6de462214 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.h +++ b/Source/Engine/Content/Storage/FlaxStorage.h @@ -90,6 +90,7 @@ protected: int64 _refCount = 0; int64 _chunksLock = 0; int64 _files = 0; + int64 _isUnloadingData = 0; double _lastRefLostTime; CriticalSection _loadLocker; @@ -129,10 +130,7 @@ public: /// /// Locks the storage chunks data to prevent disposing them. Also ensures that file handles won't be closed while chunks are locked. /// - FORCE_INLINE void LockChunks() - { - Platform::InterlockedIncrement(&_chunksLock); - } + void LockChunks(); /// /// Unlocks the storage chunks data. From 66dbba5c16be533359b4cd8365f17738c5fd551e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 9 Nov 2025 22:16:14 +0100 Subject: [PATCH 069/207] Fix crash if base material gets GCed before it's referenced by instance during loading --- Source/Engine/Content/Assets/MaterialInstance.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Engine/Content/Assets/MaterialInstance.cpp b/Source/Engine/Content/Assets/MaterialInstance.cpp index b3710ebbd..78a276a8a 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.cpp +++ b/Source/Engine/Content/Assets/MaterialInstance.cpp @@ -218,10 +218,14 @@ Asset::LoadResult MaterialInstance::load() Guid baseMaterialId; headerStream.Read(baseMaterialId); auto baseMaterial = Content::LoadAsync(baseMaterialId); + if (baseMaterial) + baseMaterial->AddReference(); // Load parameters if (Params.Load(&headerStream)) { + if (baseMaterial) + baseMaterial->RemoveReference(); LOG(Warning, "Cannot load material parameters."); return LoadResult::CannotLoadData; } @@ -239,6 +243,7 @@ Asset::LoadResult MaterialInstance::load() ParamsChanged(); } + baseMaterial->RemoveReference(); return LoadResult::Ok; } From 108678d94f10ba1dc48d22572b9fda7700bbc6c8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 9 Nov 2025 22:16:44 +0100 Subject: [PATCH 070/207] Fix crash when texture streaming mip task gets deleted after texture object on GC --- .../Engine/Graphics/Textures/StreamingTexture.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index 735bc162e..71d209301 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -338,10 +338,10 @@ public: StreamTextureMipTask(StreamingTexture* texture, int32 mipIndex, Task* rootTask) : GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span(nullptr, 0), 0, 0, false) , _streamingTexture(texture) - , _rootTask(rootTask ? rootTask : this) + , _rootTask(rootTask) , _dataLock(_streamingTexture->GetOwner()->LockData()) { - _streamingTexture->_streamingTasks.Add(_rootTask); + _streamingTexture->_streamingTasks.Add(this); _texture.Released.Bind(this); } @@ -357,7 +357,7 @@ private: if (_streamingTexture) { ScopeLock lock(_streamingTexture->GetOwner()->GetOwnerLocker()); - _streamingTexture->_streamingTasks.Remove(_rootTask); + _streamingTexture->_streamingTasks.Remove(this); _streamingTexture = nullptr; } } @@ -422,6 +422,15 @@ protected: GPUUploadTextureMipTask::OnFail(); } + + void OnCancel() override + { + GPUUploadTextureMipTask::OnCancel(); + + // Cancel the root task too (eg. mip loading from asset) + if (_rootTask != nullptr) + _rootTask->Cancel(); + } }; Task* StreamingTexture::CreateStreamingTask(int32 residency) From 62424215c1124cfb5a4ba2792ec105593636eb6d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 9 Nov 2025 23:25:16 +0100 Subject: [PATCH 071/207] Fix crash due to missing asset reference inside `MeshAccelerationStructure` --- .../Tools/ModelTool/MeshAccelerationStructure.cpp | 13 +++++++++++++ .../Tools/ModelTool/MeshAccelerationStructure.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp index 4e551e445..11c449847 100644 --- a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp +++ b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp @@ -288,6 +288,15 @@ bool MeshAccelerationStructure::RayCastBVH(int32 node, const Ray& ray, Real& hit return hit; } +MeshAccelerationStructure::~MeshAccelerationStructure() +{ + for (auto& e : _meshes) + { + if (e.Asset) + e.Asset->RemoveReference(); + } +} + void MeshAccelerationStructure::Add(Model* model, int32 lodIndex) { PROFILE_CPU(); @@ -307,6 +316,8 @@ void MeshAccelerationStructure::Add(Model* model, int32 lodIndex) } auto& meshData = _meshes.AddOne(); + meshData.Asset = model; + model->AddReference(); if (model->IsVirtual()) { meshData.Indices = mesh.GetTriangleCount() * 3; @@ -350,6 +361,7 @@ void MeshAccelerationStructure::Add(const ModelData* modelData, int32 lodIndex, } auto& meshData = _meshes.AddOne(); + meshData.Asset = nullptr; meshData.Indices = mesh->Indices.Count(); meshData.Vertices = mesh->Positions.Count(); if (copy) @@ -370,6 +382,7 @@ void MeshAccelerationStructure::Add(const ModelData* modelData, int32 lodIndex, void MeshAccelerationStructure::Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy) { auto& meshData = _meshes.AddOne(); + meshData.Asset = nullptr; if (copy) { meshData.VertexBuffer.Copy((const byte*)vb, vertices * sizeof(Float3)); diff --git a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h index 7e158e742..c40ebc729 100644 --- a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h +++ b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h @@ -20,6 +20,7 @@ class FLAXENGINE_API MeshAccelerationStructure private: struct Mesh { + Model* Asset; BytesContainer IndexBuffer, VertexBuffer; int32 Indices, Vertices; bool Use16BitIndexBuffer; @@ -57,6 +58,8 @@ private: bool RayCastBVH(int32 node, const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle) const; public: + ~MeshAccelerationStructure(); + // Adds the model geometry for the build to the structure. void Add(Model* model, int32 lodIndex); From 4805dfbdbaf56c61d281f0be85074db8f42fbf5c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 10 Nov 2025 15:02:33 +0100 Subject: [PATCH 072/207] Fix issues with model data storage when doing long actions in async (eg. SDF generation) --- Source/Editor/Tools/Terrain/TerrainTools.cpp | 5 ----- Source/Engine/Content/Assets/Model.cpp | 1 + Source/Engine/Content/Assets/ModelBase.cpp | 3 ++- Source/Engine/Content/Storage/FlaxStorage.cpp | 2 -- Source/Engine/Content/Storage/FlaxStorage.h | 6 +++++- Source/Engine/Graphics/Textures/TextureBase.cpp | 2 +- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index 065f7ffeb..861cb975b 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -74,11 +74,6 @@ struct TextureDataResult PixelFormat Format; Int2 Mip0Size; BytesContainer* Mip0DataPtr; - - TextureDataResult() - : Lock(FlaxStorage::LockData::Invalid) - { - } }; bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false) diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index 829eaeb11..66ca0aa2e 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -262,6 +262,7 @@ bool Model::GenerateSDF(float resolutionScale, int32 lodIndex, bool cacheData, f LOG(Warning, "Cannot generate SDF for virtual models on a main thread."); return true; } + auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData(); lodIndex = Math::Clamp(lodIndex, HighestResidentLODIndex(), LODs.Count() - 1); // Generate SDF diff --git a/Source/Engine/Content/Assets/ModelBase.cpp b/Source/Engine/Content/Assets/ModelBase.cpp index 599caa810..1847e96d9 100644 --- a/Source/Engine/Content/Assets/ModelBase.cpp +++ b/Source/Engine/Content/Assets/ModelBase.cpp @@ -61,7 +61,7 @@ public: model->GetLODData(_lodIndex, data); if (data.IsInvalid()) { - LOG(Warning, "Missing data chunk"); + LOG(Warning, "Missing data chunk with LOD{} for model '{}'", _lodIndex, model->ToString()); return true; } MemoryReadStream stream(data.Get(), data.Length()); @@ -234,6 +234,7 @@ bool ModelBase::Save(bool withMeshDataFromGpu, const StringView& path) LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data)."); return true; } + auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData(); ScopeLock lock(Locker); // Use a temporary chunks for data storage for virtual assets diff --git a/Source/Engine/Content/Storage/FlaxStorage.cpp b/Source/Engine/Content/Storage/FlaxStorage.cpp index 1c6be4971..6967bb5ad 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.cpp +++ b/Source/Engine/Content/Storage/FlaxStorage.cpp @@ -75,8 +75,6 @@ FlaxChunk* FlaxChunk::Clone() const const int32 FlaxStorage::MagicCode = 1180124739; -FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr); - struct Header { int32 MagicCode; diff --git a/Source/Engine/Content/Storage/FlaxStorage.h b/Source/Engine/Content/Storage/FlaxStorage.h index 6de462214..f01384b68 100644 --- a/Source/Engine/Content/Storage/FlaxStorage.h +++ b/Source/Engine/Content/Storage/FlaxStorage.h @@ -146,7 +146,6 @@ public: struct LockData { friend FlaxStorage; - static LockData Invalid; private: FlaxStorage* _storage; @@ -159,6 +158,11 @@ public: } public: + LockData() + : _storage(nullptr) + { + } + LockData(const LockData& other) : _storage(other._storage) { diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index 81b5f159c..eb838fa07 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -771,7 +771,7 @@ Task* TextureBase::RequestMipDataAsync(int32 mipIndex) FlaxStorage::LockData TextureBase::LockData() { - return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData::Invalid; + return _parent->Storage ? _parent->Storage->Lock() : FlaxStorage::LockData(); } void TextureBase::GetMipData(int32 mipIndex, BytesContainer& data) const From c7997e0c2ffdbbded2d803172b7ad34eeb5db00b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 10 Nov 2025 21:50:11 +0100 Subject: [PATCH 073/207] Fix potential error on missing reference object --- Source/Editor/Windows/Profiler/MemoryGPU.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/Profiler/MemoryGPU.cs b/Source/Editor/Windows/Profiler/MemoryGPU.cs index 74f14b584..11b8a1980 100644 --- a/Source/Editor/Windows/Profiler/MemoryGPU.cs +++ b/Source/Editor/Windows/Profiler/MemoryGPU.cs @@ -296,13 +296,15 @@ namespace FlaxEditor.Windows.Profiler var resources = _resources.Get(_memoryUsageChart.SelectedSampleIndex); if (resources == null || resources.Length == 0) return; - var resourcesOrdered = resources.OrderByDescending(x => x.MemoryUsage); + var resourcesOrdered = resources.OrderByDescending(x => x?.MemoryUsage ?? 0); // Add rows var rowColor2 = Style.Current.Background * 1.4f; int rowIndex = 0; foreach (var e in resourcesOrdered) { + if (e == null) + continue; ClickableRow row; if (_tableRowsCache.Count != 0) { From 49918a1067a710116dac558fa7ca09cee454db07 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 11 Nov 2025 21:59:16 +0200 Subject: [PATCH 074/207] Fix Windows dotnet root path detection --- Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs index f8e215320..28c3958df 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetSdk.cs @@ -217,9 +217,17 @@ namespace Flax.Build using RegistryKey sdkVersionsKey = baseKey.OpenSubKey($@"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sdk"); using RegistryKey runtimeKey = baseKey.OpenSubKey(@$"SOFTWARE\WOW6432Node\dotnet\Setup\InstalledVersions\{arch}\sharedfx\Microsoft.NETCore.App"); using RegistryKey hostKey = baseKey.OpenSubKey(@$"SOFTWARE\dotnet\Setup\InstalledVersions\{arch}\sharedhost"); - dotnetPath = (string)hostKey.GetValue("Path"); + dotnetPath = (string)hostKey?.GetValue("Path"); dotnetSdkVersions = sdkVersionsKey?.GetValueNames() ?? Enumerable.Empty(); dotnetRuntimeVersions = runtimeKey?.GetValueNames() ?? Enumerable.Empty(); + + if (string.IsNullOrEmpty(dotnetPath)) + { + // The sharedhost registry key seems to be deprecated, assume the default installation location instead + var defaultPath = Path.Combine(Path.GetPathRoot(Environment.SystemDirectory), "Program Files", "dotnet"); + if (File.Exists(Path.Combine(defaultPath, "dotnet.exe"))) + dotnetPath = defaultPath; + } } #pragma warning restore CA1416 break; From 91ee9f5e05625b4c81c1ee9182cf742f83b406e2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 13 Nov 2025 22:05:23 +0100 Subject: [PATCH 075/207] Refactor Mesh SDF generation on GPU to use raytracing for more precise results --- Content/Editor/Primitives/Cube.flax | 2 +- Content/Shaders/GlobalSignDistanceField.flax | 4 +- Content/Shaders/SDF.flax | 4 +- Source/Editor/Windows/Assets/ModelWindow.cs | 12 +- .../DirectX/DX11/GPUTimerQueryDX11.cpp | 5 +- .../ModelTool/MeshAccelerationStructure.cpp | 233 ++++++++++++++-- .../ModelTool/MeshAccelerationStructure.h | 56 +++- Source/Engine/Tools/ModelTool/ModelTool.cpp | 262 +++++------------- Source/Shaders/Collisions.hlsl | 55 ++++ Source/Shaders/MeshAccelerationStructure.hlsl | 163 +++++++++++ Source/Shaders/SDF.shader | 237 ++++------------ .../ThirdParty/TressFX/TressFXSDF.hlsl | 129 --------- 12 files changed, 615 insertions(+), 547 deletions(-) create mode 100644 Source/Shaders/MeshAccelerationStructure.hlsl delete mode 100644 Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl diff --git a/Content/Editor/Primitives/Cube.flax b/Content/Editor/Primitives/Cube.flax index b12f75e05..d082eaa5b 100644 --- a/Content/Editor/Primitives/Cube.flax +++ b/Content/Editor/Primitives/Cube.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e8d210a74ae373793eaee1ddab1372a6a50a000c489f97b2258a09cd93cc2d0 +oid sha256:6a56dc14746606f0065d136ad0a69ae1aa41e8732ea380c657d75c187aa09f54 size 5031 diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax index 2cdf124ca..590e8f3a9 100644 --- a/Content/Shaders/GlobalSignDistanceField.flax +++ b/Content/Shaders/GlobalSignDistanceField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e593e15c500b14f6e49d5e4ca6156135fd84d9cc1072a4597ee8bcea77d9339e -size 13255 +oid sha256:064f54786958f109222c49cbc0358ff4f345b30010fcd5e8cc1fab7bdc68c4fe +size 13349 diff --git a/Content/Shaders/SDF.flax b/Content/Shaders/SDF.flax index 83009c326..5141c14dd 100644 --- a/Content/Shaders/SDF.flax +++ b/Content/Shaders/SDF.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1abacca0d63b575f0c88d54839f30ce4ec1bb8912478cb81ada8219d417001f9 -size 7891 +oid sha256:d2b1dc1523cb2140db7ce5fed6e97b09d7fcebbe6cc19fca7708b5b882267040 +size 4175 diff --git a/Source/Editor/Windows/Assets/ModelWindow.cs b/Source/Editor/Windows/Assets/ModelWindow.cs index 1dc30cae3..19fc932d5 100644 --- a/Source/Editor/Windows/Assets/ModelWindow.cs +++ b/Source/Editor/Windows/Assets/ModelWindow.cs @@ -90,25 +90,15 @@ namespace FlaxEditor.Windows.Assets var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage)."); gpu.CheckBox.Checked = sdfOptions.GPU; + gpu.CheckBox.StateChanged += c => { Window._sdfOptions.GPU = c.Checked; }; var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh."); var backfacesThreshold = backfacesThresholdProp.FloatValue(); - var backfacesThresholdLabel = backfacesThresholdProp.Labels.Last(); backfacesThreshold.ValueBox.MinValue = 0.001f; backfacesThreshold.ValueBox.MaxValue = 1.0f; backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold; backfacesThreshold.ValueBox.BoxValueChanged += b => { Window._sdfOptions.BackfacesThreshold = b.Value; }; - // Toggle Backfaces Threshold visibility (CPU-only option) - gpu.CheckBox.StateChanged += c => - { - Window._sdfOptions.GPU = c.Checked; - backfacesThresholdLabel.Visible = !c.Checked; - backfacesThreshold.ValueBox.Visible = !c.Checked; - }; - backfacesThresholdLabel.Visible = !gpu.CheckBox.Checked; - backfacesThreshold.ValueBox.Visible = !gpu.CheckBox.Checked; - var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building."); lodIndex.IntValue.MinValue = 0; lodIndex.IntValue.MaxValue = Asset.LODsCount - 1; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp index 4bfed7b01..5bb825e65 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTimerQueryDX11.cpp @@ -92,9 +92,8 @@ float GPUTimerQueryDX11::GetResult() { if (!_finalized) { -#if BUILD_DEBUG - ASSERT(HasResult()); -#endif + if (!HasResult()) + return 0; UINT64 timeStart, timeEnd; auto context = _device->GetIM(); diff --git a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp index 11c449847..2cc989059 100644 --- a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp +++ b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp @@ -3,17 +3,29 @@ #if COMPILE_WITH_MODEL_TOOL #include "MeshAccelerationStructure.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" #include "Engine/Content/Content.h" #include "Engine/Content/Assets/Model.h" +#include "Engine/Graphics/GPUBuffer.h" #include "Engine/Graphics/Models/ModelData.h" #include "Engine/Profiler/ProfilerCPU.h" -void MeshAccelerationStructure::BuildBVH(int32 node, int32 maxLeafSize, Array& scratch) +PACK_STRUCT(struct GPUBVH { + Float3 BoundsMin; + uint32 Index; + Float3 BoundsMax; + int32 Count; // Negative for non-leaf nodes +}); +static_assert(sizeof(GPUBVH) == sizeof(Float4) * 2, "Invalid BVH structure size for GPU."); + +void MeshAccelerationStructure::BuildBVH(int32 node, BVHBuild& build) { auto& root = _bvh[node]; ASSERT_LOW_LAYER(root.Leaf.IsLeaf); - if (root.Leaf.TriangleCount <= maxLeafSize) + if (build.MaxLeafSize > 0 && root.Leaf.TriangleCount <= build.MaxLeafSize) + return; + if (build.MaxDepth > 0 && build.NodeDepth >= build.MaxDepth) return; // Spawn two leaves @@ -64,8 +76,8 @@ RETRY: { uint16 I0, I1, I2; }; - scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); - auto dst = (Tri*)scratch.Get(); + build.Scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); + auto dst = (Tri*)build.Scratch.Get(); auto ib16 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { @@ -90,13 +102,13 @@ RETRY: indexStart = 0; indexEnd = left.Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) - left.Bounds.Merge(vb[((uint16*)scratch.Get())[i]]); + left.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]); right.Bounds = BoundingBox(vb[dst[root.Leaf.TriangleCount - 1].I0]); indexStart = left.Leaf.TriangleCount; indexEnd = root.Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) - right.Bounds.Merge(vb[((uint16*)scratch.Get())[i]]); + right.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]); } else { @@ -104,8 +116,8 @@ RETRY: { uint32 I0, I1, I2; }; - scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); - auto dst = (Tri*)scratch.Get(); + build.Scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); + auto dst = (Tri*)build.Scratch.Get(); auto ib32 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) { @@ -130,17 +142,19 @@ RETRY: indexStart = 0; indexEnd = left.Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) - left.Bounds.Merge(vb[((uint32*)scratch.Get())[i]]); + left.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]); right.Bounds = BoundingBox(vb[dst[root.Leaf.TriangleCount - 1].I0]); indexStart = left.Leaf.TriangleCount; indexEnd = root.Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) - right.Bounds.Merge(vb[((uint32*)scratch.Get())[i]]); + right.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]); } ASSERT_LOW_LAYER(left.Leaf.TriangleCount + right.Leaf.TriangleCount == root.Leaf.TriangleCount); left.Leaf.TriangleIndex = root.Leaf.TriangleIndex; right.Leaf.TriangleIndex = left.Leaf.TriangleIndex + left.Leaf.TriangleCount; + build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount); + build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount); // Convert into a node root.Node.IsLeaf = 0; @@ -148,8 +162,11 @@ RETRY: root.Node.ChildrenCount = 2; // Split children - BuildBVH(childIndex, maxLeafSize, scratch); - BuildBVH(childIndex + 1, maxLeafSize, scratch); + build.NodeDepth++; + build.MaxNodeDepth = Math::Max(build.NodeDepth, build.MaxNodeDepth); + BuildBVH(childIndex, build); + BuildBVH(childIndex + 1, build); + build.NodeDepth--; } bool MeshAccelerationStructure::PointQueryBVH(int32 node, const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle) const @@ -160,7 +177,7 @@ bool MeshAccelerationStructure::PointQueryBVH(int32 node, const Vector3& point, { // Find closest triangle Vector3 p; - const Mesh& meshData = _meshes[root.Leaf.MeshIndex]; + const Mesh& meshData = _meshes.Get()[root.Leaf.MeshIndex]; const Float3* vb = meshData.VertexBuffer.Get(); const int32 indexStart = root.Leaf.TriangleIndex * 3; const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; @@ -229,7 +246,7 @@ bool MeshAccelerationStructure::RayCastBVH(int32 node, const Ray& ray, Real& hit if (root.Leaf.IsLeaf) { // Ray cast along triangles in the leaf - const Mesh& meshData = _meshes[root.Leaf.MeshIndex]; + const Mesh& meshData = _meshes.Get()[root.Leaf.MeshIndex]; const Float3* vb = meshData.VertexBuffer.Get(); const int32 indexStart = root.Leaf.TriangleIndex * 3; const int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; @@ -381,6 +398,7 @@ void MeshAccelerationStructure::Add(const ModelData* modelData, int32 lodIndex, void MeshAccelerationStructure::Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy) { + ASSERT(vertices % 3 == 0); auto& meshData = _meshes.AddOne(); meshData.Asset = nullptr; if (copy) @@ -395,43 +413,122 @@ void MeshAccelerationStructure::Add(Float3* vb, int32 vertices, void* ib, int32 meshData.Vertices = vertices; meshData.Indices = indices; meshData.Use16BitIndexBuffer = use16BitIndex; + BoundingBox::FromPoints(meshData.VertexBuffer.Get(), vertices, meshData.Bounds); } -void MeshAccelerationStructure::BuildBVH(int32 maxLeafSize) +void MeshAccelerationStructure::MergeMeshes(bool force16BitIndexBuffer) +{ + if (_meshes.Count() == 0) + return; + if (_meshes.Count() == 1 && (!force16BitIndexBuffer || !_meshes[0].Use16BitIndexBuffer)) + return; + PROFILE_CPU(); + auto meshes = _meshes; + _meshes.Clear(); + _meshes.Resize(1); + auto& mesh = _meshes[0]; + mesh.Asset = nullptr; + mesh.Use16BitIndexBuffer = true; + mesh.Indices = 0; + mesh.Vertices = 0; + mesh.Bounds = meshes[0].Bounds; + for (auto& e : meshes) + { + if (!e.Use16BitIndexBuffer) + mesh.Use16BitIndexBuffer = false; + mesh.Vertices += e.Vertices; + mesh.Indices += e.Indices; + BoundingBox::Merge(mesh.Bounds, e.Bounds, mesh.Bounds); + } + mesh.Use16BitIndexBuffer &= mesh.Indices <= MAX_uint16 && !force16BitIndexBuffer; + mesh.VertexBuffer.Allocate(mesh.Vertices * sizeof(Float3)); + mesh.IndexBuffer.Allocate(mesh.Indices * sizeof(uint32)); + int32 vertexCounter = 0, indexCounter = 0; + for (auto& e : meshes) + { + Platform::MemoryCopy(mesh.VertexBuffer.Get() + vertexCounter * sizeof(Float3), e.VertexBuffer.Get(), e.Vertices * sizeof(Float3)); + if (e.Use16BitIndexBuffer) + { + for (int32 i = 0; i < e.Indices; i++) + { + uint16 index = ((uint16*)e.IndexBuffer.Get())[i]; + ((uint32*)mesh.IndexBuffer.Get())[indexCounter + i] = vertexCounter + index; + } + } + else + { + for (int32 i = 0; i < e.Indices; i++) + { + uint16 index = ((uint32*)e.IndexBuffer.Get())[i]; + ((uint32*)mesh.IndexBuffer.Get())[indexCounter + i] = vertexCounter + index; + } + } + vertexCounter += e.Vertices; + indexCounter += e.Indices; + if (e.Asset) + e.Asset->RemoveReference(); + } +} + +void MeshAccelerationStructure::BuildBVH(int32 maxLeafSize, int32 maxDepth) { if (_meshes.Count() == 0) return; PROFILE_CPU(); + BVHBuild build; + build.MaxLeafSize = maxLeafSize; + build.MaxDepth = maxDepth; + // Estimate memory usage int32 trianglesCount = 0; for (const Mesh& meshData : _meshes) trianglesCount += meshData.Indices / 3; _bvh.Clear(); - _bvh.EnsureCapacity(trianglesCount / maxLeafSize); + _bvh.EnsureCapacity(trianglesCount / Math::Max(maxLeafSize, 16)); - // Init with the root node and all meshes as leaves - auto& root = _bvh.AddOne(); - root.Node.IsLeaf = 0; - root.Node.ChildIndex = 1; - root.Node.ChildrenCount = _meshes.Count(); - root.Bounds = _meshes[0].Bounds; - for (int32 i = 0; i < _meshes.Count(); i++) + // Skip using root node if BVH contains only one mesh + if (_meshes.Count() == 1) { - const Mesh& meshData = _meshes[i]; + const Mesh& meshData = _meshes.First(); auto& child = _bvh.AddOne(); child.Leaf.IsLeaf = 1; - child.Leaf.MeshIndex = i; + child.Leaf.MeshIndex = 0; child.Leaf.TriangleIndex = 0; child.Leaf.TriangleCount = meshData.Indices / 3; child.Bounds = meshData.Bounds; - BoundingBox::Merge(root.Bounds, meshData.Bounds, root.Bounds); + Array scratch; + BuildBVH(0, build); + } + else + { + // Init with the root node and all meshes as leaves + auto& root = _bvh.AddOne(); + root.Node.IsLeaf = 0; + root.Node.ChildIndex = 1; + root.Node.ChildrenCount = _meshes.Count(); + root.Bounds = _meshes[0].Bounds; + for (int32 i = 0; i < _meshes.Count(); i++) + { + const Mesh& meshData = _meshes[i]; + auto& child = _bvh.AddOne(); + child.Leaf.IsLeaf = 1; + child.Leaf.MeshIndex = i; + child.Leaf.TriangleIndex = 0; + child.Leaf.TriangleCount = meshData.Indices / 3; + child.Bounds = meshData.Bounds; + BoundingBox::Merge(root.Bounds, meshData.Bounds, root.Bounds); + } + + // Sub-divide mesh nodes into smaller leaves + build.MaxNodeDepth = build.MaxDepth = 2; + Array scratch; + for (int32 i = 0; i < _meshes.Count(); i++) + BuildBVH(i + 1, build); + build.NodeDepth = 0; } - // Sub-divide mesh nodes into smaller leaves - Array scratch; - for (int32 i = 0; i < _meshes.Count(); i++) - BuildBVH(i + 1, maxLeafSize, scratch); + LOG(Info, "BVH nodes: {}, max depth: {}, max triangles: {}", _bvh.Count(), build.MaxNodeDepth, build.MaxNodeTriangles); } bool MeshAccelerationStructure::PointQuery(const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle, Real maxDistance) const @@ -579,4 +676,80 @@ bool MeshAccelerationStructure::RayCast(const Ray& ray, Real& hitDistance, Vecto } } +MeshAccelerationStructure::GPU::~GPU() +{ + SAFE_DELETE_GPU_RESOURCE(BVHBuffer); + SAFE_DELETE_GPU_RESOURCE(VertexBuffer); + SAFE_DELETE_GPU_RESOURCE(IndexBuffer); +} + +MeshAccelerationStructure::GPU::operator bool() const +{ + // Index buffer is initialized as last one so all other buffers are fine too + return IndexBuffer && IndexBuffer->GetSize() != 0; +} + +MeshAccelerationStructure::GPU MeshAccelerationStructure::ToGPU() +{ + PROFILE_CPU(); + GPU gpu; + + // GPU BVH operates on a single mesh with 32-bit indices + MergeMeshes(true); + + // Construct BVH + const int32 BVH_STACK_SIZE = 32; // This must match HLSL shader + BuildBVH(0, BVH_STACK_SIZE); + + // Upload BVH + { + Array bvhData; + bvhData.Resize(_bvh.Count()); + for (int32 i = 0; i < _bvh.Count(); i++) + { + const auto& src = _bvh.Get()[i]; + auto& dst = bvhData.Get()[i]; + dst.BoundsMin = src.Bounds.Minimum; + dst.BoundsMax = src.Bounds.Maximum; + if (src.Leaf.IsLeaf) + { + dst.Index = src.Leaf.TriangleIndex * 3; + dst.Count = src.Leaf.TriangleCount * 3; + } + else + { + dst.Index = src.Node.ChildIndex; + dst.Count = -(int32)src.Node.ChildrenCount; // Mark as non-leaf + ASSERT(src.Node.ChildrenCount == 2); // GPU shader is hardcoded for 2 children per node + } + } + gpu.BVHBuffer = GPUBuffer::New(); + auto desc =GPUBufferDescription::Structured(_bvh.Count(), sizeof(GPUBVH)); + desc.InitData = bvhData.Get(); + if (gpu.BVHBuffer->Init(desc)) + return gpu; + } + + // Upload vertex buffer + { + const Mesh& mesh = _meshes[0]; + gpu.VertexBuffer = GPUBuffer::New(); + auto desc = GPUBufferDescription::Raw(mesh.Vertices * sizeof(Float3), GPUBufferFlags::ShaderResource); + desc.InitData = mesh.VertexBuffer.Get(); + if (gpu.VertexBuffer->Init(desc)) + return gpu; + } + + // Upload index buffer + { + const Mesh& mesh = _meshes[0]; + gpu.IndexBuffer = GPUBuffer::New(); + auto desc = GPUBufferDescription::Raw(mesh.Indices * sizeof(uint32), GPUBufferFlags::ShaderResource); + desc.InitData = mesh.IndexBuffer.Get(); + gpu.IndexBuffer->Init(desc); + } + + return gpu; +} + #endif diff --git a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h index c40ebc729..60ff772f7 100644 --- a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h +++ b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.h @@ -11,6 +11,7 @@ class Model; class ModelData; +class GPUBuffer; /// /// Acceleration Structure utility for robust ray tracing mesh geometry with optimized data structure. @@ -50,10 +51,19 @@ private: }; }; + struct BVHBuild + { + int32 MaxLeafSize, MaxDepth; + int32 NodeDepth = 0; + int32 MaxNodeDepth = 0; + int32 MaxNodeTriangles = 0; + Array Scratch; + }; + Array> _meshes; Array _bvh; - void BuildBVH(int32 node, int32 maxLeafSize, Array& scratch); + void BuildBVH(int32 node, BVHBuild& build); bool PointQueryBVH(int32 node, const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle) const; bool RayCastBVH(int32 node, const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle) const; @@ -69,14 +79,56 @@ public: // Adds the triangles geometry for the build to the structure. void Add(Float3* vb, int32 vertices, void* ib, int32 indices, bool use16BitIndex, bool copy = false); + // Merges all added meshes into a single mesh (to reduce number of BVH nodes). Required for GPU BVH build. + void MergeMeshes(bool force16BitIndexBuffer = false); + // Builds Bounding Volume Hierarchy (BVH) structure for accelerated geometry queries. - void BuildBVH(int32 maxLeafSize = 16); + void BuildBVH(int32 maxLeafSize = 16, int32 maxDepth = 0); // Queries the closest triangle. bool PointQuery(const Vector3& point, Real& hitDistance, Vector3& hitPoint, Triangle& hitTriangle, Real maxDistance = MAX_Real) const; // Ray traces the triangles. bool RayCast(const Ray& ray, Real& hitDistance, Vector3& hitNormal, Triangle& hitTriangle, Real maxDistance = MAX_Real) const; + +public: + struct GPU + { + GPUBuffer* BVHBuffer; + GPUBuffer* VertexBuffer; + GPUBuffer* IndexBuffer; + bool Valid; + + GPU() + : BVHBuffer(nullptr) + , VertexBuffer(nullptr) + , IndexBuffer(nullptr) + { + } + + GPU(GPU&& other) noexcept + : BVHBuffer(other.BVHBuffer) + , VertexBuffer(other.VertexBuffer) + , IndexBuffer(other.IndexBuffer) + { + other.BVHBuffer = nullptr; + other.VertexBuffer = nullptr; + other.IndexBuffer = nullptr; + } + + GPU& operator=(GPU other) + { + Swap(*this, other); + return *this; + } + + ~GPU(); + + operator bool() const; + }; + + // Converts the acceleration structure data to GPU format for raytracing inside a shader. + GPU ToGPU(); }; #endif diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index 78a3fe515..b3b622942 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -14,6 +14,7 @@ #include "Engine/Threading/Threading.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUBuffer.h" +#include "Engine/Graphics/GPUTimerQuery.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/Async/GPUTask.h" #include "Engine/Graphics/Shaders/GPUShader.h" @@ -81,14 +82,18 @@ class GPUModelSDFTask : public GPUTask { ConditionVariable* _signal; AssetReference _shader; + MeshAccelerationStructure* _scene; Model* _inputModel; const ModelData* _modelData; int32 _lodIndex; + float _backfacesThreshold; Int3 _resolution; ModelBase::SDFData* _sdf; - GPUBuffer *_sdfSrc, *_sdfDst; GPUTexture* _sdfResult; Float3 _xyzToLocalMul, _xyzToLocalAdd; +#if GPU_ALLOW_PROFILE_EVENTS + GPUTimerQuery* _timerQuery; +#endif const uint32 ThreadGroupSize = 64; GPU_CB_STRUCT(Data { @@ -96,7 +101,7 @@ class GPUModelSDFTask : public GPUTask uint32 ResolutionSize; float MaxDistance; uint32 VertexStride; - int32 Index16bit; + float BackfacesThreshold; uint32 TriangleCount; Float3 VoxelToPosMul; float WorldUnitsPerVoxel; @@ -105,47 +110,46 @@ class GPUModelSDFTask : public GPUTask }); public: - GPUModelSDFTask(ConditionVariable& signal, Model* inputModel, const ModelData* modelData, int32 lodIndex, const Int3& resolution, ModelBase::SDFData* sdf, GPUTexture* sdfResult, const Float3& xyzToLocalMul, const Float3& xyzToLocalAdd) - : GPUTask(Type::Custom) + GPUModelSDFTask(ConditionVariable& signal, MeshAccelerationStructure* scene, Model* inputModel, const ModelData* modelData, int32 lodIndex, const Int3& resolution, ModelBase::SDFData* sdf, GPUTexture* sdfResult, const Float3& xyzToLocalMul, const Float3& xyzToLocalAdd, float backfacesThreshold) + : GPUTask(Type::Custom, GPU_ALLOW_PROFILE_EVENTS ? 4 : GPU_ASYNC_LATENCY) // Fix timer query result reading with some more latency , _signal(&signal) , _shader(Content::LoadAsyncInternal(TEXT("Shaders/SDF"))) + , _scene(scene) , _inputModel(inputModel) , _modelData(modelData) , _lodIndex(lodIndex) + , _backfacesThreshold(backfacesThreshold) , _resolution(resolution) , _sdf(sdf) - , _sdfSrc(GPUBuffer::New()) - , _sdfDst(GPUBuffer::New()) , _sdfResult(sdfResult) , _xyzToLocalMul(xyzToLocalMul) , _xyzToLocalAdd(xyzToLocalAdd) - { -#if GPU_ENABLE_RESOURCE_NAMING - _sdfSrc->SetName(TEXT("SDFSrc")); - _sdfDst->SetName(TEXT("SDFDst")); +#if GPU_ALLOW_PROFILE_EVENTS + , _timerQuery(GPUDevice::Instance->CreateTimerQuery()) #endif + { } ~GPUModelSDFTask() { - SAFE_DELETE_GPU_RESOURCE(_sdfSrc); - SAFE_DELETE_GPU_RESOURCE(_sdfDst); +#if GPU_ALLOW_PROFILE_EVENTS + SAFE_DELETE_GPU_RESOURCE(_timerQuery); +#endif } Result run(GPUTasksContext* tasksContext) override { PROFILE_GPU_CPU("GPUModelSDFTask"); GPUContext* context = tasksContext->GPU; +#if GPU_ALLOW_PROFILE_EVENTS + _timerQuery->Begin(); +#endif // Allocate resources if (_shader == nullptr || _shader->WaitForLoaded()) return Result::Failed; GPUShader* shader = _shader->GetShader(); const uint32 resolutionSize = _resolution.X * _resolution.Y * _resolution.Z; - auto desc = GPUBufferDescription::Typed(resolutionSize, PixelFormat::R32_UInt, true); - // TODO: use transient texture (single frame) - if (_sdfSrc->Init(desc) || _sdfDst->Init(desc)) - return Result::Failed; auto cb = shader->GetCB(0); Data data; data.Resolution = _resolution; @@ -154,6 +158,13 @@ public: data.WorldUnitsPerVoxel = _sdf->WorldUnitsPerVoxel; data.VoxelToPosMul = _xyzToLocalMul; data.VoxelToPosAdd = _xyzToLocalAdd; + data.BackfacesThreshold = _backfacesThreshold - 0.05f; // Bias a bit + + // Send BVH to the GPU + auto bvh = _scene->ToGPU(); + CHECK_RETURN(bvh.BVHBuffer && bvh.VertexBuffer && bvh.IndexBuffer, Result::Failed); + data.VertexStride = sizeof(Float3); + data.TriangleCount = bvh.IndexBuffer->GetElementsCount() / 3; // Dispatch in 1D and fallback to 2D when using large resolution Int3 threadGroups(Math::CeilToInt((float)resolutionSize / ThreadGroupSize), 1, 1); @@ -165,159 +176,34 @@ public: } data.ThreadGroupsX = threadGroups.X; - // Init SDF volume + // Init constants context->BindCB(0, cb); context->UpdateCB(cb, &data); - context->BindUA(0, _sdfSrc->View()); - context->Dispatch(shader->GetCS("CS_Init"), threadGroups.X, threadGroups.Y, threadGroups.Z); - // Rendering input triangles into the SDF volume - if (_inputModel) - { - PROFILE_GPU_CPU_NAMED("Rasterize"); - const ModelLOD& lod = _inputModel->LODs[Math::Clamp(_lodIndex, _inputModel->HighestResidentLODIndex(), _inputModel->LODs.Count() - 1)]; - GPUBuffer *vbTemp = nullptr, *ibTemp = nullptr; - for (int32 i = 0; i < lod.Meshes.Count(); i++) - { - const Mesh& mesh = lod.Meshes[i]; - const MaterialSlot& materialSlot = _inputModel->MaterialSlots[mesh.GetMaterialSlotIndex()]; - if (materialSlot.Material && !materialSlot.Material->WaitForLoaded()) - { - // Skip transparent materials - if (materialSlot.Material->GetInfo().BlendMode != MaterialBlendMode::Opaque) - continue; - } - - GPUBuffer* vb = mesh.GetVertexBuffer(0); - GPUBuffer* ib = mesh.GetIndexBuffer(); - data.Index16bit = mesh.Use16BitIndexBuffer() ? 1 : 0; - data.VertexStride = vb->GetStride(); - data.TriangleCount = mesh.GetTriangleCount(); - const uint32 groups = Math::CeilToInt((float)data.TriangleCount / ThreadGroupSize); - if (groups > GPU_MAX_CS_DISPATCH_THREAD_GROUPS) - { - // TODO: support larger meshes via 2D dispatch - LOG(Error, "Not supported mesh with {} triangles.", data.TriangleCount); - continue; - } - context->UpdateCB(cb, &data); - if (!EnumHasAllFlags(vb->GetDescription().Flags, GPUBufferFlags::RawBuffer | GPUBufferFlags::ShaderResource)) - { - desc = GPUBufferDescription::Raw(vb->GetSize(), GPUBufferFlags::ShaderResource); - // TODO: use transient buffer (single frame) - if (!vbTemp) - { - vbTemp = GPUBuffer::New(); -#if GPU_ENABLE_RESOURCE_NAMING - vbTemp->SetName(TEXT("SDFvb")); -#endif - } - vbTemp->Init(desc); - context->CopyBuffer(vbTemp, vb, desc.Size); - vb = vbTemp; - } - if (!EnumHasAllFlags(ib->GetDescription().Flags, GPUBufferFlags::RawBuffer | GPUBufferFlags::ShaderResource)) - { - desc = GPUBufferDescription::Raw(ib->GetSize(), GPUBufferFlags::ShaderResource); - // TODO: use transient buffer (single frame) - if (!ibTemp) - { - ibTemp = GPUBuffer::New(); -#if GPU_ENABLE_RESOURCE_NAMING - ibTemp->SetName(TEXT("SDFib")); -#endif - } - ibTemp->Init(desc); - context->CopyBuffer(ibTemp, ib, desc.Size); - ib = ibTemp; - } - context->BindSR(0, vb->View()); - context->BindSR(1, ib->View()); - context->Dispatch(shader->GetCS("CS_RasterizeTriangle"), groups, 1, 1); - } - SAFE_DELETE_GPU_RESOURCE(vbTemp); - SAFE_DELETE_GPU_RESOURCE(ibTemp); - } - else if (_modelData) - { - PROFILE_GPU_CPU_NAMED("Rasterize"); - const ModelLodData& lod = _modelData->LODs[Math::Clamp(_lodIndex, 0, _modelData->LODs.Count() - 1)]; - auto vb = GPUBuffer::New(); - auto ib = GPUBuffer::New(); -#if GPU_ENABLE_RESOURCE_NAMING - vb->SetName(TEXT("SDFvb")); - ib->SetName(TEXT("SDFib")); -#endif - for (int32 i = 0; i < lod.Meshes.Count(); i++) - { - const MeshData* mesh = lod.Meshes[i]; - const MaterialSlotEntry& materialSlot = _modelData->Materials[mesh->MaterialSlotIndex]; - auto material = Content::LoadAsync(materialSlot.AssetID); - if (material && !material->WaitForLoaded()) - { - // Skip transparent materials - if (material->GetInfo().BlendMode != MaterialBlendMode::Opaque) - continue; - } - - data.Index16bit = 0; - data.VertexStride = sizeof(Float3); - data.TriangleCount = mesh->Indices.Count() / 3; - const uint32 groups = Math::CeilToInt((float)data.TriangleCount / ThreadGroupSize); - if (groups > GPU_MAX_CS_DISPATCH_THREAD_GROUPS) - { - // TODO: support larger meshes via 2D dispatch - LOG(Error, "Not supported mesh with {} triangles.", data.TriangleCount); - continue; - } - context->UpdateCB(cb, &data); - desc = GPUBufferDescription::Raw(mesh->Positions.Count() * sizeof(Float3), GPUBufferFlags::ShaderResource); - desc.InitData = mesh->Positions.Get(); - // TODO: use transient buffer (single frame) - vb->Init(desc); - desc = GPUBufferDescription::Raw(mesh->Indices.Count() * sizeof(uint32), GPUBufferFlags::ShaderResource); - desc.InitData = mesh->Indices.Get(); - // TODO: use transient buffer (single frame) - ib->Init(desc); - context->BindSR(0, vb->View()); - context->BindSR(1, ib->View()); - context->Dispatch(shader->GetCS("CS_RasterizeTriangle"), groups, 1, 1); - } - SAFE_DELETE_GPU_RESOURCE(vb); - SAFE_DELETE_GPU_RESOURCE(ib); - } - - // Convert SDF volume data back to floats - context->Dispatch(shader->GetCS("CS_Resolve"), threadGroups.X, threadGroups.Y, threadGroups.Z); - - // Run linear flood-fill loop to populate all voxels with valid distances (spreads the initial values from triangles rasterization) - { - PROFILE_GPU_CPU_NAMED("FloodFill"); - auto csFloodFill = shader->GetCS("CS_FloodFill"); - const int32 floodFillIterations = Math::Max(_resolution.MaxValue() / 2 + 1, 8); - for (int32 floodFill = 0; floodFill < floodFillIterations; floodFill++) - { - context->ResetUA(); - context->BindUA(0, _sdfDst->View()); - context->BindSR(0, _sdfSrc->View()); - context->Dispatch(csFloodFill, threadGroups.X, threadGroups.Y, threadGroups.Z); - Swap(_sdfSrc, _sdfDst); - } - } - - // Encode SDF values into output storage - context->ResetUA(); - context->BindSR(0, _sdfSrc->View()); - // TODO: update GPU SDF texture within this task to skip additional CPU->GPU copy - auto sdfTextureDesc = GPUTextureDescription::New3D(_resolution.X, _resolution.Y, _resolution.Z, PixelFormat::R16_UNorm, GPUTextureFlags::UnorderedAccess | GPUTextureFlags::RenderTarget); + // Allocate output texture + auto sdfTextureDesc = GPUTextureDescription::New3D(_resolution.X, _resolution.Y, _resolution.Z, PixelFormat::R16_UNorm, GPUTextureFlags::UnorderedAccess); // TODO: use transient texture (single frame) auto sdfTexture = GPUTexture::New(); #if GPU_ENABLE_RESOURCE_NAMING sdfTexture->SetName(TEXT("SDFTexture")); #endif sdfTexture->Init(sdfTextureDesc); - context->BindUA(1, sdfTexture->ViewVolume()); - context->Dispatch(shader->GetCS("CS_Encode"), threadGroups.X, threadGroups.Y, threadGroups.Z); + + // Renders directly to the output texture + context->BindUA(0, sdfTexture->ViewVolume()); + + // Init the volume (rasterization mixes with existing contents) + context->Dispatch(shader->GetCS("CS_Init"), threadGroups.X, threadGroups.Y, threadGroups.Z); + + // Render input triangles into the SDF volume + { + PROFILE_GPU("Rasterize"); + context->BindSR(0, bvh.VertexBuffer->View()); + context->BindSR(1, bvh.IndexBuffer->View()); + context->BindSR(2, bvh.BVHBuffer->View()); + auto* rasterizeCS = shader->GetCS("CS_RasterizeTriangles"); + context->Dispatch(rasterizeCS, threadGroups.X, threadGroups.Y, threadGroups.Z); + } // Copy result data into readback buffer if (_sdfResult) @@ -329,6 +215,9 @@ public: SAFE_DELETE_GPU_RESOURCE(sdfTexture); +#if GPU_ALLOW_PROFILE_EVENTS + _timerQuery->End(); +#endif return Result::Ok; } @@ -336,6 +225,10 @@ public: { GPUTask::OnSync(); _signal->NotifyOne(); +#if GPU_ALLOW_PROFILE_EVENTS + if (_timerQuery->HasResult()) + LOG(Info, "GPU SDF generation took {} ms", Utilities::RoundTo1DecimalPlace(_timerQuery->GetResult())); +#endif } void OnFail() override @@ -445,6 +338,13 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, // http://ramakarl.com/pdfs/2016_Hoetzlein_GVDB.pdf // https://www.cse.chalmers.se/~uffe/HighResolutionSparseVoxelDAGs.pdf + // Setup acceleration structure for fast ray tracing the mesh triangles + MeshAccelerationStructure scene; + if (inputModel) + scene.Add(inputModel, lodIndex); + else if (modelData) + scene.Add(modelData, lodIndex); + // Check if run SDF generation on a GPU via Compute Shader or on a Job System useGPU &= GPUDevice::Instance && GPUDevice::Instance->GetState() == GPUDevice::DeviceState::Ready @@ -465,7 +365,7 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, // Run SDF generation via GPU async task ConditionVariable signal; CriticalSection mutex; - Task* task = New(signal, inputModel, modelData, lodIndex, resolution, &sdf, sdfResult, xyzToLocalMul, xyzToLocalAdd); + Task* task = New(signal, &scene, inputModel, modelData, lodIndex, resolution, &sdf, sdfResult, xyzToLocalMul, xyzToLocalAdd, backfacesThreshold); task->Start(); mutex.Lock(); signal.Wait(mutex); @@ -489,16 +389,10 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, } else { - // Setup acceleration structure for fast ray tracing the mesh triangles - MeshAccelerationStructure scene; - if (inputModel) - scene.Add(inputModel, lodIndex); - else if (modelData) - scene.Add(modelData, lodIndex); scene.BuildBVH(); // Brute-force for each voxel to calculate distance to the closest triangle with point query and distance sign by raycasting around the voxel - constexpr int32 sampleCount = 12; + constexpr int32 sampleCount = BUILD_DEBUG ? 6 : 12; Float3 sampleDirections[sampleCount]; { RandomStream rand; @@ -526,36 +420,30 @@ bool ModelTool::GenerateModelSDF(Model* inputModel, const ModelData* modelData, Real minDistance = sdf.MaxDistance; Vector3 voxelPos = Float3((float)x, (float)y, (float)z) * xyzToLocalMul + xyzToLocalAdd; - // Point query to find the distance to the closest surface - scene.PointQuery(voxelPos, minDistance, hitPoint, hitTriangle); - // Raycast samples around voxel to count triangle backfaces hit - int32 hitBackCount = 0, hitCount = 0; + int32 hitBackCount = 0, minBackfaceHitCount = (int32)(sampleCount * backfacesThreshold); for (int32 sample = 0; sample < sampleCount; sample++) { Ray sampleRay(voxelPos, sampleDirections[sample]); sampleRay.Position -= sampleRay.Direction * 0.0001f; // Apply small margin if (scene.RayCast(sampleRay, hitDistance, hitNormal, hitTriangle)) { - if (hitDistance < minDistance) - minDistance = hitDistance; - hitCount++; - const bool backHit = Float3::Dot(sampleRay.Direction, hitTriangle.GetNormal()) > 0; - if (backHit) - hitBackCount++; + minDistance = Math::Min(hitDistance, minDistance); + if (Float3::Dot(sampleRay.Direction, hitTriangle.GetNormal()) > 0) + { + if (++hitBackCount >= minBackfaceHitCount) + break; + } } } - float distance = (float)minDistance; - // TODO: surface thickness threshold? shift reduce distance for all voxels by something like 0.01 to enlarge thin geometry - // if ((float)hitBackCount > (float)hitCount * 0.3f && hitCount != 0) - if ((float)hitBackCount > (float)sampleCount * backfacesThreshold && hitCount != 0) - { - // Voxel is inside the geometry so turn it into negative distance to the surface - distance *= -1; - } + // Point query to find the distance to the closest surface + scene.PointQuery(voxelPos, minDistance, hitPoint, hitTriangle, minDistance); + if (hitBackCount >= minBackfaceHitCount) + minDistance *= -1; // Voxel is inside the geometry so turn it into negative distance to the surface + const int32 xAddress = x + yAddress; - formatWrite(voxels.Get() + xAddress * formatStride, distance * encodeMAD.X + encodeMAD.Y); + formatWrite(voxels.Get() + xAddress * formatStride, minDistance * encodeMAD.X + encodeMAD.Y); } } }; diff --git a/Source/Shaders/Collisions.hlsl b/Source/Shaders/Collisions.hlsl index 2c503be9a..6d61a8c6a 100644 --- a/Source/Shaders/Collisions.hlsl +++ b/Source/Shaders/Collisions.hlsl @@ -18,6 +18,26 @@ bool RayHitRect(float3 r, float3 rectCenter, float3 rectX, float3 rectY, float3 return inExtentX && inExtentY; } +// Determines whether there is an intersection between a ray (rPos and rDir) and a triangle (v0, v1, v2). +// Returns true on intersection and outputs the distance along the ray to the intersection point. +// This method tests if the ray intersects either the front or back of the triangle. +bool RayIntersectsTriangle(float3 rPos, float3 rDir, float3 v0, float3 v1, float3 v2, out float distance) +{ + // [https://stackoverflow.com/a/42752998] + float3 edgeAB = v1 - v0; + float3 edgeAC = v2 - v0; + float3 triFaceVector = cross(edgeAB, edgeAC); + float3 vertRayOffset = rPos - v0; + float3 rayOffsetPerp = cross(vertRayOffset, rDir); + float determinant = -dot(rDir, triFaceVector); + float invDet = 1.0f / determinant; + distance = dot(vertRayOffset, triFaceVector) * invDet; + float u = dot(edgeAC, rayOffsetPerp) * invDet; + float v = -dot(edgeAB, rayOffsetPerp) * invDet; + float w = 1.0f - u - v; + return abs(determinant) >= 1E-8 && distance > 0 && u >= 0 && v >= 0 && w >= 0; +} + // Hits axis-aligned box (boxMin, boxMax) with a line (lineStart, lineEnd). // Returns the intersections on the line (x - closest, y - furthest). // Line hits the box if: intersections.x < intersections.y. @@ -42,4 +62,39 @@ bool BoxIntersectsSphere(float3 boxMin, float3 boxMax, float3 sphereCenter, floa return distance(sphereCenter, clampedCenter) <= sphereRadius; } +// Calculates unsigned distance from point to the AABB. If point is inside it, returns 0. +float PointDistanceBox(float3 boxMin, float3 boxMax, float3 pos) +{ + float3 clampedPos = clamp(pos, boxMin, boxMax); + return length(clampedPos - pos); +} + +float dot2(float3 v) +{ + return dot(v, v); +} + +// Calculates squared distance from point to the triangle. +float DistancePointToTriangle2(float3 p, float3 v1, float3 v2, float3 v3) +{ + // [Inigo Quilez, https://iquilezles.org/articles/triangledistance/] + float3 v21 = v2 - v1; float3 p1 = p - v1; + float3 v32 = v3 - v2; float3 p2 = p - v2; + float3 v13 = v1 - v3; float3 p3 = p - v3; + float3 nor = cross(v21, v13); + return // inside/outside test + (sign(dot(cross(v21, nor), p1)) + + sign(dot(cross(v32, nor), p2)) + + sign(dot(cross(v13, nor), p3)) < 2.0) + ? + // 3 edges + min(min( + dot2(v21 * saturate(dot(v21, p1) / dot2(v21)) - p1), + dot2(v32 * saturate(dot(v32, p2) / dot2(v32)) - p2)), + dot2(v13 * saturate(dot(v13, p3) / dot2(v13)) - p3)) + : + // 1 face + dot(nor, p1) * dot(nor, p1) / dot2(nor); +} + #endif diff --git a/Source/Shaders/MeshAccelerationStructure.hlsl b/Source/Shaders/MeshAccelerationStructure.hlsl new file mode 100644 index 000000000..c0ae24835 --- /dev/null +++ b/Source/Shaders/MeshAccelerationStructure.hlsl @@ -0,0 +1,163 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +#ifndef __MESH_ACCELERATION_STRUCTURE__ +#define __MESH_ACCELERATION_STRUCTURE__ + +#include "./Flax/Collisions.hlsl" + +// This must match MeshAccelerationStructure::ToGPU +#define BVH_STACK_SIZE 32 + +struct BVHNode +{ + float3 BoundsMin; + uint Index; + float3 BoundsMax; + int Count; // Negative for non-leaf nodes +}; + +struct BVHBuffers +{ + StructuredBuffer BVHBuffer; + ByteAddressBuffer VertexBuffer; + ByteAddressBuffer IndexBuffer; + uint VertexStride; +}; + +struct BVHHit +{ + float Distance; + bool IsBackface; +}; + +float3 LoadVertexBVH(BVHBuffers bvh, uint index) +{ + index = bvh.IndexBuffer.Load(index << 2u); + return asfloat(bvh.VertexBuffer.Load3(index * bvh.VertexStride)); +} + +// [https://tavianator.com/2011/ray_box.html] +float RayTestBoxBVH(float3 rayPos, float3 rayDir, float3 boxMin, float3 boxMax) +{ + float3 rayInvDir = rcp(rayDir); + float3 tMin = (boxMin - rayPos) * rayInvDir; + float3 tMax = (boxMax - rayPos) * rayInvDir; + float3 t1 = min(tMin, tMax); + float tNear = max(max(t1.x, t1.y), t1.z); + float3 t2 = max(tMin, tMax); + float tFar = min(min(t2.x, t2.y), t2.z); + bool hit = tFar >= tNear && tFar > 0; + return hit ? max(tNear, 0) : -1; +} + +// Performs raytracing against the BVH acceleration structure to find the closest intersection with a triangle. +bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, float maxDistance = 1000000.0f) +{ + hit = (BVHHit)0; + hit.Distance = maxDistance; + + // Stack-based recursion, starts from root node + uint stack[BVH_STACK_SIZE]; + uint stackCount = 1; + stack[0] = 0; + + bool result = false; + LOOP + while (stackCount > 0) + { + BVHNode node = bvh.BVHBuffer[stack[--stackCount]]; + + // Raytrace bounds + float boundsHit = RayTestBoxBVH(rayPos, rayDir, node.BoundsMin, node.BoundsMax); + BRANCH + if (boundsHit >= 0 && boundsHit < hit.Distance) + { + BRANCH + if (node.Count > 0) // Is leaf? + { + // Ray cast along all triangles in the leaf + uint indexStart = node.Index; + uint indexEnd = indexStart + node.Count; + for (uint i = indexStart; i < indexEnd;) + { + // Load triangle + float3 v0 = LoadVertexBVH(bvh, i++); + float3 v1 = LoadVertexBVH(bvh, i++); + float3 v2 = LoadVertexBVH(bvh, i++); + + // Raytrace triangle + float distance; + if (RayIntersectsTriangle(rayPos, rayDir, v0, v1, v2, distance) && distance < hit.Distance) + { + float3 n = normalize(cross(v1 - v0, v2 - v0)); + hit.Distance = distance; + hit.IsBackface = dot(rayDir, n) > 0; + result = true; + } + } + } + else + { + // Push children onto the stack to be tested + stack[stackCount++] = node.Index + 0; + stack[stackCount++] = node.Index + 1; + } + } + } + return result; +} + +// Performs a query against the BVH acceleration structure to find the closest distance to a triangle from a given point. +bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance = 1000000.0f) +{ + hit = (BVHHit)0; + hit.Distance = maxDistance; + + // Stack-based recursion, starts from root node + uint stack[BVH_STACK_SIZE]; + uint stackCount = 1; + stack[0] = 0; + + bool result = false; + LOOP + while (stackCount > 0) + { + BVHNode node = bvh.BVHBuffer[stack[--stackCount]]; + + // Skip too far nodes + if (PointDistanceBox(node.BoundsMin, node.BoundsMax, pos) >= hit.Distance) + continue; + + BRANCH + if (node.Count > 0) // Is leaf? + { + // Find the closest triangles in the leaf + uint indexStart = node.Index; + uint indexEnd = indexStart + node.Count; + for (uint i = indexStart; i < indexEnd;) + { + // Load triangle + float3 v0 = LoadVertexBVH(bvh, i++); + float3 v1 = LoadVertexBVH(bvh, i++); + float3 v2 = LoadVertexBVH(bvh, i++); + + // Check triangle + float distance = sqrt(DistancePointToTriangle2(pos, v0, v1, v2)); + if (distance < hit.Distance) + { + hit.Distance = distance; + result = true; + } + } + } + else + { + // Push children onto the stack to be tested + stack[stackCount++] = node.Index + 0; + stack[stackCount++] = node.Index + 1; + } + } + return result; +} + +#endif diff --git a/Source/Shaders/SDF.shader b/Source/Shaders/SDF.shader index 2239e2dbe..b2ae928f7 100644 --- a/Source/Shaders/SDF.shader +++ b/Source/Shaders/SDF.shader @@ -1,18 +1,14 @@ // Copyright (c) Wojciech Figat. All rights reserved. -// Mesh SDF generation based on https://github.com/GPUOpen-Effects/TressFX - #include "./Flax/Common.hlsl" -#include "./Flax/ThirdParty/TressFX/TressFXSDF.hlsl" - -#define THREAD_GROUP_SIZE 64 +#include "./Flax/MeshAccelerationStructure.hlsl" META_CB_BEGIN(0, Data) int3 Resolution; uint ResolutionSize; float MaxDistance; uint VertexStride; -bool Index16bit; +float BackfacesThreshold; uint TriangleCount; float3 VoxelToPosMul; float WorldUnitsPerVoxel; @@ -20,21 +16,9 @@ float3 VoxelToPosAdd; uint ThreadGroupsX; META_CB_END -RWBuffer SDF : register(u0); - -uint GetVoxelIndex(uint3 groupId, uint groupIndex) +uint GetVoxelIndex(uint3 groupId, uint groupIndex, uint groupSize) { - return groupIndex + (groupId.x + groupId.y * ThreadGroupsX) * THREAD_GROUP_SIZE; -} - -int3 ClampVoxelCoord(int3 coord) -{ - return clamp(coord, 0, Resolution - 1); -} - -int GetVoxelIndex(int3 coord) -{ - return Resolution.x * Resolution.y * coord.z + Resolution.x * coord.y + coord.x; + return groupIndex + (groupId.x + groupId.y * ThreadGroupsX) * groupSize; } float3 GetVoxelPos(int3 coord) @@ -42,12 +26,6 @@ float3 GetVoxelPos(int3 coord) return float3((float)coord.x, (float)coord.y, (float)coord.z) * VoxelToPosMul + VoxelToPosAdd; } -int3 GetVoxelCoord(float3 pos) -{ - pos = (pos - VoxelToPosAdd) / VoxelToPosMul; - return int3((int)pos.x, (int)pos.y, (int)pos.z); -} - int3 GetVoxelCoord(uint index) { uint sizeX = (uint)Resolution.x; @@ -59,191 +37,90 @@ int3 GetVoxelCoord(uint index) return int3((int)coordX, (int)coordY, (int)coordZ); } -// Clears SDF texture with the initial distance. +#ifdef _CS_Init + +#define THREAD_GROUP_SIZE 64 + +RWTexture3D SDFtex : register(u0); + +// Clears SDF texture with the maximum distance. META_CS(true, FEATURE_LEVEL_SM5) [numthreads(THREAD_GROUP_SIZE, 1, 1)] void CS_Init(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) { - uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex); + uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex, THREAD_GROUP_SIZE); if (voxelIndex >= ResolutionSize) return; - float distance = MaxDistance * 10.0f; // Start with a very large value - SDF[voxelIndex] = FloatFlip3(distance); + int3 voxelCoord = GetVoxelCoord(voxelIndex); + SDFtex[voxelCoord] = 1.0f; } -// Unpacks SDF texture into distances stores as normal float value (FloatFlip3 is used for interlocked operations on uint). -META_CS(true, FEATURE_LEVEL_SM5) -[numthreads(THREAD_GROUP_SIZE, 1, 1)] -void CS_Resolve(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) -{ - uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex); - if (voxelIndex >= ResolutionSize) - return; - SDF[voxelIndex] = IFloatFlip3(SDF[voxelIndex]); -} +#endif -#ifdef _CS_RasterizeTriangle +#ifdef _CS_RasterizeTriangles +#define THREAD_GROUP_SIZE 64 + +RWTexture3D SDFtex : register(u0); ByteAddressBuffer VertexBuffer : register(t0); ByteAddressBuffer IndexBuffer : register(t1); - -uint LoadIndex(uint i) -{ - if (Index16bit) - { - uint index = IndexBuffer.Load((i >> 1u) << 2u); - index = (i & 1u) == 1u ? (index >> 16) : index; - return index & 0xffff; - } - return IndexBuffer.Load(i << 2u); -} - -float3 LoadVertex(uint i) -{ - return asfloat(VertexBuffer.Load3(i * VertexStride)); -} +StructuredBuffer BVHBuffer : register(t2); // Renders triangle mesh into the SDF texture by writing minimum distance to the triangle into all intersecting voxels. META_CS(true, FEATURE_LEVEL_SM5) [numthreads(THREAD_GROUP_SIZE, 1, 1)] -void CS_RasterizeTriangle(uint3 DispatchThreadId : SV_DispatchThreadID) +void CS_RasterizeTriangles(uint3 GroupId : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadID, uint GroupIndex : SV_GroupIndex) { - uint triangleIndex = DispatchThreadId.x; - if (triangleIndex >= TriangleCount) + uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex, THREAD_GROUP_SIZE); + if (voxelIndex >= ResolutionSize) return; + int3 voxelCoord = GetVoxelCoord(voxelIndex); + float3 voxelPos = GetVoxelPos(voxelCoord); - // Load triangle - triangleIndex *= 3; - uint i0 = LoadIndex(triangleIndex + 0); - uint i1 = LoadIndex(triangleIndex + 1); - uint i2 = LoadIndex(triangleIndex + 2); - float3 v0 = LoadVertex(i0); - float3 v1 = LoadVertex(i1); - float3 v2 = LoadVertex(i2); + BVHBuffers bvh; + bvh.BVHBuffer = BVHBuffer; + bvh.VertexBuffer = VertexBuffer; + bvh.IndexBuffer = IndexBuffer; + bvh.VertexStride = VertexStride; - // Project triangle into SDF voxels - float3 vMargin = float3(WorldUnitsPerVoxel, WorldUnitsPerVoxel, WorldUnitsPerVoxel); - float3 vMin = min(min(v0, v1), v2) - vMargin; - float3 vMax = max(max(v0, v1), v2) + vMargin; - int3 voxelMargin = int3(1, 1, 1); - int3 voxelMin = GetVoxelCoord(vMin) - voxelMargin; - int3 voxelMax = GetVoxelCoord(vMax) + voxelMargin; - voxelMin = ClampVoxelCoord(voxelMin); - voxelMax = ClampVoxelCoord(voxelMax); + // Point query to find the distance to the closest surface + BVHHit hit; + PointQueryBVH(bvh, voxelPos, hit, MaxDistance); + float sdf = hit.Distance; - // Rasterize into SDF voxels - for (int z = voxelMin.z; z <= voxelMax.z; z++) + // Raycast triangles around voxel to count triangle backfaces hit + #define CLOSEST_CACHE_SIZE 6 + float3 closestDirections[CLOSEST_CACHE_SIZE] = { - for (int y = voxelMin.y; y <= voxelMax.y; y++) + float3(+1, 0, 0), + float3(-1, 0, 0), + float3(0, +1, 0), + float3(0, -1, 0), + float3(0, 0, +1), + float3(0, 0, -1), + }; + uint hitBackCount = 0; + uint minBackfaceHitCount = (uint)(CLOSEST_CACHE_SIZE * BackfacesThreshold); + for (uint i = 0; i < CLOSEST_CACHE_SIZE; i++) + { + float3 rayDir = closestDirections[i]; + if (RayCastBVH(bvh, voxelPos, rayDir, hit, MaxDistance)) { - for (int x = voxelMin.x; x <= voxelMax.x; x++) - { - int3 voxelCoord = int3(x, y, z); - int voxelIndex = GetVoxelIndex(voxelCoord); - float3 voxelPos = GetVoxelPos(voxelCoord); - float distance = SignedDistancePointToTriangle(voxelPos, v0, v1, v2); -#if 0 - if (distance < -10.0f) // TODO: find a better way to reject negative distance from degenerate triangles that break SDF shape - distance = abs(distance); -#endif - InterlockedMin(SDF[voxelIndex], FloatFlip3(distance)); - } + sdf = min(sdf, hit.Distance); + if (hit.IsBackface) + hitBackCount++; } } -} - -#endif - -#if defined(_CS_FloodFill) || defined(_CS_Encode) - -Buffer InSDF : register(t0); - -float GetVoxel(int voxelIndex) -{ - return asfloat(InSDF[voxelIndex]); -} - -float GetVoxel(int3 coord) -{ - coord = ClampVoxelCoord(coord); - int voxelIndex = GetVoxelIndex(coord); - return GetVoxel(voxelIndex); -} - -float CombineSDF(float sdf, int3 nearbyCoord, float nearbyDistance) -{ - // Sample nearby voxel - float sdfNearby = GetVoxel(nearbyCoord); - - // Include distance to that nearby voxel - if (sdfNearby < 0.0f) - nearbyDistance *= -1; - sdfNearby += nearbyDistance; - - if (sdfNearby > MaxDistance) + if (hitBackCount >= minBackfaceHitCount) { - // Ignore if nearby sample is invalid (see CS_Init) + // Voxel is inside the geometry so turn it into negative distance to the surface + sdf *= -1; } - else if (sdf > MaxDistance) - { - // Use nearby sample if current one is invalid (see CS_Init) - sdf = sdfNearby; - } - else - { - // Use distance closer to 0 - sdf = sdf >= 0 ? min(sdf, sdfNearby) : max(sdf, sdfNearby); - } - - return sdf; -} - -// Fills the voxels with minimum distances to the triangles. -META_CS(true, FEATURE_LEVEL_SM5) -[numthreads(THREAD_GROUP_SIZE, 1, 1)] -void CS_FloodFill(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) -{ - uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex); - if (voxelIndex >= ResolutionSize) - return; - float sdf = GetVoxel(voxelIndex); - - // Skip if the distance is already so small that we know that triangle is nearby - if (abs(sdf) > WorldUnitsPerVoxel * 1.2f) - { - int3 voxelCoord = GetVoxelCoord(voxelIndex); - int3 offset = int3(-1, 0, 1); - - // Sample nearby voxels - float nearbyDistance = WorldUnitsPerVoxel; - sdf = CombineSDF(sdf, voxelCoord + offset.zyy, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.yzy, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.yyz, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.xyy, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.yxy, nearbyDistance); - sdf = CombineSDF(sdf, voxelCoord + offset.yyx, nearbyDistance); - } - - SDF[voxelIndex] = asuint(sdf); -} - -RWTexture3D SDFtex : register(u1); - -// Encodes SDF values into the packed format with normalized distances. -META_CS(true, FEATURE_LEVEL_SM5) -[numthreads(THREAD_GROUP_SIZE, 1, 1)] -void CS_Encode(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex) -{ - uint voxelIndex = GetVoxelIndex(GroupId, GroupIndex); - if (voxelIndex >= ResolutionSize) - return; - float sdf = GetVoxel(voxelIndex); - sdf = min(sdf, MaxDistance); // Pack from range [-MaxDistance; +MaxDistance] to [0; 1] + sdf = clamp(sdf, -MaxDistance, MaxDistance); sdf = (sdf / MaxDistance) * 0.5f + 0.5f; - int3 voxelCoord = GetVoxelCoord(voxelIndex); SDFtex[voxelCoord] = sdf; } diff --git a/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl b/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl deleted file mode 100644 index 7128ba9ca..000000000 --- a/Source/Shaders/ThirdParty/TressFX/TressFXSDF.hlsl +++ /dev/null @@ -1,129 +0,0 @@ -// Source: https://github.com/GPUOpen-Effects/TressFX -// License: MIT - -// -// Copyright (c) 2019 Advanced Micro Devices, Inc. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. -// - -//When building the SDF we want to find the lowest distance at each SDF cell. In order to allow multiple threads to write to the same -//cells, it is necessary to use atomics. However, there is no support for atomics with 32-bit floats so we convert the float into unsigned int -//and use atomic_min() / InterlockedMin() as a workaround. -// -//When used with atomic_min, both FloatFlip2() and FloatFlip3() will store the float with the lowest magnitude. -//The difference is that FloatFlip2() will preper negative values ( InterlockedMin( FloatFlip2(1.0), FloatFlip2(-1.0) ) == -1.0 ), -//while FloatFlip3() prefers positive values ( InterlockedMin( FloatFlip3(1.0), FloatFlip3(-1.0) ) == 1.0 ). -//Using FloatFlip3() seems to result in a SDF with higher quality compared to FloatFlip2(). -uint FloatFlip2(float fl) -{ - uint f = asuint(fl); - return (f << 1) | (f >> 31 ^ 0x00000001); //Rotate sign bit to least significant and Flip sign bit so that (0 == negative) -} -uint IFloatFlip2(uint f2) -{ - return (f2 >> 1) | (f2 << 31 ^ 0x80000000); -} -uint FloatFlip3(float fl) -{ - uint f = asuint(fl); - return (f << 1) | (f >> 31); //Rotate sign bit to least significant -} -uint IFloatFlip3(uint f2) -{ - return (f2 >> 1) | (f2 << 31); -} - -float DistancePointToEdge(float3 p, float3 x0, float3 x1, out float3 n) -{ - // Hack to swap to ensure the order is correct (.x only for simplicity) - if (x0.x > x1.x) - { - float3 temp = x0; - x0 = x1; - x1 = temp; - } - - float3 x10 = x1 - x0; - - float t = dot(x1 - p, x10) / dot(x10, x10); - t = max(0.0f, min(t, 1.0f)); - - float3 a = p - (t*x0 + (1.0f - t)*x1); - float d = length(a); - n = a / (d + 1e-30f); - - return d; -} - -// Check if p is in the positive or negative side of triangle (x0, x1, x2) -// Positive side is where the normal vector of triangle ( (x1-x0) x (x2-x0) ) is pointing to. -float SignedDistancePointToTriangle(float3 p, float3 x0, float3 x1, float3 x2) -{ - float d = 0; - float3 x02 = x0 - x2; - float l0 = length(x02) + 1e-30f; - x02 = x02 / l0; - float3 x12 = x1 - x2; - float l1 = dot(x12, x02); - x12 = x12 - l1*x02; - float l2 = length(x12) + 1e-30f; - x12 = x12 / l2; - float3 px2 = p - x2; - - float b = dot(x12, px2) / l2; - float a = (dot(x02, px2) - l1*b) / l0; - float c = 1 - a - b; - - // normal vector of triangle. Don't need to normalize this yet. - float3 nTri = cross((x1 - x0), (x2 - x0)); - float3 n; - - float tol = 1e-8f; - - if (a >= -tol && b >= -tol && c >= -tol) - { - n = p - (a*x0 + b*x1 + c*x2); - d = length(n); - - float3 n1 = n / d; - float3 n2 = nTri / (length(nTri) + 1e-30f); // if d == 0 - - n = (d > 0) ? n1 : n2; - } - else - { - float3 n_12; - float3 n_02; - d = DistancePointToEdge(p, x0, x1, n); - - float d12 = DistancePointToEdge(p, x1, x2, n_12); - float d02 = DistancePointToEdge(p, x0, x2, n_02); - - d = min(d, d12); - d = min(d, d02); - - n = (d == d12) ? n_12 : n; - n = (d == d02) ? n_02 : n; - } - - d = (dot(p - x0, nTri) < 0.f) ? -d : d; - - return d; -} From 69173803bfc86cbd1265d57cc4d8531497e78464 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 13 Nov 2025 22:09:11 +0100 Subject: [PATCH 076/207] Fix shader warning on Vulkan --- Content/Shaders/GI/GlobalSurfaceAtlas.flax | 4 ++-- Source/Shaders/GI/GlobalSurfaceAtlas.shader | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Content/Shaders/GI/GlobalSurfaceAtlas.flax b/Content/Shaders/GI/GlobalSurfaceAtlas.flax index c0ee573e0..1b0173ba5 100644 --- a/Content/Shaders/GI/GlobalSurfaceAtlas.flax +++ b/Content/Shaders/GI/GlobalSurfaceAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64a53e850ac662cb98ce91a8122dece7a43562f55a85c37f830f44324e4d16c5 -size 12925 +oid sha256:0f34bf867df5f4296ca66ac691c2bca4efa168fb9e21ca4e613e8086669575cf +size 13296 diff --git a/Source/Shaders/GI/GlobalSurfaceAtlas.shader b/Source/Shaders/GI/GlobalSurfaceAtlas.shader index 387620c5c..6778a7cd7 100644 --- a/Source/Shaders/GI/GlobalSurfaceAtlas.shader +++ b/Source/Shaders/GI/GlobalSurfaceAtlas.shader @@ -242,7 +242,7 @@ void CS_CullObjects(uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupId if (BoxIntersectsSphere(groupMin, groupMax, objectBounds.xyz, objectBounds.w)) { uint sharedIndex; - InterlockedAdd(SharedCulledObjectsCount, 1, sharedIndex); + InterlockedAdd(SharedCulledObjectsCount, 1u, sharedIndex); if (sharedIndex < GLOBAL_SURFACE_ATLAS_SHARED_CULL_SIZE) SharedCulledObjects[sharedIndex] = objectAddress; } @@ -271,7 +271,7 @@ void CS_CullObjects(uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupId // Allocate object data size in the buffer uint objectsStart; uint objectsSize = objectsCount + 1; // Include objects count before actual objects data - RWGlobalSurfaceAtlasCulledObjects.InterlockedAdd(0, objectsSize, objectsStart); // Counter at 0 + RWGlobalSurfaceAtlasCulledObjects.InterlockedAdd(0u, objectsSize, objectsStart); // Counter at 0 if (objectsStart + objectsSize > CulledObjectsCapacity) { // Not enough space in the buffer From 3888c4ba218991f7275260db84f496cfd20842da Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 13 Nov 2025 22:53:52 +0100 Subject: [PATCH 077/207] Fix async tasks destruction to wait on the dependencies in chain --- Source/Engine/Threading/Task.cpp | 25 +++++++++++++++++++++++-- Source/Engine/Threading/Task.h | 5 +++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Threading/Task.cpp b/Source/Engine/Threading/Task.cpp index bab794728..a640019d1 100644 --- a/Source/Engine/Threading/Task.cpp +++ b/Source/Engine/Threading/Task.cpp @@ -226,6 +226,27 @@ void Task::OnEnd() { ASSERT(!IsRunning()); - // Add to delete - DeleteObject(30.0f, false); + if (_continueWith && !_continueWith->IsEnded()) + { + // Let next task do the cleanup (to ensure whole tasks chain shares the lifetime) + _continueWith->_rootForRemoval = _rootForRemoval ? _rootForRemoval : this; + } + else + { + constexpr float timeToLive = 30.0f; + + // Remove task chain starting from the root + if (_rootForRemoval) + { + auto task = _rootForRemoval; + while (task != this) + { + task->DeleteObject(timeToLive, false); + task = task->_continueWith; + } + } + + // Add to delete + DeleteObject(timeToLive, false); + } } diff --git a/Source/Engine/Threading/Task.h b/Source/Engine/Threading/Task.h index eb450effd..19fbf4fad 100644 --- a/Source/Engine/Threading/Task.h +++ b/Source/Engine/Threading/Task.h @@ -63,6 +63,11 @@ protected: /// Task* _continueWith = nullptr; + /// + /// The task that's starts removal chain, used to sync whole task chain lifetime. + /// + Task* _rootForRemoval = nullptr; + void SetState(TaskState state) { Platform::AtomicStore((int64 volatile*)&_state, (uint64)state); From 636a1ff9308cd45a091a23da7e137a6e9abad786 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 13 Nov 2025 23:04:24 +0100 Subject: [PATCH 078/207] Fix material shader generation when material layer fails to load --- .../MaterialGenerator.Layer.cpp | 27 +++---------------- .../MaterialGenerator.Layers.cpp | 3 +++ 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp index fd68ccb9b..73b3058a3 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp @@ -43,32 +43,15 @@ void MaterialGenerator::AddLayer(MaterialLayer* layer) MaterialLayer* MaterialGenerator::GetLayer(const Guid& id, Node* caller) { // Find layer first - for (int32 i = 0; i < _layers.Count(); i++) + for (MaterialLayer* layer : _layers) { - if (_layers[i]->ID == id) - { - // Found - return _layers[i]; - } + if (layer->ID == id) + return layer; } // Load asset Asset* asset = Assets.LoadAsync(id); - if (asset == nullptr || asset->WaitForLoaded(30000)) - { - OnError(caller, nullptr, TEXT("Failed to load material asset.")); - return nullptr; - } - - // Special case for engine exit event - if (Engine::ShouldExit()) - { - // End - return nullptr; - } - - // Check if load failed - if (!asset->IsLoaded()) + if (asset == nullptr || asset->WaitForLoaded(10 * 1000)) { OnError(caller, nullptr, TEXT("Failed to load material asset.")); return nullptr; @@ -79,13 +62,11 @@ MaterialLayer* MaterialGenerator::GetLayer(const Guid& id, Node* caller) Asset* iterator = asset; while (material == nullptr) { - // Wait for material to be loaded if (iterator->WaitForLoaded()) { OnError(caller, nullptr, TEXT("Material asset load failed.")); return nullptr; } - if (iterator->GetTypeName() == MaterialInstance::TypeName) { auto instance = ((MaterialInstance*)iterator); diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layers.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layers.cpp index ea1f6cc7a..a15551b40 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layers.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layers.cpp @@ -15,12 +15,14 @@ void MaterialGenerator::ProcessGroupLayers(Box* box, Node* node, Value& value) if (!id.IsValid()) { OnError(node, box, TEXT("Missing material.")); + value = MaterialValue::InitForZero(VariantType::Void); break; } ASSERT(GetRootLayer() != nullptr && GetRootLayer()->ID.IsValid()); if (id == GetRootLayer()->ID) { OnError(node, box, TEXT("Cannot use current material as layer.")); + value = MaterialValue::InitForZero(VariantType::Void); break; } @@ -29,6 +31,7 @@ void MaterialGenerator::ProcessGroupLayers(Box* box, Node* node, Value& value) if (layer == nullptr) { OnError(node, box, TEXT("Cannot load material.")); + value = MaterialValue::InitForZero(VariantType::Void); break; } ASSERT(_layers.Contains(layer)); From e9070b30a0aa5fbc62ad88bb7fd7726dd7085bbe Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 13 Nov 2025 23:05:13 +0100 Subject: [PATCH 079/207] Minor tweaks --- Source/Engine/Content/Content.cpp | 1 + Source/Engine/Particles/ParticlesSimulation.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index b82a329a8..66e18a231 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -700,6 +700,7 @@ Asset* Content::GetAsset(const StringView& outputPath) { if (outputPath.IsEmpty()) return nullptr; + PROFILE_CPU(); ScopeLock lock(AssetsLocker); for (auto i = Assets.Begin(); i.IsNotEnd(); ++i) { diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp index d25127e2f..bc1fa9eea 100644 --- a/Source/Engine/Particles/ParticlesSimulation.cpp +++ b/Source/Engine/Particles/ParticlesSimulation.cpp @@ -156,7 +156,7 @@ void ParticleSystemInstance::Sync(ParticleSystem* system) if (GPUParticlesCountReadback) GPUParticlesCountReadback->ReleaseGPU(); } - ASSERT(Emitters.Count() == system->Emitters.Count()); + CHECK(Emitters.Count() == system->Emitters.Count()); } bool ParticleSystemInstance::ContainsEmitter(ParticleEmitter* emitter) const From 4008e19ca91221530bb680c5ee4d4fbe84e7f417 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 14 Nov 2025 00:52:14 -0800 Subject: [PATCH 080/207] Fix various build issuesin uncommon configurations --- Source/Editor/Cooker/Steps/DeployDataStep.cpp | 2 +- Source/Engine/Physics/Colliders/Collider.cpp | 1 - Source/Engine/Physics/PhysicsBackendEmpty.cpp | 32 +++++++------------ Source/Engine/Scripting/ManagedCLR/MClass.h | 4 +++ Source/Engine/Scripting/ManagedCLR/MEvent.h | 2 ++ Source/Engine/Scripting/ManagedCLR/MField.h | 2 ++ Source/Engine/Scripting/ManagedCLR/MMethod.h | 2 ++ .../Engine/Scripting/ManagedCLR/MProperty.h | 2 ++ Source/Engine/Scripting/ScriptingObject.cpp | 2 ++ Source/Engine/Tools/ModelTool/ModelTool.cpp | 2 +- 10 files changed, 27 insertions(+), 24 deletions(-) diff --git a/Source/Editor/Cooker/Steps/DeployDataStep.cpp b/Source/Editor/Cooker/Steps/DeployDataStep.cpp index b5b24c251..775b040de 100644 --- a/Source/Editor/Cooker/Steps/DeployDataStep.cpp +++ b/Source/Editor/Cooker/Steps/DeployDataStep.cpp @@ -265,7 +265,7 @@ bool DeployDataStep::Perform(CookingData& data) } if (version.IsEmpty()) { - data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for the current host platform."), maxVer, minVer)); + data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for {} platform."), maxVer, minVer, platformName)); return true; } } diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index 6980fc4e6..96bd9c7ae 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -194,7 +194,6 @@ void Collider::UpdateLayerBits() // Own layer mask const uint32 mask1 = Physics::LayerMasks[GetLayer()]; - ASSERT(_shape); PhysicsBackend::SetShapeFilterMask(_shape, mask0, mask1); } diff --git a/Source/Engine/Physics/PhysicsBackendEmpty.cpp b/Source/Engine/Physics/PhysicsBackendEmpty.cpp index 2e816157b..03973e6fd 100644 --- a/Source/Engine/Physics/PhysicsBackendEmpty.cpp +++ b/Source/Engine/Physics/PhysicsBackendEmpty.cpp @@ -3,6 +3,7 @@ #if COMPILE_WITH_EMPTY_PHYSICS #include "Engine/Core/Log.h" +#include "Engine/Physics/PhysicsBackend.h" #include "Engine/Physics/CollisionData.h" #include "Engine/Physics/PhysicalMaterial.h" #include "Engine/Physics/PhysicsScene.h" @@ -226,26 +227,6 @@ bool PhysicsBackend::CheckConvex(void* scene, const Vector3& center, const Colli return false; } -bool PhysicsBackend::OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) -{ - return false; -} - -bool PhysicsBackend::OverlapSphere(void* scene, const Vector3& center, const float radius, Array& results, uint32 layerMask, bool hitTriggers) -{ - return false; -} - -bool PhysicsBackend::OverlapCapsule(void* scene, const Vector3& center, const float radius, const float height, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) -{ - return false; -} - -bool PhysicsBackend::OverlapConvex(void* scene, const Vector3& center, const CollisionData* convexMesh, const Vector3& scale, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) -{ - return false; -} - bool PhysicsBackend::OverlapBox(void* scene, const Vector3& center, const Vector3& halfExtents, Array& results, const Quaternion& rotation, uint32 layerMask, bool hitTriggers) { return false; @@ -353,7 +334,7 @@ Vector3 PhysicsBackend::GetRigidDynamicActorCenterOfMass(void* actor) return Vector3::Zero; } -void PhysicsBackend::SetRigidDynamicActorCenterOfMassOffset(void* actor, const Float3& value) +void PhysicsBackend::AddRigidDynamicActorCenterOfMassOffset(void* actor, const Float3& value) { } @@ -698,6 +679,15 @@ void PhysicsBackend::SetControllerStepOffset(void* controller, float value) { } +Vector3 PhysicsBackend::GetControllerBasePosition(void* controller) +{ + return Vector3::Zero; +} + +void PhysicsBackend::SetControllerBasePosition(void* controller, const Vector3& value) +{ +} + Vector3 PhysicsBackend::GetControllerUpDirection(void* controller) { return Vector3::Up; diff --git a/Source/Engine/Scripting/ManagedCLR/MClass.h b/Source/Engine/Scripting/ManagedCLR/MClass.h index 9484a2c44..61273dae7 100644 --- a/Source/Engine/Scripting/ManagedCLR/MClass.h +++ b/Source/Engine/Scripting/ManagedCLR/MClass.h @@ -23,6 +23,10 @@ private: StringAnsiView _fullname; uint32 _types = 0; mutable uint32 _size = 0; +#else + StringAnsiView _name; + StringAnsiView _namespace; + StringAnsiView _fullname; #endif MAssembly* _assembly; diff --git a/Source/Engine/Scripting/ManagedCLR/MEvent.h b/Source/Engine/Scripting/ManagedCLR/MEvent.h index 9d8551774..e5ef1d6df 100644 --- a/Source/Engine/Scripting/ManagedCLR/MEvent.h +++ b/Source/Engine/Scripting/ManagedCLR/MEvent.h @@ -19,6 +19,8 @@ protected: #elif USE_NETCORE void* _handle; StringAnsiView _name; +#else + StringAnsiView _name; #endif mutable MMethod* _addMethod; diff --git a/Source/Engine/Scripting/ManagedCLR/MField.h b/Source/Engine/Scripting/ManagedCLR/MField.h index 66213d6ab..9ef5271a5 100644 --- a/Source/Engine/Scripting/ManagedCLR/MField.h +++ b/Source/Engine/Scripting/ManagedCLR/MField.h @@ -24,6 +24,8 @@ protected: void* _type; int32 _fieldOffset; StringAnsiView _name; +#else + StringAnsiView _name; #endif MClass* _parentClass; diff --git a/Source/Engine/Scripting/ManagedCLR/MMethod.h b/Source/Engine/Scripting/ManagedCLR/MMethod.h index 700fa9593..51264d1b8 100644 --- a/Source/Engine/Scripting/ManagedCLR/MMethod.h +++ b/Source/Engine/Scripting/ManagedCLR/MMethod.h @@ -30,6 +30,8 @@ protected: mutable void* _returnType; mutable Array> _parameterTypes; void CacheSignature() const; +#else + StringAnsiView _name; #endif MClass* _parentClass; MVisibility _visibility; diff --git a/Source/Engine/Scripting/ManagedCLR/MProperty.h b/Source/Engine/Scripting/ManagedCLR/MProperty.h index e48d98375..7ef7e97dd 100644 --- a/Source/Engine/Scripting/ManagedCLR/MProperty.h +++ b/Source/Engine/Scripting/ManagedCLR/MProperty.h @@ -22,6 +22,8 @@ protected: #elif USE_NETCORE void* _handle; StringAnsiView _name; +#else + StringAnsiView _name; #endif mutable MMethod* _getMethod; diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index 35bd16eb6..dee97a849 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -93,6 +93,8 @@ ScriptingObject::ScriptingObject(const SpawnParams& params) : _gcHandle((MGCHandle)params.Managed) #elif !COMPILE_WITHOUT_CSHARP : _gcHandle(params.Managed ? MCore::GCHandle::New(params.Managed) : 0) +#else + : _gcHandle(0) #endif , _type(params.Type) , _id(params.ID) diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index b3b622942..843822b98 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -8,6 +8,7 @@ #include "Engine/Core/RandomStream.h" #include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Ray.h" +#include "Engine/Core/Utilities.h" #include "Engine/Platform/ConditionVariable.h" #include "Engine/Profiler/Profiler.h" #include "Engine/Threading/JobSystem.h" @@ -27,7 +28,6 @@ #include "Engine/Serialization/MemoryWriteStream.h" #include "Engine/Engine/Units.h" #if USE_EDITOR -#include "Engine/Core/Utilities.h" #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Types/TimeSpan.h" From 5e690abd76425889b71033d74ae2b7158254ae86 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 14 Nov 2025 01:06:01 -0800 Subject: [PATCH 081/207] Fix initial state of `DummyVertexBuffer` on DX12 --- Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index a26584486..b33f94d61 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -307,6 +307,7 @@ void GPUContextDX12::Reset() _device->DummyVB = _device->CreateBuffer(TEXT("DummyVertexBuffer")); auto* layout = GPUVertexLayout::Get({ { VertexElement::Types::Attribute3, 0, 0, 0, PixelFormat::R32G32B32A32_Float } }); _device->DummyVB->Init(GPUBufferDescription::Vertex(layout, sizeof(Color), 1, &Color::Transparent)); + SetResourceState(_device->DummyVB, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER, 0); } ((GPUBufferDX12*)_device->DummyVB)->GetVBView(dummyVBView); _commandList->IASetVertexBuffers(GPU_MAX_VB_BINDED, 1, &dummyVBView); From 2a36edf528e8ff36efb6029e6216cff4301d01bc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 14 Nov 2025 02:11:13 -0800 Subject: [PATCH 082/207] Add option to link OpenMP on GDK platforms if needed --- Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs b/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs index 220a07bef..45e39e63c 100644 --- a/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/GDK/GDKToolchain.cs @@ -13,6 +13,11 @@ namespace Flax.Build.Platforms /// public abstract class GDKToolchain : WindowsToolchainBase { + /// + /// Enables OpenMP library as dynamic dependency. + /// + protected bool OpenMP = false; + /// /// Gets the version of Xbox Services toolset. /// @@ -74,6 +79,12 @@ namespace Flax.Build.Platforms options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vccorlib140.dll")); options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140.dll")); options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140_1.dll")); + if (OpenMP) + { + redistToolsPath = Path.Combine(paths[0], "x64", "Microsoft.VC" + (int)crtToolset + ".OpenMP"); + redistToolsPath = Utilities.RemovePathRelativeParts(redistToolsPath); + options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcomp140.dll")); + } } } } From 2f670495ac51c8b549dc7c897ed826739963ea83 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 14 Nov 2025 14:56:10 +0100 Subject: [PATCH 083/207] Migrate Xbox to using static linking with nethost lib --- Source/ThirdParty/nethost/nethost.Build.cs | 8 +++++--- .../Tools/Flax.Build/Deps/Dependencies/nethost.cs | 14 +++++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index bac529778..69d96d637 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -48,9 +48,6 @@ public class nethost : ThirdPartyModule switch (options.Platform.Target) { case TargetPlatform.Windows: - case TargetPlatform.XboxOne: - case TargetPlatform.XboxScarlett: - case TargetPlatform.UWP: if (hostRuntime.Type == DotNetSdk.HostType.CoreCLR) { options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "nethost.lib")); @@ -63,6 +60,11 @@ public class nethost : ThirdPartyModule options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "coreclr.dll")); } break; + case TargetPlatform.XboxOne: + case TargetPlatform.XboxScarlett: + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "monosgen-2.0.lib")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "mono-profiler-aot.lib")); + break; case TargetPlatform.Linux: options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.a")); options.DependencyFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.so")); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index 1991aa605..0a8181903 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -89,7 +89,10 @@ namespace Flax.Deps.Dependencies os = "windows"; runtimeFlavor = "Mono"; buildMonoAotCross = true; - buildArgs = $" -subset mono+libs -cmakeargs \"-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1\" /p:FeaturePerfTracing=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; + var defines = "-D_GAMING_XBOX=1-DDISABLE_JIT=1-DENABLE_PERFTRACING=0-DDISABLE_REFLECTION_EMIT=1-DDISABLE_EVENTPIPE=1-DDISABLE_COM=1-DDISABLE_PROFILER=1-DDISABLE_COMPONENTS=1"; + defines += targetPlatform == TargetPlatform.XboxScarlett ? "-D_GAMING_XBOX_SCARLETT=1" : "-D_GAMING_XBOX_XBOXONE=1"; + defines += "-DDISABLE_EXECUTABLES=1-DDISABLE_SHARED_LIBS=1"; + buildArgs = $" -subset mono+libs -cmakeargs \"{defines}\" /p:FeaturePerfTracing=false /p:FeatureWin32Registry=false /p:FeatureCominteropApartmentSupport=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; break; case TargetPlatform.Linux: os = "linux"; @@ -241,8 +244,13 @@ namespace Flax.Deps.Dependencies case TargetPlatform.XboxScarlett: libs1 = new[] { - "lib/coreclr.dll", - "lib/coreclr.import.lib", + // When using shared library: + //"lib/coreclr.dll", + //"lib/coreclr.import.lib", + + // When using static library: + "lib/monosgen-2.0.lib", + "lib/mono-profiler-aot.lib", }; libs2 = new string[] { From f640452b7b92d0c7d3ec48ea1631347a748820ee Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Sat, 15 Nov 2025 21:57:14 +0100 Subject: [PATCH 084/207] Fix BVH node splitting using stale pointer to invalidated array memory Ensure BuildBVH refreshes its node pointer after growing _bvh so reallocations no longer leave it operating on freed memory, eliminating the sporadic SDF-generation crash. --- .../ModelTool/MeshAccelerationStructure.cpp | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp index 2cc989059..6e1b5ae6f 100644 --- a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp +++ b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp @@ -21,9 +21,9 @@ static_assert(sizeof(GPUBVH) == sizeof(Float4) * 2, "Invalid BVH structure size void MeshAccelerationStructure::BuildBVH(int32 node, BVHBuild& build) { - auto& root = _bvh[node]; - ASSERT_LOW_LAYER(root.Leaf.IsLeaf); - if (build.MaxLeafSize > 0 && root.Leaf.TriangleCount <= build.MaxLeafSize) + auto* root = &_bvh[node]; + ASSERT_LOW_LAYER(root->Leaf.IsLeaf); + if (build.MaxLeafSize > 0 && root->Leaf.TriangleCount <= build.MaxLeafSize) return; if (build.MaxDepth > 0 && build.NodeDepth >= build.MaxDepth) return; @@ -31,15 +31,16 @@ void MeshAccelerationStructure::BuildBVH(int32 node, BVHBuild& build) // Spawn two leaves const int32 childIndex = _bvh.Count(); _bvh.AddDefault(2); + root = &_bvh[node]; auto& left = _bvh.Get()[childIndex]; auto& right = _bvh.Get()[childIndex + 1]; left.Leaf.IsLeaf = 1; right.Leaf.IsLeaf = 1; - left.Leaf.MeshIndex = root.Leaf.MeshIndex; - right.Leaf.MeshIndex = root.Leaf.MeshIndex; + left.Leaf.MeshIndex = root->Leaf.MeshIndex; + right.Leaf.MeshIndex = root->Leaf.MeshIndex; // Mid-point splitting based on the largest axis - const Float3 boundsSize = root.Bounds.GetSize(); + const Float3 boundsSize = root->Bounds.GetSize(); int32 axisCount = 0; int32 axis = 0; RETRY: @@ -63,11 +64,11 @@ RETRY: // Go to the next axis axis = (axis + 1) % 3; } - const float midPoint = (float)(root.Bounds.Minimum.Raw[axis] + boundsSize.Raw[axis] * 0.5f); - const Mesh& meshData = _meshes[root.Leaf.MeshIndex]; + const float midPoint = (float)(root->Bounds.Minimum.Raw[axis] + boundsSize.Raw[axis] * 0.5f); + const Mesh& meshData = _meshes[root->Leaf.MeshIndex]; const Float3* vb = meshData.VertexBuffer.Get(); - int32 indexStart = root.Leaf.TriangleIndex * 3; - int32 indexEnd = indexStart + root.Leaf.TriangleCount * 3; + int32 indexStart = root->Leaf.TriangleIndex * 3; + int32 indexEnd = indexStart + root->Leaf.TriangleCount * 3; left.Leaf.TriangleCount = 0; right.Leaf.TriangleCount = 0; if (meshData.Use16BitIndexBuffer) @@ -76,7 +77,7 @@ RETRY: { uint16 I0, I1, I2; }; - build.Scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); + build.Scratch.Resize(root->Leaf.TriangleCount * sizeof(Tri)); auto dst = (Tri*)build.Scratch.Get(); auto ib16 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) @@ -89,9 +90,9 @@ RETRY: if (centroid <= midPoint) dst[left.Leaf.TriangleCount++] = tri; // Left else - dst[root.Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right + dst[root->Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right } - Platform::MemoryCopy(ib16 + indexStart, dst, root.Leaf.TriangleCount * 3 * sizeof(uint16)); + Platform::MemoryCopy(ib16 + indexStart, dst, root->Leaf.TriangleCount * 3 * sizeof(uint16)); if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0) { axisCount++; @@ -104,9 +105,9 @@ RETRY: for (int32 i = indexStart; i < indexEnd; i++) left.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]); - right.Bounds = BoundingBox(vb[dst[root.Leaf.TriangleCount - 1].I0]); + right.Bounds = BoundingBox(vb[dst[root->Leaf.TriangleCount - 1].I0]); indexStart = left.Leaf.TriangleCount; - indexEnd = root.Leaf.TriangleCount * 3; + indexEnd = root->Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) right.Bounds.Merge(vb[((uint16*)build.Scratch.Get())[i]]); } @@ -116,7 +117,7 @@ RETRY: { uint32 I0, I1, I2; }; - build.Scratch.Resize(root.Leaf.TriangleCount * sizeof(Tri)); + build.Scratch.Resize(root->Leaf.TriangleCount * sizeof(Tri)); auto dst = (Tri*)build.Scratch.Get(); auto ib32 = meshData.IndexBuffer.Get(); for (int32 i = indexStart; i < indexEnd;) @@ -129,9 +130,9 @@ RETRY: if (centroid <= midPoint) dst[left.Leaf.TriangleCount++] = tri; // Left else - dst[root.Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right + dst[root->Leaf.TriangleCount - ++right.Leaf.TriangleCount] = tri; // Right } - Platform::MemoryCopy(ib32 + indexStart, dst, root.Leaf.TriangleCount * 3 * sizeof(uint32)); + Platform::MemoryCopy(ib32 + indexStart, dst, root->Leaf.TriangleCount * 3 * sizeof(uint32)); if (left.Leaf.TriangleCount == 0 || right.Leaf.TriangleCount == 0) { axisCount++; @@ -144,22 +145,22 @@ RETRY: for (int32 i = indexStart; i < indexEnd; i++) left.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]); - right.Bounds = BoundingBox(vb[dst[root.Leaf.TriangleCount - 1].I0]); + right.Bounds = BoundingBox(vb[dst[root->Leaf.TriangleCount - 1].I0]); indexStart = left.Leaf.TriangleCount; - indexEnd = root.Leaf.TriangleCount * 3; + indexEnd = root->Leaf.TriangleCount * 3; for (int32 i = indexStart; i < indexEnd; i++) right.Bounds.Merge(vb[((uint32*)build.Scratch.Get())[i]]); } - ASSERT_LOW_LAYER(left.Leaf.TriangleCount + right.Leaf.TriangleCount == root.Leaf.TriangleCount); - left.Leaf.TriangleIndex = root.Leaf.TriangleIndex; + ASSERT_LOW_LAYER(left.Leaf.TriangleCount + right.Leaf.TriangleCount == root->Leaf.TriangleCount); + left.Leaf.TriangleIndex = root->Leaf.TriangleIndex; right.Leaf.TriangleIndex = left.Leaf.TriangleIndex + left.Leaf.TriangleCount; build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount); build.MaxNodeTriangles = Math::Max(build.MaxNodeTriangles, (int32)right.Leaf.TriangleCount); // Convert into a node - root.Node.IsLeaf = 0; - root.Node.ChildIndex = childIndex; - root.Node.ChildrenCount = 2; + root->Node.IsLeaf = 0; + root->Node.ChildIndex = childIndex; + root->Node.ChildrenCount = 2; // Split children build.NodeDepth++; From ac3b2c0ef22d0b8f6410a25efd8880d0a2ba91c7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 15 Nov 2025 22:13:22 +0100 Subject: [PATCH 085/207] Fix shader warning --- Source/Shaders/MeshAccelerationStructure.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Shaders/MeshAccelerationStructure.hlsl b/Source/Shaders/MeshAccelerationStructure.hlsl index c0ae24835..7c281f02a 100644 --- a/Source/Shaders/MeshAccelerationStructure.hlsl +++ b/Source/Shaders/MeshAccelerationStructure.hlsl @@ -32,8 +32,8 @@ struct BVHHit float3 LoadVertexBVH(BVHBuffers bvh, uint index) { - index = bvh.IndexBuffer.Load(index << 2u); - return asfloat(bvh.VertexBuffer.Load3(index * bvh.VertexStride)); + uint vertexIndex = bvh.IndexBuffer.Load(index << 2u); + return asfloat(bvh.VertexBuffer.Load3(vertexIndex * bvh.VertexStride)); } // [https://tavianator.com/2011/ray_box.html] From 6c29877b20cbfcdf08c916d5ac458cad9ad5c1cc Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Nov 2025 15:58:50 -0600 Subject: [PATCH 086/207] Fix spline bezier drawing. --- Source/Engine/Level/Actors/Spline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actors/Spline.cpp b/Source/Engine/Level/Actors/Spline.cpp index 25bdc4ce9..d6c2417c8 100644 --- a/Source/Engine/Level/Actors/Spline.cpp +++ b/Source/Engine/Level/Actors/Spline.cpp @@ -518,7 +518,7 @@ namespace Vector3 nextPos = transform.LocalToWorld(next->Value.Translation); DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(nextPos, NodeSizeByDistance(nextPos, scaleByDistance)), color, 0.0f, depthTest); const float d = (next->Time - prev->Time) / 3.0f; - DEBUG_DRAW_BEZIER(prevPos, prevPos + prev->TangentOut.Translation * d, nextPos + next->TangentIn.Translation * d, nextPos, color, 0.0f, depthTest); + DEBUG_DRAW_BEZIER(prevPos, transform.LocalToWorld(prev->Value.Translation + prev->TangentOut.Translation * d), transform.LocalToWorld(next->Value.Translation + next->TangentIn.Translation * d), nextPos, color, 0.0f, depthTest); prev = next; prevPos = nextPos; } From f91c33e17caaf3009c46b422533a6d0037601efc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 15 Nov 2025 23:42:03 +0100 Subject: [PATCH 087/207] Another fix for shader compilation on Vulkan --- Source/Shaders/MeshAccelerationStructure.hlsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Shaders/MeshAccelerationStructure.hlsl b/Source/Shaders/MeshAccelerationStructure.hlsl index 7c281f02a..d98d25404 100644 --- a/Source/Shaders/MeshAccelerationStructure.hlsl +++ b/Source/Shaders/MeshAccelerationStructure.hlsl @@ -32,7 +32,8 @@ struct BVHHit float3 LoadVertexBVH(BVHBuffers bvh, uint index) { - uint vertexIndex = bvh.IndexBuffer.Load(index << 2u); + int addr = index << 2u; + uint vertexIndex = bvh.IndexBuffer.Load(addr); return asfloat(bvh.VertexBuffer.Load3(vertexIndex * bvh.VertexStride)); } From 4aba0153f8f3af4714bd85739f9aae38d957d41a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 15 Nov 2025 14:57:12 -0800 Subject: [PATCH 088/207] Prioritize Dotnet libs on Mono AOT --- Source/Engine/Scripting/Runtime/DotNet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 671b6b31b..1b220b017 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -2028,13 +2028,13 @@ static MonoAssembly* OnMonoAssemblyLoad(const char* aname) String fileName = name; if (!name.EndsWith(TEXT(".dll")) && !name.EndsWith(TEXT(".exe"))) fileName += TEXT(".dll"); - String path = fileName; + String path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName; if (!FileSystem::FileExists(path)) { path = Globals::ProjectFolder / String(TEXT("/Dotnet/shared/Microsoft.NETCore.App/")) / fileName; if (!FileSystem::FileExists(path)) { - path = Globals::ProjectFolder / String(TEXT("/Dotnet/")) / fileName; + path = fileName; } } From e79af2fd6033ec8cf8e41a3096dd33a3ed039779 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Nov 2025 21:39:21 -0600 Subject: [PATCH 089/207] Handle additional edge cases for anim event. --- Source/Engine/Animations/Graph/AnimGroup.Animation.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index c76bddf3f..71b90a1fb 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -246,11 +246,16 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f; #define ADD_OUTGOING_EVENT(type) context.Data->OutgoingEvents.Add({ k.Value.Instance, (AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime, AnimGraphInstanceData::OutgoingEvent::type }) if ((k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration - && (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration()))) + && (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) + && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration()))) // Handle the edge case of an event on 0 or on max animation duration during looping || (loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == anim->GetDuration()) || (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f) || (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f) + || (Math::FloorToInt(animPos) == 1 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 1.0f) + || (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 1 && k.Time == 1.0f) + || (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && k.Time == Math::CeilToInt(anim->GetDuration()) - 1.0f) + || (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == Math::CeilToInt(anim->GetDuration()) - 1.0f) ) { int32 stateIndex = -1; From 95629e792d6d628615824f3ce13ae4fa7b438fbc Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sat, 15 Nov 2025 22:12:35 -0600 Subject: [PATCH 090/207] Fix additional edge cases --- Source/Engine/Animations/Graph/AnimGroup.Animation.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 71b90a1fb..fd62ec537 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -249,13 +249,16 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float && (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration()))) // Handle the edge case of an event on 0 or on max animation duration during looping - || (loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == anim->GetDuration()) + || (!loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration())) || (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f) || (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f) + || (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration())) + || (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration())) || (Math::FloorToInt(animPos) == 1 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 1.0f) || (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 1 && k.Time == 1.0f) - || (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && k.Time == Math::CeilToInt(anim->GetDuration()) - 1.0f) - || (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == Math::CeilToInt(anim->GetDuration()) - 1.0f) + || (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f)) + || (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f)) + || (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 0.0f) ) { int32 stateIndex = -1; From 371a16e37b1888fb8f07049c43c782e06fc3209d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 16 Nov 2025 14:50:22 -0800 Subject: [PATCH 091/207] Fixes for Xbox with Mono AOT --- Source/ThirdParty/nethost/nethost.Build.cs | 1 + .../Flax.Build/Deps/Dependencies/nethost.cs | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Source/ThirdParty/nethost/nethost.Build.cs b/Source/ThirdParty/nethost/nethost.Build.cs index 69d96d637..2910e401d 100644 --- a/Source/ThirdParty/nethost/nethost.Build.cs +++ b/Source/ThirdParty/nethost/nethost.Build.cs @@ -64,6 +64,7 @@ public class nethost : ThirdPartyModule case TargetPlatform.XboxScarlett: options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "monosgen-2.0.lib")); options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "mono-profiler-aot.lib")); + options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "System.Globalization.Native-Static.lib")); break; case TargetPlatform.Linux: options.OutputFiles.Add(Path.Combine(hostRuntime.Path, "libnethost.a")); diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index 0a8181903..4d6a1aab6 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -93,6 +93,7 @@ namespace Flax.Deps.Dependencies defines += targetPlatform == TargetPlatform.XboxScarlett ? "-D_GAMING_XBOX_SCARLETT=1" : "-D_GAMING_XBOX_XBOXONE=1"; defines += "-DDISABLE_EXECUTABLES=1-DDISABLE_SHARED_LIBS=1"; buildArgs = $" -subset mono+libs -cmakeargs \"{defines}\" /p:FeaturePerfTracing=false /p:FeatureWin32Registry=false /p:FeatureCominteropApartmentSupport=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; + envVars.Add("_GAMING_XBOX", "1"); break; case TargetPlatform.Linux: os = "linux"; @@ -240,20 +241,28 @@ namespace Flax.Deps.Dependencies switch (targetPlatform) { case TargetPlatform.Windows: + libs1 = new[] + { + "lib/coreclr.dll", + "lib/coreclr.import.lib", + }; + libs2 = new[] + { + "System.Globalization.Native.dll", + "System.IO.Compression.Native.dll", + }; + break; case TargetPlatform.XboxOne: case TargetPlatform.XboxScarlett: libs1 = new[] { - // When using shared library: - //"lib/coreclr.dll", - //"lib/coreclr.import.lib", - - // When using static library: "lib/monosgen-2.0.lib", "lib/mono-profiler-aot.lib", }; - libs2 = new string[] + libs2 = new[] { + "lib/System.Globalization.Native-Static.lib", + "lib/System.IO.Compression.Native-Static.lib", }; break; default: From 7a9c58003d04c81f74fa28ffc4bc462b0df62c04 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 17 Nov 2025 04:41:57 -0800 Subject: [PATCH 092/207] Fix video playback on Xbox --- Source/Engine/Video/MF/VideoBackendMF.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Video/MF/VideoBackendMF.cpp b/Source/Engine/Video/MF/VideoBackendMF.cpp index 75950f3aa..b8ef7ee4e 100644 --- a/Source/Engine/Video/MF/VideoBackendMF.cpp +++ b/Source/Engine/Video/MF/VideoBackendMF.cpp @@ -120,6 +120,14 @@ namespace MF else { // Reconfigure decoder to output supported format by force +#if PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT + // Xbox supports NV12 via HV decoder + auto fallbackFormat = PixelFormat::NV12; + GUID fallbackFormatGuid = MFVideoFormat_NV12; +#else + auto fallbackFormat = PixelFormat::YUY2; + GUID fallbackFormatGuid = MFVideoFormat_YUY2; +#endif IMFMediaType* customType = nullptr; hr = MFCreateMediaType(&customType); if (FAILED(hr)) @@ -128,7 +136,7 @@ namespace MF goto END; } customType->SetGUID(MF_MT_MAJOR_TYPE, majorType); - customType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_YUY2); + customType->SetGUID(MF_MT_SUBTYPE, fallbackFormatGuid); MFSetAttributeSize(customType, MF_MT_FRAME_SIZE, width, height); hr = playerMF.SourceReader->SetCurrentMediaType(streamIndex, nullptr, customType); if (FAILED(hr)) @@ -136,7 +144,7 @@ namespace MF VIDEO_API_MF_ERROR(SetCurrentMediaType, hr); goto END; } - player.Format = PixelFormat::YUY2; + player.Format = fallbackFormat; customType->Release(); } } From 329ebb6482999ec0330e5451991a88a9b5de1f1d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 12:07:39 +0100 Subject: [PATCH 093/207] Add custom shader compiler for Xbox Scarlett --- .../Cooker/Platform/GDK/GDKPlatformTools.cpp | 30 +++-- Source/Editor/Cooker/Steps/CookAssetsStep.cpp | 1 + Source/Engine/ShadersCompilation/Config.h | 8 +- .../DirectX/ShaderCompilerD3D.cpp | 2 +- .../DirectX/ShaderCompilerDX.cpp | 20 ++-- .../DirectX/ShaderCompilerDX.h | 8 +- .../ShadersCompilation/ShaderCompiler.h | 16 ++- .../ShadersCompilation.Build.cs | 2 + .../ShadersCompilation/ShadersCompilation.cpp | 106 +++++++++--------- .../ShadersCompilation/ShadersCompilation.h | 4 +- 10 files changed, 112 insertions(+), 85 deletions(-) diff --git a/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp b/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp index f9a8f1b82..fa1ac4d0b 100644 --- a/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp @@ -15,26 +15,32 @@ #include "Editor/ProjectInfo.h" #include "Editor/Utilities/EditorUtilities.h" -GDKPlatformTools::GDKPlatformTools() +String GetGDK() { - // Find GDK - Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), _gdkPath); - if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath)) + String gdk; + Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), gdk); + if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk)) { - _gdkPath.Clear(); - Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), _gdkPath); - if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath)) + gdk.Clear(); + Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), gdk); + if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk)) { - _gdkPath.Clear(); + gdk.Clear(); } else { - if (_gdkPath.EndsWith(TEXT("GRDK\\"))) - _gdkPath.Remove(_gdkPath.Length() - 6); - else if (_gdkPath.EndsWith(TEXT("GRDK"))) - _gdkPath.Remove(_gdkPath.Length() - 5); + if (gdk.EndsWith(TEXT("GRDK\\"))) + gdk.Remove(gdk.Length() - 6); + else if (gdk.EndsWith(TEXT("GRDK"))) + gdk.Remove(gdk.Length() - 5); } } + return gdk; +} + +GDKPlatformTools::GDKPlatformTools() +{ + _gdkPath = GetGDK(); } DotNetAOTModes GDKPlatformTools::UseAOT() const diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index 030c31c41..57654a6b3 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -526,6 +526,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass #if PLATFORM_TOOLS_XBOX_SCARLETT case BuildPlatform::XboxScarlett: { + options.Platform = PlatformType::XboxScarlett; const char* platformDefineName = "PLATFORM_XBOX_SCARLETT"; COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE); break; diff --git a/Source/Engine/ShadersCompilation/Config.h b/Source/Engine/ShadersCompilation/Config.h index 8d9730f70..65d596235 100644 --- a/Source/Engine/ShadersCompilation/Config.h +++ b/Source/Engine/ShadersCompilation/Config.h @@ -15,7 +15,6 @@ class MemoryWriteStream; struct FLAXENGINE_API ShaderCompilationOptions { public: - /// /// Name of the target object (name of the shader or material for better logging readability) /// @@ -37,12 +36,16 @@ public: uint32 SourceLength = 0; public: - /// /// Target shader profile /// ShaderProfile Profile = ShaderProfile::Unknown; + /// + /// Target platform, set to invalid value of 0 if not used (platform-independent compilation). + /// + PlatformType Platform = (PlatformType)0; + /// /// Disables shaders compiler optimizations. Can be used to debug shaders on a target platform or to speed up the shaders compilation time. /// @@ -64,7 +67,6 @@ public: Array Macros; public: - /// /// Output stream to write compiled shader cache to /// diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp index 3edad2488..01ce8faef 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerD3D.cpp @@ -196,7 +196,7 @@ bool ShaderCompilerD3D::CompileShader(ShaderFunctionMeta& meta, WritePermutation default: return true; } - if (_profile == ShaderProfile::DirectX_SM5) + if (GetProfile() == ShaderProfile::DirectX_SM5) { profileName += "_5_0"; } diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp index 5e49da710..aa2f70ac7 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp @@ -59,7 +59,8 @@ public: *ppIncludeSource = nullptr; const char* source; int32 sourceLength; - const StringAnsi filename(pFilename); + StringAnsi filename(pFilename); + filename.Replace('\\', '/'); if (ShaderCompiler::GetIncludedFileSource(_context, "", filename.Get(), source, sourceLength)) return E_FAIL; IDxcBlobEncoding* textBlob; @@ -70,25 +71,25 @@ public: } }; -ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile) - : ShaderCompiler(profile) +ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile, PlatformType platform, void* dxcCreateInstanceProc) + : ShaderCompiler(profile, platform) { IDxcCompiler3* compiler = nullptr; IDxcLibrary* library = nullptr; IDxcContainerReflection* containerReflection = nullptr; - if (FAILED(DxcCreateInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast(&compiler))) || - FAILED(DxcCreateInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast(&library))) || - FAILED(DxcCreateInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast(&containerReflection)))) + DxcCreateInstanceProc createInstance = dxcCreateInstanceProc ? (DxcCreateInstanceProc)dxcCreateInstanceProc : &DxcCreateInstance; + if (FAILED(createInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast(&compiler))) || + FAILED(createInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast(&library))) || + FAILED(createInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast(&containerReflection)))) { LOG(Error, "DxcCreateInstance failed"); } _compiler = compiler; _library = library; _containerReflection = containerReflection; - static bool PrintVersion = true; - if (PrintVersion) + static HashSet PrintVersions; + if (PrintVersions.Add(createInstance)) { - PrintVersion = false; IDxcVersionInfo* version = nullptr; if (compiler && SUCCEEDED(compiler->QueryInterface(__uuidof(version), reinterpret_cast(&version)))) { @@ -221,6 +222,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD argsFull.Add(TEXT("-D")); argsFull.Add(*d); } + GetArgs(argsFull); // Compile ComPtr results; diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h index 7973fe397..a3e6d7073 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h @@ -22,7 +22,9 @@ public: /// Initializes a new instance of the class. /// /// The profile. - ShaderCompilerDX(ShaderProfile profile); + /// The platform. + /// The custom DXC Compiler factory function. + ShaderCompilerDX(ShaderProfile profile, PlatformType platform = (PlatformType)0, void* dxcCreateInstanceProc = nullptr); /// /// Finalizes an instance of the class. @@ -30,6 +32,10 @@ public: ~ShaderCompilerDX(); protected: + virtual void GetArgs(Array> args) + { + } + // [ShaderCompiler] bool CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite = nullptr) override; bool OnCompileBegin() override; diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.h b/Source/Engine/ShadersCompilation/ShaderCompiler.h index 79cdb1cae..36fd592e6 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.h +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.h @@ -23,10 +23,11 @@ public: }; private: + ShaderProfile _profile; + PlatformType _platform; Array _funcNameDefineBuffer; protected: - ShaderProfile _profile; ShaderCompilationContext* _context = nullptr; Array _globalMacros; Array _macros; @@ -37,8 +38,10 @@ public: /// Initializes a new instance of the class. /// /// The profile. - ShaderCompiler(ShaderProfile profile) + /// The platform. + ShaderCompiler(ShaderProfile profile, PlatformType platform = (PlatformType)0) : _profile(profile) + , _platform(platform) { } @@ -51,12 +54,19 @@ public: /// /// Gets shader profile supported by this compiler. /// - /// The shader profile. FORCE_INLINE ShaderProfile GetProfile() const { return _profile; } + /// + /// Gets target platform supported by this compiler. Returns invalid value of '0' if any platform works. + /// + FORCE_INLINE PlatformType GetPlatform() const + { + return _platform; + } + /// /// Performs the shader compilation. /// diff --git a/Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs b/Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs index 0e203f656..03ab0a74e 100644 --- a/Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs +++ b/Source/Engine/ShadersCompilation/ShadersCompilation.Build.cs @@ -63,6 +63,8 @@ public class ShadersCompilation : EngineModule options.PrivateDependencies.Add("ShaderCompilerPS4"); if (Sdk.HasValid("PS5Sdk")) options.PrivateDependencies.Add("ShaderCompilerPS5"); + if (Flax.Build.Platform.GetPlatform(TargetPlatform.XboxScarlett, true) != null) + options.PrivateDependencies.Add("ShaderCompilerXboxScarlett"); } /// diff --git a/Source/Engine/ShadersCompilation/ShadersCompilation.cpp b/Source/Engine/ShadersCompilation/ShadersCompilation.cpp index accfeaad5..f7d1367ba 100644 --- a/Source/Engine/ShadersCompilation/ShadersCompilation.cpp +++ b/Source/Engine/ShadersCompilation/ShadersCompilation.cpp @@ -48,6 +48,9 @@ #if COMPILE_WITH_PS5_SHADER_COMPILER #include "Platforms/PS5/Engine/ShaderCompilerPS5/ShaderCompilerPS5.h" #endif +#if COMPILE_WITH_XBOX_SCARLETT_SHADER_COMPILER +#include "Platforms/XboxScarlett/Engine/ShaderCompilerXboxScarlett/ShaderCompilerXboxScarlett.h" +#endif namespace ShadersCompilationImpl { @@ -165,20 +168,16 @@ bool ShadersCompilation::Compile(ShaderCompilationOptions& options) bool result; { ShaderCompilationContext context(&options, &meta); - - // Request shaders compiler - auto compiler = RequestCompiler(options.Profile); + auto compiler = RequestCompiler(options.Profile, options.Platform); if (compiler == nullptr) { LOG(Error, "Shader compiler request failed."); return true; } - ASSERT(compiler->GetProfile() == options.Profile); + ASSERT_LOW_LAYER(compiler->GetProfile() == options.Profile); // Call compilation process result = compiler->Compile(&context); - - // Dismiss compiler FreeCompiler(compiler); #if GPU_USE_SHADERS_DEBUG_LAYER @@ -210,65 +209,65 @@ bool ShadersCompilation::Compile(ShaderCompilationOptions& options) return result; } -ShaderCompiler* ShadersCompilation::CreateCompiler(ShaderProfile profile) +ShaderCompiler* ShadersCompilation::RequestCompiler(ShaderProfile profile, PlatformType platform) { - ShaderCompiler* result = nullptr; - - switch (profile) - { -#if COMPILE_WITH_D3D_SHADER_COMPILER - case ShaderProfile::DirectX_SM4: - case ShaderProfile::DirectX_SM5: - result = New(profile); - break; -#endif -#if COMPILE_WITH_DX_SHADER_COMPILER - case ShaderProfile::DirectX_SM6: - result = New(profile); - break; -#endif -#if COMPILE_WITH_VK_SHADER_COMPILER - case ShaderProfile::Vulkan_SM5: - result = New(profile); - break; -#endif -#if COMPILE_WITH_PS4_SHADER_COMPILER - case ShaderProfile::PS4: - result = New(); - break; -#endif -#if COMPILE_WITH_PS5_SHADER_COMPILER - case ShaderProfile::PS5: - result = New(); - break; -#endif - default: - break; - } - ASSERT_LOW_LAYER(result == nullptr || result->GetProfile() == profile); - - return result; -} - -ShaderCompiler* ShadersCompilation::RequestCompiler(ShaderProfile profile) -{ - ShaderCompiler* compiler; ScopeLock lock(Locker); // Try to find ready compiler + ShaderCompiler* compiler = nullptr; for (int32 i = 0; i < ReadyCompilers.Count(); i++) { - compiler = ReadyCompilers[i]; - if (compiler->GetProfile() == profile) + compiler = ReadyCompilers.Get()[i]; + if (compiler->GetProfile() == profile && + (compiler->GetPlatform() == platform || (int32)compiler->GetPlatform() == 0)) { - // Use it ReadyCompilers.RemoveAt(i); return compiler; } } // Create new compiler for a target profile - compiler = CreateCompiler(profile); + switch (profile) + { +#if COMPILE_WITH_D3D_SHADER_COMPILER + case ShaderProfile::DirectX_SM4: + case ShaderProfile::DirectX_SM5: + compiler = New(profile); + break; +#endif +#if COMPILE_WITH_DX_SHADER_COMPILER + case ShaderProfile::DirectX_SM6: + switch (platform) + { + case PlatformType::XboxScarlett: +#if COMPILE_WITH_XBOX_SCARLETT_SHADER_COMPILER + compiler = New(); +#endif + break; + default: + compiler = New(profile); + break; + } + break; +#endif +#if COMPILE_WITH_VK_SHADER_COMPILER + case ShaderProfile::Vulkan_SM5: + compiler = New(profile); + break; +#endif +#if COMPILE_WITH_PS4_SHADER_COMPILER + case ShaderProfile::PS4: + compiler = New(); + break; +#endif +#if COMPILE_WITH_PS5_SHADER_COMPILER + case ShaderProfile::PS5: + compiler = New(); + break; +#endif + default: + break; + } if (compiler == nullptr) { LOG(Error, "Cannot create Shader Compiler for profile {0}", ::ToString(profile)); @@ -414,7 +413,8 @@ String ShadersCompilation::ResolveShaderPath(StringView path) // Skip to the last root start './' but preserve the leading one for (int32 i = path.Length() - 2; i >= 2; i--) { - if (StringUtils::Compare(path.Get() + i, TEXT("./"), 2) == 0) + const Char* pos = path.Get() + i; + if (pos[0] == '.' && pos[1] == '/') { path = path.Substring(i); break; diff --git a/Source/Engine/ShadersCompilation/ShadersCompilation.h b/Source/Engine/ShadersCompilation/ShadersCompilation.h index 0c251f61e..b32c9f51a 100644 --- a/Source/Engine/ShadersCompilation/ShadersCompilation.h +++ b/Source/Engine/ShadersCompilation/ShadersCompilation.h @@ -48,9 +48,7 @@ public: static String CompactShaderPath(StringView path); private: - - static ShaderCompiler* CreateCompiler(ShaderProfile profile); - static ShaderCompiler* RequestCompiler(ShaderProfile profile); + static ShaderCompiler* RequestCompiler(ShaderProfile profile, PlatformType platform); static void FreeCompiler(ShaderCompiler* compiler); }; From 7e6b040258fde3f6f1e56e2ae5cc1121ca0f7a7b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 12:08:11 +0100 Subject: [PATCH 094/207] Update DXC shader compiler to 1.8 version (for D3D12) --- .../Windows/Binaries/ThirdParty/x64/d3dcompiler_47.dll | 4 ++-- .../Windows/Binaries/ThirdParty/x64/d3dcompiler_47.lib | 4 ++-- .../Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll | 4 ++-- .../Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib | 4 ++-- Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.dll index 0b80ba584..ae087886a 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:352c4ce7151fe9bc37dd9ceddb958a5ee75851c57df961f6381ec552affba198 -size 4916856 +oid sha256:0c38df5cc6a1263def77ae1c06216e8ec2e57c34be5087efc42e84154afad5cb +size 4741464 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.lib index 95e6bc166..21d7c5f14 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/d3dcompiler_47.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be5917a82c8dc43cad798f7a6c11b1dd885eec8ee9675057804ec367f322e7ad -size 8314 +oid sha256:2529bc6d166df699df5fd0f77d9101a2bd24f3231b4040ef72feac5851801674 +size 7930 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll index 4e56df12a..2c0773566 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f504ba5f5173c50ba4ce2771c3d32618f886e1c990960ba706f58872224541b -size 14722664 +oid sha256:c56309c84bc59540b1087ad4ea1930752fa37d10d730919fc7676749a4653ed0 +size 14316936 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib index c77fc6661..a83957886 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxcompiler.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17dfb5b37d5aec03943083c04899b0815556d7f10f020fa8f9f9061c971dc010 -size 2086 +oid sha256:b40e1d5069fdd07c0e7675b2a48860588b89732760a5ff348c5a4de61aa084cd +size 2002 diff --git a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll index ec6f74a5b..9dbf6a623 100644 --- a/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll +++ b/Source/Platforms/Windows/Binaries/ThirdParty/x64/dxil.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6c37738cd2fb4d659b0f49dead8311ae75c93b8c6602b991c00e070f7be20bc1 -size 1508472 +oid sha256:39dbd950f39f75f5c37bcd224c524accba76686adaa372b8287facfc46892b1c +size 1509720 From 5f0e1253cca20e757d2fdb91eb344ae32faf91aa Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 12:08:54 +0100 Subject: [PATCH 095/207] Refactor DX12 Root Signature creation to support offline construction Fix running D3D12 on integrated AMD GPU --- Source/Engine/Core/Types/StringBuilder.h | 5 + .../DirectX/DX12/GPUContextDX12.cpp | 1 + .../DirectX/DX12/GPUDeviceDX12.cpp | 480 ++++++++++++------ .../DirectX/DX12/GPUDeviceDX12.h | 5 - .../DirectX/DX12/RootSignatureDX12.h | 33 ++ 5 files changed, 354 insertions(+), 170 deletions(-) create mode 100644 Source/Engine/GraphicsDevice/DirectX/DX12/RootSignatureDX12.h diff --git a/Source/Engine/Core/Types/StringBuilder.h b/Source/Engine/Core/Types/StringBuilder.h index 925111cc3..620ac8a13 100644 --- a/Source/Engine/Core/Types/StringBuilder.h +++ b/Source/Engine/Core/Types/StringBuilder.h @@ -215,6 +215,11 @@ public: return String(_data.Get(), _data.Count()); } + StringAnsi ToStringAnsi() const + { + return StringAnsi(_data.Get(), _data.Count()); + } + StringView ToStringView() const; }; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index b33f94d61..13769f2dd 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -29,6 +29,7 @@ #include "GPUVertexLayoutDX12.h" #include "CommandQueueDX12.h" #include "DescriptorHeapDX12.h" +#include "RootSignatureDX12.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h" #include "Engine/Debug/Exceptions/NotImplementedException.h" diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 88334be60..62070ecda 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -12,6 +12,9 @@ #include "GPUSamplerDX12.h" #include "GPUVertexLayoutDX12.h" #include "GPUSwapChainDX12.h" +#include "RootSignatureDX12.h" +#include "UploadBufferDX12.h" +#include "CommandQueueDX12.h" #include "Engine/Engine/Engine.h" #include "Engine/Engine/CommandLine.h" #include "Engine/Graphics/RenderTask.h" @@ -21,20 +24,23 @@ #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Core/Log.h" #include "Engine/Core/Config/PlatformSettings.h" -#include "UploadBufferDX12.h" -#include "CommandQueueDX12.h" +#include "Engine/Core/Types/StringBuilder.h" #include "Engine/Core/Utilities.h" #include "Engine/Threading/Threading.h" #include "CommandSignatureDX12.h" static bool CheckDX12Support(IDXGIAdapter* adapter) { +#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE + return true; +#else // Try to create device if (SUCCEEDED(D3D12CreateDevice(adapter, D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), nullptr))) { return true; } return false; +#endif } GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& elements, bool explicitOffsets) @@ -55,6 +61,310 @@ GPUVertexLayoutDX12::GPUVertexLayoutDX12(GPUDeviceDX12* device, const Elements& } } +RootSignatureDX12::RootSignatureDX12() +{ + // Clear structures + Platform::MemoryClear(this, sizeof(*this)); + + // Descriptor tables + { + // SRVs + D3D12_DESCRIPTOR_RANGE& range = _ranges[0]; + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; + range.NumDescriptors = GPU_MAX_SR_BINDED; + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + { + // UAVs + D3D12_DESCRIPTOR_RANGE& range = _ranges[1]; + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + range.NumDescriptors = GPU_MAX_UA_BINDED; + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + { + // Samplers + D3D12_DESCRIPTOR_RANGE& range = _ranges[2]; + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; + range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT; + range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT; + range.RegisterSpace = 0; + range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; + } + + // Root parameters + for (int32 i = 0; i < GPU_MAX_CB_BINDED; i++) + { + // CBs + D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_CB + i]; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + param.Descriptor.ShaderRegister = i; + param.Descriptor.RegisterSpace = 0; + } + { + // SRVs + D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SR]; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + param.DescriptorTable.NumDescriptorRanges = 1; + param.DescriptorTable.pDescriptorRanges = &_ranges[0]; + } + { + // UAVs + D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_UA]; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + param.DescriptorTable.NumDescriptorRanges = 1; + param.DescriptorTable.pDescriptorRanges = &_ranges[1]; + } + { + // Samplers + D3D12_ROOT_PARAMETER& param = _parameters[DX12_ROOT_SIGNATURE_SAMPLER]; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + param.DescriptorTable.NumDescriptorRanges = 1; + param.DescriptorTable.pDescriptorRanges = &_ranges[2]; + } + + // Static samplers + static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(_staticSamplers), "Update static samplers setup."); + // Linear Clamp + InitSampler(0, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP); + // Point Clamp + InitSampler(1, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP); + // Linear Wrap + InitSampler(2, D3D12_FILTER_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_WRAP); + // Point Wrap + InitSampler(3, D3D12_FILTER_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_WRAP); + // Shadow + InitSampler(4, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL); + // Shadow PCF + InitSampler(5, D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR, D3D12_TEXTURE_ADDRESS_MODE_CLAMP, D3D12_COMPARISON_FUNC_LESS_EQUAL); + + // Init + _desc.NumParameters = ARRAY_COUNT(_parameters); + _desc.pParameters = _parameters; + _desc.NumStaticSamplers = ARRAY_COUNT(_staticSamplers); + _desc.pStaticSamplers = _staticSamplers; + _desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; +} + +void RootSignatureDX12::InitSampler(int32 i, D3D12_FILTER filter, D3D12_TEXTURE_ADDRESS_MODE address, D3D12_COMPARISON_FUNC comparisonFunc) +{ + auto& sampler = _staticSamplers[i]; + sampler.Filter = filter; + sampler.AddressU = address; + sampler.AddressV = address; + sampler.AddressW = address; + sampler.MipLODBias = 0.0f; + sampler.MaxAnisotropy = 1; + sampler.ComparisonFunc = comparisonFunc; + sampler.BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; + sampler.MinLOD = 0; + sampler.MaxLOD = D3D12_FLOAT32_MAX; + sampler.ShaderRegister = i; + sampler.RegisterSpace = 0; + sampler.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; +} + +ComPtr RootSignatureDX12::Serialize() const +{ + ComPtr signature; + ComPtr error; + VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&_desc, D3D_ROOT_SIGNATURE_VERSION_1_0, &signature, &error)); + if (error.Get()) + { + LOG(Error, "D3D12SerializeRootSignature failed with error: {}", String((const char*)error->GetBufferPointer())); + } + return signature; +} + +#if USE_EDITOR + +const Char* GetRootSignatureShaderVisibility(D3D12_SHADER_VISIBILITY visibility) +{ + switch (visibility) + { + case D3D12_SHADER_VISIBILITY_VERTEX: + return TEXT(", visibility=SHADER_VISIBILITY_VERTEX"); + case D3D12_SHADER_VISIBILITY_HULL: + return TEXT(", visibility=SHADER_VISIBILITY_HULL"); + case D3D12_SHADER_VISIBILITY_DOMAIN: + return TEXT(", visibility=SHADER_VISIBILITY_DOMAIN"); + case D3D12_SHADER_VISIBILITY_GEOMETRY: + return TEXT(", visibility=SHADER_VISIBILITY_GEOMETRY"); + case D3D12_SHADER_VISIBILITY_PIXEL: + return TEXT(", visibility=SHADER_VISIBILITY_PIXEL"); + case D3D12_SHADER_VISIBILITY_AMPLIFICATION: + return TEXT(", visibility=SHADER_VISIBILITY_AMPLIFICATION"); + case D3D12_SHADER_VISIBILITY_MESH: + return TEXT(", visibility=SHADER_VISIBILITY_MESH"); + case D3D12_SHADER_VISIBILITY_ALL: + default: + return TEXT(""); // Default + } +} + +const Char* GetRootSignatureSamplerFilter(D3D12_FILTER filter) +{ + switch (filter) + { + case D3D12_FILTER_MIN_MAG_MIP_POINT: + return TEXT("FILTER_MIN_MAG_MIP_POINT"); + case D3D12_FILTER_MIN_MAG_MIP_LINEAR: + return TEXT("FILTER_MIN_MAG_MIP_LINEAR"); + case D3D12_FILTER_ANISOTROPIC: + return TEXT("FILTER_ANISOTROPIC"); + case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT: + return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_POINT"); + case D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR: + return TEXT("FILTER_COMPARISON_MIN_MAG_MIP_LINEAR"); + default: + CRASH; // Not implemented + } +} + +const Char* GetRootSignatureSamplerAddress(D3D12_TEXTURE_ADDRESS_MODE address) +{ + switch (address) + { + case D3D12_TEXTURE_ADDRESS_MODE_WRAP: + return TEXT("TEXTURE_ADDRESS_WRAP"); + case D3D12_TEXTURE_ADDRESS_MODE_MIRROR: + return TEXT("TEXTURE_ADDRESS_MIRROR"); + case D3D12_TEXTURE_ADDRESS_MODE_CLAMP: + return TEXT("TEXTURE_ADDRESS_CLAMP"); + case D3D12_TEXTURE_ADDRESS_MODE_BORDER: + return TEXT("TEXTURE_ADDRESS_BORDER"); + case D3D12_TEXTURE_ADDRESS_MODE_MIRROR_ONCE: + return TEXT("TEXTURE_ADDRESS_MIRROR_ONCE"); + default: + return TEXT(""); + } +} + +const Char* GetRootSignatureSamplerComparisonFunc(D3D12_COMPARISON_FUNC func) +{ + switch (func) + { + case D3D12_COMPARISON_FUNC_NEVER: + return TEXT("COMPARISON_NEVER"); + case D3D12_COMPARISON_FUNC_LESS: + return TEXT("COMPARISON_LESS"); + case D3D12_COMPARISON_FUNC_EQUAL: + return TEXT("COMPARISON_EQUAL"); + case D3D12_COMPARISON_FUNC_LESS_EQUAL: + return TEXT("COMPARISON_LESS_EQUAL"); + case D3D12_COMPARISON_FUNC_GREATER: + return TEXT("COMPARISON_GREATER"); + case D3D12_COMPARISON_FUNC_NOT_EQUAL: + return TEXT("COMPARISON_NOT_EQUAL"); + case D3D12_COMPARISON_FUNC_GREATER_EQUAL: + return TEXT("COMPARISON_GREATER_EQUAL"); + case D3D12_COMPARISON_FUNC_ALWAYS: + default: + return TEXT("COMPARISON_ALWAYS"); + } +} + +void RootSignatureDX12::ToString(StringBuilder& sb, bool singleLine) const +{ + // Flags + sb.Append(TEXT("RootFlags(ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT)")); + + // Parameters + const Char newLine = singleLine ? ' ' : '\n'; + for (const D3D12_ROOT_PARAMETER& param : _parameters) + { + const Char* visibility = GetRootSignatureShaderVisibility(param.ShaderVisibility); + switch (param.ParameterType) + { + case D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE: + sb.AppendFormat(TEXT(",{}DescriptorTable("), newLine); + for (uint32 rangeIndex = 0; rangeIndex < param.DescriptorTable.NumDescriptorRanges; rangeIndex++) + { + if (rangeIndex) + sb.Append(TEXT(", ")); + const D3D12_DESCRIPTOR_RANGE& range = param.DescriptorTable.pDescriptorRanges[rangeIndex]; + switch (range.RangeType) + { + case D3D12_DESCRIPTOR_RANGE_TYPE_SRV: + sb.AppendFormat(TEXT("SRV(t{}"), range.BaseShaderRegister); + break; + case D3D12_DESCRIPTOR_RANGE_TYPE_UAV: + sb.AppendFormat(TEXT("UAV(u{}"), range.BaseShaderRegister); + break; + case D3D12_DESCRIPTOR_RANGE_TYPE_CBV: + sb.AppendFormat(TEXT("CBV(b{}"), range.BaseShaderRegister); + break; + case D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER: + sb.AppendFormat(TEXT("Sampler(s{}"), range.BaseShaderRegister); + break; + } + if (range.NumDescriptors != 1) + { + if (range.NumDescriptors == UINT_MAX) + sb.Append(TEXT(", numDescriptors=unbounded")); + else + sb.AppendFormat(TEXT(", numDescriptors={}"), range.NumDescriptors); + } + if (range.OffsetInDescriptorsFromTableStart != D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND) + sb.AppendFormat(TEXT(", offset={}"), range.OffsetInDescriptorsFromTableStart); + sb.Append(')'); + } + sb.AppendFormat(TEXT("{})"), visibility); + break; + case D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS: + sb.AppendFormat(TEXT(",{}RootConstants(num32BitConstants={}, b{}{})"), newLine, param.Constants.Num32BitValues, param.Constants.ShaderRegister, visibility); + break; + case D3D12_ROOT_PARAMETER_TYPE_CBV: + sb.AppendFormat(TEXT(",{}CBV(b{}{})"), newLine, param.Descriptor.ShaderRegister, visibility); + break; + case D3D12_ROOT_PARAMETER_TYPE_SRV: + sb.AppendFormat(TEXT(",{}SRV(t{}{})"), newLine, param.Descriptor.ShaderRegister, visibility); + break; + case D3D12_ROOT_PARAMETER_TYPE_UAV: + sb.AppendFormat(TEXT(",{}UAV(u{}{})"), newLine, param.Descriptor.ShaderRegister, visibility); + break; + } + } + + // Static Samplers + for (const D3D12_STATIC_SAMPLER_DESC& sampler : _staticSamplers) + { + const Char* visibility = GetRootSignatureShaderVisibility(sampler.ShaderVisibility); + sb.AppendFormat(TEXT(",{}StaticSampler(s{}"), newLine, sampler.ShaderRegister); + sb.AppendFormat(TEXT(", filter={}"), GetRootSignatureSamplerFilter(sampler.Filter)); + sb.AppendFormat(TEXT(", addressU={}"), GetRootSignatureSamplerAddress(sampler.AddressU)); + sb.AppendFormat(TEXT(", addressV={}"), GetRootSignatureSamplerAddress(sampler.AddressV)); + sb.AppendFormat(TEXT(", addressW={}"), GetRootSignatureSamplerAddress(sampler.AddressW)); + sb.AppendFormat(TEXT(", comparisonFunc={}"), GetRootSignatureSamplerComparisonFunc(sampler.ComparisonFunc)); + sb.AppendFormat(TEXT(", maxAnisotropy={}"), sampler.MaxAnisotropy); + sb.Append(TEXT(", borderColor=STATIC_BORDER_COLOR_OPAQUE_BLACK")); + sb.AppendFormat(TEXT("{})"), visibility); + } +} + +String RootSignatureDX12::ToString() const +{ + StringBuilder sb; + ToString(sb); + return sb.ToString(); +} + +StringAnsi RootSignatureDX12::ToStringAnsi() const +{ + StringBuilder sb; + ToString(sb); + return sb.ToStringAnsi(); +} + +#endif + GPUDevice* GPUDeviceDX12::Create() { #if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE @@ -561,170 +871,10 @@ bool GPUDeviceDX12::Init() } // Create root signature - // TODO: maybe create set of different root signatures? for UAVs, for compute, for simple drawing, for post fx? { - // Descriptor tables - D3D12_DESCRIPTOR_RANGE r[3]; // SRV+UAV+Sampler - { - D3D12_DESCRIPTOR_RANGE& range = r[0]; - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV; - range.NumDescriptors = GPU_MAX_SR_BINDED; - range.BaseShaderRegister = 0; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - { - D3D12_DESCRIPTOR_RANGE& range = r[1]; - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - range.NumDescriptors = GPU_MAX_UA_BINDED; - range.BaseShaderRegister = 0; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - { - D3D12_DESCRIPTOR_RANGE& range = r[2]; - range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER; - range.NumDescriptors = GPU_MAX_SAMPLER_BINDED - GPU_STATIC_SAMPLERS_COUNT; - range.BaseShaderRegister = GPU_STATIC_SAMPLERS_COUNT; - range.RegisterSpace = 0; - range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; - } - - // Root parameters - D3D12_ROOT_PARAMETER rootParameters[GPU_MAX_CB_BINDED + 3]; - for (int32 i = 0; i < GPU_MAX_CB_BINDED; i++) - { - // CB - D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_CB + i]; - rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; - rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - rootParam.Descriptor.ShaderRegister = i; - rootParam.Descriptor.RegisterSpace = 0; - } - { - // SRVs - D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_SR]; - rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - rootParam.DescriptorTable.NumDescriptorRanges = 1; - rootParam.DescriptorTable.pDescriptorRanges = &r[0]; - } - { - // UAVs - D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_UA]; - rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - rootParam.DescriptorTable.NumDescriptorRanges = 1; - rootParam.DescriptorTable.pDescriptorRanges = &r[1]; - } - { - // Samplers - D3D12_ROOT_PARAMETER& rootParam = rootParameters[DX12_ROOT_SIGNATURE_SAMPLER]; - rootParam.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - rootParam.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - rootParam.DescriptorTable.NumDescriptorRanges = 1; - rootParam.DescriptorTable.pDescriptorRanges = &r[2]; - } - - // Static samplers - D3D12_STATIC_SAMPLER_DESC staticSamplers[6]; - static_assert(GPU_STATIC_SAMPLERS_COUNT == ARRAY_COUNT(staticSamplers), "Update static samplers setup."); - // Linear Clamp - staticSamplers[0].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; - staticSamplers[0].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[0].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[0].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[0].MipLODBias = 0.0f; - staticSamplers[0].MaxAnisotropy = 1; - staticSamplers[0].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[0].MinLOD = 0; - staticSamplers[0].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[0].ShaderRegister = 0; - staticSamplers[0].RegisterSpace = 0; - staticSamplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Point Clamp - staticSamplers[1].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; - staticSamplers[1].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[1].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[1].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[1].MipLODBias = 0.0f; - staticSamplers[1].MaxAnisotropy = 1; - staticSamplers[1].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[1].MinLOD = 0; - staticSamplers[1].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[1].ShaderRegister = 1; - staticSamplers[1].RegisterSpace = 0; - staticSamplers[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Linear Wrap - staticSamplers[2].Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; - staticSamplers[2].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[2].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[2].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[2].MipLODBias = 0.0f; - staticSamplers[2].MaxAnisotropy = 1; - staticSamplers[2].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[2].MinLOD = 0; - staticSamplers[2].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[2].ShaderRegister = 2; - staticSamplers[2].RegisterSpace = 0; - staticSamplers[2].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Point Wrap - staticSamplers[3].Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; - staticSamplers[3].AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[3].AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[3].AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; - staticSamplers[3].MipLODBias = 0.0f; - staticSamplers[3].MaxAnisotropy = 1; - staticSamplers[3].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[3].MinLOD = 0; - staticSamplers[3].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[3].ShaderRegister = 3; - staticSamplers[3].RegisterSpace = 0; - staticSamplers[3].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Shadow - staticSamplers[4].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_POINT; - staticSamplers[4].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[4].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[4].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[4].MipLODBias = 0.0f; - staticSamplers[4].MaxAnisotropy = 1; - staticSamplers[4].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; - staticSamplers[4].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[4].MinLOD = 0; - staticSamplers[4].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[4].ShaderRegister = 4; - staticSamplers[4].RegisterSpace = 0; - staticSamplers[4].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - // Shadow PCF - staticSamplers[5].Filter = D3D12_FILTER_COMPARISON_MIN_MAG_MIP_LINEAR; - staticSamplers[5].AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[5].AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[5].AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; - staticSamplers[5].MipLODBias = 0.0f; - staticSamplers[5].MaxAnisotropy = 1; - staticSamplers[5].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; - staticSamplers[5].BorderColor = D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK; - staticSamplers[5].MinLOD = 0; - staticSamplers[5].MaxLOD = D3D12_FLOAT32_MAX; - staticSamplers[5].ShaderRegister = 5; - staticSamplers[5].RegisterSpace = 0; - staticSamplers[5].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; - - // Init - D3D12_ROOT_SIGNATURE_DESC rootSignatureDesc; - rootSignatureDesc.NumParameters = ARRAY_COUNT(rootParameters); - rootSignatureDesc.pParameters = rootParameters; - rootSignatureDesc.NumStaticSamplers = ARRAY_COUNT(staticSamplers); - rootSignatureDesc.pStaticSamplers = staticSamplers; - rootSignatureDesc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; - - // Serialize - ComPtr signature; - ComPtr error; - VALIDATE_DIRECTX_CALL(D3D12SerializeRootSignature(&rootSignatureDesc, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); - - // Create - VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&_rootSignature))); + RootSignatureDX12 signature; + ComPtr signatureBlob = signature.Serialize(); + VALIDATE_DIRECTX_CALL(_device->CreateRootSignature(0, signatureBlob->GetBufferPointer(), signatureBlob->GetBufferSize(), IID_PPV_ARGS(&_rootSignature))); } if (TimestampQueryHeap.Init()) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h index 064ed9a01..bb5d53458 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h @@ -18,11 +18,6 @@ #define DX12_BACK_BUFFER_COUNT 2 #endif -#define DX12_ROOT_SIGNATURE_CB 0 -#define DX12_ROOT_SIGNATURE_SR (GPU_MAX_CB_BINDED+0) -#define DX12_ROOT_SIGNATURE_UA (GPU_MAX_CB_BINDED+1) -#define DX12_ROOT_SIGNATURE_SAMPLER (GPU_MAX_CB_BINDED+2) - class Engine; class WindowsWindow; class GPUContextDX12; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/RootSignatureDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/RootSignatureDX12.h new file mode 100644 index 000000000..156b6ae04 --- /dev/null +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/RootSignatureDX12.h @@ -0,0 +1,33 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Graphics/Config.h" +#include "../IncludeDirectXHeaders.h" + +#define DX12_ROOT_SIGNATURE_CB 0 +#define DX12_ROOT_SIGNATURE_SR (GPU_MAX_CB_BINDED+0) +#define DX12_ROOT_SIGNATURE_UA (GPU_MAX_CB_BINDED+1) +#define DX12_ROOT_SIGNATURE_SAMPLER (GPU_MAX_CB_BINDED+2) + +struct RootSignatureDX12 +{ +private: + D3D12_ROOT_SIGNATURE_DESC _desc; + D3D12_DESCRIPTOR_RANGE _ranges[3]; + D3D12_ROOT_PARAMETER _parameters[GPU_MAX_CB_BINDED + 3]; + D3D12_STATIC_SAMPLER_DESC _staticSamplers[6]; + +public: + RootSignatureDX12(); + + ComPtr Serialize() const; +#if USE_EDITOR + void ToString(class StringBuilder& sb, bool singleLine = false) const; + String ToString() const; + StringAnsi ToStringAnsi() const; +#endif + +private: + void InitSampler(int32 i, D3D12_FILTER filter, D3D12_TEXTURE_ADDRESS_MODE address, D3D12_COMPARISON_FUNC comparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL); +}; From e03d0f332298e95242ce267e2c0a61474516c88c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 12:09:20 +0100 Subject: [PATCH 096/207] Fix shader compilation with HLSL 2021 --- Content/Shaders/VolumetricFog.flax | 4 +-- Source/Shaders/Common.hlsl | 34 +++++++++++++++++++++++ Source/Shaders/GammaCorrectionCommon.hlsl | 2 +- Source/Shaders/Math.hlsl | 2 +- Source/Shaders/VolumetricFog.shader | 2 +- 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax index 6f8776336..6dfecbb26 100644 --- a/Content/Shaders/VolumetricFog.flax +++ b/Content/Shaders/VolumetricFog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a3e331b7d688d4d3a11da6bb50d4608dbc6437978433a83cc79a365f520bc58 -size 13206 +oid sha256:872ac3560279bfd0aeb989ebac1b49750dd142b985bc40058888dfd2b63fe9b2 +size 13214 diff --git a/Source/Shaders/Common.hlsl b/Source/Shaders/Common.hlsl index 834348ade..10b2855f4 100644 --- a/Source/Shaders/Common.hlsl +++ b/Source/Shaders/Common.hlsl @@ -93,6 +93,40 @@ #endif +// Compiler support for HLSL 2021 that is stricter (need to use or/and/select for vector-based logical operators) +#if !defined(__DXC_VERSION_MAJOR) || (__DXC_VERSION_MAJOR <= 1 && __DXC_VERSION_MINOR < 7) + +bool InternalAnd(bool a, bool b) { return bool(a && b); } +bool2 InternalAnd(bool2 a, bool2 b) { return bool2(a.x && b.x, a.y && b.y); } +bool3 InternalAnd(bool3 a, bool3 b) { return bool3(a.x && b.x, a.y && b.y, a.z && b.z); } +bool4 InternalAnd(bool4 a, bool4 b) { return bool4(a.x && b.x, a.y && b.y, a.z && b.z, a.w && b.w); } + +bool InternalOr(bool a, bool b) { return bool(a || b); } +bool2 InternalOr(bool2 a, bool2 b) { return bool2(a.x || b.x, a.y || b.y); } +bool3 InternalOr(bool3 a, bool3 b) { return bool3(a.x || b.x, a.y || b.y, a.z || b.z); } +bool4 InternalOr(bool4 a, bool4 b) { return bool4(a.x || b.x, a.y || b.y, a.z || b.z, a.w || b.w); } + +#define SELECT_INTERNAL(type) \ + type InternalSelect(bool c, type a, type b) { return type (c ? a.x : b.x); } \ + type##2 InternalSelect(bool c, type##2 a, type##2 b) { return type##2(c ? a.x : b.x, c ? a.y : b.y); } \ + type##2 InternalSelect(bool2 c, type a, type b) { return type##2(c.x ? a : b, c.y ? a : b); } \ + type##2 InternalSelect(bool2 c, type##2 a, type##2 b) { return type##2(c.x ? a.x : b.x, c.y ? a.y : b.y); } \ + type##3 InternalSelect(bool c, type##3 a, type##3 b) { return type##3(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z); } \ + type##3 InternalSelect(bool3 c, type a, type b) { return type##3(c.x ? a : b, c.y ? a : b, c.z ? a : b); } \ + type##3 InternalSelect(bool3 c, type##3 a, type##3 b) { return type##3(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z); } \ + type##4 InternalSelect(bool c, type##4 a, type##4 b) { return type##4(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z, c ? a.w : b.w); } \ + type##4 InternalSelect(bool4 c, type a, type b) { return type##4(c.x ? a : b, c.y ? a : b, c.z ? a : b, c.w ? a : b); } \ + type##4 InternalSelect(bool4 c, type##4 a, type##4 b) { return type##4(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z, c.w ? a.w : b.w); } +SELECT_INTERNAL(uint) +SELECT_INTERNAL(float) +#undef SELECT_INTERNAL + +#define and(a, b) InternalAnd(a, b) +#define or(a, b) InternalOr(a, b) +#define select(c, a, b) InternalSelect(c, a, b) + +#endif + // Compiler attribute fallback #ifndef UNROLL #define UNROLL diff --git a/Source/Shaders/GammaCorrectionCommon.hlsl b/Source/Shaders/GammaCorrectionCommon.hlsl index e410400ad..df188b347 100644 --- a/Source/Shaders/GammaCorrectionCommon.hlsl +++ b/Source/Shaders/GammaCorrectionCommon.hlsl @@ -52,7 +52,7 @@ float3 LinearToSrgb(float3 linearColor) float3 sRGBToLinear(float3 color) { color = max(6.10352e-5, color); - return color > 0.04045 ? pow(color * (1.0 / 1.055) + 0.0521327, 2.4) : color * (1.0 / 12.92); + return select(color > 0.04045, pow(color * (1.0 / 1.055) + 0.0521327, 2.4), color * (1.0 / 12.92)); } float3 LogToLinear(float3 logColor) diff --git a/Source/Shaders/Math.hlsl b/Source/Shaders/Math.hlsl index 4ba7bbbbb..b590b60b4 100644 --- a/Source/Shaders/Math.hlsl +++ b/Source/Shaders/Math.hlsl @@ -8,7 +8,7 @@ uint NextPow2(uint value) { - uint mask = (1 << firstbithigh(value)) - 1; + uint mask = (1u << firstbithigh(value)) - 1u; return (value + mask) & ~mask; } diff --git a/Source/Shaders/VolumetricFog.shader b/Source/Shaders/VolumetricFog.shader index d3885c20b..c55242996 100644 --- a/Source/Shaders/VolumetricFog.shader +++ b/Source/Shaders/VolumetricFog.shader @@ -341,7 +341,7 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_ if (all(gridCoordinate < GridSizeInt)) { - scatteringAndExtinction = isnan(scatteringAndExtinction) || isinf(scatteringAndExtinction) ? 0 : scatteringAndExtinction; + scatteringAndExtinction = select(or(isnan(scatteringAndExtinction), isinf(scatteringAndExtinction)), 0, scatteringAndExtinction); RWLightScattering[gridCoordinate] = max(scatteringAndExtinction, 0); } } From 1e4f96486fe4038d39b5e8e9c663d2c1e97be936 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 17 Nov 2025 09:58:08 -0800 Subject: [PATCH 097/207] Fix audio playback bug from video on Xbox --- Source/Engine/Video/MF/VideoBackendMF.cpp | 11 ++++++++++- Source/Engine/Video/Video.Build.cs | 11 +++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Video/MF/VideoBackendMF.cpp b/Source/Engine/Video/MF/VideoBackendMF.cpp index b8ef7ee4e..df24f5eed 100644 --- a/Source/Engine/Video/MF/VideoBackendMF.cpp +++ b/Source/Engine/Video/MF/VideoBackendMF.cpp @@ -12,6 +12,12 @@ #if USE_EDITOR #include "Editor/Editor.h" #endif +#if PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT +#define USE_STOCKD3D 0 +#include +#include +#include +#else #include #if WINVER >= _WIN32_WINNT_WINBLUE && WINVER < _WIN32_WINNT_WIN10 // Fix compilation for Windows 8.1 on the latest Windows SDK @@ -24,6 +30,7 @@ typedef enum _MFVideoSphericalFormat { } MFVideoSphericalFormat; #include #include #include +#endif #define VIDEO_API_MF_ERROR(api, err) LOG(Warning, "[VideoBackendMF] {} failed with error 0x{:x}", TEXT(#api), (uint64)err) @@ -111,7 +118,7 @@ namespace MF player.Format = PixelFormat::NV12; else if (subtype == MFVideoFormat_YUY2) player.Format = PixelFormat::YUY2; -#if (WDK_NTDDI_VERSION >= NTDDI_WIN10) +#if (WDK_NTDDI_VERSION >= NTDDI_WIN10) && PLATFORM_WINDOWS else if (subtype == MFVideoFormat_A2R10G10B10) player.Format = PixelFormat::R10G10B10A2_UNorm; else if (subtype == MFVideoFormat_A16B16G16R16F) @@ -150,6 +157,7 @@ namespace MF } else if (majorType == MFMediaType_Audio) { +#if !(PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT) // TODO: fix missing MFAudioFormat_PCM/MFAudioFormat_Float convertion on Xbox (bug?) player.AudioInfo.SampleRate = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_SAMPLES_PER_SECOND, 0); player.AudioInfo.NumChannels = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_NUM_CHANNELS, 0); player.AudioInfo.BitDepth = MFGetAttributeUINT32(mediaType, MF_MT_AUDIO_BITS_PER_SAMPLE, 16); @@ -173,6 +181,7 @@ namespace MF } customType->Release(); } +#endif } result = false; diff --git a/Source/Engine/Video/Video.Build.cs b/Source/Engine/Video/Video.Build.cs index fe99c1433..3975480ee 100644 --- a/Source/Engine/Video/Video.Build.cs +++ b/Source/Engine/Video/Video.Build.cs @@ -22,8 +22,6 @@ public class Video : EngineModule { case TargetPlatform.Windows: case TargetPlatform.UWP: - case TargetPlatform.XboxOne: - case TargetPlatform.XboxScarlett: // Media Foundation options.SourcePaths.Add(Path.Combine(FolderPath, "MF")); options.CompileEnv.PreprocessorDefinitions.Add("VIDEO_API_MF"); @@ -34,6 +32,15 @@ public class Video : EngineModule options.OutputFiles.Add("mfreadwrite.lib"); options.OutputFiles.Add("mfuuid.lib"); break; + case TargetPlatform.XboxOne: + case TargetPlatform.XboxScarlett: + // Media Foundation + options.SourcePaths.Add(Path.Combine(FolderPath, "MF")); + options.CompileEnv.PreprocessorDefinitions.Add("VIDEO_API_MF"); + options.OutputFiles.Add("mfplat.lib"); + options.OutputFiles.Add("mfreadwrite.lib"); + options.OutputFiles.Add("mfuuid.lib"); + break; case TargetPlatform.Mac: case TargetPlatform.iOS: // AVFoundation From c437daf9be4f2643eb70ac490c8346fd669850f8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 05:42:02 -0800 Subject: [PATCH 098/207] Fix new compiler DXC changes --- Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp | 4 +++- Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp | 2 +- Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 13769f2dd..6752a9b8d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -630,7 +630,9 @@ void GPUContextDX12::flushPS() LOG(Error, "Missing Vertex Layout (not assigned to GPUBuffer). Vertex Shader won't read valid data resulting incorrect visuals."); } #endif - _commandList->SetPipelineState(_currentState->GetState(_rtDepth, _rtCount, _rtHandles, _vertexLayout)); + ID3D12PipelineState* pso = _currentState->GetState(_rtDepth, _rtCount, _rtHandles, _vertexLayout); + ASSERT(pso); + _commandList->SetPipelineState(pso); if (_primitiveTopology != _currentState->PrimitiveTopology) { _primitiveTopology = _currentState->PrimitiveTopology; diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp index aa2f70ac7..ca04c916d 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp @@ -222,7 +222,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD argsFull.Add(TEXT("-D")); argsFull.Add(*d); } - GetArgs(argsFull); + GetArgs(meta, argsFull); // Compile ComPtr results; diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h index a3e6d7073..bf522cf3b 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h @@ -32,7 +32,7 @@ public: ~ShaderCompilerDX(); protected: - virtual void GetArgs(Array> args) + virtual void GetArgs(ShaderFunctionMeta& meta, Array>& args) { } From de9e282bad4430b7f634908a6c1d772351cfc0b8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 05:42:21 -0800 Subject: [PATCH 099/207] Fix config version in GDK manifest --- Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp b/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp index fa1ac4d0b..256a3478d 100644 --- a/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/GDK/GDKPlatformTools.cpp @@ -127,7 +127,7 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla validName.Add('\0'); sb.Append(TEXT("\n")); - sb.Append(TEXT("\n")); + sb.Append(TEXT("\n")); sb.AppendFormat(TEXT(" \n"), validName.Get(), platformSettings->PublisherName.HasChars() ? platformSettings->PublisherName : TEXT("CN=") + gameSettings->CompanyName, From 2ca435a414c92ba7b08440130d90f40fcbb4d2a3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 16:11:31 +0100 Subject: [PATCH 100/207] Fix shader graph assets loading to wait for async task #3802 --- Source/Engine/Content/AssetsContainer.h | 6 ++++-- .../Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp | 8 ++++---- .../Tools/MaterialGenerator/MaterialGenerator.Layer.cpp | 4 ++-- .../MaterialGenerator/MaterialGenerator.Material.cpp | 8 ++++---- Source/Engine/Visject/ShaderGraph.cpp | 4 ++-- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Content/AssetsContainer.h b/Source/Engine/Content/AssetsContainer.h index aceba1f07..dd9b549b5 100644 --- a/Source/Engine/Content/AssetsContainer.h +++ b/Source/Engine/Content/AssetsContainer.h @@ -18,7 +18,7 @@ public: /// The asset id. /// Loaded asset of null. template - T* LoadAsync(const Guid& id) + T* Load(const Guid& id) { for (auto& e : *this) { @@ -26,8 +26,10 @@ public: return (T*)e.Get(); } auto asset = (T*)::LoadAsset(id, T::TypeInitializer); - if (asset) + if (asset && !asset->WaitForLoaded()) Add(asset); + else + asset = nullptr; return asset; } diff --git a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp index 197b53e13..6ae810d82 100644 --- a/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.Particles.cpp @@ -425,8 +425,8 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va case 300: { // Load function asset - const auto function = Assets.LoadAsync((Guid)node->Values[0]); - if (!function || function->WaitForLoaded()) + const auto function = Assets.Load((Guid)node->Values[0]); + if (!function) { OnError(node, box, TEXT("Missing or invalid function.")); value = Value::Zero; @@ -439,7 +439,7 @@ void ParticleEmitterGPUGenerator::ProcessGroupParticles(Box* box, Node* node, Va { if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300)) { - const auto callFunc = Assets.LoadAsync((Guid)_callStack[i]->Values[0]); + const auto callFunc = Assets.Load((Guid)_callStack[i]->Values[0]); if (callFunc == function) { OnError(node, box, String::Format(TEXT("Recursive call to function '{0}'!"), function->ToString())); @@ -514,7 +514,7 @@ void ParticleEmitterGPUGenerator::ProcessGroupFunction(Box* box, Node* node, Val value = Value::Zero; break; } - const auto function = Assets.LoadAsync((Guid)functionCallNode->Values[0]); + const auto function = Assets.Load((Guid)functionCallNode->Values[0]); if (!_functions.TryGet(functionCallNode, graph) || !function) { OnError(node, box, TEXT("Missing calling function graph.")); diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp index 73b3058a3..c0f1ede7e 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Layer.cpp @@ -50,8 +50,8 @@ MaterialLayer* MaterialGenerator::GetLayer(const Guid& id, Node* caller) } // Load asset - Asset* asset = Assets.LoadAsync(id); - if (asset == nullptr || asset->WaitForLoaded(10 * 1000)) + Asset* asset = Assets.Load(id); + if (asset == nullptr) { OnError(caller, nullptr, TEXT("Failed to load material asset.")); return nullptr; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index 4594446ec..c778c03ee 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -285,8 +285,8 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) case 24: { // Load function asset - const auto function = Assets.LoadAsync((Guid)node->Values[0]); - if (!function || function->WaitForLoaded()) + const auto function = Assets.Load((Guid)node->Values[0]); + if (!function) { OnError(node, box, TEXT("Missing or invalid function.")); value = Value::Zero; @@ -299,7 +299,7 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value) { if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(1, 24)) { - const auto callFunc = Assets.LoadAsync((Guid)_callStack[i]->Values[0]); + const auto callFunc = Assets.Load((Guid)_callStack[i]->Values[0]); if (callFunc == function) { OnError(node, box, String::Format(TEXT("Recursive call to function '{0}'!"), function->ToString())); @@ -808,7 +808,7 @@ void MaterialGenerator::ProcessGroupFunction(Box* box, Node* node, Value& value) value = Value::Zero; break; } - const auto function = Assets.LoadAsync((Guid)functionCallNode->Values[0]); + const auto function = Assets.Load((Guid)functionCallNode->Values[0]); if (!_functions.TryGet(functionCallNode, graph) || !function) { OnError(node, box, TEXT("Missing calling function graph.")); diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index b6616d159..688e46382 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -704,8 +704,8 @@ void ShaderGenerator::ProcessGroupTools(Box* box, Node* node, Value& value) case 16: { // Get the variable type - auto asset = Assets.LoadAsync((Guid)node->Values[0]); - if (!asset || asset->WaitForLoaded()) + auto asset = Assets.Load((Guid)node->Values[0]); + if (!asset) { OnError(node, box, TEXT("Failed to load Gameplay Global asset.")); value = Value::Zero; From ed408917c6a1298e103ef73a147b01737c3695e8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 16:41:24 +0100 Subject: [PATCH 101/207] Fix Visject Surface node dependent connection types init on load #3802 --- Source/Editor/Surface/SurfaceNode.cs | 21 ------------------ .../VisjectSurfaceContext.Serialization.cs | 22 ++++++++++++++----- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index e1d7c131f..6312bd68d 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -431,27 +431,6 @@ namespace FlaxEditor.Surface /// public bool HasIndependentBoxes => Archetype.IndependentBoxes != null; - /// - /// Gets a value indicating whether this node has dependent boxes with assigned valid types. Otherwise any box has no dependent type assigned. - /// - public bool HasDependentBoxesSetup - { - get - { - if (Archetype.DependentBoxes == null || Archetype.IndependentBoxes == null) - return true; - - for (int i = 0; i < Archetype.DependentBoxes.Length; i++) - { - var b = GetBox(Archetype.DependentBoxes[i]); - if (b != null && b.CurrentType == b.DefaultType) - return false; - } - - return true; - } - } - private static readonly List UpdateStack = new List(); /// diff --git a/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs b/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs index a00d37aef..ed19b937f 100644 --- a/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs +++ b/Source/Editor/Surface/VisjectSurfaceContext.Serialization.cs @@ -178,19 +178,31 @@ namespace FlaxEditor.Surface // Update boxes types for nodes that dependant box types based on incoming connections { - bool keepUpdating = false; - int updateLimit = 100; + bool keepUpdating = true; + int updatesMin = 2, updatesMax = 100; do { + keepUpdating = false; for (int i = 0; i < RootControl.Children.Count; i++) { - if (RootControl.Children[i] is SurfaceNode node && !node.HasDependentBoxesSetup) + if (RootControl.Children[i] is SurfaceNode node) { node.UpdateBoxesTypes(); - keepUpdating = true; + var arch = node.Archetype; + if (arch.DependentBoxes != null && arch.IndependentBoxes != null) + { + foreach (var boxId in arch.DependentBoxes) + { + var b = node.GetBox(boxId); + if (b != null && b.CurrentType == b.DefaultType) + { + keepUpdating = true; + } + } + } } } - } while (keepUpdating && updateLimit-- > 0); + } while ((keepUpdating && --updatesMax > 0) || --updatesMin > 0); } Loaded?.Invoke(this); From 3efb981f00be75f8b82d45791a5935cc9e93713c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 16:51:43 +0100 Subject: [PATCH 102/207] Fix rare issues on input bugs in Visject --- Source/Editor/Surface/VisjectSurface.Input.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 51fd96ad6..cbc041c09 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -469,7 +469,8 @@ namespace FlaxEditor.Surface bool handled = base.OnMouseDown(location, button); if (!handled) CustomMouseDown?.Invoke(ref location, button, ref handled); - if (handled) + var root = Root; + if (handled || root == null) { // Clear flags _isMovingSelection = false; @@ -523,11 +524,11 @@ namespace FlaxEditor.Surface if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation)) { // Check if user is pressing control - if (Root.GetKey(KeyboardKeys.Control)) + if (root.GetKey(KeyboardKeys.Control)) { AddToSelection(controlUnderMouse); } - else if (Root.GetKey(KeyboardKeys.Shift)) + else if (root.GetKey(KeyboardKeys.Shift)) { RemoveFromSelection(controlUnderMouse); } @@ -539,7 +540,7 @@ namespace FlaxEditor.Surface } // Start moving selected nodes - if (!Root.GetKey(KeyboardKeys.Shift)) + if (!root.GetKey(KeyboardKeys.Shift)) { StartMouseCapture(); _movingSelectionViewPos = _rootControl.Location; @@ -559,7 +560,7 @@ namespace FlaxEditor.Surface // Start selecting or commenting StartMouseCapture(); - if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift)) + if (!root.GetKey(KeyboardKeys.Control) && !root.GetKey(KeyboardKeys.Shift)) { ClearSelection(); } From c0dda45c7b2cd95235e408900ddebaf5eac9490f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 18:00:02 +0100 Subject: [PATCH 103/207] Fix crash on exit when C# code was bound to asset unloading event called after C# shutdown --- Source/Engine/Scripting/ManagedCLR/MCore.cpp | 6 ++++++ Source/Engine/Scripting/ManagedCLR/MCore.h | 4 ++++ Source/Engine/Scripting/Runtime/DotNet.cpp | 4 +++- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.cpp index 4300434e3..350cc39d2 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MCore.cpp @@ -21,6 +21,7 @@ MDomain* MRootDomain = nullptr; MDomain* MActiveDomain = nullptr; Array> MDomains; +bool MCore::Ready = false; MClass* MCore::TypeCache::Void = nullptr; MClass* MCore::TypeCache::Object = nullptr; @@ -301,6 +302,11 @@ bool MProperty::IsStatic() const return false; } +void MCore::OnManagedEventAfterShutdown(const char* eventName) +{ + LOG(Error, "Found a binding leak on '{}' event used by C# scripting after shutdown. Ensure to unregister scripting events from objects during disposing.", ::String(eventName)); +} + MDomain* MCore::GetRootDomain() { return MRootDomain; diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.h b/Source/Engine/Scripting/ManagedCLR/MCore.h index cedebe7b8..1f972af0d 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.h +++ b/Source/Engine/Scripting/ManagedCLR/MCore.h @@ -57,6 +57,10 @@ public: static void UnloadScriptingAssemblyLoadContext(); #endif + // Utility for guarding against using C# scripting runtime after shutdown (eg. when asset delegate is not properly disposed). + static bool Ready; + static void OnManagedEventAfterShutdown(const char* eventName); + public: /// /// Utilities for C# object management. diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 1b220b017..d8e5acaff 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -316,7 +316,8 @@ bool MCore::LoadEngine() char* buildInfo = CallStaticMethod(GetStaticMethodPointer(TEXT("GetRuntimeInformation"))); LOG(Info, ".NET runtime version: {0}", ::String(buildInfo)); - MCore::GC::FreeMemory(buildInfo); + GC::FreeMemory(buildInfo); + Ready = true; return false; } @@ -327,6 +328,7 @@ void MCore::UnloadEngine() return; PROFILE_CPU(); CallStaticMethod(GetStaticMethodPointer(TEXT("Exit"))); + Ready = false; MDomains.ClearDelete(); MRootDomain = nullptr; ShutdownHostfxr(); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 2f251932f..190036af4 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -2043,6 +2043,7 @@ namespace Flax.Build.Bindings contents.Append(')').AppendLine(); contents.Append(" {").AppendLine(); contents.Append(" static MMethod* method = nullptr;").AppendLine(); + contents.AppendFormat(" if (!MCore::Ready) {{ MCore::OnManagedEventAfterShutdown(\"{0}.{1}\"); return; }}", classTypeNameManaged, eventInfo.Name).AppendLine(); contents.AppendFormat(" if (!method) {{ method = {1}::TypeInitializer.GetClass()->GetMethod(\"Internal_{0}_Invoke\", {2}); CHECK(method); }}", eventInfo.Name, classTypeNameNative, paramsCount).AppendLine(); contents.Append(" MObject* exception = nullptr;").AppendLine(); if (paramsCount == 0) From 8f56ab9534c24a18906c07e9fff1ed900fe024b8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 18:45:43 +0100 Subject: [PATCH 104/207] Fix crash when creating empty material instance --- Source/Engine/Content/Assets/MaterialInstance.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Content/Assets/MaterialInstance.cpp b/Source/Engine/Content/Assets/MaterialInstance.cpp index 78a276a8a..8d142626e 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.cpp +++ b/Source/Engine/Content/Assets/MaterialInstance.cpp @@ -243,7 +243,8 @@ Asset::LoadResult MaterialInstance::load() ParamsChanged(); } - baseMaterial->RemoveReference(); + if (baseMaterial) + baseMaterial->RemoveReference(); return LoadResult::Ok; } From dfb1fb91a51a90228b38adb7975f43d3a147c172 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 18 Nov 2025 21:29:51 +0100 Subject: [PATCH 105/207] Fix ability to override material instance hidden parameters #3802 --- Source/Editor/Windows/Assets/MaterialInstanceWindow.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs b/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs index 4a896c674..cb39930f9 100644 --- a/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs +++ b/Source/Editor/Windows/Assets/MaterialInstanceWindow.cs @@ -14,7 +14,6 @@ using FlaxEditor.Surface; using FlaxEditor.Viewport.Previews; using FlaxEngine; using FlaxEngine.GUI; -using FlaxEngine.Utilities; namespace FlaxEditor.Windows.Assets { @@ -430,7 +429,7 @@ namespace FlaxEditor.Windows.Assets for (var i = 0; i < parameters.Length; i++) { var p = parameters[i]; - if (p.IsOverride) + if (p.IsOverride && p.IsPublic) { p.IsOverride = false; actions.Add(new EditParamOverrideAction From e2aaef9b88033d7cfffc1b5b277c1cf45c01bcc3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 19 Nov 2025 08:27:33 -0800 Subject: [PATCH 106/207] Fix shader warning --- Source/Shaders/MaterialCommon.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Shaders/MaterialCommon.hlsl b/Source/Shaders/MaterialCommon.hlsl index 02f754c94..8c9567c29 100644 --- a/Source/Shaders/MaterialCommon.hlsl +++ b/Source/Shaders/MaterialCommon.hlsl @@ -311,7 +311,7 @@ float3 AOMultiBounce(float visibility, float3 albedo) float2 Flipbook(float2 uv, float frame, float2 sizeXY, float2 flipXY = 0.0f) { - float tile = (int)fmod(frame, sizeXY.x * sizeXY.y); + float tile = (float)(int)fmod(frame, sizeXY.x * sizeXY.y); float2 tileCount = float2(1.0, 1.0) / sizeXY; float tileY = abs(flipXY.y * sizeXY.y - (floor(tile * tileCount.x) + flipXY.y * 1)); float tileX = abs(flipXY.x * sizeXY.x - ((tile - sizeXY.x * floor(tile * tileCount.x)) + flipXY.x * 1)); From 032f698c7ba4101356d227153211925ba4994394 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 19 Nov 2025 23:29:28 -0800 Subject: [PATCH 107/207] Fix shader warning --- Source/Shaders/AtmosphereFog.hlsl | 7 ++----- Source/Shaders/ShadowsSampling.hlsl | 5 ++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Source/Shaders/AtmosphereFog.hlsl b/Source/Shaders/AtmosphereFog.hlsl index 81bb2e1fd..527260d5a 100644 --- a/Source/Shaders/AtmosphereFog.hlsl +++ b/Source/Shaders/AtmosphereFog.hlsl @@ -329,7 +329,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi if (intersectAtmosphere(viewPosition, viewDir, offset, maxPathLength)) { return float3(offset / 10, 0, 0); - +#if 0 float pathLength = distance(viewPosition, surfacePos); //return pathLength.xxx; @@ -413,6 +413,7 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi float sunIntensity = 10; inscatteredLight *= sunIntensity; } +#endif } return inscatteredLight; @@ -420,8 +421,6 @@ float3 GetInscatteredLight(AtmosphericFogData atmosphericFog, in float3 viewPosi float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 viewPosition, float3 viewVector, float sceneDepth, float3 sceneColor) { - float4 result = float4(1, 0, 0, 1); - #if 0 float scale = 0.00001f * atmosphericFog.AtmosphericFogDistanceScale;// convert cm to km @@ -536,8 +535,6 @@ float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float //return float4(sun + groundColor + inscatterColor, 1); #endif - - return result; } float4 GetAtmosphericFog(AtmosphericFogData atmosphericFog, float viewFar, float3 worldPosition, float3 cameraPosition) diff --git a/Source/Shaders/ShadowsSampling.hlsl b/Source/Shaders/ShadowsSampling.hlsl index 071b6f723..ac181732b 100644 --- a/Source/Shaders/ShadowsSampling.hlsl +++ b/Source/Shaders/ShadowsSampling.hlsl @@ -227,7 +227,8 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer // Increase the sharpness for higher cascades to match the filter radius const float SharpnessScale[MaxNumCascades] = { 1.0f, 1.5f, 3.0f, 3.5f }; shadow.Sharpness *= SharpnessScale[cascadeIndex]; - + + result.TransmissionShadow = 1; #if defined(USE_GBUFFER_CUSTOM_DATA) // Subsurface shadowing BRANCH @@ -239,8 +240,6 @@ ShadowSample SampleDirectionalLightShadowCascade(LightData light, Buffer result.TransmissionShadow = CalculateSubsurfaceOcclusion(opacity, shadowPosition.z, shadowMapDepth); result.TransmissionShadow = PostProcessShadow(shadow, result.TransmissionShadow); } -#else - result.TransmissionShadow = 1; #endif result.SurfaceShadow = PostProcessShadow(shadow, result.SurfaceShadow); From a1cb7dcbe797016aa0dd8648f2626b4681cfab2c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 20 Nov 2025 10:39:16 +0100 Subject: [PATCH 108/207] Fix GPU BVH shader compilation for macOS/iOS --- Content/Shaders/SDF.flax | 4 +- Source/Shaders/MeshAccelerationStructure.hlsl | 37 +++++++++---------- Source/Shaders/SDF.shader | 10 +---- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/Content/Shaders/SDF.flax b/Content/Shaders/SDF.flax index 5141c14dd..709cc20f2 100644 --- a/Content/Shaders/SDF.flax +++ b/Content/Shaders/SDF.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2b1dc1523cb2140db7ce5fed6e97b09d7fcebbe6cc19fca7708b5b882267040 -size 4175 +oid sha256:d3922811f0eb56cbb515c93cd53d80316740ea78219aa81118d2c9dee4a9d230 +size 4142 diff --git a/Source/Shaders/MeshAccelerationStructure.hlsl b/Source/Shaders/MeshAccelerationStructure.hlsl index d98d25404..0559e5f55 100644 --- a/Source/Shaders/MeshAccelerationStructure.hlsl +++ b/Source/Shaders/MeshAccelerationStructure.hlsl @@ -16,13 +16,10 @@ struct BVHNode int Count; // Negative for non-leaf nodes }; -struct BVHBuffers -{ - StructuredBuffer BVHBuffer; - ByteAddressBuffer VertexBuffer; - ByteAddressBuffer IndexBuffer; - uint VertexStride; -}; +// Pass all data via separate params (SPIR-V doesn't support buffers in structures) +#define BVHBuffers_Param StructuredBuffer BVHBuffer, ByteAddressBuffer VertexBuffer, ByteAddressBuffer IndexBuffer, uint VertexStride +#define BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride) BVHBuffer, VertexBuffer, IndexBuffer, VertexStride +#define BVHBuffers_Pass BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride) struct BVHHit { @@ -30,11 +27,11 @@ struct BVHHit bool IsBackface; }; -float3 LoadVertexBVH(BVHBuffers bvh, uint index) +float3 LoadVertexBVH(BVHBuffers_Param, uint index) { int addr = index << 2u; - uint vertexIndex = bvh.IndexBuffer.Load(addr); - return asfloat(bvh.VertexBuffer.Load3(vertexIndex * bvh.VertexStride)); + uint vertexIndex = IndexBuffer.Load(addr); + return asfloat(VertexBuffer.Load3(vertexIndex * VertexStride)); } // [https://tavianator.com/2011/ray_box.html] @@ -52,7 +49,7 @@ float RayTestBoxBVH(float3 rayPos, float3 rayDir, float3 boxMin, float3 boxMax) } // Performs raytracing against the BVH acceleration structure to find the closest intersection with a triangle. -bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, float maxDistance = 1000000.0f) +bool RayCastBVH(BVHBuffers_Param, float3 rayPos, float3 rayDir, out BVHHit hit, float maxDistance = 1000000.0f) { hit = (BVHHit)0; hit.Distance = maxDistance; @@ -66,7 +63,7 @@ bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, fl LOOP while (stackCount > 0) { - BVHNode node = bvh.BVHBuffer[stack[--stackCount]]; + BVHNode node = BVHBuffer[stack[--stackCount]]; // Raytrace bounds float boundsHit = RayTestBoxBVH(rayPos, rayDir, node.BoundsMin, node.BoundsMax); @@ -82,9 +79,9 @@ bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, fl for (uint i = indexStart; i < indexEnd;) { // Load triangle - float3 v0 = LoadVertexBVH(bvh, i++); - float3 v1 = LoadVertexBVH(bvh, i++); - float3 v2 = LoadVertexBVH(bvh, i++); + float3 v0 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v1 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v2 = LoadVertexBVH(BVHBuffers_Pass, i++); // Raytrace triangle float distance; @@ -109,7 +106,7 @@ bool RayCastBVH(BVHBuffers bvh, float3 rayPos, float3 rayDir, out BVHHit hit, fl } // Performs a query against the BVH acceleration structure to find the closest distance to a triangle from a given point. -bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance = 1000000.0f) +bool PointQueryBVH(BVHBuffers_Param, float3 pos, out BVHHit hit, float maxDistance = 1000000.0f) { hit = (BVHHit)0; hit.Distance = maxDistance; @@ -123,7 +120,7 @@ bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance LOOP while (stackCount > 0) { - BVHNode node = bvh.BVHBuffer[stack[--stackCount]]; + BVHNode node = BVHBuffer[stack[--stackCount]]; // Skip too far nodes if (PointDistanceBox(node.BoundsMin, node.BoundsMax, pos) >= hit.Distance) @@ -138,9 +135,9 @@ bool PointQueryBVH(BVHBuffers bvh, float3 pos, out BVHHit hit, float maxDistance for (uint i = indexStart; i < indexEnd;) { // Load triangle - float3 v0 = LoadVertexBVH(bvh, i++); - float3 v1 = LoadVertexBVH(bvh, i++); - float3 v2 = LoadVertexBVH(bvh, i++); + float3 v0 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v1 = LoadVertexBVH(BVHBuffers_Pass, i++); + float3 v2 = LoadVertexBVH(BVHBuffers_Pass, i++); // Check triangle float distance = sqrt(DistancePointToTriangle2(pos, v0, v1, v2)); diff --git a/Source/Shaders/SDF.shader b/Source/Shaders/SDF.shader index b2ae928f7..77e27d8f9 100644 --- a/Source/Shaders/SDF.shader +++ b/Source/Shaders/SDF.shader @@ -77,15 +77,9 @@ void CS_RasterizeTriangles(uint3 GroupId : SV_GroupID, uint3 GroupThreadID : SV_ int3 voxelCoord = GetVoxelCoord(voxelIndex); float3 voxelPos = GetVoxelPos(voxelCoord); - BVHBuffers bvh; - bvh.BVHBuffer = BVHBuffer; - bvh.VertexBuffer = VertexBuffer; - bvh.IndexBuffer = IndexBuffer; - bvh.VertexStride = VertexStride; - // Point query to find the distance to the closest surface BVHHit hit; - PointQueryBVH(bvh, voxelPos, hit, MaxDistance); + PointQueryBVH(BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride), voxelPos, hit, MaxDistance); float sdf = hit.Distance; // Raycast triangles around voxel to count triangle backfaces hit @@ -104,7 +98,7 @@ void CS_RasterizeTriangles(uint3 GroupId : SV_GroupID, uint3 GroupThreadID : SV_ for (uint i = 0; i < CLOSEST_CACHE_SIZE; i++) { float3 rayDir = closestDirections[i]; - if (RayCastBVH(bvh, voxelPos, rayDir, hit, MaxDistance)) + if (RayCastBVH(BVHBuffers_Init(BVHBuffer, VertexBuffer, IndexBuffer, VertexStride), voxelPos, rayDir, hit, MaxDistance)) { sdf = min(sdf, hit.Distance); if (hit.IsBackface) From fb07071e243bbf2556f6e3a67f968deec2957edc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 20 Nov 2025 10:44:47 +0100 Subject: [PATCH 109/207] Restore Global Surface Atlas and DDGI on Apple platforms #3797 --- Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 1c1b9e4e3..ce0ec1881 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -551,9 +551,6 @@ bool GlobalSurfaceAtlasPass::Init() // Check platform support const auto device = GPUDevice::Instance; _supported = device->GetFeatureLevel() >= FeatureLevel::SM5 && device->Limits.HasCompute && device->Limits.HasTypedUAVLoad; -#if PLATFORM_APPLE_FAMILY - _supported = false; // Vulkan over Metal has some issues in complex scenes with DDGI -#endif return false; } From caa902ea9b814fa5617050d1d56f7ce108bfdef0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 20 Nov 2025 13:07:28 +0100 Subject: [PATCH 110/207] Fix shader compilation without HLSL 2021 on Vulkan --- Source/Engine/Renderer/DepthOfFieldPass.h | 9 ++++--- Source/Shaders/Common.hlsl | 31 +++-------------------- 2 files changed, 9 insertions(+), 31 deletions(-) diff --git a/Source/Engine/Renderer/DepthOfFieldPass.h b/Source/Engine/Renderer/DepthOfFieldPass.h index b7351c4b1..098c4061e 100644 --- a/Source/Engine/Renderer/DepthOfFieldPass.h +++ b/Source/Engine/Renderer/DepthOfFieldPass.h @@ -52,9 +52,12 @@ private: void OnShaderReloading(Asset* obj) { _psDofDepthBlurGeneration->ReleaseGPU(); - _psBokehGeneration->ReleaseGPU(); - _psBokeh->ReleaseGPU(); - _psBokehComposite->ReleaseGPU(); + if (_psBokehGeneration) + _psBokehGeneration->ReleaseGPU(); + if (_psBokeh) + _psBokeh->ReleaseGPU(); + if (_psBokehComposite) + _psBokehComposite->ReleaseGPU(); invalidateResources(); } #endif diff --git a/Source/Shaders/Common.hlsl b/Source/Shaders/Common.hlsl index 10b2855f4..66303546a 100644 --- a/Source/Shaders/Common.hlsl +++ b/Source/Shaders/Common.hlsl @@ -96,34 +96,9 @@ // Compiler support for HLSL 2021 that is stricter (need to use or/and/select for vector-based logical operators) #if !defined(__DXC_VERSION_MAJOR) || (__DXC_VERSION_MAJOR <= 1 && __DXC_VERSION_MINOR < 7) -bool InternalAnd(bool a, bool b) { return bool(a && b); } -bool2 InternalAnd(bool2 a, bool2 b) { return bool2(a.x && b.x, a.y && b.y); } -bool3 InternalAnd(bool3 a, bool3 b) { return bool3(a.x && b.x, a.y && b.y, a.z && b.z); } -bool4 InternalAnd(bool4 a, bool4 b) { return bool4(a.x && b.x, a.y && b.y, a.z && b.z, a.w && b.w); } - -bool InternalOr(bool a, bool b) { return bool(a || b); } -bool2 InternalOr(bool2 a, bool2 b) { return bool2(a.x || b.x, a.y || b.y); } -bool3 InternalOr(bool3 a, bool3 b) { return bool3(a.x || b.x, a.y || b.y, a.z || b.z); } -bool4 InternalOr(bool4 a, bool4 b) { return bool4(a.x || b.x, a.y || b.y, a.z || b.z, a.w || b.w); } - -#define SELECT_INTERNAL(type) \ - type InternalSelect(bool c, type a, type b) { return type (c ? a.x : b.x); } \ - type##2 InternalSelect(bool c, type##2 a, type##2 b) { return type##2(c ? a.x : b.x, c ? a.y : b.y); } \ - type##2 InternalSelect(bool2 c, type a, type b) { return type##2(c.x ? a : b, c.y ? a : b); } \ - type##2 InternalSelect(bool2 c, type##2 a, type##2 b) { return type##2(c.x ? a.x : b.x, c.y ? a.y : b.y); } \ - type##3 InternalSelect(bool c, type##3 a, type##3 b) { return type##3(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z); } \ - type##3 InternalSelect(bool3 c, type a, type b) { return type##3(c.x ? a : b, c.y ? a : b, c.z ? a : b); } \ - type##3 InternalSelect(bool3 c, type##3 a, type##3 b) { return type##3(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z); } \ - type##4 InternalSelect(bool c, type##4 a, type##4 b) { return type##4(c ? a.x : b.x, c ? a.y : b.y, c ? a.z : b.z, c ? a.w : b.w); } \ - type##4 InternalSelect(bool4 c, type a, type b) { return type##4(c.x ? a : b, c.y ? a : b, c.z ? a : b, c.w ? a : b); } \ - type##4 InternalSelect(bool4 c, type##4 a, type##4 b) { return type##4(c.x ? a.x : b.x, c.y ? a.y : b.y, c.z ? a.z : b.z, c.w ? a.w : b.w); } -SELECT_INTERNAL(uint) -SELECT_INTERNAL(float) -#undef SELECT_INTERNAL - -#define and(a, b) InternalAnd(a, b) -#define or(a, b) InternalOr(a, b) -#define select(c, a, b) InternalSelect(c, a, b) +#define and(a, b) (a) && (b) +#define or(a, b) (a) || (b) +#define select(c, a, b) (c) ? (a) : (b) #endif From 2a55cda583d789a40435215101ae1daac3c0e566 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 20 Nov 2025 14:58:30 -0800 Subject: [PATCH 111/207] Add fallback location of engine managed library on AOT platforms --- Source/Engine/Scripting/Scripting.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 2f7b9f9d6..4e17bb80a 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -568,10 +568,14 @@ bool Scripting::Load() #endif // Load FlaxEngine - const String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); auto* flaxEngineModule = (NativeBinaryModule*)GetBinaryModuleFlaxEngine(); if (!flaxEngineModule->Assembly->IsLoaded()) { + String flaxEnginePath = Globals::BinariesFolder / TEXT("FlaxEngine.CSharp.dll"); +#if USE_MONO_AOT + if (!FileSystem::FileExists(flaxEnginePath)) + flaxEnginePath = Globals::BinariesFolder / TEXT("Dotnet") / TEXT("FlaxEngine.CSharp.dll"); +#endif if (flaxEngineModule->Assembly->Load(flaxEnginePath)) { LOG(Error, "Failed to load FlaxEngine C# assembly."); From f8dc8ab903a9f246240a08d79b8683bfd05ae153 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 23 Nov 2025 14:19:11 -0600 Subject: [PATCH 112/207] Fix not being able to replay same animation in animation slot. --- Source/Engine/Animations/Graph/AnimGroup.Animation.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index fd62ec537..fe0ad52e1 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -2441,10 +2441,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu { if (bucket.LoopsLeft == 0) { - // End playing animation + // End playing animation and reset bucket params value = tryGetValue(node->GetBox(1), Value::Null); bucket.Index = -1; slot.Animation = nullptr; + bucket.TimePosition = 0.0f; + bucket.BlendInPosition = 0.0f; + bucket.BlendOutPosition = 0.0f; + bucket.LoopsDone = 0; return; } From 2d56411e5f8f4a292b328eeac51b6c3d59fe04f5 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 23 Nov 2025 14:19:37 -0600 Subject: [PATCH 113/207] Add slot stop methods without anim param. --- Source/Engine/Level/Actors/AnimatedModel.cpp | 24 ++++++++++++++++++++ Source/Engine/Level/Actors/AnimatedModel.h | 12 ++++++++++ 2 files changed, 36 insertions(+) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index ee66c6c77..a8c091eb9 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -563,6 +563,20 @@ void AnimatedModel::StopSlotAnimation(const StringView& slotName, Animation* ani } } +void AnimatedModel::StopSlotAnimation(const StringView& slotName) +{ + for (auto& slot : GraphInstance.Slots) + { + if (slot.Name == slotName) + { + //slot.Animation = nullptr; // TODO: make an immediate version of this method and set the animation to nullptr. + if (slot.Animation != nullptr) + slot.Reset = true; + break; + } + } +} + void AnimatedModel::PauseSlotAnimation() { for (auto& slot : GraphInstance.Slots) @@ -601,6 +615,16 @@ bool AnimatedModel::IsPlayingSlotAnimation(const StringView& slotName, Animation return false; } +bool AnimatedModel::IsPlayingSlotAnimation(const StringView& slotName) +{ + for (auto& slot : GraphInstance.Slots) + { + if (slot.Name == slotName && !slot.Pause) + return true; + } + return false; +} + void AnimatedModel::ApplyRootMotion(const Transform& rootMotionDelta) { // Skip if no motion diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index a011be0d0..dc837c658 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -414,6 +414,12 @@ public: /// The name of the slot. /// The animation to stop. API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim); + + /// + /// Stops the animation playback on the slot in Anim Graph. + /// + /// The name of the slot. + API_FUNCTION() void StopSlotAnimation(const StringView& slotName); /// /// Pauses all the animations playback on the all slots in Anim Graph. @@ -439,6 +445,12 @@ public: /// The animation to check. API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim); + /// + /// Checks if the animation playback is active on the slot in Anim Graph (not paused). + /// + /// The name of the slot. + API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName); + private: void ApplyRootMotion(const Transform& rootMotionDelta); void SyncParameters(); From 92254eefcc265360266d6c34341d1630506b2cef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 25 Nov 2025 00:20:14 -0800 Subject: [PATCH 114/207] SImplify some code and update code for platforms --- Source/Engine/Engine/NativeInterop.cs | 9 +++-- .../Platform/Android/AndroidFileSystem.cpp | 20 +--------- .../Engine/Platform/Base/FileSystemBase.cpp | 37 ++++++++++++++++++ Source/Engine/Platform/Base/FileSystemBase.h | 2 +- .../Engine/Platform/Unix/UnixFileSystem.cpp | 39 +------------------ Source/Engine/Scripting/Runtime/DotNet.cpp | 6 +-- 6 files changed, 50 insertions(+), 63 deletions(-) diff --git a/Source/Engine/Engine/NativeInterop.cs b/Source/Engine/Engine/NativeInterop.cs index 209eb54e2..564c53801 100644 --- a/Source/Engine/Engine/NativeInterop.cs +++ b/Source/Engine/Engine/NativeInterop.cs @@ -79,10 +79,11 @@ namespace FlaxEngine.Interop NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), NativeLibraryImportResolver); // Change default culture to match with Mono runtime default culture - CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture; - CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.InvariantCulture; - System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; - System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture; + var culture = CultureInfo.InvariantCulture; + CultureInfo.DefaultThreadCurrentCulture = culture; + CultureInfo.DefaultThreadCurrentUICulture = culture; + System.Threading.Thread.CurrentThread.CurrentCulture = culture; + System.Threading.Thread.CurrentThread.CurrentUICulture = culture; } diff --git a/Source/Engine/Platform/Android/AndroidFileSystem.cpp b/Source/Engine/Platform/Android/AndroidFileSystem.cpp index 930b4017b..27fefb94e 100644 --- a/Source/Engine/Platform/Android/AndroidFileSystem.cpp +++ b/Source/Engine/Platform/Android/AndroidFileSystem.cpp @@ -422,24 +422,8 @@ bool AndroidFileSystem::getFilesFromDirectoryTop(Array& results, const c if (S_ISREG(statEntry.st_mode) != 0) { // Validate with filter - const int32 fullPathLength = StringUtils::Length(fullPath); - const int32 searchPatternLength = StringUtils::Length(searchPattern); - if (searchPatternLength == 0 || StringUtils::Compare(searchPattern, "*") == 0) - { - // All files - } - else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1) == 0) - { - // Path ending - } - else - { - // TODO: implement all cases in a generic way - continue; - } - - // Add file - results.Add(String(fullPath)); + if (FileSystem::PathFilterHelper(fullPath, searchPattern)) + results.Add(String(fullPath)); } } diff --git a/Source/Engine/Platform/Base/FileSystemBase.cpp b/Source/Engine/Platform/Base/FileSystemBase.cpp index 1ac5aa6ea..36b4182de 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.cpp +++ b/Source/Engine/Platform/Base/FileSystemBase.cpp @@ -7,6 +7,7 @@ #include "Engine/Core/Types/StringView.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Math/Math.h" +#include "Engine/Core/Log.h" #include "Engine/Engine/Globals.h" bool FileSystemBase::ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array& filenames) @@ -313,3 +314,39 @@ bool FileSystemBase::DirectoryCopyHelper(const String& dst, const String& src, b return false; } + +bool FileSystemBase::PathFilterHelper(const char* path, const char* searchPattern) +{ + // Validate with filter + const int32 pathLength = StringUtils::Length(path); + const int32 searchPatternLength = StringUtils::Length(searchPattern); + if (searchPatternLength == 0 || + StringUtils::Compare(searchPattern, "*") == 0 || + StringUtils::Compare(searchPattern, "*.*") == 0) + { + // All files + return true; + } + else if (searchPattern[0] == '*' && searchPatternLength < pathLength && StringUtils::Compare(path + pathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0) + { + // Path ending + return true; + } + else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength - 1] == '*') + { + // Contains pattern + bool match = false; + for (int32 i = 0; i < pathLength - searchPatternLength - 1; i++) + { + int32 len = Math::Min(searchPatternLength - 2, pathLength - i); + if (StringUtils::Compare(&path[i], &searchPattern[1], len) == 0) + return true; + } + } + else + { + // TODO: implement all cases in a generic way + LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented"); + } + return false; +} diff --git a/Source/Engine/Platform/Base/FileSystemBase.h b/Source/Engine/Platform/Base/FileSystemBase.h index 844d5fa5f..1a4980d95 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.h +++ b/Source/Engine/Platform/Base/FileSystemBase.h @@ -284,6 +284,6 @@ public: /// Relative path static String ConvertAbsolutePathToRelative(const String& basePath, const String& path); -private: static bool DirectoryCopyHelper(const String& dst, const String& src, bool withSubDirectories); + static bool PathFilterHelper(const char* path, const char* searchPattern); }; diff --git a/Source/Engine/Platform/Unix/UnixFileSystem.cpp b/Source/Engine/Platform/Unix/UnixFileSystem.cpp index 528df5b88..d85f60563 100644 --- a/Source/Engine/Platform/Unix/UnixFileSystem.cpp +++ b/Source/Engine/Platform/Unix/UnixFileSystem.cpp @@ -367,43 +367,8 @@ bool UnixFileSystem::getFilesFromDirectoryTop(Array& results, const char if (S_ISREG(statEntry.st_mode) != 0) { // Validate with filter - const int32 fullPathLength = StringUtils::Length(fullPath); - const int32 searchPatternLength = StringUtils::Length(searchPattern); - if (searchPatternLength == 0 || - StringUtils::Compare(searchPattern, "*") == 0 || - StringUtils::Compare(searchPattern, "*.*") == 0) - { - // All files - } - else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0) - { - // Path ending - } - else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength-1] == '*') - { - // Contains pattern - bool match = false; - for (int32 i = 0; i < pathLength - searchPatternLength - 1; i++) - { - int32 len = Math::Min(searchPatternLength - 2, pathLength - i); - if (StringUtils::Compare(&entry->d_name[i], &searchPattern[1], len) == 0) - { - match = true; - break; - } - } - if (!match) - continue; - } - else - { - // TODO: implement all cases in a generic way - LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented"); - continue; - } - - // Add file - results.Add(String(fullPath)); + if (FileSystem::PathFilterHelper(fullPath, searchPattern)) + results.Add(String(fullPath)); } } diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index d8e5acaff..2e98094b2 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -2155,9 +2155,9 @@ bool InitHostfxr() #endif // Platform-specific setup -#if PLATFORM_IOS || PLATFORM_SWITCH - setenv("MONO_AOT_MODE", "aot", 1); - setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1", 1); +#if PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5 + Platform::SetEnvironmentVariable(TEXT("MONO_AOT_MODE"), TEXT("aot")); + Platform::SetEnvironmentVariable(TEXT("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT"), TEXT("1")); #endif #ifdef USE_MONO_AOT_MODULE From a62ca5452eeb1fb01e15d23cb5216e733e6c7e6c Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 25 Nov 2025 17:33:11 +0100 Subject: [PATCH 115/207] Fixed missing move semantics in script object reference --- .../Scripting/ScriptingObjectReference.h | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Source/Engine/Scripting/ScriptingObjectReference.h b/Source/Engine/Scripting/ScriptingObjectReference.h index a71d22eae..58fed7668 100644 --- a/Source/Engine/Scripting/ScriptingObjectReference.h +++ b/Source/Engine/Scripting/ScriptingObjectReference.h @@ -33,6 +33,13 @@ public: { } + ScriptingObjectReferenceBase(ScriptingObjectReferenceBase&& other) noexcept + : _object(nullptr) + { + OnSet(other._object); + other.OnSet(nullptr); + } + /// /// Initializes a new instance of the class. /// @@ -96,6 +103,16 @@ protected: void OnSet(ScriptingObject* object); void OnDeleted(ScriptingObject* obj); + + ScriptingObjectReferenceBase& operator=(ScriptingObjectReferenceBase&& other) noexcept + { + if (this != &other) + { + OnSet(other._object); + other.OnSet(nullptr); + } + return *this; + } }; /// @@ -133,6 +150,11 @@ public: { } + ScriptingObjectReference(ScriptingObjectReference&& other) noexcept + : ScriptingObjectReferenceBase(MoveTemp(other)) + { + } + /// /// Finalizes an instance of the class. /// @@ -173,6 +195,12 @@ public: return *this; } + ScriptingObjectReference& operator=(ScriptingObjectReference&& other) noexcept + { + ScriptingObjectReferenceBase::operator=(MoveTemp(other)); + return *this; + } + FORCE_INLINE ScriptingObjectReference& operator=(const Guid& id) { OnSet(static_cast(FindObject(id, T::GetStaticClass()))); From 465f30661f5752426212cb5ef6150dd306b0a20f Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 25 Nov 2025 17:33:54 +0100 Subject: [PATCH 116/207] Minor memory layout optimization --- .../Engine/Networking/NetworkReplicator.cpp | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index c584d3526..bcca584b8 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -116,17 +116,6 @@ PACK_STRUCT(struct NetworkMessageObjectRpc struct NetworkReplicatedObject { - ScriptingObjectReference Object; - Guid ObjectId; - Guid ParentId; - uint32 OwnerClientId; - uint32 LastOwnerFrame = 0; - NetworkObjectRole Role; - uint8 Spawned : 1; - uint8 Synced : 1; - DataContainer TargetClientIds; - INetworkObject* AsNetworkObject; - struct { NetworkClientsMask Mask; @@ -139,6 +128,17 @@ struct NetworkReplicatedObject } } RepCache; + ScriptingObjectReference Object; + Guid ObjectId; + Guid ParentId; + DataContainer TargetClientIds; + INetworkObject* AsNetworkObject; + uint32 OwnerClientId; + uint32 LastOwnerFrame = 0; + NetworkObjectRole Role; + uint8 Spawned : 1; + uint8 Synced : 1; + NetworkReplicatedObject() { Spawned = 0; From d9a18b1d31ab76070d5a2ba7a54f7c89a37db4e8 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 25 Nov 2025 21:20:20 +0100 Subject: [PATCH 117/207] Fixed HashSet compact rehash under heavy collisions - Compact now iterates over the old bucket array using the saved oldSize, and frees with that size, avoiding out-of-bounds when _size changes. - If reinsertion finds no free slot during compaction (pathological collisions), the table grows once and retries, preventing AVs. - This fix addresses problems with weak hash keys (like #3824). --- Source/Engine/Core/Collections/HashSetBase.h | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Core/Collections/HashSetBase.h b/Source/Engine/Core/Collections/HashSetBase.h index 36fcf275d..96adabf79 100644 --- a/Source/Engine/Core/Collections/HashSetBase.h +++ b/Source/Engine/Core/Collections/HashSetBase.h @@ -409,26 +409,33 @@ protected: else { // Rebuild entire table completely + const int32 oldSize = _size; AllocationData oldAllocation; - AllocationUtils::MoveToEmpty(oldAllocation, _allocation, _size, _size); + AllocationUtils::MoveToEmpty(oldAllocation, _allocation, oldSize, oldSize); _allocation.Allocate(_size); BucketType* data = _allocation.Get(); for (int32 i = 0; i < _size; ++i) data[i]._state = HashSetBucketState::Empty; BucketType* oldData = oldAllocation.Get(); FindPositionResult pos; - for (int32 i = 0; i < _size; ++i) + for (int32 i = 0; i < oldSize; ++i) { BucketType& oldBucket = oldData[i]; if (oldBucket.IsOccupied()) { FindPosition(oldBucket.GetKey(), pos); + if (pos.FreeSlotIndex == -1) + { + // Grow and retry to handle pathological cases (eg. heavy collisions) + EnsureCapacity(_size + 1, true); + FindPosition(oldBucket.GetKey(), pos); + } ASSERT(pos.FreeSlotIndex != -1); BucketType& bucket = _allocation.Get()[pos.FreeSlotIndex]; bucket = MoveTemp(oldBucket); } } - for (int32 i = 0; i < _size; ++i) + for (int32 i = 0; i < oldSize; ++i) oldData[i].Free(); } _deletedCount = 0; From 1bf6612002c75cd3188804a80ef9d60e53eb3d5e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 25 Nov 2025 17:26:57 -0600 Subject: [PATCH 118/207] Fix exception thrown when reloading open windows. --- Source/Editor/Modules/WindowsModule.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Modules/WindowsModule.cs b/Source/Editor/Modules/WindowsModule.cs index 5c9c613d2..218394a3b 100644 --- a/Source/Editor/Modules/WindowsModule.cs +++ b/Source/Editor/Modules/WindowsModule.cs @@ -896,9 +896,11 @@ namespace FlaxEditor.Modules if (type.IsAssignableTo(typeof(AssetEditorWindow))) { - var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) }); var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID); + var assetType = assetItem.GetType(); + var ctor = type.GetConstructor(new Type[] { typeof(Editor), assetType }); var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem }); + win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue); if (winData.DockState == DockState.Float) { From bea75f51bd85b54cfc3cd779bc1cbe52c03a2de5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 00:02:40 -0800 Subject: [PATCH 119/207] Fix AOT libs cooking to avoid file dirtying for more accurate iterative cooking --- .../Cooker/Steps/CompileScriptsStep.cpp | 5 ++- .../Cooker/Steps/PrecompileAssembliesStep.cpp | 1 + .../Flax.Build/Build/DotNet/DotNetAOT.cs | 2 +- Source/Tools/Flax.Build/Build/EngineModule.cs | 3 +- .../Tools/Flax.Build/Utilities/Utilities.cs | 44 +++++++++++++++++-- 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp index c0a8e452a..1fdb1e7c0 100644 --- a/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp +++ b/Source/Editor/Cooker/Steps/CompileScriptsStep.cpp @@ -10,9 +10,10 @@ #include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/JsonWriters.h" #include "Editor/Cooker/PlatformTools.h" +#include "Engine/Engine/Globals.h" #include "Editor/Editor.h" #include "Editor/ProjectInfo.h" -#include "Engine/Engine/Globals.h" +#include "Editor/Utilities/EditorUtilities.h" #if PLATFORM_MAC #include #endif @@ -127,7 +128,7 @@ bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, c const String dst = dstPath / StringUtils::GetFileName(file); if (dst == file) continue; - if (FileSystem::CopyFile(dst, file)) + if (EditorUtilities::CopyFileIfNewer(dst, file)) { data.Error(String::Format(TEXT("Failed to copy file from {0} to {1}."), file, dst)); return true; diff --git a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp index 472a3cca6..626b532a6 100644 --- a/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp +++ b/Source/Editor/Cooker/Steps/PrecompileAssembliesStep.cpp @@ -59,6 +59,7 @@ bool PrecompileAssembliesStep::Perform(CookingData& data) data.StepProgress(infoMsg, 0); // Override Newtonsoft.Json with AOT-version (one that doesn't use System.Reflection.Emit) + // TODO: remove it since EngineModule does properly reference AOT lib now EditorUtilities::CopyFileIfNewer(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.dll"), Globals::StartupFolder / TEXT("Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll")); FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.xml")); FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.pdb")); diff --git a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs index e421b6999..96551a91d 100644 --- a/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs +++ b/Source/Tools/Flax.Build/Build/DotNet/DotNetAOT.cs @@ -462,7 +462,7 @@ namespace Flax.Build else { // Copy to the destination folder - Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName)); + Utilities.FileCopy(assemblyPath, Path.Combine(dotnetOutputPath, assemblyFileName), Utilities.CopyMode.OverrideIfNewer); } }; if (Configuration.MaxConcurrency > 1 && Configuration.ConcurrencyProcessorScale > 0.0f && !dotnetAotDebug) diff --git a/Source/Tools/Flax.Build/Build/EngineModule.cs b/Source/Tools/Flax.Build/Build/EngineModule.cs index 679a558d2..5fc470665 100644 --- a/Source/Tools/Flax.Build/Build/EngineModule.cs +++ b/Source/Tools/Flax.Build/Build/EngineModule.cs @@ -42,7 +42,8 @@ namespace Flax.Build BinaryModuleName = "FlaxEngine"; options.ScriptingAPI.Defines.Add("FLAX"); options.ScriptingAPI.Defines.Add("FLAX_ASSERTIONS"); - options.ScriptingAPI.FileReferences.Add(Utilities.RemovePathRelativeParts(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", "Newtonsoft.Json.dll"))); + var newtonsoftJsonPath = options.Platform?.HasDynamicCodeExecutionSupport ?? true ? "Newtonsoft.Json.dll" : "AOT/Newtonsoft.Json.dll"; + options.ScriptingAPI.FileReferences.Add(Utilities.RemovePathRelativeParts(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "DotNet", newtonsoftJsonPath))); options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter"); } } diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index 0ce5c8b7b..917b8aa77 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -144,24 +144,51 @@ namespace Flax.Build return new TwoWayEnumerator(source.GetEnumerator()); } + /// + /// File copy modes. + /// + public enum CopyMode + { + /// + /// Copies the file to the destination, fails if it already exists. + /// + New, + + /// + /// If destination file exists, it will be overriden. + /// + OverrideIfExists, + + /// + /// If destination file exists, has the same size and is newer than source file, it won't be overriden (avoids unnecessary copies). + /// + OverrideIfNewer, + } + /// /// Copies the file. /// /// The source file path. /// The destination file path. - /// if the destination file can be overwritten; otherwise, . - public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite = true) + /// Copy operation modes. + public static void FileCopy(string srcFilePath, string dstFilePath, CopyMode mode = CopyMode.OverrideIfExists) { if (string.IsNullOrEmpty(srcFilePath)) throw new ArgumentNullException(nameof(srcFilePath)); if (string.IsNullOrEmpty(dstFilePath)) throw new ArgumentNullException(nameof(dstFilePath)); + if (mode == CopyMode.OverrideIfNewer && + File.Exists(dstFilePath) && + File.GetLastWriteTime(srcFilePath) <= File.GetLastWriteTime(dstFilePath) && + new FileInfo(dstFilePath).Length == new FileInfo(srcFilePath).Length) + return; + Log.Verbose(srcFilePath + " -> " + dstFilePath); try { - File.Copy(srcFilePath, dstFilePath, overwrite); + File.Copy(srcFilePath, dstFilePath, mode != CopyMode.New); } catch (Exception ex) { @@ -173,6 +200,17 @@ namespace Flax.Build } } + /// + /// Copies the file. + /// + /// The source file path. + /// The destination file path. + /// if the destination file can be overwritten; otherwise, . + public static void FileCopy(string srcFilePath, string dstFilePath, bool overwrite) + { + FileCopy(srcFilePath, dstFilePath, overwrite ? CopyMode.OverrideIfExists : CopyMode.New); + } + /// /// Copies the directories. /// From cf048c98042a85323ab75a7bac8e858f368093d3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 00:07:00 -0800 Subject: [PATCH 120/207] Fix path filter query warning --- Source/Engine/Platform/Base/FileSystemBase.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Platform/Base/FileSystemBase.cpp b/Source/Engine/Platform/Base/FileSystemBase.cpp index 36b4182de..f414bbd01 100644 --- a/Source/Engine/Platform/Base/FileSystemBase.cpp +++ b/Source/Engine/Platform/Base/FileSystemBase.cpp @@ -327,10 +327,10 @@ bool FileSystemBase::PathFilterHelper(const char* path, const char* searchPatter // All files return true; } - else if (searchPattern[0] == '*' && searchPatternLength < pathLength && StringUtils::Compare(path + pathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0) + else if (searchPattern[0] == '*' && StringUtils::Find(searchPattern + 1, "*") == nullptr) { // Path ending - return true; + return searchPatternLength < pathLength && StringUtils::Compare(path + pathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0; } else if (searchPattern[0] == '*' && searchPatternLength > 2 && searchPattern[searchPatternLength - 1] == '*') { @@ -346,7 +346,7 @@ bool FileSystemBase::PathFilterHelper(const char* path, const char* searchPatter else { // TODO: implement all cases in a generic way - LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented"); + LOG(Warning, "DirectoryGetFiles: Wildcard filter is not implemented ({})", String(searchPattern)); } return false; } From c8839b8587f01b8451d7f990caaa294a2fa6f2dd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 00:22:48 -0800 Subject: [PATCH 121/207] Add support for Cooperative Suspend when running on Mono Informs mono runtime that Job System, Thread Pool or Content Load threads can wait when they are going idle between tasks. --- Source/Engine/Content/Content.cpp | 5 ++ .../Engine/Scripting/Internal/InternalCalls.h | 48 ++++++++++++++++++- Source/Engine/Scripting/Runtime/DotNet.cpp | 6 +++ Source/Engine/Threading/JobSystem.cpp | 5 ++ Source/Engine/Threading/ThreadPool.cpp | 5 ++ 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index 66e18a231..4a9ea500b 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -30,6 +30,7 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Scripting/ManagedCLR/MClass.h" +#include "Engine/Scripting/Internal/InternalCalls.h" #include "Engine/Scripting/Scripting.h" #if USE_EDITOR #include "Editor/Editor.h" @@ -346,17 +347,21 @@ int32 LoadingThread::Run() ContentLoadTask* task; ThisLoadThread = this; + MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr; while (Platform::AtomicRead(&_exitFlag) == 0) { if (LoadTasks.try_dequeue(task)) { Run(task); + MONO_THREAD_INFO_GET(monoThreadInfo); } else { + MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo); LoadTasksMutex.Lock(); LoadTasksSignal.Wait(LoadTasksMutex); LoadTasksMutex.Unlock(); + MONO_EXIT_GC_SAFE_WITH_INFO; } } diff --git a/Source/Engine/Scripting/Internal/InternalCalls.h b/Source/Engine/Scripting/Internal/InternalCalls.h index b72993234..81d86ea34 100644 --- a/Source/Engine/Scripting/Internal/InternalCalls.h +++ b/Source/Engine/Scripting/Internal/InternalCalls.h @@ -9,7 +9,7 @@ #if defined(__clang__) // Helper utility to override vtable entry with automatic restore -// See BindingsGenerator.Cpp.cs that generates virtuall method wrappers for scripting to properly call overriden base method +// See BindingsGenerator.Cpp.cs that generates virtual method wrappers for scripting to properly call overriden base method struct FLAXENGINE_API VTableFunctionInjector { void** VTableAddr; @@ -100,3 +100,49 @@ T& InternalGetReference(T* obj) DebugLog::ThrowNullReference(); return *obj; } + +#ifdef USE_MONO_AOT_COOP + +// Cooperative Suspend - where threads suspend themselves when the runtime requests it. +// https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/ +typedef struct _MonoStackData { + void* stackpointer; + const char* function_name; +} MonoStackData; +#if BUILD_DEBUG +#define MONO_STACKDATA(x) MonoStackData x = { &x, __func__ } +#else +#define MONO_STACKDATA(x) MonoStackData x = { &x, NULL } +#endif +#define MONO_THREAD_INFO_TYPE struct MonoThreadInfo +DLLIMPORT extern "C" MONO_THREAD_INFO_TYPE* mono_thread_info_attach(void); +DLLIMPORT extern "C" void* mono_threads_enter_gc_safe_region_with_info(MONO_THREAD_INFO_TYPE* info, MonoStackData* stackdata); +DLLIMPORT extern "C" void mono_threads_exit_gc_safe_region_internal(void* cookie, MonoStackData* stackdata); +#ifndef _MONO_UTILS_FORWARD_ +typedef struct _MonoDomain MonoDomain; +DLLIMPORT extern "C" MonoDomain* mono_domain_get(void); +#endif +#define MONO_ENTER_GC_SAFE \ + do { \ + MONO_STACKDATA(__gc_safe_dummy); \ + void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_internal(&__gc_safe_dummy) +#define MONO_EXIT_GC_SAFE \ + mono_threads_exit_gc_safe_region_internal(__gc_safe_cookie, &__gc_safe_dummy); \ + } while (0) +#define MONO_ENTER_GC_SAFE_WITH_INFO(info) \ + do { \ + MONO_STACKDATA(__gc_safe_dummy); \ + void* __gc_safe_cookie = mono_threads_enter_gc_safe_region_with_info((info), &__gc_safe_dummy) +#define MONO_EXIT_GC_SAFE_WITH_INFO MONO_EXIT_GC_SAFE +#define MONO_THREAD_INFO_GET(info) if (!info && mono_domain_get()) info = mono_thread_info_attach() + +#else + +#define MONO_ENTER_GC_SAFE +#define MONO_EXIT_GC_SAFE +#define MONO_ENTER_GC_SAFE_WITH_INFO(info) +#define MONO_EXIT_GC_SAFE_WITH_INFO +#define MONO_THREAD_INFO_GET(info) +#define mono_thread_info_attach() nullptr + +#endif diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 2e98094b2..ed662801e 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -2147,7 +2147,13 @@ bool InitHostfxr() #endif // Adjust GC threads suspending mode to not block attached native threads (eg. Job System) + // https://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/ +#if USE_MONO_AOT_COOP + Platform::SetEnvironmentVariable(TEXT("MONO_THREADS_SUSPEND"), TEXT("coop")); + Platform::SetEnvironmentVariable(TEXT("MONO_SLEEP_ABORT_LIMIT"), TEXT("5000")); // in ms +#else Platform::SetEnvironmentVariable(TEXT("MONO_THREADS_SUSPEND"), TEXT("preemptive")); +#endif #if defined(USE_MONO_AOT_MODE) // Enable AOT mode (per-platform) diff --git a/Source/Engine/Threading/JobSystem.cpp b/Source/Engine/Threading/JobSystem.cpp index e90a2e847..692a088b7 100644 --- a/Source/Engine/Threading/JobSystem.cpp +++ b/Source/Engine/Threading/JobSystem.cpp @@ -15,6 +15,7 @@ #include "Engine/Profiler/ProfilerMemory.h" #if USE_CSHARP #include "Engine/Scripting/ManagedCLR/MCore.h" +#include "Engine/Scripting/Internal/InternalCalls.h" #endif #define JOB_SYSTEM_ENABLED 1 @@ -184,6 +185,7 @@ int32 JobSystemThread::Run() JobData data; Function job; bool attachCSharpThread = true; + MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr; while (Platform::AtomicRead(&ExitFlag) == 0) { // Try to get a job @@ -205,6 +207,7 @@ int32 JobSystemThread::Run() { MCore::Thread::Attach(); attachCSharpThread = false; + monoThreadInfo = mono_thread_info_attach(); } #endif @@ -244,9 +247,11 @@ int32 JobSystemThread::Run() else { // Wait for signal + MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo); JobsMutex.Lock(); JobsSignal.Wait(JobsMutex); JobsMutex.Unlock(); + MONO_EXIT_GC_SAFE_WITH_INFO; } } return 0; diff --git a/Source/Engine/Threading/ThreadPool.cpp b/Source/Engine/Threading/ThreadPool.cpp index 4943469a5..d7410d0fc 100644 --- a/Source/Engine/Threading/ThreadPool.cpp +++ b/Source/Engine/Threading/ThreadPool.cpp @@ -15,6 +15,7 @@ #include "Engine/Platform/CPUInfo.h" #include "Engine/Platform/Thread.h" #include "Engine/Profiler/ProfilerMemory.h" +#include "Engine/Scripting/Internal/InternalCalls.h" FLAXENGINE_API bool IsInMainThread() { @@ -117,6 +118,7 @@ int32 ThreadPool::ThreadProc() Platform::SetThreadAffinityMask(THREAD_POOL_AFFINITY_MASK((int32)index)); #endif ThreadPoolTask* task; + MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr; // Work until end while (Platform::AtomicRead(&ThreadPoolImpl::ExitFlag) == 0) @@ -125,12 +127,15 @@ int32 ThreadPool::ThreadProc() if (ThreadPoolImpl::Jobs.try_dequeue(task)) { task->Execute(); + MONO_THREAD_INFO_GET(monoThreadInfo); } else { + MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo); ThreadPoolImpl::JobsMutex.Lock(); ThreadPoolImpl::JobsSignal.Wait(ThreadPoolImpl::JobsMutex); ThreadPoolImpl::JobsMutex.Unlock(); + MONO_EXIT_GC_SAFE_WITH_INFO; } } From 403d2cedc0a8665328a7efeb3817f6544b642978 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 06:28:54 -0800 Subject: [PATCH 122/207] Updates to engine for porting to blue platform --- Source/Engine/Scripting/Scripting.cs | 4 +- Source/Tools/Flax.Build/Build/EngineTarget.cs | 2 +- .../Platforms/Unix/UnixToolchain.cs | 46 +++++++------------ 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index b308f5dae..7f9f2980c 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -198,7 +198,7 @@ namespace FlaxEngine private static void OnLocalizationChanged() { // Invariant-globalization only (see InitHostfxr with Mono) -#if !(PLATFORM_IOS || PLATFORM_SWITCH) +#if !(PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5) var currentThread = Thread.CurrentThread; var language = Localization.CurrentLanguage; if (language != null) @@ -261,7 +261,7 @@ namespace FlaxEngine internal static ManagedHandle CultureInfoToManaged(int lcid) { -#if PLATFORM_IOS || PLATFORM_SWITCH +#if PLATFORM_IOS || PLATFORM_SWITCH || PLATFORM_PS4 || PLATFORM_PS5 // Invariant-globalization only (see InitHostfxr with Mono) lcid = 0; #endif diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index 827205e91..039f9fb16 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -142,7 +142,7 @@ namespace Flax.Build } /// - /// Returns true if this build target should use separate (aka main-only) executable file and separate runtime (in shared library). Used on platforms that don't support linking again executable file but only shared library (see HasExecutableFileReferenceSupport). + /// Returns true if this build target should use separate (aka main-only) executable file and separate runtime (in shared library). Used on platforms that don't support linking (or symbol lookup) against executable file but only shared library (see HasExecutableFileReferenceSupport). /// public virtual bool UseSeparateMainExecutable(BuildOptions buildOptions) { diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index 6d5aacdde..fcb4397a1 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -320,6 +320,17 @@ namespace Flax.Build.Platforms { } + /// + /// Gets linker argument to reference a specific shared library file. + /// + /// The graph. + /// The options. + /// The shared library file path. + protected virtual string GetSharedLibraryLinkArg(TaskGraph graph, BuildOptions options, string library) + { + return string.Format("\"-l{0}\"", GetLibName(library)); + } + /// public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List sourceFiles, string outputPath) { @@ -527,7 +538,8 @@ namespace Flax.Build.Platforms // Input libraries var libraryPaths = new HashSet(); - foreach (var library in linkEnvironment.InputLibraries) + var dynamicLibExt = Platform.SharedLibraryFileExtension; + foreach (var library in linkEnvironment.InputLibraries.Concat(options.Libraries)) { var dir = Path.GetDirectoryName(library); var ext = Path.GetExtension(library); @@ -539,37 +551,12 @@ namespace Flax.Build.Platforms { // Skip executable } - else if (ext == ".so") + else if (ext == dynamicLibExt) { // Link against dynamic library task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); - args.Add(string.Format("\"-l{0}\"", GetLibName(library))); - } - else - { - task.PrerequisiteFiles.Add(library); - args.Add(string.Format("\"{0}\"", GetLibName(library))); - } - } - foreach (var library in options.Libraries) - { - var dir = Path.GetDirectoryName(library); - var ext = Path.GetExtension(library); - if (string.IsNullOrEmpty(dir)) - { - args.Add(string.Format("\"-l{0}\"", library)); - } - else if (string.IsNullOrEmpty(ext)) - { - // Skip executable - } - else if (ext == ".so") - { - // Link against dynamic library - task.PrerequisiteFiles.Add(library); - libraryPaths.Add(dir); - args.Add(string.Format("\"-l{0}\"", GetLibName(library))); + args.Add(GetSharedLibraryLinkArg(graph, options, library)); } else { @@ -580,8 +567,7 @@ namespace Flax.Build.Platforms // Input files (link static libraries last) task.PrerequisiteFiles.AddRange(linkEnvironment.InputFiles); - foreach (var file in linkEnvironment.InputFiles.Where(x => !x.EndsWith(".a")) - .Concat(linkEnvironment.InputFiles.Where(x => x.EndsWith(".a")))) + foreach (var file in linkEnvironment.InputFiles.Where(x => !x.EndsWith(".a")).Concat(linkEnvironment.InputFiles.Where(x => x.EndsWith(".a")))) { args.Add(string.Format("\"{0}\"", file.Replace('\\', '/'))); } From 0007185b5fc9515b5a8222dc2c7efaeec876862f Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Wed, 26 Nov 2025 17:46:41 +0100 Subject: [PATCH 123/207] Fixed late-join network replication - Adjusted replication to resend unchanged state only to missing clients. - Skip server serialization when no recipients, and downgrade unknown-despawn noise. --- .../Engine/Networking/NetworkReplicator.cpp | 80 +++++++++++++++---- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index c584d3526..5e2979db0 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -698,14 +698,11 @@ void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients) return; auto& item = it->Item; const bool isClient = NetworkManager::IsClient(); + const NetworkClientsMask fullTargetClients = targetClients; - // Skip serialization of objects that none will receive - if (!isClient) - { - BuildCachedTargets(item, targetClients); - if (CachedTargets.Count() == 0) - return; - } + // If server has no recipients, skip early. + if (!isClient && !targetClients) + return; if (item.AsNetworkObject) item.AsNetworkObject->OnNetworkSerialize(); @@ -727,19 +724,33 @@ void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients) } #if USE_NETWORK_REPLICATOR_CACHE + // Check if only newly joined clients are missing this data to avoid resending it to everyone + NetworkClientsMask missingClients; + missingClients.Word0 = targetClients.Word0 & ~item.RepCache.Mask.Word0; + missingClients.Word1 = targetClients.Word1 & ~item.RepCache.Mask.Word1; + // Process replication cache to skip sending object data if it didn't change - if (item.RepCache.Data.Length() == size && - item.RepCache.Mask == targetClients && - Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0) + if (item.RepCache.Data.Length() == size && Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0) { - return; + // If data is the same and only the client set changed, replicate to missing clients only + if (!missingClients) + return; + targetClients = missingClients; } - item.RepCache.Mask = targetClients; + item.RepCache.Mask = fullTargetClients; item.RepCache.Data.Copy(stream->GetBuffer(), size); #endif // TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state) constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable; + // Skip serialization of objects that none will receive + if (!isClient) + { + BuildCachedTargets(item, targetClients); + if (CachedTargets.Count() == 0) + return; + } + // Send object to clients NetworkMessageObjectReplicate msgData; msgData.OwnerFrame = NetworkManager::Frame; @@ -1530,7 +1541,21 @@ void NetworkReplicator::DespawnObject(ScriptingObject* obj) // Register for despawning (batched during update) auto& despawn = DespawnQueue.AddOne(); despawn.Id = obj->GetID(); - despawn.Targets = item.TargetClientIds; + if (item.TargetClientIds.IsValid()) + { + despawn.Targets = item.TargetClientIds; + } + else + { + // Snapshot current recipients to avoid sending despawn to clients that connect later (and never got the spawn) + Array> clientIds; + for (const NetworkClient* client : NetworkManager::Clients) + { + if (client->State == NetworkConnectionState::Connected && client->ClientId != item.OwnerClientId) + clientIds.Add(client->ClientId); + } + despawn.Targets.Copy(clientIds); + } // Prevent spawning for (int32 i = 0; i < SpawnQueue.Count(); i++) @@ -1823,6 +1848,31 @@ void NetworkInternal::NetworkReplicatorClientConnected(NetworkClient* client) { ScopeLock lock(ObjectsLock); NewClients.Add(client); + + // Ensure cached replication acknowledges the new client without resending to others. + // Clear the new client's bit in RepCache and schedule a near-term replication. + const int32 clientIndex = NetworkManager::Clients.Find(client); + if (clientIndex != -1) + { + const uint64 bitMask = 1ull << (uint64)(clientIndex % 64); + const int32 wordIndex = clientIndex / 64; + for (auto it = Objects.Begin(); it.IsNotEnd(); ++it) + { + auto& item = it->Item; + ScriptingObject* obj = item.Object.Get(); + if (!obj || !item.Spawned || item.Role != NetworkObjectRole::OwnedAuthoritative) + continue; + + // Mark this client as missing cached data + uint64* word = wordIndex == 0 ? &item.RepCache.Mask.Word0 : &item.RepCache.Mask.Word1; + *word &= ~bitMask; + + // Force next replication tick for this object so the new client gets data promptly + if (Hierarchy) + Hierarchy->DirtyObject(obj); + } + } + ASSERT(sizeof(NetworkClientsMask) * 8 >= (uint32)NetworkManager::Clients.Count()); // Ensure that clients mask can hold all of clients } @@ -2275,7 +2325,9 @@ void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, Network } else { - NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Failed to despawn object {}", objectId); + // If this client never had the object (eg. it was targeted to other clients only), drop the message quietly. + DespawnedObjects.Add(objectId); + NETWORK_REPLICATOR_LOG(Warning, "[NetworkReplicator] Failed to despawn object {}", objectId); } } From 1e3ce48024efbaba333caa92451762d5b3f2a1a9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 Nov 2025 23:43:20 -0800 Subject: [PATCH 124/207] Fix compilation regression --- Source/Engine/Scripting/Internal/InternalCalls.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Scripting/Internal/InternalCalls.h b/Source/Engine/Scripting/Internal/InternalCalls.h index 81d86ea34..4535952ea 100644 --- a/Source/Engine/Scripting/Internal/InternalCalls.h +++ b/Source/Engine/Scripting/Internal/InternalCalls.h @@ -138,6 +138,7 @@ DLLIMPORT extern "C" MonoDomain* mono_domain_get(void); #else +#define MONO_THREAD_INFO_TYPE void #define MONO_ENTER_GC_SAFE #define MONO_EXIT_GC_SAFE #define MONO_ENTER_GC_SAFE_WITH_INFO(info) From a1999183f21855aa591b377607b6fac5596f32f2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 27 Nov 2025 09:13:14 +0100 Subject: [PATCH 125/207] Fix compilation regression --- Source/Engine/Platform/Unix/UnixFileSystem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Unix/UnixFileSystem.cpp b/Source/Engine/Platform/Unix/UnixFileSystem.cpp index d85f60563..df854c138 100644 --- a/Source/Engine/Platform/Unix/UnixFileSystem.cpp +++ b/Source/Engine/Platform/Unix/UnixFileSystem.cpp @@ -367,7 +367,7 @@ bool UnixFileSystem::getFilesFromDirectoryTop(Array& results, const char if (S_ISREG(statEntry.st_mode) != 0) { // Validate with filter - if (FileSystem::PathFilterHelper(fullPath, searchPattern)) + if (FileSystemBase::PathFilterHelper(fullPath, searchPattern)) results.Add(String(fullPath)); } } From 64cd898a659c103c76e964b0f596eeb6cdc1ff3a Mon Sep 17 00:00:00 2001 From: Alex Ray Date: Thu, 27 Nov 2025 18:09:11 +0100 Subject: [PATCH 126/207] Bypassing Call Logic in Editor Preview --- .../MaterialGenerator.Material.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index c778c03ee..bdade721e 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -790,9 +790,21 @@ void MaterialGenerator::ProcessGroupFunction(Box* box, Node* node, Value& value) // Function Input case 1: { + // Check the stack count.If only 1 graph is present, + // we are processing the graph in isolation (e.g., in the Editor Preview). + // In this case, we skip the caller-finding logic and use the node's default value. + if (_graphStack.Count() < 2) + { + // Use the default value from the function input node's box (usually box 1) + value = tryGetValue(node->TryGetBox(1), Value::Zero); + break; + } + // Find the function call Node* functionCallNode = nullptr; - ASSERT(_graphStack.Count() >= 2); + + // The original ASSERT has been effectively replaced by the 'if' above. + //ASSERT(_graphStack.Count() >= 2); Graph* graph; for (int32 i = _callStack.Count() - 1; i >= 0; i--) { From 56beca0db4d7e8b5d14323e988ab86275fcdf071 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Thu, 27 Nov 2025 22:54:43 +0100 Subject: [PATCH 127/207] Fixed network replicated-object deduplication by hashing/equality on ObjectId Aligned NetworkReplicatedObject equality with its hash (compare ObjectId, not pointer). --- Source/Engine/Networking/NetworkReplicator.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index c584d3526..bf874b3c4 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -152,12 +152,12 @@ struct NetworkReplicatedObject bool operator==(const NetworkReplicatedObject& other) const { - return Object == other.Object; + return ObjectId == other.ObjectId; } bool operator==(const ScriptingObject* other) const { - return Object == other; + return other && ObjectId == other->GetID(); } bool operator==(const Guid& other) const @@ -176,6 +176,11 @@ inline uint32 GetHash(const NetworkReplicatedObject& key) return GetHash(key.ObjectId); } +inline uint32 GetHash(const ScriptingObject* key) +{ + return key ? GetHash(key->GetID()) : 0; +} + struct Serializer { NetworkReplicator::SerializeFunc Methods[2]; From 00f9a28729e3c4c0c18efecb99d19dd647592bc4 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Fri, 28 Nov 2025 15:51:19 +0100 Subject: [PATCH 128/207] Fixed HashSet compaction count after mid-compact growth Ensure HashSetBase::Compact() preserves _elementsCount even when EnsureCapacity() triggers during compaction. The growth path resets the counter; we now cache the original count and restore it after moving all buckets so Count() stays correct in heavy-collision scenarios. --- Source/Engine/Core/Collections/HashSetBase.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Core/Collections/HashSetBase.h b/Source/Engine/Core/Collections/HashSetBase.h index 96adabf79..58f1702c2 100644 --- a/Source/Engine/Core/Collections/HashSetBase.h +++ b/Source/Engine/Core/Collections/HashSetBase.h @@ -408,6 +408,7 @@ protected: } else { + const int32 elementsCount = _elementsCount; // Rebuild entire table completely const int32 oldSize = _size; AllocationData oldAllocation; @@ -437,6 +438,7 @@ protected: } for (int32 i = 0; i < oldSize; ++i) oldData[i].Free(); + _elementsCount = elementsCount; } _deletedCount = 0; } From 93217da6191cc9f7ca0b4ca9762499bde49ab3d9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 29 Nov 2025 15:04:11 -0800 Subject: [PATCH 129/207] Add option to merge vertex layout with reference order maintained --- .../Graphics/Shaders/GPUVertexLayout.cpp | 33 +++++++++++++++++-- .../Engine/Graphics/Shaders/GPUVertexLayout.h | 3 +- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp index 585568d60..23382673f 100644 --- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp +++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.cpp @@ -216,20 +216,21 @@ GPUVertexLayout* GPUVertexLayout::Get(const Span& layouts) return result; } -GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride) +GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused, bool addMissing, int32 missingSlotOverride, bool referenceOrder) { GPUVertexLayout* result = base ? base : reference; if (base && reference && base != reference) { bool elementsModified = false; Elements newElements = base->GetElements(); + const Elements& refElements = reference->GetElements(); if (removeUnused) { for (int32 i = newElements.Count() - 1; i >= 0; i--) { bool missing = true; const VertexElement& e = newElements.Get()[i]; - for (const VertexElement& ee : reference->GetElements()) + for (const VertexElement& ee : refElements) { if (ee.Type == e.Type) { @@ -247,7 +248,7 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* } if (addMissing) { - for (const VertexElement& e : reference->GetElements()) + for (const VertexElement& e : refElements) { bool missing = true; for (const VertexElement& ee : base->GetElements()) @@ -282,6 +283,32 @@ GPUVertexLayout* GPUVertexLayout::Merge(GPUVertexLayout* base, GPUVertexLayout* } } } + if (referenceOrder) + { + for (int32 i = 0, j = 0; i < newElements.Count() && j < refElements.Count(); j++) + { + if (newElements[i].Type == refElements[j].Type) + { + // Elements match so move forward + i++; + continue; + } + + // Find reference element in a new list + for (int32 k = i + 1; k < newElements.Count(); k++) + { + if (newElements[k].Type == refElements[j].Type) + { + // Move matching element to the reference position + VertexElement e = newElements[k]; + newElements.RemoveAt(k); + newElements.Insert(i, e); + i++; + break; + } + } + } + } if (elementsModified) result = Get(newElements, true); } diff --git a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h index 9d33566fb..04815565b 100644 --- a/Source/Engine/Graphics/Shaders/GPUVertexLayout.h +++ b/Source/Engine/Graphics/Shaders/GPUVertexLayout.h @@ -84,8 +84,9 @@ public: /// True to remove elements from base layout that don't exist in a reference layout. /// True to add missing elements to base layout that exist in a reference layout. /// Allows to override the input slot for missing elements. Use value -1 to inherit slot from the reference layout. + /// True to reorder result elements to match the reference layout. For example, if input vertex buffer layout is different than vertex shader then it can match those. /// Vertex layout object. Doesn't need to be cleared as it's cached for an application lifetime. - static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1); + static GPUVertexLayout* Merge(GPUVertexLayout* base, GPUVertexLayout* reference, bool removeUnused = false, bool addMissing = true, int32 missingSlotOverride = -1, bool referenceOrder = false); public: // [GPUResource] From 6a3ce862cb37e9185c0eb644c06906d266c8d268 Mon Sep 17 00:00:00 2001 From: Inertia Date: Mon, 1 Dec 2025 11:19:35 +1100 Subject: [PATCH 130/207] - Add X11 Class hints for easy hooking by WMs for window-specific rules (required to fix some bugs in WMs like Hyprland) --- Source/Engine/Platform/Linux/LinuxWindow.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Source/Engine/Platform/Linux/LinuxWindow.cpp b/Source/Engine/Platform/Linux/LinuxWindow.cpp index a2bd17222..e7996229b 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.cpp +++ b/Source/Engine/Platform/Linux/LinuxWindow.cpp @@ -18,6 +18,7 @@ #include "Engine/Graphics/PixelFormatSampler.h" #include "Engine/Graphics/Textures/TextureData.h" #include "IncludeX11.h" +#include "ThirdParty/X11/Xutil.h" // ICCCM #define WM_NormalState 1L // window normal state @@ -178,6 +179,20 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings) X11::XSetTransientForHint(display, window, (X11::Window)((LinuxWindow*)settings.Parent)->GetNativePtr()); } + // Provides class hint for WMs like Hyprland to hook onto and apply window rules + X11::XClassHint* classHint = X11::XAllocClassHint(); + if (classHint) + { + const char* className = settings.IsRegularWindow ? "FlexEditor" : "FlaxPopup"; + + classHint->res_name = const_cast(className); + classHint->res_class = const_cast(className); + + X11::XSetClassHint(display, window, classHint); + + XFree(classHint); + } + _dpi = Platform::GetDpi(); _dpiScale = (float)_dpi / (float)DefaultDPI; From 77aea0c69cae0b688bc8643716f5e451c10fb13e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 1 Dec 2025 08:18:54 -0800 Subject: [PATCH 131/207] Fix fatal error reporting from multiple therads to sync and properly log (eg. out of memory) --- Source/Engine/Platform/Base/PlatformBase.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/Engine/Platform/Base/PlatformBase.cpp b/Source/Engine/Platform/Base/PlatformBase.cpp index e472f7e1a..4512a59b3 100644 --- a/Source/Engine/Platform/Base/PlatformBase.cpp +++ b/Source/Engine/Platform/Base/PlatformBase.cpp @@ -51,6 +51,7 @@ Array> PlatformBase::Users; Delegate PlatformBase::UserAdded; Delegate PlatformBase::UserRemoved; void* OutOfMemoryBuffer = nullptr; +volatile int64 FatalReporting = 0; const Char* ToString(NetworkConnectionType value) { @@ -306,11 +307,20 @@ int32 PlatformBase::GetCacheLineSize() void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType error) { + // Let only one thread to report the error (and wait for it to end to have valid log before crash) +RETRY: + if (Platform::InterlockedCompareExchange(&FatalReporting, 1, 0) != 0) + { + Platform::Sleep(1); + goto RETRY; + } + // Check if is already during fatal state if (Engine::FatalError != FatalErrorType::None) { // Just send one more error to the log and back LOG(Error, "Error after fatal error: {0}", msg); + Platform::AtomicStore(&FatalReporting, 0); return; } @@ -429,6 +439,8 @@ void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType er } #endif + Platform::AtomicStore(&FatalReporting, 0); + // Show error message if (Engine::ReportCrash.IsBinded()) Engine::ReportCrash(msg, context); From 02429266b1a2b180b10870a926b6fb0c4508d283 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 3 Dec 2025 05:03:21 -0800 Subject: [PATCH 132/207] Fix `Array::RemoveAtKeepOrder` to avoid memory override with large mem copy --- Source/Engine/Core/Collections/Array.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index 4f660d2a9..919ff8f80 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -658,7 +658,10 @@ public: --_count; T* data = _allocation.Get(); if (index < _count) - Memory::MoveAssignItems(data + index, data + (index + 1), _count - index); + { + for (int32 i = index; i < _count; i++) + data[i] = MoveTemp(data[i + 1]); + } Memory::DestructItems(data + _count, 1); } From 3a798a70fadeefde6b6c32ddd0bca411d70c19a0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Dec 2025 23:29:15 +0100 Subject: [PATCH 133/207] Fix collections capacity growing to use the closest power of two Capacity was incorrectly 2x larger than needed. Added unit test to ensure it stays correct. --- Source/Engine/Core/Memory/Allocation.h | 4 +--- Source/Engine/Tests/TestCollections.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Core/Memory/Allocation.h b/Source/Engine/Core/Memory/Allocation.h index ffd51d96c..f274d37a5 100644 --- a/Source/Engine/Core/Memory/Allocation.h +++ b/Source/Engine/Core/Memory/Allocation.h @@ -18,9 +18,7 @@ namespace AllocationUtils capacity |= capacity >> 8; capacity |= capacity >> 16; uint64 capacity64 = (uint64)(capacity + 1) * 2; - if (capacity64 > MAX_int32) - capacity64 = MAX_int32; - return (int32)capacity64; + return capacity64 >= MAX_int32 ? MAX_int32 : (int32)capacity64 / 2; } // Aligns the input value to the next power of 2 to be used as bigger memory allocation block. diff --git a/Source/Engine/Tests/TestCollections.cpp b/Source/Engine/Tests/TestCollections.cpp index b477140cf..a96c6ce1c 100644 --- a/Source/Engine/Tests/TestCollections.cpp +++ b/Source/Engine/Tests/TestCollections.cpp @@ -27,6 +27,19 @@ void CheckBitArray(const BitArray& array) TEST_CASE("Array") { + SECTION("Test Capacity") + { + // Ensure correct collections capacity growing to meet proper memory usage vs safe slack + CHECK(AllocationUtils::CalculateCapacityGrow(1, 0) == 8); + CHECK(AllocationUtils::CalculateCapacityGrow(7, 0) == 8); + CHECK(AllocationUtils::CalculateCapacityGrow(1, 16) == 16); + CHECK(AllocationUtils::CalculateCapacityGrow(31, 0) == 32); + CHECK(AllocationUtils::CalculateCapacityGrow(32, 0) == 32); + CHECK(AllocationUtils::CalculateCapacityGrow(1000, 0) == 1024); + CHECK(AllocationUtils::CalculateCapacityGrow(1024, 0) == 1024); + CHECK(AllocationUtils::CalculateCapacityGrow(1025, 0) == 2048); + } + SECTION("Test Allocators") { Array a1; From 32bd72fecd591cc25f781cd1c9da60ec20b99b7c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 4 Dec 2025 23:51:07 +0100 Subject: [PATCH 134/207] Minor fix to the game cooker assets summary log of a single asset --- Source/Editor/Cooker/Steps/CookAssetsStep.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index 57654a6b3..c95306505 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -1368,7 +1368,10 @@ bool CookAssetsStep::Perform(CookingData& data) { typeName = e.TypeName; } - LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize)); + if (e.Count == 1) + LOG(Info, "{0}: 1 asset of total size {1}", typeName, Utilities::BytesToText(e.ContentSize)); + else + LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize)); } LOG(Info, ""); } From bd78db72b99a8a85201022df91295ea9516a7e76 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 5 Dec 2025 03:46:28 -0800 Subject: [PATCH 135/207] Add Mono AOT dynamic module preloading to speed up startup time --- Source/Engine/Scripting/Runtime/DotNet.cpp | 59 +++++++++++++++++++--- 1 file changed, 53 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index ed662801e..37b906743 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -2137,6 +2137,53 @@ static void* OnMonoDlFallbackClose(void* handle, void* user_data) #endif +#ifdef USE_MONO_AOT_MODULE + +#include "Engine/Threading/ThreadPoolTask.h" +#include "Engine/Engine/EngineService.h" + +class MonoAotPreloadTask : public ThreadPoolTask +{ +public: + bool Run() override; +}; + +// Preloads in-build AOT dynamic module in async +class MonoAotPreloadService : public EngineService +{ +public: + volatile int64 Ready = 0; + void* Library = nullptr; + + MonoAotPreloadService() + : EngineService(TEXT("AOT Preload"), -800) + { + } + + bool Init() override + { + New()->Start(); + return false; + } +}; + +MonoAotPreloadService MonoAotPreloadServiceInstance; + +bool MonoAotPreloadTask::Run() +{ + // Load AOT module + Stopwatch aotModuleLoadStopwatch; + LOG(Info, "Loading Mono AOT module..."); + MonoAotPreloadServiceInstance.Library = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE)); + aotModuleLoadStopwatch.Stop(); + LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds()); + + Platform::AtomicStore(&MonoAotPreloadServiceInstance.Ready, 1); + return false; +} + +#endif + bool InitHostfxr() { #if DOTNET_HOST_MONO_DEBUG @@ -2167,10 +2214,12 @@ bool InitHostfxr() #endif #ifdef USE_MONO_AOT_MODULE - // Load AOT module - Stopwatch aotModuleLoadStopwatch; - LOG(Info, "Loading Mono AOT module..."); - void* libAotModule = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE)); + // Wait for AOT module preloading + while (Platform::AtomicRead(&MonoAotPreloadServiceInstance.Ready) == 0) + Platform::Yield(); + + // Initialize AOT module + void* libAotModule = MonoAotPreloadServiceInstance.Library; if (libAotModule == nullptr) { LOG(Error, "Failed to laod Mono AOT module (" TEXT(USE_MONO_AOT_MODULE) ")"); @@ -2193,8 +2242,6 @@ bool InitHostfxr() mono_aot_register_module((void**)modules[i]); } Allocator::Free(modules); - aotModuleLoadStopwatch.Stop(); - LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds()); #endif // Setup debugger From 56278b17ee135df748891f426baacb939112e7f3 Mon Sep 17 00:00:00 2001 From: Phantom Date: Sun, 7 Dec 2025 16:53:43 +0100 Subject: [PATCH 136/207] Add Text Color Highlighted on Dropdown --- Source/Engine/UI/GUI/Common/Dropdown.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 868db4b89..7af0185aa 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -303,6 +303,12 @@ namespace FlaxEngine.GUI [EditorDisplay("Text Style"), EditorOrder(2023), ExpandGroups] public Color TextColor { get; set; } + /// + /// Gets or sets the color used to display highlighted text. + /// + [EditorDisplay("Text Style"), EditorOrder(2023), ExpandGroups] + public Color TextColorHighlighted { get; set; } + /// /// Gets or sets the horizontal text alignment within the control bounds. /// @@ -386,6 +392,7 @@ namespace FlaxEngine.GUI var style = Style.Current; Font = new FontReference(style.FontMedium); TextColor = style.Foreground; + TextColorHighlighted = style.Foreground; BackgroundColor = style.BackgroundNormal; BackgroundColorHighlighted = BackgroundColor; BackgroundColorSelected = BackgroundColor; @@ -749,7 +756,7 @@ namespace FlaxEngine.GUI // Draw text of the selected item var textRect = new Rectangle(margin, 0, clientRect.Width - boxSize - 2.0f * margin, clientRect.Height); Render2D.PushClip(textRect); - var textColor = TextColor; + var textColor = (IsMouseOver || IsNavFocused) ? TextColorHighlighted : TextColor; string text = _items[_selectedIndex]; string format = TextFormat != null ? TextFormat : null; if (!string.IsNullOrEmpty(format)) From a7e77f6e21f15e2ea2ab92891d2226d256f53169 Mon Sep 17 00:00:00 2001 From: Phantom Date: Sun, 7 Dec 2025 18:23:38 +0100 Subject: [PATCH 137/207] Update CreatePopupItem method -Modify the `TextColour` property to use a dynamic value based on `TextColour` multiplied by `0.9f` instead of a fixed value (`Colour.White * 0.9f`). -Modify the `TextColourHighlighted` property to use the dynamic value of `TextColourHighlighted` instead of a fixed value (`Colour.White`). --- Source/Engine/UI/GUI/Common/Dropdown.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 7af0185aa..0d24c5491 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -594,8 +594,8 @@ namespace FlaxEngine.GUI X = margin, Size = new Float2(size.X - margin, size.Y), Font = Font, - TextColor = Color.White * 0.9f, - TextColorHighlighted = Color.White, + TextColor = TextColor * 0.9f, + TextColorHighlighted = TextColorHighlighted, HorizontalAlignment = HorizontalAlignment, VerticalAlignment = VerticalAlignment, Text = _items[i], From ed50ce9c90197dca45e90f7cf8b90aad3a4d2242 Mon Sep 17 00:00:00 2001 From: Phantom Date: Sun, 7 Dec 2025 18:48:16 +0100 Subject: [PATCH 138/207] Change Dropdown's EditorOrder from 2023 to 2024 --- Source/Engine/UI/GUI/Common/Dropdown.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs index 0d24c5491..a227e5acd 100644 --- a/Source/Engine/UI/GUI/Common/Dropdown.cs +++ b/Source/Engine/UI/GUI/Common/Dropdown.cs @@ -306,7 +306,7 @@ namespace FlaxEngine.GUI /// /// Gets or sets the color used to display highlighted text. /// - [EditorDisplay("Text Style"), EditorOrder(2023), ExpandGroups] + [EditorDisplay("Text Style"), EditorOrder(2024), ExpandGroups] public Color TextColorHighlighted { get; set; } /// From 56066a3212e157598f0a31d2bd33421018107357 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 8 Dec 2025 14:41:55 -0800 Subject: [PATCH 139/207] Porting to a famous blue platform --- .../Graphics/Materials/DeferredMaterialShader.cpp | 1 + .../Graphics/Materials/ForwardMaterialShader.cpp | 5 +++++ .../Graphics/Materials/ParticleMaterialShader.cpp | 5 +++++ Source/Engine/Graphics/RenderBuffers.cpp | 3 ++- Source/Engine/Renderer/PostProcessingPass.cpp | 1 + Source/Engine/Renderer/RenderList.cpp | 3 ++- Source/Engine/Renderer/RenderList.h | 2 +- Source/Engine/Scripting/Runtime/DotNet.cpp | 2 +- Source/Engine/ShadersCompilation/ShaderCompiler.cpp | 11 +++++++++++ Source/Engine/ShadersCompilation/ShaderCompiler.h | 1 + 10 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp index bbcdc8207..7b31a606e 100644 --- a/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/DeferredMaterialShader.cpp @@ -201,6 +201,7 @@ bool DeferredMaterialShader::Load() psDesc.DepthWriteEnable = true; psDesc.DepthEnable = true; psDesc.DepthFunc = ComparisonFunc::Less; + psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::None; psDesc.HS = nullptr; psDesc.DS = nullptr; GPUShaderProgramVS* instancedDepthPassVS; diff --git a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp index 72ec3c7bd..d8d986163 100644 --- a/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ForwardMaterialShader.cpp @@ -195,5 +195,10 @@ bool ForwardMaterialShader::Load() psDesc.VS = _shader->GetVS("VS_Skinned"); _cache.DepthSkinned.Init(psDesc); +#if PLATFORM_PS5 + // Fix shader binding issues on forward shading materials on PS5 + _drawModes = DrawPass::None; +#endif + return false; } diff --git a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp index 6c780cc55..a2c45a6ee 100644 --- a/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/ParticleMaterialShader.cpp @@ -264,5 +264,10 @@ bool ParticleMaterialShader::Load() // Lazy initialization _cacheVolumetricFog.Desc.PS = nullptr; +#if PLATFORM_PS5 + // Fix shader binding issues on forward shading materials on PS5 + _drawModes = DrawPass::None; +#endif + return false; } diff --git a/Source/Engine/Graphics/RenderBuffers.cpp b/Source/Engine/Graphics/RenderBuffers.cpp index ed49260ec..931312aa5 100644 --- a/Source/Engine/Graphics/RenderBuffers.cpp +++ b/Source/Engine/Graphics/RenderBuffers.cpp @@ -113,7 +113,8 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context) PixelFormat RenderBuffers::GetOutputFormat() const { - return _useAlpha ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float; + // TODO: fix incorrect alpha leaking into reflections on PS5 with R11G11B10_Float + return _useAlpha || PLATFORM_PS5 ? PixelFormat::R16G16B16A16_Float : PixelFormat::R11G11B10_Float; } bool RenderBuffers::GetUseAlpha() const diff --git a/Source/Engine/Renderer/PostProcessingPass.cpp b/Source/Engine/Renderer/PostProcessingPass.cpp index a9eba14d2..030541e4c 100644 --- a/Source/Engine/Renderer/PostProcessingPass.cpp +++ b/Source/Engine/Renderer/PostProcessingPass.cpp @@ -375,6 +375,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input, RENDER_TARGET_POOL_SET_NAME(bloomBuffer1, "PostProcessing.Bloom"); RENDER_TARGET_POOL_SET_NAME(bloomBuffer2, "PostProcessing.Bloom"); + // TODO: skip this clear? or do it at once for the whole textures (2 calls instead of per-mip) for (int32 mip = 0; mip < bloomMipCount; mip++) { context->Clear(bloomBuffer1->View(0, mip), Color::Transparent); diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index bb580aaf3..ace6f9342 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -917,6 +917,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL perDraw.DrawPadding = Float3::Zero; GPUConstantBuffer* perDrawCB = IMaterial::BindParameters::PerDrawConstants; context->BindCB(2, perDrawCB); // TODO: use rootSignature/pushConstants on D3D12/Vulkan + context->UpdateCB(perDrawCB, &perDraw); constexpr int32 vbMax = ARRAY_COUNT(DrawCall::Geometry.VertexBuffers); if (useInstancing) { @@ -1057,7 +1058,7 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL materialBinds += list.PreBatchedDrawCalls.Count(); if (list.Batches.IsEmpty() && list.Indices.Count() != 0) { - // Draw calls list has bot been batched so execute draw calls separately + // Draw calls list has not been batched so execute draw calls separately for (int32 j = 0; j < list.Indices.Count(); j++) { perDraw.DrawObjectIndex = listData[j]; diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 7a3ac867a..8eb3540e0 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -273,7 +273,7 @@ struct DrawCallsList /// /// True if draw calls batches list can be rendered using hardware instancing, otherwise false. /// - bool CanUseInstancing; + bool CanUseInstancing = true; void Clear(); bool IsEmpty() const; diff --git a/Source/Engine/Scripting/Runtime/DotNet.cpp b/Source/Engine/Scripting/Runtime/DotNet.cpp index 37b906743..1c8c2bcdd 100644 --- a/Source/Engine/Scripting/Runtime/DotNet.cpp +++ b/Source/Engine/Scripting/Runtime/DotNet.cpp @@ -2173,7 +2173,7 @@ bool MonoAotPreloadTask::Run() { // Load AOT module Stopwatch aotModuleLoadStopwatch; - LOG(Info, "Loading Mono AOT module..."); + //LOG(Info, "Loading Mono AOT module..."); MonoAotPreloadServiceInstance.Library = Platform::LoadLibrary(TEXT(USE_MONO_AOT_MODULE)); aotModuleLoadStopwatch.Stop(); LOG(Info, "Mono AOT module loaded in {0}ms", aotModuleLoadStopwatch.GetMilliseconds()); diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp index b6b859afa..cfb08b925 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp @@ -278,6 +278,17 @@ bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* co return false; } +bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache1, int32 cache1Size, const void* cache2, int32 cache2Size) +{ + auto output = context->Output; + output->Write((uint32)(cache1Size + cache2Size + headerSize)); + output->WriteBytes(header, headerSize); + output->WriteBytes(cache1, cache1Size); + output->WriteBytes(cache2, cache2Size); + output->Write(bindings); + return false; +} + bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize) { auto output = context->Output; diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.h b/Source/Engine/ShadersCompilation/ShaderCompiler.h index 36fd592e6..9666d8f13 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.h +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.h @@ -108,6 +108,7 @@ protected: static bool WriteShaderFunctionBegin(ShaderCompilationContext* context, ShaderFunctionMeta& meta); static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache, int32 cacheSize); + static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache1, int32 cache1Size, const void* cache2, int32 cache2Size); static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize); static bool WriteShaderFunctionEnd(ShaderCompilationContext* context, ShaderFunctionMeta& meta); static bool WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array& macros, void* additionalData); From 5c81c7111697d1216f3d704e7c19f3930071c4b8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 9 Dec 2025 09:51:53 +0100 Subject: [PATCH 140/207] Move constant buffer init for instanced draws only, others do it in all paths --- Source/Engine/Renderer/RenderList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index ace6f9342..544438bb5 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -917,10 +917,10 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL perDraw.DrawPadding = Float3::Zero; GPUConstantBuffer* perDrawCB = IMaterial::BindParameters::PerDrawConstants; context->BindCB(2, perDrawCB); // TODO: use rootSignature/pushConstants on D3D12/Vulkan - context->UpdateCB(perDrawCB, &perDraw); constexpr int32 vbMax = ARRAY_COUNT(DrawCall::Geometry.VertexBuffers); if (useInstancing) { + context->UpdateCB(perDrawCB, &perDraw); GPUBuffer* vb[vbMax + 1]; uint32 vbOffsets[vbMax + 1]; vb[3] = _instanceBuffer.GetBuffer(); // Pass object index in a vertex stream at slot 3 (used by VS in Surface.shader) From 0a8752ec0af77f01065a4dc56f464f22e2906edd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Dec 2025 09:48:27 +0100 Subject: [PATCH 141/207] Fix cross-building building engine with separate executable and library for Unix platforms on Windows --- Source/Tools/Flax.Build/Build/EngineTarget.cs | 3 ++- Source/Tools/Flax.Build/Build/Graph/TaskGraph.cs | 9 ++------- Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs | 3 ++- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/EngineTarget.cs b/Source/Tools/Flax.Build/Build/EngineTarget.cs index 039f9fb16..e22a924d9 100644 --- a/Source/Tools/Flax.Build/Build/EngineTarget.cs +++ b/Source/Tools/Flax.Build/Build/EngineTarget.cs @@ -217,7 +217,8 @@ namespace Flax.Build var engineLibraryType = LinkerOutput.SharedLibrary; if (buildOptions.Toolchain?.Compiler == TargetCompiler.MSVC) engineLibraryType = LinkerOutput.ImportLibrary; // MSVC links DLL against import library - exeBuildOptions.LinkEnv.InputLibraries.Add(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(LibraryName, engineLibraryType))); + var engineLibraryPath = Utilities.NormalizePath(Path.Combine(buildOptions.OutputFolder, buildOptions.Platform.GetLinkOutputFileName(LibraryName, engineLibraryType))); + exeBuildOptions.LinkEnv.InputLibraries.Add(engineLibraryPath); exeBuildOptions.LinkEnv.InputFiles.AddRange(mainModuleOptions.OutputFiles); exeBuildOptions.DependencyFiles.AddRange(mainModuleOptions.DependencyFiles); exeBuildOptions.NugetPackageReferences.AddRange(mainModuleOptions.NugetPackageReferences); diff --git a/Source/Tools/Flax.Build/Build/Graph/TaskGraph.cs b/Source/Tools/Flax.Build/Build/Graph/TaskGraph.cs index bd0ef2ad5..6c459b33e 100644 --- a/Source/Tools/Flax.Build/Build/Graph/TaskGraph.cs +++ b/Source/Tools/Flax.Build/Build/Graph/TaskGraph.cs @@ -134,7 +134,7 @@ namespace Flax.Build.Graph } /// - /// Performs tasks list sorting based on task dependencies and cost heuristics to to improve parallelism of the graph execution. + /// Performs tasks list sorting based on task dependencies and cost heuristics to improve parallelism of the graph execution. /// public void SortTasks() { @@ -149,12 +149,7 @@ namespace Flax.Build.Graph { if (FileToProducingTaskMap.TryGetValue(prerequisiteFile, out var prerequisiteTask)) { - HashSet dependentTasks; - if (taskToDependentActionsMap.ContainsKey(prerequisiteTask)) - { - dependentTasks = taskToDependentActionsMap[prerequisiteTask]; - } - else + if (!taskToDependentActionsMap.TryGetValue(prerequisiteTask, out var dependentTasks)) { dependentTasks = new HashSet(); taskToDependentActionsMap[prerequisiteTask] = dependentTasks; diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index fcb4397a1..1d623c568 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -605,7 +605,8 @@ namespace Flax.Build.Platforms /// public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { - outputFilePath = outputFilePath.Replace('\\', '/'); + //outputFilePath = outputFilePath.Replace('\\', '/'); + outputFilePath = Utilities.NormalizePath(outputFilePath.Replace('\\', '/')); Task linkTask; switch (options.LinkEnv.Output) From 3b9b49950ccdc77ecf2942ba647796e97254e17a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Dec 2025 09:48:47 +0100 Subject: [PATCH 142/207] Fixes for Xbox One --- Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp | 6 ++++++ .../app/src/main/java/com/flaxengine/GameActivity.java | 2 +- Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 62070ecda..4b9298b6c 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -628,6 +628,7 @@ bool GPUDeviceDX12::Init() VALIDATE_DIRECTX_CALL(dxgiAdapter->EnumOutputs(0, dxgiOutput.GetAddressOf())); DXGI_FORMAT backbufferFormat = RenderToolsDX::ToDxgiFormat(GPU_BACK_BUFFER_PIXEL_FORMAT); UINT modesCount = 0; +#ifdef _GAMING_XBOX_SCARLETT VALIDATE_DIRECTX_CALL(dxgiOutput->GetDisplayModeList(backbufferFormat, 0, &modesCount, NULL)); Array modes; modes.Resize((int32)modesCount); @@ -642,6 +643,11 @@ bool GPUDeviceDX12::Init() videoOutput.RefreshRate = Math::Max(videoOutput.RefreshRate, mode.RefreshRate.Numerator / (float)mode.RefreshRate.Denominator); } modes.Resize(0); +#else + videoOutput.Width = 1920; + videoOutput.Height = 1080; + videoOutput.RefreshRate = 60; +#endif #if PLATFORM_GDK GDKPlatform::Suspended.Bind(this); diff --git a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java index b19663fc9..ecb27b013 100644 --- a/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java +++ b/Source/Platforms/Android/Binaries/Project/app/src/main/java/com/flaxengine/GameActivity.java @@ -1,4 +1,4 @@ -// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. +// Copyright (c) Wojciech Figat. All rights reserved. package com.flaxengine; diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs index 4d6a1aab6..66909f6b9 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/nethost.cs @@ -94,6 +94,7 @@ namespace Flax.Deps.Dependencies defines += "-DDISABLE_EXECUTABLES=1-DDISABLE_SHARED_LIBS=1"; buildArgs = $" -subset mono+libs -cmakeargs \"{defines}\" /p:FeaturePerfTracing=false /p:FeatureWin32Registry=false /p:FeatureCominteropApartmentSupport=false /p:FeatureManagedEtw=false /p:FeatureManagedEtwChannels=false /p:FeatureEtw=false /p:ApiCompatValidateAssemblies=false"; envVars.Add("_GAMING_XBOX", "1"); + envVars.Add(targetPlatform == TargetPlatform.XboxScarlett ? "_GAMING_XBOX_SCARLETT" : "_GAMING_XBOX_XBOXONE", "1"); break; case TargetPlatform.Linux: os = "linux"; From 43665aa7eb7cef67ecf4e9d80f632c3717314117 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Dec 2025 13:00:59 +0100 Subject: [PATCH 143/207] Rename `GPUContext::ClearState` to `ResetState` for constentency --- Source/Engine/Graphics/GPUContext.cpp | 2 +- Source/Engine/Graphics/GPUContext.h | 11 ++++++++++- .../GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp | 2 +- .../GraphicsDevice/DirectX/DX11/GPUContextDX11.h | 2 +- .../GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp | 2 +- .../GraphicsDevice/DirectX/DX12/GPUContextDX12.h | 2 +- Source/Engine/GraphicsDevice/Null/GPUContextNull.h | 2 +- .../Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp | 2 +- .../Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h | 2 +- Source/Engine/Renderer/ProbesRenderer.cpp | 4 ++-- Source/Engine/Renderer/Renderer.cpp | 2 +- Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp | 4 ++-- 12 files changed, 23 insertions(+), 14 deletions(-) diff --git a/Source/Engine/Graphics/GPUContext.cpp b/Source/Engine/Graphics/GPUContext.cpp index 55f87ba3b..905919794 100644 --- a/Source/Engine/Graphics/GPUContext.cpp +++ b/Source/Engine/Graphics/GPUContext.cpp @@ -67,7 +67,7 @@ void GPUContext::FrameBegin() void GPUContext::FrameEnd() { - ClearState(); + ResetState(); FlushState(); } diff --git a/Source/Engine/Graphics/GPUContext.h b/Source/Engine/Graphics/GPUContext.h index 4f1306567..5f4bd8020 100644 --- a/Source/Engine/Graphics/GPUContext.h +++ b/Source/Engine/Graphics/GPUContext.h @@ -618,7 +618,16 @@ public: /// /// Clears the context state. /// - API_FUNCTION() virtual void ClearState() = 0; + DEPRECATED("Use ResetState instead") + API_FUNCTION() void ClearState() + { + ResetState(); + } + + /// + /// Resets the context state. + /// + API_FUNCTION() virtual void ResetState() = 0; /// /// Flushes the internal cached context state with a command buffer. diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index a0ec80bb1..f623f53b5 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -724,7 +724,7 @@ void GPUContextDX11::SetState(GPUPipelineState* state) } } -void GPUContextDX11::ClearState() +void GPUContextDX11::ResetState() { if (!_context) return; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h index 7dc693019..eee2699df 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h @@ -158,7 +158,7 @@ public: void SetScissor(const Rectangle& scissorRect) override; GPUPipelineState* GetState() const override; void SetState(GPUPipelineState* state) override; - void ClearState() override; + void ResetState() override; void FlushState() override; void Flush() override; void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 6752a9b8d..98143c7c3 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -1304,7 +1304,7 @@ void GPUContextDX12::SetState(GPUPipelineState* state) } } -void GPUContextDX12::ClearState() +void GPUContextDX12::ResetState() { if (!_commandList) return; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h index 4bd1b54a1..51f24f4a6 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h @@ -201,7 +201,7 @@ public: void SetScissor(const Rectangle& scissorRect) override; GPUPipelineState* GetState() const override; void SetState(GPUPipelineState* state) override; - void ClearState() override; + void ResetState() override; void FlushState() override; void Flush() override; void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override; diff --git a/Source/Engine/GraphicsDevice/Null/GPUContextNull.h b/Source/Engine/GraphicsDevice/Null/GPUContextNull.h index 0ea111d24..22786c157 100644 --- a/Source/Engine/GraphicsDevice/Null/GPUContextNull.h +++ b/Source/Engine/GraphicsDevice/Null/GPUContextNull.h @@ -177,7 +177,7 @@ public: { } - void ClearState() override + void ResetState() override { } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index c374bbeed..979ccc0f8 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -1329,7 +1329,7 @@ void GPUContextVulkan::SetState(GPUPipelineState* state) } } -void GPUContextVulkan::ClearState() +void GPUContextVulkan::ResetState() { ResetRenderTarget(); ResetSR(); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h index 8ed541089..fa94aa139 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h @@ -193,7 +193,7 @@ public: void SetScissor(const Rectangle& scissorRect) override; GPUPipelineState* GetState() const override; void SetState(GPUPipelineState* state) override; - void ClearState() override; + void ResetState() override; void FlushState() override; void Flush() override; void UpdateBuffer(GPUBuffer* buffer, const void* data, uint32 size, uint32 offset) override; diff --git a/Source/Engine/Renderer/ProbesRenderer.cpp b/Source/Engine/Renderer/ProbesRenderer.cpp index eaf7a53ca..4c0a43cb6 100644 --- a/Source/Engine/Renderer/ProbesRenderer.cpp +++ b/Source/Engine/Renderer/ProbesRenderer.cpp @@ -509,7 +509,7 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context) // Render frame Renderer::Render(_task); - context->ClearState(); + context->ResetState(); // Copy frame to cube face { @@ -568,7 +568,7 @@ void ProbesRendererService::OnRender(RenderTask* task, GPUContext* context) } // Cleanup - context->ClearState(); + context->ResetState(); if (_workStep < 7) return; // Continue rendering next frame diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 278c8237d..7a72cd923 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -200,7 +200,7 @@ void Renderer::Render(SceneRenderTask* task) // Prepare GPU context auto context = GPUDevice::Instance->GetMainContext(); - context->ClearState(); + context->ResetState(); context->FlushState(); const Viewport viewport = task->GetViewport(); context->SetViewportAndScissors(viewport); diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index a66a0e283..b77ee51b6 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -376,7 +376,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) EnableLightmapsUsage = _giBounceRunningIndex != 0; // Renderer::Render(_task); - context->ClearState(); + context->ResetState(); // IsRunningRadiancePass = false; EnableLightmapsUsage = true; @@ -515,7 +515,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) } // Cleanup after rendering - context->ClearState(); + context->ResetState(); // Mark job as done Platform::AtomicStore(&_wasJobDone, 1); From 20a7fcf6a0accd31cf1ba4fa43a4bff379d90096 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Dec 2025 13:01:24 +0100 Subject: [PATCH 144/207] Add profiler wait event for GPU wait on D3D12 --- Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp index 81fef4965..a384b6383 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.cpp @@ -143,6 +143,8 @@ void CommandQueueDX12::WaitForFence(uint64 fenceValue) void CommandQueueDX12::WaitForGPU() { + PROFILE_CPU(); + ZoneColor(TracyWaitZoneColor); const uint64 value = _fence.Signal(this); _fence.WaitCPU(value); } From ca52122656fc4312303e6b423b70ebc310fd4fd5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Dec 2025 14:53:51 +0100 Subject: [PATCH 145/207] Fix validation error on Windows for textures but optimize buffers instead --- Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp | 2 +- Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp index 0d9ff88d4..6f3ff5fba 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp @@ -137,7 +137,7 @@ bool GPUBufferDX12::OnInit() // Create resource ID3D12Resource* resource; #if PLATFORM_WINDOWS - D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED; + D3D12_HEAP_FLAGS heapFlags = EnumHasAnyFlags(_desc.Flags, GPUBufferFlags::VertexBuffer | GPUBufferFlags::IndexBuffer) || _desc.InitData ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE; #else D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE; #endif diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp index e7e4c5ca2..a4fbc683d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp @@ -159,7 +159,7 @@ bool GPUTextureDX12::OnInit() initialState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; // Create texture -#if PLATFORM_WINDOWS +#if PLATFORM_WINDOWS && 0 D3D12_HEAP_FLAGS heapFlags = useRTV || useDSV ? D3D12_HEAP_FLAG_CREATE_NOT_ZEROED : D3D12_HEAP_FLAG_NONE; #else D3D12_HEAP_FLAGS heapFlags = D3D12_HEAP_FLAG_NONE; From a63b97d31d63674c1893168de736193bccf48e17 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Dec 2025 14:58:12 +0100 Subject: [PATCH 146/207] Add stripping DXIL debug data from the shader cache when not used --- .../DirectX/ShaderCompilerDX.cpp | 38 +++++++++++++++---- .../DirectX/ShaderCompilerDX.h | 1 + 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp index ca04c916d..7dd7182cf 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp @@ -76,16 +76,19 @@ ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile, PlatformType platform, { IDxcCompiler3* compiler = nullptr; IDxcLibrary* library = nullptr; + IDxcContainerBuilder* builder = nullptr; IDxcContainerReflection* containerReflection = nullptr; DxcCreateInstanceProc createInstance = dxcCreateInstanceProc ? (DxcCreateInstanceProc)dxcCreateInstanceProc : &DxcCreateInstance; if (FAILED(createInstance(CLSID_DxcCompiler, __uuidof(compiler), reinterpret_cast(&compiler))) || FAILED(createInstance(CLSID_DxcLibrary, __uuidof(library), reinterpret_cast(&library))) || + FAILED(createInstance(CLSID_DxcContainerBuilder, __uuidof(builder), reinterpret_cast(&builder))) || FAILED(createInstance(CLSID_DxcContainerReflection, __uuidof(containerReflection), reinterpret_cast(&containerReflection)))) { LOG(Error, "DxcCreateInstance failed"); } _compiler = compiler; _library = library; + _builder = builder; _containerReflection = containerReflection; static HashSet PrintVersions; if (PrintVersions.Add(createInstance)) @@ -103,14 +106,13 @@ ShaderCompilerDX::ShaderCompilerDX(ShaderProfile profile, PlatformType platform, ShaderCompilerDX::~ShaderCompilerDX() { - auto compiler = (IDxcCompiler2*)_compiler; - if (compiler) + if (auto compiler = (IDxcCompiler2*)_compiler) compiler->Release(); - auto library = (IDxcLibrary*)_library; - if (library) + if (auto library = (IDxcLibrary*)_library) library->Release(); - auto containerReflection = (IDxcContainerReflection*)_containerReflection; - if (containerReflection) + if (auto builder = (IDxcContainerBuilder*)_builder) + builder->Release(); + if (auto containerReflection = (IDxcContainerReflection*)_containerReflection) containerReflection->Release(); } @@ -254,7 +256,7 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD } // Get the output - ComPtr shaderBuffer = nullptr; + ComPtr shaderBuffer; if (FAILED(results->GetResult(&shaderBuffer))) { LOG(Error, "IDxcOperationResult::GetResult failed."); @@ -460,6 +462,28 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD } } + // Strip reflection data + if (!options->GenerateDebugData) + { + if (auto builder = (IDxcContainerBuilder*)_builder) + { + if (builder->Load(shaderBuffer) == S_OK) + { + builder->RemovePart(DXC_PART_PDB); + builder->RemovePart(DXC_PART_REFLECTION_DATA); + ComPtr serializeResult; + if (builder->SerializeContainer(&serializeResult) == S_OK) + { + ComPtr optimizedShaderBuffer; + if (SUCCEEDED(serializeResult->GetResult(&optimizedShaderBuffer))) + { + shaderBuffer = optimizedShaderBuffer; + } + } + } + } + } + if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), shaderBuffer->GetBufferPointer(), (int32)shaderBuffer->GetBufferSize())) return true; diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h index bf522cf3b..0b758cd3f 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.h @@ -15,6 +15,7 @@ private: Array _funcNameDefineBuffer; void* _compiler; void* _library; + void* _builder; void* _containerReflection; public: From 02cff3973a5fa7f6a92d6a4875b8e9163f12bd68 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Dec 2025 15:01:53 +0100 Subject: [PATCH 147/207] Bump up engine version --- Flax.flaxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 5123e5b8f..100d4e9ff 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -4,7 +4,7 @@ "Major": 1, "Minor": 11, "Revision": 0, - "Build": 6804 + "Build": 6805 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.", From c39c642b608ce15c431a5db43d6976dc374ff3cb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Dec 2025 17:39:18 +0100 Subject: [PATCH 148/207] Add safety check for invalid math values in shader graph generation --- Source/Engine/Visject/ShaderGraph.cpp | 21 ++++++++++++++++++++- Source/Engine/Visject/ShaderGraph.h | 2 ++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index 688e46382..faf344263 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -146,6 +146,8 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) Box* b2 = node->GetBox(1); Value v1 = tryGetValue(b1, 0, Value::Zero); Value v2 = tryGetValue(b2, 1, Value::Zero); + if (SanitizeMathValue(v1, node, b1, &value)) + break; if (b1->HasConnection()) v2 = v2.Cast(v1.Type); else @@ -251,7 +253,10 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value) // Lerp case 25: { - Value a = tryGetValue(node->GetBox(0), 0, Value::Zero); + auto boxA = node->GetBox(0); + Value a = tryGetValue(boxA, 0, Value::Zero); + if (SanitizeMathValue(a, node, boxA, &value)) + break; Value b = tryGetValue(node->GetBox(1), 1, Value::One).Cast(a.Type); Value alpha = tryGetValue(node->GetBox(2), 2, Value::Zero).Cast(ValueType::Float); String text = String::Format(TEXT("lerp({0}, {1}, {2})"), a.Value, b.Value, alpha.Value); @@ -1364,6 +1369,20 @@ SerializedMaterialParam& ShaderGenerator::findOrAddGlobalSDF() return param; } +bool ShaderGenerator::SanitizeMathValue(Value& value, Node* node, Box* box, Value* resultOnInvalid) +{ + bool invalid = value.Type == VariantType::Object; + if (invalid) + { + OnError(node, box, TEXT("Invalid input type for math operation")); + if (resultOnInvalid) + *resultOnInvalid = Value::Zero; + else + value = Value::Zero; + } + return invalid; +} + String ShaderGenerator::getLocalName(int32 index) { return TEXT("local") + StringUtils::ToString(index); diff --git a/Source/Engine/Visject/ShaderGraph.h b/Source/Engine/Visject/ShaderGraph.h index ab0e7d405..5b17604d0 100644 --- a/Source/Engine/Visject/ShaderGraph.h +++ b/Source/Engine/Visject/ShaderGraph.h @@ -255,6 +255,8 @@ protected: SerializedMaterialParam& findOrAddTextureGroupSampler(int32 index); SerializedMaterialParam& findOrAddGlobalSDF(); + bool SanitizeMathValue(Value& value, Node* node, Box* box, Value* resultOnInvalid = nullptr); + static String getLocalName(int32 index); static String getParamName(int32 index); }; From 9f07a2a54ed75701dba16031210475ed75d4db22 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 10 Dec 2025 18:58:43 +0100 Subject: [PATCH 149/207] Attempt to fix regression from 32bd72fecd591cc25f781cd1c9da60ec20b99b7c --- Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index 1d623c568..b6e2d7998 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -518,7 +518,7 @@ namespace Flax.Build.Platforms var args = new List(); args.AddRange(options.LinkEnv.CustomArgs); { - args.Add(string.Format("-o \"{0}\"", outputFilePath)); + args.Add(string.Format("-o \"{0}\"", outputFilePath.Replace('\\', '/'))); if (!options.LinkEnv.DebugInformation) { @@ -605,8 +605,7 @@ namespace Flax.Build.Platforms /// public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { - //outputFilePath = outputFilePath.Replace('\\', '/'); - outputFilePath = Utilities.NormalizePath(outputFilePath.Replace('\\', '/')); + outputFilePath = Utilities.NormalizePath(outputFilePath); Task linkTask; switch (options.LinkEnv.Output) From b5286af52626c096cba06ea9575ce0af305f7f7b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 11 Dec 2025 14:48:18 +0100 Subject: [PATCH 150/207] Attempt to fix regression from 32bd72fecd591cc25f781cd1c9da60ec20b99b7c --- .../Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index fe5abe4a3..583b93b55 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -778,6 +778,7 @@ namespace Flax.Build.Platforms /// public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { + outputFilePath = Utilities.NormalizePath(outputFilePath); var linkEnvironment = options.LinkEnv; var task = graph.Add(); From 71391cf1cc890fc96732391c6dae767cc91f3622 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 11 Dec 2025 16:38:28 +0100 Subject: [PATCH 151/207] Fix deprecated tag placement --- Source/Engine/Graphics/GPUContext.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Graphics/GPUContext.h b/Source/Engine/Graphics/GPUContext.h index 5f4bd8020..1144d6f49 100644 --- a/Source/Engine/Graphics/GPUContext.h +++ b/Source/Engine/Graphics/GPUContext.h @@ -189,7 +189,7 @@ public: /// [Deprecated in v1.10] /// /// true if depth buffer is binded; otherwise, false. - DEPRECATED("IsDepthBufferBinded has been deprecated and will be removed in ") + DEPRECATED("IsDepthBufferBinded has been deprecated and will be removed in future") virtual bool IsDepthBufferBinded() = 0; public: @@ -617,9 +617,9 @@ public: /// /// Clears the context state. + /// [Deprecated in v1.12] /// - DEPRECATED("Use ResetState instead") - API_FUNCTION() void ClearState() + API_FUNCTION() DEPRECATED("Use ResetState instead") void ClearState() { ResetState(); } From 82bd91527440dafe28fc841f06852778974f1843 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 12 Dec 2025 14:44:24 +0200 Subject: [PATCH 152/207] Fix out-of-bounds write while parsing command-line arguments --- Source/Engine/Main/MainUtil.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Main/MainUtil.h b/Source/Engine/Main/MainUtil.h index 668c69c3e..9e1a1503e 100644 --- a/Source/Engine/Main/MainUtil.h +++ b/Source/Engine/Main/MainUtil.h @@ -16,7 +16,7 @@ const Char* GetCommandLine(int argc, char* argv[]) const Char* cmdLine; if (length != 0) { - Char* str = (Char*)malloc(length * sizeof(Char)); + Char* str = (Char*)malloc((length + 1) * sizeof(Char)); cmdLine = str; for (int i = 1; i < argc; i++) { From 2a53d0a46217c76e98ed86a79d9e60eb4e735fd3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 13 Dec 2025 02:10:41 +0100 Subject: [PATCH 153/207] Fix crash on Visual Script missing asset ref after hot-reload in Editor #3823 --- Source/Editor/Content/Items/VisualScriptItem.cs | 11 +++++++++-- Source/Editor/Scripting/TypeUtils.cs | 2 ++ Source/Editor/Surface/SurfaceUtils.cs | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Content/Items/VisualScriptItem.cs b/Source/Editor/Content/Items/VisualScriptItem.cs index 8eea041ce..5b2f505d7 100644 --- a/Source/Editor/Content/Items/VisualScriptItem.cs +++ b/Source/Editor/Content/Items/VisualScriptItem.cs @@ -281,6 +281,13 @@ namespace FlaxEditor.Content private void CacheData() { + if (!_asset) + { + _parameters = Utils.GetEmptyArray(); + _methods = Utils.GetEmptyArray(); + _attributes = Utils.GetEmptyArray(); + return; + } if (_parameters != null) return; if (_asset.WaitForLoaded()) @@ -344,13 +351,13 @@ namespace FlaxEditor.Content } /// - public string Name => Path.GetFileNameWithoutExtension(_asset.Path); + public string Name => _asset ? Path.GetFileNameWithoutExtension(_asset.Path) : null; /// public string Namespace => string.Empty; /// - public string TypeName => JsonSerializer.GetStringID(_asset.ID); + public string TypeName => _asset ? JsonSerializer.GetStringID(_asset.ID) : null; /// public bool IsPublic => true; diff --git a/Source/Editor/Scripting/TypeUtils.cs b/Source/Editor/Scripting/TypeUtils.cs index 2451e1d87..3537b6218 100644 --- a/Source/Editor/Scripting/TypeUtils.cs +++ b/Source/Editor/Scripting/TypeUtils.cs @@ -406,6 +406,8 @@ namespace FlaxEngine.Utilities { if (type == ScriptType.Null) return null; + if (type.BaseType == null) + return type.Type; while (type.Type == null) type = type.BaseType; return type.Type; diff --git a/Source/Editor/Surface/SurfaceUtils.cs b/Source/Editor/Surface/SurfaceUtils.cs index d75efb5a0..7a19567fa 100644 --- a/Source/Editor/Surface/SurfaceUtils.cs +++ b/Source/Editor/Surface/SurfaceUtils.cs @@ -400,7 +400,7 @@ namespace FlaxEditor.Surface return scriptType.GetGenericTypeDefinition() == typeof(Dictionary<,>); } var managedType = TypeUtils.GetType(scriptType); - return !TypeUtils.IsDelegate(managedType); + return managedType != null && !TypeUtils.IsDelegate(managedType); } internal static bool IsValidVisualScriptFunctionType(ScriptType scriptType) @@ -408,7 +408,7 @@ namespace FlaxEditor.Surface if (scriptType.IsGenericType || scriptType.IsStatic || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true)) return false; var managedType = TypeUtils.GetType(scriptType); - return !TypeUtils.IsDelegate(managedType); + return managedType != null && !TypeUtils.IsDelegate(managedType); } internal static string GetVisualScriptTypeDescription(ScriptType type) From 0c887cd29eaa3a886ca6bf3e72e967b4111d960d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 13 Dec 2025 23:11:01 +0100 Subject: [PATCH 154/207] Use fix from #3830 in particle and anim graphs too --- Source/Engine/Animations/Graph/AnimGroup.Animation.cpp | 8 +++++++- .../Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp | 8 +++++++- .../MaterialGenerator/MaterialGenerator.Material.cpp | 5 +---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index fd62ec537..a1f2a4565 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -2553,9 +2553,15 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va // Function Input case 1: { + // Skip when graph is too small (eg. preview) and fallback with default value from the function graph + if (context.GraphStack.Count() < 2) + { + value = tryGetValue(node->TryGetBox(1), Value::Zero); + break; + } + // Find the function call AnimGraphNode* functionCallNode = nullptr; - ASSERT(context.GraphStack.Count() >= 2); Graph* graph; for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index a47e15275..1a7ba37d1 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -482,9 +482,15 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node, // Function Input case 1: { + // Skip when graph is too small (eg. preview) and fallback with default value from the function graph + if (context.GraphStack.Count() < 2) + { + value = tryGetValue(node->TryGetBox(1), Value::Zero); + break; + } + // Find the function call Node* functionCallNode = nullptr; - ASSERT(context.GraphStack.Count() >= 2); ParticleEmitterGraphCPU* graph; for (int32 i = context.CallStackSize - 1; i >= 0; i--) { diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp index bdade721e..09ac78e6b 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Material.cpp @@ -790,7 +790,7 @@ void MaterialGenerator::ProcessGroupFunction(Box* box, Node* node, Value& value) // Function Input case 1: { - // Check the stack count.If only 1 graph is present, + // Check the stack count. If only 1 graph is present, // we are processing the graph in isolation (e.g., in the Editor Preview). // In this case, we skip the caller-finding logic and use the node's default value. if (_graphStack.Count() < 2) @@ -802,9 +802,6 @@ void MaterialGenerator::ProcessGroupFunction(Box* box, Node* node, Value& value) // Find the function call Node* functionCallNode = nullptr; - - // The original ASSERT has been effectively replaced by the 'if' above. - //ASSERT(_graphStack.Count() >= 2); Graph* graph; for (int32 i = _callStack.Count() - 1; i >= 0; i--) { From 0e627577fc1d039942b1fa10bd2bb4292c2ec377 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 14 Dec 2025 15:00:44 -0600 Subject: [PATCH 155/207] Simplify code. --- Source/Engine/Level/Actors/AnimatedModel.cpp | 27 ++------------------ Source/Engine/Level/Actors/AnimatedModel.h | 16 ++---------- 2 files changed, 4 insertions(+), 39 deletions(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index a8c091eb9..991ac3e05 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -554,20 +554,7 @@ void AnimatedModel::StopSlotAnimation(const StringView& slotName, Animation* ani { for (auto& slot : GraphInstance.Slots) { - if (slot.Animation == anim && slot.Name == slotName) - { - //slot.Animation = nullptr; // TODO: make an immediate version of this method and set the animation to nullptr. - slot.Reset = true; - break; - } - } -} - -void AnimatedModel::StopSlotAnimation(const StringView& slotName) -{ - for (auto& slot : GraphInstance.Slots) - { - if (slot.Name == slotName) + if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName) { //slot.Animation = nullptr; // TODO: make an immediate version of this method and set the animation to nullptr. if (slot.Animation != nullptr) @@ -609,17 +596,7 @@ bool AnimatedModel::IsPlayingSlotAnimation(const StringView& slotName, Animation { for (auto& slot : GraphInstance.Slots) { - if (slot.Animation == anim && slot.Name == slotName && !slot.Pause) - return true; - } - return false; -} - -bool AnimatedModel::IsPlayingSlotAnimation(const StringView& slotName) -{ - for (auto& slot : GraphInstance.Slots) - { - if (slot.Name == slotName && !slot.Pause) + if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName && !slot.Pause) return true; } return false; diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index dc837c658..344f633cc 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -413,13 +413,7 @@ public: /// /// The name of the slot. /// The animation to stop. - API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim); - - /// - /// Stops the animation playback on the slot in Anim Graph. - /// - /// The name of the slot. - API_FUNCTION() void StopSlotAnimation(const StringView& slotName); + API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim = nullptr); /// /// Pauses all the animations playback on the all slots in Anim Graph. @@ -443,13 +437,7 @@ public: /// /// The name of the slot. /// The animation to check. - API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim); - - /// - /// Checks if the animation playback is active on the slot in Anim Graph (not paused). - /// - /// The name of the slot. - API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName); + API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim = nullptr); private: void ApplyRootMotion(const Transform& rootMotionDelta); From 5fdbed2b56f9d1f738f4ed499efafd316c1eb2f2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Dec 2025 22:41:00 +0100 Subject: [PATCH 156/207] Minor codestyle adjustments --- Source/Engine/Core/Collections/HashSetBase.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Core/Collections/HashSetBase.h b/Source/Engine/Core/Collections/HashSetBase.h index 58f1702c2..94bbd149d 100644 --- a/Source/Engine/Core/Collections/HashSetBase.h +++ b/Source/Engine/Core/Collections/HashSetBase.h @@ -408,14 +408,14 @@ protected: } else { - const int32 elementsCount = _elementsCount; // Rebuild entire table completely + const int32 elementsCount = _elementsCount; const int32 oldSize = _size; AllocationData oldAllocation; AllocationUtils::MoveToEmpty(oldAllocation, _allocation, oldSize, oldSize); _allocation.Allocate(_size); BucketType* data = _allocation.Get(); - for (int32 i = 0; i < _size; ++i) + for (int32 i = 0; i < oldSize; ++i) data[i]._state = HashSetBucketState::Empty; BucketType* oldData = oldAllocation.Get(); FindPositionResult pos; @@ -430,8 +430,8 @@ protected: // Grow and retry to handle pathological cases (eg. heavy collisions) EnsureCapacity(_size + 1, true); FindPosition(oldBucket.GetKey(), pos); + ASSERT(pos.FreeSlotIndex != -1); } - ASSERT(pos.FreeSlotIndex != -1); BucketType& bucket = _allocation.Get()[pos.FreeSlotIndex]; bucket = MoveTemp(oldBucket); } From 2b6339c05cd8a66e7964b29076c3b3053f19c891 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Dec 2025 22:58:53 +0100 Subject: [PATCH 157/207] Minor code cleanup --- .../Networking/NetworkReplicationHierarchy.h | 29 +++++++++++++++++++ .../Engine/Networking/NetworkReplicator.cpp | 10 +++---- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Networking/NetworkReplicationHierarchy.h b/Source/Engine/Networking/NetworkReplicationHierarchy.h index 9db851996..29cf0b30f 100644 --- a/Source/Engine/Networking/NetworkReplicationHierarchy.h +++ b/Source/Engine/Networking/NetworkReplicationHierarchy.h @@ -100,6 +100,35 @@ API_STRUCT(NoDefault, Namespace="FlaxEngine.Networking") struct FLAXENGINE_API N return Word0 + Word1 != 0; } + NetworkClientsMask operator&(const NetworkClientsMask& other) const + { + return { Word0 & other.Word0, Word1 & other.Word1 }; + } + + NetworkClientsMask operator|(const NetworkClientsMask& other) const + { + return { Word0 | other.Word0, Word1 | other.Word1 }; + } + + NetworkClientsMask operator~() const + { + return { ~Word0, ~Word1 }; + } + + NetworkClientsMask& operator|=(const NetworkClientsMask& other) + { + Word0 |= other.Word0; + Word1 |= other.Word1; + return *this; + } + + NetworkClientsMask& operator&=(const NetworkClientsMask& other) + { + Word0 &= other.Word0; + Word1 &= other.Word1; + return *this; + } + bool operator==(const NetworkClientsMask& other) const { return Word0 == other.Word0 && Word1 == other.Word1; diff --git a/Source/Engine/Networking/NetworkReplicator.cpp b/Source/Engine/Networking/NetworkReplicator.cpp index 02333aa64..e5a7d232e 100644 --- a/Source/Engine/Networking/NetworkReplicator.cpp +++ b/Source/Engine/Networking/NetworkReplicator.cpp @@ -729,14 +729,12 @@ void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients) } #if USE_NETWORK_REPLICATOR_CACHE - // Check if only newly joined clients are missing this data to avoid resending it to everyone - NetworkClientsMask missingClients; - missingClients.Word0 = targetClients.Word0 & ~item.RepCache.Mask.Word0; - missingClients.Word1 = targetClients.Word1 & ~item.RepCache.Mask.Word1; - // Process replication cache to skip sending object data if it didn't change if (item.RepCache.Data.Length() == size && Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0) { + // Check if only newly joined clients are missing this data to avoid resending it to everyone + NetworkClientsMask missingClients = targetClients & ~item.RepCache.Mask; + // If data is the same and only the client set changed, replicate to missing clients only if (!missingClients) return; @@ -2330,7 +2328,7 @@ void NetworkInternal::OnNetworkMessageObjectDespawn(NetworkEvent& event, Network } else { - // If this client never had the object (eg. it was targeted to other clients only), drop the message quietly. + // If this client never had the object (eg. it was targeted to other clients only), drop the message quietly DespawnedObjects.Add(objectId); NETWORK_REPLICATOR_LOG(Warning, "[NetworkReplicator] Failed to despawn object {}", objectId); } From 056de752ed02d9d145ef7eee61e08267f589d63f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 14 Dec 2025 23:03:34 +0100 Subject: [PATCH 158/207] Add docs --- Source/Engine/Level/Actors/AnimatedModel.cpp | 2 +- Source/Engine/Level/Actors/AnimatedModel.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 991ac3e05..343710183 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -574,7 +574,7 @@ void AnimatedModel::PauseSlotAnimation(const StringView& slotName, Animation* an { for (auto& slot : GraphInstance.Slots) { - if (slot.Animation == anim && slot.Name == slotName) + if ((slot.Animation == anim || anim == nullptr) && slot.Name == slotName) { slot.Pause = true; break; diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 344f633cc..a520d6723 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -412,7 +412,7 @@ public: /// Stops the animation playback on the slot in Anim Graph. /// /// The name of the slot. - /// The animation to stop. + /// The animation to check. Null to use slot name only. API_FUNCTION() void StopSlotAnimation(const StringView& slotName, Animation* anim = nullptr); /// @@ -424,8 +424,8 @@ public: /// Pauses the animation playback on the slot in Anim Graph. /// /// The name of the slot. - /// The animation to pause. - API_FUNCTION() void PauseSlotAnimation(const StringView& slotName, Animation* anim); + /// The animation to check. Null to use slot name only. + API_FUNCTION() void PauseSlotAnimation(const StringView& slotName, Animation* anim = nullptr); /// /// Checks if any animation playback is active on any slot in Anim Graph (not paused). @@ -436,7 +436,7 @@ public: /// Checks if the animation playback is active on the slot in Anim Graph (not paused). /// /// The name of the slot. - /// The animation to check. + /// The animation to check. Null to use slot name only. API_FUNCTION() bool IsPlayingSlotAnimation(const StringView& slotName, Animation* anim = nullptr); private: From b70ab01308fec3eca83b63cd342d7f895e7374ab Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 16 Dec 2025 01:18:30 +0200 Subject: [PATCH 159/207] Fix duplicate properties for default configuration in C# projects --- .../Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs index 671cfb5e8..ce3903874 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs @@ -132,6 +132,8 @@ namespace Flax.Build.Projects.VisualStudio // Configurations foreach (var configuration in project.Configurations) { + if (configuration.Equals(defaultConfiguration)) + continue; WriteConfiguration(project, csProjectFileContent, projectDirectory, configuration); } From 1327fa40cbeefe3ecb86d4d42130bb0d4bfdc5a2 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 16 Dec 2025 01:20:49 +0200 Subject: [PATCH 160/207] Run editor with selected project configuration in launchSettings.json --- .../Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs | 1 + .../Projects/VisualStudio/VisualStudioProjectGenerator.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs index ce3903874..ffc95e047 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/CSSDKProjectGenerator.cs @@ -311,6 +311,7 @@ namespace Flax.Build.Projects.VisualStudio } csProjectFileContent.AppendLine(string.Format(" {0}\\{1}.CSharp.xml", outputPath, project.BaseName)); csProjectFileContent.AppendLine(" true"); + csProjectFileContent.AppendLine(string.Format(" {0}", configuration.ConfigurationName)); csProjectFileContent.AppendLine(" "); diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index d657f6247..34dc3f380 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -630,7 +630,7 @@ namespace Flax.Build.Projects.VisualStudio { var profiles = new Dictionary(); var profile = new StringBuilder(); - var configuration = "Development"; + var configuration = "$(FlaxConfiguration)"; var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), configuration, $"FlaxEditor{Utilities.GetPlatformExecutableExt()}")).Replace('\\', '/'); var workspacePath = Utilities.NormalizePath(solutionDirectory).Replace('\\', '/'); foreach (var project in projects) From f280412ef474456705b109c0a8e0c514bb813b14 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Dec 2025 16:04:56 +0100 Subject: [PATCH 161/207] Remove `NeedsHitNormal`/`HitNormal` feature from `GlobalSDFTrace` to simplify code --- Source/Shaders/GI/GlobalSurfaceAtlas.shader | 2 - Source/Shaders/GlobalSignDistanceField.hlsl | 15 ------- Source/Shaders/GlobalSignDistanceField.shader | 39 ++++++++++++------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/Source/Shaders/GI/GlobalSurfaceAtlas.shader b/Source/Shaders/GI/GlobalSurfaceAtlas.shader index 6778a7cd7..6930107d1 100644 --- a/Source/Shaders/GI/GlobalSurfaceAtlas.shader +++ b/Source/Shaders/GI/GlobalSurfaceAtlas.shader @@ -328,7 +328,6 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target float3 viewRay = lerp(lerp(ViewFrustumWorldRays[3], ViewFrustumWorldRays[0], input.TexCoord.x), lerp(ViewFrustumWorldRays[2], ViewFrustumWorldRays[1], input.TexCoord.x), 1 - input.TexCoord.y).xyz; viewRay = normalize(viewRay - ViewWorldPos); trace.Init(ViewWorldPos, viewRay, ViewNearPlane, ViewFarPlane); - trace.NeedsHitNormal = true; GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace); float3 color; @@ -337,7 +336,6 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target // Sample Global Surface Atlas at the hit location float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, hit); color = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, GlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hit.GetHitPosition(trace), -viewRay, surfaceThreshold).rgb; - //color = hit.HitNormal * 0.5f + 0.5f; } else { diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index 8075c081d..67a48e183 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -33,7 +33,6 @@ struct GlobalSDFTrace float3 WorldDirection; float MaxDistance; float StepScale; - bool NeedsHitNormal; void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance, float stepScale = 1.0f) { @@ -42,7 +41,6 @@ struct GlobalSDFTrace MinDistance = minDistance; MaxDistance = maxDistance; StepScale = stepScale; - NeedsHitNormal = false; } }; @@ -326,19 +324,6 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D hit.HitTime = max(stepTime + stepDistance - minSurfaceThickness, 0.0f); hit.HitCascade = cascade; hit.HitSDF = stepDistance; - if (trace.NeedsHitNormal) - { - // Calculate hit normal from SDF gradient - float texelOffset = 1.0f / data.Resolution; - ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV); - float xp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = tex.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; - hit.HitNormal = normalize(float3(xp - xn, yp - yn, zp - zn)); - } } // Move forward diff --git a/Source/Shaders/GlobalSignDistanceField.shader b/Source/Shaders/GlobalSignDistanceField.shader index 461dba08d..fe4bafda5 100644 --- a/Source/Shaders/GlobalSignDistanceField.shader +++ b/Source/Shaders/GlobalSignDistanceField.shader @@ -311,26 +311,39 @@ float4 PS_Debug(Quad_VS2PS input) : SV_Target float3 viewRay = lerp(lerp(ViewFrustumWorldRays[3], ViewFrustumWorldRays[0], input.TexCoord.x), lerp(ViewFrustumWorldRays[2], ViewFrustumWorldRays[1], input.TexCoord.x), 1 - input.TexCoord.y).xyz; viewRay = normalize(viewRay - ViewWorldPos); trace.Init(ViewWorldPos, viewRay, ViewNearPlane, ViewFarPlane); - trace.NeedsHitNormal = true; GlobalSDFHit hit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, trace); // Debug draw - float3 color = saturate(hit.StepsCount / 80.0f).xxx; - if (!hit.IsHit()) - color.rg *= 0.4f; -#if 0 - else - { + float3 color = saturate(hit.StepsCount / 50.0f).xxx; + if (hit.IsHit()) + { +#if 1 + float3 hitPosition = hit.GetHitPosition(trace); + float hitSDF; + float3 hitNormal = SampleGlobalSDFGradient(GlobalSDF, GlobalSDFTex, GlobalSDFMip, hitPosition, hitSDF, hit.HitCascade); +#if 1 + // Composite step count with SDF normals + //color.rgb *= saturate(normalize(hitNormal) * 0.5f + 0.7f) + 0.3f; + color = lerp(normalize(hitNormal) * 0.5f + 0.5f, 1 - color, saturate(hit.StepsCount / 80.0f)); +#else // Debug draw SDF normals - color.rgb = normalize(hit.HitNormal) * 0.5f + 0.5f; - } -#elif 1 + color = normalize(hitNormal) * 0.5f + 0.5f; +#endif +#else + // Heatmap with step count + if (hit.StepsCount > 40) + color = float3(saturate(hit.StepsCount / 80.0f), 0, 0); + else if (hit.StepsCount > 20) + color = float3(saturate(hit.StepsCount / 40.0f).xx, 0); + else + color = float3(0, saturate(hit.StepsCount / 20.0f), 0); +#endif + } else { - // Composite with SDF normals - color.rgb *= saturate(normalize(hit.HitNormal) * 0.5f + 0.7f) + 0.1f; + // Bluish sky + color.rg *= 0.4f; } -#endif return float4(color, 1); } From 3c8b80152b7dc4a3730ca9838bcae3c9aa73eb20 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Dec 2025 16:05:49 +0100 Subject: [PATCH 162/207] Fix sampling Global SDF gradient at lower border --- Source/Shaders/GlobalSignDistanceField.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index 67a48e183..1ea27371e 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -77,8 +77,8 @@ void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldP void ClampGlobalSDFTextureGradientUV(const GlobalSDFData data, uint cascade, float texelOffset, inout float3 textureUV) { float cascadeSizeUV = 1.0f / data.CascadesCount; - float cascadeUVStart = cascadeSizeUV * cascade + texelOffset; - float cascadeUVEnd = cascadeUVStart + cascadeSizeUV - texelOffset * 3; + float cascadeUVStart = cascadeSizeUV * cascade + texelOffset * 2; + float cascadeUVEnd = cascadeUVStart + cascadeSizeUV - texelOffset * 4; textureUV.x = clamp(textureUV.x, cascadeUVStart, cascadeUVEnd); } From 779e8e71694890e19280fc36433a072799d5bbea Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Dec 2025 16:07:46 +0100 Subject: [PATCH 163/207] Optimize Global SDF tracing to not scale steps nearby geometry and properly trace mip cascade --- Source/Shaders/GlobalSignDistanceField.hlsl | 77 ++++++++++----------- 1 file changed, 36 insertions(+), 41 deletions(-) diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index 1ea27371e..2fcf1c88b 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -32,15 +32,13 @@ struct GlobalSDFTrace float MinDistance; float3 WorldDirection; float MaxDistance; - float StepScale; - void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance, float stepScale = 1.0f) + void Init(float3 worldPosition, float3 worldDirection, float minDistance, float maxDistance) { WorldPosition = worldPosition; WorldDirection = worldDirection; MinDistance = minDistance; MaxDistance = maxDistance; - StepScale = stepScale; } }; @@ -73,7 +71,18 @@ void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldP textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // Cascades are placed next to each other on X axis } -// Clamps Global SDF cascade UV to ensure it can be sued for gradient sampling (clamps first and last pixels). +void GetGlobalSDFCascadeUV(const GlobalSDFData data, uint cascade, float3 worldPosition, out float3 cascadeUV, out float3 textureUV, out float3 textureMipUV) +{ + float4 cascadePosDistance = data.CascadePosDistance[cascade]; + float3 posInCascade = worldPosition - cascadePosDistance.xyz; + float cascadeSize = cascadePosDistance.w * 2; + cascadeUV = saturate(posInCascade / cascadeSize + 0.5f); + textureUV = float3(((float)cascade + cascadeUV.x) / (float)data.CascadesCount, cascadeUV.y, cascadeUV.z); // Cascades are placed next to each other on X axis + float halfTexelOffsetMip = (GLOBAL_SDF_RASTERIZE_MIP_FACTOR * 0.5f) / data.Resolution; + textureMipUV = textureUV + float3(halfTexelOffsetMip / (float)data.CascadesCount, halfTexelOffsetMip, halfTexelOffsetMip); // Mipmaps are offset by half texel to sample correctly +} + +// Clamps Global SDF cascade UV to ensure it can be used for gradient sampling (clamps first and last pixels). void ClampGlobalSDFTextureGradientUV(const GlobalSDFData data, uint cascade, float texelOffset, inout float3 textureUV) { float cascadeSizeUV = 1.0f / data.CascadesCount; @@ -142,13 +151,13 @@ float SampleGlobalSDF(const GlobalSDFData data, Texture3D tex, Text startCascade = min(startCascade, data.CascadesCount - 1); for (uint cascade = startCascade; cascade < data.CascadesCount; cascade++) { - float3 cascadeUV, textureUV; - GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeUV, textureUV); + float3 cascadeUV, textureUV, textureMipUV; + GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeUV, textureUV, textureMipUV); float voxelSize = data.CascadeVoxelSize[cascade]; float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; - float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0); + float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureMipUV, 0); if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1)) { distance = distanceMip * maxDistanceMip; @@ -206,13 +215,13 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D startCascade = min(startCascade, data.CascadesCount - 1); for (uint cascade = startCascade; cascade < data.CascadesCount; cascade++) { - float3 cascadeUV, textureUV; - GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeUV, textureUV); + float3 cascadeUV, textureUV, textureMipUV; + GetGlobalSDFCascadeUV(data, cascade, worldPosition, cascadeUV, textureUV, textureMipUV); float voxelSize = data.CascadeVoxelSize[cascade]; float chunkSize = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; float chunkMargin = voxelSize * (GLOBAL_SDF_CHUNK_MARGIN_SCALE * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN); float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; - float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceMip; + float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureMipUV, 0) * maxDistanceMip; if (distanceMip < chunkSize && all(cascadeUV > 0) && all(cascadeUV < 1)) { float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; @@ -234,13 +243,13 @@ float3 SampleGlobalSDFGradient(const GlobalSDFData data, Texture3D { distance = distanceMip; float texelOffset = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR / data.Resolution; - ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureUV); - float xp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x + texelOffset, textureUV.y, textureUV.z), 0).x; - float xn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x - texelOffset, textureUV.y, textureUV.z), 0).x; - float yp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y + texelOffset, textureUV.z), 0).x; - float yn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y - texelOffset, textureUV.z), 0).x; - float zp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z + texelOffset), 0).x; - float zn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureUV.x, textureUV.y, textureUV.z - texelOffset), 0).x; + ClampGlobalSDFTextureGradientUV(data, cascade, texelOffset, textureMipUV); + float xp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x + texelOffset, textureMipUV.y, textureMipUV.z), 0).x; + float xn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x - texelOffset, textureMipUV.y, textureMipUV.z), 0).x; + float yp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x, textureMipUV.y + texelOffset, textureMipUV.z), 0).x; + float yn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x, textureMipUV.y - texelOffset, textureMipUV.z), 0).x; + float zp = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x, textureMipUV.y, textureMipUV.z + texelOffset), 0).x; + float zn = mip.SampleLevel(GLOBAL_SDF_SAMPLER, float3(textureMipUV.x, textureMipUV.y, textureMipUV.z - texelOffset), 0).x; gradient = float3(xp - xn, yp - yn, zp - zn) * maxDistanceMip; } break; @@ -291,33 +300,19 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D for (; step < 250 && stepTime < intersections.y && hit.HitTime < 0.0f; step++) { float3 stepPosition = trace.WorldPosition + trace.WorldDirection * stepTime; - float stepScale = trace.StepScale; // Sample SDF - float stepDistance, voxelSizeScale = (float)GLOBAL_SDF_RASTERIZE_MIP_FACTOR; - float3 cascadeUV, textureUV; - GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeUV, textureUV); - float distanceMip = mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceMip; - if (distanceMip < chunkSize) - { - stepDistance = distanceMip; - float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; - if (distanceTex < chunkMargin) - { - stepDistance = distanceTex; - voxelSizeScale = 1.0f; - stepScale *= 0.63f; // Perform smaller steps nearby geometry - } - } - else - { - // Assume no SDF nearby so perform a jump to the next chunk - stepDistance = chunkSize; - voxelSizeScale = 1.0f; - } + float stepDistance; + float3 cascadeUV, textureUV, textureMipUV; + GetGlobalSDFCascadeUV(data, cascade, stepPosition, cascadeUV, textureUV, textureMipUV); + stepDistance = min(mip.SampleLevel(GLOBAL_SDF_SAMPLER, textureMipUV, 0) * maxDistanceMip, chunkSize); + float distanceTex = tex.SampleLevel(GLOBAL_SDF_SAMPLER, textureUV, 0) * maxDistanceTex; + FLATTEN + if (distanceTex < chunkMargin) + stepDistance = distanceTex; // Detect surface hit - float minSurfaceThickness = voxelSizeScale * voxelExtent * saturate(stepTime / voxelSize); + float minSurfaceThickness = voxelExtent * saturate(stepTime / voxelSize); if (stepDistance < minSurfaceThickness) { // Surface hit @@ -327,7 +322,7 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D } // Move forward - stepTime += max(stepDistance * stepScale, voxelSize); + stepTime += max(stepDistance, voxelSize); } hit.StepsCount += step; } From 8bf51512ac4a6f95ab02562488e091a75d7bde00 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Dec 2025 16:08:20 +0100 Subject: [PATCH 164/207] Fix Global SDF trace loop limit down to prevent too long shader execution in extreme scenarios --- Source/Shaders/GlobalSignDistanceField.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Shaders/GlobalSignDistanceField.hlsl b/Source/Shaders/GlobalSignDistanceField.hlsl index 2fcf1c88b..c1bd4250b 100644 --- a/Source/Shaders/GlobalSignDistanceField.hlsl +++ b/Source/Shaders/GlobalSignDistanceField.hlsl @@ -297,7 +297,7 @@ GlobalSDFHit RayTraceGlobalSDF(const GlobalSDFData data, Texture3D float maxDistanceTex = data.CascadeMaxDistanceTex[cascade]; float maxDistanceMip = data.CascadeMaxDistanceMip[cascade]; LOOP - for (; step < 250 && stepTime < intersections.y && hit.HitTime < 0.0f; step++) + for (; step < 100 && stepTime < intersections.y && hit.HitTime < 0.0f; step++) { float3 stepPosition = trace.WorldPosition + trace.WorldDirection * stepTime; From 0e76585709946a9184bdba0cceabd501206d1ee4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 16 Dec 2025 23:56:45 +0100 Subject: [PATCH 165/207] Fix DDGI iradiance to use debanding by applying quantization error to reduce yellowish artifacts due to `R11G11B10` format --- Source/Engine/Graphics/RenderTools.cpp | 34 +++++++++++++++++++ Source/Engine/Graphics/RenderTools.h | 3 ++ .../GI/DynamicDiffuseGlobalIllumination.cpp | 4 +++ Source/Shaders/GI/DDGI.shader | 6 ++++ Source/Shaders/Noise.hlsl | 20 +++++++++++ 5 files changed, 67 insertions(+) diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp index b0d587c8d..effbe6e1b 100644 --- a/Source/Engine/Graphics/RenderTools.cpp +++ b/Source/Engine/Graphics/RenderTools.cpp @@ -620,6 +620,40 @@ void RenderTools::ComputeSphereModelDrawMatrix(const RenderView& view, const Flo resultIsViewInside = Float3::DistanceSquared(view.Position, position) < Math::Square(radius * 1.1f); // Manually tweaked bias } +Float3 RenderTools::GetColorQuantizationError(PixelFormat format) +{ + Float3 mantissaBits; + switch (format) + { + case PixelFormat::R11G11B10_Float: + mantissaBits = Float3(6, 6, 5); + break; + case PixelFormat::R10G10B10A2_UNorm: + mantissaBits = Float3(10, 10, 10); + break; + case PixelFormat::R16G16B16A16_Float: + mantissaBits = Float3(16, 16, 16); + break; + case PixelFormat::R32G32B32A32_Float: + mantissaBits = Float3(23, 23, 23); + break; + case PixelFormat::R9G9B9E5_SharedExp: + mantissaBits = Float3(5, 6, 5); + break; + case PixelFormat::R8G8B8A8_UNorm: + case PixelFormat::B8G8R8A8_UNorm: + mantissaBits = Float3(8, 8, 8); + break; + default: + return Float3::Zero; + } + return { + Math::Pow(0.5f, mantissaBits.X), + Math::Pow(0.5f, mantissaBits.Y), + Math::Pow(0.5f, mantissaBits.Z) + }; +} + int32 MipLevelsCount(int32 width) { int32 result = 1; diff --git a/Source/Engine/Graphics/RenderTools.h b/Source/Engine/Graphics/RenderTools.h index 18357a13a..5f0dc23dc 100644 --- a/Source/Engine/Graphics/RenderTools.h +++ b/Source/Engine/Graphics/RenderTools.h @@ -140,6 +140,9 @@ public: static void CalculateTangentFrame(Float3& resultNormal, Float4& resultTangent, const Float3& normal, const Float3& tangent); static void ComputeSphereModelDrawMatrix(const RenderView& view, const Float3& position, float radius, Matrix& resultWorld, bool& resultIsViewInside); + + // Calculates error for a given render target format to reduce floating-point precision artifacts via QuantizeColor (from Noise.hlsl). + static Float3 GetColorQuantizationError(PixelFormat format); }; // Calculates mip levels count for a texture 1D. diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index a954cf31f..95bcb18e9 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -68,6 +68,8 @@ GPU_CB_STRUCT(Data0 { Int4 ProbeScrollClears[4]; Float3 ViewDir; float Padding1; + Float3 QuantizationError; + int32 FrameIndexMod8; }); GPU_CB_STRUCT(Data1 { @@ -535,6 +537,8 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont data.TemporalTime = renderContext.List->Setup.UseTemporalAAJitter ? RenderTools::ComputeTemporalTime() : 0.0f; data.ViewDir = renderContext.View.Direction; data.SkyboxIntensity = renderContext.List->Sky ? renderContext.List->Sky->GetIndirectLightingIntensity() : 1.0f; + data.QuantizationError = RenderTools::GetColorQuantizationError(ddgiData.ProbesIrradiance->Format()); + data.FrameIndexMod8 = (int32)(Engine::FrameCount % 8); GBufferPass::SetInputs(renderContext.View, data.GBuffer); context->UpdateCB(_cb0, &data); context->BindCB(0, _cb0); diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index daad2018d..3dbfd1ed5 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -13,6 +13,7 @@ #include "./Flax/Math.hlsl" #include "./Flax/Noise.hlsl" #include "./Flax/Quaternion.hlsl" +#include "./Flax/MonteCarlo.hlsl" #include "./Flax/GlobalSignDistanceField.hlsl" #include "./Flax/GI/GlobalSurfaceAtlas.hlsl" #include "./Flax/GI/DDGI.hlsl" @@ -42,6 +43,8 @@ float TemporalTime; int4 ProbeScrollClears[4]; float3 ViewDir; float Padding1; +float3 QuantizationError; +uint FrameIndexMod8; META_CB_END META_CB_BEGIN(1, Data1) @@ -704,6 +707,9 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ result = float4(lerp(result.rg, previous.rg, historyWeight), 0.0f, 1.0f); #endif + // Write output irradiance (apply quantization error to reduce yellowish artifacts due to R11G11B10 format) + float noise = InterleavedGradientNoise(octahedralCoords, FrameIndexMod8); + result.rgb = QuantizeColor(result.rgb, noise, QuantizationError); RWOutput[outputCoords] = result; GroupMemoryBarrierWithGroupSync(); diff --git a/Source/Shaders/Noise.hlsl b/Source/Shaders/Noise.hlsl index dc35f1efc..df5a041fa 100644 --- a/Source/Shaders/Noise.hlsl +++ b/Source/Shaders/Noise.hlsl @@ -54,6 +54,26 @@ float2 PerlinNoiseFade(float2 t) return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } +// "Next Generation Post Processing in Call of Duty: Advanced Warfare" +// http://advances.realtimerendering.com/s2014/index.html +float InterleavedGradientNoise(float2 uv, uint frameCount) +{ + const float2 magicFrameScale = float2(47, 17) * 0.695; + uv += frameCount * magicFrameScale; + const float3 magic = float3(0.06711056, 0.00583715, 52.9829189); + return frac(magic.z * frac(dot(uv, magic.xy))); +} + +// Removes error from the color to properly store it in lower precision formats (error = 2^(-mantissaBits)) +float3 QuantizeColor(float3 color, float noise, float3 error) +{ + float3 delta = color * error; + delta.x = asfloat(asuint(delta.x) & ~0x007fffff); + delta.y = asfloat(asuint(delta.y) & ~0x007fffff); + delta.z = asfloat(asuint(delta.z) & ~0x007fffff); + return color + delta * noise; +} + float rand2dTo1d(float2 value, float2 dotDir = float2(12.9898, 78.233)) { // https://www.ronja-tutorials.com/post/024-white-noise/ From 8db29a30e0372f9f07281aea153ec2a7a931a2d4 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 20 Apr 2025 01:11:29 +0300 Subject: [PATCH 166/207] Fix VC++ project file building on macOS Rider --- .../Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs index 1a52274a7..371cf3933 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs @@ -115,6 +115,7 @@ namespace Flax.Build.Projects.VisualStudio if (Version >= VisualStudioVersion.VisualStudio2022) vcProjectFileContent.AppendLine(" false"); vcProjectFileContent.AppendLine(" ./"); + vcProjectFileContent.AppendLine(" ./"); vcProjectFileContent.AppendLine(" "); // Default properties @@ -377,9 +378,9 @@ namespace Flax.Build.Projects.VisualStudio vcUserFileContent.AppendLine(""); - if (platforms.Any(x => x.Target == TargetPlatform.Linux)) + if (platforms.Any(x => x.Target == TargetPlatform.Linux || x.Target == TargetPlatform.Mac)) { - // Override MSBuild .targets file with one that runs NMake commands (workaround for Rider on Linux) + // Override MSBuild .targets file with one that runs NMake commands (workaround for Rider not finding "Microsoft.Cpp.Default.props" file) var cppTargetsFileContent = new StringBuilder(); cppTargetsFileContent.AppendLine(""); cppTargetsFileContent.AppendLine(" "); From 518f9dc9dc5598eb10a31c77e76cd6174c7b5b84 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 17 Dec 2025 02:14:22 +0200 Subject: [PATCH 167/207] Launch editor when debugging VC++ projects in Rider Linux/macOS --- .../Projects/VisualStudio/VCProjectGenerator.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs index 371cf3933..290d194cf 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VCProjectGenerator.cs @@ -380,6 +380,10 @@ namespace Flax.Build.Projects.VisualStudio if (platforms.Any(x => x.Target == TargetPlatform.Linux || x.Target == TargetPlatform.Mac)) { + var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), "$(Configuration.Split('.')[2])", $"FlaxEditor{Utilities.GetPlatformExecutableExt()}")).Replace('\\', '/'); + var debuggerProjectPath = Globals.Project.Name == "Flax" ? "" : Globals.Project.ProjectFolderPath; + var debuggerWorkingDirectory = Globals.Project.ProjectFolderPath; + // Override MSBuild .targets file with one that runs NMake commands (workaround for Rider not finding "Microsoft.Cpp.Default.props" file) var cppTargetsFileContent = new StringBuilder(); cppTargetsFileContent.AppendLine(""); @@ -396,6 +400,12 @@ namespace Flax.Build.Projects.VisualStudio cppTargetsFileContent.AppendLine(" "); cppTargetsFileContent.AppendLine(" $(RootNamespace)$(Configuration.Split('.')[0])"); cppTargetsFileContent.AppendLine(" $(OutDir)/$(TargetName)$(TargetExt)"); + if (!string.IsNullOrEmpty(debuggerProjectPath)) + cppTargetsFileContent.AppendLine(string.Format(" -project \"{0}\"", debuggerProjectPath)); + else + cppTargetsFileContent.AppendLine(" "); + cppTargetsFileContent.AppendLine(string.Format(" {0}", editorPath)); + cppTargetsFileContent.AppendLine(string.Format(" {0}", debuggerWorkingDirectory)); cppTargetsFileContent.AppendLine(" "); cppTargetsFileContent.AppendLine(""); From f14ba6ace6e88f2b56e982e5ada126a7641f9c55 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Wed, 17 Dec 2025 02:15:09 +0200 Subject: [PATCH 168/207] Fix Flax C#-project launchSettings.json to launch without project --- .../Projects/VisualStudio/VisualStudioProjectGenerator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs index d657f6247..6a573e0fd 100644 --- a/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs +++ b/Source/Tools/Flax.Build/Projects/VisualStudio/VisualStudioProjectGenerator.cs @@ -633,6 +633,7 @@ namespace Flax.Build.Projects.VisualStudio var configuration = "Development"; var editorPath = Utilities.NormalizePath(Path.Combine(Globals.EngineRoot, Platform.GetEditorBinaryDirectory(), configuration, $"FlaxEditor{Utilities.GetPlatformExecutableExt()}")).Replace('\\', '/'); var workspacePath = Utilities.NormalizePath(solutionDirectory).Replace('\\', '/'); + var args = Globals.Project.Name == "Flax" ? "" : $"-project \\\"{workspacePath}\\\""; foreach (var project in projects) { if (project.Type == TargetType.DotNetCore) @@ -645,7 +646,7 @@ namespace Flax.Build.Projects.VisualStudio profile.AppendLine(" \"commandName\": \"Executable\","); profile.AppendLine($" \"workingDirectory\": \"{workspacePath}\","); profile.AppendLine($" \"executablePath\": \"{editorPath}\","); - profile.AppendLine($" \"commandLineArgs\": \"-project \\\"{workspacePath}\\\"\","); + profile.AppendLine($" \"commandLineArgs\": \"{args}\","); profile.AppendLine(" \"nativeDebugging\": false"); profile.Append(" }"); if (profiles.ContainsKey(path)) From 4504d0ae96a274951742ec50fa0749f6de8a0499 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 16 Dec 2025 18:22:26 -0600 Subject: [PATCH 169/207] Fix compute distance crash if no closest point param is defined. --- Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp index 18929ab73..3edfaa0c6 100644 --- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp +++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp @@ -2787,7 +2787,8 @@ float PhysicsBackend::ComputeShapeSqrDistanceToPoint(void* shape, const Vector3& #if USE_LARGE_WORLDS PxVec3 closestPointPx; float result = PxGeometryQuery::pointDistance(C2P(point), shapePhysX->getGeometry(), trans, &closestPointPx); - *closestPoint = P2C(closestPointPx); + if (closestPoint) + *closestPoint = P2C(closestPointPx); return result; #else return PxGeometryQuery::pointDistance(C2P(point), shapePhysX->getGeometry(), trans, (PxVec3*)closestPoint); From 2a959d0531bd034f2755983b9d826c40695c87c1 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sun, 12 Oct 2025 14:42:22 +0300 Subject: [PATCH 170/207] Support Visual Studio 2026 as a generator for CMake dependencies --- .../Tools/Flax.Build/Deploy/VCEnvironment.cs | 6 ++++- Source/Tools/Flax.Build/Deps/Dependency.cs | 26 ++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs b/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs index cfcbf9866..afe5c8e3c 100644 --- a/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs +++ b/Source/Tools/Flax.Build/Deploy/VCEnvironment.cs @@ -241,7 +241,11 @@ namespace Flax.Deploy if (!File.Exists(solutionFile)) { - throw new Exception(string.Format("Unable to build solution {0}. Solution file not found.", solutionFile)); + // CMake VS2026 generator prefers .slnx solution files, just swap the extension for CMake dependencies + if (File.Exists(Path.ChangeExtension(solutionFile, "slnx"))) + solutionFile = Path.ChangeExtension(solutionFile, "slnx"); + else + throw new Exception(string.Format("Unable to build solution {0}. Solution file not found.", solutionFile)); } string cmdLine = string.Format("\"{0}\" /m /t:Restore,Build /p:Configuration=\"{1}\" /p:Platform=\"{2}\" {3} /nologo", solutionFile, buildConfig, buildPlatform, Verbosity); diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index 43cdcc146..010a45175 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -47,6 +47,24 @@ namespace Flax.Deps /// protected static TargetPlatform BuildPlatform => Platform.BuildPlatform.Target; + + private static Version? _cmakeVersion; + protected static Version CMakeVersion + { + get + { + if (_cmakeVersion == null) + { + var versionOutput = Utilities.ReadProcessOutput("cmake", "--version"); + var versionStart = versionOutput.IndexOf("cmake version ") + "cmake version ".Length; + var versionEnd = versionOutput.IndexOfAny(['-', '\n', '\r'], versionStart); // End of line or dash before Git hash + var versionString = versionOutput.Substring(versionStart, versionEnd - versionStart); + _cmakeVersion = new Version(versionString); + } + return _cmakeVersion; + } + } + /// /// Gets the platforms list supported by this dependency to build on the current build platform (based on ). /// @@ -309,7 +327,13 @@ namespace Flax.Deps break; default: throw new InvalidArchitectureException(architecture); } - cmdLine = string.Format("CMakeLists.txt -G \"Visual Studio 17 2022\" -A {0}", arch); + if (CMakeVersion.Major > 4 || (CMakeVersion.Major == 4 && CMakeVersion.Minor >= 2)) + { + // This generates both .sln and .slnx solution files + cmdLine = string.Format("CMakeLists.txt -G \"Visual Studio 17 2022\" -G \"Visual Studio 18 2026\" -A {0}", arch); + } + else + cmdLine = string.Format("CMakeLists.txt -G \"Visual Studio 17 2022\" -A {0}", arch); break; } case TargetPlatform.PS4: From d64c28f6726387e56cbfa344a1200e1122946156 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 19 Dec 2025 00:03:41 +0200 Subject: [PATCH 171/207] Fix compilation errors with `/permissive-` standard conformance mode --- Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp | 2 +- Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h | 2 +- Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp index a05585696..bce8271db 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUTextureDX11.cpp @@ -553,7 +553,7 @@ void GPUTextureDX11::initHandles() if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format)) { PixelFormat stencilFormat; - switch (_dxgiFormatDSV) + switch (static_cast(_dxgiFormatDSV)) { case PixelFormat::D24_UNorm_S8_UInt: srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h index bb5d53458..e9c1cacaa 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h @@ -210,7 +210,7 @@ public: /// The graphics device. /// The resource name. GPUResourceDX12(GPUDeviceDX12* device, const StringView& name) - : GPUResourceBase(device, name) + : GPUResourceBase(device, name) { } }; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp index a4fbc683d..eada546e3 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp @@ -732,7 +732,7 @@ void GPUTextureDX12::initHandles() if (useDSV && useSRV && PixelFormatExtensions::HasStencil(format)) { PixelFormat stencilFormat; - switch (_dxgiFormatDSV) + switch (static_cast(_dxgiFormatDSV)) { case PixelFormat::D24_UNorm_S8_UInt: srDesc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT; From c2c92eba824bf52ab1b1ca82f3948b22495436de Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 19 Dec 2025 00:08:24 +0100 Subject: [PATCH 172/207] Adjust 0e76585709946a9184bdba0cceabd501206d1ee4 to not affect depth --- Source/Shaders/GI/DDGI.hlsl | 11 ++++++----- Source/Shaders/GI/DDGI.shader | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index c116b597a..9d8f5509c 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -168,7 +168,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Loop over the closest probes to accumulate their contributions float4 irradiance = float4(0, 0, 0, 0); - const int3 SearchAxisMasks[3] = { int3(1, 0, 0), int3(0, 1, 0), int3(0, 0, 1) }; + const int3 SearchAxes[3] = { int3(1, 0, 0), int3(0, 1, 0), int3(0, 0, 1) }; for (uint i = 0; i < 8; i++) { uint3 probeCoordsOffset = uint3(i, i >> 1, i >> 2) & 1; @@ -182,10 +182,11 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes { // Search nearby probes to find any nearby GI sample for (int searchDistance = 1; searchDistance < 3 && probeState == DDGI_PROBE_STATE_INACTIVE; searchDistance++) + { for (uint searchAxis = 0; searchAxis < 3; searchAxis++) { - int searchAxisDir = probeCoordsOffset[searchAxis] ? 1 : -1; - int3 searchCoordsOffset = SearchAxisMasks[searchAxis] * searchAxisDir * searchDistance; + int searchAxisSign = probeCoordsOffset[searchAxis] ? 1 : -1; + int3 searchCoordsOffset = SearchAxes[searchAxis] * (searchAxisSign * searchDistance); uint3 searchCoords = clamp((int3)probeCoords + searchCoordsOffset, int3(0, 0, 0), (int3)probeCoordsEnd); uint searchIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, searchCoords); float4 searchData = LoadDDGIProbeData(data, probesData, cascadeIndex, searchIndex); @@ -200,6 +201,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes break; } } + } if (probeState == DDGI_PROBE_STATE_INACTIVE) continue; } @@ -232,8 +234,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Adjust weight curve to inject a small portion of light const float minWeightThreshold = 0.2f; - if (weight < minWeightThreshold) - weight *= Square(weight) / Square(minWeightThreshold); + if (weight < minWeightThreshold) weight *= (weight * weight) * (1.0f / (minWeightThreshold * minWeightThreshold)); // Calculate trilinear weights based on the distance to each probe to smoothly transition between grid of 8 probes float3 trilinear = lerp(1.0f - biasAlpha, biasAlpha, (float3)probeCoordsOffset); diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 3dbfd1ed5..bcd1f59fb 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -703,16 +703,17 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ //result.rgb = previous + (irradianceDelta * 0.25f); } result = float4(lerp(result.rgb, previous.rgb, historyWeight), 1.0f); + + // Apply quantization error to reduce yellowish artifacts due to R11G11B10 format + float noise = InterleavedGradientNoise(octahedralCoords, FrameIndexMod8); + result.rgb = QuantizeColor(result.rgb, noise, QuantizationError); #else result = float4(lerp(result.rg, previous.rg, historyWeight), 0.0f, 1.0f); #endif - // Write output irradiance (apply quantization error to reduce yellowish artifacts due to R11G11B10 format) - float noise = InterleavedGradientNoise(octahedralCoords, FrameIndexMod8); - result.rgb = QuantizeColor(result.rgb, noise, QuantizationError); RWOutput[outputCoords] = result; - GroupMemoryBarrierWithGroupSync(); + uint2 baseCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex) * (DDGI_PROBE_RESOLUTION + 2); #if DDGI_PROBE_UPDATE_MODE == 0 From c0b54be6922648bc0d68f10a8640ff427c6d1d58 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 16 Dec 2025 00:53:03 +0200 Subject: [PATCH 173/207] Fix clang bindings code generation for non-const ref parameters --- .../Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 190036af4..8554152fb 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1293,11 +1293,21 @@ namespace Flax.Build.Bindings callParams += parameterInfo.Name; callParams += "Temp"; } - // Instruct for more optoimized value move operation + // Instruct for more optimized value move operation else if (parameterInfo.Type.IsMoveRef) { callParams += $"MoveTemp({param})"; } + else if (parameterInfo.Type.IsRef && !parameterInfo.Type.IsConst) + { + // Non-const lvalue reference parameters needs to be passed via temporary value + if (parameterInfo.IsOut || parameterInfo.IsRef) + contents.Append(indent).AppendFormat("{2}& {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); + else + contents.Append(indent).AppendFormat("{2} {0}Temp = {1};", parameterInfo.Name, param, parameterInfo.Type.ToString(false)).AppendLine(); + callParams += parameterInfo.Name; + callParams += "Temp"; + } else { callParams += param; From 518a19c857c3333a51af14493995f93bd2b1229b Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 16 Dec 2025 14:27:32 +0200 Subject: [PATCH 174/207] Expose `RenderContext` to scripting API --- Source/Engine/Graphics/RenderTask.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Graphics/RenderTask.h b/Source/Engine/Graphics/RenderTask.h index 07926fd64..8cba1006e 100644 --- a/Source/Engine/Graphics/RenderTask.h +++ b/Source/Engine/Graphics/RenderTask.h @@ -450,7 +450,7 @@ public: /// /// The high-level renderer context. Used to collect the draw calls for the scene rendering. Can be used to perform a custom rendering. /// -API_STRUCT(NoDefault) struct RenderContext +API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContext { DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContext); @@ -491,7 +491,7 @@ API_STRUCT(NoDefault) struct RenderContext /// /// The high-level renderer context batch that encapsulates multiple rendering requests within a single task (eg. optimize main view scene rendering and shadow projections at once). /// -API_STRUCT(NoDefault) struct RenderContextBatch +API_STRUCT(NoDefault) struct FLAXENGINE_API RenderContextBatch { DECLARE_SCRIPTING_TYPE_MINIMAL(RenderContextBatch); From f95cbb0e52e1802ff8193bc637f4531a3913b588 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 16 Dec 2025 14:29:42 +0200 Subject: [PATCH 175/207] Fix missing exports for managed converter structures --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 8554152fb..33f9d3514 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -3005,7 +3005,7 @@ namespace Flax.Build.Bindings // Generate MConverter for a structure header.Append("template<>").AppendLine(); - header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); + header.AppendFormat("struct DLLEXPORT MConverter<{0}>", fullName).AppendLine(); header.Append('{').AppendLine(); header.AppendFormat(" MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); header.Append(" {").AppendLine(); @@ -3115,7 +3115,7 @@ namespace Flax.Build.Bindings { // Generate MConverter for a class header.Append("template<>").AppendLine(); - header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); + header.AppendFormat("struct DLLEXPORT MConverter<{0}>", fullName).AppendLine(); header.Append('{').AppendLine(); header.AppendFormat(" static MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); From 52e2327527bde83806f2de052b16e45aae6cc877 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 16 Dec 2025 21:11:36 +0200 Subject: [PATCH 176/207] Add `USED` attribute for forcing compiler to emit the symbol --- Source/Engine/Core/Compiler.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Core/Compiler.h b/Source/Engine/Core/Compiler.h index a45a4628a..d84713857 100644 --- a/Source/Engine/Core/Compiler.h +++ b/Source/Engine/Core/Compiler.h @@ -4,8 +4,9 @@ #if defined(__clang__) -#define DLLEXPORT __attribute__ ((__visibility__ ("default"))) +#define DLLEXPORT __attribute__((__visibility__("default"))) #define DLLIMPORT +#define USED __attribute__((used)) #define THREADLOCAL __thread #define STDCALL __attribute__((stdcall)) #define CDECL __attribute__((cdecl)) @@ -19,7 +20,7 @@ #define PACK_BEGIN() #define PACK_END() __attribute__((__packed__)) #define ALIGN_BEGIN(_align) -#define ALIGN_END(_align) __attribute__( (aligned(_align) ) ) +#define ALIGN_END(_align) __attribute__((aligned(_align))) #define OFFSET_OF(X, Y) __builtin_offsetof(X, Y) #define PRAGMA_DISABLE_DEPRECATION_WARNINGS \ _Pragma("clang diagnostic push") \ @@ -37,8 +38,9 @@ #elif defined(__GNUC__) -#define DLLEXPORT __attribute__ ((__visibility__ ("default"))) +#define DLLEXPORT __attribute__((__visibility__("default"))) #define DLLIMPORT +#define USED __attribute__((used)) #define THREADLOCAL __thread #define STDCALL __attribute__((stdcall)) #define CDECL __attribute__((cdecl)) @@ -52,7 +54,7 @@ #define PACK_BEGIN() #define PACK_END() __attribute__((__packed__)) #define ALIGN_BEGIN(_align) -#define ALIGN_END(_align) __attribute__( (aligned(_align) ) ) +#define ALIGN_END(_align) __attribute__((aligned(_align))) #define OFFSET_OF(X, Y) __builtin_offsetof(X, Y) #define PRAGMA_DISABLE_DEPRECATION_WARNINGS #define PRAGMA_ENABLE_DEPRECATION_WARNINGS @@ -67,6 +69,7 @@ #define DLLEXPORT __declspec(dllexport) #define DLLIMPORT __declspec(dllimport) +#define USED #define THREADLOCAL __declspec(thread) #define STDCALL __stdcall #define CDECL __cdecl From 3eb690fe58918d31df3ac4361cb672336b93da4b Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 16 Dec 2025 21:13:00 +0200 Subject: [PATCH 177/207] Ensure managed converter functions are exported with optimizations --- .../Bindings/BindingsGenerator.Cpp.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 33f9d3514..570ca4dcc 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -3005,18 +3005,21 @@ namespace Flax.Build.Bindings // Generate MConverter for a structure header.Append("template<>").AppendLine(); - header.AppendFormat("struct DLLEXPORT MConverter<{0}>", fullName).AppendLine(); + header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); header.Append('{').AppendLine(); - header.AppendFormat(" MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); + + header.AppendFormat(" DLLEXPORT USED MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" auto managed = ToManaged(data);").AppendLine(); header.Append(" return MCore::Object::Box((void*)&managed, klass);").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void Unbox({0}& result, MObject* data)", fullName).AppendLine(); + + header.AppendFormat(" DLLEXPORT USED void Unbox({0}& result, MObject* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" result = ToNative(*reinterpret_cast<{0}*>(MCore::Object::Unbox(data)));", wrapperName).AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); + + header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine(); header.AppendFormat(" {0}* resultPtr = ({0}*)MCore::Array::GetAddress(result);", wrapperName).AppendLine(); @@ -3026,7 +3029,8 @@ namespace Flax.Build.Bindings header.Append(" MCore::GC::WriteValue(&resultPtr[i], &managed, 1, klass);").AppendLine(); header.Append(" }").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); + + header.AppendFormat(" DLLEXPORT USED void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" {0}* dataPtr = ({0}*)MCore::Array::GetAddress(data);", wrapperName).AppendLine(); header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine(); @@ -3115,10 +3119,10 @@ namespace Flax.Build.Bindings { // Generate MConverter for a class header.Append("template<>").AppendLine(); - header.AppendFormat("struct DLLEXPORT MConverter<{0}>", fullName).AppendLine(); + header.AppendFormat("struct MConverter<{0}>", fullName).AppendLine(); header.Append('{').AppendLine(); - header.AppendFormat(" static MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED static MObject* Box(const {0}& data, const MClass* klass)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" MObject* obj = MCore::Object::New(klass);").AppendLine(); for (var i = 0; i < fields.Count; i++) @@ -3138,13 +3142,13 @@ namespace Flax.Build.Bindings header.Append(" return obj;").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" static MObject* Box(const {0}& data)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED static MObject* Box(const {0}& data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" MClass* klass = {0}::TypeInitializer.GetClass();", fullName).AppendLine(); header.Append(" return Box(data, klass);").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" static void Unbox({0}& result, MObject* obj)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED static void Unbox({0}& result, MObject* obj)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" MClass* klass = MCore::Object::GetClass(obj);").AppendLine(); header.Append(" void* v = nullptr;").AppendLine(); @@ -3166,20 +3170,20 @@ namespace Flax.Build.Bindings } header.Append(" }").AppendLine(); - header.AppendFormat(" static {0} Unbox(MObject* data)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED static {0} Unbox(MObject* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.AppendFormat(" {0} result;", fullName).AppendLine(); header.Append(" Unbox(result, data);").AppendLine(); header.Append(" return result;").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED void ToManagedArray(MArray* result, const Span<{0}>& data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" for (int32 i = 0; i < data.Length(); i++)").AppendLine(); header.Append(" MCore::GC::WriteArrayRef(result, Box(data[i]), i);").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); + header.AppendFormat(" DLLEXPORT USED void ToNativeArray(Span<{0}>& result, const MArray* data)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" MObject** dataPtr = (MObject**)MCore::Array::GetAddress(data);").AppendLine(); header.Append(" for (int32 i = 0; i < result.Length(); i++)").AppendLine(); From dc22fa406150fbbde86083ae614656479448d864 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 18 Dec 2025 21:49:03 -0600 Subject: [PATCH 178/207] Fix blend shape always applying zero weight if default weight is zero. --- Source/Engine/Level/Actors/AnimatedModel.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 343710183..f75174f72 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -668,7 +668,11 @@ void AnimatedModel::RunBlendShapeDeformer(const MeshBase* mesh, MeshDeformationD { if (q.First == blendShape.Name) { - const float weight = q.Second * blendShape.Weight; + float weight = q.Second; + if (!Math::IsZero(blendShape.Weight)) + weight *= blendShape.Weight; + if (Math::IsZero(weight)) + break; blendShapes.Add(Pair(blendShape, weight)); minVertexIndex = Math::Min(minVertexIndex, blendShape.MinVertexIndex); maxVertexIndex = Math::Max(maxVertexIndex, blendShape.MaxVertexIndex); From c68aa3b371c681ff7adde67d13ff482468833a2e Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 19 Dec 2025 08:13:24 -0600 Subject: [PATCH 179/207] Remove not needed include that produces ambiguity for the Label class. --- Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs b/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs index 53dade508..653478ecd 100644 --- a/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/RigidBodyEditor.cs @@ -1,7 +1,6 @@ // Copyright (c) Wojciech Figat. All rights reserved. using System.Collections.Generic; -using System.Reflection.Emit; using FlaxEditor.CustomEditors.GUI; using FlaxEngine; using FlaxEngine.GUI; From e3f5af530b21513591f686ee959cecd5de2cf9ee Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Fri, 19 Dec 2025 23:57:31 +0200 Subject: [PATCH 180/207] Fix incorrect class namespace in bindings class name lookups --- .../Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 190036af4..89e5dcd10 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -387,17 +387,7 @@ namespace Flax.Build.Bindings // Find namespace for this type to build a fullname if (apiType != null) - { - var e = apiType.Parent; - while (!(e is FileInfo)) - { - e = e.Parent; - } - if (e is FileInfo fileInfo && !managedType.StartsWith(fileInfo.Namespace)) - { - managedType = fileInfo.Namespace + '.' + managedType.Replace(".", "+"); - } - } + managedType = apiType.Namespace + '.' + managedType.Replace(".", "+"); // Use runtime lookup from fullname of the C# class return "Scripting::FindClass(\"" + managedType + "\")"; From fa38f0ac91eb2a771ffac271709664a6e0deca9d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 20 Dec 2025 23:44:56 +0100 Subject: [PATCH 181/207] Fix DDGI cascade offset towards bottom that was too intense making probes flicker --- Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 95bcb18e9..726c76410 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -366,7 +366,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont const Float3 probesDistance = Float3(probesCounts) * cascadeProbesSpacing; const float probesDistanceMax = probesDistance.MaxValue(); const Float3 viewRayHit = CollisionsHelper::LineHitsBox(viewOrigin, viewOrigin + viewDirection * (probesDistanceMax * 2.0f), viewOrigin - probesDistance, viewOrigin + probesDistance); - const float viewOriginOffset = viewRayHit.Y * probesDistanceMax * 0.6f; + const float viewOriginOffset = viewRayHit.Y * probesDistanceMax * 0.4f; viewOrigin += viewDirection * viewOriginOffset; const float viewOriginSnapping = cascadeProbesSpacing; viewOrigin = Float3::Floor(viewOrigin / viewOriginSnapping) * viewOriginSnapping; From 90a21696f68d1af5c1e632645fd1df52469d467e Mon Sep 17 00:00:00 2001 From: Phantom Date: Sun, 21 Dec 2025 15:47:52 +0100 Subject: [PATCH 182/207] Fix Linux Dependencies error --- .github/workflows/cd.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 56d663921..74915c1ef 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -87,7 +87,8 @@ jobs: git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Install dependencies run: | - sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get update + sudo apt-get install -y libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -118,7 +119,8 @@ jobs: git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull - name: Install dependencies run: | - sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get update + sudo apt-get install -y libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET From 23f5f4d85fff2a427ab1e100c9f0e3477b9c5cac Mon Sep 17 00:00:00 2001 From: Phantom Date: Sun, 21 Dec 2025 16:41:51 +0100 Subject: [PATCH 183/207] Add fix-missing --- .github/workflows/cd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 74915c1ef..9b7c5c965 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -88,7 +88,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET @@ -120,7 +120,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update - sudo apt-get install -y libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET From 4453453d46b47bd3816ae7decd607350e317c2be Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Tue, 23 Dec 2025 00:36:57 +0200 Subject: [PATCH 184/207] Fix managed wrapper function parameter handling for `BytesContainer` --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 190036af4..0274ca45b 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -925,7 +925,7 @@ namespace Flax.Build.Bindings // BytesContainer if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null) - return "MUtils::ToArray({0})"; + return $"MUtils::ToArray({value})"; // Construct native typename for MUtils template argument var nativeType = new StringBuilder(64); From 35f8051a62fbefbdc260e064b3f424089619a028 Mon Sep 17 00:00:00 2001 From: Ari Vuollet Date: Sat, 20 Dec 2025 01:51:37 +0200 Subject: [PATCH 185/207] Fix managed boolean array conversion to native array --- .../Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 0274ca45b..9836d1cb6 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -770,6 +770,11 @@ namespace Flax.Build.Bindings genericArgs += ", " + typeInfo.GenericArgs[1]; result = $"Array<{genericArgs}>({result})"; } + else if (arrayApiType?.Name == "bool") + { + type = "bool*"; + result = "Array({0}, {1})"; + } return result; } @@ -1244,8 +1249,12 @@ namespace Flax.Build.Bindings callParams += ", "; separator = true; var name = parameterInfo.Name; + var countParamName = $"__{parameterInfo.Name}Count"; if (CppParamsThatNeedConversion[i] && (!FindApiTypeInfo(buildData, parameterInfo.Type, caller)?.IsStruct ?? false)) + { name = '*' + name; + countParamName = '*' + countParamName; + } string param = string.Empty; if (string.IsNullOrWhiteSpace(CppParamsWrappersCache[i])) @@ -1258,7 +1267,7 @@ namespace Flax.Build.Bindings else { // Convert value - param += string.Format(CppParamsWrappersCache[i], name); + param += string.Format(CppParamsWrappersCache[i], name, countParamName); } // Special case for output result parameters that needs additional converting from native to managed format (such as non-POD structures or output array parameter) From c30d457b1c86f291d3d6e0891686d0fb3f813f86 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 29 Dec 2025 22:05:01 +0100 Subject: [PATCH 186/207] Add unit test of code from #3858 to ensure stability --- Source/Engine/Tests/TestScripting.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Engine/Tests/TestScripting.h b/Source/Engine/Tests/TestScripting.h index 10a1cf2b8..ca13ac307 100644 --- a/Source/Engine/Tests/TestScripting.h +++ b/Source/Engine/Tests/TestScripting.h @@ -204,6 +204,9 @@ public: { return str.Length(); } + + // Test parameter passing with non-cost ref + API_FUNCTION() virtual void StringParamRef(String& str) {} }; // Test debug commands via static class. From 6f6dd2f4a3328d09061c837a3be428419028db6d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 29 Dec 2025 22:06:55 +0100 Subject: [PATCH 187/207] Fix crash on script VTable setup due to async #3580 --- Source/Engine/Scripting/BinaryModule.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 07feedf10..3c7ee7a38 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -477,8 +477,8 @@ void ScriptingType::SetupScriptObjectVTable(void* object, ScriptingTypeHandle ba } // Duplicate vtable - Script.VTable = (void**)((byte*)Platform::Allocate(totalSize, 16) + prefixSize); - Utilities::UnsafeMemoryCopy((byte*)Script.VTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size); + void** scriptVTable = (void**)((byte*)Platform::Allocate(totalSize, 16) + prefixSize); + Utilities::UnsafeMemoryCopy((byte*)scriptVTable - prefixSize, (byte*)vtable - prefixSize, prefixSize + size); // Override vtable entries if (interfacesCount) @@ -492,7 +492,7 @@ void ScriptingType::SetupScriptObjectVTable(void* object, ScriptingTypeHandle ba if (eType.Script.SetupScriptObjectVTable) { // Override vtable entries for this class - eType.Script.SetupScriptObjectVTable(Script.ScriptVTable, Script.ScriptVTableBase, Script.VTable, entriesCount, wrapperIndex); + eType.Script.SetupScriptObjectVTable(Script.ScriptVTable, Script.ScriptVTableBase, scriptVTable, entriesCount, wrapperIndex); } auto interfaces = eType.Interfaces; @@ -511,13 +511,13 @@ void ScriptingType::SetupScriptObjectVTable(void* object, ScriptingTypeHandle ba const int32 interfaceSize = interfaceCount * sizeof(void*); // Duplicate interface vtable - Utilities::UnsafeMemoryCopy((byte*)Script.VTable + interfaceOffset, (byte*)vtableInterface - prefixSize, prefixSize + interfaceSize); + Utilities::UnsafeMemoryCopy((byte*)scriptVTable + interfaceOffset, (byte*)vtableInterface - prefixSize, prefixSize + interfaceSize); // Override interface vtable entries const auto scriptOffset = interfaces->ScriptVTableOffset; const auto nativeOffset = interfaceOffset + prefixSize; void** interfaceVTable = (void**)((byte*)Script.VTable + nativeOffset); - interfaceType.Interface.SetupScriptObjectVTable(Script.ScriptVTable + scriptOffset, Script.ScriptVTableBase + scriptOffset, interfaceVTable, interfaceCount, wrapperIndex); + interfaceType.Interface.SetupScriptObjectVTable(scriptVTable + scriptOffset, Script.ScriptVTableBase + scriptOffset, interfaceVTable, interfaceCount, wrapperIndex); Script.InterfacesOffsets[interfacesCount++] = (uint16)nativeOffset; interfaceOffset += prefixSize + interfaceSize; @@ -527,6 +527,9 @@ void ScriptingType::SetupScriptObjectVTable(void* object, ScriptingTypeHandle ba } e = eType.GetBaseType(); } + + // Assign once it's ready + Script.VTable = scriptVTable; } void ScriptingType::HackObjectVTable(void* object, ScriptingTypeHandle baseTypeHandle, int32 wrapperIndex) From 1bdb577b60fc1c3fe893e9c2694233ae0a652a06 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 29 Dec 2025 22:24:55 +0100 Subject: [PATCH 188/207] Fix engine services before exit event order --- Source/Engine/Engine/EngineService.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Engine/EngineService.cpp b/Source/Engine/Engine/EngineService.cpp index 433cf089f..240f60a63 100644 --- a/Source/Engine/Engine/EngineService.cpp +++ b/Source/Engine/Engine/EngineService.cpp @@ -26,7 +26,7 @@ static bool CompareEngineServices(EngineService* const& a, EngineService* const& { \ ZoneScoped; \ auto& services = GetServices(); \ - for (int32 i = 0; i < services.Count(); i++) \ + for (int32 i = services.Count() - 1; i >= 0; i--) \ services[i]->name(); \ } From 7cdc2456cd9690ca3d150bb071f1c3ab73ae15fb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 29 Dec 2025 22:53:37 +0100 Subject: [PATCH 189/207] Clone #3878 into more usages --- .github/workflows/build_linux.yml | 3 ++- .github/workflows/tests.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 6290ff525..760041b6a 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -16,7 +16,8 @@ jobs: uses: actions/checkout@v3 - name: Install dependencies run: | - sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get update + sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Setup Vulkan uses: ./.github/actions/vulkan - name: Setup .NET diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7c28121fd..fdc8e80c9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -28,7 +28,8 @@ jobs: git lfs pull - name: Install dependencies run: | - sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev + sudo apt-get update + sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev - name: Build run: | ./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8 From 7f56d9456bbc127fc1e3fdb4b40b0ae586dfebb3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Dec 2025 22:51:37 +0100 Subject: [PATCH 190/207] Fix bindings warning when using curve property/field --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 54329634f..034803d25 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -703,6 +703,8 @@ namespace Flax.Build.Bindings else if (nativeType.EndsWith("[]")) { parameterMarshalType = $"MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>))"; + if (!parameterInfo.IsOut && !parameterInfo.IsRef) + parameterMarshalType += ", In"; // The usage of 'LibraryImportAttribute' does not follow recommendations. It is recommended to use explicit '[In]' and '[Out]' attributes on array parameters. } if (!string.IsNullOrEmpty(parameterMarshalType)) From ce45fa3d547e508e83cf1b87161c30537f653a39 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 30 Dec 2025 23:41:49 +0100 Subject: [PATCH 191/207] Fix crash on Global Surface Atlas objects buffer building --- Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index ce0ec1881..7216a8fa8 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -428,6 +428,7 @@ public: // Write to objects buffer (this must match unpacking logic in HLSL) uint32 objectAddress = ObjectsBuffer.Data.Count() / sizeof(Float4); ObjectsListBuffer.Write(objectAddress); + ObjectsBuffer.Data.EnsureCapacity(ObjectsBuffer.Data.Count() + sizeof(Float4) * (GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE + 6 * GLOBAL_SURFACE_ATLAS_TILE_DATA_STRIDE)); auto* objectData = ObjectsBuffer.WriteReserve(GLOBAL_SURFACE_ATLAS_OBJECT_DATA_STRIDE); objectData[0] = Float4(object.Position, object.Radius); objectData[1] = Float4::Zero; @@ -511,6 +512,7 @@ public: { // Dirty object to redraw object->LastFrameUpdated = 0; + return; } GlobalSurfaceAtlasLight* light = Lights.TryGet(a->GetID()); if (light) From 37df16a3e4f94434d3228abd5af28d286e3d5562 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 31 Dec 2025 00:19:51 +0100 Subject: [PATCH 192/207] Refactor DDGI irradiance filtering for smoother and more accurate lighting --- Source/Shaders/GI/DDGI.hlsl | 21 +++++++++++++-------- Source/Shaders/GI/DDGI.shader | 7 ++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index 9d8f5509c..eb3995ada 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -25,6 +25,7 @@ #define DDGI_CASCADE_BLEND_SMOOTH 0 // Enables smooth cascade blending, otherwise dithering will be used #endif #define DDGI_SRGB_BLENDING 1 // Enables blending in sRGB color space, otherwise irradiance blending is done in linear space +#define DDGI_DEFAULT_BIAS 0.2f // Default value for DDGI sampling bias // DDGI data for a constant buffer struct DDGIData @@ -181,13 +182,14 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes if (probeState == DDGI_PROBE_STATE_INACTIVE) { // Search nearby probes to find any nearby GI sample + LOOP for (int searchDistance = 1; searchDistance < 3 && probeState == DDGI_PROBE_STATE_INACTIVE; searchDistance++) { for (uint searchAxis = 0; searchAxis < 3; searchAxis++) { int searchAxisSign = probeCoordsOffset[searchAxis] ? 1 : -1; int3 searchCoordsOffset = SearchAxes[searchAxis] * (searchAxisSign * searchDistance); - uint3 searchCoords = clamp((int3)probeCoords + searchCoordsOffset, int3(0, 0, 0), (int3)probeCoordsEnd); + uint3 searchCoords = clamp((uint3)((int3)probeCoords + searchCoordsOffset), uint3(0, 0, 0), probeCoordsEnd); uint searchIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, searchCoords); float4 searchData = LoadDDGIProbeData(data, probesData, cascadeIndex, searchIndex); uint searchState = DecodeDDGIProbeState(searchData); @@ -205,8 +207,9 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes if (probeState == DDGI_PROBE_STATE_INACTIVE) continue; } - float3 probeBasePosition = baseProbeWorldPosition + ((probeCoords - baseProbeCoords) * probesSpacing); - float3 probePosition = probeBasePosition + probeData.xyz * probesSpacing; // Probe offset is [-1;1] within probes spacing + + // Calculate probe position + float3 probePosition = baseProbeWorldPosition + (((float3)probeCoords - (float3)baseProbeCoords) * probesSpacing) + probeData.xyz * probesSpacing; // Calculate the distance and direction from the (biased and non-biased) shading point and the probe float3 worldPosToProbe = normalize(probePosition - worldPosition); @@ -215,6 +218,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Smooth backface test float weight = Square(dot(worldPosToProbe, worldNormal) * 0.5f + 0.5f); + weight = max(weight, 0.1f); // Sample distance texture float2 octahedralCoords = GetOctahedralCoords(-biasedPosToProbe); @@ -226,7 +230,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes { float variance = abs(Square(probeDistance.x) - probeDistance.y); float visibilityWeight = variance / (variance + Square(biasedPosToProbeDist - probeDistance.x)); - weight *= max(visibilityWeight * visibilityWeight * visibilityWeight, 0.05f); + weight *= max(visibilityWeight * visibilityWeight * visibilityWeight, 0.0f); } // Avoid a weight of zero @@ -238,7 +242,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Calculate trilinear weights based on the distance to each probe to smoothly transition between grid of 8 probes float3 trilinear = lerp(1.0f - biasAlpha, biasAlpha, (float3)probeCoordsOffset); - weight *= max(trilinear.x * trilinear.y * trilinear.z, 0.001f); + weight *= saturate(trilinear.x * trilinear.y * trilinear.z * 2.0f); // Sample irradiance texture octahedralCoords = GetOctahedralCoords(worldNormal); @@ -270,7 +274,8 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes if (irradiance.a > 0.0f) { // Normalize irradiance - irradiance.rgb /= irradiance.a; + //irradiance.rgb /= irradiance.a; + irradiance.rgb /= lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f)); #if DDGI_SRGB_BLENDING irradiance.rgb *= irradiance.rgb; #endif @@ -282,13 +287,13 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes float3 GetDDGISurfaceBias(float3 viewDir, float probesSpacing, float3 worldNormal, float bias) { // Bias the world-space position to reduce artifacts - return (worldNormal * 0.2f + viewDir * 0.8f) * (0.75f * probesSpacing * bias); + return (worldNormal * 0.2f + viewDir * 0.8f) * (0.6f * probesSpacing * bias); } // Samples DDGI probes volume at the given world-space position and returns the irradiance. // bias - scales the bias vector to the initial sample point to reduce self-shading artifacts // dither - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending -float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = 0.2f, float dither = 0.0f) +float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = DDGI_DEFAULT_BIAS, float dither = 0.0f) { // Select the highest cascade that contains the sample location uint cascadeIndex = 0; diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index bcd1f59fb..fd82531ac 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -395,6 +395,8 @@ void CS_TraceRays(uint3 DispatchThreadId : SV_DispatchThreadID) // Add some bias to prevent self occlusion artifacts in Chebyshev due to Global SDF being very incorrect in small scale radiance.w = max(radiance.w + GlobalSDF.CascadeVoxelSize[hit.HitCascade] * 0.5f, 0); + float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; + radiance.w += probesSpacing * 0.05f; } } else @@ -793,10 +795,9 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0) } // Sample irradiance - float bias = 0.2f; float dither = RandN2(input.TexCoord + TemporalTime).x; - float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, bias, dither); - + float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, DDGI_DEFAULT_BIAS, dither); + // Calculate lighting float3 diffuseColor = GetDiffuseColor(gBuffer); float3 diffuse = Diffuse_Lambert(diffuseColor); From b87f8b96e1c616b4b44ae6d378eead89618d0af9 Mon Sep 17 00:00:00 2001 From: fibref Date: Thu, 1 Jan 2026 17:44:33 +0800 Subject: [PATCH 193/207] fix crash on asset auto-upgrade --- Source/Engine/Content/Factories/BinaryAssetFactory.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Engine/Content/Factories/BinaryAssetFactory.cpp b/Source/Engine/Content/Factories/BinaryAssetFactory.cpp index e9479a107..c492bd5b0 100644 --- a/Source/Engine/Content/Factories/BinaryAssetFactory.cpp +++ b/Source/Engine/Content/Factories/BinaryAssetFactory.cpp @@ -146,6 +146,10 @@ bool BinaryAssetFactoryBase::UpgradeAsset(const AssetInfo& info, FlaxStorage* st context.Input = context.Output; } while (upgrader->ShouldUpgrade(context.Input.SerializedVersion)); + // Prevent other threads from loading the storage when it is upgrading + // It works because CriticalSection allows recursion + ScopeLock upgradeLock(storage->_loadLocker); + // Release storage internal data (should also close file handles) { // HACK: file is locked by some tasks: the current one that called asset data upgrade (LoadAssetTask) From 8facb46defc4de91e2127694a82eefeb6ceb4e53 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 2 Jan 2026 17:06:27 +0100 Subject: [PATCH 194/207] Fix regression from 6f6dd2f4a3328d09061c837a3be428419028db6d on script interface vtable setup --- Source/Engine/Scripting/BinaryModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/BinaryModule.cpp b/Source/Engine/Scripting/BinaryModule.cpp index 3c7ee7a38..4d26e678b 100644 --- a/Source/Engine/Scripting/BinaryModule.cpp +++ b/Source/Engine/Scripting/BinaryModule.cpp @@ -516,7 +516,7 @@ void ScriptingType::SetupScriptObjectVTable(void* object, ScriptingTypeHandle ba // Override interface vtable entries const auto scriptOffset = interfaces->ScriptVTableOffset; const auto nativeOffset = interfaceOffset + prefixSize; - void** interfaceVTable = (void**)((byte*)Script.VTable + nativeOffset); + void** interfaceVTable = (void**)((byte*)scriptVTable + nativeOffset); interfaceType.Interface.SetupScriptObjectVTable(scriptVTable + scriptOffset, Script.ScriptVTableBase + scriptOffset, interfaceVTable, interfaceCount, wrapperIndex); Script.InterfacesOffsets[interfacesCount++] = (uint16)nativeOffset; From 400e2f1b0ee35821c52994aabdd7b533c9bc7ee4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 2 Jan 2026 21:30:14 +0100 Subject: [PATCH 195/207] Fix DDGI flickering on floors aligned to axis on value `0` (eg. floor) --- Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 726c76410..bdb9bbe2a 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -11,6 +11,7 @@ #include "Engine/Core/Math/Quaternion.h" #include "Engine/Core/Config/GraphicsSettings.h" #include "Engine/Engine/Engine.h" +#include "Engine/Engine/Units.h" #include "Engine/Content/Content.h" #include "Engine/Debug/DebugDraw.h" #include "Engine/Graphics/GPUContext.h" @@ -371,6 +372,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont const float viewOriginSnapping = cascadeProbesSpacing; viewOrigin = Float3::Floor(viewOrigin / viewOriginSnapping) * viewOriginSnapping; //viewOrigin = Float3::Zero; + viewOrigin -= UNITS_TO_METERS(0.5f); // Bias to avoid precision issues (eg. if floor mesh is exactly at Y=0) viewOrigins[cascadeIndex] = viewOrigin; } From b24d98df9eea0ed2d085ac0a2ed48d5fb4af8188 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 3 Jan 2026 00:48:36 +0100 Subject: [PATCH 196/207] Fix DDGI cascades blending to be smoother --- .../Graphics/Materials/MaterialShader.h | 2 +- .../GI/DynamicDiffuseGlobalIllumination.cpp | 7 +++++-- .../GI/DynamicDiffuseGlobalIllumination.h | 3 ++- Source/Shaders/GI/DDGI.hlsl | 21 ++++++++++++------- Source/Shaders/GI/DDGI.shader | 2 +- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index 5da4ee04f..bb68520c0 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -10,7 +10,7 @@ /// /// Current materials shader version. /// -#define MATERIAL_GRAPH_VERSION 178 +#define MATERIAL_GRAPH_VERSION 179 class Material; class GPUShader; diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index bdb9bbe2a..63a8ef151 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -338,7 +338,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont } // Calculate the probes count based on the amount of cascades and the distance to cover - const float cascadesDistanceScales[] = { 1.0f, 3.0f, 6.0f, 10.0f }; // Scales each cascade further away from the camera origin + const float cascadesDistanceScales[] = { 1.0f, 3.0f, 5.0f, 10.0f }; // Scales each cascade further away from the camera origin const float distanceExtent = distance / cascadesDistanceScales[cascadesCount - 1]; const float verticalRangeScale = 0.8f; // Scales the probes volume size at Y axis (horizontal aspect ratio makes the DDGI use less probes vertically to cover whole screen) Int3 probesCounts(Float3::Ceil(Float3(distanceExtent, distanceExtent * verticalRangeScale, distanceExtent) / probesSpacing)); @@ -354,6 +354,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont // Initialize cascades float probesSpacings[4]; Float3 viewOrigins[4]; + Float3 blendOrigins[4]; for (int32 cascadeIndex = 0; cascadeIndex < cascadesCount; cascadeIndex++) { // Each cascade has higher spacing between probes @@ -369,9 +370,10 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont const Float3 viewRayHit = CollisionsHelper::LineHitsBox(viewOrigin, viewOrigin + viewDirection * (probesDistanceMax * 2.0f), viewOrigin - probesDistance, viewOrigin + probesDistance); const float viewOriginOffset = viewRayHit.Y * probesDistanceMax * 0.4f; viewOrigin += viewDirection * viewOriginOffset; + //viewOrigin = Float3::Zero; + blendOrigins[cascadeIndex] = viewOrigin; const float viewOriginSnapping = cascadeProbesSpacing; viewOrigin = Float3::Floor(viewOrigin / viewOriginSnapping) * viewOriginSnapping; - //viewOrigin = Float3::Zero; viewOrigin -= UNITS_TO_METERS(0.5f); // Bias to avoid precision issues (eg. if floor mesh is exactly at Y=0) viewOrigins[cascadeIndex] = viewOrigin; } @@ -504,6 +506,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont { auto& cascade = ddgiData.Cascades[cascadeIndex]; ddgiData.Result.Constants.ProbesOriginAndSpacing[cascadeIndex] = Float4(cascade.ProbesOrigin, cascade.ProbesSpacing); + ddgiData.Result.Constants.BlendOrigin[cascadeIndex] = Float4(blendOrigins[cascadeIndex], 0.0f); ddgiData.Result.Constants.ProbesScrollOffsets[cascadeIndex] = Int4(cascade.ProbeScrollOffsets, 0); } ddgiData.Result.Constants.RayMaxDistance = distance; diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h index e6ace0373..123a47c6d 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h @@ -15,7 +15,8 @@ public: // Constant buffer data for DDGI access on a GPU. GPU_CB_STRUCT(ConstantsData { Float4 ProbesOriginAndSpacing[4]; - Int4 ProbesScrollOffsets[4]; + Float4 BlendOrigin[4]; // w is unused + Int4 ProbesScrollOffsets[4]; // w is unused uint32 ProbesCounts[3]; uint32 CascadesCount; float IrradianceGamma; diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index eb3995ada..e70454c2b 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -20,7 +20,7 @@ #define DDGI_PROBE_ATTENTION_MAX 0.98f // Maximum probe attention value that still makes it active (but not activated which is 1.0f). #define DDGI_PROBE_RESOLUTION_IRRADIANCE 6 // Resolution (in texels) for probe irradiance data (excluding 1px padding on each side) #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) -#define DDGI_CASCADE_BLEND_SIZE 2.5f // Distance in probes over which cascades blending happens +#define DDGI_CASCADE_BLEND_SIZE 2.0f // Distance in probes over which cascades blending happens #ifndef DDGI_CASCADE_BLEND_SMOOTH #define DDGI_CASCADE_BLEND_SMOOTH 0 // Enables smooth cascade blending, otherwise dithering will be used #endif @@ -31,7 +31,8 @@ struct DDGIData { float4 ProbesOriginAndSpacing[4]; - int4 ProbesScrollOffsets[4]; // w unused + float4 BlendOrigin[4]; // w is unused + int4 ProbesScrollOffsets[4]; // w is unused uint3 ProbesCounts; uint CascadesCount; float IrradianceGamma; @@ -290,6 +291,13 @@ float3 GetDDGISurfaceBias(float3 viewDir, float probesSpacing, float3 worldNorma return (worldNormal * 0.2f + viewDir * 0.8f) * (0.6f * probesSpacing * bias); } +// [Inigo Quilez, https://iquilezles.org/articles/distfunctions/] +float sdRoundBox(float3 p, float3 b, float r) +{ + float3 q = abs(p) - b + r; + return length(max(q, 0.0f)) + min(max(q.x, max(q.y, q.z)), 0.0f) - r; +} + // Samples DDGI probes volume at the given world-space position and returns the irradiance. // bias - scales the bias vector to the initial sample point to reduce self-shading artifacts // dither - randomized per-pixel value in range 0-1, used to smooth dithering for cascades blending @@ -312,13 +320,10 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, T biasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, probesSpacing, worldNormal, bias); // Calculate cascade blending weight (use input bias to smooth transition) - float cascadeBlendSmooth = frac(max(distance(data.ViewPos, worldPosition) - probesExtent.x, 0) / probesSpacing) * 0.1f; - float3 cascadeBlendPoint = worldPosition - probesOrigin - cascadeBlendSmooth * probesSpacing; float fadeDistance = probesSpacing * DDGI_CASCADE_BLEND_SIZE; -#if DDGI_CASCADE_BLEND_SMOOTH - fadeDistance *= 2.0f; // Make it even smoother when using linear blending -#endif - cascadeWeight = saturate(Min3(probesExtent - abs(cascadeBlendPoint)) / fadeDistance); + float3 blendPos = worldPosition - data.BlendOrigin[cascadeIndex].xyz; + cascadeWeight = sdRoundBox(blendPos, probesExtent - probesSpacing, probesSpacing * 2) + fadeDistance; + cascadeWeight = 1 - saturate(cascadeWeight / fadeDistance); if (cascadeWeight > dither) break; } diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index fd82531ac..6f68cef34 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -122,7 +122,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) float prevProbesSpacing = DDGI.ProbesOriginAndSpacing[prevCascade].w; float3 prevProbesOrigin = DDGI.ProbesScrollOffsets[prevCascade].xyz * prevProbesSpacing + DDGI.ProbesOriginAndSpacing[prevCascade].xyz; float3 prevProbesExtent = (DDGI.ProbesCounts - 1) * (prevProbesSpacing * 0.5f); - prevProbesExtent -= probesSpacing * ceil(DDGI_CASCADE_BLEND_SIZE); // Apply safe margin to allow probes on cascade edges + prevProbesExtent -= probesSpacing * ceil(DDGI_CASCADE_BLEND_SIZE) * 2; // Apply safe margin to allow probes on cascade edges float prevCascadeWeight = Min3(prevProbesExtent - abs(probeBasePosition - prevProbesOrigin)); if (prevCascadeWeight > 0.1f) { From 6c79a17c7aa6a43740f944a07c6ba6040c64e345 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 3 Jan 2026 20:57:17 +0100 Subject: [PATCH 197/207] Fix DDGI cascade placement when using large probes spacing Revert fa38f0ac91eb2a771ffac271709664a6e0deca9d after b24d98df9eea0ed2d085ac0a2ed48d5fb4af8188 fixed the source of the problem --- .../Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 63a8ef151..0ccb9aa02 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -365,10 +365,10 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont // Calculate view origin for cascade by shifting it towards the view direction to account for better view frustum coverage Float3 viewOrigin = renderContext.View.Position; Float3 viewDirection = renderContext.View.Direction; - const Float3 probesDistance = Float3(probesCounts) * cascadeProbesSpacing; + const Float3 probesDistance = Float3(probesCounts - 1) * cascadeProbesSpacing; const float probesDistanceMax = probesDistance.MaxValue(); const Float3 viewRayHit = CollisionsHelper::LineHitsBox(viewOrigin, viewOrigin + viewDirection * (probesDistanceMax * 2.0f), viewOrigin - probesDistance, viewOrigin + probesDistance); - const float viewOriginOffset = viewRayHit.Y * probesDistanceMax * 0.4f; + const float viewOriginOffset = viewRayHit.Y * probesDistanceMax * 0.6f; viewOrigin += viewDirection * viewOriginOffset; //viewOrigin = Float3::Zero; blendOrigins[cascadeIndex] = viewOrigin; From c5a28a5734ff9a132b278efd23a08a8cdd56e3da Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Jan 2026 12:04:02 +0100 Subject: [PATCH 198/207] Refactor DDGI irradiance sampling when nearby probe is missing to use precomputed fallback probes Also, insert fallback/dummy probes when there is no SDF nearby to have lighting in all cases. This both improves sampling performance and fixes issues when sampling in areas far away from valid GI. --- .../GI/DynamicDiffuseGlobalIllumination.cpp | 13 +++ .../GI/DynamicDiffuseGlobalIllumination.h | 1 + Source/Shaders/GI/DDGI.hlsl | 52 ++++----- Source/Shaders/GI/DDGI.shader | 109 +++++++++++++++++- 4 files changed, 141 insertions(+), 34 deletions(-) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 0ccb9aa02..a85ba6342 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -42,6 +42,7 @@ #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) #define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8 #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 +#define DDGI_PROBE_EMPTY_AREA_DENSITY 10 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency #define DDGI_DEBUG_STATS 0 // Enables additional GPU-driven stats for probe/rays count #define DDGI_DEBUG_INSTABILITY 0 // Enables additional probe irradiance instability debugging @@ -217,6 +218,7 @@ bool DynamicDiffuseGlobalIlluminationPass::setupResources() return true; _csClassify = shader->GetCS("CS_Classify"); _csUpdateProbesInitArgs = shader->GetCS("CS_UpdateProbesInitArgs"); + _csUpdateInactiveProbes = shader->GetCS("CS_UpdateInactiveProbes"); _csTraceRays[0] = shader->GetCS("CS_TraceRays", 0); _csTraceRays[1] = shader->GetCS("CS_TraceRays", 1); _csTraceRays[2] = shader->GetCS("CS_TraceRays", 2); @@ -248,6 +250,7 @@ void DynamicDiffuseGlobalIlluminationPass::OnShaderReloading(Asset* obj) LastFrameShaderReload = Engine::FrameCount; _csClassify = nullptr; _csUpdateProbesInitArgs = nullptr; + _csUpdateInactiveProbes = nullptr; _csTraceRays[0] = nullptr; _csTraceRays[1] = nullptr; _csTraceRays[2] = nullptr; @@ -590,6 +593,16 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont context->ResetUA(); } + // For inactive probes, search nearby ones to find the closest valid for quick fallback when sampling irradiance + { + PROFILE_GPU_CPU_NAMED("Update Inactive Probes"); + context->BindUA(0, ddgiData.Result.ProbesData); + int32 iterations = Math::Min(probesCounts.MaxValue() - 1, DDGI_PROBE_EMPTY_AREA_DENSITY) * 10; + for (int32 i = 0; i < iterations; i++) + context->Dispatch(_csUpdateInactiveProbes, threadGroupsX, 1, 1); + context->ResetUA(); + } + // Update probes in batches so ProbesTrace texture can be smaller uint32 arg = 0; // TODO: use rays allocator to dispatch raytracing in packets (eg. 8 threads in a group instead of hardcoded limit) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h index 123a47c6d..85478261f 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h @@ -45,6 +45,7 @@ private: GPUConstantBuffer* _cb1 = nullptr; GPUShaderProgramCS* _csClassify; GPUShaderProgramCS* _csUpdateProbesInitArgs; + GPUShaderProgramCS* _csUpdateInactiveProbes; GPUShaderProgramCS* _csTraceRays[4]; GPUShaderProgramCS* _csUpdateProbesIrradiance; GPUShaderProgramCS* _csUpdateProbesDistance; diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index e70454c2b..a7de92577 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -26,6 +26,9 @@ #endif #define DDGI_SRGB_BLENDING 1 // Enables blending in sRGB color space, otherwise irradiance blending is done in linear space #define DDGI_DEFAULT_BIAS 0.2f // Default value for DDGI sampling bias +#define DDGI_FALLBACK_COORDS_ENCODE(coord) ((float3)coord / 128.0f) +#define DDGI_FALLBACK_COORDS_DECODE(data) (uint3)(data.xyz * 128.0f) +//#define DDGI_DEBUG_CASCADE 0 // Forces a specific cascade to be only in use (for debugging) // DDGI data for a constant buffer struct DDGIData @@ -170,7 +173,6 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Loop over the closest probes to accumulate their contributions float4 irradiance = float4(0, 0, 0, 0); - const int3 SearchAxes[3] = { int3(1, 0, 0), int3(0, 1, 0), int3(0, 0, 1) }; for (uint i = 0; i < 8; i++) { uint3 probeCoordsOffset = uint3(i, i >> 1, i >> 2) & 1; @@ -180,33 +182,19 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes // Load probe position and state float4 probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex); uint probeState = DecodeDDGIProbeState(probeData); + uint useVisibility = true; + float minWight = 0.000001f; if (probeState == DDGI_PROBE_STATE_INACTIVE) { - // Search nearby probes to find any nearby GI sample - LOOP - for (int searchDistance = 1; searchDistance < 3 && probeState == DDGI_PROBE_STATE_INACTIVE; searchDistance++) - { - for (uint searchAxis = 0; searchAxis < 3; searchAxis++) - { - int searchAxisSign = probeCoordsOffset[searchAxis] ? 1 : -1; - int3 searchCoordsOffset = SearchAxes[searchAxis] * (searchAxisSign * searchDistance); - uint3 searchCoords = clamp((uint3)((int3)probeCoords + searchCoordsOffset), uint3(0, 0, 0), probeCoordsEnd); - uint searchIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, searchCoords); - float4 searchData = LoadDDGIProbeData(data, probesData, cascadeIndex, searchIndex); - uint searchState = DecodeDDGIProbeState(searchData); - if (searchState != DDGI_PROBE_STATE_INACTIVE) - { - // Use nearby probe as a fallback (visibility test might ignore it but with smooth gradient) - probeCoords = searchCoords; - probeIndex = searchIndex; - probeData = searchData; - probeState = searchState; - break; - } - } - } - if (probeState == DDGI_PROBE_STATE_INACTIVE) - continue; + // Use fallback probe that is closest to this one + uint3 fallbackCoords = DDGI_FALLBACK_COORDS_DECODE(probeData); + float fallbackToProbeDist = length((float3)probeCoords - (float3)fallbackCoords); + useVisibility = fallbackToProbeDist <= 1.0f; // Skip visibility test that blocks too far probes due to limiting max distance to 1.5 of probe spacing + if (fallbackToProbeDist > 2.0f) + minWight = 1.0f; + probeCoords = fallbackCoords; + probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, fallbackCoords); + probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex); } // Calculate probe position @@ -227,7 +215,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes float2 probeDistance = probesDistance.SampleLevel(SamplerLinearClamp, uv, 0).rg * 2.0f; // Visibility weight (Chebyshev) - if (biasedPosToProbeDist > probeDistance.x) + if (biasedPosToProbeDist > probeDistance.x && useVisibility) { float variance = abs(Square(probeDistance.x) - probeDistance.y); float visibilityWeight = variance / (variance + Square(biasedPosToProbeDist - probeDistance.x)); @@ -235,7 +223,7 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes } // Avoid a weight of zero - weight = max(weight, 0.000001f); + weight = max(weight, minWight); // Adjust weight curve to inject a small portion of light const float minWeightThreshold = 0.2f; @@ -304,13 +292,16 @@ float sdRoundBox(float3 p, float3 b, float r) float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, float bias = DDGI_DEFAULT_BIAS, float dither = 0.0f) { // Select the highest cascade that contains the sample location - uint cascadeIndex = 0; float probesSpacing = 0, cascadeWeight = 0; float3 probesOrigin = (float3)0, probesExtent = (float3)0, biasedWorldPosition = (float3)0; float3 viewDir = normalize(data.ViewPos - worldPosition); #if DDGI_CASCADE_BLEND_SMOOTH dither = 0.0f; #endif +#ifdef DDGI_DEBUG_CASCADE + uint cascadeIndex = DDGI_DEBUG_CASCADE; +#else + uint cascadeIndex = 0; for (; cascadeIndex < data.CascadesCount; cascadeIndex++) { // Get cascade data @@ -327,6 +318,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, T if (cascadeWeight > dither) break; } +#endif if (cascadeIndex == data.CascadesCount) return data.FallbackIrradiance; @@ -335,7 +327,7 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, T // Blend with the next cascade (or fallback irradiance outside the volume) cascadeIndex++; -#if DDGI_CASCADE_BLEND_SMOOTH +#if DDGI_CASCADE_BLEND_SMOOTH && !defined(DDGI_DEBUG_CASCADE) result *= cascadeWeight; if (cascadeIndex < data.CascadesCount && cascadeWeight < 0.99f) { diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 6f68cef34..d12803430 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -27,6 +27,7 @@ #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 #define DDGI_PROBE_RELOCATE_ITERATIVE 1 // If true, probes relocation algorithm tries to move them in additive way, otherwise all nearby locations are checked to find the best position #define DDGI_PROBE_RELOCATE_FIND_BEST 1 // If true, probes relocation algorithm tries to move to the best matching location within nearby area +#define DDGI_PROBE_EMPTY_AREA_DENSITY 10 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency #define DDGI_DEBUG_STATS 0 // Enables additional GPU-driven stats for probe/rays count #define DDGI_DEBUG_INSTABILITY 0 // Enables additional probe irradiance instability debugging @@ -115,6 +116,14 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) float probesSpacing = DDGI.ProbesOriginAndSpacing[CascadeIndex].w; float3 probeBasePosition = GetDDGIProbeWorldPosition(DDGI, CascadeIndex, probeCoords); +#ifdef DDGI_DEBUG_CASCADE + // Single cascade-only debugging + if (CascadeIndex != DDGI_DEBUG_CASCADE) + { + RWProbesData[probeDataCoords] = EncodeDDGIProbeData(float3(0, 0, 0), DDGI_PROBE_STATE_INACTIVE, 0.0f); + return; + } +#else // Disable probes that are is in the range of higher-quality cascade if (CascadeIndex > 0) { @@ -126,11 +135,11 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) float prevCascadeWeight = Min3(prevProbesExtent - abs(probeBasePosition - prevProbesOrigin)); if (prevCascadeWeight > 0.1f) { - // Disable probe RWProbesData[probeDataCoords] = EncodeDDGIProbeData(float3(0, 0, 0), DDGI_PROBE_STATE_INACTIVE, 0.0f); return; } } +#endif // Check if probe was scrolled int3 probeScrollClears = ProbeScrollClears[CascadeIndex].xyz; @@ -174,9 +183,18 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) float voxelLimit = GlobalSDF.CascadeVoxelSize[CascadeIndex] * 0.8f; float distanceLimit = probesSpacing * ProbesDistanceLimits[CascadeIndex]; float relocateLimit = probesSpacing * ProbesRelocateLimits[CascadeIndex]; - if (sdfDst > distanceLimit + length(probeOffset)) // Probe is too far from geometry (or deep inside) + uint3 probeCoordsStable = GetDDGIProbeCoords(DDGI, probeIndex); + if (sdf > probesSpacing * DDGI.ProbesCounts.x * 0.3f && + probeCoordsStable.x % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.y % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.z % DDGI_PROBE_EMPTY_AREA_DENSITY == 0) { - // Disable it + // Addd some fallback probes in empty areas to provide valid GI for nearby dynamic objects or transparency + probeOffset = float3(0, 0, 0); + probeState = wasScrolled || probeStateOld == DDGI_PROBE_STATE_INACTIVE ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE; + probeAttention = DDGI_PROBE_ATTENTION_MIN; + } + else if (sdfDst > distanceLimit + length(probeOffset)) + { + // Probe is too far from geometry (or deep inside) so disable it probeOffset = float3(0, 0, 0); probeState = DDGI_PROBE_STATE_INACTIVE; probeAttention = 0.0f; @@ -197,6 +215,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) probeAttention = clamp(probeAttention, DDGI_PROBE_ATTENTION_MIN, DDGI_PROBE_ATTENTION_MAX); // Relocate only if probe location is not good enough + BRANCH if (sdf <= voxelLimit) { #if DDGI_PROBE_RELOCATE_ITERATIVE @@ -268,6 +287,7 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) bool wasActivated = probeStateOld == DDGI_PROBE_STATE_INACTIVE; bool wasRelocated = distance(probeOffset, probeOffsetOld) > 2.0f; #if DDGI_PROBE_RELOCATE_FIND_BEST || DDGI_PROBE_RELOCATE_ITERATIVE + BRANCH if (wasRelocated && !wasActivated) { // If probe was relocated but the previous location is visible from the new one, then don't re-activate it for smoother blend @@ -326,6 +346,87 @@ void CS_UpdateProbesInitArgs() #endif +#ifdef _CS_UpdateInactiveProbes + +globallycoherent RWTexture2D RWProbesData : register(u0); + +void CheckNearbyProbe(inout uint3 fallbackCoords, inout uint probeState, uint3 probeCoords, int3 probeCoordsEnd, int3 offset) +{ + uint3 nearbyCoords = (uint3)clamp(((int3)probeCoords + offset), int3(0, 0, 0), probeCoordsEnd); + uint nearbyIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, nearbyCoords); + float4 nearbyData = RWProbesData[GetDDGIProbeTexelCoords(DDGI, CascadeIndex, nearbyIndex)]; + uint nearbyState = DecodeDDGIProbeState(nearbyData); + uint3 nearbyFallbackCoords = DDGI_FALLBACK_COORDS_DECODE(nearbyData); + if (nearbyState != DDGI_PROBE_STATE_INACTIVE) + { + // Use nearby probe + fallbackCoords = nearbyCoords; + probeState = nearbyState; + } + // TODO: optimize distance check with squared dst comparision + else if (distance((float3)nearbyFallbackCoords, (float3)probeCoords) < distance((float3)fallbackCoords, (float3)probeCoords)) + { + // Check if fallback probe is actually active (not some leftover memory) + nearbyIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, nearbyFallbackCoords); + nearbyData = RWProbesData[GetDDGIProbeTexelCoords(DDGI, CascadeIndex, nearbyIndex)]; + nearbyState = DecodeDDGIProbeState(nearbyData); + if (nearbyState != DDGI_PROBE_STATE_INACTIVE) + { + // Use fallback of the nearby probe + fallbackCoords = nearbyFallbackCoords; + probeState = DDGI_PROBE_STATE_ACTIVE; + } + } +} + +// Compute shader to store closest valid probe coords inside inactive probes data for quick fallback lookup when sampling irradiance. +META_CS(true, FEATURE_LEVEL_SM5) +[numthreads(DDGI_PROBE_CLASSIFY_GROUP_SIZE, 1, 1)] +void CS_UpdateInactiveProbes(uint3 DispatchThreadId : SV_DispatchThreadID) +{ + uint probeIndex = min(DispatchThreadId.x, ProbesCount - 1); + uint3 fallbackCoords = uint3(1000, 1000, 1000); + + // Load probe data for the current thread + uint3 probeCoords = GetDDGIProbeCoords(DDGI, probeIndex); + probeIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, probeCoords); + int2 probeDataCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex); + float4 probeData = RWProbesData[probeDataCoords]; + uint probeState = DecodeDDGIProbeState(probeData); + if (probeState == DDGI_PROBE_STATE_INACTIVE) + { + // Find the closest active probe (flood fill) + int3 probeCoordsEnd = (int3)DDGI.ProbesCounts - int3(1, 1, 1); + // Corners + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, 1, 1)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, 1, 1)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, -1, 1)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, -1, 1)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, 1, -1)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, 1, -1)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, -1, -1)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, -1, -1)); + // Sides + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, 0, 0)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(0, 1, 0)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(0, 0, 1)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, 0, 0)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(0, -1, 0)); + CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(0, 0, -1)); + } + + // Ensure all threads (within dispatch) got proepr data before writing back to the same memory + DeviceMemoryBarrierWithGroupSync(); + + // Write modified probe data back (remain inactive) + if (probeState != DDGI_PROBE_STATE_INACTIVE && DispatchThreadId.x < ProbesCount && fallbackCoords.x != 1000) + { + RWProbesData[probeDataCoords] = EncodeDDGIProbeData(DDGI_FALLBACK_COORDS_ENCODE(fallbackCoords), DDGI_PROBE_STATE_INACTIVE, 0.0f); + } +} + +#endif + #ifdef _CS_TraceRays RWTexture2D RWProbesTrace : register(u0); @@ -644,7 +745,7 @@ void CS_UpdateProbes(uint3 GroupThreadId : SV_GroupThreadID, uint3 GroupId : SV_ // Add distance (R), distance^2 (G) and weight (A) float rayDistance = CachedProbesTraceDistance[rayIndex]; - result += float4(rayDistance * rayWeight, (rayDistance * rayDistance) * rayWeight, 0.0f, rayWeight); + result += float4(rayDistance, rayDistance * rayDistance, 0.0f, 1.0f) * rayWeight; #endif } From 11ea889fa9af59c36cc7c2f98081a5d8842e32a7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Jan 2026 16:22:00 +0100 Subject: [PATCH 199/207] Refactor DDGI fallback radiance to use alpha for blending between fixed color and color at snapped location of the last cascade This means artists don't need to adjust the value anymore as it can cover vista geometry with GI at last cascade borders. --- .../Materials/MaterialShaderFeatures.cpp | 2 +- Source/Engine/Graphics/PostProcessSettings.h | 2 +- .../GI/DynamicDiffuseGlobalIllumination.cpp | 3 +- .../GI/DynamicDiffuseGlobalIllumination.h | 3 +- Source/Shaders/GI/DDGI.hlsl | 33 +++++++++---------- Source/Shaders/GI/DDGI.shader | 22 +++++++++++-- 6 files changed, 38 insertions(+), 27 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp index 64dfe8303..19f2042f4 100644 --- a/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShaderFeatures.cpp @@ -191,7 +191,7 @@ bool GlobalIlluminationFeature::Bind(MaterialShader::BindParameters& params, Spa { // Unbind SRVs to prevent issues data.DDGI.CascadesCount = 0; - data.DDGI.FallbackIrradiance = Float3::Zero; + data.DDGI.FallbackIrradiance = Float4::Zero; params.GPUContext->UnBindSR(srv + 0); params.GPUContext->UnBindSR(srv + 1); params.GPUContext->UnBindSR(srv + 2); diff --git a/Source/Engine/Graphics/PostProcessSettings.h b/Source/Engine/Graphics/PostProcessSettings.h index 670d99611..a300063e7 100644 --- a/Source/Engine/Graphics/PostProcessSettings.h +++ b/Source/Engine/Graphics/PostProcessSettings.h @@ -378,7 +378,7 @@ API_STRUCT() struct FLAXENGINE_API GlobalIlluminationSettings : ISerializable /// The irradiance lighting outside the GI range used as a fallback to prevent pure-black scene outside the Global Illumination range. /// API_FIELD(Attributes="EditorOrder(40), PostProcessSetting((int)GlobalIlluminationSettingsOverride.FallbackIrradiance)") - Color FallbackIrradiance = Color::Black; + Color FallbackIrradiance = Color::Transparent; public: /// diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index a85ba6342..27e3b49eb 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -328,7 +328,6 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont const float indirectLightingIntensity = settings.Intensity; const float probeHistoryWeight = Math::Clamp(settings.TemporalResponse, 0.0f, 0.98f); const float distance = settings.Distance; - const Color fallbackIrradiance = settings.FallbackIrradiance; // Automatically calculate amount of cascades to cover the GI distance at the current probes spacing const int32 idealProbesCount = 20; // Ideal amount of probes per-cascade to try to fit in order to cover whole distance @@ -518,7 +517,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont ddgiData.Result.Constants.ProbeHistoryWeight = probeHistoryWeight; ddgiData.Result.Constants.IrradianceGamma = 1.5f; ddgiData.Result.Constants.IndirectLightingIntensity = indirectLightingIntensity; - ddgiData.Result.Constants.FallbackIrradiance = fallbackIrradiance.ToFloat3() * fallbackIrradiance.A; + ddgiData.Result.Constants.FallbackIrradiance = settings.FallbackIrradiance.ToFloat4(); ddgiData.Result.ProbesData = ddgiData.ProbesData->View(); ddgiData.Result.ProbesDistance = ddgiData.ProbesDistance->View(); ddgiData.Result.ProbesIrradiance = ddgiData.ProbesIrradiance->View(); diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h index 85478261f..5953da887 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h @@ -25,8 +25,7 @@ public: float IndirectLightingIntensity; Float3 ViewPos; uint32 RaysCount; - Float3 FallbackIrradiance; - float Padding0; + Float4 FallbackIrradiance; }); // Binding data for the GPU. diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index a7de92577..662a634a0 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -44,8 +44,7 @@ struct DDGIData float IndirectLightingIntensity; float3 ViewPos; uint RaysCount; - float3 FallbackIrradiance; - float Padding0; + float4 FallbackIrradiance; }; uint GetDDGIProbeIndex(DDGIData data, uint3 probeCoords) @@ -164,6 +163,8 @@ float2 GetDDGIProbeUV(DDGIData data, uint cascadeIndex, uint probeIndex, float2 float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probesData, Texture2D probesDistance, Texture2D probesIrradiance, float3 worldPosition, float3 worldNormal, uint cascadeIndex, float3 probesOrigin, float3 probesExtent, float probesSpacing, float3 biasedWorldPosition) { + bool invalidCascade = cascadeIndex >= data.CascadesCount; + cascadeIndex = min(cascadeIndex, data.CascadesCount - 1); uint3 probeCoordsEnd = data.ProbesCounts - uint3(1, 1, 1); uint3 baseProbeCoords = clamp(uint3((worldPosition - probesOrigin + probesExtent) / probesSpacing), uint3(0, 0, 0), probeCoordsEnd); @@ -190,11 +191,11 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes uint3 fallbackCoords = DDGI_FALLBACK_COORDS_DECODE(probeData); float fallbackToProbeDist = length((float3)probeCoords - (float3)fallbackCoords); useVisibility = fallbackToProbeDist <= 1.0f; // Skip visibility test that blocks too far probes due to limiting max distance to 1.5 of probe spacing - if (fallbackToProbeDist > 2.0f) - minWight = 1.0f; + if (fallbackToProbeDist > 2.0f) minWight = 1.0f; probeCoords = fallbackCoords; probeIndex = GetDDGIScrollingProbeIndex(data, cascadeIndex, fallbackCoords); probeData = LoadDDGIProbeData(data, probesData, cascadeIndex, probeIndex); + //if (DecodeDDGIProbeState(probeData) == DDGI_PROBE_STATE_INACTIVE) continue; } // Calculate probe position @@ -264,7 +265,8 @@ float3 SampleDDGIIrradianceCascade(DDGIData data, Texture2D probes { // Normalize irradiance //irradiance.rgb /= irradiance.a; - irradiance.rgb /= lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f)); + //irradiance.rgb /= lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f)); + irradiance.rgb /= invalidCascade ? irradiance.a : lerp(1, irradiance.a, saturate(irradiance.a * irradiance.a + 0.9f)); #if DDGI_SRGB_BLENDING irradiance.rgb *= irradiance.rgb; #endif @@ -319,16 +321,13 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, T break; } #endif - if (cascadeIndex == data.CascadesCount) - return data.FallbackIrradiance; // Sample cascade float3 result = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascadeIndex, probesOrigin, probesExtent, probesSpacing, biasedWorldPosition); // Blend with the next cascade (or fallback irradiance outside the volume) - cascadeIndex++; #if DDGI_CASCADE_BLEND_SMOOTH && !defined(DDGI_DEBUG_CASCADE) - result *= cascadeWeight; + cascadeIndex++; if (cascadeIndex < data.CascadesCount && cascadeWeight < 0.99f) { probesSpacing = data.ProbesOriginAndSpacing[cascadeIndex].w; @@ -336,18 +335,16 @@ float3 SampleDDGIIrradiance(DDGIData data, Texture2D probesData, T probesExtent = (data.ProbesCounts - 1) * (probesSpacing * 0.5f); biasedWorldPosition = worldPosition + GetDDGISurfaceBias(viewDir, probesSpacing, worldNormal, bias); float3 resultNext = SampleDDGIIrradianceCascade(data, probesData, probesDistance, probesIrradiance, worldPosition, worldNormal, cascadeIndex, probesOrigin, probesExtent, probesSpacing, biasedWorldPosition); + result *= cascadeWeight; result += resultNext * (1 - cascadeWeight); } - else - { - result += data.FallbackIrradiance * (1 - cascadeWeight); - } -#else - if (cascadeIndex == data.CascadesCount) - { - result += data.FallbackIrradiance * (1 - cascadeWeight); - } #endif + if (cascadeIndex >= data.CascadesCount) + { + // Blend between the last cascade and the fallback irradiance + float fallbackWeight = (1 - cascadeWeight) * data.FallbackIrradiance.a; + result = lerp(result, data.FallbackIrradiance.rgb, fallbackWeight); + } return result; } diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index d12803430..0c68e4856 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -102,6 +102,11 @@ float3 Remap(float3 value, float3 fromMin, float3 fromMax, float3 toMin, float3 return (value - fromMin) / (fromMax - fromMin) * (toMax - toMin) + toMin; } +bool IsProbeAtBorder(uint3 probeCoords) +{ + return min(probeCoords.x, min(probeCoords.y, probeCoords.z)) == 0 || probeCoords.x == DDGI.ProbesCounts.x - 1 || probeCoords.y == DDGI.ProbesCounts.y - 1 || probeCoords.z == DDGI.ProbesCounts.z - 1; +} + // Compute shader for updating probes state between active and inactive and performing probes relocation. META_CS(true, FEATURE_LEVEL_SM5) [numthreads(DDGI_PROBE_CLASSIFY_GROUP_SIZE, 1, 1)] @@ -183,16 +188,27 @@ void CS_Classify(uint3 DispatchThreadId : SV_DispatchThreadID) float voxelLimit = GlobalSDF.CascadeVoxelSize[CascadeIndex] * 0.8f; float distanceLimit = probesSpacing * ProbesDistanceLimits[CascadeIndex]; float relocateLimit = probesSpacing * ProbesRelocateLimits[CascadeIndex]; +#ifdef DDGI_PROBE_EMPTY_AREA_DENSITY uint3 probeCoordsStable = GetDDGIProbeCoords(DDGI, probeIndex); - if (sdf > probesSpacing * DDGI.ProbesCounts.x * 0.3f && - probeCoordsStable.x % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.y % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.z % DDGI_PROBE_EMPTY_AREA_DENSITY == 0) + if (sdf > probesSpacing * DDGI.ProbesCounts.x * 0.3f +#if DDGI_PROBE_EMPTY_AREA_DENSITY > 1 + && ( + // Low-density grid grid + (probeCoordsStable.x % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.y % DDGI_PROBE_EMPTY_AREA_DENSITY == 0 && probeCoordsStable.z % DDGI_PROBE_EMPTY_AREA_DENSITY == 0) + // Edge probes at the last cascade (for good fallback irradiance outside the GI distance) + //|| (CascadeIndex + 1 == DDGI.CascadesCount && IsProbeAtBorder(probeCoords)) + ) +#endif + ) { // Addd some fallback probes in empty areas to provide valid GI for nearby dynamic objects or transparency probeOffset = float3(0, 0, 0); probeState = wasScrolled || probeStateOld == DDGI_PROBE_STATE_INACTIVE ? DDGI_PROBE_STATE_ACTIVATED : DDGI_PROBE_STATE_ACTIVE; probeAttention = DDGI_PROBE_ATTENTION_MIN; } - else if (sdfDst > distanceLimit + length(probeOffset)) + else +#endif + if (sdfDst > distanceLimit + length(probeOffset)) { // Probe is too far from geometry (or deep inside) so disable it probeOffset = float3(0, 0, 0); From cf3bcc45490ab285d6d51d9911da214e2083aa7c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Jan 2026 17:19:16 +0100 Subject: [PATCH 200/207] Fix DDGI flickering on floors aligned to world axes Apply a small bias to sample location and dither it a bit to smooth out. --- Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp | 1 - Source/Shaders/GI/DDGI.shader | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 27e3b49eb..0b0d9a365 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -376,7 +376,6 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont blendOrigins[cascadeIndex] = viewOrigin; const float viewOriginSnapping = cascadeProbesSpacing; viewOrigin = Float3::Floor(viewOrigin / viewOriginSnapping) * viewOriginSnapping; - viewOrigin -= UNITS_TO_METERS(0.5f); // Bias to avoid precision issues (eg. if floor mesh is exactly at Y=0) viewOrigins[cascadeIndex] = viewOrigin; } diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index 0c68e4856..f6f2d05a9 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -913,7 +913,8 @@ void PS_IndirectLighting(Quad_VS2PS input, out float4 output : SV_Target0) // Sample irradiance float dither = RandN2(input.TexCoord + TemporalTime).x; - float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, gBuffer.WorldPos, gBuffer.Normal, DDGI_DEFAULT_BIAS, dither); + float3 samplePos = gBuffer.WorldPos + gBuffer.Normal * (dither * 0.1f + 0.1f); + float3 irradiance = SampleDDGIIrradiance(DDGI, ProbesData, ProbesDistance, ProbesIrradiance, samplePos, gBuffer.Normal, DDGI_DEFAULT_BIAS, dither); // Calculate lighting float3 diffuseColor = GetDiffuseColor(gBuffer); From 0975ac2a251a56b13e82a10b3d31a70ae5e0193e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 5 Jan 2026 18:02:05 +0100 Subject: [PATCH 201/207] Fix odd typo in DDGI inactive probes flood fill --- Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 0b0d9a365..973f91b3d 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -595,7 +595,7 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont { PROFILE_GPU_CPU_NAMED("Update Inactive Probes"); context->BindUA(0, ddgiData.Result.ProbesData); - int32 iterations = Math::Min(probesCounts.MaxValue() - 1, DDGI_PROBE_EMPTY_AREA_DENSITY) * 10; + int32 iterations = Math::Min(probesCounts.MaxValue() - 1, DDGI_PROBE_EMPTY_AREA_DENSITY); for (int32 i = 0; i < iterations; i++) context->Dispatch(_csUpdateInactiveProbes, threadGroupsX, 1, 1); context->ResetUA(); From 32e725392b58b9cdd79d8413e8fe57e43ec09067 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Jan 2026 00:01:17 +0100 Subject: [PATCH 202/207] Optimize inactive probes update in DDGI with faster Jump Flood instead of Flood Fill --- .../GI/DynamicDiffuseGlobalIllumination.cpp | 16 +++-- Source/Shaders/GI/DDGI.hlsl | 5 +- Source/Shaders/GI/DDGI.shader | 70 ++++++++----------- 3 files changed, 46 insertions(+), 45 deletions(-) diff --git a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp index 973f91b3d..25550ecd8 100644 --- a/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp +++ b/Source/Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.cpp @@ -42,7 +42,7 @@ #define DDGI_PROBE_RESOLUTION_DISTANCE 14 // Resolution (in texels) for probe distance data (excluding 1px padding on each side) #define DDGI_PROBE_UPDATE_BORDERS_GROUP_SIZE 8 #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 -#define DDGI_PROBE_EMPTY_AREA_DENSITY 10 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency +#define DDGI_PROBE_EMPTY_AREA_DENSITY 8 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency #define DDGI_DEBUG_STATS 0 // Enables additional GPU-driven stats for probe/rays count #define DDGI_DEBUG_INSTABILITY 0 // Enables additional probe irradiance instability debugging @@ -76,7 +76,8 @@ GPU_CB_STRUCT(Data0 { GPU_CB_STRUCT(Data1 { // TODO: use push constants on Vulkan or root signature data on DX12 to reduce overhead of changing single DWORD - Float2 Padding2; + float Padding2; + int32 StepSize; uint32 CascadeIndex; uint32 ProbeIndexOffset; }); @@ -594,10 +595,17 @@ bool DynamicDiffuseGlobalIlluminationPass::RenderInner(RenderContext& renderCont // For inactive probes, search nearby ones to find the closest valid for quick fallback when sampling irradiance { PROFILE_GPU_CPU_NAMED("Update Inactive Probes"); + // TODO: this could run within GPUComputePass during Trace Rays or Update Probes to overlap compute works context->BindUA(0, ddgiData.Result.ProbesData); - int32 iterations = Math::Min(probesCounts.MaxValue() - 1, DDGI_PROBE_EMPTY_AREA_DENSITY); - for (int32 i = 0; i < iterations; i++) + Data1 data; + data.CascadeIndex = cascadeIndex; + int32 iterations = Math::CeilToInt(Math::Log2((float)Math::Min(probesCounts.MaxValue(), DDGI_PROBE_EMPTY_AREA_DENSITY) + 1.0f)); + for (int32 i = iterations - 1; i >= 0; i--) + { + data.StepSize = Math::FloorToInt(Math::Pow(2, (float)i) + 0.5f); // Jump Flood step size + context->UpdateCB(_cb1, &data); context->Dispatch(_csUpdateInactiveProbes, threadGroupsX, 1, 1); + } context->ResetUA(); } diff --git a/Source/Shaders/GI/DDGI.hlsl b/Source/Shaders/GI/DDGI.hlsl index 662a634a0..3e31c2e53 100644 --- a/Source/Shaders/GI/DDGI.hlsl +++ b/Source/Shaders/GI/DDGI.hlsl @@ -26,8 +26,9 @@ #endif #define DDGI_SRGB_BLENDING 1 // Enables blending in sRGB color space, otherwise irradiance blending is done in linear space #define DDGI_DEFAULT_BIAS 0.2f // Default value for DDGI sampling bias -#define DDGI_FALLBACK_COORDS_ENCODE(coord) ((float3)coord / 128.0f) -#define DDGI_FALLBACK_COORDS_DECODE(data) (uint3)(data.xyz * 128.0f) +#define DDGI_FALLBACK_COORDS_ENCODE(coord) ((float3)(coord + 1) / 128.0f) +#define DDGI_FALLBACK_COORDS_DECODE(data) (uint3)(data.xyz * 128.0f - 1) +#define DDGI_FALLBACK_COORDS_VALID(data) (length(data.xyz) > 0) //#define DDGI_DEBUG_CASCADE 0 // Forces a specific cascade to be only in use (for debugging) // DDGI data for a constant buffer diff --git a/Source/Shaders/GI/DDGI.shader b/Source/Shaders/GI/DDGI.shader index f6f2d05a9..b080efc0b 100644 --- a/Source/Shaders/GI/DDGI.shader +++ b/Source/Shaders/GI/DDGI.shader @@ -27,7 +27,7 @@ #define DDGI_PROBE_CLASSIFY_GROUP_SIZE 32 #define DDGI_PROBE_RELOCATE_ITERATIVE 1 // If true, probes relocation algorithm tries to move them in additive way, otherwise all nearby locations are checked to find the best position #define DDGI_PROBE_RELOCATE_FIND_BEST 1 // If true, probes relocation algorithm tries to move to the best matching location within nearby area -#define DDGI_PROBE_EMPTY_AREA_DENSITY 10 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency +#define DDGI_PROBE_EMPTY_AREA_DENSITY 8 // Spacing (in probe grid) between fallback probes placed into empty areas to provide valid GI for nearby dynamic objects or transparency #define DDGI_DEBUG_STATS 0 // Enables additional GPU-driven stats for probe/rays count #define DDGI_DEBUG_INSTABILITY 0 // Enables additional probe irradiance instability debugging @@ -49,7 +49,8 @@ uint FrameIndexMod8; META_CB_END META_CB_BEGIN(1, Data1) -float2 Padding2; +float Padding2; +int StepSize; uint CascadeIndex; uint ProbeIndexOffset; META_CB_END @@ -364,38 +365,35 @@ void CS_UpdateProbesInitArgs() #ifdef _CS_UpdateInactiveProbes -globallycoherent RWTexture2D RWProbesData : register(u0); +RWTexture2D RWProbesData : register(u0); -void CheckNearbyProbe(inout uint3 fallbackCoords, inout uint probeState, uint3 probeCoords, int3 probeCoordsEnd, int3 offset) +void CheckNearbyProbe(inout uint3 fallbackCoords, inout uint probeState, inout float minDistance, uint3 probeCoords, int3 probeCoordsEnd, int3 offset) { uint3 nearbyCoords = (uint3)clamp(((int3)probeCoords + offset), int3(0, 0, 0), probeCoordsEnd); uint nearbyIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, nearbyCoords); float4 nearbyData = RWProbesData[GetDDGIProbeTexelCoords(DDGI, CascadeIndex, nearbyIndex)]; - uint nearbyState = DecodeDDGIProbeState(nearbyData); - uint3 nearbyFallbackCoords = DDGI_FALLBACK_COORDS_DECODE(nearbyData); - if (nearbyState != DDGI_PROBE_STATE_INACTIVE) + float nearbyDist = distance((float3)nearbyCoords, (float3)probeCoords); + if (DecodeDDGIProbeState(nearbyData) != DDGI_PROBE_STATE_INACTIVE && nearbyDist < minDistance) { // Use nearby probe fallbackCoords = nearbyCoords; - probeState = nearbyState; + probeState = DDGI_PROBE_STATE_ACTIVE; + minDistance = nearbyDist; + return; } - // TODO: optimize distance check with squared dst comparision - else if (distance((float3)nearbyFallbackCoords, (float3)probeCoords) < distance((float3)fallbackCoords, (float3)probeCoords)) + nearbyCoords = DDGI_FALLBACK_COORDS_DECODE(nearbyData); + nearbyDist = distance((float3)nearbyCoords, (float3)probeCoords); + if (DDGI_FALLBACK_COORDS_VALID(nearbyData) && nearbyDist < minDistance) { - // Check if fallback probe is actually active (not some leftover memory) - nearbyIndex = GetDDGIScrollingProbeIndex(DDGI, CascadeIndex, nearbyFallbackCoords); - nearbyData = RWProbesData[GetDDGIProbeTexelCoords(DDGI, CascadeIndex, nearbyIndex)]; - nearbyState = DecodeDDGIProbeState(nearbyData); - if (nearbyState != DDGI_PROBE_STATE_INACTIVE) - { - // Use fallback of the nearby probe - fallbackCoords = nearbyFallbackCoords; - probeState = DDGI_PROBE_STATE_ACTIVE; - } + // Use fallback probe + fallbackCoords = nearbyCoords; + probeState = DDGI_PROBE_STATE_ACTIVE; + minDistance = nearbyDist; } } // Compute shader to store closest valid probe coords inside inactive probes data for quick fallback lookup when sampling irradiance. +// Uses Jump Flood algorithm. META_CS(true, FEATURE_LEVEL_SM5) [numthreads(DDGI_PROBE_CLASSIFY_GROUP_SIZE, 1, 1)] void CS_UpdateInactiveProbes(uint3 DispatchThreadId : SV_DispatchThreadID) @@ -409,32 +407,26 @@ void CS_UpdateInactiveProbes(uint3 DispatchThreadId : SV_DispatchThreadID) int2 probeDataCoords = GetDDGIProbeTexelCoords(DDGI, CascadeIndex, probeIndex); float4 probeData = RWProbesData[probeDataCoords]; uint probeState = DecodeDDGIProbeState(probeData); + BRANCH if (probeState == DDGI_PROBE_STATE_INACTIVE) { - // Find the closest active probe (flood fill) + // Find the closest active probe (Jump Flood) int3 probeCoordsEnd = (int3)DDGI.ProbesCounts - int3(1, 1, 1); - // Corners - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, 1, 1)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, 1, 1)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, -1, 1)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, -1, 1)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, 1, -1)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, 1, -1)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, -1, -1)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, -1, -1)); - // Sides - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(1, 0, 0)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(0, 1, 0)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(0, 0, 1)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(-1, 0, 0)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(0, -1, 0)); - CheckNearbyProbe(fallbackCoords, probeState, probeCoords, probeCoordsEnd, int3(0, 0, -1)); + float minDistance = 1e27f; + UNROLL for (int z = -1; z <= 1; z++) + UNROLL for (int y = -1; y <= 1; y++) + UNROLL for (int x = -1; x <= 1; x++) + { + int3 offset = int3(x, y, z) * StepSize; + CheckNearbyProbe(fallbackCoords, probeState, minDistance, probeCoords, probeCoordsEnd, offset); + } } - // Ensure all threads (within dispatch) got proepr data before writing back to the same memory - DeviceMemoryBarrierWithGroupSync(); + // Ensure all threads (within dispatch) got proper data before writing back to the same memory + AllMemoryBarrierWithGroupSync(); // Write modified probe data back (remain inactive) + BRANCH if (probeState != DDGI_PROBE_STATE_INACTIVE && DispatchThreadId.x < ProbesCount && fallbackCoords.x != 1000) { RWProbesData[probeDataCoords] = EncodeDDGIProbeData(DDGI_FALLBACK_COORDS_ENCODE(fallbackCoords), DDGI_PROBE_STATE_INACTIVE, 0.0f); From 76b869146d26178b608456ca988e6ba5f9aedc6b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Jan 2026 00:03:35 +0100 Subject: [PATCH 203/207] Fix missing scripting api tag on `MeshAccessor::Stream` --- Source/Engine/Graphics/Models/MeshAccessor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Graphics/Models/MeshAccessor.h b/Source/Engine/Graphics/Models/MeshAccessor.h index 67b30e502..25fc01a1a 100644 --- a/Source/Engine/Graphics/Models/MeshAccessor.h +++ b/Source/Engine/Graphics/Models/MeshAccessor.h @@ -17,7 +17,7 @@ public: /// /// Mesh data stream. /// - struct Stream + struct FLAXENGINE_API Stream { friend MeshAccessor; From afc65f75571fca59294d8a23c03a1f558bfcaf7c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Jan 2026 23:31:18 +0100 Subject: [PATCH 204/207] Update engine materials after version change --- Content/Editor/Camera/M_Camera.flax | 2 +- Content/Editor/CubeTexturePreviewMaterial.flax | 2 +- Content/Editor/DebugMaterials/DDGIDebugProbes.flax | 2 +- Content/Editor/DebugMaterials/SingleColor/Decal.flax | 4 ++-- Content/Editor/DebugMaterials/SingleColor/Particle.flax | 2 +- Content/Editor/DebugMaterials/SingleColor/Surface.flax | 2 +- .../Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax | 2 +- Content/Editor/DebugMaterials/SingleColor/Terrain.flax | 2 +- Content/Editor/DefaultFontMaterial.flax | 2 +- Content/Editor/Gizmo/FoliageBrushMaterial.flax | 2 +- Content/Editor/Gizmo/Material.flax | 2 +- Content/Editor/Gizmo/MaterialWire.flax | 2 +- Content/Editor/Gizmo/SelectionOutlineMaterial.flax | 4 ++-- Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax | 2 +- Content/Editor/Highlight Material.flax | 4 ++-- Content/Editor/Icons/IconsMaterial.flax | 2 +- Content/Editor/IesProfilePreviewMaterial.flax | 2 +- Content/Editor/Particles/Particle Material Color.flax | 2 +- Content/Editor/Particles/Smoke Material.flax | 4 ++-- Content/Editor/SpriteMaterial.flax | 2 +- Content/Editor/Terrain/Circle Brush Material.flax | 2 +- Content/Editor/Terrain/Highlight Terrain Material.flax | 2 +- Content/Editor/TexturePreviewMaterial.flax | 2 +- Content/Editor/Wires Debug Material.flax | 4 ++-- Content/Engine/DefaultDeformableMaterial.flax | 4 ++-- Content/Engine/DefaultMaterial.flax | 4 ++-- Content/Engine/DefaultRadialMenu.flax | 2 +- Content/Engine/DefaultTerrainMaterial.flax | 4 ++-- Content/Engine/SingleColorMaterial.flax | 2 +- Content/Engine/SkyboxMaterial.flax | 2 +- Content/Shaders/GI/DDGI.flax | 4 ++-- Content/Shaders/GI/GlobalSurfaceAtlas.flax | 4 ++-- Content/Shaders/GlobalSignDistanceField.flax | 4 ++-- 33 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Content/Editor/Camera/M_Camera.flax b/Content/Editor/Camera/M_Camera.flax index 7d7213a8d..7d4c71666 100644 --- a/Content/Editor/Camera/M_Camera.flax +++ b/Content/Editor/Camera/M_Camera.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7edc1b9d2c7fbd32fcf778814deb719c71781f657da050ac0c7c78984aeb360d +oid sha256:b73d774c71bd7b46c9c4198a4c957055e6447e31d8252813b272db92301475e7 size 29533 diff --git a/Content/Editor/CubeTexturePreviewMaterial.flax b/Content/Editor/CubeTexturePreviewMaterial.flax index 5969c90fa..2d732c086 100644 --- a/Content/Editor/CubeTexturePreviewMaterial.flax +++ b/Content/Editor/CubeTexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac6023e5d6525c3b7c385a380ed9d6fc71ec9d683c587391d14c9daf6653e31a +oid sha256:c4ec07a3b7e0a2dfd4332598a982c3192c0c357c6bcd128d7a7797fb483780e7 size 31445 diff --git a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax index fc45d33cc..d082bd8e7 100644 --- a/Content/Editor/DebugMaterials/DDGIDebugProbes.flax +++ b/Content/Editor/DebugMaterials/DDGIDebugProbes.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ecd573c40f534f293d4827b1a8150d439d4f5e7729552474926208c5814f3d3e +oid sha256:2830919bea988e1f8bd8299ceac34b8a3695418e2f22ca670f2fec3b3d6d1a2f size 41149 diff --git a/Content/Editor/DebugMaterials/SingleColor/Decal.flax b/Content/Editor/DebugMaterials/SingleColor/Decal.flax index 05e99be76..b94f22bc8 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Decal.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Decal.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0b2ad25738c2bc55bb3e76fc94fc81992b1d65b8b3091b132c75b2ed064c517 -size 10398 +oid sha256:588c29a4b239c32d4b125052e4054a29cf5140562e90ca6fac4d2952e03f66c7 +size 10397 diff --git a/Content/Editor/DebugMaterials/SingleColor/Particle.flax b/Content/Editor/DebugMaterials/SingleColor/Particle.flax index 7a328e7a0..de2043874 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Particle.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Particle.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:02ddea5bcb3fccb697081e47cc26a0b546b23b89ceca299e702a1d431775dfd6 +oid sha256:b39cd76254f341c93e83625475b6e7896ef34f1d6d650da52e649bc055d0d03e size 33503 diff --git a/Content/Editor/DebugMaterials/SingleColor/Surface.flax b/Content/Editor/DebugMaterials/SingleColor/Surface.flax index 84e05ee36..7ae8a69c3 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Surface.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Surface.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:741a7619b5aebc6c7c6a573a0407e8b7aa42d1b50d0ed5cf6a21026932807d0e +oid sha256:5861e912cf822c9478f824390f6258d84821b7289e3e993a7dee38b77c5a2f80 size 29398 diff --git a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax index ab4591176..fdcb880df 100644 --- a/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax +++ b/Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:358370943d21a97f8b45ff2181b7c6c2d7a6297e3f166ae7a77363aadf89b152 +oid sha256:b9ed2869a2a754423e0b8c456eed621bd06bdb50cacf7a972a7f024e40a1ea6a size 32954 diff --git a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax index 54151179a..ad27a422c 100644 --- a/Content/Editor/DebugMaterials/SingleColor/Terrain.flax +++ b/Content/Editor/DebugMaterials/SingleColor/Terrain.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:486b4db3e1d825d026753d944a04defe4d72eb73eb03a438944c366f19de824e +oid sha256:05c27ac416ef922ee247adc12a115fd522eb3a1d8873e1056914cd96893a3097 size 21096 diff --git a/Content/Editor/DefaultFontMaterial.flax b/Content/Editor/DefaultFontMaterial.flax index 8d48c5827..d84425aab 100644 --- a/Content/Editor/DefaultFontMaterial.flax +++ b/Content/Editor/DefaultFontMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebdfc478caabc84a3a75384a64d262d2d509bbac3540eea462e45911719c288f +oid sha256:8e3d4ca149e143fee31e2d038b8efec526ca995dbe13258fbb68c89cd43ecbf7 size 29627 diff --git a/Content/Editor/Gizmo/FoliageBrushMaterial.flax b/Content/Editor/Gizmo/FoliageBrushMaterial.flax index 79385ada6..eb7e784c9 100644 --- a/Content/Editor/Gizmo/FoliageBrushMaterial.flax +++ b/Content/Editor/Gizmo/FoliageBrushMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa4f1a733150c62064cac60c07980df7c84bb6163dc9507782aa98df07f48874 +oid sha256:7af1150d6e7cb6ecce5cd039f0edc92967c986a13903a201d6dc15ed0751dc57 size 39637 diff --git a/Content/Editor/Gizmo/Material.flax b/Content/Editor/Gizmo/Material.flax index ace3bde90..bbb114662 100644 --- a/Content/Editor/Gizmo/Material.flax +++ b/Content/Editor/Gizmo/Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26e1832496c01cb31bd4dc9000d3cd326ea4fd54de02910d3801d2641bff685c +oid sha256:d575ca1b202c84b8268687b391be5fc8d55497ffa23fb3cd4287fa667de654ab size 34240 diff --git a/Content/Editor/Gizmo/MaterialWire.flax b/Content/Editor/Gizmo/MaterialWire.flax index 7ea0a596f..fb4b8acca 100644 --- a/Content/Editor/Gizmo/MaterialWire.flax +++ b/Content/Editor/Gizmo/MaterialWire.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca8bc1ac9d45534d3efd3b4308d7492fa016726b4ec744be26619069ce911b73 +oid sha256:26f2d88aab9c0cad36ae527b038a36b69755ff3a5a920e8c4563dd5e1ed8ec65 size 32689 diff --git a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax index 0c1461b72..b5d224d58 100644 --- a/Content/Editor/Gizmo/SelectionOutlineMaterial.flax +++ b/Content/Editor/Gizmo/SelectionOutlineMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:09f7dff17af9cd055352e0da534f3466c8efa235c40faf5e56da92c788342f6a -size 17394 +oid sha256:5bb75934622d9251a8a9e72cfe4905091770798ffed22de680a70f98434d0ed7 +size 16241 diff --git a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax index bd4935d96..5a5262e2b 100644 --- a/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax +++ b/Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bc0005c64c561a430a17e4707abc000e06498af968890e2c4e223dc07f07c12 +oid sha256:a1afa76c3f9400da065c150a6a58adc904c3596f650e04dfd87b5e1c1b34695e size 30655 diff --git a/Content/Editor/Highlight Material.flax b/Content/Editor/Highlight Material.flax index ccecb98aa..9d09ea792 100644 --- a/Content/Editor/Highlight Material.flax +++ b/Content/Editor/Highlight Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95d172cd12bb3c818fbccf737e78ab282bc8d0880aa8f45af0562850b0eabe4b -size 31616 +oid sha256:1290ae85e4fe41f9d8c1919b33e165287f79377aeddc68f9117c1795ca341003 +size 31267 diff --git a/Content/Editor/Icons/IconsMaterial.flax b/Content/Editor/Icons/IconsMaterial.flax index b24941463..2ccbce8c9 100644 --- a/Content/Editor/Icons/IconsMaterial.flax +++ b/Content/Editor/Icons/IconsMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ca4baa1419080395dcf2b5757676406288f112754bc3cd2f27610b58d199622 +oid sha256:340cc500a160344b43b21ed8c4c22b6d776f406581f606ced62a3e92c5bef18a size 31300 diff --git a/Content/Editor/IesProfilePreviewMaterial.flax b/Content/Editor/IesProfilePreviewMaterial.flax index 99bc2662c..b3a382132 100644 --- a/Content/Editor/IesProfilePreviewMaterial.flax +++ b/Content/Editor/IesProfilePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3b4c61b04d372d2430a7c08dec612af6caa0e57b1cb47ea44d171d729d3f8f8 +oid sha256:d444cd33ec8d2e1e0e6651c3979260f05c06c8bac33ce2441d6974ae4fa178e4 size 20443 diff --git a/Content/Editor/Particles/Particle Material Color.flax b/Content/Editor/Particles/Particle Material Color.flax index 19eb7a3c2..91b06b2fb 100644 --- a/Content/Editor/Particles/Particle Material Color.flax +++ b/Content/Editor/Particles/Particle Material Color.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f3b8a7c48c55e33a41f9fe4dbf9b3109b0e734ff154d6cbd3e4101013b01649 +oid sha256:906443c7db821361b32780c17735bc9477ea96c8979dee371a4899635246af48 size 31708 diff --git a/Content/Editor/Particles/Smoke Material.flax b/Content/Editor/Particles/Smoke Material.flax index 527d19842..e6396c194 100644 --- a/Content/Editor/Particles/Smoke Material.flax +++ b/Content/Editor/Particles/Smoke Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2275282d4e3b5e012a0bbc93fca0d6ffdad89e5a5f0c289678f70748f2efab56 -size 40655 +oid sha256:16db9c1a18b64aea2dcdf3e74f9a44c652bf8bd9b33a5bfda39555d8c002a358 +size 39774 diff --git a/Content/Editor/SpriteMaterial.flax b/Content/Editor/SpriteMaterial.flax index d967a4ea4..2a05418b2 100644 --- a/Content/Editor/SpriteMaterial.flax +++ b/Content/Editor/SpriteMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f5e82be7efa6489cfdfd1babeb1fbb90507aaff7c04eb5f64a4971adf0a2164 +oid sha256:56254b02ffc937d61e8e8fa6492d4805e944ca639c7fcfc0f751b4ac2442365d size 30734 diff --git a/Content/Editor/Terrain/Circle Brush Material.flax b/Content/Editor/Terrain/Circle Brush Material.flax index 6ddc5f3e9..f481be389 100644 --- a/Content/Editor/Terrain/Circle Brush Material.flax +++ b/Content/Editor/Terrain/Circle Brush Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c7fde7be7d6f9876f9c0db02632c098ab95ade7de57c583d2e495c8ae8665bd +oid sha256:16eefa75a2ae99bba658c4e9b8e8741187b90e577193f76394872764fff2ca0b size 28232 diff --git a/Content/Editor/Terrain/Highlight Terrain Material.flax b/Content/Editor/Terrain/Highlight Terrain Material.flax index c573eb3ee..579db477c 100644 --- a/Content/Editor/Terrain/Highlight Terrain Material.flax +++ b/Content/Editor/Terrain/Highlight Terrain Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9369a554ea1776154f5e39d4aaed044f928d98f1f5955b7590b0972015b07438 +oid sha256:e25a3c9e130e51b28dfe5ce43678f52c277c0def83142a2853c4c8ca84dbf417 size 21179 diff --git a/Content/Editor/TexturePreviewMaterial.flax b/Content/Editor/TexturePreviewMaterial.flax index 2c91f9d8f..d75e19d5e 100644 --- a/Content/Editor/TexturePreviewMaterial.flax +++ b/Content/Editor/TexturePreviewMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4d61f178e72e4d983a919b76368e03c66995ecf50935f6f55b660e34f58755a2 +oid sha256:79de09ba0616eb6066171c2b80cdb6c4235cb52be4836d23162bb9c2585760a0 size 11058 diff --git a/Content/Editor/Wires Debug Material.flax b/Content/Editor/Wires Debug Material.flax index 308a6230a..b1f87a7d0 100644 --- a/Content/Editor/Wires Debug Material.flax +++ b/Content/Editor/Wires Debug Material.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c7a42b1bc5a34f9c47d1aeb773ef26ce470b2d88c2b092828f0fcb439583ef27 -size 31616 +oid sha256:02d4c767fb59c67fef16ccc081f6f371bad329a5333047f9f79fd3d50b911f93 +size 31753 diff --git a/Content/Engine/DefaultDeformableMaterial.flax b/Content/Engine/DefaultDeformableMaterial.flax index a397d1ad8..1244ae3ec 100644 --- a/Content/Engine/DefaultDeformableMaterial.flax +++ b/Content/Engine/DefaultDeformableMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be21bb7eecd9c774196dbaa89d1b049b108fc0929d648795056c977fe00364ab -size 19582 +oid sha256:d1f556b230cea8e83d00bd4357d34a77e5e468389a5f3bb615e30f6a3ce3ace4 +size 19734 diff --git a/Content/Engine/DefaultMaterial.flax b/Content/Engine/DefaultMaterial.flax index eddcbace8..bd57e7d44 100644 --- a/Content/Engine/DefaultMaterial.flax +++ b/Content/Engine/DefaultMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a8a4ad5e763704263b94a7a7e0cc30ab7b1cd1abcb5ccae2d4c6062a65920df -size 31928 +oid sha256:c4ec872b3433d58f8aed640c6efee3d911f226740b4844cb07ed0bf94c00ea18 +size 32080 diff --git a/Content/Engine/DefaultRadialMenu.flax b/Content/Engine/DefaultRadialMenu.flax index 60e2ba5f9..5fba9092e 100644 --- a/Content/Engine/DefaultRadialMenu.flax +++ b/Content/Engine/DefaultRadialMenu.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c4151a58e5314937efcd3bdcb9fe0bdd5047b8705931e45e0a4e71a4470e16a0 +oid sha256:0da99403c069966d05daea7fc11d32f20f88bac0463fbc08724840e249ee3bd2 size 21700 diff --git a/Content/Engine/DefaultTerrainMaterial.flax b/Content/Engine/DefaultTerrainMaterial.flax index b302ade35..4147fe0e4 100644 --- a/Content/Engine/DefaultTerrainMaterial.flax +++ b/Content/Engine/DefaultTerrainMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c5cf6924809b9bd7ad3c09722a93f327a0d111676060d136df9c14ab34e8475b -size 23930 +oid sha256:bdfa3b4842a5734d2cd8110af03599b4a5280b33a72b2ba435cd19487cebcde6 +size 24082 diff --git a/Content/Engine/SingleColorMaterial.flax b/Content/Engine/SingleColorMaterial.flax index d6d179150..6d556af2b 100644 --- a/Content/Engine/SingleColorMaterial.flax +++ b/Content/Engine/SingleColorMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:750f69ce59ef020d2e2186ed6c4bf7aac67ecb1692287e358eaed969fc36381a +oid sha256:6ff8f127d46e68e3423339a352f623c079f2c5d93512c5e9b25841edc7cd0f05 size 29615 diff --git a/Content/Engine/SkyboxMaterial.flax b/Content/Engine/SkyboxMaterial.flax index cc369ceee..b51c5bce7 100644 --- a/Content/Engine/SkyboxMaterial.flax +++ b/Content/Engine/SkyboxMaterial.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3eecc9556af6c2a79d39a7e1c52e4019bdccfb43b074eaddd18600a5854dbffe +oid sha256:14c9833ed19302ea7c6e730fff63f1b72dbac71dc2b49c1d62edb61ccaa68b6f size 31974 diff --git a/Content/Shaders/GI/DDGI.flax b/Content/Shaders/GI/DDGI.flax index 6739b2436..257953bf9 100644 --- a/Content/Shaders/GI/DDGI.flax +++ b/Content/Shaders/GI/DDGI.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5577ef4ce821b08a38afe17b9e5d11cb0b409eb05dd89b2ca76ea95d88085dc0 -size 32893 +oid sha256:5b017cf857f443553020e4bc7c8c8c5da3a826a2514322664a023ffa6005f7a5 +size 38217 diff --git a/Content/Shaders/GI/GlobalSurfaceAtlas.flax b/Content/Shaders/GI/GlobalSurfaceAtlas.flax index 1b0173ba5..57990c249 100644 --- a/Content/Shaders/GI/GlobalSurfaceAtlas.flax +++ b/Content/Shaders/GI/GlobalSurfaceAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f34bf867df5f4296ca66ac691c2bca4efa168fb9e21ca4e613e8086669575cf -size 13296 +oid sha256:615dff65b01507be6c4de722e126324aba20fc197f8e12dafaa94a05e46cba6e +size 13222 diff --git a/Content/Shaders/GlobalSignDistanceField.flax b/Content/Shaders/GlobalSignDistanceField.flax index 590e8f3a9..5afcb4bf4 100644 --- a/Content/Shaders/GlobalSignDistanceField.flax +++ b/Content/Shaders/GlobalSignDistanceField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:064f54786958f109222c49cbc0358ff4f345b30010fcd5e8cc1fab7bdc68c4fe -size 13349 +oid sha256:1f07ebb16820897e8598ae7a0627cb75b3d28e9dceea3ad4bd9ff543d5cdd01c +size 13979 From 01e3ece2afde31c784fd98e6eecbc46e7bf06277 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 6 Jan 2026 23:31:32 +0100 Subject: [PATCH 205/207] Remove leftover debug --- Source/Engine/Platform/Windows/WindowsPlatform.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index e40ae1ca7..42accf298 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -543,7 +543,6 @@ void WindowsPlatform::ReleaseMutex() } } -PRAGMA_DISABLE_OPTIMIZATION; void CheckInstructionSet() { #if PLATFORM_ARCH_X86 || PLATFORM_ARCH_X64 From d71ea5a72be7534fef2f0bd42502885191f2b77f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 7 Jan 2026 17:57:03 +0100 Subject: [PATCH 206/207] DIsable temporal jitter in debug views of Vertex Colors and Quad Overdraw --- Source/Engine/Renderer/Renderer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Renderer/Renderer.cpp b/Source/Engine/Renderer/Renderer.cpp index 7a72cd923..fd7d43c8b 100644 --- a/Source/Engine/Renderer/Renderer.cpp +++ b/Source/Engine/Renderer/Renderer.cpp @@ -402,6 +402,8 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext, RenderCont case ViewMode::MaterialComplexity: case ViewMode::Wireframe: case ViewMode::NoPostFx: + case ViewMode::VertexColors: + case ViewMode::QuadOverdraw: setup.UseTemporalAAJitter = false; break; } From 688d9c77cb74f0f2fa79f923b6a7bf3ea66a0f3f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 7 Jan 2026 18:24:48 +0100 Subject: [PATCH 207/207] Fix rendering postfx with color grading only in use and optimize color grading LUT to be skipped when unsued --- Source/Engine/Renderer/ColorGradingPass.cpp | 91 +++++++++++-------- Source/Engine/Renderer/PostProcessingPass.cpp | 2 +- 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/Source/Engine/Renderer/ColorGradingPass.cpp b/Source/Engine/Renderer/ColorGradingPass.cpp index d6e164622..c0b40d3f6 100644 --- a/Source/Engine/Renderer/ColorGradingPass.cpp +++ b/Source/Engine/Renderer/ColorGradingPass.cpp @@ -37,8 +37,45 @@ GPU_CB_STRUCT(Data { Float3 Dummy; float LutWeight; + + void Init(const PostProcessSettings& settings, GPUTexture*& lut) + { + Dummy = Float2::Zero; + auto& toneMapping = settings.ToneMapping; + auto& colorGrading = settings.ColorGrading; + // White Balance + WhiteTemp = toneMapping.WhiteTemperature; + WhiteTint = toneMapping.WhiteTint; + // Shadows + ColorSaturationShadows = colorGrading.ColorSaturationShadows * colorGrading.ColorSaturation; + ColorContrastShadows = colorGrading.ColorContrastShadows * colorGrading.ColorContrast; + ColorGammaShadows = colorGrading.ColorGammaShadows * colorGrading.ColorGamma; + ColorGainShadows = colorGrading.ColorGainShadows * colorGrading.ColorGain; + ColorOffsetShadows = colorGrading.ColorOffsetShadows + colorGrading.ColorOffset; + ColorCorrectionShadowsMax = colorGrading.ShadowsMax; + // Midtones + ColorSaturationMidtones = colorGrading.ColorSaturationMidtones * colorGrading.ColorSaturation; + ColorContrastMidtones = colorGrading.ColorContrastMidtones * colorGrading.ColorContrast; + ColorGammaMidtones = colorGrading.ColorGammaMidtones * colorGrading.ColorGamma; + ColorGainMidtones = colorGrading.ColorGainMidtones * colorGrading.ColorGain; + ColorOffsetMidtones = colorGrading.ColorOffsetMidtones + colorGrading.ColorOffset; + // Highlights + ColorSaturationHighlights = colorGrading.ColorSaturationHighlights * colorGrading.ColorSaturation; + ColorContrastHighlights = colorGrading.ColorContrastHighlights * colorGrading.ColorContrast; + ColorGammaHighlights = colorGrading.ColorGammaHighlights * colorGrading.ColorGamma; + ColorGainHighlights = colorGrading.ColorGainHighlights * colorGrading.ColorGain; + ColorOffsetHighlights = colorGrading.ColorOffsetHighlights + colorGrading.ColorOffset; + ColorCorrectionHighlightsMin = colorGrading.HighlightsMin; + // + Texture* lutTexture = colorGrading.LutTexture.Get(); + const bool useLut = lutTexture && lutTexture->IsLoaded() && lutTexture->GetResidentMipLevels() > 0 && colorGrading.LutWeight > ZeroTolerance; + LutWeight = useLut ? colorGrading.LutWeight : 0.0f; + lut = useLut ? lutTexture->GetTexture() : nullptr; + } }); +Data DefaultData; + // Custom render buffer for caching Color Grading LUT. class ColorGradingCustomBuffer : public RenderBuffers::CustomBuffer { @@ -46,7 +83,7 @@ public: GPUTexture* LUT = nullptr; Data CachedData; ToneMappingMode Mode = ToneMappingMode::None; - Texture* LutTexture = nullptr; + GPUTexture* LutTexture = nullptr; #if COMPILE_WITH_DEV_ENV uint64 FrameRendered = 0; #endif @@ -82,6 +119,9 @@ bool ColorGradingPass::Init() #if COMPILE_WITH_DEV_ENV _shader.Get()->OnReloading.Bind(this); #endif + PostProcessSettings defaultSettings; + GPUTexture* defaultLut; + DefaultData.Init(defaultSettings, defaultLut); return false; } @@ -125,6 +165,18 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) { PROFILE_CPU(); + // Prepare the parameters + Data data; + GPUTexture* lutTexture; + auto& toneMapping = renderContext.List->Settings.ToneMapping; + data.Init(renderContext.List->Settings, lutTexture); + + // Skip if color grading is unsued + if (Platform::MemoryCompare(&DefaultData, &data, sizeof(Data)) == 0 && + lutTexture == nullptr && + toneMapping.Mode == ToneMappingMode::None) + return nullptr; + // Check if can use volume texture (3D) for a LUT (faster on modern platforms, requires geometry shader) const auto device = GPUDevice::Instance; bool use3D = GPU_ALLOW_GEOMETRY_SHADERS && Graphics::PostProcessing::ColorGradingVolumeLUT; @@ -172,41 +224,8 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) RENDER_TARGET_POOL_SET_NAME(colorGradingBuffer.LUT, "ColorGrading.LUT"); } - // Prepare the parameters - Data data; - data.Dummy = Float2::Zero; - auto& toneMapping = renderContext.List->Settings.ToneMapping; - auto& colorGrading = renderContext.List->Settings.ColorGrading; - // White Balance - data.WhiteTemp = toneMapping.WhiteTemperature; - data.WhiteTint = toneMapping.WhiteTint; - // Shadows - data.ColorSaturationShadows = colorGrading.ColorSaturationShadows * colorGrading.ColorSaturation; - data.ColorContrastShadows = colorGrading.ColorContrastShadows * colorGrading.ColorContrast; - data.ColorGammaShadows = colorGrading.ColorGammaShadows * colorGrading.ColorGamma; - data.ColorGainShadows = colorGrading.ColorGainShadows * colorGrading.ColorGain; - data.ColorOffsetShadows = colorGrading.ColorOffsetShadows + colorGrading.ColorOffset; - data.ColorCorrectionShadowsMax = colorGrading.ShadowsMax; - // Midtones - data.ColorSaturationMidtones = colorGrading.ColorSaturationMidtones * colorGrading.ColorSaturation; - data.ColorContrastMidtones = colorGrading.ColorContrastMidtones * colorGrading.ColorContrast; - data.ColorGammaMidtones = colorGrading.ColorGammaMidtones * colorGrading.ColorGamma; - data.ColorGainMidtones = colorGrading.ColorGainMidtones * colorGrading.ColorGain; - data.ColorOffsetMidtones = colorGrading.ColorOffsetMidtones + colorGrading.ColorOffset; - // Highlights - data.ColorSaturationHighlights = colorGrading.ColorSaturationHighlights * colorGrading.ColorSaturation; - data.ColorContrastHighlights = colorGrading.ColorContrastHighlights * colorGrading.ColorContrast; - data.ColorGammaHighlights = colorGrading.ColorGammaHighlights * colorGrading.ColorGamma; - data.ColorGainHighlights = colorGrading.ColorGainHighlights * colorGrading.ColorGain; - data.ColorOffsetHighlights = colorGrading.ColorOffsetHighlights + colorGrading.ColorOffset; - data.ColorCorrectionHighlightsMin = colorGrading.HighlightsMin; - // - Texture* lutTexture = colorGrading.LutTexture.Get(); - const bool useLut = lutTexture && lutTexture->IsLoaded() && lutTexture->GetResidentMipLevels() > 0 && colorGrading.LutWeight > ZeroTolerance; - data.LutWeight = useLut ? colorGrading.LutWeight : 0.0f; - // Check if LUT parameter hasn't been changed since the last time - if (Platform::MemoryCompare(&colorGradingBuffer.CachedData , &data, sizeof(Data)) == 0 && + if (Platform::MemoryCompare(&colorGradingBuffer.CachedData, &data, sizeof(Data)) == 0 && colorGradingBuffer.Mode == toneMapping.Mode && #if COMPILE_WITH_DEV_ENV colorGradingBuffer.FrameRendered > _reloadedFrame && @@ -232,7 +251,7 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext) context->BindCB(0, cb); context->SetViewportAndScissors((float)lutDesc.Width, (float)lutDesc.Height); context->SetState(_psLut.Get((int32)toneMapping.Mode)); - context->BindSR(0, useLut ? lutTexture->GetTexture() : nullptr); + context->BindSR(0, lutTexture); #if GPU_ALLOW_GEOMETRY_SHADERS if (use3D) { diff --git a/Source/Engine/Renderer/PostProcessingPass.cpp b/Source/Engine/Renderer/PostProcessingPass.cpp index 030541e4c..5ac204523 100644 --- a/Source/Engine/Renderer/PostProcessingPass.cpp +++ b/Source/Engine/Renderer/PostProcessingPass.cpp @@ -269,7 +269,7 @@ void PostProcessingPass::Render(RenderContext& renderContext, GPUTexture* input, int32 bloomMipCount = CalculateBloomMipCount(w1, h1); // Ensure to have valid data and if at least one effect should be applied - if (!(useBloom || useToneMapping || useCameraArtifacts) || checkIfSkipPass() || w8 <= 1 || h8 <= 1) + if (!(useBloom || useToneMapping || useCameraArtifacts || colorGradingLUT) || checkIfSkipPass() || w8 <= 1 || h8 <= 1) { // Resources are missing. Do not perform rendering. Just copy raw frame context->SetViewportAndScissors((float)output->Width(), (float)output->Height());