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/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/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();
}
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);
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
diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp
index c76bddf3f..fd62ec537 100644
--- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp
+++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp
@@ -246,11 +246,19 @@ 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 && 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 && 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;
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;
}
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/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;
}
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/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/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;
}
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/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;
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
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)
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)