Merge branch 'FlaxEngine:master' into flax-msdf-font

This commit is contained in:
fibref
2025-11-01 19:39:37 +08:00
committed by GitHub
65 changed files with 665 additions and 406 deletions
+1 -1
View File
@@ -31,7 +31,7 @@ body:
- '1.10'
- '1.11'
- master branch
default: 2
default: 3
validations:
required: true
- type: textarea
Binary file not shown.
+1 -1
View File
@@ -4,7 +4,7 @@
"Major": 1,
"Minor": 11,
"Revision": 0,
"Build": 6801
"Build": 6803
},
"Company": "Flax",
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
+2 -2
View File
@@ -49,7 +49,7 @@ Follow the instructions below to compile and run the engine from source.
* Fedora: `sudo dnf install dotnet-sdk-8.0`
* Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host`
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
* Ubuntu: `sudo apt install vulkan-sdk`
* Ubuntu: `sudo apt install vulkan-sdk` (deprecated, follow official docs)
* Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers`
* Arch: `sudo pacman -S vulkan-headers vulkan-tools vulkan-validation-layers`
* Install Git with LFS
@@ -60,7 +60,7 @@ Follow the instructions below to compile and run the engine from source.
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
* Install Clang compiler (version 6 or later):
* Install Clang compiler (version 14 or later):
* Ubuntu: `sudo apt-get install clang lldb lld`
* Fedora: `sudo dnf install clang llvm lldb lld`
* Arch: `sudo pacman -S clang lldb lld`
@@ -130,6 +130,11 @@ namespace FlaxEditor.Content
eyeAdaptation.Mode = EyeAdaptationMode.None;
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
var antiAliasing = preview.PostFxVolume.AntiAliasing;
antiAliasing.Mode = AntialiasingMode.FastApproximateAntialiasing;
antiAliasing.OverrideFlags |= AntiAliasingSettingsOverride.Mode;
preview.PostFxVolume.AntiAliasing = antiAliasing;
}
}
}
@@ -914,9 +914,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
// Remove drop down arrows and containment lines if no objects in the group
if (group.Children.Count == 0)
{
group.Panel.Close();
group.Panel.ArrowImageOpened = null;
group.Panel.ArrowImageClosed = null;
group.Panel.EnableContainmentLines = false;
group.Panel.CanOpenClose = false;
}
// Scripts arrange bar
+1 -1
View File
@@ -1587,7 +1587,7 @@ namespace FlaxEditor
if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null)
result = dockedTo.SelectedTab.Size;
else
result = gameWin.Viewport.Size;
result = gameWin.Viewport.ContentSize;
result *= root.DpiScale;
result = Float2.Round(result);
+125 -17
View File
@@ -10,17 +10,117 @@ using FlaxEditor.Modules;
using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
namespace FlaxEditor.Windows
{
/// <summary>
/// Render output control with content scaling support.
/// </summary>
public class ScaledRenderOutputControl : RenderOutputControl
{
/// <summary>
/// Custom scale.
/// </summary>
public float ContentScale = 1.0f;
/// <summary>
/// Actual bounds size for content (incl. scale).
/// </summary>
public Float2 ContentSize => Size / ContentScale;
/// <inheritdoc />
public ScaledRenderOutputControl(SceneRenderTask task)
: base(task)
{
}
/// <inheritdoc />
public override void Draw()
{
DrawSelf();
// Draw children with scale
var scaling = new Float3(ContentScale, ContentScale, 1);
Matrix3x3.Scaling(ref scaling, out Matrix3x3 scale);
Render2D.PushTransform(scale);
if (ClipChildren)
{
GetDesireClientArea(out var clientArea);
Render2D.PushClip(ref clientArea);
DrawChildren();
Render2D.PopClip();
}
else
{
DrawChildren();
}
Render2D.PopTransform();
}
/// <inheritdoc />
public override void GetDesireClientArea(out Rectangle rect)
{
// Scale the area for the client controls
rect = new Rectangle(Float2.Zero, Size / ContentScale);
}
/// <inheritdoc />
public override bool IntersectsContent(ref Float2 locationParent, out Float2 location)
{
// Skip local PointFromParent but use base code
location = base.PointFromParent(ref locationParent);
return ContainsPoint(ref location);
}
/// <inheritdoc />
public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation)
{
location /= ContentScale;
return base.IntersectsChildContent(child, location, out childSpaceLocation);
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Ignore as utility-only element
return false;
return base.ContainsPoint(ref location, precise);
}
/// <inheritdoc />
public override bool RayCast(ref Float2 location, out Control hit)
{
var p = location / ContentScale;
if (RayCastChildren(ref p, out hit))
return true;
return base.RayCast(ref location, out hit);
}
/// <inheritdoc />
public override Float2 PointToParent(ref Float2 location)
{
var result = base.PointToParent(ref location);
result *= ContentScale;
return result;
}
/// <inheritdoc />
public override Float2 PointFromParent(ref Float2 location)
{
var result = base.PointFromParent(ref location);
result /= ContentScale;
return result;
}
}
/// <summary>
/// Provides in-editor play mode simulation.
/// </summary>
/// <seealso cref="FlaxEditor.Windows.EditorWindow" />
public class GameWindow : EditorWindow
{
private readonly RenderOutputControl _viewport;
private readonly ScaledRenderOutputControl _viewport;
private readonly GameRoot _guiRoot;
private bool _showGUI = true, _editGUI = true;
private bool _showDebugDraw = false;
@@ -77,7 +177,7 @@ namespace FlaxEditor.Windows
/// <summary>
/// Gets the viewport.
/// </summary>
public RenderOutputControl Viewport => _viewport;
public ScaledRenderOutputControl Viewport => _viewport;
/// <summary>
/// Gets or sets a value indicating whether show game GUI in the view or keep it hidden.
@@ -295,7 +395,7 @@ namespace FlaxEditor.Windows
var task = MainRenderTask.Instance;
// Setup viewport
_viewport = new RenderOutputControl(task)
_viewport = new ScaledRenderOutputControl(task)
{
AnchorPreset = AnchorPresets.StretchAll,
Offsets = Margin.Zero,
@@ -396,11 +496,8 @@ namespace FlaxEditor.Windows
{
if (v == null)
return;
if (v.Size.Y <= 0 || v.Size.X <= 0)
{
return;
}
if (string.Equals(v.Label, "Free Aspect", StringComparison.Ordinal) && v.Size == new Int2(1, 1))
{
@@ -448,15 +545,7 @@ namespace FlaxEditor.Windows
private void ResizeViewport()
{
if (!_freeAspect)
{
_windowAspectRatio = Width / Height;
}
else
{
_windowAspectRatio = 1;
}
_windowAspectRatio = _freeAspect ? 1 : Width / Height;
var scaleWidth = _viewportAspectRatio / _windowAspectRatio;
var scaleHeight = _windowAspectRatio / _viewportAspectRatio;
@@ -468,6 +557,24 @@ namespace FlaxEditor.Windows
{
_viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height);
}
if (_viewport.KeepAspectRatio)
{
var resolution = _viewport.CustomResolution.HasValue ? (Float2)_viewport.CustomResolution.Value : Size;
if (scaleHeight < 1)
{
_viewport.ContentScale = _viewport.Width / resolution.X;
}
else
{
_viewport.ContentScale = _viewport.Height / resolution.Y;
}
}
else
{
_viewport.ContentScale = 1;
}
_viewport.SyncBackbufferSize();
PerformLayout();
}
@@ -907,6 +1014,7 @@ namespace FlaxEditor.Windows
return child.OnNavigate(direction, Float2.Zero, this, visited);
}
}
return null;
}
@@ -957,7 +1065,7 @@ namespace FlaxEditor.Windows
else
_defaultScaleActiveIndex = 0;
}
if (_customScaleActiveIndex != -1)
{
var options = Editor.UI.CustomViewportScaleOptions;
+2
View File
@@ -425,6 +425,8 @@ namespace FlaxEngine.Interop
var fieldOffsetPtr = (IntPtr*)field.FieldHandle.Value; // Pointer to MonoClassField
fieldOffsetPtr += 3; // Skip three pointers (type, name, parent_and_flags)
return *(int*)fieldOffsetPtr - IntPtr.Size * 2; // Load the value of a pointer (4 bytes, int32), then subtracting 16 bytes from it (2 pointers for vtable and threadsync)
#else
throw new NotImplementedException();
#endif
}
@@ -491,7 +491,7 @@ void GPUContextDX12::flushUAVs()
ASSERT(uaCount <= GPU_MAX_UA_BINDED);
// Fill table with source descriptors
DxShaderHeader& header = _currentCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header;
DxShaderHeader& header = _isCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header;
D3D12_CPU_DESCRIPTOR_HANDLE srcDescriptorRangeStarts[GPU_MAX_UA_BINDED];
for (uint32 i = 0; i < uaCount; i++)
{
@@ -114,7 +114,7 @@ GPUContextVulkan::GPUContextVulkan(GPUDeviceVulkan* device, QueueVulkan* queue)
#if GPU_ENABLE_TRACY
#if VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset && !PLATFORM_SWITCH
// Use calibrated timestamps extension
if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT)
if (vkResetQueryPoolEXT && vkGetCalibratedTimestampsEXT && _device->PhysicalDeviceFeatures12.hostQueryReset)
{
_tracyContext = tracy::CreateVkContext(_device->Adapter->Gpu, _device->Device, vkResetQueryPoolEXT, vkGetPhysicalDeviceCalibrateableTimeDomainsEXT, vkGetCalibratedTimestampsEXT);
}
@@ -1568,7 +1568,15 @@ bool GPUDeviceVulkan::Init()
vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queueCount, QueueFamilyProps.Get());
// Query device features
RenderToolsVulkan::ZeroStruct(PhysicalDeviceFeatures12, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES);
vkGetPhysicalDeviceFeatures(gpu, &PhysicalDeviceFeatures);
if (vkGetPhysicalDeviceFeatures2)
{
VkPhysicalDeviceFeatures2 features2;
RenderToolsVulkan::ZeroStruct(features2, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2);
features2.pNext = &PhysicalDeviceFeatures12;
vkGetPhysicalDeviceFeatures2(gpu, &features2);
}
// Get extensions and layers
Array<const char*> deviceExtensions;
@@ -1671,6 +1679,16 @@ bool GPUDeviceVulkan::Init()
VulkanPlatform::RestrictEnabledPhysicalDeviceFeatures(PhysicalDeviceFeatures, enabledFeatures);
deviceInfo.pEnabledFeatures = &enabledFeatures;
#if GPU_ENABLE_TRACY && VK_EXT_calibrated_timestamps && VK_EXT_host_query_reset
VkPhysicalDeviceHostQueryResetFeatures resetFeatures;
if (PhysicalDeviceFeatures12.hostQueryReset)
{
RenderToolsVulkan::ZeroStruct(resetFeatures, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES);
resetFeatures.hostQueryReset = VK_TRUE;
deviceInfo.pNext = &resetFeatures;
}
#endif
// Create the device
VALIDATE_VULKAN_RESULT(vkCreateDevice(gpu, &deviceInfo, nullptr, &Device));
@@ -496,6 +496,7 @@ public:
/// The physical device enabled features.
/// </summary>
VkPhysicalDeviceFeatures PhysicalDeviceFeatures;
VkPhysicalDeviceVulkan12Features PhysicalDeviceFeatures12;
Array<BufferedQueryPoolVulkan*> TimestampQueryPools;
Array<BufferedQueryPoolVulkan*> OcclusionQueryPools;
+3 -1
View File
@@ -8,7 +8,7 @@
#endif
// Internal version number of networking implementation. Updated once engine changes serialization or connection rules.
#define NETWORK_PROTOCOL_VERSION 4
#define NETWORK_PROTOCOL_VERSION 5
// Enables encoding object ids and typenames via uint32 keys rather than full data send.
#define USE_NETWORK_KEYS 1
@@ -29,6 +29,7 @@ enum class NetworkMessageIDs : uint8
ObjectDespawn,
ObjectRole,
ObjectRpc,
ObjectRpcPart,
MAX,
};
@@ -48,6 +49,7 @@ public:
static void OnNetworkMessageObjectDespawn(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
static void OnNetworkMessageObjectRole(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
static void OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
static void OnNetworkMessageObjectRpcPart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer);
#if COMPILE_WITH_PROFILER
@@ -391,6 +391,7 @@ namespace
NetworkInternal::OnNetworkMessageObjectDespawn,
NetworkInternal::OnNetworkMessageObjectRole,
NetworkInternal::OnNetworkMessageObjectRpc,
NetworkInternal::OnNetworkMessageObjectRpcPart,
};
}
+333 -254
View File
@@ -55,14 +55,14 @@ PACK_STRUCT(struct NetworkMessageObjectReplicate
uint32 OwnerFrame;
});
PACK_STRUCT(struct NetworkMessageObjectReplicatePayload
PACK_STRUCT(struct NetworkMessageObjectPartPayload
{
uint16 DataSize;
uint16 PartsCount;
uint16 PartSize;
});
PACK_STRUCT(struct NetworkMessageObjectReplicatePart
PACK_STRUCT(struct NetworkMessageObjectPart
{
NetworkMessageIDs ID = NetworkMessageIDs::ObjectReplicatePart;
uint32 OwnerFrame;
@@ -111,7 +111,7 @@ PACK_STRUCT(struct NetworkMessageObjectRole
PACK_STRUCT(struct NetworkMessageObjectRpc
{
NetworkMessageIDs ID = NetworkMessageIDs::ObjectRpc;
uint16 ArgsSize;
uint32 OwnerFrame;
});
struct NetworkReplicatedObject
@@ -182,13 +182,14 @@ struct Serializer
void* Tags[2];
};
struct ReplicateItem
struct PartsItem
{
ScriptingObjectReference<ScriptingObject> Object;
Guid ObjectId;
uint16 PartsLeft;
uint32 OwnerFrame;
uint32 OwnerClientId;
const void* Tag;
Array<byte> Data;
};
@@ -220,7 +221,7 @@ struct DespawnItem
DataContainer<uint32> Targets;
};
struct RpcItem
struct RpcSendItem
{
ScriptingObjectReference<ScriptingObject> Object;
NetworkRpcName Name;
@@ -233,11 +234,12 @@ namespace
{
CriticalSection ObjectsLock;
HashSet<NetworkReplicatedObject> Objects;
Array<ReplicateItem> ReplicationParts;
Array<PartsItem> ReplicationParts;
Array<PartsItem> RpcParts;
Array<SpawnItemParts> SpawnParts;
Array<SpawnItem> SpawnQueue;
Array<DespawnItem> DespawnQueue;
Array<RpcItem> RpcQueue;
Array<RpcSendItem> RpcQueue;
Dictionary<Guid, Guid> IdsRemappingTable;
NetworkStream* CachedWriteStream = nullptr;
NetworkStream* CachedReadStream = nullptr;
@@ -251,6 +253,7 @@ namespace
#endif
Array<Guid> DespawnedObjects;
uint32 SpawnId = 0;
uint32 RpcId = 0;
#if USE_EDITOR
void OnScriptsReloading()
@@ -505,6 +508,76 @@ void SetupObjectSpawnMessageItem(SpawnItem* e, NetworkMessage& msg)
msg.WriteStructure(msgDataItem);
}
void SendInParts(NetworkPeer* peer, NetworkChannelType channel, const byte* data, const uint16 dataSize, NetworkMessage& msg, const NetworkRpcName& name, bool toServer, const Guid& objectId, uint32 ownerFrame, NetworkMessageIDs partId)
{
NetworkMessageObjectPartPayload msgDataPayload;
msgDataPayload.DataSize = dataSize;
const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid);
const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectPartPayload);
const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectPart) - networkKeyIdWorstCaseSize;
uint32 partsCount = 1;
uint32 dataStart = 0;
uint32 msgDataSize = dataSize;
if (dataSize > msgMaxData)
{
// Send msgMaxData within first message
msgDataSize = msgMaxData;
dataStart += msgMaxData;
// Send rest of the data in separate parts
partsCount += Math::DivideAndRoundUp(dataSize - dataStart, partMaxData);
// TODO: promote channel to Ordered when using parts?
}
else
dataStart += dataSize;
ASSERT(partsCount <= MAX_uint8);
msgDataPayload.PartsCount = partsCount;
msgDataPayload.PartSize = msgDataSize;
msg.WriteStructure(msgDataPayload);
msg.WriteBytes(data, msgDataSize);
uint32 messageSize = msg.Length;
if (toServer)
peer->EndSendMessage(channel, msg);
else
peer->EndSendMessage(channel, msg, CachedTargets);
// Send all other parts
for (uint32 partIndex = 1; partIndex < partsCount; partIndex++)
{
NetworkMessageObjectPart msgDataPart;
msgDataPart.ID = partId;
msgDataPart.OwnerFrame = ownerFrame;
msgDataPart.DataSize = msgDataPayload.DataSize;
msgDataPart.PartsCount = msgDataPayload.PartsCount;
msgDataPart.PartStart = dataStart;
msgDataPart.PartSize = Math::Min(dataSize - dataStart, partMaxData);
msg = peer->BeginSendMessage();
msg.WriteStructure(msgDataPart);
msg.WriteNetworkId(objectId);
msg.WriteBytes(data + msgDataPart.PartStart, msgDataPart.PartSize);
messageSize += msg.Length;
dataStart += msgDataPart.PartSize;
if (toServer)
peer->EndSendMessage(channel, msg);
else
peer->EndSendMessage(channel, msg, CachedTargets);
}
ASSERT_LOW_LAYER(dataStart == dataSize);
#if COMPILE_WITH_PROFILER
// Network stats recording
if (NetworkInternal::EnableProfiling)
{
auto& profileEvent = NetworkInternal::ProfilerEvents[name];
profileEvent.Count++;
profileEvent.DataSize += dataSize;
profileEvent.MessageSize += messageSize;
profileEvent.Receivers += toServer ? 1 : CachedTargets.Count();
}
#endif
}
void SendObjectSpawnMessage(const SpawnGroup& group, const Array<NetworkClient*>& clients)
{
PROFILE_CPU();
@@ -589,7 +662,7 @@ void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkCli
msg.WriteNetworkId(objectId);
if (NetworkManager::IsClient())
{
NetworkManager::Peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
}
else
{
@@ -598,6 +671,160 @@ void SendObjectRoleMessage(const NetworkReplicatedObject& item, const NetworkCli
}
}
void SendDespawn(DespawnItem& e)
{
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object ID={}", e.Id.ToString());
NetworkMessageObjectDespawn msgData;
Guid objectId = e.Id;
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(objectId, &objectId);
}
auto peer = NetworkManager::Peer;
NetworkMessage msg = peer->BeginSendMessage();
msg.WriteStructure(msgData);
msg.WriteNetworkId(objectId);
BuildCachedTargets(NetworkManager::Clients, e.Targets);
if (NetworkManager::IsClient())
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
else
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg, CachedTargets);
}
void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients)
{
auto it = Objects.Find(obj->GetID());
if (it.IsEnd())
return;
auto& item = it->Item;
const bool isClient = NetworkManager::IsClient();
// Skip serialization of objects that none will receive
if (!isClient)
{
BuildCachedTargets(item, targetClients);
if (CachedTargets.Count() == 0)
return;
}
if (item.AsNetworkObject)
item.AsNetworkObject->OnNetworkSerialize();
// Serialize object
NetworkStream* stream = CachedWriteStream;
stream->Initialize();
const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true);
if (failed)
{
//NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString());
return;
}
const uint32 size = stream->GetPosition();
if (size > MAX_uint16)
{
LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16);
return;
}
#if USE_NETWORK_REPLICATOR_CACHE
// Process replication cache to skip sending object data if it didn't change
if (item.RepCache.Data.Length() == size &&
item.RepCache.Mask == targetClients &&
Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0)
{
return;
}
item.RepCache.Mask = targetClients;
item.RepCache.Data.Copy(stream->GetBuffer(), size);
#endif
// TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state)
constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable;
// Send object to clients
NetworkMessageObjectReplicate msgData;
msgData.OwnerFrame = NetworkManager::Frame;
Guid objectId = item.ObjectId, parentId = item.ParentId;
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(objectId, &objectId);
IdsRemappingTable.KeyOf(parentId, &parentId);
}
NetworkPeer* peer = NetworkManager::Peer;
NetworkMessage msg = peer->BeginSendMessage();
msg.WriteStructure(msgData);
msg.WriteNetworkId(objectId);
msg.WriteNetworkId(parentId);
msg.WriteNetworkName(obj->GetType().Fullname);
const NetworkRpcName name(obj->GetTypeHandle(), StringAnsiView::Empty);
SendInParts(peer, repChannel, stream->GetBuffer(), size, msg, name, isClient, objectId, msgData.OwnerFrame, NetworkMessageIDs::ObjectReplicatePart);
}
void SendRpc(RpcSendItem& e)
{
ScriptingObject* obj = e.Object.Get();
if (!obj)
return;
auto it = Objects.Find(obj->GetID());
if (it == Objects.End())
{
#if !BUILD_RELEASE
if (!DespawnedObjects.Contains(obj->GetID()))
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID());
#endif
return;
}
auto& item = it->Item;
if (e.ArgsData.Length() > MAX_uint16)
{
LOG(Error, "Too much data for object RPC method '{}.{}' on object '{}' ({} bytes provided while limit is {}).", e.Name.First.ToString(), e.Name.Second.ToString(), obj->GetID(), e.ArgsData.Length(), MAX_uint16);
return;
}
const NetworkManagerMode mode = NetworkManager::Mode;
NetworkPeer* peer = NetworkManager::Peer;
bool toServer;
if (e.Info.Server && mode == NetworkManagerMode::Client)
{
// Client -> Server
#if USE_NETWORK_REPLICATOR_LOG
if (e.Targets.Length() != 0)
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}.{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString());
#endif
toServer = true;
}
else if (e.Info.Client && (mode == NetworkManagerMode::Server || mode == NetworkManagerMode::Host))
{
// Server -> Client(s)
BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId);
if (CachedTargets.IsEmpty())
return;
toServer = false;
}
else
return;
// Send RPC message
//NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}.{} object ID={}", e.Name.First.ToString(), e.Name.Second.ToString(), item.ToString());
NetworkMessageObjectRpc msgData;
msgData.OwnerFrame = ++RpcId;
Guid objectId = item.ObjectId;
Guid parentId = item.ParentId;
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(objectId, &objectId);
IdsRemappingTable.KeyOf(parentId, &parentId);
}
NetworkMessage msg = peer->BeginSendMessage();
msg.WriteStructure(msgData);
msg.WriteNetworkId(objectId);
msg.WriteNetworkId(parentId);
msg.WriteNetworkName(obj->GetType().Fullname);
msg.WriteNetworkName(e.Name.First.GetType().Fullname);
msg.WriteNetworkName(e.Name.Second);
NetworkChannelType channel = (NetworkChannelType)e.Info.Channel;
SendInParts(peer, channel, e.ArgsData.Get(), e.ArgsData.Length(), msg, e.Name, toServer, objectId, msgData.OwnerFrame, NetworkMessageIDs::ObjectRpcPart);
}
void DeleteNetworkObject(ScriptingObject* obj)
{
// Remove from the mapping table
@@ -708,38 +935,43 @@ FORCE_INLINE void DirtyObjectImpl(NetworkReplicatedObject& item, ScriptingObject
Hierarchy->DirtyObject(obj);
}
ReplicateItem* AddObjectReplicateItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId)
PartsItem* AddPartsItem(Array<PartsItem>& items, NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId)
{
// Reuse or add part item
ReplicateItem* replicateItem = nullptr;
for (auto& e : ReplicationParts)
PartsItem* item = nullptr;
for (auto& e : items)
{
if (e.OwnerFrame == ownerFrame && e.Data.Count() == dataSize && e.ObjectId == objectId)
{
// Reuse
replicateItem = &e;
item = &e;
break;
}
}
if (!replicateItem)
if (!item)
{
// Add
replicateItem = &ReplicationParts.AddOne();
replicateItem->ObjectId = objectId;
replicateItem->PartsLeft = partsCount;
replicateItem->OwnerFrame = ownerFrame;
replicateItem->OwnerClientId = senderClientId;
replicateItem->Data.Resize(dataSize);
item = &items.AddOne();
item->ObjectId = objectId;
item->PartsLeft = partsCount;
item->OwnerFrame = ownerFrame;
item->OwnerClientId = senderClientId;
item->Data.Resize(dataSize);
}
// Copy part data
ASSERT(replicateItem->PartsLeft > 0);
replicateItem->PartsLeft--;
ASSERT(partStart + partSize <= replicateItem->Data.Count());
ASSERT(item->PartsLeft > 0);
item->PartsLeft--;
ASSERT(partStart + partSize <= item->Data.Count());
const void* partData = event.Message.SkipBytes(partSize);
Platform::MemoryCopy(replicateItem->Data.Get() + partStart, partData, partSize);
Platform::MemoryCopy(item->Data.Get() + partStart, partData, partSize);
return replicateItem;
return item;
}
FORCE_INLINE PartsItem* AddObjectReplicateItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId)
{
return AddPartsItem(ReplicationParts, event, ownerFrame, partsCount, dataSize, objectId, partStart, partSize, senderClientId);
}
void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, byte* data, uint32 dataSize, uint32 senderClientId)
@@ -787,6 +1019,24 @@ void InvokeObjectReplication(NetworkReplicatedObject& item, uint32 ownerFrame, b
DirtyObjectImpl(item, obj);
}
FORCE_INLINE PartsItem* AddObjectRpcItem(NetworkEvent& event, uint32 ownerFrame, uint16 partsCount, uint16 dataSize, const Guid& objectId, uint16 partStart, uint16 partSize, uint32 senderClientId)
{
return AddPartsItem(RpcParts, event, ownerFrame, partsCount, dataSize, objectId, partStart, partSize, senderClientId);
}
void InvokeObjectRpc(const NetworkRpcInfo* info, byte* data, uint32 dataSize, uint32 senderClientId, ScriptingObject* obj)
{
// Setup message reading stream
if (CachedReadStream == nullptr)
CachedReadStream = New<NetworkStream>();
NetworkStream* stream = CachedReadStream;
stream->SenderId = senderClientId;
stream->Initialize(data, dataSize);
// Execute RPC
info->Execute(obj, stream, info->Tag);
}
void InvokeObjectSpawn(const NetworkMessageObjectSpawn& msgData, const Guid& prefabId, const NetworkMessageObjectSpawnItem* msgDataItems)
{
ScopeLock lock(ObjectsLock);
@@ -1652,9 +1902,6 @@ void NetworkInternal::NetworkReplicatorUpdate()
if (Objects.Count() == 0)
return;
const bool isClient = NetworkManager::IsClient();
const bool isServer = NetworkManager::IsServer();
const bool isHost = NetworkManager::IsHost();
NetworkPeer* peer = NetworkManager::Peer;
if (!isClient && NewClients.Count() != 0)
{
@@ -1694,22 +1941,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
PROFILE_CPU_NAMED("DespawnQueue");
for (DespawnItem& e : DespawnQueue)
{
// Send despawn message
NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Despawn object ID={}", e.Id.ToString());
NetworkMessageObjectDespawn msgData;
Guid objectId = e.Id;
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(objectId, &objectId);
}
NetworkMessage msg = peer->BeginSendMessage();
msg.WriteStructure(msgData);
msg.WriteNetworkId(objectId);
BuildCachedTargets(NetworkManager::Clients, e.Targets);
if (isClient)
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg);
else
peer->EndSendMessage(NetworkChannelType::ReliableOrdered, msg, CachedTargets);
SendDespawn(e);
}
DespawnQueue.Clear();
}
@@ -1830,6 +2062,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
}
}
// TODO: remove items from RpcParts after some TTL to reduce memory usage
// TODO: remove items from SpawnParts after some TTL to reduce memory usage
// Replicate all owned networked objects with other clients or server
@@ -1871,136 +2104,11 @@ void NetworkInternal::NetworkReplicatorUpdate()
PROFILE_CPU_NAMED("Replication");
if (CachedWriteStream == nullptr)
CachedWriteStream = New<NetworkStream>();
NetworkStream* stream = CachedWriteStream;
stream->SenderId = NetworkManager::LocalClientId;
CachedWriteStream->SenderId = NetworkManager::LocalClientId;
// TODO: use Job System when replicated objects count is large
for (auto& e : CachedReplicationResult->_entries)
{
ScriptingObject* obj = e.Object;
auto it = Objects.Find(obj->GetID());
if (it.IsEnd())
continue;
auto& item = it->Item;
// Skip serialization of objects that none will receive
if (!isClient)
{
BuildCachedTargets(item, e.TargetClients);
if (CachedTargets.Count() == 0)
continue;
}
if (item.AsNetworkObject)
item.AsNetworkObject->OnNetworkSerialize();
// Serialize object
stream->Initialize();
const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true);
if (failed)
{
//NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString());
continue;
}
const uint32 size = stream->GetPosition();
if (size > MAX_uint16)
{
LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16);
continue;
}
#if USE_NETWORK_REPLICATOR_CACHE
// Process replication cache to skip sending object data if it didn't change
if (item.RepCache.Data.Length() == size &&
item.RepCache.Mask == e.TargetClients &&
Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0)
{
continue;
}
item.RepCache.Mask = e.TargetClients;
item.RepCache.Data.Copy(stream->GetBuffer(), size);
#endif
// TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state)
constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable;
// Send object to clients
NetworkMessageObjectReplicate msgData;
msgData.OwnerFrame = NetworkManager::Frame;
Guid objectId = item.ObjectId, parentId = item.ParentId;
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(objectId, &objectId);
IdsRemappingTable.KeyOf(parentId, &parentId);
}
NetworkMessage msg = peer->BeginSendMessage();
msg.WriteStructure(msgData);
msg.WriteNetworkId(objectId);
msg.WriteNetworkId(parentId);
msg.WriteNetworkName(obj->GetType().Fullname);
NetworkMessageObjectReplicatePayload msgDataPayload;
msgDataPayload.DataSize = size;
const uint32 networkKeyIdWorstCaseSize = sizeof(uint32) + sizeof(Guid);
const uint32 msgMaxData = peer->Config.MessageSize - msg.Position - sizeof(NetworkMessageObjectReplicatePayload);
const uint32 partMaxData = peer->Config.MessageSize - sizeof(NetworkMessageObjectReplicatePart) - networkKeyIdWorstCaseSize;
uint32 partsCount = 1;
uint32 dataStart = 0;
uint32 msgDataSize = size;
if (size > msgMaxData)
{
// Send msgMaxData within first message
msgDataSize = msgMaxData;
dataStart += msgMaxData;
// Send rest of the data in separate parts
partsCount += Math::DivideAndRoundUp(size - dataStart, partMaxData);
}
else
dataStart += size;
ASSERT(partsCount <= MAX_uint8);
msgDataPayload.PartsCount = partsCount;
msgDataPayload.PartSize = msgDataSize;
msg.WriteStructure(msgDataPayload);
msg.WriteBytes(stream->GetBuffer(), msgDataSize);
uint32 dataSize = msgDataSize, messageSize = msg.Length;
if (isClient)
peer->EndSendMessage(repChannel, msg);
else
peer->EndSendMessage(repChannel, msg, CachedTargets);
// Send all other parts
for (uint32 partIndex = 1; partIndex < partsCount; partIndex++)
{
NetworkMessageObjectReplicatePart msgDataPart;
msgDataPart.OwnerFrame = msgData.OwnerFrame;
msgDataPart.DataSize = msgDataPayload.DataSize;
msgDataPart.PartsCount = msgDataPayload.PartsCount;
msgDataPart.PartStart = dataStart;
msgDataPart.PartSize = Math::Min(size - dataStart, partMaxData);
msg = peer->BeginSendMessage();
msg.WriteStructure(msgDataPart);
msg.WriteNetworkId(objectId);
msg.WriteBytes(stream->GetBuffer() + msgDataPart.PartStart, msgDataPart.PartSize);
messageSize += msg.Length;
dataSize += msgDataPart.PartSize;
dataStart += msgDataPart.PartSize;
if (isClient)
peer->EndSendMessage(repChannel, msg);
else
peer->EndSendMessage(repChannel, msg, CachedTargets);
}
ASSERT_LOW_LAYER(dataStart == size);
#if COMPILE_WITH_PROFILER
// Network stats recording
if (EnableProfiling)
{
const Pair<ScriptingTypeHandle, StringAnsiView> name(obj->GetTypeHandle(), StringAnsiView::Empty);
auto& profileEvent = ProfilerEvents[name];
profileEvent.Count++;
profileEvent.DataSize += dataSize;
profileEvent.MessageSize += messageSize;
profileEvent.Receivers += isClient ? 1 : CachedTargets.Count();
}
#endif
SendReplication(e.Object, e.TargetClients);
}
}
@@ -2009,70 +2117,7 @@ void NetworkInternal::NetworkReplicatorUpdate()
PROFILE_CPU_NAMED("Rpc");
for (auto& e : RpcQueue)
{
ScriptingObject* obj = e.Object.Get();
if (!obj)
continue;
auto it = Objects.Find(obj->GetID());
if (it == Objects.End())
{
#if USE_EDITOR || !BUILD_RELEASE
if (!DespawnedObjects.Contains(obj->GetID()))
LOG(Error, "Cannot invoke RPC method '{0}.{1}' on object '{2}' that is not registered in networking (use 'NetworkReplicator.AddObject').", e.Name.First.ToString(), String(e.Name.Second), obj->GetID());
#endif
continue;
}
auto& item = it->Item;
// Send RPC message
//NETWORK_REPLICATOR_LOG(Info, "[NetworkReplicator] Rpc {}::{} object ID={}", e.Name.First.ToString(), String(e.Name.Second), item.ToString());
NetworkMessageObjectRpc msgData;
Guid msgObjectId = item.ObjectId;
Guid msgParentId = item.ParentId;
{
// Remap local client object ids into server ids
IdsRemappingTable.KeyOf(msgObjectId, &msgObjectId);
IdsRemappingTable.KeyOf(msgParentId, &msgParentId);
}
msgData.ArgsSize = (uint16)e.ArgsData.Length();
NetworkMessage msg = peer->BeginSendMessage();
msg.WriteStructure(msgData);
msg.WriteNetworkId(msgObjectId);
msg.WriteNetworkId(msgParentId);
msg.WriteNetworkName(obj->GetType().Fullname);
msg.WriteNetworkName(e.Name.First.GetType().Fullname);
msg.WriteNetworkName(e.Name.Second);
msg.WriteBytes(e.ArgsData.Get(), e.ArgsData.Length());
uint32 dataSize = e.ArgsData.Length(), messageSize = msg.Length, receivers = 0;
NetworkChannelType channel = (NetworkChannelType)e.Info.Channel;
if (e.Info.Server && isClient)
{
// Client -> Server
#if USE_NETWORK_REPLICATOR_LOG
if (e.Targets.Length() != 0)
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Server RPC '{}::{}' called with non-empty list of targets is not supported (only server will receive it)", e.Name.First.ToString(), e.Name.Second.ToString());
#endif
peer->EndSendMessage(channel, msg);
receivers = 1;
}
else if (e.Info.Client && (isServer || isHost))
{
// Server -> Client(s)
BuildCachedTargets(NetworkManager::Clients, item.TargetClientIds, e.Targets, NetworkManager::LocalClientId);
peer->EndSendMessage(channel, msg, CachedTargets);
receivers = CachedTargets.Count();
}
#if COMPILE_WITH_PROFILER
// Network stats recording
if (EnableProfiling && receivers)
{
auto& profileEvent = ProfilerEvents[e.Name];
profileEvent.Count++;
profileEvent.DataSize += dataSize;
profileEvent.MessageSize += messageSize;
profileEvent.Receivers += receivers;
}
#endif
SendRpc(e);
}
RpcQueue.Clear();
}
@@ -2085,7 +2130,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
{
PROFILE_CPU();
NetworkMessageObjectReplicate msgData;
NetworkMessageObjectReplicatePayload msgDataPayload;
NetworkMessageObjectPartPayload msgDataPayload;
Guid objectId, parentId;
StringAnsiView objectTypeName;
event.Message.ReadStructure(msgData);
@@ -2095,7 +2140,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
event.Message.ReadStructure(msgDataPayload);
ScopeLock lock(ObjectsLock);
if (DespawnedObjects.Contains(objectId))
return; // Skip replicating not-existing objects
return; // Skip replicating non-existing objects
NetworkReplicatedObject* e = ResolveObject(objectId, parentId, objectTypeName);
if (!e)
return;
@@ -2114,7 +2159,7 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
else
{
// Add to replication from multiple parts
ReplicateItem* replicateItem = AddObjectReplicateItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId);
PartsItem* replicateItem = AddObjectReplicateItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId);
replicateItem->Object = e->Object;
}
}
@@ -2122,13 +2167,13 @@ void NetworkInternal::OnNetworkMessageObjectReplicate(NetworkEvent& event, Netwo
void NetworkInternal::OnNetworkMessageObjectReplicatePart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer)
{
PROFILE_CPU();
NetworkMessageObjectReplicatePart msgData;
NetworkMessageObjectPart msgData;
Guid objectId;
event.Message.ReadStructure(msgData);
event.Message.ReadNetworkId(objectId);
ScopeLock lock(ObjectsLock);
if (DespawnedObjects.Contains(objectId))
return; // Skip replicating not-existing objects
return; // Skip replicating non-existing objects
const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId;
AddObjectReplicateItem(event, msgData.OwnerFrame, msgData.PartsCount, msgData.DataSize, objectId, msgData.PartStart, msgData.PartSize, senderClientId);
@@ -2288,14 +2333,16 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
{
PROFILE_CPU();
NetworkMessageObjectRpc msgData;
Guid msgObjectId, msgParentId;
NetworkMessageObjectPartPayload msgDataPayload;
Guid objectId, parentId;
StringAnsiView objectTypeName, rpcTypeName, rpcName;
event.Message.ReadStructure(msgData);
event.Message.ReadNetworkId(msgObjectId);
event.Message.ReadNetworkId(msgParentId);
event.Message.ReadNetworkId(objectId);
event.Message.ReadNetworkId(parentId);
event.Message.ReadNetworkName(objectTypeName);
event.Message.ReadNetworkName(rpcTypeName);
event.Message.ReadNetworkName(rpcName);
event.Message.ReadStructure(msgDataPayload);
ScopeLock lock(ObjectsLock);
// Find RPC info
@@ -2305,11 +2352,11 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(name);
if (!info)
{
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(rpcTypeName), String(rpcName), msgObjectId);
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown RPC {}::{} for object {}", String(rpcTypeName), String(rpcName), objectId);
return;
}
NetworkReplicatedObject* e = ResolveObject(msgObjectId, msgParentId, objectTypeName);
NetworkReplicatedObject* e = ResolveObject(objectId, parentId, objectTypeName);
if (e)
{
auto& item = *e;
@@ -2329,18 +2376,50 @@ void NetworkInternal::OnNetworkMessageObjectRpc(NetworkEvent& event, NetworkClie
return;
}
// Setup message reading stream
if (CachedReadStream == nullptr)
CachedReadStream = New<NetworkStream>();
NetworkStream* stream = CachedReadStream;
stream->SenderId = client ? client->ClientId : NetworkManager::ServerClientId;
stream->Initialize(event.Message.Buffer + event.Message.Position, msgData.ArgsSize);
// Execute RPC
info->Execute(obj, stream, info->Tag);
const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId;
if (msgDataPayload.PartsCount == 1)
{
// Call RPC
InvokeObjectRpc(info, event.Message.Buffer + event.Message.Position, msgDataPayload.DataSize, senderClientId, obj);
}
else
{
// Add to RPC from multiple parts
PartsItem* rpcItem = AddObjectRpcItem(event, msgData.OwnerFrame, msgDataPayload.PartsCount, msgDataPayload.DataSize, objectId, 0, msgDataPayload.PartSize, senderClientId);
rpcItem->Object = e->Object;
rpcItem->Tag = info;
}
}
else if (info->Channel != static_cast<uint8>(NetworkChannelType::Unreliable) && info->Channel != static_cast<uint8>(NetworkChannelType::UnreliableOrdered))
{
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", msgObjectId, String(rpcTypeName), String(rpcName));
NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Unknown object {} RPC {}::{}", objectId, String(rpcTypeName), String(rpcName));
}
}
void NetworkInternal::OnNetworkMessageObjectRpcPart(NetworkEvent& event, NetworkClient* client, NetworkPeer* peer)
{
PROFILE_CPU();
NetworkMessageObjectPart msgData;
Guid objectId;
event.Message.ReadStructure(msgData);
event.Message.ReadNetworkId(objectId);
ScopeLock lock(ObjectsLock);
if (DespawnedObjects.Contains(objectId))
return; // Skip replicating non-existing objects
const uint32 senderClientId = client ? client->ClientId : NetworkManager::ServerClientId;
PartsItem* rpcItem = AddObjectRpcItem(event, msgData.OwnerFrame, msgData.PartsCount, msgData.DataSize, objectId, msgData.PartStart, msgData.PartSize, senderClientId);
if (rpcItem && rpcItem->PartsLeft == 0)
{
// Got all parts so invoke RPC
ScriptingObject* obj = rpcItem->Object.Get();
if (obj)
{
InvokeObjectRpc((const NetworkRpcInfo*)rpcItem->Tag, rpcItem->Data.Get(), rpcItem->Data.Count(), rpcItem->OwnerClientId, obj);
}
// Remove item
int32 partIndex = (int32)((RpcParts.Get() - rpcItem) / sizeof(rpcItem));
RpcParts.RemoveAt(partIndex);
}
}
@@ -1969,6 +1969,7 @@ void PhysicsBackend::EndSimulateScene(void* scene)
scenePhysX->EventsCallback.SendTriggerEvents();
scenePhysX->EventsCallback.SendCollisionEvents();
scenePhysX->EventsCallback.SendJointEvents();
scenePhysX->EventsCallback.Clear();
}
// Clear delta after simulation ended
@@ -4466,14 +4467,14 @@ void PhysicsBackend::FlushRequests(void* scene)
}
if (scenePhysX->RemoveColliders.HasItems())
{
for (int32 i = 0; i < scenePhysX->RemoveColliders.Count(); i++)
scenePhysX->EventsCallback.OnColliderRemoved(scenePhysX->RemoveColliders[i]);
for (PhysicsColliderActor* e : scenePhysX->RemoveColliders)
scenePhysX->EventsCallback.OnColliderRemoved(e);
scenePhysX->RemoveColliders.Clear();
}
if (scenePhysX->RemoveJoints.HasItems())
{
for (int32 i = 0; i < scenePhysX->RemoveJoints.Count(); i++)
scenePhysX->EventsCallback.OnJointRemoved(scenePhysX->RemoveJoints[i]);
for (Joint* e : scenePhysX->RemoveJoints)
scenePhysX->EventsCallback.OnJointRemoved(e);
scenePhysX->RemoveJoints.Clear();
}
@@ -543,11 +543,9 @@ void WindowsPlatform::ReleaseMutex()
}
}
void WindowsPlatform::PreInit(void* hInstance)
PRAGMA_DISABLE_OPTIMIZATION;
void CheckInstructionSet()
{
ASSERT(hInstance);
Instance = hInstance;
#if PLATFORM_ARCH_X86 || PLATFORM_ARCH_X64
// Check the minimum vector instruction set support
int32 cpuInfo[4] = { -1 };
@@ -597,10 +595,19 @@ void WindowsPlatform::PreInit(void* hInstance)
{
// Not supported CPU
CPUBrand cpu;
Error(String::Format(TEXT("Cannot start program due to lack of CPU feature {}.\n\n{}"), missingFeature, String(cpu.Buffer)));
Platform::Error(String::Format(TEXT("Cannot start program due to lack of CPU feature {}.\n\n{}"), missingFeature, String(cpu.Buffer)));
exit(-1);
}
#endif
}
PRAGMA_ENABLE_OPTIMIZATION;
void WindowsPlatform::PreInit(void* hInstance)
{
ASSERT(hInstance);
Instance = hInstance;
CheckInstructionSet();
// Disable the process from being showing "ghosted" while not responding messages during slow tasks
DisableProcessWindowsGhosting();
@@ -47,6 +47,9 @@ public:
Data CachedData;
ToneMappingMode Mode = ToneMappingMode::None;
Texture* LutTexture = nullptr;
#if COMPILE_WITH_DEV_ENV
uint64 FrameRendered = 0;
#endif
~ColorGradingCustomBuffer()
{
@@ -54,6 +57,17 @@ public:
}
};
#if COMPILE_WITH_DEV_ENV
void ColorGradingPass::OnShaderReloading(Asset* obj)
{
_psLut.Release();
invalidateResources();
_reloadedFrame = Engine::FrameCount;
}
#endif
String ColorGradingPass::ToString() const
{
return TEXT("ColorGradingPass");
@@ -194,6 +208,9 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
// Check if LUT parameter hasn't been changed since the last time
if (Platform::MemoryCompare(&colorGradingBuffer.CachedData , &data, sizeof(Data)) == 0 &&
colorGradingBuffer.Mode == toneMapping.Mode &&
#if COMPILE_WITH_DEV_ENV
colorGradingBuffer.FrameRendered > _reloadedFrame &&
#endif
Engine::FrameCount > 30 && // Skip caching when engine is starting TODO: find why this hack is needed
colorGradingBuffer.LutTexture == lutTexture)
{
@@ -203,6 +220,9 @@ GPUTexture* ColorGradingPass::RenderLUT(RenderContext& renderContext)
colorGradingBuffer.CachedData = data;
colorGradingBuffer.Mode = toneMapping.Mode;
colorGradingBuffer.LutTexture = lutTexture;
#if COMPILE_WITH_DEV_ENV
colorGradingBuffer.FrameRendered = Engine::FrameCount;
#endif
// Render LUT
PROFILE_GPU("Color Grading LUT");
+2 -5
View File
@@ -25,11 +25,8 @@ public:
private:
#if COMPILE_WITH_DEV_ENV
void OnShaderReloading(Asset* obj)
{
_psLut.Release();
invalidateResources();
}
uint64 _reloadedFrame = 0;
void OnShaderReloading(Asset* obj);
#endif
public:
@@ -98,6 +98,7 @@ void EyeAdaptationPass::Render(RenderContext& renderContext, GPUTexture* colorBu
if (mode == EyeAdaptationMode::Manual)
{
// Apply fixed manual exposure
context->ResetSR();
context->SetRenderTarget(*colorBuffer);
context->SetViewportAndScissors((float)colorBuffer->Width(), (float)colorBuffer->Height());
context->SetState(_psManual);
+1 -1
View File
@@ -32,7 +32,7 @@ public class Scripting : EngineModule
options.ScriptingAPI.Defines.Add("NET");
AddFrameworkDefines("NET{0}_{1}", dotnetSdk.Version.Major, 0); // "NET7_0"
for (int i = 5; i <= dotnetSdk.Version.Major; i++)
AddFrameworkDefines("NET{0}_{1}_OR_GREATER", dotnetSdk.Version.Major, 0); // "NET7_0_OR_GREATER"
AddFrameworkDefines("NET{0}_{1}_OR_GREATER", i, 0); // "NET7_0_OR_GREATER"
options.ScriptingAPI.Defines.Add("NETCOREAPP");
AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 3, 1); // "NETCOREAPP3_1_OR_GREATER"
AddFrameworkDefines("NETCOREAPP{0}_{1}_OR_GREATER", 2, 2);
@@ -65,6 +65,8 @@
#endif
#undef MessageBox
static void stbWrite(void* context, void* data, int size)
{
auto file = (FileWriteStream*)context;
+12 -5
View File
@@ -104,13 +104,20 @@ namespace FlaxEngine.GUI
/// <summary>
/// Gets or sets a value indicating whether enable drop down icon drawing.
/// </summary>
[EditorOrder(1)]
[EditorOrder(2)]
public bool EnableDropDownIcon { get; set; }
/// <summary>
/// Get or sets a value indicating whether the panel can be opened or closed via the user interacting with the ui.
/// Changing the open/ closed state from code or the Properties panel will still work regardless.
/// </summary>
[EditorOrder(1)]
public bool CanOpenClose { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether to enable containment line drawing,
/// </summary>
[EditorOrder(2)]
[EditorOrder(3)]
public bool EnableContainmentLines { get; set; } = false;
/// <summary>
@@ -369,7 +376,7 @@ namespace FlaxEngine.GUI
}
// Header
var color = _mouseOverHeader ? HeaderColorMouseOver : HeaderColor;
var color = _mouseOverHeader && CanOpenClose ? HeaderColorMouseOver : HeaderColor;
if (color.A > 0.0f)
{
Render2D.FillRectangle(new Rectangle(0, 0, Width, HeaderHeight), color);
@@ -510,7 +517,7 @@ namespace FlaxEngine.GUI
if (button == MouseButton.Left && _mouseButtonLeftDown)
{
_mouseButtonLeftDown = false;
if (_mouseOverHeader)
if (_mouseOverHeader && CanOpenClose)
Toggle();
return true;
}
@@ -540,7 +547,7 @@ namespace FlaxEngine.GUI
if (button == MouseButton.Left && _mouseButtonLeftDown)
{
_mouseButtonLeftDown = false;
if (_mouseOverHeader)
if (_mouseOverHeader && CanOpenClose)
Toggle();
return true;
}
+1 -16
View File
@@ -180,7 +180,7 @@ namespace FlaxEngine.GUI
}
/// <inheritdoc />
public override void Draw()
public override void DrawSelf()
{
var bounds = new Rectangle(Float2.Zero, Size);
@@ -205,21 +205,6 @@ namespace FlaxEngine.GUI
Render2D.DrawTexture(buffer, bounds, color);
else
Render2D.FillRectangle(bounds, Color.Black);
// Push clipping mask
if (ClipChildren)
{
GetDesireClientArea(out var clientArea);
Render2D.PushClip(ref clientArea);
}
DrawChildren();
// Pop clipping mask
if (ClipChildren)
{
Render2D.PopClip();
}
}
/// <summary>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+3 -4
View File
@@ -164,16 +164,14 @@ float3 TonemapACES(float3 linearColor)
// The code was originally written by Stephen Hill (@self_shadow).
// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
static const float3x3 ACESInputMat =
{
static const float3x3 ACESInputMat = {
{0.59719, 0.35458, 0.04823},
{0.07600, 0.90834, 0.01566},
{0.02840, 0.13383, 0.83777}
};
// ODT_SAT => XYZ => D60_2_D65 => sRGB
static const float3x3 ACESOutputMat =
{
static const float3x3 ACESOutputMat = {
{ 1.60475, -0.53108, -0.07367},
{-0.10208, 1.10813, -0.00605},
{-0.00327, -0.07276, 1.07602}
@@ -188,6 +186,7 @@ float3 TonemapACES(float3 linearColor)
color = a / b;
color = mul(ACESOutputMat, color);
return saturate(color);
}
+2 -5
View File
@@ -37,7 +37,7 @@ float4 FastTonemapInvert(float4 c)
return float4(FastTonemapInvert(c.rgb), c.a);
}
float LinearToSrgbChannel(float linearColor)
float LinearToSrgb(float linearColor)
{
if (linearColor < 0.00313067)
return linearColor * 12.92;
@@ -46,10 +46,7 @@ float LinearToSrgbChannel(float linearColor)
float3 LinearToSrgb(float3 linearColor)
{
return float3(
LinearToSrgbChannel(linearColor.r),
LinearToSrgbChannel(linearColor.g),
LinearToSrgbChannel(linearColor.b));
return float3(LinearToSrgb(linearColor.r), LinearToSrgb(linearColor.g), LinearToSrgb(linearColor.b));
}
float3 sRGBToLinear(float3 color)
+4
View File
@@ -72,7 +72,11 @@ __BEGIN_DECLS
#include <stdint.h>
#include <stdbool.h>
#if defined(_MSC_VER)
#define DETEX_INLINE_ONLY __forceinline
#else
#define DETEX_INLINE_ONLY __attribute__((always_inline)) inline
#endif
#define DETEX_RESTRICT __restrict
/* Maximum uncompressed block size in bytes. */
+8
View File
@@ -6,6 +6,10 @@
#include "../common/TracyYield.hpp"
#if PLATFORM_WINDOWS
extern void CheckInstructionSet();
#endif
namespace tracy
{
@@ -18,6 +22,10 @@ tracy_no_inline static void InitRpmallocPlumbing()
const auto done = RpInitDone.load( std::memory_order_acquire );
if( !done )
{
#if PLATFORM_WINDOWS
// Check instruction set before executing any code (Tracy init static vars before others)
CheckInstructionSet();
#endif
int expected = 0;
while( !RpInitLock.compare_exchange_weak( expected, 1, std::memory_order_release, std::memory_order_relaxed ) ) { expected = 0; YieldThread(); }
const auto done = RpInitDone.load( std::memory_order_acquire );
@@ -127,11 +127,12 @@ namespace Flax.Deps.Dependencies
{ "CC", "clang-" + Configuration.LinuxClangMinVer },
{ "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer },
{ "CXX", "clang++-" + Configuration.LinuxClangMinVer },
{ "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel },
};
// Build for Linux
RunCmake(root, platform, TargetArchitecture.x64, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + globalConfig, envVars);
Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool, envVars);
configHeaderFilePath = Path.Combine(root, "include", "assimp", "config.h");
var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64);
Utilities.FileCopy(Path.Combine(root, "lib", "libassimp.a"), Path.Combine(depsFolder, "libassimp.a"));
@@ -143,11 +144,11 @@ namespace Flax.Deps.Dependencies
foreach (var architecture in new[] { TargetArchitecture.x64, TargetArchitecture.ARM64 })
{
RunCmake(root, platform, architecture, " -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF " + globalConfig);
Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool);
configHeaderFilePath = Path.Combine(root, "include", "assimp", "config.h");
var depsFolder = GetThirdPartyFolder(options, platform, architecture);
Utilities.FileCopy(Path.Combine(root, "lib", "libassimp.a"), Path.Combine(depsFolder, "libassimp.a"));
Utilities.Run("make", "clean", null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("make", "clean", null, root, Utilities.RunOptions.DefaultTool);
}
break;
}
@@ -115,6 +115,7 @@ namespace Flax.Deps.Dependencies
var buildFolder = Path.Combine(nvCloth, "compiler", platform.ToString() + '_' + architecture.ToString());
var envVars = new Dictionary<string, string>();
envVars["GW_DEPS_ROOT"] = root;
envVars["CMAKE_BUILD_PARALLEL_LEVEL"] = CmakeBuildParallel;
switch (platform)
{
case TargetPlatform.Windows:
@@ -166,6 +167,8 @@ namespace Flax.Deps.Dependencies
cmakeArgs += " -DTARGET_BUILD_PLATFORM=linux";
cmakeName = "linux";
binariesPrefix = "lib";
envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer);
envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer);
break;
default: throw new InvalidPlatformException(platform);
}
@@ -123,6 +123,7 @@ namespace Flax.Deps.Dependencies
{
{ "CC", "clang-" + Configuration.LinuxClangMinVer },
{ "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer },
{ "CXX", "clang++-" + Configuration.LinuxClangMinVer },
{ "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel },
};
var config = $"-DALSOFT_REQUIRE_ALSA=ON " +
@@ -238,6 +238,8 @@ namespace Flax.Deps.Dependencies
case TargetPlatform.Linux:
envVars.Add("CC", "clang-" + Configuration.LinuxClangMinVer);
envVars.Add("CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer);
envVars.Add("CXX", "clang++-" + Configuration.LinuxClangMinVer);
envVars.Add("CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel);
break;
case TargetPlatform.Mac: break;
default: throw new InvalidPlatformException(BuildPlatform);
@@ -258,7 +260,7 @@ namespace Flax.Deps.Dependencies
Log.Info("Building PhysX version " + File.ReadAllText(Path.Combine(root, "physx", "version.txt")) + " to " + binariesSubDir);
// Generate project files
Utilities.Run(projectGenPath, preset, null, projectGenDir, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run(projectGenPath, preset, null, projectGenDir, Utilities.RunOptions.DefaultTool, envVars);
switch (targetPlatform)
{
@@ -304,10 +306,10 @@ namespace Flax.Deps.Dependencies
}
break;
case TargetPlatform.Linux:
Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.ConsoleLogOutput);
Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.ConsoleLogOutput, envVars);
break;
case TargetPlatform.Mac:
Utilities.Run("xcodebuild", "-project PhysXSDK.xcodeproj -alltargets -configuration " + configuration, null, Path.Combine(projectGenDir, "compiler", preset), Utilities.RunOptions.ConsoleLogOutput);
Utilities.Run("xcodebuild", "-project PhysXSDK.xcodeproj -alltargets -configuration " + configuration, null, Path.Combine(projectGenDir, "compiler", preset), Utilities.RunOptions.ConsoleLogOutput, envVars);
break;
default: throw new InvalidPlatformException(BuildPlatform);
}
@@ -107,13 +107,14 @@ namespace Flax.Deps.Dependencies
{
{ "CC", "clang-" + Configuration.LinuxClangMinVer },
{ "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer },
{ "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel },
};
var buildDir = Path.Combine(root, "build");
SetupDirectory(buildDir, true);
Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("make", "install", null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.DefaultTool);
Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.DefaultTool, envVars);
Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool);
Utilities.Run("make", "install", null, root, Utilities.RunOptions.DefaultTool);
var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64);
var filename = "libcurl.a";
Utilities.FileCopy(Path.Combine(buildDir, "lib", filename), Path.Combine(depsFolder, filename));
@@ -153,11 +154,11 @@ namespace Flax.Deps.Dependencies
};
var buildDir = Path.Combine(root, "build");
SetupDirectory(buildDir, true);
Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("chmod", "+x install-sh", null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --host=" + archName + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run("make", null, null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("make", "install", null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.DefaultTool);
Utilities.Run("chmod", "+x install-sh", null, root, Utilities.RunOptions.DefaultTool);
Utilities.Run(Path.Combine(root, "configure"), string.Join(" ", settings) + " --host=" + archName + " --prefix=\"" + buildDir + "\"", null, root, Utilities.RunOptions.DefaultTool, envVars);
Utilities.Run("make", null, null, root, Utilities.RunOptions.DefaultTool);
Utilities.Run("make", "install", null, root, Utilities.RunOptions.DefaultTool);
var depsFolder = GetThirdPartyFolder(options, platform, architecture);
var filename = "libcurl.a";
Utilities.FileCopy(Path.Combine(buildDir, "lib", filename), Path.Combine(depsFolder, filename));
@@ -114,19 +114,22 @@ namespace Flax.Deps.Dependencies
}
case TargetPlatform.Linux:
{
var envVars = new Dictionary<string, string>
var envVars = new Dictionary<string, string>
{
{ "CC", "clang-" + Configuration.LinuxClangMinVer },
{ "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }
{ "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer },
{ "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel },
};
// Fix scripts
Utilities.Run("sed", "-i -e \'s/\r$//\' autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run("sed", "-i -e \'s/\r$//\' configure", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run("dos2unix", "autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run("dos2unix", "configure", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
//Utilities.Run("sed", "-i -e \'s/\r$//\' autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
//Utilities.Run("sed", "-i -e \'s/\r$//\' configure", null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run("chmod", "+x autogen.sh", null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("chmod", "+x configure", null, root, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.Default, envVars);
Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
// Disable using libpng even if it's found on the system
var cmakeFile = Path.Combine(root, "CMakeLists.txt");
@@ -140,8 +143,8 @@ namespace Flax.Deps.Dependencies
// Build for Linux
SetupDirectory(buildDir, true);
var toolchain = UnixToolchain.GetToolchainName(platform, TargetArchitecture.x64);
Utilities.Run("cmake", string.Format("-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_TARGET={0} ..", toolchain), null, buildDir, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run("cmake", string.Format("-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DFT_WITH_BZIP2=OFF -DFT_WITH_ZLIB=OFF -DFT_WITH_PNG=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_TARGET={0} ..", toolchain), null, buildDir, Utilities.RunOptions.DefaultTool, envVars);
Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.DefaultTool, envVars);
var depsFolder = GetThirdPartyFolder(options, platform, TargetArchitecture.x64);
Utilities.FileCopy(Path.Combine(buildDir, libraryFileName), Path.Combine(depsFolder, libraryFileName));
@@ -303,9 +303,9 @@ namespace Flax.Deps.Dependencies
}
// Ensure to have dependencies installed
Utilities.Run("ninja", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("cmake", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("python", "--version", null, null, Utilities.RunOptions.ThrowExceptionOnError);
Utilities.Run("ninja", "--version", null, null, Utilities.RunOptions.DefaultTool);
Utilities.Run("cmake", "--version", null, null, Utilities.RunOptions.DefaultTool);
Utilities.Run("python", "--version", null, null, Utilities.RunOptions.DefaultTool);
// Get the source
if (!Directory.Exists(Path.Combine(root, ".git")))
@@ -366,15 +366,17 @@ namespace Flax.Deps.Dependencies
var envVars = new Dictionary<string, string>
{
{ "CC", "clang-" + Configuration.LinuxClangMinVer },
{ "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer }
{ "CC_FOR_BUILD", "clang-" + Configuration.LinuxClangMinVer },
{ "CXX", "clang++-" + Configuration.LinuxClangMinVer },
{ "CMAKE_BUILD_PARALLEL_LEVEL", CmakeBuildParallel },
};
var buildDir = Path.Combine(root, "build");
Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.Default, envVars);
Utilities.Run(Path.Combine(root, "autogen.sh"), null, null, root, Utilities.RunOptions.DefaultTool, envVars);
// Build for Linux
var toolchain = UnixToolchain.GetToolchainName(platform, TargetArchitecture.x64);
Utilities.Run(Path.Combine(root, "configure"), string.Format("--host={0}", toolchain), null, root, Utilities.RunOptions.Default, envVars);
Utilities.Run(Path.Combine(root, "configure"), string.Format("--host={0}", toolchain), null, root, Utilities.RunOptions.ThrowExceptionOnError, envVars);
SetupDirectory(buildDir, true);
Utilities.Run("cmake", "-G \"Unix Makefiles\" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE=Release ..", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars);
Utilities.Run("cmake", "--build .", null, buildDir, Utilities.RunOptions.ConsoleLogOutput, envVars);
+1 -1
View File
@@ -456,7 +456,7 @@ namespace Flax.Deps
case TargetPlatform.Mac: break;
default: throw new InvalidPlatformException(BuildPlatform);
}
Utilities.Run(path, args, null, workspace, Utilities.RunOptions.ThrowExceptionOnError, envVars);
Utilities.Run(path, args, null, workspace, Utilities.RunOptions.DefaultTool, envVars);
}
internal bool GetMsBuildForPlatform(TargetPlatform targetPlatform, out VisualStudioVersion vsVersion, out string msBuildPath)
@@ -14,7 +14,7 @@ namespace Flax.Build
/// Specifies the minimum Clang compiler version to use on Linux (eg. 10).
/// </summary>
[CommandLine("linuxClangMinVer", "<version>", "Specifies the minimum Clang compiler version to use on Linux (eg. 10).")]
public static string LinuxClangMinVer = "13";
public static string LinuxClangMinVer = "14";
}
}