From 748d69a3cb8830c083409ab5ef7651415ec3b413 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 6 Feb 2022 14:11:13 +0100 Subject: [PATCH 01/31] Add `CollisionMeshesPrefix` option to import collision data from model asset --- .../Editor/Content/Import/ModelImportEntry.cs | 11 +++++- .../Content/Proxy/CollisionDataProxy.cs | 20 ++++++++++ .../Editor/Managed/ManagedEditor.Internal.cpp | 4 +- .../ContentImporters/CreateCollisionData.h | 4 +- Source/Engine/Graphics/Models/ModelData.cpp | 11 ++++++ Source/Engine/Graphics/Models/ModelData.h | 11 +----- .../Engine/Tools/ModelTool/ModelTool.Build.cs | 1 + .../Tools/ModelTool/ModelTool.Options.cpp | 2 + Source/Engine/Tools/ModelTool/ModelTool.cpp | 38 ++++++++++++++++++- Source/Engine/Tools/ModelTool/ModelTool.h | 1 + 10 files changed, 88 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Content/Import/ModelImportEntry.cs b/Source/Editor/Content/Import/ModelImportEntry.cs index 58e438c46..23a15157a 100644 --- a/Source/Editor/Content/Import/ModelImportEntry.cs +++ b/Source/Editor/Content/Import/ModelImportEntry.cs @@ -163,6 +163,12 @@ namespace FlaxEditor.Content.Import [EditorOrder(90), DefaultValue(ModelLightmapUVsSource.Disable), EditorDisplay("Geometry", "Lightmap UVs Source"), Tooltip("Model lightmap UVs source")] public ModelLightmapUVsSource LightmapUVsSource { get; set; } = ModelLightmapUVsSource.Disable; + /// + /// If specified, all meshes which name starts with this prefix will be imported as a separate collision data (excluded used for rendering). + /// + [EditorOrder(100), DefaultValue(""), EditorDisplay("Geometry")] + public string CollisionMeshesPrefix { get; set; } + /// /// Custom uniform import scale. /// @@ -284,7 +290,7 @@ namespace FlaxEditor.Content.Import /// [EditorOrder(420), DefaultValue(true), EditorDisplay("Materials", "Restore Materials On Reimport"), Tooltip("If checked, the importer will try to restore the assigned materials to the model slots.")] public bool RestoreMaterialsOnReimport { get; set; } = true; - + /// /// If checked, the imported mesh/animations are splitted into separate assets. Used if ObjectIndex is set to -1. /// @@ -314,6 +320,7 @@ namespace FlaxEditor.Content.Import public byte ImportVertexColors; public byte ImportBlendShapes; public ModelLightmapUVsSource LightmapUVsSource; + public string CollisionMeshesPrefix; // Transform public float Scale; @@ -364,6 +371,7 @@ namespace FlaxEditor.Content.Import ImportVertexColors = (byte)(ImportVertexColors ? 1 : 0), ImportBlendShapes = (byte)(ImportBlendShapes ? 1 : 0), LightmapUVsSource = LightmapUVsSource, + CollisionMeshesPrefix = CollisionMeshesPrefix, Scale = Scale, Rotation = Rotation, Translation = Translation, @@ -403,6 +411,7 @@ namespace FlaxEditor.Content.Import ImportVertexColors = options.ImportVertexColors != 0; ImportBlendShapes = options.ImportBlendShapes != 0; LightmapUVsSource = options.LightmapUVsSource; + CollisionMeshesPrefix = options.CollisionMeshesPrefix; Scale = options.Scale; Rotation = options.Rotation; Translation = options.Translation; diff --git a/Source/Editor/Content/Proxy/CollisionDataProxy.cs b/Source/Editor/Content/Proxy/CollisionDataProxy.cs index da82f73c5..c07873189 100644 --- a/Source/Editor/Content/Proxy/CollisionDataProxy.cs +++ b/Source/Editor/Content/Proxy/CollisionDataProxy.cs @@ -86,6 +86,7 @@ namespace FlaxEditor.Content { foreach (var child in modelItem.ParentFolder.Children) { + // Check if there is collision that was made with this model if (child is BinaryAssetItem b && b.IsOfType()) { var collisionData = FlaxEngine.Content.Load(b.ID); @@ -97,6 +98,25 @@ namespace FlaxEditor.Content return; } } + + // Check if there is a auto-imported collision + if (child is ContentFolder childFolder && childFolder.ShortName == modelItem.ShortName) + { + foreach (var childFolderChild in childFolder.Children) + { + if (childFolderChild is BinaryAssetItem c && c.IsOfType()) + { + var collisionData = FlaxEngine.Content.Load(c.ID); + if (collisionData && collisionData.Options.Model == model.ID || collisionData.Options.Model == Guid.Empty) + { + Editor.Instance.Windows.ContentWin.Select(c); + if (created != null) + FlaxEngine.Scripting.InvokeOnUpdate(() => created(collisionData)); + return; + } + } + } + } } } diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index da04854ec..b02267647 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -166,6 +166,7 @@ struct InternalModelOptions byte ImportVertexColors; byte ImportBlendShapes; ModelLightmapUVsSource LightmapUVsSource; + MonoString* CollisionMeshesPrefix; // Transform float Scale; @@ -212,7 +213,7 @@ struct InternalModelOptions to->ImportLODs = from->ImportLODs; to->ImportVertexColors = from->ImportVertexColors; to->ImportBlendShapes = from->ImportBlendShapes; - to->LightmapUVsSource = from->LightmapUVsSource; + to->CollisionMeshesPrefix = MUtils::ToString(from->CollisionMeshesPrefix); to->Scale = from->Scale; to->Rotation = from->Rotation; to->Translation = from->Translation; @@ -251,6 +252,7 @@ struct InternalModelOptions to->ImportVertexColors = from->ImportVertexColors; to->ImportBlendShapes = from->ImportBlendShapes; to->LightmapUVsSource = from->LightmapUVsSource; + to->CollisionMeshesPrefix = MUtils::ToString(from->CollisionMeshesPrefix); to->Scale = from->Scale; to->Rotation = from->Rotation; to->Translation = from->Translation; diff --git a/Source/Engine/ContentImporters/CreateCollisionData.h b/Source/Engine/ContentImporters/CreateCollisionData.h index 3126cfd56..547a10485 100644 --- a/Source/Engine/ContentImporters/CreateCollisionData.h +++ b/Source/Engine/ContentImporters/CreateCollisionData.h @@ -6,7 +6,9 @@ #if COMPILE_WITH_ASSETS_IMPORTER +#if COMPILE_WITH_PHYSICS_COOKING #include "Engine/Physics/CollisionCooking.h" +#endif /// /// Creating collision data asset utility @@ -23,7 +25,6 @@ public: static CreateAssetResult Create(CreateAssetContext& context); #if COMPILE_WITH_PHYSICS_COOKING - /// /// Cooks the mesh collision data and saves it to the asset using format. /// @@ -31,7 +32,6 @@ public: /// The input argument data. /// True if failed, otherwise false. See log file to track errors better. static bool CookMeshCollision(const String& outputPath, CollisionCooking::Argument& arg); - #endif }; diff --git a/Source/Engine/Graphics/Models/ModelData.cpp b/Source/Engine/Graphics/Models/ModelData.cpp index 4b39cef65..d1e867c6c 100644 --- a/Source/Engine/Graphics/Models/ModelData.cpp +++ b/Source/Engine/Graphics/Models/ModelData.cpp @@ -589,6 +589,17 @@ void MeshData::Merge(MeshData& other) } } +bool MaterialSlotEntry::UsesProperties() const +{ + return Diffuse.Color != Color::White || + Diffuse.TextureIndex != -1 || + Emissive.Color != Color::Transparent || + Emissive.TextureIndex != -1 || + !Math::IsOne(Opacity.Value) || + Opacity.TextureIndex != -1 || + Normals.TextureIndex != -1; +} + void ModelData::CalculateLODsScreenSizes() { const float autoComputeLodPowerBase = 0.5f; diff --git a/Source/Engine/Graphics/Models/ModelData.h b/Source/Engine/Graphics/Models/ModelData.h index 2b99d0c83..4d4733fb7 100644 --- a/Source/Engine/Graphics/Models/ModelData.h +++ b/Source/Engine/Graphics/Models/ModelData.h @@ -373,16 +373,7 @@ struct FLAXENGINE_API MaterialSlotEntry bool TwoSided = false; - bool UsesProperties() const - { - return Diffuse.Color != Color::White || - Diffuse.TextureIndex != -1 || - Emissive.Color != Color::Transparent || - Emissive.TextureIndex != -1 || - !Math::IsOne(Opacity.Value) || - Opacity.TextureIndex != -1 || - Normals.TextureIndex != -1; - } + bool UsesProperties() const; }; /// diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Build.cs b/Source/Engine/Tools/ModelTool/ModelTool.Build.cs index 720527cb0..25f43d98a 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Build.cs +++ b/Source/Engine/Tools/ModelTool/ModelTool.Build.cs @@ -63,6 +63,7 @@ public class ModelTool : EngineModule options.PrivateDependencies.Add("meshoptimizer"); options.PrivateDependencies.Add("MikkTSpace"); + options.PrivateDependencies.Add("Physics"); options.PublicDefinitions.Add("COMPILE_WITH_MODEL_TOOL"); } diff --git a/Source/Engine/Tools/ModelTool/ModelTool.Options.cpp b/Source/Engine/Tools/ModelTool/ModelTool.Options.cpp index d59563160..2b3620616 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.Options.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.Options.cpp @@ -42,6 +42,7 @@ void ModelTool::Options::Serialize(SerializeStream& stream, const void* otherObj SERIALIZE(ImportVertexColors); SERIALIZE(ImportBlendShapes); SERIALIZE(LightmapUVsSource); + SERIALIZE(CollisionMeshesPrefix); SERIALIZE(Scale); SERIALIZE(Rotation); SERIALIZE(Translation); @@ -79,6 +80,7 @@ void ModelTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifi DESERIALIZE(ImportVertexColors); DESERIALIZE(ImportBlendShapes); DESERIALIZE(LightmapUVsSource); + DESERIALIZE(CollisionMeshesPrefix); DESERIALIZE(Scale); DESERIALIZE(Rotation); DESERIALIZE(Translation); diff --git a/Source/Engine/Tools/ModelTool/ModelTool.cpp b/Source/Engine/Tools/ModelTool/ModelTool.cpp index b18392d7d..f014bd571 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.cpp +++ b/Source/Engine/Tools/ModelTool/ModelTool.cpp @@ -16,6 +16,7 @@ #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/ContentImporters/AssetsImportingManager.h" #include "Engine/ContentImporters/CreateMaterial.h" +#include "Engine/ContentImporters/CreateCollisionData.h" #include "Editor/Utilities/EditorUtilities.h" #include @@ -562,7 +563,7 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op materialOptions.Opacity.Texture = data.Textures[material.Opacity.TextureIndex].AssetID; if (material.Normals.TextureIndex != -1) materialOptions.Normals.Texture = data.Textures[material.Normals.TextureIndex].AssetID; - if (material.TwoSided | material.Diffuse.HasAlphaMask) + if (material.TwoSided || material.Diffuse.HasAlphaMask) materialOptions.Info.CullMode = CullMode::TwoSided; if (!Math::IsOne(material.Opacity.Value) || material.Opacity.TextureIndex != -1) materialOptions.Info.BlendMode = MaterialBlendMode::Transparent; @@ -624,6 +625,41 @@ bool ModelTool::ImportModel(const String& path, ModelData& meshData, Options& op } } + // Collision mesh output + if (options.CollisionMeshesPrefix.HasChars()) + { + // Extract collision meshes + ModelData collisionModel; + for (auto& lod : data.LODs) + { + for (int32 i = lod.Meshes.Count() - 1; i >= 0; i--) + { + auto mesh = lod.Meshes[i]; + if (mesh->Name.StartsWith(options.CollisionMeshesPrefix, StringSearchCase::IgnoreCase)) + { + if (collisionModel.LODs.Count() == 0) + collisionModel.LODs.AddOne(); + collisionModel.LODs[0].Meshes.Add(mesh); + lod.Meshes.RemoveAtKeepOrder(i); + if (lod.Meshes.IsEmpty()) + break; + } + } + } + if (collisionModel.LODs.HasItems()) + { + // Create collision + CollisionCooking::Argument arg; + arg.Type = CollisionDataType::TriangleMesh; + arg.OverrideModelData = &collisionModel; + auto assetPath = autoImportOutput / StringUtils::GetFileNameWithoutExtension(path) + TEXT("Collision") ASSET_FILES_EXTENSION_WITH_DOT; + if (CreateCollisionData::CookMeshCollision(assetPath, arg)) + { + LOG(Error, "Failed to create collision mesh."); + } + } + } + // For generated lightmap UVs coordinates needs to be moved so all meshes are in unique locations in [0-1]x[0-1] coordinates space if (options.LightmapUVsSource == ModelLightmapUVsSource::Generate && data.LODs.HasItems() && data.LODs[0].Meshes.Count() > 1) { diff --git a/Source/Engine/Tools/ModelTool/ModelTool.h b/Source/Engine/Tools/ModelTool/ModelTool.h index fc38b8192..6e22b4cd6 100644 --- a/Source/Engine/Tools/ModelTool/ModelTool.h +++ b/Source/Engine/Tools/ModelTool/ModelTool.h @@ -177,6 +177,7 @@ public: bool ImportVertexColors = true; bool ImportBlendShapes = false; ModelLightmapUVsSource LightmapUVsSource = ModelLightmapUVsSource::Disable; + String CollisionMeshesPrefix; // Transform float Scale = 1.0f; From 9cfc638ce000395f262fc6bb06b19df9b6fff386 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 6 Feb 2022 14:11:31 +0100 Subject: [PATCH 02/31] Fix for editor --- Source/Editor/Content/Proxy/CollisionDataProxy.cs | 2 +- Source/Editor/CustomEditors/Values/ValueContainer.cs | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Content/Proxy/CollisionDataProxy.cs b/Source/Editor/Content/Proxy/CollisionDataProxy.cs index c07873189..fbcfaad40 100644 --- a/Source/Editor/Content/Proxy/CollisionDataProxy.cs +++ b/Source/Editor/Content/Proxy/CollisionDataProxy.cs @@ -107,7 +107,7 @@ namespace FlaxEditor.Content if (childFolderChild is BinaryAssetItem c && c.IsOfType()) { var collisionData = FlaxEngine.Content.Load(c.ID); - if (collisionData && collisionData.Options.Model == model.ID || collisionData.Options.Model == Guid.Empty) + if (collisionData && (collisionData.Options.Model == model.ID || collisionData.Options.Model == Guid.Empty)) { Editor.Instance.Windows.ContentWin.Select(c); if (created != null) diff --git a/Source/Editor/CustomEditors/Values/ValueContainer.cs b/Source/Editor/CustomEditors/Values/ValueContainer.cs index 89387fe5f..00303d310 100644 --- a/Source/Editor/CustomEditors/Values/ValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/ValueContainer.cs @@ -229,7 +229,13 @@ namespace FlaxEditor.CustomEditors for (int i = 0; i < Count; i++) { if (!Equals(this[i], _defaultValue)) + { + // Special case for String (null string is kind of equal to empty string from the user perspective) + if (this[i] == null && _defaultValue is string defaultValueStr && defaultValueStr.Length == 0) + continue; + return true; + } } } return false; From 73976149da080965d24d9644d98156d0741e73fc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 6 Feb 2022 21:25:52 +0100 Subject: [PATCH 03/31] Fix blend shape dirty vertices range --- Source/Engine/Graphics/Models/BlendShape.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Graphics/Models/BlendShape.cpp b/Source/Engine/Graphics/Models/BlendShape.cpp index fb1c27b48..5f1f75c1a 100644 --- a/Source/Engine/Graphics/Models/BlendShape.cpp +++ b/Source/Engine/Graphics/Models/BlendShape.cpp @@ -9,7 +9,7 @@ BlendShapesInstance::MeshInstance::MeshInstance() : IsUsed(false) , IsDirty(false) , DirtyMinVertexIndex(0) - , DirtyMaxVertexIndex(MAX_uint32) + , DirtyMaxVertexIndex(MAX_uint32 - 1) , VertexBuffer(0, sizeof(VB0SkinnedElementType), TEXT("Skinned Mesh Blend Shape")) { } @@ -112,7 +112,7 @@ void BlendShapesInstance::Update(SkinnedModel* skinnedModel) // Initialize the dynamic vertex buffer data (use the dirty range from the previous update to be cleared with initial data) instance.VertexBuffer.Data.Resize(vertexBuffer.Length()); const uint32 dirtyVertexDataStart = instance.DirtyMinVertexIndex * sizeof(VB0SkinnedElementType); - const uint32 dirtyVertexDataLength = Math::Min(instance.DirtyMaxVertexIndex - instance.DirtyMinVertexIndex, vertexCount) * sizeof(VB0SkinnedElementType); + const uint32 dirtyVertexDataLength = Math::Min(instance.DirtyMaxVertexIndex - instance.DirtyMinVertexIndex + 1, vertexCount) * sizeof(VB0SkinnedElementType); Platform::MemoryCopy(instance.VertexBuffer.Data.Get() + dirtyVertexDataStart, vertexBuffer.Get() + dirtyVertexDataStart, dirtyVertexDataLength); // Blend all blend shapes From 3feeec1649929726d37aad54aa5a35eb2b5adbec Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 6 Feb 2022 22:44:40 +0100 Subject: [PATCH 04/31] Fix structure initialization with default field value if attribute has different value HasInvalidPathChar #688 --- Source/Editor/Utilities/Utils.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index d85a06296..9ec08aacc 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -829,7 +829,13 @@ namespace FlaxEditor.Utilities var attr = field.GetAttribute(); if (attr != null) { - field.SetValue(obj, attr.Value); + // Prevent value type conflicts + var value = attr.Value; + var fieldType = field.ValueType.Type; + if (value != null && value.GetType() != fieldType) + value = Convert.ChangeType(value, fieldType); + + field.SetValue(obj, value); } else if (isStructure) { @@ -845,7 +851,13 @@ namespace FlaxEditor.Utilities var attr = property.GetAttribute(); if (attr != null) { - property.SetValue(obj, attr.Value); + // Prevent value type conflicts + var value = attr.Value; + var propertyType = property.ValueType.Type; + if (value != null && value.GetType() != propertyType) + value = Convert.ChangeType(value, propertyType); + + property.SetValue(obj, value); } } } From 3c9a5bcf1ac381056c846f4cfd4204470aa8a9ec Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Tue, 8 Feb 2022 17:56:45 +0100 Subject: [PATCH 05/31] Fix `CollisionsHelper::ClosestPointPointTriangle` --- Source/Engine/Core/Math/CollisionsHelper.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Source/Engine/Core/Math/CollisionsHelper.cpp b/Source/Engine/Core/Math/CollisionsHelper.cpp index 8ec9d0212..b966523ec 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.cpp +++ b/Source/Engine/Core/Math/CollisionsHelper.cpp @@ -93,14 +93,20 @@ void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vec const float d1 = Vector3::Dot(ab, ap); const float d2 = Vector3::Dot(ac, ap); if (d1 <= 0.0f && d2 <= 0.0f) + { result = vertex1; //Barycentric coordinates (1,0,0) + return; + } // Check if P in vertex region outside B const Vector3 bp = point - vertex2; const float d3 = Vector3::Dot(ab, bp); const float d4 = Vector3::Dot(ac, bp); if (d3 >= 0.0f && d4 <= d3) + { result = vertex2; // Barycentric coordinates (0,1,0) + return; + } // Check if P in edge region of AB, if so return projection of P onto AB const float vc = d1 * d4 - d3 * d2; @@ -108,6 +114,7 @@ void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vec { const float v = d1 / (d1 - d3); result = vertex1 + v * ab; //Barycentric coordinates (1-v,v,0) + return; } //Check if P in vertex region outside C @@ -115,7 +122,10 @@ void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vec const float d5 = Vector3::Dot(ab, cp); const float d6 = Vector3::Dot(ac, cp); if (d6 >= 0.0f && d5 <= d6) + { result = vertex3; //Barycentric coordinates (0,0,1) + return; + } //Check if P in edge region of AC, if so return projection of P onto AC const float vb = d5 * d2 - d1 * d6; @@ -123,6 +133,7 @@ void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vec { const float w = d2 / (d2 - d6); result = vertex1 + w * ac; //Barycentric coordinates (1-w,0,w) + return; } //Check if P in edge region of BC, if so return projection of P onto BC @@ -131,6 +142,7 @@ void CollisionsHelper::ClosestPointPointTriangle(const Vector3& point, const Vec { const float w = (d4 - d3) / (d4 - d3 + (d5 - d6)); result = vertex2 + w * (vertex3 - vertex2); //Barycentric coordinates (0,1-w,w) + return; } //P inside face region. Compute Q through its Barycentric coordinates (u,v,w) From 066a4c65bb97599e2c36135b7a9353b44d5b8db2 Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Tue, 8 Feb 2022 18:04:44 +0100 Subject: [PATCH 06/31] Add additional `UploadMipMapAsync` for `GPUTexture` update with custom row/slice pitch --- .../Async/Tasks/GPUUploadTextureMipTask.h | 20 ++++++++--------- .../Engine/Graphics/Textures/GPUTexture.cpp | 22 +++++++++++-------- Source/Engine/Graphics/Textures/GPUTexture.h | 14 +++++++++++- .../Graphics/Textures/StreamingTexture.cpp | 2 +- Source/Engine/Render2D/FontTextureAtlas.cpp | 4 +++- Source/Engine/Terrain/TerrainPatch.cpp | 8 +++++-- 6 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Source/Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h b/Source/Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h index f96b93d68..2573505ba 100644 --- a/Source/Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h +++ b/Source/Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h @@ -16,7 +16,7 @@ class GPUUploadTextureMipTask : public GPUTask protected: GPUTextureReference _texture; - int32 _mipIndex; + int32 _mipIndex, _rowPitch, _slicePitch; BytesContainer _data; public: @@ -27,11 +27,15 @@ public: /// The target texture. /// The target texture mip data. /// The data to upload. + /// The data row pitch. + /// The data slice pitch. /// True if copy data to the temporary buffer, otherwise the input data will be used directly. Then ensure it is valid during the copy operation period (for the next few frames). - GPUUploadTextureMipTask(GPUTexture* texture, int32 mipIndex, Span data, bool copyData) + GPUUploadTextureMipTask(GPUTexture* texture, int32 mipIndex, Span data, int32 rowPitch, int32 slicePitch, bool copyData) : GPUTask(Type::UploadTexture) , _texture(texture) , _mipIndex(mipIndex) + , _rowPitch(rowPitch) + , _slicePitch(slicePitch) { _texture.OnUnload.Bind(this); @@ -66,18 +70,14 @@ protected: return Result::MissingResources; ASSERT(texture->IsAllocated()); - // Cache data - const int32 arraySize = texture->ArraySize(); - uint32 rowPitch, slicePitch; - texture->ComputePitch(_mipIndex, rowPitch, slicePitch); - ASSERT((uint32)_data.Length() >= slicePitch * arraySize); - // Update all array slices const byte* dataSource = _data.Get(); + const int32 arraySize = texture->ArraySize(); + ASSERT(_data.Length() >= _slicePitch * arraySize); for (int32 arrayIndex = 0; arrayIndex < arraySize; arrayIndex++) { - context->GPU->UpdateTexture(texture, arrayIndex, _mipIndex, dataSource, rowPitch, slicePitch); - dataSource += slicePitch; + context->GPU->UpdateTexture(texture, arrayIndex, _mipIndex, dataSource, _rowPitch, _slicePitch); + dataSource += _slicePitch; } return Result::Ok; diff --git a/Source/Engine/Graphics/Textures/GPUTexture.cpp b/Source/Engine/Graphics/Textures/GPUTexture.cpp index 4c37a7ed4..f9cf13cab 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.cpp +++ b/Source/Engine/Graphics/Textures/GPUTexture.cpp @@ -595,17 +595,21 @@ void GPUTexture::OnReleaseGPU() _residentMipLevels = 0; } -GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipIndex) +GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, bool copyData) { - ASSERT(IsRegularTexture() && IsAllocated()); - //ASSERT(Math::IsInRange(mipIndex, HighestResidentMipIndex() - 1, MipLevels() - 1) && mipIndex < MipLevels() && data.IsValid()); + uint32 rowPitch, slicePitch; + ComputePitch(mipIndex, rowPitch, slicePitch); + return UploadMipMapAsync(data, mipIndex, rowPitch, slicePitch, copyData); +} + +GPUTask* GPUTexture::UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, int32 rowPitch, int32 slicePitch, bool copyData) +{ + ASSERT(IsAllocated()); ASSERT(mipIndex < MipLevels() && data.IsValid()); - ASSERT(data.Length() == SlicePitch(mipIndex) * ArraySize()); - - // Create task - auto task = ::New(this, mipIndex, data, false); - ASSERT(task && task->HasReference(this)); - + ASSERT(data.Length() >= slicePitch); + // TODO: support texture data upload to the GPU on a main thread during rendering without this async task (faster direct upload) + auto task = ::New(this, mipIndex, data, rowPitch, slicePitch, copyData); + ASSERT_LOW_LAYER(task && task->HasReference(this)); return task; } diff --git a/Source/Engine/Graphics/Textures/GPUTexture.h b/Source/Engine/Graphics/Textures/GPUTexture.h index be817a49e..28007196d 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.h +++ b/Source/Engine/Graphics/Textures/GPUTexture.h @@ -524,8 +524,20 @@ public: /// /// Data to upload (it must be valid for the next a few frames due to GPU latency and async works executing) /// Mip level index. + /// If true, the data will be copied to the async execution task instead of using the input pointer provided. /// Created async task or null if cannot. - GPUTask* UploadMipMapAsync(const BytesContainer& data, int32 mipIndex); + GPUTask* UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, bool copyData = false); + + /// + /// Uploads mip map data to the GPU. Creates async GPU task. + /// + /// Data to upload (it must be valid for the next a few frames due to GPU latency and async works executing) + /// Mip level index. + /// The data row pitch. + /// The data slice pitch. + /// If true, the data will be copied to the async execution task instead of using the input pointer provided. + /// Created async task or null if cannot. + GPUTask* UploadMipMapAsync(const BytesContainer& data, int32 mipIndex, int32 rowPitch, int32 slicePitch, bool copyData = false); /// /// Stops current thread execution to gather texture data from the GPU. diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index 0d7062e93..0290048fc 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -316,7 +316,7 @@ private: public: StreamTextureMipTask(StreamingTexture* texture, int32 mipIndex) - : GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span(nullptr, 0), false) + : GPUUploadTextureMipTask(texture->GetTexture(), mipIndex, Span(nullptr, 0), 0, 0, false) , _streamingTexture(texture) , _dataLock(_streamingTexture->GetOwner()->LockData()) { diff --git a/Source/Engine/Render2D/FontTextureAtlas.cpp b/Source/Engine/Render2D/FontTextureAtlas.cpp index 9595d5c79..88bc47e0d 100644 --- a/Source/Engine/Render2D/FontTextureAtlas.cpp +++ b/Source/Engine/Render2D/FontTextureAtlas.cpp @@ -210,7 +210,9 @@ void FontTextureAtlas::Flush() // Upload data to the GPU BytesContainer data; data.Link(_data); - _texture->UploadMipMapAsync(data, 0)->Start(); + auto task = _texture->UploadMipMapAsync(data, 0); + if (task) + task->Start(); // Clear dirty flag _isDirty = false; diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp index 28d103f05..f4320400c 100644 --- a/Source/Engine/Terrain/TerrainPatch.cpp +++ b/Source/Engine/Terrain/TerrainPatch.cpp @@ -1627,7 +1627,9 @@ bool TerrainPatch::ModifySplatMap(int32 index, const Color32* samples, const Int LOG(Warning, "Failed to update splatmap texture. It's not allocated."); continue; } - t->UploadMipMapAsync(dataSplatmap->Mips[mipIndex].Data, mipIndex)->Start(); + auto task = t->UploadMipMapAsync(dataSplatmap->Mips[mipIndex].Data, mipIndex); + if (task) + task->Start(); } } else @@ -1745,7 +1747,9 @@ bool TerrainPatch::UpdateHeightData(const TerrainDataUpdateInfo& info, const Int // Update terrain texture (on a GPU) for (int32 mipIndex = 0; mipIndex < _dataHeightmap->Mips.Count(); mipIndex++) { - texture->UploadMipMapAsync(_dataHeightmap->Mips[mipIndex].Data, mipIndex)->Start(); + auto task = texture->UploadMipMapAsync(_dataHeightmap->Mips[mipIndex].Data, mipIndex); + if (task) + task->Start(); } #if 1 From bac8058aa84d07162b0bc53c20ae5976bea4d4ff Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Tue, 8 Feb 2022 18:05:20 +0100 Subject: [PATCH 07/31] Fix depth pitch in `UpdateTexture` on D3D11 for volume textures --- Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index dd422bf1c..53234330e 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -725,7 +725,8 @@ void GPUContextDX11::UpdateTexture(GPUTexture* texture, int32 arrayIndex, int32 auto textureDX11 = static_cast(texture); const int32 subresourceIndex = RenderToolsDX::CalcSubresourceIndex(mipIndex, arrayIndex, texture->MipLevels()); - _context->UpdateSubresource(textureDX11->GetResource(), subresourceIndex, nullptr, data, static_cast(rowPitch), slicePitch); + const uint32 depthPitch = texture->IsVolume() ? slicePitch / texture->Depth() : slicePitch; + _context->UpdateSubresource(textureDX11->GetResource(), subresourceIndex, nullptr, data, (UINT)rowPitch, (UINT)depthPitch); //D3D11_MAPPED_SUBRESOURCE mapped; //_device->GetIM()->Map(_resource, textureMipIndex, D3D11_MAP_WRITE_DISCARD, 0, &mapped); From afed5a30bcd9eead8cd444d1275a7511d6c0ee7e Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Tue, 8 Feb 2022 18:06:02 +0100 Subject: [PATCH 08/31] Add `ClearUA` to GPUContext to clear texture with float values --- .../DirectX/DX11/GPUContextDX11.cpp | 7 +++++++ .../DirectX/DX11/GPUContextDX11.h | 1 + .../DirectX/DX12/GPUContextDX12.cpp | 14 ++++++++++++++ .../DirectX/DX12/GPUContextDX12.h | 1 + .../Engine/GraphicsDevice/Null/GPUContextNull.h | 4 ++++ .../GraphicsDevice/Vulkan/GPUContextVulkan.cpp | 17 +++++++++++++++++ .../GraphicsDevice/Vulkan/GPUContextVulkan.h | 1 + 7 files changed, 45 insertions(+) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index 53234330e..f558152f1 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -188,6 +188,13 @@ void GPUContextDX11::ClearUA(GPUTexture* texture, const uint32 value[4]) _context->ClearUnorderedAccessViewUint(uav, value); } +void GPUContextDX11::ClearUA(GPUTexture* texture, const Vector4& value) +{ + ASSERT(texture != nullptr && texture->IsUnorderedAccess()); + auto uav = ((GPUTextureViewDX11*)(texture->IsVolume() ? texture->ViewVolume() : texture->View()))->UAV(); + _context->ClearUnorderedAccessViewFloat(uav, value.Raw); +} + void GPUContextDX11::ResetRenderTarget() { if (_rtCount != 0 || _rtDepth) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h index 65aa31a5f..766aefe45 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.h @@ -110,6 +110,7 @@ public: void ClearUA(GPUBuffer* buf, const Vector4& value) override; void ClearUA(GPUBuffer* buf, const uint32 value[4]) override; void ClearUA(GPUTexture* texture, const uint32 value[4]) override; + void ClearUA(GPUTexture* texture, const Vector4& value) override; void ResetRenderTarget() override; void SetRenderTarget(GPUTextureView* rt) override; void SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView* rt) override; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 966ce6dc8..2e2f57b06 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -755,6 +755,20 @@ void GPUContextDX12::ClearUA(GPUTexture* texture, const uint32 value[4]) _commandList->ClearUnorderedAccessViewUint(desc.GPU, uav, texDX12->GetResource(), value, 0, nullptr); } +void GPUContextDX12::ClearUA(GPUTexture* texture, const Vector4& value) +{ + ASSERT(texture != nullptr && texture->IsUnorderedAccess()); + auto texDX12 = reinterpret_cast(texture); + + SetResourceState(texDX12, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); + flushRBs(); + + auto uav = ((GPUTextureViewDX12*)(texDX12->IsVolume() ? texDX12->ViewVolume() : texDX12->View(0)))->UAV(); + Descriptor desc; + GetActiveHeapDescriptor(uav, desc); + _commandList->ClearUnorderedAccessViewFloat(desc.GPU, uav, texDX12->GetResource(), value.Raw, 0, nullptr); +} + void GPUContextDX12::ResetRenderTarget() { if (_rtDepth || _rtCount != 0) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h index 029745f37..b2285bd84 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h @@ -160,6 +160,7 @@ public: void ClearUA(GPUBuffer* buf, const Vector4& value) override; void ClearUA(GPUBuffer* buf, const uint32 value[4]) override; void ClearUA(GPUTexture* texture, const uint32 value[4]) override; + void ClearUA(GPUTexture* texture, const Vector4& value) override; void ResetRenderTarget() override; void SetRenderTarget(GPUTextureView* rt) override; void SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView* rt) override; diff --git a/Source/Engine/GraphicsDevice/Null/GPUContextNull.h b/Source/Engine/GraphicsDevice/Null/GPUContextNull.h index 3731ef204..64c4b1aca 100644 --- a/Source/Engine/GraphicsDevice/Null/GPUContextNull.h +++ b/Source/Engine/GraphicsDevice/Null/GPUContextNull.h @@ -64,6 +64,10 @@ public: { } + void ClearUA(GPUTexture* texture, const Vector4& value) override + { + } + void ResetRenderTarget() override { } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index f4c4f9330..f392a256a 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -874,6 +874,23 @@ void GPUContextVulkan::ClearUA(GPUTexture* texture, const uint32 value[4]) } } +void GPUContextVulkan::ClearUA(GPUTexture* texture, const Vector4& value) +{ + const auto texVulkan = static_cast(texture); + if (texVulkan) + { + auto rtVulkan = ((GPUTextureViewVulkan*)(texVulkan->IsVolume() ? texVulkan->ViewVolume() : texVulkan->View(0))); + const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); + if (cmdBuffer->IsInsideRenderPass()) + EndRenderPass(); + + AddImageBarrier(rtVulkan, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + FlushBarriers(); + + vkCmdClearColorImage(cmdBuffer->GetHandle(), rtVulkan->Image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, (const VkClearColorValue*)value.Raw, 1, &rtVulkan->Info.subresourceRange); + } +} + void GPUContextVulkan::ResetRenderTarget() { if (_rtDepth || _rtCount != 0) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h index 5665ef29b..a559d0e87 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h @@ -176,6 +176,7 @@ public: void ClearUA(GPUBuffer* buf, const Vector4& value) override; void ClearUA(GPUBuffer* buf, const uint32 value[4]) override; void ClearUA(GPUTexture* texture, const uint32 value[4]) override; + void ClearUA(GPUTexture* texture, const Vector4& value) override; void ResetRenderTarget() override; void SetRenderTarget(GPUTextureView* rt) override; void SetRenderTarget(GPUTextureView* depthBuffer, GPUTextureView* rt) override; From 69a1e007a62fcee12d96991c811dc20a94321689 Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Tue, 8 Feb 2022 18:06:56 +0100 Subject: [PATCH 09/31] Add some missing changes --- Source/Engine/Graphics/GPUBuffer.cpp | 1 + Source/Engine/Graphics/GPUContext.h | 7 +++++++ Source/Engine/Graphics/Models/MeshBase.h | 1 - Source/Engine/Graphics/RenderBuffers.cpp | 8 +++++--- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Graphics/GPUBuffer.cpp b/Source/Engine/Graphics/GPUBuffer.cpp index 72a1c07bc..bf52943db 100644 --- a/Source/Engine/Graphics/GPUBuffer.cpp +++ b/Source/Engine/Graphics/GPUBuffer.cpp @@ -244,6 +244,7 @@ bool GPUBuffer::DownloadData(BytesContainer& result) // Ensure not running on main thread if (IsInMainThread()) { + // TODO: support mesh data download from GPU on a main thread during rendering LOG(Warning, "Cannot download GPU buffer data on a main thread. Use staging readback buffer or invoke this function from another thread."); return true; } diff --git a/Source/Engine/Graphics/GPUContext.h b/Source/Engine/Graphics/GPUContext.h index 941dac961..59699417e 100644 --- a/Source/Engine/Graphics/GPUContext.h +++ b/Source/Engine/Graphics/GPUContext.h @@ -218,6 +218,13 @@ public: /// The clear value. virtual void ClearUA(GPUTexture* texture, const uint32 value[4]) = 0; + /// + /// Clears an unordered access texture with a float value. + /// + /// The texture to clear. + /// The clear value. + virtual void ClearUA(GPUTexture* texture, const Vector4& value) = 0; + public: /// diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h index 4bb000917..5a76e4f46 100644 --- a/Source/Engine/Graphics/Models/MeshBase.h +++ b/Source/Engine/Graphics/Models/MeshBase.h @@ -95,7 +95,6 @@ public: /// /// Determines whether this mesh is using 16 bit index buffer, otherwise it's 32 bit. /// - /// True if this mesh is using 16 bit index buffer, otherwise 32 bit index buffer. API_PROPERTY() FORCE_INLINE bool Use16BitIndexBuffer() const { return _use16BitIndexBuffer; diff --git a/Source/Engine/Graphics/RenderBuffers.cpp b/Source/Engine/Graphics/RenderBuffers.cpp index 681910307..df2962e52 100644 --- a/Source/Engine/Graphics/RenderBuffers.cpp +++ b/Source/Engine/Graphics/RenderBuffers.cpp @@ -7,6 +7,9 @@ #include "Engine/Graphics/RenderTargetPool.h" #include "Engine/Engine/Engine.h" +// How many frames keep cached buffers for temporal or optional effects? +#define LAZY_FRAMES_COUNT 4 + RenderBuffers::RenderBuffers(const SpawnParams& params) : ScriptingObject(params) { @@ -35,8 +38,7 @@ void RenderBuffers::Prepare() if (VolumetricFog) { ASSERT(VolumetricFogHistory); - - if (frameIndex - LastFrameVolumetricFog >= 4) + if (frameIndex - LastFrameVolumetricFog >= LAZY_FRAMES_COUNT) { RenderTargetPool::Release(VolumetricFog); VolumetricFog = nullptr; @@ -48,7 +50,7 @@ void RenderBuffers::Prepare() } } #define UPDATE_LAZY_KEEP_RT(name) \ - if (name && frameIndex - LastFrame##name >= 4) \ + if (name && frameIndex - LastFrame##name >= LAZY_FRAMES_COUNT) \ { \ RenderTargetPool::Release(name); \ name = nullptr; \ From 71b9324bcbae572a60e61bc4705b44722cb40a16 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 19 Feb 2022 00:27:53 +0100 Subject: [PATCH 10/31] Fix potential exception in PluginManager dispose in Editor --- Source/Editor/GUI/Tabs/Tabs.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index db7b4f5d5..598aae016 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -227,8 +227,8 @@ namespace FlaxEditor.GUI.Tabs /// public Tab SelectedTab { - get => _selectedIndex == -1 ? null : Children[_selectedIndex + 1] as Tab; - set => SelectedTabIndex = Children.IndexOf(value) - 1; + get => _selectedIndex == -1 && Children.Count > _selectedIndex + 1 ? null : Children[_selectedIndex + 1] as Tab; + set => SelectedTabIndex = value != null ? Children.IndexOf(value) - 1 : -1; } /// From 56491569df56d24442a413889f4f13651c4f0bad Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 19 Feb 2022 00:29:09 +0100 Subject: [PATCH 11/31] Optimize C# bindings in Engine code to static functions that native ABI matches managed signature --- Source/Editor/Scripting/CodeEditor.cpp | 37 +++---- Source/Editor/Scripting/CodeEditor.h | 6 -- Source/Editor/Scripting/ScriptsBuilder.cpp | 4 +- Source/Editor/Scripting/ScriptsBuilder.h | 2 +- .../Engine/Graphics/PixelFormatExtensions.cpp | 1 - .../Bindings/BindingsGenerator.CSharp.cs | 1 + .../Bindings/BindingsGenerator.Cache.cs | 2 +- .../Bindings/BindingsGenerator.Cpp.cs | 102 ++++++++++-------- .../Flax.Build/Bindings/BindingsGenerator.cs | 1 + .../Flax.Build/Bindings/ClassStructInfo.cs | 19 +--- .../Flax.Build/Bindings/StructureInfo.cs | 1 - 11 files changed, 81 insertions(+), 95 deletions(-) diff --git a/Source/Editor/Scripting/CodeEditor.cpp b/Source/Editor/Scripting/CodeEditor.cpp index 198d28396..1cfc4e737 100644 --- a/Source/Editor/Scripting/CodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditor.cpp @@ -180,7 +180,22 @@ void CodeEditingManager::OpenSolution(CodeEditorTypes editorType) const auto editor = GetCodeEditor(editorType); if (editor) { - OpenSolution(editor); + // Ensure that no async task is running + if (IsAsyncOpenRunning()) + { + // TODO: enqueue action and handle many actions in the queue + LOG(Warning, "Cannot use code editor during async open action."); + return; + } + + if (editor->UseAsyncForOpen()) + { + AsyncOpenTask::OpenSolution(editor); + } + else + { + editor->OpenSolution(); + } } else { @@ -201,26 +216,6 @@ void CodeEditingManager::OnFileAdded(CodeEditorTypes editorType, const String& p } } -void CodeEditingManager::OpenSolution(CodeEditor* editor) -{ - // Ensure that no async task is running - if (IsAsyncOpenRunning()) - { - // TODO: enqueue action and handle many actions in the queue - LOG(Warning, "Cannot use code editor during async open action."); - return; - } - - if (editor->UseAsyncForOpen()) - { - AsyncOpenTask::OpenSolution(editor); - } - else - { - editor->OpenSolution(); - } -} - void OnAsyncBegin(Thread* thread) { ASSERT(AsyncOpenThread == nullptr); diff --git a/Source/Editor/Scripting/CodeEditor.h b/Source/Editor/Scripting/CodeEditor.h index 638f1d997..a837f0d6e 100644 --- a/Source/Editor/Scripting/CodeEditor.h +++ b/Source/Editor/Scripting/CodeEditor.h @@ -193,12 +193,6 @@ public: /// The path. API_FUNCTION() static void OnFileAdded(CodeEditorTypes editorType, const String& path); - /// - /// Opens the solution project. Handles async opening. - /// - /// The code editor. - static void OpenSolution(CodeEditor* editor); - /// /// The asynchronous open begins. /// diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index 8005524e7..25e22eaca 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -156,7 +156,7 @@ bool ScriptsBuilder::IsSourceWorkspaceDirty() return _wasProjectStructureChanged; } -bool ScriptsBuilder::IsSourceDirty(const TimeSpan& timeout) +bool ScriptsBuilder::IsSourceDirtyFor(const TimeSpan& timeout) { ScopeLock scopeLock(_locker); return _lastSourceCodeEdited > (_lastCompileAction + timeout); @@ -626,7 +626,7 @@ void ScriptsBuilderService::Update() // Check if compile code (if has been edited) const TimeSpan timeToCallCompileIfDirty = TimeSpan::FromMilliseconds(50); auto mainWindow = Engine::MainWindow; - if (ScriptsBuilder::IsSourceDirty(timeToCallCompileIfDirty) && mainWindow && mainWindow->IsFocused()) + if (ScriptsBuilder::IsSourceDirtyFor(timeToCallCompileIfDirty) && mainWindow && mainWindow->IsFocused()) { // Check if auto reload is enabled if (Editor::Managed->CanAutoReloadScripts()) diff --git a/Source/Editor/Scripting/ScriptsBuilder.h b/Source/Editor/Scripting/ScriptsBuilder.h index df6ff604a..52fdd8457 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.h +++ b/Source/Editor/Scripting/ScriptsBuilder.h @@ -63,7 +63,7 @@ public: /// /// Time to use for checking. /// True if source code is dirty, otherwise false. - static bool IsSourceDirty(const TimeSpan& timeout); + static bool IsSourceDirtyFor(const TimeSpan& timeout); /// /// Returns true if scripts are being now compiled/reloaded. diff --git a/Source/Engine/Graphics/PixelFormatExtensions.cpp b/Source/Engine/Graphics/PixelFormatExtensions.cpp index e79699830..c22c9aba6 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.cpp +++ b/Source/Engine/Graphics/PixelFormatExtensions.cpp @@ -1,7 +1,6 @@ // Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. #include "PixelFormatExtensions.h" -#include "Engine/Core/Collections/Dictionary.h" #include "Engine/Core/Math/Math.h" // ReSharper disable CppClangTidyClangDiagnosticSwitchEnum diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 5c419ccb5..e7e4b5133 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -19,6 +19,7 @@ namespace Flax.Build.Bindings internal static readonly Dictionary CSharpNativeToManagedBasicTypes = new Dictionary() { // Language types + { "bool", "bool" }, { "int8", "sbyte" }, { "int16", "short" }, { "int32", "int" }, diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs index 0072c1240..e9d2076ed 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs @@ -19,7 +19,7 @@ namespace Flax.Build.Bindings partial class BindingsGenerator { private static readonly Dictionary TypeCache = new Dictionary(); - private const int CacheVersion = 11; + private const int CacheVersion = 12; internal static void Write(BinaryWriter writer, string e) { diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 77821e29f..25269ca6a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -16,6 +16,7 @@ namespace Flax.Build.Bindings private static readonly string[] CppParamsThatNeedConversionWrappers = new string[64]; private static readonly string[] CppParamsThatNeedConversionTypes = new string[64]; private static readonly string[] CppParamsWrappersCache = new string[64]; + public static readonly List> CppInternalCalls = new List>(); public static readonly List CppUsedNonPodTypes = new List(); private static readonly List CppUsedNonPodTypesList = new List(); public static readonly HashSet CppReferencesFiles = new HashSet(); @@ -771,8 +772,53 @@ namespace Flax.Build.Bindings return $"MUtils::Box<{nativeType}>({value}, {GenerateCppGetNativeClass(buildData, typeInfo, caller, null)})"; } + private static bool GenerateCppWrapperFunctionImplicitBinding(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller) + { + if (typeInfo.IsVoid) + return true; + if (typeInfo.IsPtr || typeInfo.IsRef || typeInfo.IsArray || typeInfo.IsBitField || (typeInfo.GenericArgs != null && typeInfo.GenericArgs.Count != 0)) + return false; + if (CSharpNativeToManagedBasicTypes.ContainsKey(typeInfo.Type) || CSharpNativeToManagedBasicTypes.ContainsValue(typeInfo.Type)) + return true; + var apiType = FindApiTypeInfo(buildData, typeInfo, caller); + if (apiType != null) + { + if (apiType.IsEnum) + return true; + } + return false; + } + + private static bool GenerateCppWrapperFunctionImplicitBinding(BuildData buildData, FunctionInfo functionInfo, ApiTypeInfo caller) + { + if (!functionInfo.IsStatic || functionInfo.Access != AccessLevel.Public || (functionInfo.Glue.CustomParameters != null && functionInfo.Glue.CustomParameters.Count != 0)) + return false; + if (!GenerateCppWrapperFunctionImplicitBinding(buildData, functionInfo.ReturnType, caller)) + return false; + for (int i = 0; i < functionInfo.Parameters.Count; i++) + { + var parameterInfo = functionInfo.Parameters[i]; + if (parameterInfo.IsOut || parameterInfo.IsRef || !GenerateCppWrapperFunctionImplicitBinding(buildData, parameterInfo.Type, caller)) + return false; + } + return true; + } + private static void GenerateCppWrapperFunction(BuildData buildData, StringBuilder contents, ApiTypeInfo caller, FunctionInfo functionInfo, string callFormat = "{0}({1})") { + // Optimize static function wrappers that match C# internal call ABI exactly + // Use it for Engine-internally only because in games this makes it problematic to use the same function name but with different signature that is not visible to scripting + if (CurrentModule.Module is EngineModule && callFormat == "{0}({1})" && GenerateCppWrapperFunctionImplicitBinding(buildData, functionInfo, caller)) + { + // Ensure the function name is unique within a class/structure + if (caller is ClassStructInfo classStructInfo && classStructInfo.Functions.All(f => f.Name != functionInfo.Name || f == functionInfo)) + { + // Use native method binding directly (no generated wrapper) + CppInternalCalls.Add(new KeyValuePair(functionInfo.UniqueName, classStructInfo.Name + "::" + functionInfo.Name)); + return; + } + } + // Setup function binding glue to ensure that wrapper method signature matches for C++ and C# functionInfo.Glue = new FunctionInfo.GlueInfo { @@ -797,6 +843,7 @@ namespace Flax.Build.Bindings }); } + CppInternalCalls.Add(new KeyValuePair(functionInfo.UniqueName, functionInfo.UniqueName)); contents.AppendFormat(" static {0} {1}(", returnValueType, functionInfo.UniqueName); var separator = false; @@ -1453,6 +1500,7 @@ namespace Flax.Build.Bindings var useScripting = classInfo.IsStatic || classInfo.IsScriptingObject; var useCSharp = EngineConfiguration.WithCSharp(buildData.TargetOptions); var hasInterface = classInfo.Interfaces != null && classInfo.Interfaces.Any(x => x.Access == AccessLevel.Public); + CppInternalCalls.Clear(); if (classInfo.IsAutoSerialization) GenerateCppAutoSerialization(buildData, contents, moduleInfo, classInfo, classTypeNameNative); @@ -1520,7 +1568,7 @@ namespace Flax.Build.Bindings { // Convert value back from managed to native (could be modified there) paramType.IsRef = false; - var managedToNative = GenerateCppWrapperManagedToNative(buildData, paramType, classInfo, out var managedType, null, out var _); + var managedToNative = GenerateCppWrapperManagedToNative(buildData, paramType, classInfo, out var managedType, null, out _); var passAsParamPtr = managedType.EndsWith("*"); var paramValue = $"({managedType}{(passAsParamPtr ? "" : "*")})params[{i}]"; if (!string.IsNullOrEmpty(managedToNative)) @@ -1538,6 +1586,7 @@ namespace Flax.Build.Bindings contents.Append(" }").AppendLine().AppendLine(); // C# event wrapper binding method (binds/unbinds C# wrapper to C++ delegate) + CppInternalCalls.Add(new KeyValuePair(eventInfo.Name + "_Bind", eventInfo.Name + "_ManagedBind")); contents.AppendFormat(" static void {0}_ManagedBind(", eventInfo.Name); if (!eventInfo.IsStatic) contents.AppendFormat("{0}* obj, ", classTypeNameNative); @@ -1685,47 +1734,9 @@ namespace Flax.Build.Bindings } if (useScripting && useCSharp) { - foreach (var eventInfo in classInfo.Events) + foreach (var e in CppInternalCalls) { - if (eventInfo.IsHidden) - continue; - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{eventInfo.Name}_Bind\", &{eventInfo.Name}_ManagedBind);"); - } - foreach (var fieldInfo in classInfo.Fields) - { - if (fieldInfo.IsHidden) - continue; - if (fieldInfo.Getter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});"); - if (fieldInfo.Setter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});"); - } - foreach (var propertyInfo in classInfo.Properties) - { - if (propertyInfo.IsHidden) - continue; - if (propertyInfo.Getter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Getter.UniqueName}\", &{propertyInfo.Getter.UniqueName});"); - if (propertyInfo.Setter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{propertyInfo.Setter.UniqueName}\", &{propertyInfo.Setter.UniqueName});"); - } - foreach (var functionInfo in classInfo.Functions) - { - if (functionInfo.IsHidden) - continue; - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});"); - } - if (hasInterface) - { - foreach (var interfaceInfo in classInfo.Interfaces) - { - if (interfaceInfo.Access != AccessLevel.Public) - continue; - foreach (var functionInfo in interfaceInfo.Functions) - { - contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{functionInfo.UniqueName}\", &{functionInfo.UniqueName});"); - } - } + contents.AppendLine($" ADD_INTERNAL_CALL(\"{classTypeNameManagedInternalCall}::Internal_{e.Key}\", &{e.Value});"); } } GenerateCppClassInitRuntime?.Invoke(buildData, classInfo, contents); @@ -1801,6 +1812,7 @@ namespace Flax.Build.Bindings if (structureInfo.Parent != null && !(structureInfo.Parent is FileInfo)) structureTypeNameInternal = structureInfo.Parent.FullNameNative + '_' + structureTypeNameInternal; var useCSharp = EngineConfiguration.WithCSharp(buildData.TargetOptions); + CppInternalCalls.Clear(); if (structureInfo.IsAutoSerialization) GenerateCppAutoSerialization(buildData, contents, moduleInfo, structureInfo, structureTypeNameNative); @@ -1869,12 +1881,9 @@ namespace Flax.Build.Bindings if (useCSharp) { - foreach (var fieldInfo in structureInfo.Fields) + foreach (var e in CppInternalCalls) { - if (fieldInfo.Getter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{fieldInfo.Getter.UniqueName}\", &{fieldInfo.Getter.UniqueName});"); - if (fieldInfo.Setter != null) - contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{fieldInfo.Setter.UniqueName}\", &{fieldInfo.Setter.UniqueName});"); + contents.AppendLine($" ADD_INTERNAL_CALL(\"{structureTypeNameManagedInternalCall}::Internal_{e.Key}\", &{e.Value});"); } } @@ -2189,6 +2198,7 @@ namespace Flax.Build.Bindings CppIncludeFilesList.Clear(); CppVariantToTypes.Clear(); CppVariantFromTypes.Clear(); + CurrentModule = moduleInfo; // Disable C# scripting based on configuration ScriptingLangInfos[0].Enabled = EngineConfiguration.WithCSharp(buildData.TargetOptions); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index 7a84947b0..ca683066a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -48,6 +48,7 @@ namespace Flax.Build.Bindings public static event GenerateModuleBindingsDelegate GenerateModuleBindings; public static event GenerateBinaryModuleBindingsDelegate GenerateBinaryModuleBindings; + public static ModuleInfo CurrentModule; public static ModuleInfo ParseModule(BuildData buildData, Module module, BuildOptions moduleOptions = null) { diff --git a/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs index 36d97cfde..3f212a3a5 100644 --- a/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs @@ -16,6 +16,7 @@ namespace Flax.Build.Bindings public ClassStructInfo BaseType; public List Interfaces; public List Inheritance; // Data from parsing, used to interfaces and base type construct in Init + public List Functions = new List(); public override void Init(Builder.BuildData buildData) { @@ -52,6 +53,7 @@ namespace Flax.Build.Bindings writer.Write((byte)BaseTypeInheritance); BindingsGenerator.Write(writer, BaseType); BindingsGenerator.Write(writer, Inheritance); + BindingsGenerator.Write(writer, Functions); base.Write(writer); } @@ -62,6 +64,7 @@ namespace Flax.Build.Bindings BaseTypeInheritance = (AccessLevel)reader.ReadByte(); BaseType = BindingsGenerator.Read(reader, BaseType); Inheritance = BindingsGenerator.Read(reader, Inheritance); + Functions = BindingsGenerator.Read(reader, Functions); base.Read(reader); } @@ -72,8 +75,6 @@ namespace Flax.Build.Bindings /// public abstract class VirtualClassInfo : ClassStructInfo { - public List Functions = new List(); - internal HashSet UniqueFunctionNames; public override void Init(Builder.BuildData buildData) @@ -100,20 +101,6 @@ namespace Flax.Build.Bindings public abstract int GetScriptVTableOffset(VirtualClassInfo classInfo); - public override void Write(BinaryWriter writer) - { - BindingsGenerator.Write(writer, Functions); - - base.Write(writer); - } - - public override void Read(BinaryReader reader) - { - Functions = BindingsGenerator.Read(reader, Functions); - - base.Read(reader); - } - public override void AddChild(ApiTypeInfo apiTypeInfo) { apiTypeInfo.Namespace = null; diff --git a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs index adc527c18..24c700c22 100644 --- a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs @@ -12,7 +12,6 @@ namespace Flax.Build.Bindings public class StructureInfo : ClassStructInfo { public List Fields = new List(); - public List Functions = new List(); public bool IsAutoSerialization; public bool ForceNoPod; From 4424d93d56935e8237437939b4ee805341d577ef Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 19 Feb 2022 12:09:29 +0100 Subject: [PATCH 12/31] Use Windows Server 2019 for Github Actions --- .github/workflows/build_windows.yml | 4 ++-- .github/workflows/cd.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index c6a841d06..1d5c1adbc 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -6,7 +6,7 @@ jobs: # Editor editor-windows: name: Editor (Windows, Development x64) - runs-on: "windows-latest" + runs-on: "windows-2019" steps: - name: Checkout repo uses: actions/checkout@v2 @@ -23,7 +23,7 @@ jobs: # Game game-windows: name: Game (Windows, Release x64) - runs-on: "windows-latest" + runs-on: "windows-2019" steps: - name: Checkout repo uses: actions/checkout@v2 diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 4b9530788..f5c8726b6 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -9,7 +9,7 @@ jobs: # Windows package-windows-editor: name: Editor (Windows) - runs-on: "windows-latest" + runs-on: "windows-2019" steps: - name: Checkout repo uses: actions/checkout@v2 @@ -34,7 +34,7 @@ jobs: path: Output/EditorDebugSymbols.zip package-windows-game: name: Game (Windows) - runs-on: "windows-latest" + runs-on: "windows-2019" steps: - name: Checkout repo uses: actions/checkout@v2 From 111a2f3b254553bd3f2fb9d79e2e8296560728c3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 19 Feb 2022 17:59:40 +0100 Subject: [PATCH 13/31] Fix crash with terrain in prefab window #689 --- Source/Engine/Terrain/Terrain.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index c405b390e..4907d0f8a 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -559,6 +559,12 @@ void Terrain::Draw(RenderContext& renderContext) void Terrain::DrawGeneric(RenderContext& renderContext) { + // Prevent issues if no BeginPlay was called + if (!IsDuringPlay()) + { + CacheNeighbors(); + } + Draw(renderContext); } From 3a07d767fb080f87c86f79e67a0db9624078bbb1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 20 Feb 2022 20:28:19 +0100 Subject: [PATCH 14/31] Fix rare crash on division by 0 --- Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index 5628a2471..169036aa9 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -423,7 +423,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) #endif // Report progress - float hemispheresProgress = static_cast(_workerStagePosition1) / lightmapEntry.Hemispheres.Count(); + float hemispheresProgress = static_cast(_workerStagePosition1) / Math::Max(lightmapEntry.Hemispheres.Count(), 1); float lightmapsProgress = static_cast(_workerStagePosition0 + hemispheresProgress) / scene->Lightmaps.Count(); float bouncesProgress = static_cast(_giBounceRunningIndex) / _bounceCount; reportProgress(BuildProgressStep::RenderHemispheres, lightmapsProgress / _bounceCount + bouncesProgress); From 6b84773190c00e7a7e8ee0f4cea959de09370c10 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 23 Feb 2022 22:41:06 +0100 Subject: [PATCH 15/31] Fix crash on CharacterController in prefab --- Source/Engine/Physics/Colliders/CharacterController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index 84c282fa1..fdc2fc86c 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -242,7 +242,7 @@ void CharacterController::CreateShape() void CharacterController::UpdateBounds() { - void* actor = PhysicsBackend::GetShapeActor(_shape); + void* actor = _shape ? PhysicsBackend::GetShapeActor(_shape) : nullptr; if (actor) PhysicsBackend::GetActorBounds(actor, _box); else From 58de04ccd6567ea7d134a92a280cd4a5a47d84fc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 23 Feb 2022 22:41:26 +0100 Subject: [PATCH 16/31] Fix crash on particles instance invalidated --- Source/Engine/Particles/Particles.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 87faece63..c9b4f8fd6 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -1025,9 +1025,11 @@ void UpdateGPU(RenderTask* task, GPUContext* context) if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled) continue; const uint32 emitterIndex = track.AsEmitter.Index; - auto emitter = particleSystem->Emitters[emitterIndex].Get(); - auto& data = instance.Emitters[emitterIndex]; - if (!emitter || !emitter->IsLoaded() || !data.Buffer || emitter->SimulationMode != ParticlesSimulationMode::GPU) + ParticleEmitter* emitter = particleSystem->Emitters[emitterIndex].Get(); + if (!emitter || !emitter->IsLoaded() || emitter->SimulationMode != ParticlesSimulationMode::GPU || instance.Emitters.Count() <= emitterIndex) + continue; + ParticleEmitterInstance& data = instance.Emitters[emitterIndex]; + if (!data.Buffer) continue; ASSERT(emitter->Capacity != 0 && emitter->Graph.Layout.Size != 0); From d2c252879e56f48a612b0d905a00239515dcce5c Mon Sep 17 00:00:00 2001 From: Diewa Date: Thu, 24 Feb 2022 21:42:00 +0100 Subject: [PATCH 17/31] Fixes for macOS --- .../Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp | 2 +- Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp | 5 +++++ .../Engine/GraphicsDevice/Vulkan/Mac/MacVulkanPlatform.cpp | 1 + Source/Engine/Platform/Mac/MacWindow.cpp | 5 +++-- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp index 8d5a9c4b5..522fc1a89 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.Layers.cpp @@ -366,7 +366,7 @@ void GPUDeviceVulkan::GetInstanceLayersAndExtensions(Array& outInst } #if VK_EXT_debug_utils - if (!vkTrace && outDebugUtils && FindLayerExtension(globalLayerExtensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) + if (!vkTrace && FindLayerExtension(globalLayerExtensions, VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { outInstanceExtensions.Add(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp index 0ee9e0c82..be8f26255 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUTextureVulkan.cpp @@ -270,6 +270,11 @@ bool GPUTextureVulkan::OnInit() imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; if (useUAV) imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT; +#if PLATFORM_MAC + // MoltenVK: VK_ERROR_FEATURE_NOT_PRESENT: vkCreateImageView(): 2D views on 3D images can only be used as color attachments. + if (IsVolume() && _desc.HasPerSliceViews()) + imageInfo.usage &= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +#endif imageInfo.tiling = optimalTiling ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR; imageInfo.samples = (VkSampleCountFlagBits)MultiSampleLevel(); // TODO: set initialLayout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL for IsRegularTexture() ??? diff --git a/Source/Engine/GraphicsDevice/Vulkan/Mac/MacVulkanPlatform.cpp b/Source/Engine/GraphicsDevice/Vulkan/Mac/MacVulkanPlatform.cpp index 38cc28d22..9b2998296 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/Mac/MacVulkanPlatform.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/Mac/MacVulkanPlatform.cpp @@ -8,6 +8,7 @@ void MacVulkanPlatform::GetInstanceExtensions(Array& extensions, Array& layers) { + extensions.Add(VK_KHR_SURFACE_EXTENSION_NAME); extensions.Add(VK_MVK_MACOS_SURFACE_EXTENSION_NAME); } diff --git a/Source/Engine/Platform/Mac/MacWindow.cpp b/Source/Engine/Platform/Mac/MacWindow.cpp index 123d390ae..e76304163 100644 --- a/Source/Engine/Platform/Mac/MacWindow.cpp +++ b/Source/Engine/Platform/Mac/MacWindow.cpp @@ -837,8 +837,8 @@ DragDropEffect MacWindow::DoDragDrop(const StringView& data) void MacWindow::SetCursor(CursorType type) { WindowBase::SetCursor(type); - if (!_isMouseOver) - return; + //if (!_isMouseOver) + // return; NSCursor* cursor = nullptr; switch (type) { @@ -875,6 +875,7 @@ void MacWindow::SetCursor(CursorType type) if (cursor) { [cursor set]; + [NSCursor unhide]; } } From 5c443f305d6a57397b7976c93db155d66d72d369 Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Tue, 15 Feb 2022 12:14:02 +0100 Subject: [PATCH 18/31] Fix memory leak in RenderTargetPool on texture init fail --- Source/Engine/Graphics/RenderTargetPool.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Graphics/RenderTargetPool.cpp b/Source/Engine/Graphics/RenderTargetPool.cpp index 102d22bc3..26fa3f6ac 100644 --- a/Source/Engine/Graphics/RenderTargetPool.cpp +++ b/Source/Engine/Graphics/RenderTargetPool.cpp @@ -73,6 +73,7 @@ GPUTexture* RenderTargetPool::Get(const GPUTextureDescription& desc) auto newRenderTarget = GPUDevice::Instance->CreateTexture(name); if (newRenderTarget->Init(desc)) { + Delete(newRenderTarget); LOG(Error, "Cannot create temporary render target. Description: {0}", desc.ToString()); return nullptr; } From 87cb553c6159995c3306610e2f2e739ef1c7f7ee Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 28 Feb 2022 19:14:14 +0100 Subject: [PATCH 19/31] Optimize `Vector3` method to be inlined more often --- Source/Engine/Core/Math/Vector3.cpp | 77 ----------------------------- Source/Engine/Core/Math/Vector3.h | 60 +++++++++++++++------- 2 files changed, 41 insertions(+), 96 deletions(-) diff --git a/Source/Engine/Core/Math/Vector3.cpp b/Source/Engine/Core/Math/Vector3.cpp index 79ef60eaa..83faa28a3 100644 --- a/Source/Engine/Core/Math/Vector3.cpp +++ b/Source/Engine/Core/Math/Vector3.cpp @@ -131,83 +131,6 @@ Vector3 Vector3::Frac(const Vector3& v) ); } -Vector3 Vector3::Clamp(const Vector3& value, const Vector3& min, const Vector3& max) -{ - float x = value.X; - x = x > max.X ? max.X : x; - x = x < min.X ? min.X : x; - - float y = value.Y; - y = y > max.Y ? max.Y : y; - y = y < min.Y ? min.Y : y; - - float z = value.Z; - z = z > max.Z ? max.Z : z; - z = z < min.Z ? min.Z : z; - - return Vector3(x, y, z); -} - -void Vector3::Clamp(const Vector3& value, const Vector3& min, const Vector3& max, Vector3& result) -{ - float x = value.X; - x = x > max.X ? max.X : x; - x = x < min.X ? min.X : x; - - float y = value.Y; - y = y > max.Y ? max.Y : y; - y = y < min.Y ? min.Y : y; - - float z = value.Z; - z = z > max.Z ? max.Z : z; - z = z < min.Z ? min.Z : z; - - result = Vector3(x, y, z); -} - -float Vector3::Distance(const Vector3& value1, const Vector3& value2) -{ - const float x = value1.X - value2.X; - const float y = value1.Y - value2.Y; - const float z = value1.Z - value2.Z; - return Math::Sqrt(x * x + y * y + z * z); -} - -float Vector3::DistanceSquared(const Vector3& value1, const Vector3& value2) -{ - const float x = value1.X - value2.X; - const float y = value1.Y - value2.Y; - const float z = value1.Z - value2.Z; - return x * x + y * y + z * z; -} - -Vector3 Vector3::Normalize(const Vector3& input) -{ - Vector3 output = input; - const float length = input.Length(); - if (!Math::IsZero(length)) - { - const float inv = 1.0f / length; - output.X *= inv; - output.Y *= inv; - output.Z *= inv; - } - return output; -} - -void Vector3::Normalize(const Vector3& input, Vector3& result) -{ - result = input; - const float length = input.Length(); - if (!Math::IsZero(length)) - { - const float inv = 1.0f / length; - result.X *= inv; - result.Y *= inv; - result.Z *= inv; - } -} - void Vector3::Hermite(const Vector3& value1, const Vector3& tangent1, const Vector3& value2, const Vector3& tangent2, float amount, Vector3& result) { const float squared = amount * amount; diff --git a/Source/Engine/Core/Math/Vector3.h b/Source/Engine/Core/Math/Vector3.h index aedbc91bb..b6c1ce7fb 100644 --- a/Source/Engine/Core/Math/Vector3.h +++ b/Source/Engine/Core/Math/Vector3.h @@ -586,11 +586,6 @@ public: static Vector3 Floor(const Vector3& v); static Vector3 Frac(const Vector3& v); - static float ScalarProduct(const Vector3& a, const Vector3& b) - { - return a.X * b.X + a.Y * b.Y + a.Z * b.Z; - } - public: // Restricts a value to be within a specified range @@ -598,31 +593,61 @@ public: // @param min The minimum value, // @param max The maximum value // @returns Clamped value - static Vector3 Clamp(const Vector3& value, const Vector3& min, const Vector3& max); + static Vector3 Clamp(const Vector3& value, const Vector3& min, const Vector3& max) + { + return Vector3(Math::Clamp(value.X, min.X, max.X), Math::Clamp(value.Y, min.Y, max.Y), Math::Clamp(value.Z, min.Z, max.Z)); + } // Restricts a value to be within a specified range // @param value The value to clamp // @param min The minimum value, // @param max The maximum value // @param result When the method completes, contains the clamped value - static void Clamp(const Vector3& value, const Vector3& min, const Vector3& max, Vector3& result); + static void Clamp(const Vector3& value, const Vector3& min, const Vector3& max, Vector3& result) + { + result = Vector3(Math::Clamp(value.X, min.X, max.X), Math::Clamp(value.Y, min.Y, max.Y), Math::Clamp(value.Z, min.Z, max.Z)); + } // Calculates the distance between two vectors // @param value1 The first vector // @param value2 The second vector // @returns The distance between the two vectors - static float Distance(const Vector3& value1, const Vector3& value2); + static float Distance(const Vector3& value1, const Vector3& value2) + { + const float x = value1.X - value2.X; + const float y = value1.Y - value2.Y; + const float z = value1.Z - value2.Z; + return Math::Sqrt(x * x + y * y + z * z); + } // Calculates the squared distance between two vectors // @param value1 The first vector // @param value2 The second vector // @returns The squared distance between the two vectors - static float DistanceSquared(const Vector3& value1, const Vector3& value2); + static float DistanceSquared(const Vector3& value1, const Vector3& value2) + { + const float x = value1.X - value2.X; + const float y = value1.Y - value2.Y; + const float z = value1.Z - value2.Z; + return x * x + y * y + z * z; + } // Performs vector normalization (scales vector up to unit length) // @param inout Input vector to normalize // @returns Output vector that is normalized (has unit length) - static Vector3 Normalize(const Vector3& input); + static Vector3 Normalize(const Vector3& input) + { + Vector3 output = input; + const float length = input.Length(); + if (Math::Abs(length) >= ZeroTolerance) + { + const float inv = 1.0f / length; + output.X *= inv; + output.Y *= inv; + output.Z *= inv; + } + return output; + } // Performs vector normalization (scales vector up to unit length). This is a faster version that does not performs check for length equal 0 (it assumes that input vector is not empty). // @param inout Input vector to normalize (cannot be zero). @@ -636,7 +661,10 @@ public: // Performs vector normalization (scales vector up to unit length) // @param inout Input vector to normalize // @param output Output vector that is normalized (has unit length) - static void Normalize(const Vector3& input, Vector3& result); + static FORCE_INLINE void Normalize(const Vector3& input, Vector3& result) + { + result = Normalize(input); + } // dot product with another vector static float Dot(const Vector3& a, const Vector3& b) @@ -650,10 +678,7 @@ public: // @param result When the method completes, contains the cross product of the two vectors static void Cross(const Vector3& a, const Vector3& b, Vector3& result) { - result = Vector3( - a.Y * b.Z - a.Z * b.Y, - a.Z * b.X - a.X * b.Z, - a.X * b.Y - a.Y * b.X); + result = Vector3(a.Y * b.Z - a.Z * b.Y,a.Z * b.X - a.X * b.Z,a.X * b.Y - a.Y * b.X); } // Calculates the cross product of two vectors @@ -662,10 +687,7 @@ public: // @returns Cross product of the two vectors static Vector3 Cross(const Vector3& a, const Vector3& b) { - return Vector3( - a.Y * b.Z - a.Z * b.Y, - a.Z * b.X - a.X * b.Z, - a.X * b.Y - a.Y * b.X); + return Vector3(a.Y * b.Z - a.Z * b.Y,a.Z * b.X - a.X * b.Z,a.X * b.Y - a.Y * b.X); } // Performs a linear interpolation between two vectors From 57e408761d0edb39f6cc20f0ad8cd38ac0ec8577 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 28 Feb 2022 19:14:52 +0100 Subject: [PATCH 20/31] Bump up the build number --- Flax.flaxproj | 2 +- Source/FlaxEngine.Gen.cs | 4 ++-- Source/FlaxEngine.Gen.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 802aee16b..58ccc3787 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -3,7 +3,7 @@ "Version": { "Major": 1, "Minor": 3, - "Build": 6227 + "Build": 6228 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.", diff --git a/Source/FlaxEngine.Gen.cs b/Source/FlaxEngine.Gen.cs index 3d471d37d..55b6cfa67 100644 --- a/Source/FlaxEngine.Gen.cs +++ b/Source/FlaxEngine.Gen.cs @@ -13,5 +13,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("b8442186-4a70-7c85-704a-857cc7990797")] -[assembly: AssemblyVersion("1.3.6227")] -[assembly: AssemblyFileVersion("1.3.6227")] +[assembly: AssemblyVersion("1.3.6228")] +[assembly: AssemblyFileVersion("1.3.6228")] diff --git a/Source/FlaxEngine.Gen.h b/Source/FlaxEngine.Gen.h index 0584eaee0..8bfb490c9 100644 --- a/Source/FlaxEngine.Gen.h +++ b/Source/FlaxEngine.Gen.h @@ -3,11 +3,11 @@ #pragma once #define FLAXENGINE_NAME "FlaxEngine" -#define FLAXENGINE_VERSION Version(1, 3, 6227) -#define FLAXENGINE_VERSION_TEXT "1.3.6227" +#define FLAXENGINE_VERSION Version(1, 3, 6228) +#define FLAXENGINE_VERSION_TEXT "1.3.6228" #define FLAXENGINE_VERSION_MAJOR 1 #define FLAXENGINE_VERSION_MINOR 3 -#define FLAXENGINE_VERSION_BUILD 6227 +#define FLAXENGINE_VERSION_BUILD 6228 #define FLAXENGINE_COMPANY "Flax" #define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2022 Wojciech Figat. All rights reserved." From 6738bc8dea3375d2a5e4cff91f7a9f7720e4c372 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 28 Feb 2022 19:15:02 +0100 Subject: [PATCH 21/31] Fix minor things --- Source/Engine/Particles/Particles.cpp | 2 +- Source/Engine/Scripting/Events.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index c9b4f8fd6..672644fad 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -1024,7 +1024,7 @@ void UpdateGPU(RenderTask* task, GPUContext* context) const auto& track = particleSystem->Tracks[j]; if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled) continue; - const uint32 emitterIndex = track.AsEmitter.Index; + const int32 emitterIndex = track.AsEmitter.Index; ParticleEmitter* emitter = particleSystem->Emitters[emitterIndex].Get(); if (!emitter || !emitter->IsLoaded() || emitter->SimulationMode != ParticlesSimulationMode::GPU || instance.Emitters.Count() <= emitterIndex) continue; diff --git a/Source/Engine/Scripting/Events.h b/Source/Engine/Scripting/Events.h index 6599f2cda..c5b798e12 100644 --- a/Source/Engine/Scripting/Events.h +++ b/Source/Engine/Scripting/Events.h @@ -18,7 +18,7 @@ class FLAXENGINE_API ScriptingEvents public: /// - /// Global table for registered even binder methods (key is pair of type and event name, value is method that takes instance with event, object to bind and flag to bind or unbind). + /// Global table for registered event binder methods (key is pair of type and event name, value is method that takes instance with event, object to bind and flag to bind or unbind). /// /// /// Key: pair of event type, event name. From 4a9c746bedcc637fabad88405124b75a17f951ad Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 28 Feb 2022 23:15:35 +0100 Subject: [PATCH 22/31] Add `SoftAssetReference` type --- Source/Engine/Content/Asset.cpp | 60 +++++ Source/Engine/Content/SoftAssetReference.h | 234 ++++++++++++++++++ Source/Engine/Serialization/Serialization.h | 22 ++ .../Bindings/BindingsGenerator.CSharp.cs | 24 +- .../Bindings/BindingsGenerator.Cpp.cs | 24 +- 5 files changed, 352 insertions(+), 12 deletions(-) create mode 100644 Source/Engine/Content/SoftAssetReference.h diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index 407a7d016..8d520a62d 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -2,6 +2,7 @@ #include "Asset.h" #include "Content.h" +#include "SoftAssetReference.h" #include "Cache/AssetsCache.h" #include "Loading/ContentLoadingManager.h" #include "Loading/Tasks/LoadAssetTask.h" @@ -102,6 +103,65 @@ void WeakAssetReferenceBase::OnUnloaded(Asset* asset) _asset = nullptr; } +String SoftAssetReferenceBase::ToString() const +{ + return _asset ? _asset->ToString() : (_id.IsValid() ? _id.ToString() : TEXT("")); +} + +void SoftAssetReferenceBase::OnSet(Asset* asset) +{ + if (_asset == asset) + return; + if (_asset) + { + _asset->OnUnloaded.Unbind(this); + _asset->RemoveReference(); + } + _asset = asset; + _id = asset ? asset->GetID() : Guid::Empty; + if (asset) + { + asset->AddReference(); + asset->OnUnloaded.Bind(this); + } + Changed(); +} + +void SoftAssetReferenceBase::OnSet(const Guid& id) +{ + if (_id == id) + return; + if (_asset) + { + _asset->OnUnloaded.Unbind(this); + _asset->RemoveReference(); + } + _asset = nullptr; + _id = id; + Changed(); +} + +void SoftAssetReferenceBase::OnResolve(const ScriptingTypeHandle& type) +{ + ASSERT(!_asset); + _asset = ::LoadAsset(_id, type); + if (_asset) + { + _asset->OnUnloaded.Bind(this); + _asset->AddReference(); + } +} + +void SoftAssetReferenceBase::OnUnloaded(Asset* asset) +{ + ASSERT(_asset == asset); + _asset->RemoveReference(); + _asset->OnUnloaded.Unbind(this); + _asset = nullptr; + _id = Guid::Empty; + Changed(); +} + Asset::Asset(const SpawnParams& params, const AssetInfo* info) : ManagedScriptingObject(params) , _refCount(0) diff --git a/Source/Engine/Content/SoftAssetReference.h b/Source/Engine/Content/SoftAssetReference.h new file mode 100644 index 000000000..eb182220d --- /dev/null +++ b/Source/Engine/Content/SoftAssetReference.h @@ -0,0 +1,234 @@ +// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Content/Asset.h" + +/// +/// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it). +/// +class FLAXENGINE_API SoftAssetReferenceBase +{ +protected: + Asset* _asset = nullptr; + Guid _id = Guid::Empty; + +public: + /// + /// Action fired when field gets changed (link a new asset or change to the another value). + /// + Delegate<> Changed; + +public: + NON_COPYABLE(SoftAssetReferenceBase); + + /// + /// Initializes a new instance of the class. + /// + SoftAssetReferenceBase() = default; + + /// + /// Finalizes an instance of the class. + /// + ~SoftAssetReferenceBase() + { + } + +public: + /// + /// Gets the asset ID or Guid::Empty if not set. + /// + FORCE_INLINE Guid GetID() const + { + return _id; + } + + /// + /// Gets the asset property value as string. + /// + String ToString() const; + +protected: + void OnSet(Asset* asset); + void OnSet(const Guid& id); + void OnResolve(const ScriptingTypeHandle& type); + void OnUnloaded(Asset* asset); +}; + +/// +/// The asset soft reference. Asset gets referenced (loaded) on actual use (ID reference is resolving it). +/// +template +API_CLASS(InBuild) class SoftAssetReference : public SoftAssetReferenceBase +{ +public: + typedef T AssetType; + typedef SoftAssetReference Type; + +public: + /// + /// Initializes a new instance of the class. + /// + SoftAssetReference() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The asset to set. + SoftAssetReference(T* asset) + { + OnSet(asset); + } + + /// + /// Initializes a new instance of the class. + /// + /// The other. + SoftAssetReference(const SoftAssetReference& other) + { + OnSet(other.Get()); + } + + SoftAssetReference(SoftAssetReference&& other) + { + OnSet(other.Get()); + other.OnSet(nullptr); + } + + /// + /// Finalizes an instance of the class. + /// + ~SoftAssetReference() + { + } + +public: + FORCE_INLINE bool operator==(T* other) + { + return Get() == other; + } + FORCE_INLINE bool operator==(const SoftAssetReference& other) + { + return GetID() == other.GetID(); + } + FORCE_INLINE bool operator!=(T* other) + { + return Get() != other; + } + FORCE_INLINE bool operator!=(const SoftAssetReference& other) + { + return GetID() != other.GetID(); + } + SoftAssetReference& operator=(const SoftAssetReference& other) + { + if (this != &other) + OnSet(other.GetID()); + return *this; + } + SoftAssetReference& operator=(SoftAssetReference&& other) + { + if (this != &other) + { + OnSet(other.GetID()); + other.OnSet(nullptr); + } + return *this; + } + FORCE_INLINE SoftAssetReference& operator=(const T& other) + { + OnSet(&other); + return *this; + } + FORCE_INLINE SoftAssetReference& operator=(T* other) + { + OnSet(other); + return *this; + } + FORCE_INLINE SoftAssetReference& operator=(const Guid& id) + { + OnSet(id); + return *this; + } + FORCE_INLINE operator T*() const + { + return (T*)Get(); + } + FORCE_INLINE operator bool() const + { + return Get() != nullptr; + } + FORCE_INLINE T* operator->() const + { + return (T*)Get(); + } + template + FORCE_INLINE U* As() const + { + return static_cast(Get()); + } + +public: + + /// + /// Gets the asset (or null if unassigned). + /// + T* Get() const + { + if (!_asset) + const_cast(this)->OnResolve(T::TypeInitializer); + return (T*)_asset; + } + + /// + /// Gets managed instance object (or null if no asset linked). + /// + MObject* GetManagedInstance() const + { + auto asset = Get(); + return asset ? asset->GetOrCreateManagedInstance() : nullptr; + } + + /// + /// Determines whether asset is assigned and managed instance of the asset is alive. + /// + bool HasManagedInstance() const + { + auto asset = Get(); + return asset && asset->HasManagedInstance(); + } + + /// + /// Gets the managed instance object or creates it if missing or null if not assigned. + /// + MObject* GetOrCreateManagedInstance() const + { + auto asset = Get(); + return asset ? asset->GetOrCreateManagedInstance() : nullptr; + } + + /// + /// Sets the asset. + /// + /// The object ID. Uses Scripting to find the registered asset of the given ID. + FORCE_INLINE void Set(const Guid& id) + { + OnSet(id); + } + + /// + /// Sets the asset. + /// + /// The asset. + FORCE_INLINE void Set(T* asset) + { + OnSet(asset); + } +}; + +template +uint32 GetHash(const SoftAssetReference& key) +{ + return GetHash(key.GetID()); +} diff --git a/Source/Engine/Serialization/Serialization.h b/Source/Engine/Serialization/Serialization.h index b6a9a6da3..259e15298 100644 --- a/Source/Engine/Serialization/Serialization.h +++ b/Source/Engine/Serialization/Serialization.h @@ -13,6 +13,8 @@ struct Version; struct VariantType; +template +class SoftAssetReference; // @formatter:off @@ -513,6 +515,26 @@ namespace Serialization v = id; } + // Soft Asset Reference + + template + inline bool ShouldSerialize(const SoftAssetReference& v, const void* otherObj) + { + return !otherObj || v.Get() != ((SoftAssetReference*)otherObj)->Get(); + } + template + inline void Serialize(ISerializable::SerializeStream& stream, const SoftAssetReference& v, const void* otherObj) + { + stream.Guid(v.GetID()); + } + template + inline void Deserialize(ISerializable::DeserializeStream& stream, SoftAssetReference& v, ISerializeModifier* modifier) + { + Guid id; + Deserialize(stream, id, modifier); + v = id; + } + // Array template diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index e7e4b5133..b28744a50 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -234,8 +234,12 @@ namespace Flax.Build.Bindings return result; } - // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) + // Object reference property + if ((typeInfo.Type == "ScriptingObjectReference" || + typeInfo.Type == "AssetReference" || + typeInfo.Type == "WeakAssetReference" || + typeInfo.Type == "SoftAssetReference" || + typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller); // Array or Span @@ -311,8 +315,12 @@ namespace Flax.Build.Bindings return "IntPtr"; } - // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) + // Object reference property + if ((typeInfo.Type == "ScriptingObjectReference" || + typeInfo.Type == "AssetReference" || + typeInfo.Type == "WeakAssetReference" || + typeInfo.Type == "SoftAssetReference" || + typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return "IntPtr"; // Function @@ -365,8 +373,12 @@ namespace Flax.Build.Bindings return string.Format("FlaxEngine.Object.GetUnmanagedInterface({{0}}, typeof({0}))", apiType.FullNameManaged); } - // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) + // Object reference property + if ((typeInfo.Type == "ScriptingObjectReference" || + typeInfo.Type == "AssetReference" || + typeInfo.Type == "WeakAssetReference" || + typeInfo.Type == "SoftAssetReference" || + typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return "FlaxEngine.Object.GetUnmanagedPtr({0})"; // Default diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 25269ca6a..cc101638e 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -131,7 +131,11 @@ namespace Flax.Build.Bindings return value; if (typeInfo.Type == "String") return $"Variant(StringView({value}))"; - if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference") + if (typeInfo.Type == "AssetReference" || + typeInfo.Type == "WeakAssetReference" || + typeInfo.Type == "SoftAssetReference" || + typeInfo.Type == "ScriptingObjectReference" || + typeInfo.Type == "SoftObjectReference") return $"Variant({value}.Get())"; if (typeInfo.IsArray) { @@ -178,7 +182,7 @@ namespace Flax.Build.Bindings return $"(StringView){value}"; if (typeInfo.IsPtr && typeInfo.IsConst && typeInfo.Type == "Char") return $"((StringView){value}).GetNonTerminatedText()"; // (StringView)Variant, if not empty, is guaranteed to point to a null-terminated buffer. - if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference") + if (typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftAssetReference") return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((Asset*){value})"; if (typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "SoftObjectReference") return $"ScriptingObject::Cast<{typeInfo.GenericArgs[0].Type}>((ScriptingObject*){value})"; @@ -401,8 +405,12 @@ namespace Flax.Build.Bindings type = "void*"; return "MUtils::ToManaged({0})"; default: - // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) + // Object reference property + if ((typeInfo.Type == "ScriptingObjectReference" || + typeInfo.Type == "AssetReference" || + typeInfo.Type == "WeakAssetReference" || + typeInfo.Type == "SoftAssetReference" || + typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) { type = "MonoObject*"; return "{0}.GetManagedInstance()"; @@ -569,8 +577,12 @@ namespace Flax.Build.Bindings type = "void*"; return "MUtils::ToNative({0})"; default: - // ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference - if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) + // Object reference property + if ((typeInfo.Type == "ScriptingObjectReference" || + typeInfo.Type == "AssetReference" || + typeInfo.Type == "WeakAssetReference" || + typeInfo.Type == "SoftAssetReference" || + typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) { // For non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code) if (CppNonPodTypesConvertingGeneration) From 3a1475fa9c024cf5b3151803470d3d9701496f91 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 28 Feb 2022 23:16:00 +0100 Subject: [PATCH 23/31] Use SoftObjectReference for deformable asset ref to prevent removing it by Content GC --- Source/Engine/Graphics/GPUDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index 91e6b14f1..51827397a 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -11,6 +11,7 @@ #include "Engine/Content/Assets/Shader.h" #include "Engine/Content/Assets/Material.h" #include "Engine/Content/Content.h" +#include "Engine/Content/SoftAssetReference.h" #include "Engine/Platform/Windows/WindowsWindow.h" #include "Engine/Render2D/Render2D.h" #include "Engine/Engine/CommandLine.h" @@ -19,7 +20,6 @@ #include "Engine/Profiler/Profiler.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Core/Utilities.h" -#include "Engine/Scripting/SoftObjectReference.h" GPUPipelineState* GPUPipelineState::Spawn(const SpawnParams& params) { @@ -253,7 +253,7 @@ struct GPUDevice::PrivateData GPUPipelineState* PS_Clear = nullptr; GPUBuffer* FullscreenTriangleVB = nullptr; AssetReference DefaultMaterial; - SoftObjectReference DefaultDeformableMaterial; + SoftAssetReference DefaultDeformableMaterial; AssetReference DefaultNormalMap; AssetReference DefaultWhiteTexture; AssetReference DefaultBlackTexture; From cdc74a9c49e644bbe39e1c547b7eccb5f6cf7cde Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 28 Feb 2022 23:16:15 +0100 Subject: [PATCH 24/31] Reduce includes in Serialization header --- Source/Engine/Scripting/SoftObjectReference.h | 1 + Source/Engine/Serialization/Serialization.cpp | 2 ++ Source/Engine/Serialization/Serialization.h | 13 +++++++++---- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Scripting/SoftObjectReference.h b/Source/Engine/Scripting/SoftObjectReference.h index 8ee877324..3af64f595 100644 --- a/Source/Engine/Scripting/SoftObjectReference.h +++ b/Source/Engine/Scripting/SoftObjectReference.h @@ -24,6 +24,7 @@ public: Delegate<> Changed; public: + NON_COPYABLE(SoftObjectReferenceBase); /// /// Initializes a new instance of the class. diff --git a/Source/Engine/Serialization/Serialization.cpp b/Source/Engine/Serialization/Serialization.cpp index daee80978..447871f1d 100644 --- a/Source/Engine/Serialization/Serialization.cpp +++ b/Source/Engine/Serialization/Serialization.cpp @@ -23,6 +23,8 @@ #include "Engine/Core/Math/Matrix.h" #include "Engine/Scripting/ManagedSerialization.h" #include "Engine/Scripting/ManagedCLR/MUtils.h" +#include "Engine/Scripting/ScriptingObjectReference.h" +#include "Engine/Content/Asset.h" #include "Engine/Utilities/Encryption.h" #if USE_MONO #include diff --git a/Source/Engine/Serialization/Serialization.h b/Source/Engine/Serialization/Serialization.h index 259e15298..6db037ac8 100644 --- a/Source/Engine/Serialization/Serialization.h +++ b/Source/Engine/Serialization/Serialization.h @@ -5,15 +5,20 @@ #include "SerializationFwd.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Dictionary.h" -#include "Engine/Scripting/ScriptingObjectReference.h" -#include "Engine/Scripting/SoftObjectReference.h" -#include "Engine/Content/AssetReference.h" -#include "Engine/Content/WeakAssetReference.h" +#include "Engine/Scripting/ScriptingObject.h" #include "Engine/Utilities/Encryption.h" struct Version; struct VariantType; template +class ScriptingObjectReference; +template +class SoftObjectReference; +template +class AssetReference; +template +class WeakAssetReference; +template class SoftAssetReference; // @formatter:off From e52cf67447ff649e9736a4e11a02104edcd79b13 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 2 Mar 2022 10:00:06 +0100 Subject: [PATCH 25/31] Fix crash on Vulkan swapchain resize if the previous size was 0 (eg. due to window animation on Windows 11r) --- .../Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index 1e321149a..6fab41edd 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -185,6 +185,8 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) // Flush removed resources _device->DeferredDeletionQueue.ReleaseResources(true); } + ASSERT(_surface == VK_NULL_HANDLE); + ASSERT_LOW_LAYER(_backBuffers.Count() == 0); // Create platform-dependent surface VulkanPlatform::CreateSurface(windowHandle, GPUDeviceVulkan::Instance, &_surface); @@ -205,7 +207,7 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, _surface, &surfaceFormatsCount, nullptr)); ASSERT(surfaceFormatsCount > 0); - Array surfaceFormats; + Array> surfaceFormats; surfaceFormats.AddZeroed(surfaceFormatsCount); VALIDATE_VULKAN_RESULT(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, _surface, &surfaceFormatsCount, surfaceFormats.Get())); @@ -414,8 +416,8 @@ bool GPUSwapChainVulkan::CreateSwapChain(int32 width, int32 height) } } - // Calculate memory usage - _memoryUsage = RenderTools::CalculateTextureMemoryUsage(_format, _width, _height, 1) * _backBuffers.Count(); + // Estimate memory usage + _memoryUsage = 1024 + RenderTools::CalculateTextureMemoryUsage(_format, _width, _height, 1) * _backBuffers.Count(); return false; } From 20075e0fbd473afe98adee0d4bdeb0eddae95086 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 2 Mar 2022 21:06:21 +0100 Subject: [PATCH 26/31] Fixes for compilation on Windows for x86 --- .../Vulkan/GPUContextVulkan.cpp | 2 +- Source/Engine/Platform/Win32/Win32Platform.h | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index f392a256a..1142e3a64 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -1042,7 +1042,7 @@ void GPUContextVulkan::BindIB(GPUBuffer* indexBuffer) void GPUContextVulkan::BindSampler(int32 slot, GPUSampler* sampler) { ASSERT(slot >= GPU_STATIC_SAMPLERS_COUNT && slot < GPU_MAX_SAMPLER_BINDED); - const auto handle = sampler ? ((GPUSamplerVulkan*)sampler)->Sampler : nullptr; + const auto handle = sampler ? ((GPUSamplerVulkan*)sampler)->Sampler : VK_NULL_HANDLE; _samplerHandles[slot] = handle; } diff --git a/Source/Engine/Platform/Win32/Win32Platform.h b/Source/Engine/Platform/Win32/Win32Platform.h index e31e3e985..b2009e803 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.h +++ b/Source/Engine/Platform/Win32/Win32Platform.h @@ -25,7 +25,11 @@ public: static void MemoryBarrier(); static int64 InterlockedExchange(int64 volatile* dst, int64 exchange) { +#if WIN64 return _InterlockedExchange64(dst, exchange); +#else + return _interlockedexchange64(dst, exchange); +#endif } static int32 InterlockedCompareExchange(int32 volatile* dst, int32 exchange, int32 comperand) { @@ -37,15 +41,27 @@ public: } static int64 InterlockedIncrement(int64 volatile* dst) { +#if WIN64 return _InterlockedExchangeAdd64(dst, 1) + 1; +#else + return _interlockedexchange64(dst, 1) + 1; +#endif } static int64 InterlockedDecrement(int64 volatile* dst) { +#if WIN64 return _InterlockedExchangeAdd64(dst, -1) - 1; +#else + return _interlockedexchangeadd64(dst, -1) - 1; +#endif } static int64 InterlockedAdd(int64 volatile* dst, int64 value) { +#if WIN64 return _InterlockedExchangeAdd64(dst, value); +#else + return _interlockedexchangeadd64(dst, value); +#endif } static int32 AtomicRead(int32 volatile* dst) { @@ -61,7 +77,11 @@ public: } static void AtomicStore(int64 volatile* dst, int64 value) { +#if WIN64 _InterlockedExchange64(dst, value); +#else + _interlockedexchange64(dst, value); +#endif } static void Prefetch(void const* ptr); static void* Allocate(uint64 size, uint64 alignment); From 09c995d67b9e30ddd2a9ce80b4edda46ef6d5c61 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 4 Mar 2022 23:14:36 +0100 Subject: [PATCH 27/31] Fix for macOS dylib path #693 --- .../Flax.Build/Platforms/Mac/MacToolchain.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs index 682b655a2..9173fc826 100644 --- a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs @@ -370,6 +370,29 @@ namespace Flax.Build.Platforms task.Cost = task.PrerequisiteFiles.Count; task.ProducedFiles.Add(outputFilePath); + Task lastTask = task; + if (options.LinkEnv.Output == LinkerOutput.Executable) + { + // Fix rpath for dynamic libraries + foreach (var library in options.Libraries) + { + if (!library.EndsWith(".dylib")) + continue; + var rpathTask = graph.Add(); + rpathTask.ProducedFiles.Add(outputFilePath); + rpathTask.WorkingDirectory = options.WorkingDirectory; + rpathTask.CommandPath = "install_name_tool"; + var filename = Path.GetFileName(library); + var outputFolder = Path.GetDirectoryName(outputFilePath); + rpathTask.CommandArguments = string.Format("-change \"{0}/{1}\" \"@loader_path/{1}\"", outputFolder, filename); + rpathTask.InfoMessage = "Fixing rpath " + outputFilePath; + rpathTask.Cost = 1; + rpathTask.DisableCache = true; + rpathTask.DependentTasks = new HashSet(); + rpathTask.DependentTasks.Add(lastTask); + lastTask = rpathTask; + } + } if (!options.LinkEnv.DebugInformation) { // Strip debug symbols @@ -382,7 +405,7 @@ namespace Flax.Build.Platforms stripTask.Cost = 1; stripTask.DisableCache = true; stripTask.DependentTasks = new HashSet(); - stripTask.DependentTasks.Add(task); + stripTask.DependentTasks.Add(lastTask); } } From aed6f0403d5b3a36518e940844811827417217a2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 4 Mar 2022 23:20:45 +0100 Subject: [PATCH 28/31] Fix dylibs list #693 --- Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs index 9173fc826..77f24782c 100644 --- a/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Mac/MacToolchain.cs @@ -271,6 +271,7 @@ namespace Flax.Build.Platforms // Input libraries var libraryPaths = new HashSet(); + var dylibs = new HashSet(); foreach (var library in linkEnvironment.InputLibraries) { var dir = Path.GetDirectoryName(library); @@ -290,6 +291,7 @@ namespace Flax.Build.Platforms else if (ext == ".dylib") { // Link against dynamic library + dylibs.Add(library); task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); args.Add(string.Format("\"{0}\"", library)); @@ -319,6 +321,7 @@ namespace Flax.Build.Platforms else if (ext == ".dylib") { // Link against dynamic library + dylibs.Add(library); task.PrerequisiteFiles.Add(library); libraryPaths.Add(dir); args.Add(string.Format("\"{0}\"", library)); @@ -374,18 +377,16 @@ namespace Flax.Build.Platforms if (options.LinkEnv.Output == LinkerOutput.Executable) { // Fix rpath for dynamic libraries - foreach (var library in options.Libraries) + foreach (var library in dylibs) { - if (!library.EndsWith(".dylib")) - continue; var rpathTask = graph.Add(); rpathTask.ProducedFiles.Add(outputFilePath); rpathTask.WorkingDirectory = options.WorkingDirectory; rpathTask.CommandPath = "install_name_tool"; var filename = Path.GetFileName(library); var outputFolder = Path.GetDirectoryName(outputFilePath); - rpathTask.CommandArguments = string.Format("-change \"{0}/{1}\" \"@loader_path/{1}\"", outputFolder, filename); - rpathTask.InfoMessage = "Fixing rpath " + outputFilePath; + rpathTask.CommandArguments = string.Format("-change \"{0}/{1}\" \"@loader_path/{1}\" {2}", outputFolder, filename, outputFilePath); + rpathTask.InfoMessage = "Fixing rpath to " + filename; rpathTask.Cost = 1; rpathTask.DisableCache = true; rpathTask.DependentTasks = new HashSet(); From 8e28fef91f2ae7e0cd575fe513aaeb1ee08b8319 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 13 Mar 2022 19:31:33 +0100 Subject: [PATCH 29/31] Fix `Lightmap UVs Source` not working in model import options --- Source/Editor/Managed/ManagedEditor.Internal.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index b02267647..248aa757f 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -213,6 +213,7 @@ struct InternalModelOptions to->ImportLODs = from->ImportLODs; to->ImportVertexColors = from->ImportVertexColors; to->ImportBlendShapes = from->ImportBlendShapes; + to->LightmapUVsSource = from->LightmapUVsSource; to->CollisionMeshesPrefix = MUtils::ToString(from->CollisionMeshesPrefix); to->Scale = from->Scale; to->Rotation = from->Rotation; From 6da3dff5de4e75b51c4ce130e4ac8cca039c7541 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Mar 2022 20:32:10 +0100 Subject: [PATCH 30/31] Fix editor options startup to not log error on missing file --- Source/Editor/Options/OptionsModule.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index 0c22759aa..410a369b4 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -93,6 +93,11 @@ namespace FlaxEditor.Options public void Load() { Editor.Log("Loading editor options"); + if (!File.Exists(_optionsFilePath)) + { + Editor.LogWarning("Missing editor settings"); + return; + } try { @@ -100,12 +105,12 @@ namespace FlaxEditor.Options var asset = FlaxEngine.Content.LoadAsync(_optionsFilePath); if (asset == null) { - Editor.LogWarning("Missing or invalid editor settings"); + Editor.LogWarning("Invalid editor settings"); return; } if (asset.WaitForLoaded()) { - Editor.LogWarning("Failed to load editor settings"); + Editor.LogError("Failed to load editor settings"); return; } From b780e33a5b0c430c874af21bf309cc1bce047da7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Mar 2022 20:32:38 +0100 Subject: [PATCH 31/31] Fix for faster models exporting --- Source/Engine/ContentExporters/ExportModel.cpp | 8 ++++---- Source/Engine/Serialization/Stream.cpp | 6 ++---- Source/ThirdParty/fmt/core.h | 1 + 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Source/Engine/ContentExporters/ExportModel.cpp b/Source/Engine/ContentExporters/ExportModel.cpp index dab2862c0..079ce8245 100644 --- a/Source/Engine/ContentExporters/ExportModel.cpp +++ b/Source/Engine/ContentExporters/ExportModel.cpp @@ -75,8 +75,8 @@ ExportAssetResult AssetExporters::ExportModel(ExportAssetContext& context) for (uint32 i = 0; i < vertices; i++) { - auto v = vb1[i].TexCoord; - output->WriteText(StringAnsi::Format("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y))); + auto v = vb1[i].TexCoord.ToVector2(); + output->WriteText(StringAnsi::Format("vt {0} {1}\n", v.X, v.Y)); } output->WriteChar('\n'); @@ -180,8 +180,8 @@ ExportAssetResult AssetExporters::ExportSkinnedModel(ExportAssetContext& context for (uint32 i = 0; i < vertices; i++) { - auto v = vb0[i].TexCoord; - output->WriteText(StringAnsi::Format("vt {0} {1}\n", Float16Compressor::Decompress(v.X), Float16Compressor::Decompress(v.Y))); + auto v = vb0[i].TexCoord.ToVector2(); + output->WriteText(StringAnsi::Format("vt {0} {1}\n", v.X, v.Y)); } output->WriteChar('\n'); diff --git a/Source/Engine/Serialization/Stream.cpp b/Source/Engine/Serialization/Stream.cpp index c7e7f9f91..d86a941bc 100644 --- a/Source/Engine/Serialization/Stream.cpp +++ b/Source/Engine/Serialization/Stream.cpp @@ -519,14 +519,12 @@ void ReadStream::ReadJson(ISerializable* obj) void WriteStream::WriteText(const StringView& text) { - for (int32 i = 0; i < text.Length(); i++) - WriteChar(text[i]); + WriteBytes(text.Get(), sizeof(Char) * text.Length()); } void WriteStream::WriteText(const StringAnsiView& text) { - for (int32 i = 0; i < text.Length(); i++) - WriteChar(text[i]); + WriteBytes(text.Get(), sizeof(char) * text.Length()); } void WriteStream::WriteString(const StringView& data) diff --git a/Source/ThirdParty/fmt/core.h b/Source/ThirdParty/fmt/core.h index 7de04519f..7d2686a50 100644 --- a/Source/ThirdParty/fmt/core.h +++ b/Source/ThirdParty/fmt/core.h @@ -18,6 +18,7 @@ #define FMT_USE_WINDOWS_H 0 //#define FMT_USE_STRING_VIEW 1 #define FMT_USE_STRING 0 +#define FMT_USE_LONG_DOUBLE 0 #define FMT_USE_ITERATOR 0 #define FMT_USE_LOCALE_GROUPING 0 #define FMT_EXCEPTIONS 0